pax_global_header00006660000000000000000000000064141424660610014516gustar00rootroot0000000000000052 comment=8a62c1fe3245f031989b0b163b11435698b43e53 lazyarray-0.5.0/000077500000000000000000000000001414246606100135365ustar00rootroot00000000000000lazyarray-0.5.0/.gitignore000066400000000000000000000001311414246606100155210ustar00rootroot00000000000000.coverage __pycache__/ cover/ lazyarray.egg-info/ *.pyc test/__pycache__/ MANIFEST dist/ lazyarray-0.5.0/.travis.yml000066400000000000000000000013201414246606100156430ustar00rootroot00000000000000language: python sudo: false python: - 3.4 - 3.6 - 3.9 env: - NUMPY_VERSION="1.12.1" SCIPY_VERSION="0.17.1" - NUMPY_VERSION="1.13.1" SCIPY_VERSION="0.19.1" - NUMPY_VERSION="1.19.4" SCIPY_VERSION="1.5.4" matrix: exclude: - python: 3.4 env: NUMPY_VERSION="1.19.4" SCIPY_VERSION="1.5.4" - python: 3.6 env: NUMPY_VERSION="1.12.1" SCIPY_VERSION="0.17.1" - python: 3.9 env: NUMPY_VERSION="1.12.1" SCIPY_VERSION="0.17.1" - python: 3.9 env: NUMPY_VERSION="1.13.1" SCIPY_VERSION="0.19.1" install: - pip install "numpy==$NUMPY_VERSION" "scipy==$SCIPY_VERSION" coverage coveralls script: - nosetests --with-coverage --cover-package=lazyarray after_success: - coveralls lazyarray-0.5.0/LICENSE000066400000000000000000000027711414246606100145520ustar00rootroot00000000000000Copyright (c) 2012-2020, Andrew P. Davison, Joël Chavas and Elodie Legouée (CNRS) 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 names of the copyright holders nor the names of the 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. lazyarray-0.5.0/MANIFEST.in000066400000000000000000000001451414246606100152740ustar00rootroot00000000000000include LICENSE include README.rst include changelog.txt recursive-include doc *.txt prune doc/_buildlazyarray-0.5.0/README.rst000066400000000000000000000022671414246606100152340ustar00rootroot00000000000000 ========= lazyarray ========= lazyarray is a Python package that provides a lazily-evaluated numerical array class, ``larray``, based on and compatible with NumPy arrays. Lazy evaluation means that any operations on the array (potentially including array construction) are not performed immediately, but are delayed until evaluation is specifically requested. Evaluation of only parts of the array is also possible. Use of an ``larray`` can potentially save considerable computation time and memory in cases where: * arrays are used conditionally (i.e. there are cases in which the array is never used) * only parts of an array are used (for example in distributed computation, in which each MPI node operates on a subset of the elements of the array) .. image:: https://readthedocs.org/projects/lazyarray/badge/?version=latest :target: http://lazyarray.readthedocs.io/en/latest/ .. image:: https://travis-ci.org/NeuralEnsemble/lazyarray.svg?branch=master :target: https://travis-ci.org/NeuralEnsemble/lazyarray/ .. image:: https://coveralls.io/repos/github/NeuralEnsemble/lazyarray/badge.svg?branch=master :target: https://coveralls.io/github/NeuralEnsemble/lazyarray?branch=master lazyarray-0.5.0/bitbucket-pipelines.yml000066400000000000000000000007321414246606100202250ustar00rootroot00000000000000# This is a sample build configuration for Python. # Check our guides at https://confluence.atlassian.com/x/x4UWN for more examples. # Only use spaces to indent your .yml configuration. # ----- # You can specify a custom docker image from Docker Hub as your build environment. image: python:3.5.1 pipelines: default: - step: caches: - pip script: - pip install -r requirements.txt - pip install nose - nosetestslazyarray-0.5.0/changelog.txt000066400000000000000000000065461414246606100162410ustar00rootroot00000000000000======= Changes ======= Release 0.2.0 ============= * Fixed problems with deepcopying lazy arrays. * Optimization - uses `x.min()` rather than `min(x)` where possible. * Some fixes for when using boolean addressing. * Setting shape on an larray now also sets it on all larrays within the operations list. * Added `__eq__` method to larray. * Replaced some assertions with more specific Exceptions. * Added support for 'vectorized iterables', i.e. objects with a `next(n)` method so that you can return multiple values at once. * Fixed some bugs when creating a lazy array from an existing lazy array. * Added `dtype` attribute to `larray` class. Release 0.2.1 ============= * Previous release didn't work with Python 3. Release 0.2.2 ============= * Fixed behaviour of larray(VectorizedIterable) to match that of numpy array when indexing a single item. * Pulled out `partial_shape()` and `full_address()` methods of `larray` as standalone functions. * Better support for lists as masks. * `larray`s are now callable, provided their `base_value` is callable and the argument is another `larray`. Release 0.2.3 ============= * Support `numpy.int64` as indices. * Better support for boolean indices. * Handle the case of constant `larray`s of size 1. Release 0.2.4 ============= * Fixed bugs related to indexing multiple axes at the same time (`#3`_, `#4`_) Release 0.2.5 ============= * Fixed a bug where the base value was homogeneous but one or more operations involved inhomogeneous arrays. Release 0.2.6 ============= * Fixed a bug with callable lazyarrays. Release 0.2.7 ============= * When deepcopying, VectorizedIterable objects as base_value are no longer copied, rather we keep a reference to the original. Release 0.2.8 ============= * Slices which go past the array limits are now correctly handled. (`#5`_) Release 0.2.9 ============= * Support base values of type `numpy.float` (which have an empty `shape` attribute) Release 0.2.10 ============== * We don't create a new instance if the base value is already of the required dtype Release 0.3.0 ============= * Support SciPy sparse matrices as base values * Support any object that implements a method `lazily_evaluate` as a base value * Allow more flexibility in checking 'equality' of types, e.g. accept an array of dtype `numpy.float32` when the specified dtype is `float` Release 0.3.1 ============= * Fix a packaging issue, update project homepage. Release 0.3.2 ============= * Ensure SciPy is optional Release 0.3.3 ============= * Do not raise a "shape mismatch" `ValueError` if the value shape is empty. Release 0.3.4 ============= * Add support for Brian quantities, and perhaps NumPy scalars in general * Updated to test with more recent versions of Python, NumPy and SciPy * Can now compare equality of lazyarrays to numbers and arrays Release 0.4.0 ============= * Drop support for Python 2.7 * Added a more general way to specify that an array-like object should be treated as a scalar by lazyarray (for arrays of arrays, etc.) Release 0.5.0 ============= * Add partial support for NumPy ufuncs that require two arguments, e.g. "power". The second argument must be a scalar, array-valued second args are not yet supported. .. _`#3`: https://bitbucket.org/apdavison/lazyarray/issue/3/ .. _`#4`: https://bitbucket.org/apdavison/lazyarray/issue/4/ .. _`#5`: https://bitbucket.org/apdavison/lazyarray/issue/5/ lazyarray-0.5.0/doc/000077500000000000000000000000001414246606100143035ustar00rootroot00000000000000lazyarray-0.5.0/doc/Makefile000066400000000000000000000107461414246606100157530ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyNN.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyNN.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/PyNN" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyNN" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." lazyarray-0.5.0/doc/conf.py000066400000000000000000000205461414246606100156110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # lazyarray documentation build configuration file # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os sys.path.append(os.path.abspath('..')) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # 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.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.txt' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'lazyarray' copyright = u'2012-2021, Andrew P. Davison, Joël Chavas and Elodie Legouée (CNRS)' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.5' # The full version, including alpha/beta/rc tags. release = '0.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- 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 = 'nature' # 'agogo', 'haiku' # 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 themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = 'lazyarray_logo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = 'lazyarray_icon.ico' # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'lazyarraydoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). latex_paper_size = 'a4' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'lazyarray.tex', u'Lazyarray Documentation', u'Andrew P. Davison, Joël Chavas and Elodie Legouée', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'lazyarray', u'lazyarray documentation', [u'Andrew P. Davison'], 1) ] # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'lazyarray' epub_author = u'Andrew P. Davison, Joël Chavas and Elodie Legouée' epub_publisher = u'Andrew P. Davison' epub_copyright = u'2012-2021, Andrew P. Davison, Joël Chavas and Elodie Legouée' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. #epub_exclude_files = [] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True # -- Options for doctests ---- doctest_global_setup = """ from lazyarray import larray import numpy as np """ lazyarray-0.5.0/doc/developers.txt000066400000000000000000000012721414246606100172160ustar00rootroot00000000000000================= Developers' guide ================= TO BE COMPLETED Testing ======= In the `test` sub-directory, run:: $ nosetests To see how well the tests cover the code base, run:: $ nosetests --with-coverage --cover-package=lazyarray --cover-erase --cover-html Making a release ================ * Update the version numbers in setup.py, lazyarray.py, doc/conf.py and doc/installation.txt * Update changelog.txt * Run all the tests with both Python 2 and Python 3 * python setup.py sdist upload * Update the download link in doc/installation.txt * Commit the changes, tag with release number, push to Github * Rebuild the documentation at http://lazyarray.readthedocs.org/lazyarray-0.5.0/doc/index.txt000066400000000000000000000026241414246606100161570ustar00rootroot00000000000000========= lazyarray ========= lazyarray is a Python package that provides a lazily-evaluated numerical array class, :class:`larray`, based on and compatible with NumPy arrays. Lazy evaluation means that any operations on the array (potentially including array construction) are not performed immediately, but are delayed until evaluation is specifically requested. Evaluation of only parts of the array is also possible. Use of an :class:`larray` can potentially save considerable computation time and memory in cases where: * arrays are used conditionally (i.e. there are cases in which the array is never used); * only parts of an array are used (for example in distributed computation, in which each MPI node operates on a subset of the elements of the array). It appears that much of this functionality may appear in a future version of NumPy (see `this discussion`_ on the numpy-discussion mailing list), but at the time of writing I couldn't find anything equivalent out there. DistNumPy_ might also be an alternative for some of the use cases of lazyarray. Contents ======== .. toctree:: :maxdepth: 2 installation tutorial performance reference developers Licence ======= The code is released under the Modified BSD licence. .. _`this discussion`: http://www.mail-archive.com/numpy-discussion@scipy.org/msg29732.html .. _DistNumPy: http://sites.google.com/site/distnumpy/lazyarray-0.5.0/doc/installation.txt000066400000000000000000000022641414246606100175510ustar00rootroot00000000000000============ Installation ============ Dependencies ============ * Python >= 3.4 * numpy_ >= 1.12 * (optional) scipy_ >= 0.17 Installing from the Python Package Index ======================================== If you have pip_ installed:: $ pip install lazyarray This will automatically download and install the latest release (you may need to have administrator privileges on the machine you are installing on). To download and install manually, download: https://pypi.python.org/packages/source/l/lazyarray/lazyarray-0.5.0.tar.gz Then:: $ tar xzf lazyarray-0.5.0.tar.gz $ cd lazyarray-0.5.0 $ python setup.py install or:: $ python3 setup.py install depending on which version of Python you are using. Installing from source ====================== To install the latest version of lazyarray from the Git repository:: $ git clone https://github.com/NeuralEnsemble/lazyarray $ cd lazyarray $ python setup.py install .. _`numpy`: http://numpy.scipy.org/ .. _`scipy`: http://scipy.org/ .. _`quantities`: http://pypi.python.org/pypi/quantities .. _`pip`: http://pypi.python.org/pypi/pip .. _`setuptools`: http://pypi.python.org/pypi/setuptoolslazyarray-0.5.0/doc/make.bat000066400000000000000000000106331414246606100157130ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyNN.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyNN.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end lazyarray-0.5.0/doc/performance.txt000066400000000000000000000037211414246606100173500ustar00rootroot00000000000000=========== Performance =========== The main aim of lazyarray is to improve performance (increased speed and reduced memory use) in two scenarios: * arrays are used conditionally (i.e. there are cases in which the array is never used); * only parts of an array are used (for example in distributed computation, in which each MPI node operates on a subset of the elements of the array). However, at the same time use of :class:`larray` objects should not be too much slower than plain NumPy arrays in normal use. Here we see that using a lazyarray adds minimal overhead compared to using a plain array: :: >>> from timeit import repeat >>> repeat('np.fromfunction(lambda i,j: i*i + 2*i*j + 3, (5000, 5000))', ... setup='import numpy as np', number=1, repeat=5) [1.9397640228271484, 1.92628812789917, 1.8796701431274414, 1.6766629219055176, 1.6844701766967773] >>> repeat('larray(lambda i,j: i*i + 2*i*j + 3, (5000, 5000)).evaluate()', ... setup='from lazyarray import larray', number=1, repeat=5) [1.686661958694458, 1.6836578845977783, 1.6853220462799072, 1.6538069248199463, 1.645576000213623] while if we only need to evaluate part of the array (perhaps because the other parts are being evaluated on other nodes), there is a major gain from using a lazy array. :: >>> repeat('np.fromfunction(lambda i,j: i*i + 2*i*j + 3, (5000, 5000))[:, 0:4999:10]', ... setup='import numpy as np', number=1, repeat=5) [1.691796064376831, 1.668884038925171, 1.647057056427002, 1.6792259216308594, 1.652547836303711] >>> repeat('larray(lambda i,j: i*i + 2*i*j + 3, (5000, 5000))[:, 0:4999:10]', ... setup='from lazyarray import larray', number=1, repeat=5) [0.23157119750976562, 0.16121792793273926, 0.1594078540802002, 0.16096210479736328, 0.16096997261047363] .. note:: These timings were done on a MacBook Pro 2.8 GHz Intel Core 2 Duo with 4 GB RAM, Python 2.7 and NumPy 1.6.1.lazyarray-0.5.0/doc/reference.txt000066400000000000000000000005061414246606100170030ustar00rootroot00000000000000========= Reference ========= .. autoclass:: lazyarray.larray :members: apply, nrows, ncols, is_homogeneous, shape .. method:: evaluate(simplify=False) Return the lazy array as a real NumPy array. If the array is homogeneous and ``simplify`` is ``True``, return a single numerical value. lazyarray-0.5.0/doc/tutorial.txt000066400000000000000000000264471414246606100167240ustar00rootroot00000000000000======== Tutorial ======== The :mod:`lazyarray` module contains a single class, :class:`larray`. .. doctest:: >>> from lazyarray import larray Creating a lazy array ===================== Lazy arrays may be created from single numbers, from sequences (lists, NumPy arrays), from iterators, from generators, or from a certain class of functions. Here are some examples: .. doctest:: >>> from_number = larray(20.0) >>> from_list = larray([0, 1, 1, 2, 3, 5, 8]) >>> import numpy as np >>> from_array = larray(np.arange(6).reshape((2, 3))) >>> from_iter = larray(iter(range(8))) >>> from_gen = larray((x**2 + 2*x + 3 for x in range(5))) To create a lazy array from a function or other callable, the function must accept one or more integers as arguments (depending on the dimensionality of the array) and return a single number. .. doctest:: >>> def f(i, j): ... return i*np.sin(np.pi*j/100) >>> from_func = larray(f) Specifying array shape ---------------------- Where the :class:`larray` is created from something that does not already have a known shape (i.e. from something that is not a list or array), it is possible to specify the shape of the array at the time of construction: .. doctest:: >>> from_func2 = larray(lambda i: 2*i, shape=(6,)) >>> print(from_func2.shape) (6,) For sequences, the shape is introspected: .. doctest:: >>> from_list.shape (7,) >>> from_array.shape (2, 3) Otherwise, the :attr:`shape` attribute is set to ``None``, and must be set later before the array can be evaluated. .. doctest:: >>> print(from_number.shape) None >>> print(from_iter.shape) None >>> print(from_gen.shape) None >>> print(from_func.shape) None Evaluating a lazy array ======================= The simplest way to evaluate a lazy array is with the :meth:`evaluate` method, which returns a NumPy array: .. doctest:: >>> from_list.evaluate() array([0, 1, 1, 2, 3, 5, 8]) >>> from_array.evaluate() array([[0, 1, 2], [3, 4, 5]]) >>> from_number.evaluate() Traceback (most recent call last): File "", line 1, in File "/Users/andrew/dev/lazyarray/lazyarray.py", line 35, in wrapped_meth raise ValueError("Shape of larray not specified") ValueError: Shape of larray not specified >>> from_number.shape = (2, 2) >>> from_number.evaluate() array([[ 20., 20.], [ 20., 20.]]) Note that an :class:`larray` can only be evaluated once its shape has been defined. Note also that a lazy array created from a single number evaluates to a homogeneous array containing that number. To obtain just the value, use the ``simplify`` argument: .. doctest:: >>> from_number.evaluate(simplify=True) 20.0 Evaluating a lazy array created from an iterator or generator fills the array in row-first order. The number of values generated by the iterator must fit within the array shape: .. doctest:: >>> from_iter.shape = (2, 4) >>> from_iter.evaluate() array([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.]]) >>> from_gen.shape = (5,) >>> from_gen.evaluate() array([ 3., 6., 11., 18., 27.]) If it doesn't, an Exception is raised: .. doctest:: >>> from_iter.shape = (7,) >>> from_iter.evaluate() Traceback (most recent call last): File "", line 1, in from_iter.evaluate() File "/Users/andrew/dev/lazyarray/lazyarray.py", line 36, in wrapped_meth return meth(self, *args, **kwargs) File "/Users/andrew/dev/lazyarray/lazyarray.py", line 235, in evaluate x = x.reshape(self.shape) ValueError: total size of new array must be unchanged When evaluating a lazy array created from a callable, the function is called with the indices of each element of the array: .. doctest:: >>> from_func.shape = (3, 4) >>> from_func.evaluate() array([[ 0. , 0. , 0. , 0. ], [ 0. , 0.03141076, 0.06279052, 0.09410831], [ 0. , 0.06282152, 0.12558104, 0.18821663]]) It is also possible to evaluate only parts of an array. This is explained below. Performing operations on a lazy array ===================================== Just as with a normal NumPy array, it is possible to perform elementwise arithmetic operations: .. doctest:: >>> a = from_list + 2 >>> b = 2*a >>> print(type(b)) However, these operations are not carried out immediately, rather they are queued up to be carried out later, which can lead to large time and memory savings if the evaluation step turns out later not to be needed, or if only part of the array needs to be evaluated. .. doctest:: >>> b.evaluate() array([ 4, 6, 6, 8, 10, 14, 20]) Some more examples: .. doctest:: >>> a = 1.0/(from_list + 1) >>> a.evaluate() array([ 1. , 0.5 , 0.5 , 0.33333333, 0.25 , 0.16666667, 0.11111111]) >>> (from_list < 2).evaluate() array([ True, True, True, False, False, False, False], dtype=bool) >>> (from_list**2).evaluate() array([ 0, 1, 1, 4, 9, 25, 64]) >>> x = from_list >>> (x**2 - 2*x + 5).evaluate() array([ 5, 4, 4, 5, 8, 20, 53]) Numpy ufuncs cannot be used directly with lazy arrays, as NumPy does not know what to do with :class:`larray` objects. The lazyarray module therefore provides lazy array-compatible versions of a subset of the NumPy ufuncs, e.g.: .. doctest:: >>> from lazyarray import sqrt >>> sqrt(from_list).evaluate() array([ 0. , 1. , 1. , 1.41421356, 1.73205081, 2.23606798, 2.82842712]) For any other function that operates on a NumPy array, it can be applied to a lazy array using the :meth:`apply()` method: .. doctest:: >>> def g(x): ... return x**2 - 2*x + 5 >>> from_list.apply(g) >>> from_list.evaluate() array([ 5, 4, 4, 5, 8, 20, 53]) Partial evaluation ================== When accessing a single element of an array, only that element is evaluated, where possible, not the whole array: .. doctest:: >>> x = larray(lambda i,j: 2*i + 3*j, shape=(4, 5)) >>> x[3, 2] 12 >>> y = larray(lambda i: i*(2-i), shape=(6,)) >>> y[4] -8 The same is true for accessing individual rows or columns: .. doctest:: >>> x[1] array([ 2, 5, 8, 11, 14]) >>> x[:, 4] array([12, 14, 16, 18]) >>> x[:, (0, 4)] array([[ 0, 12], [ 2, 14], [ 4, 16], [ 6, 18]]) Creating lazy arrays from SciPy sparse matrices =============================================== Lazy arrays may also be created from SciPy sparse matrices. There are 7 different sparse matrices. - csc_matrix(arg1[, shape, dtype, copy]) Compressed Sparse Column matrix - csr_matrix(arg1[, shape, dtype, copy]) Compressed Sparse Row matrix - bsr_matrix(arg1[, shape, dtype, copy, blocksize]) Block Sparse Row matrix - lil_matrix(arg1[, shape, dtype, copy]) Row-based linked list sparse matrix - dok_matrix(arg1[, shape, dtype, copy]) Dictionary Of Keys based sparse matrix. - coo_matrix(arg1[, shape, dtype, copy]) A sparse matrix in COOrdinate format. - dia_matrix(arg1[, shape, dtype, copy]) Sparse matrix with DIAgonal storage Here are some examples to use them. Creating sparse matrices ------------------------ Sparse matrices comes from SciPy package for numerical data. First to use them it is necessary to import libraries. .. doctest:: >>> import numpy as np >>> from lazyarray import larray >>> from scipy.sparse import bsr_matrix, coo_matrix, csc_matrix, csr_matrix, dia_matrix, dok_matrix, lil_matrix Creating a sparse matrix requires filling each row and column with data. For example : .. doctest:: >>> row = np.array([0, 2, 2, 0, 1, 2]) >>> col = np.array([0, 0, 1, 2, 2, 2]) >>> data = np.array([1, 2, 3, 4, 5, 6]) The 7 sparse matrices are not defined in the same way. The bsr_matrix, coo_matrix, csc_matrix and csr_matrix are defined as follows : .. doctest:: >>> sparr = bsr_matrix((data, (row, col)), shape=(3, 3)) >>> sparr = coo_matrix((data, (row, col)), shape=(3, 3)) >>> sparr = csc_matrix((data, (row, col)), shape=(3, 3)) >>> sparr = csr_matrix((data, (row, col)), shape=(3, 3)) In regards to the dia_matrix : .. doctest:: >>> data_dia = np.array([[1, 2, 3, 4]]).repeat(3, axis=0) >>> offsets = np.array([0, -1, 2]) >>> sparr = dia_matrix((data_dia, offsets), shape=(4, 4)) For the dok_matrix : .. doctest:: >>> sparr = dok_matrix(((row, col)), shape=(3, 3)) For the lil_matrix : .. doctest:: >>> sparr = lil_matrix(data, shape=(3, 3)) In the continuation of this tutorial, the sparse matrix used will be called sparr and refers to the csc_matrix. It is possible to convert the sparse matrix as a NumPy array, as follows: .. doctest:: >>> print(sparr.toarray()) array([[1, 0, 4], [0, 0, 5], [2, 3, 6]]) Specifying the shape and the type of a sparse matrix ---------------------------------------------------- To know the shape and the type of the sparse matrices, you can use : .. doctest:: >>> larr = larray(sparr) >>> print (larr.shape) (3, 3) >>> print (larr.dtype) dtype('int64') Evaluating a sparse matrix -------------------------- Evaluating a sparse matrix refers to the evaluate() method, which returns a NumPy array : .. doctest:: >>> print (larr.evaluate()) array([[1, 0, 4], [0, 0, 5], [2, 3, 6]]) When creating a sparse matrix, some values ​​may remain empty. In this case, the evaluate () method has the argument, called empty_val, referring to the special value nan, for Not a Number, defined in NumPy. This method fills these empty with this nan value. .. doctest:: >>> print (larr.evaluate(empty_val=np.nan)) array([[1, nan, 4], [nan, nan, 5], [2, 3, 6]]) Accessing individual rows or columns of a sparse matrix ------------------------------------------------------- To access specific elements of the matrix, like individual rows or columns : .. doctest:: >>> larr[2, :] In this case, the third line of the sparse matrix is obtained. However, this method is different depending on the sparse matrices used : For csc_matrix and csr_matrix : .. doctest:: >>> print (larr[2, :]) array([2, 3, 6]) During execution, the matrices bsr_matrix, coo_matrix and dia_matrix, do not support indexing. The solution is to convert them to another format. It is therefore necessary to go through csr_matrix in order to perform the calculation. .. doctest:: >>> print(sparr.tocsr()[2,:]) Depending on the definition given previously to the matrix, for the dok_matrix : .. doctest:: >>> print (larr[1, :]) And for lil_matrix : .. doctest:: >>> print (larr[0, :]) In case we want to access an element of a column, we must proceed in the same way as previously, by changing index. Here is an example of how to access an item in the third column of the sparse matrix. .. doctest:: >>> larr[:, 2] Finally, to have information on the sparse matrix : .. doctest:: >>>print (larr.base_value) <3x3 sparse matrix of type '' with 6 stored elements in Compressed Sparse Column format> lazyarray-0.5.0/lazyarray.py000066400000000000000000000556551414246606100161460ustar00rootroot00000000000000# encoding: utf-8 """ lazyarray is a Python package that provides a lazily-evaluated numerical array class, ``larray``, based on and compatible with NumPy arrays. Copyright Andrew P. Davison, Joël Chavas and Elodie Legouée (CNRS), 2012-2020 """ import numbers import operator from copy import deepcopy import collections from functools import wraps, reduce import logging import numpy as np try: from scipy import sparse from scipy.sparse import bsr_matrix, coo_matrix, csc_matrix, csr_matrix, dia_matrix, dok_matrix, lil_matrix have_scipy = True except ImportError: have_scipy = False __version__ = "0.5.0" logger = logging.getLogger("lazyarray") def check_shape(meth): """ Decorator for larray magic methods, to ensure that the operand has the same shape as the array. """ @wraps(meth) def wrapped_meth(self, val): if isinstance(val, (larray, np.ndarray)) and val.shape: if val.shape != self._shape: raise ValueError("shape mismatch: objects cannot be broadcast to a single shape") return meth(self, val) return wrapped_meth def requires_shape(meth): @wraps(meth) def wrapped_meth(self, *args, **kwargs): if self._shape is None: raise ValueError("Shape of larray not specified") return meth(self, *args, **kwargs) return wrapped_meth def full_address(addr, full_shape): if not (isinstance(addr, np.ndarray) and addr.dtype == bool and addr.ndim == len(full_shape)): if not isinstance(addr, tuple): addr = (addr,) if len(addr) < len(full_shape): full_addr = [slice(None)] * len(full_shape) for i, val in enumerate(addr): full_addr[i] = val addr = full_addr return addr def partial_shape(addr, full_shape): """ Calculate the size of the sub-array represented by `addr` """ def size(x, max): if isinstance(x, (int, np.integer)): return None elif isinstance(x, slice): y = min(max, x.stop or max) # slice limits can go past the bounds return 1 + (y - (x.start or 0) - 1) // (x.step or 1) elif isinstance(x, collections.Sized): if hasattr(x, 'dtype') and x.dtype == bool: return x.sum() else: return len(x) else: raise TypeError("Unsupported index type %s" % type(x)) addr = full_address(addr, full_shape) if isinstance(addr, np.ndarray) and addr.dtype == bool: return (addr.sum(),) elif all(isinstance(x, collections.Sized) for x in addr): return (len(addr[0]),) else: shape = [size(x, max) for (x, max) in zip(addr, full_shape)] return tuple([x for x in shape if x is not None]) # remove empty dimensions def reverse(func): """Given a function f(a, b), returns f(b, a)""" @wraps(func) def reversed_func(a, b): return func(b, a) reversed_func.__doc__ = "Reversed argument form of %s" % func.__doc__ reversed_func.__name__ = "reversed %s" % func.__name__ return reversed_func # "The hash of a function object is hash(func_code) ^ id(func_globals)" ? # see http://mail.python.org/pipermail/python-dev/2000-April/003397.html def lazy_operation(name, reversed=False): def op(self, val): new_map = deepcopy(self) f = getattr(operator, name) if reversed: f = reverse(f) new_map.operations.append((f, val)) return new_map return check_shape(op) def lazy_inplace_operation(name): def op(self, val): self.operations.append((getattr(operator, name), val)) return self return check_shape(op) def lazy_unary_operation(name): def op(self): new_map = deepcopy(self) new_map.operations.append((getattr(operator, name), None)) return new_map return op def is_array_like(value): # False for numbers, generators, functions, iterators if not isinstance(value, collections.Sized): return False if sparse.issparse(value): return True if isinstance(value, collections.Mapping): # because we may wish to have lazy arrays in which each # item is a dict, for example return False if getattr(value, "is_lazyarray_scalar", False): # for user-defined classes that are "Sized" but that should # be treated as individual elements in a lazy array # the attribute "is_lazyarray_scalar" can be defined with value # True. return False return True class larray(object): """ Optimises storage of and operations on arrays in various ways: - stores only a single value if all the values in the array are the same; - if the array is created from a function `f(i)` or `f(i,j)`, then elements are only evaluated when they are accessed. Any operations performed on the array are also queued up to be executed on access. Two use cases for the latter are: - to save memory for very large arrays by accessing them one row or column at a time: the entire array need never be in memory. - in parallelized code, different rows or columns may be evaluated on different nodes or in different threads. """ def __init__(self, value, shape=None, dtype=None): """ Create a new lazy array. `value` : may be an int, float, bool, NumPy array, iterator, generator or a function, `f(i)` or `f(i,j)`, depending on the dimensions of the array. `f(i,j)` should return a single number when `i` and `j` are integers, and a 1D array when either `i` or `j` or both is a NumPy array (in the latter case the two arrays must have equal lengths). """ self.dtype = dtype self.operations = [] if isinstance(value, str): raise TypeError("An larray cannot be created from a string") elif isinstance(value, larray): if shape is not None and value.shape is not None: assert shape == value.shape self._shape = shape or value.shape self.base_value = value.base_value self.dtype = dtype or value.dtype self.operations = value.operations # should deepcopy? elif is_array_like(value): # False for numbers, generators, functions, iterators if have_scipy and sparse.issparse(value): # For sparse matrices self.dtype = dtype or value.dtype elif not isinstance(value, np.ndarray): value = np.array(value, dtype=dtype) elif dtype is not None: assert np.can_cast(value.dtype, dtype, casting='safe') # or could convert value to the provided dtype if shape and value.shape and value.shape != shape: raise ValueError("Array has shape %s, value has shape %s" % (shape, value.shape)) if value.shape: self._shape = value.shape else: self._shape = shape self.base_value = value else: assert np.isreal(value) # also True for callables, generators, iterators self._shape = shape if dtype is None or isinstance(value, dtype): self.base_value = value else: try: self.base_value = dtype(value) except TypeError: self.base_value = value def __eq__(self, other): if isinstance(other, self.__class__): return self.base_value == other.base_value and self.operations == other.operations and self._shape == other.shape elif isinstance(other, numbers.Number): if len(self.operations) == 0: if isinstance(self.base_value, numbers.Number): return self.base_value == other elif isinstance(self.base_value, np.ndarray): return (self.base_value == other).all() # todo: we could perform the evaluation ourselves, but that could have a performance hit raise Exception("You will need to evaluate this lazyarray before checking for equality") else: # todo: add support for NumPy arrays raise TypeError("Cannot at present compare equality of lazyarray and {}".format(type(other))) def __deepcopy__(self, memo): obj = type(self).__new__(type(self)) if isinstance(self.base_value, VectorizedIterable): # special case, but perhaps need to rethink obj.base_value = self.base_value # whether deepcopy is appropriate everywhere else: try: obj.base_value = deepcopy(self.base_value) except TypeError: # base_value cannot be copied, e.g. is a generator (but see generator_tools from PyPI) obj.base_value = self.base_value # so here we create a reference rather than deepcopying - could cause problems obj._shape = self._shape obj.dtype = self.dtype obj.operations = [] for f, arg in self.operations: if isinstance(f, np.ufunc): obj.operations.append((f, deepcopy(arg))) else: obj.operations.append((deepcopy(f), deepcopy(arg))) return obj def __repr__(self): return "" % (self.base_value, self.shape, self.dtype, self.operations) def _set_shape(self, value): if (hasattr(self.base_value, "shape") and self.base_value.shape and # values of type np.float have an empty shape self.base_value.shape != value): raise ValueError("Lazy array has fixed shape %s, cannot be changed to %s" % (self.base_value.shape, value)) self._shape = value for op in self.operations: if isinstance(op[1], larray): op[1].shape = value shape = property(fget=lambda self: self._shape, fset=_set_shape, doc="Shape of the array") @property @requires_shape def nrows(self): """Size of the first dimension of the array.""" return self._shape[0] @property @requires_shape def ncols(self): """Size of the second dimension (if it exists) of the array.""" if len(self.shape) > 1: return self._shape[1] else: return 1 @property @requires_shape def size(self): """Total number of elements in the array.""" return reduce(operator.mul, self._shape) @property def is_homogeneous(self): """True if all the elements of the array are the same.""" hom_base = isinstance(self.base_value, (int, np.integer, float, bool)) \ or type(self.base_value) == self.dtype \ or (isinstance(self.dtype, type) and isinstance(self.base_value, self.dtype)) hom_ops = all(obj.is_homogeneous for f, obj in self.operations if isinstance(obj, larray)) return hom_base and hom_ops def _partial_shape(self, addr): """ Calculate the size of the sub-array represented by `addr` """ return partial_shape(addr, self._shape) def _homogeneous_array(self, addr): self.check_bounds(addr) shape = self._partial_shape(addr) return np.ones(shape, type(self.base_value)) def _full_address(self, addr): return full_address(addr, self._shape) def _array_indices(self, addr): self.check_bounds(addr) def axis_indices(x, max): if isinstance(x, (int, np.integer)): return x elif isinstance(x, slice): # need to handle negative values in slice return np.arange((x.start or 0), (x.stop or max), (x.step or 1), dtype=int) elif isinstance(x, collections.Sized): if hasattr(x, 'dtype') and x.dtype == bool: return np.arange(max)[x] else: return np.array(x) else: raise TypeError("Unsupported index type %s" % type(x)) addr = self._full_address(addr) if isinstance(addr, np.ndarray) and addr.dtype == bool: if addr.ndim == 1: return (np.arange(self._shape[0])[addr],) else: raise NotImplementedError() elif all(isinstance(x, collections.Sized) for x in addr): indices = [np.array(x) for x in addr] return indices else: indices = [axis_indices(x, max) for (x, max) in zip(addr, self._shape)] if len(indices) == 1: return indices elif len(indices) == 2: if isinstance(indices[0], collections.Sized): if isinstance(indices[1], collections.Sized): mesh_xy = np.meshgrid(*indices) return (mesh_xy[0].T, mesh_xy[1].T) # meshgrid works on (x,y), not (i,j) return indices else: raise NotImplementedError("Only 1D and 2D arrays supported") @requires_shape def __getitem__(self, addr): """ Return one or more items from the array, as for NumPy arrays. `addr` may be a single integer, a slice, a NumPy boolean array or a NumPy integer array. """ return self._partially_evaluate(addr, simplify=False) def _partially_evaluate(self, addr, simplify=False): """ Return part of the lazy array. """ if self.is_homogeneous: if simplify: base_val = self.base_value else: base_val = self._homogeneous_array(addr) * self.base_value elif isinstance(self.base_value, (int, np.integer, float, bool)): base_val = self._homogeneous_array(addr) * self.base_value elif isinstance(self.base_value, np.ndarray): base_val = self.base_value[addr] elif have_scipy and sparse.issparse(self.base_value): # For sparse matrices larr[2, :] base_val = self.base_value[addr] elif callable(self.base_value): indices = self._array_indices(addr) base_val = self.base_value(*indices) if isinstance(base_val, np.ndarray) and base_val.shape == (1,): base_val = base_val[0] elif hasattr(self.base_value, "lazily_evaluate"): base_val = self.base_value.lazily_evaluate(addr, shape=self._shape) elif isinstance(self.base_value, VectorizedIterable): partial_shape = self._partial_shape(addr) if partial_shape: n = reduce(operator.mul, partial_shape) else: n = 1 base_val = self.base_value.next(n) # note that the array contents will depend on the order of access to elements if n == 1: base_val = base_val[0] elif partial_shape and base_val.shape != partial_shape: base_val = base_val.reshape(partial_shape) elif isinstance(self.base_value, collections.Iterator): raise NotImplementedError("coming soon...") else: raise ValueError("invalid base value for array (%s)" % self.base_value) return self._apply_operations(base_val, addr, simplify=simplify) @requires_shape def check_bounds(self, addr): """ Check whether the given address is within the array bounds. """ def is_boolean_array(arr): return hasattr(arr, 'dtype') and arr.dtype == bool def check_axis(x, size): if isinstance(x, (int, np.integer)): lower = upper = x elif isinstance(x, slice): lower = x.start or 0 upper = min(x.stop or size - 1, size - 1) # slices are allowed to go past the bounds elif isinstance(x, collections.Sized): if is_boolean_array(x): lower = 0 upper = x.size - 1 else: if len(x) == 0: raise ValueError("Empty address component (address was %s)" % str(addr)) if hasattr(x, "min"): lower = x.min() else: lower = min(x) if hasattr(x, "max"): upper = x.max() else: upper = max(x) else: raise TypeError("Invalid array address: %s (element of type %s)" % (str(addr), type(x))) if (lower < -size) or (upper >= size): raise IndexError("Index out of bounds") full_addr = self._full_address(addr) if isinstance(addr, np.ndarray) and addr.dtype == bool: if len(addr.shape) > len(self._shape): raise IndexError("Too many indices for array") for xmax, size in zip(addr.shape, self._shape): upper = xmax - 1 if upper >= size: raise IndexError("Index out of bounds") else: for i, size in zip(full_addr, self._shape): check_axis(i, size) def apply(self, f): """ Add the function `f(x)` to the list of the operations to be performed, where `x` will be a scalar or a numpy array. >>> m = larray(4, shape=(2,2)) >>> m.apply(np.sqrt) >>> m.evaluate() array([[ 2., 2.], [ 2., 2.]]) """ self.operations.append((f, None)) def _apply_operations(self, x, addr=None, simplify=False): for f, arg in self.operations: if arg is None: x = f(x) elif isinstance(arg, larray): if addr is None: x = f(x, arg.evaluate(simplify=simplify)) else: x = f(x, arg._partially_evaluate(addr, simplify=simplify)) else: x = f(x, arg) return x @requires_shape def evaluate(self, simplify=False, empty_val=0): """ Return the lazy array as a real NumPy array. If the array is homogeneous and ``simplify`` is ``True``, return a single numerical value. """ # need to catch the situation where a generator-based larray is evaluated a second time if self.is_homogeneous: if simplify: x = self.base_value else: x = self.base_value * np.ones(self._shape, dtype=self.dtype) elif isinstance(self.base_value, (int, np.integer, float, bool, np.bool_)): x = self.base_value * np.ones(self._shape, dtype=self.dtype) elif isinstance(self.base_value, np.ndarray): x = self.base_value elif callable(self.base_value): x = np.array(np.fromfunction(self.base_value, shape=self._shape, dtype=int), dtype=self.dtype) elif hasattr(self.base_value, "lazily_evaluate"): x = self.base_value.lazily_evaluate(shape=self._shape) elif isinstance(self.base_value, VectorizedIterable): x = self.base_value.next(self.size) if x.shape != self._shape: x = x.reshape(self._shape) elif have_scipy and sparse.issparse(self.base_value): # For sparse matrices if empty_val!=0: x = self.base_value.toarray((sparse.csc_matrix)) x = np.where(x, x, np.nan) else: x = self.base_value.toarray((sparse.csc_matrix)) elif isinstance(self.base_value, collections.Iterator): x = np.fromiter(self.base_value, dtype=self.dtype or float, count=self.size) if x.shape != self._shape: x = x.reshape(self._shape) else: raise ValueError("invalid base value for array") return self._apply_operations(x, simplify=simplify) def __call__(self, arg): if callable(self.base_value): if isinstance(arg, larray): new_map = deepcopy(arg) elif callable(arg): new_map = larray(arg) else: raise Exception("Argument must be either callable or an larray.") new_map.operations.append((self.base_value, None)) new_map.operations.extend(self.operations) return new_map else: raise Exception("larray is not callable") __iadd__ = lazy_inplace_operation('add') __isub__ = lazy_inplace_operation('sub') __imul__ = lazy_inplace_operation('mul') __idiv__ = lazy_inplace_operation('div') __ipow__ = lazy_inplace_operation('pow') __add__ = lazy_operation('add') __radd__ = __add__ __sub__ = lazy_operation('sub') __rsub__ = lazy_operation('sub', reversed=True) __mul__ = lazy_operation('mul') __rmul__ = __mul__ __div__ = lazy_operation('div') __rdiv__ = lazy_operation('div', reversed=True) __truediv__ = lazy_operation('truediv') __rtruediv__ = lazy_operation('truediv', reversed=True) __pow__ = lazy_operation('pow') __lt__ = lazy_operation('lt') __gt__ = lazy_operation('gt') __le__ = lazy_operation('le') __ge__ = lazy_operation('ge') __neg__ = lazy_unary_operation('neg') __pos__ = lazy_unary_operation('pos') __abs__ = lazy_unary_operation('abs') class VectorizedIterable(object): """ Base class for any class which has a method `next(n)`, i.e., where you can choose how many values to return rather than just returning one at a time. """ pass def _build_ufunc(func): """Return a ufunc that works with lazy arrays""" def larray_compatible_ufunc(x): if isinstance(x, larray): y = deepcopy(x) y.apply(func) return y else: return func(x) return larray_compatible_ufunc def _build_ufunc_2nd_arg(func): """Return a ufunc taking a second, non-array argument, that works with lazy arrays""" def larray_compatible_ufunc2(x1, x2): if not isinstance(x2, numbers.Number): raise TypeError("lazyarry ufuncs do not accept an array as the second argument") if isinstance(x1, larray): def partial(x): return func(x, x2) y = deepcopy(x1) y.apply(partial) return y else: return func(x1, x2) return larray_compatible_ufunc2 # build lazy-array compatible versions of NumPy ufuncs namespace = globals() for name in dir(np): obj = getattr(np, name) if isinstance(obj, np.ufunc) and name not in namespace: if name in ("power", "fmod", "arctan2, hypot, ldexp, maximum, minimum"): namespace[name] = _build_ufunc_2nd_arg(obj) else: namespace[name] = _build_ufunc(obj) lazyarray-0.5.0/requirements.txt000066400000000000000000000000131414246606100170140ustar00rootroot00000000000000numpy>=1.8 lazyarray-0.5.0/setup.py000066400000000000000000000015641414246606100152560ustar00rootroot00000000000000# -*- coding: utf-8 -*- from distutils.core import setup setup( name='lazyarray', version='0.5.0', py_modules=['lazyarray'], license='Modified BSD', author="Andrew P. Davison", author_email="andrew.davison@cnrs.fr", url="http://github.com/NeuralEnsemble/lazyarray/", description="a Python package that provides a lazily-evaluated numerical array class, larray, based on and compatible with NumPy arrays.", long_description=open('README.rst').read(), install_requires=[ "numpy >= 1.8", ], classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', 'Topic :: Scientific/Engineering', ] ) lazyarray-0.5.0/test/000077500000000000000000000000001414246606100145155ustar00rootroot00000000000000lazyarray-0.5.0/test/__init__.py000066400000000000000000000000001414246606100166140ustar00rootroot00000000000000lazyarray-0.5.0/test/performance.py000066400000000000000000000035011414246606100173670ustar00rootroot00000000000000 import numpy as np from numpy.testing import assert_array_equal from lazyarray import larray from timeit import repeat def test_function(i, j): return i * i + 2 * i * j + 3 def array_from_function_full(f, shape): return np.fromfunction(f, shape) def larray_from_function_full(f, shape): return larray(f, shape).evaluate() def array_from_function_slice(f, shape): return np.fromfunction(f, shape)[:, 0:-1:10] def larray_from_function_slice(f, shape): return larray(f, shape)[:, 0:shape[1] - 1:10] if __name__ == "__main__": assert_array_equal(array_from_function_full(test_function, (5000, 5000)), larray_from_function_full(test_function, (5000, 5000))) print "Array from function: full array" print(repeat('array_from_function_full(test_function, (5000, 5000))', setup='from __main__ import array_from_function_full, test_function', number=1, repeat=5)) print "Lazy array from function: full array" print(repeat('larray_from_function_full(test_function, (5000, 5000))', setup='from __main__ import larray_from_function_full, test_function', number=1, repeat=5)) assert_array_equal(array_from_function_slice(test_function, (5000, 5000)), larray_from_function_slice(test_function, (5000, 5000))) print "Array from function: slice" print(repeat('array_from_function_slice(test_function, (5000, 5000))', setup='from __main__ import array_from_function_slice, test_function', number=1, repeat=5)) print "Lazy array from function: slice" print(repeat('larray_from_function_slice(test_function, (5000, 5000))', setup='from __main__ import larray_from_function_slice, test_function', number=1, repeat=5)) lazyarray-0.5.0/test/test_lazy_arrays_from_Sparse_Matrices.py000066400000000000000000000137771414246606100246740ustar00rootroot00000000000000# Support creating lazy arrays from SciPy sparse matrices # # 1 program for the 7 sparse matrices classes : # # csc_matrix(arg1[, shape, dtype, copy]) Compressed Sparse Column matrix # csr_matrix(arg1[, shape, dtype, copy]) Compressed Sparse Row matrix # bsr_matrix(arg1[, shape, dtype, copy, blocksize]) Block Sparse Row matrix # lil_matrix(arg1[, shape, dtype, copy]) Row-based linked list sparse matrix # dok_matrix(arg1[, shape, dtype, copy]) Dictionary Of Keys based sparse matrix. # coo_matrix(arg1[, shape, dtype, copy]) A sparse matrix in COOrdinate format. # dia_matrix(arg1[, shape, dtype, copy]) Sparse matrix with DIAgonal storage # import numpy as np from lazyarray import larray from scipy import sparse import random ################ # Random numbers ################ i = random.randint(-100, 100) j = random.randint(-100, 100) k = random.randint(-100, 100) l = random.randint(-100, 100) m = random.randint(-100, 100) n = random.randint(-100, 100) p = random.randint(-100, 100) q = random.randint(-100, 100) r = random.randint(-100, 100) ################ # An example ################ #i = 1 #j = 2 #k = 0 #l = 0 #m = 0 #n = 3 #p = 1 #q = 0 #r = 4 #print "i =", i #print "j =", j #print "k =", k #print "l =", l #print "m =", m #print "n =", n #print "p =", p #print "q =", q #print "r =", r ############################################################## # Definition of an array ############################################################## def test_function_array_general(): A = np.array([[i, j, k], [l, m, n], [p, q, r]]) #print "A =" #print A return A ############################################################## # Definition of 7 sparse matrices ############################################################## def sparse_csc_matrices(): csc = sparse.csc_matrix([[i, j, k], [l, m, n], [p, q, r]]) #print "csc matrices =" #print csc return csc def sparse_csr_matrices(): csr = sparse.csr_matrix([[i, j, k], [l, m, n], [p, q, r]]) #print "csr matrices =" #print csr return csr def sparse_bsr_matrices(): bsr = sparse.bsr_matrix([[i, j, k], [l, m, n], [p, q, r]]) #print "bsr matrices =" #print bsr return bsr def sparse_lil_matrices(): lil = sparse.lil_matrix([[i, j, k], [l, m, n], [p, q, r]]) #print "lil matrices =" #print lil return lil def sparse_dok_matrices(): dok = sparse.dok_matrix([[i, j, k], [l, m, n], [p, q, r]]) #print "dok matrices =" #print dok return dok def sparse_coo_matrices(): coo = sparse.coo_matrix([[i, j, k], [l, m, n], [p, q, r]]) #print "coo matrices =" #print coo return coo def sparse_dia_matrices(): dia = sparse.dia_matrix([[i, j, k], [l, m, n], [p, q, r]]) #print "dia matrices =" #print dia return dia if __name__ == "__main__": ############################################################## # Call test_function_array_general # Create a sparse matrix from array # There are 7 sparse matrices ############################################################## #print "Array general =" test_function_array_general() #print "Array =" #print test_function_array_general() # print "----" # print "Sparse array csc general =" sA_csc_general = sparse.csc_matrix(test_function_array_general()) #print ("sparse csc matrices", sparse.csc_matrix(test_function_array_general())) #print "sparse csc matrices =" #print sA_csc_general # print "----" # print "Sparse array csr general =" sA_csr = sparse.csr_matrix(test_function_array_general()) #print ("sparse csr matrices", sparse.csr_matrix(test_function_array_general())) #print "sparse csr matrices =" #print sA_csr # print "----" # print "Sparse array bsr general =" sA_bsr = sparse.bsr_matrix(test_function_array_general()) # print ("sparse bsr matrices", sparse.bsr_matrix(test_function_array_general())) # print "sparse bsr matrices =" # print sA_bsr # print "----" # print "Sparse array lil general =" sA_lil = sparse.lil_matrix(test_function_array_general()) # print ("sparse lil matrices", sparse.lil_matrix(test_function_array_general())) # print "sparse lil matrices =" # print sA_lil # print "----" # print "Sparse array dok general =" sA_dok = sparse.dok_matrix(test_function_array_general()) # print ("sparse dok matrices", sparse.dok_matrix(test_function_array_general())) # print "sparse dok matrices =" # print sA_dok # print "----" # print "Sparse array coo general =" sA_coo = sparse.coo_matrix(test_function_array_general()) # print ("sparse coo matrices", sparse.coo_matrix(test_function_array_general())) # print "sparse coo matrices =" # print sA_coo # print "----" # print "Sparse array dia general =" sA_dia = sparse.dia_matrix(test_function_array_general()) # print ("sparse dia matrices", sparse.dia_matrix(test_function_array_general())) # print "sparse dia matrices =" # print sA_dia #print "----------------------------------------------------------------------" ############################################################## # Call the sparse matrices # Create a lazy array from sparse matrices ############################################################## Array_csc_matrices = sparse_csc_matrices().toarray() #print "Array csc matrices =" #print Array_csc_matrices Array_csr_matrices = sparse_csr_matrices().toarray() #print "Array csr matrices =" #print Array_csr_matrices Array_bsr_matrices = sparse_bsr_matrices().toarray() #print "Array bsr matrices =" #print Array_bsr_matrices Array_lil_matrices = sparse_lil_matrices().toarray() #print "Array lil matrices =" #print Array_lil_matrices Array_dok_matrices = sparse_dok_matrices().toarray() #print "Array dok matrices =" #print Array_dok_matrices Array_coo_matrices = sparse_coo_matrices().toarray() #print "Array coo matrices =" #print Array_coo_matrices Array_dia_matrices = sparse_dia_matrices().toarray() #print "Array dia matrices =" #print Array_dia_matriceslazyarray-0.5.0/test/test_lazyarray.py000066400000000000000000000721071414246606100201530ustar00rootroot00000000000000# encoding: utf-8 """ Unit tests for ``larray`` class Copyright Andrew P. Davison, Joël Chavas and Elodie Legouée (CNRS), 2012-2020 """ from lazyarray import larray, VectorizedIterable, sqrt, partial_shape import numpy as np from nose.tools import assert_raises, assert_equal, assert_not_equal from nose import SkipTest from numpy.testing import assert_array_equal, assert_array_almost_equal import operator from copy import deepcopy from scipy.sparse import bsr_matrix, coo_matrix, csc_matrix, csr_matrix, dia_matrix, dok_matrix, lil_matrix class MockRNG(VectorizedIterable): def __init__(self, start, delta): self.start = start self.delta = delta def next(self, n): s = self.start self.start += n * self.delta return s + self.delta * np.arange(n) # test larray def test_create_with_int(): A = larray(3, shape=(5,)) assert A.shape == (5,) assert A.evaluate(simplify=True) == 3 def test_create_with_int_and_dtype(): A = larray(3, shape=(5,), dtype=float) assert A.shape == (5,) assert A.evaluate(simplify=True) == 3 def test_create_with_float(): A = larray(3.0, shape=(5,)) assert A.shape == (5,) assert A.evaluate(simplify=True) == 3.0 def test_create_with_list(): A = larray([1, 2, 3], shape=(3,)) assert A.shape == (3,) assert_array_equal(A.evaluate(), np.array([1, 2, 3])) def test_create_with_array(): A = larray(np.array([1, 2, 3]), shape=(3,)) assert A.shape == (3,) assert_array_equal(A.evaluate(), np.array([1, 2, 3])) def test_create_with_array_and_dtype(): A = larray(np.array([1, 2, 3]), shape=(3,), dtype=int) assert A.shape == (3,) assert_array_equal(A.evaluate(), np.array([1, 2, 3])) def test_create_with_generator(): def plusone(): i = 0 while True: yield i i += 1 A = larray(plusone(), shape=(5, 11)) assert_array_equal(A.evaluate(), np.arange(55).reshape((5, 11))) def test_create_with_function1D(): A = larray(lambda i: 99 - i, shape=(3,)) assert_array_equal(A.evaluate(), np.array([99, 98, 97])) def test_create_with_function1D_and_dtype(): A = larray(lambda i: 99 - i, shape=(3,), dtype=float) assert_array_equal(A.evaluate(), np.array([99.0, 98.0, 97.0])) def test_create_with_function2D(): A = larray(lambda i, j: 3 * j - 2 * i, shape=(2, 3)) assert_array_equal(A.evaluate(), np.array([[0, 3, 6], [-2, 1, 4]])) def test_create_inconsistent(): assert_raises(ValueError, larray, [1, 2, 3], shape=4) def test_create_with_string(): assert_raises(TypeError, larray, "123", shape=3) def test_create_with_larray(): A = 3 + larray(lambda i: 99 - i, shape=(3,)) B = larray(A, shape=(3,), dtype=int) assert_array_equal(B.evaluate(), np.array([102, 101, 100])) ## For sparse matrices def test_create_with_sparse_array(): row = np.array([0, 2, 2, 0, 1, 2]) col = np.array([0, 0, 1, 2, 2, 2]) data = np.array([1, 2, 3, 4, 5, 6]) bsr = larray(bsr_matrix((data, (row, col)), shape=(3, 3))) # For bsr_matrix coo = larray(coo_matrix((data, (row, col)), shape=(3, 3))) # For coo_matrix csc = larray(csc_matrix((data, (row, col)), shape=(3, 3))) # For csc_matrix csr = larray(csr_matrix((data, (row, col)), shape=(3, 3))) # For csr_matrix data_dia = np.array([[1, 2, 3, 4]]).repeat(3, axis=0) # For dia_matrix offsets_dia = np.array([0, -1, 2]) # For dia_matrix dia = larray(dia_matrix((data_dia, offsets_dia), shape=(4, 4))) # For dia_matrix dok = larray(dok_matrix(((row, col)), shape=(3, 3))) # For dok_matrix lil = larray(lil_matrix(data, shape=(3, 3))) # For lil_matrix assert bsr.shape == (3, 3) assert coo.shape == (3, 3) assert csc.shape == (3, 3) assert csr.shape == (3, 3) assert dia.shape == (4, 4) assert dok.shape == (2, 6) assert lil.shape == (1, 6) def test_evaluate_with_sparse_array(): assert_array_equal(bsr.evaluate(), bsr_matrix((data, (row, col))).toarray()) # For bsr_matrix assert_array_equal(coo.evaluate(), coo_matrix((data, (row, col))).toarray()) # For coo_matrix assert_array_equal(csc.evaluate(), csc_matrix((data, (row, col))).toarray()) # For csc_matrix assert_array_equal(csr.evaluate(), csr_matrix((data, (row, col))).toarray()) # For csr_matrix assert_array_equal(dia.evaluate(), dia_matrix((data_dia, (row, col))).toarray()) # For dia_matrix assert_array_equal(dok.evaluate(), dok_matrix((data, (row, col))).toarray()) # For dok_matrix assert_array_equal(lil.evaluate(), lil_matrix((data, (row, col))).toarray()) # For lil_matrix def test_multiple_operations_with_sparse_array(): # For bsr_matrix bsr0 = bsr /100.0 bsr1 = 0.2 + bsr0 assert_array_almost_equal(bsr0.evaluate(), np.array([[0.01, 0., 0.04], [0., 0., 0.05], [0.02, 0.03, 0.06]])) assert_array_almost_equal(bsr0.evaluate(), np.array([[0.21, 0.2, 0.24], [0.2, 0.2, 0.25], [0.22, 0.23, 0.26]])) # For coo_matrix coo0 = coo /100.0 coo1 = 0.2 + coo0 assert_array_almost_equal(coo0.evaluate(), np.array([[0.01, 0., 0.04], [0., 0., 0.05], [0.02, 0.03, 0.06]])) assert_array_almost_equal(coo0.evaluate(), np.array([[0.21, 0.2, 0.24], [0.2, 0.2, 0.25], [0.22, 0.23, 0.26]])) # For csc_matrix csc0 = csc /100.0 csc1 = 0.2 + csc0 assert_array_almost_equal(csc0.evaluate(), np.array([[0.01, 0., 0.04], [0., 0., 0.05], [0.02, 0.03, 0.06]])) assert_array_almost_equal(csc0.evaluate(), np.array([[0.21, 0.2, 0.24], [0.2, 0.2, 0.25], [0.22, 0.23, 0.26]])) # For csr_matrix csr0 = csr /100.0 csr1 = 0.2 + csr0 assert_array_almost_equal(csc0.evaluate(), np.array([[0.01, 0., 0.04], [0., 0., 0.05], [0.02, 0.03, 0.06]])) assert_array_almost_equal(csc0.evaluate(), np.array([[0.21, 0.2, 0.24], [0.2, 0.2, 0.25], [0.22, 0.23, 0.26]])) # For dia_matrix dia0 = dia /100.0 dia1 = 0.2 + dia0 assert_array_almost_equal(dia0.evaluate(), np.array([[0.01, 0.02, 0.03, 0.04]])) assert_array_almost_equal(dia1.evaluate(), np.array([[0.21, 0.22, 0.23, 0.24]])) # For dok_matrix dok0 = dok /100.0 dok1 = 0.2 + dok0 assert_array_almost_equal(dok0.evaluate(), np.array([[0., 0.02, 0.02, 0., 0.01, 0.02], [0., 0., 0.01, 0.02, 0.02, 0.02]])) assert_array_almost_equal(dok1.evaluate(), np.array([[0.2, 0.22, 0.22, 0.2, 0.21, 0.22], [0.2, 0.2, 0.21, 0.22, 0.22, 0.22]])) # For lil_matrix lil0 = lil /100.0 lil1 = 0.2 + lil0 assert_array_almost_equal(lil0.evaluate(), np.array([[0.01, 0.02, 0.03, 0.04, 0.05, 0.06]])) assert_array_almost_equal(lil1.evaluate(), np.array([[0.21, 0.22, 0.23, 0.24, 0.25, 0.26]])) def test_getitem_from_2D_sparse_array(): assert_raises(IndexError, bsr.__getitem__, (3, 0)) assert_raises(IndexError, coo.__getitem__, (3, 0)) assert_raises(IndexError, csc.__getitem__, (3, 0)) assert_raises(IndexError, csr.__getitem__, (3, 0)) assert_raises(IndexError, dia.__getitem__, (3, 0)) assert_raises(IndexError, dok.__getitem__, (3, 0)) assert_raises(IndexError, lil.__getitem__, (3, 0)) # def test_columnwise_iteration_with_flat_array(): # m = larray(5, shape=(4,3)) # 4 rows, 3 columns # cols = [col for col in m.by_column()] # assert_equal(cols, [5, 5, 5]) # # def test_columnwise_iteration_with_structured_array(): # input = np.arange(12).reshape((4,3)) # m = larray(input, shape=(4,3)) # 4 rows, 3 columns # cols = [col for col in m.by_column()] # assert_array_equal(cols[0], input[:,0]) # assert_array_equal(cols[2], input[:,2]) # # def test_columnwise_iteration_with_function(): # input = lambda i,j: 2*i + j # m = larray(input, shape=(4,3)) # cols = [col for col in m.by_column()] # assert_array_equal(cols[0], np.array([0, 2, 4, 6])) # assert_array_equal(cols[1], np.array([1, 3, 5, 7])) # assert_array_equal(cols[2], np.array([2, 4, 6, 8])) # # def test_columnwise_iteration_with_flat_array_and_mask(): # m = larray(5, shape=(4,3)) # 4 rows, 3 columns # mask = np.array([True, False, True]) # cols = [col for col in m.by_column(mask=mask)] # assert_equal(cols, [5, 5]) # # def test_columnwise_iteration_with_structured_array_and_mask(): # input = np.arange(12).reshape((4,3)) # m = larray(input, shape=(4,3)) # 4 rows, 3 columns # mask = np.array([False, True, True]) # cols = [col for col in m.by_column(mask=mask)] # assert_array_equal(cols[0], input[:,1]) # assert_array_equal(cols[1], input[:,2]) def test_size_related_properties(): m1 = larray(1, shape=(9, 7)) m2 = larray(1, shape=(13,)) m3 = larray(1) assert_equal(m1.nrows, 9) assert_equal(m1.ncols, 7) assert_equal(m1.size, 63) assert_equal(m2.nrows, 13) assert_equal(m2.ncols, 1) assert_equal(m2.size, 13) assert_raises(ValueError, lambda: m3.nrows) assert_raises(ValueError, lambda: m3.ncols) assert_raises(ValueError, lambda: m3.size) def test_evaluate_with_flat_array(): m = larray(5, shape=(4, 3)) assert_array_equal(m.evaluate(), 5 * np.ones((4, 3))) def test_evaluate_with_structured_array(): input = np.arange(12).reshape((4, 3)) m = larray(input, shape=(4, 3)) assert_array_equal(m.evaluate(), input) def test_evaluate_with_functional_array(): input = lambda i, j: 2 * i + j m = larray(input, shape=(4, 3)) assert_array_equal(m.evaluate(), np.array([[0, 1, 2], [2, 3, 4], [4, 5, 6], [6, 7, 8]])) def test_evaluate_with_vectorized_iterable(): input = MockRNG(0, 1) m = larray(input, shape=(7, 3)) assert_array_equal(m.evaluate(), np.arange(21).reshape((7, 3))) def test_evaluate_twice_with_vectorized_iterable(): input = MockRNG(0, 1) m1 = larray(input, shape=(7, 3)) + 3 m2 = larray(input, shape=(7, 3)) + 17 assert_array_equal(m1.evaluate(), np.arange(3, 24).reshape((7, 3))) assert_array_equal(m2.evaluate(), np.arange(38, 59).reshape((7, 3))) def test_evaluate_structured_array_size_1_simplify(): m = larray([5.0], shape=(1,)) assert_equal(m.evaluate(simplify=True), 5.0) n = larray([2.0], shape=(1,)) assert_equal((m/n).evaluate(simplify=True), 2.5) def test_iadd_with_flat_array(): m = larray(5, shape=(4, 3)) m += 2 assert_array_equal(m.evaluate(), 7 * np.ones((4, 3))) assert_equal(m.base_value, 5) assert_equal(m.evaluate(simplify=True), 7) def test_add_with_flat_array(): m0 = larray(5, shape=(4, 3)) m1 = m0 + 2 assert_equal(m1.evaluate(simplify=True), 7) assert_equal(m0.evaluate(simplify=True), 5) def test_lt_with_flat_array(): m0 = larray(5, shape=(4, 3)) m1 = m0 < 10 assert_equal(m1.evaluate(simplify=True), True) assert_equal(m0.evaluate(simplify=True), 5) def test_lt_with_structured_array(): input = np.arange(12).reshape((4, 3)) m0 = larray(input, shape=(4, 3)) m1 = m0 < 5 assert_array_equal(m1.evaluate(simplify=True), input < 5) def test_structured_array_lt_array(): input = np.arange(12).reshape((4, 3)) m0 = larray(input, shape=(4, 3)) comparison = 5 * np.ones((4, 3)) m1 = m0 < comparison assert_array_equal(m1.evaluate(simplify=True), input < comparison) def test_rsub_with_structured_array(): m = larray(np.arange(12).reshape((4, 3))) assert_array_equal((11 - m).evaluate(), np.arange(11, -1, -1).reshape((4, 3))) def test_inplace_mul_with_structured_array(): m = larray((3 * x for x in range(4)), shape=(4,)) m *= 7 assert_array_equal(m.evaluate(), np.arange(0, 84, 21)) def test_abs_with_structured_array(): m = larray(lambda i, j: i - j, shape=(3, 4)) assert_array_equal(abs(m).evaluate(), np.array([[0, 1, 2, 3], [1, 0, 1, 2], [2, 1, 0, 1]])) def test_multiple_operations_with_structured_array(): input = np.arange(12).reshape((4, 3)) m0 = larray(input, shape=(4, 3)) m1 = (m0 + 2) < 5 m2 = (m0 < 5) + 2 assert_array_equal(m1.evaluate(simplify=True), (input + 2) < 5) assert_array_equal(m2.evaluate(simplify=True), (input < 5) + 2) assert_array_equal(m0.evaluate(simplify=True), input) def test_multiple_operations_with_functional_array(): m = larray(lambda i: i, shape=(5,)) m0 = m / 100.0 m1 = 0.2 + m0 assert_array_almost_equal(m0.evaluate(), np.array([0.0, 0.01, 0.02, 0.03, 0.04]), decimal=12) assert_array_almost_equal(m1.evaluate(), np.array([0.20, 0.21, 0.22, 0.23, 0.24]), decimal=12) assert_equal(m1[0], 0.2) def test_operations_combining_constant_and_structured_arrays(): m0 = larray(10, shape=(5,)) m1 = larray(np.arange(5)) m2 = m0 + m1 assert_array_almost_equal(m2.evaluate(), np.arange(10, 15)) def test_apply_function_to_constant_array(): f = lambda m: 2 * m + 3 m0 = larray(5, shape=(4, 3)) m1 = f(m0) assert isinstance(m1, larray) assert_equal(m1.evaluate(simplify=True), 13) # the following tests the internals, not the behaviour # it is just to check I understand what's going on assert_equal(m1.operations, [(operator.mul, 2), (operator.add, 3)]) def test_apply_function_to_structured_array(): f = lambda m: 2 * m + 3 input = np.arange(12).reshape((4, 3)) m0 = larray(input, shape=(4, 3)) m1 = f(m0) assert isinstance(m1, larray) assert_array_equal(m1.evaluate(simplify=True), input * 2 + 3) def test_apply_function_to_functional_array(): input = lambda i, j: 2 * i + j m0 = larray(input, shape=(4, 3)) f = lambda m: 2 * m + 3 m1 = f(m0) assert_array_equal(m1.evaluate(), np.array([[3, 5, 7], [7, 9, 11], [11, 13, 15], [15, 17, 19]])) def test_add_two_constant_arrays(): m0 = larray(5, shape=(4, 3)) m1 = larray(7, shape=(4, 3)) m2 = m0 + m1 assert_equal(m2.evaluate(simplify=True), 12) # the following tests the internals, not the behaviour # it is just to check I understand what's going on assert_equal(m2.base_value, m0.base_value) assert_equal(m2.operations, [(operator.add, m1)]) def test_add_incommensurate_arrays(): m0 = larray(5, shape=(4, 3)) m1 = larray(7, shape=(5, 3)) assert_raises(ValueError, m0.__add__, m1) def test_getitem_from_2D_constant_array(): m = larray(3, shape=(4, 3)) assert m[0, 0] == m[3, 2] == m[-1, 2] == m[-4, 2] == m[2, -3] == 3 assert_raises(IndexError, m.__getitem__, (4, 0)) assert_raises(IndexError, m.__getitem__, (2, -4)) def test_getitem_from_1D_constant_array(): m = larray(3, shape=(43,)) assert m[0] == m[42] == 3 def test_getitem__with_slice_from_constant_array(): m = larray(3, shape=(4, 3)) assert_array_equal(m[:3, 0], np.array([3, 3, 3])) def test_getitem__with_thinslice_from_constant_array(): m = larray(3, shape=(4, 3)) assert_equal(m[2:3, 0:1], 3) def test_getitem__with_mask_from_constant_array(): m = larray(3, shape=(4, 3)) assert_array_equal(m[1, (0, 2)], np.array([3, 3])) def test_getitem_with_numpy_integers_from_2D_constant_array(): if not hasattr(np, "int64"): raise SkipTest("test requires a 64-bit system") m = larray(3, shape=(4, 3)) assert m[np.int64(0), np.int32(0)] == 3 def test_getslice_from_constant_array(): m = larray(3, shape=(4, 3)) assert_array_equal(m[:2], np.array([[3, 3, 3], [3, 3, 3]])) def test_getslice_past_bounds_from_constant_array(): m = larray(3, shape=(5,)) assert_array_equal(m[2:10], np.array([3, 3, 3])) def test_getitem_from_structured_array(): m = larray(3 * np.ones((4, 3)), shape=(4, 3)) assert m[0, 0] == m[3, 2] == m[-1, 2] == m[-4, 2] == m[2, -3] == 3 assert_raises(IndexError, m.__getitem__, (4, 0)) assert_raises(IndexError, m.__getitem__, (2, -4)) def test_getitem_from_2D_functional_array(): m = larray(lambda i, j: 2 * i + j, shape=(6, 5)) assert_equal(m[5, 4], 14) def test_getitem_from_1D_functional_array(): m = larray(lambda i: i ** 3, shape=(6,)) assert_equal(m[5], 125) def test_getitem_from_3D_functional_array(): m = larray(lambda i, j, k: i + j + k, shape=(2, 3, 4)) assert_raises(NotImplementedError, m.__getitem__, (0, 1, 2)) def test_getitem_from_vectorized_iterable(): input = MockRNG(0, 1) m = larray(input, shape=(7,)) m3 = m[3] assert isinstance(m3, (int, np.integer)) assert_equal(m3, 0) assert_equal(m[0], 1) def test_getitem_with_slice_from_2D_functional_array(): m = larray(lambda i, j: 2 * i + j, shape=(6, 5)) assert_array_equal(m[2:5, 3:], np.array([[7, 8], [9, 10], [11, 12]])) def test_getitem_with_slice_from_2D_functional_array_2(): def test_function(i, j): return i * i + 2 * i * j + 3 m = larray(test_function, shape=(3, 15)) assert_array_equal(m[:, 3:14:3], np.fromfunction(test_function, shape=(3, 15))[:, 3:14:3]) def test_getitem_with_mask_from_2D_functional_array(): a = np.arange(30).reshape((6, 5)) m = larray(lambda i, j: 5 * i + j, shape=(6, 5)) assert_array_equal(a[[2, 3], [3, 4]], np.array([13, 19])) assert_array_equal(m[[2, 3], [3, 4]], np.array([13, 19])) def test_getitem_with_mask_from_1D_functional_array(): m = larray(lambda i: np.sqrt(i), shape=(10,)) assert_array_equal(m[[0, 1, 4, 9]], np.array([0, 1, 2, 3])) def test_getitem_with_boolean_mask_from_1D_functional_array(): m = larray(lambda i: np.sqrt(i), shape=(10,)) assert_array_equal(m[np.array([1, 1, 0, 0, 1, 0, 0, 0, 0, 1], dtype=bool)], np.array([0, 1, 2, 3])) def test_getslice_from_2D_functional_array(): m = larray(lambda i, j: 2 * i + j, shape=(6, 5)) assert_array_equal(m[1:3], np.array([[2, 3, 4, 5, 6], [4, 5, 6, 7, 8]])) def test_getitem_from_iterator_array(): m = larray(iter([1, 2, 3]), shape=(3,)) assert_raises(NotImplementedError, m.__getitem__, 2) def test_getitem_from_array_with_operations(): a1 = np.array([[1, 3, 5], [7, 9, 11]]) m1 = larray(a1) f = lambda i, j: np.sqrt(i * i + j * j) a2 = np.fromfunction(f, shape=(2, 3)) m2 = larray(f, shape=(2, 3)) a3 = 3 * a1 + a2 m3 = 3 * m1 + m2 assert_array_equal(a3[:, (0, 2)], m3[:, (0, 2)]) def test_evaluate_with_invalid_base_value(): m = larray(range(5)) m.base_value = "foo" assert_raises(ValueError, m.evaluate) def test_partially_evaluate_with_invalid_base_value(): m = larray(range(5)) m.base_value = "foo" assert_raises(ValueError, m._partially_evaluate, 3) def test_check_bounds_with_invalid_address(): m = larray([[1, 3, 5], [7, 9, 11]]) assert_raises(TypeError, m.check_bounds, (object(), 1)) def test_check_bounds_with_invalid_address2(): m = larray([[1, 3, 5], [7, 9, 11]]) assert_raises(ValueError, m.check_bounds, ([], 1)) def test_partially_evaluate_constant_array_with_one_element(): m = larray(3, shape=(1,)) a = 3 * np.ones((1,)) m1 = larray(3, shape=(1, 1)) a1 = 3 * np.ones((1, 1)) m2 = larray(3, shape=(1, 1, 1)) a2 = 3 * np.ones((1, 1, 1)) assert_equal(a[0], m[0]) assert_equal(a.shape, m.shape) assert_equal(a[:].shape, m[:].shape) assert_equal(a, m.evaluate()) assert_equal(a1.shape, m1.shape) assert_equal(a1[0,:].shape, m1[0,:].shape) assert_equal(a1[:].shape, m1[:].shape) assert_equal(a1, m1.evaluate()) assert_equal(a2.shape, m2.shape) assert_equal(a2[:, 0,:].shape, m2[:, 0,:].shape) assert_equal(a2[:].shape, m2[:].shape) assert_equal(a2, m2.evaluate()) def test_partially_evaluate_constant_array_with_boolean_index(): m = larray(3, shape=(4, 5)) a = 3 * np.ones((4, 5)) addr_bool = np.array([True, True, False, False, True]) addr_int = np.array([0, 1, 4]) assert_equal(a[::2, addr_bool].shape, a[::2, addr_int].shape) assert_equal(a[::2, addr_int].shape, m[::2, addr_int].shape) assert_equal(a[::2, addr_bool].shape, m[::2, addr_bool].shape) def test_partially_evaluate_constant_array_with_all_boolean_indices_false(): m = larray(3, shape=(3,)) a = 3 * np.ones((3,)) addr_bool = np.array([False, False, False]) assert_equal(a[addr_bool].shape, m[addr_bool].shape) def test_partially_evaluate_constant_array_with_only_one_boolean_indice_true(): m = larray(3, shape=(3,)) a = 3 * np.ones((3,)) addr_bool = np.array([False, True, False]) assert_equal(a[addr_bool].shape, m[addr_bool].shape) assert_equal(m[addr_bool][0], a[0]) def test_partially_evaluate_constant_array_with_boolean_indice_as_random_valid_ndarray(): m = larray(3, shape=(3,)) a = 3 * np.ones((3,)) addr_bool = np.random.rand(3) > 0.5 while not addr_bool.any(): # random array, but not [False, False, False] addr_bool = np.random.rand(3) > 0.5 assert_equal(a[addr_bool].shape, m[addr_bool].shape) assert_equal(m[addr_bool][0], a[addr_bool][0]) def test_partially_evaluate_constant_array_size_one_with_boolean_index_true(): m = larray(3, shape=(1,)) a = np.array([3]) addr_bool = np.array([True]) m1 = larray(3, shape=(1, 1)) a1 = 3 * np.ones((1, 1)) addr_bool1 = np.array([[True]], ndmin=2) assert_equal(m[addr_bool][0], a[0]) assert_equal(m[addr_bool], a[addr_bool]) assert_equal(m[addr_bool].shape, a[addr_bool].shape) assert_equal(m1[addr_bool1][0], a1[addr_bool1][0]) assert_equal(m1[addr_bool1].shape, a1[addr_bool1].shape) def test_partially_evaluate_constant_array_size_two_with_boolean_index_true(): m2 = larray(3, shape=(1, 2)) a2 = 3 * np.ones((1, 2)) addr_bool2 = np.ones((1, 2), dtype=bool) assert_equal(m2[addr_bool2][0], a2[addr_bool2][0]) assert_equal(m2[addr_bool2].shape, a2[addr_bool2].shape) def test_partially_evaluate_constant_array_size_one_with_boolean_index_false(): m = larray(3, shape=(1,)) m1 = larray(3, shape=(1, 1)) a = np.array([3]) a1 = np.array([[3]], ndmin=2) addr_bool = np.array([False]) addr_bool1 = np.array([[False]], ndmin=2) addr_bool2 = np.array([False]) assert_equal(m[addr_bool].shape, a[addr_bool].shape) assert_equal(m1[addr_bool1].shape, a1[addr_bool1].shape) def test_partially_evaluate_constant_array_size_with_empty_boolean_index(): m = larray(3, shape=(1,)) a = np.array([3]) addr_bool = np.array([], dtype='bool') assert_equal(m[addr_bool].shape, a[addr_bool].shape) assert_equal(m[addr_bool].shape, (0,)) def test_partially_evaluate_functional_array_with_boolean_index(): m = larray(lambda i, j: 5 * i + j, shape=(4, 5)) a = np.arange(20.0).reshape((4, 5)) addr_bool = np.array([True, True, False, False, True]) addr_int = np.array([0, 1, 4]) assert_equal(a[::2, addr_bool].shape, a[::2, addr_int].shape) assert_equal(a[::2, addr_int].shape, m[::2, addr_int].shape) assert_equal(a[::2, addr_bool].shape, m[::2, addr_bool].shape) def test_getslice_with_vectorized_iterable(): input = MockRNG(0, 1) m = larray(input, shape=(7, 3)) assert_array_equal(m[::2, (0, 2)], np.arange(8).reshape((4, 2))) def test_equality_with_lazyarray(): m1 = larray(42.0, shape=(4, 5)) / 23.0 + 2.0 m2 = larray(42.0, shape=(4, 5)) / 23.0 + 2.0 assert_equal(m1, m2) def test_equality_with_number(): m1 = larray(42.0, shape=(4, 5)) m2 = larray([42, 42, 42]) m3 = larray([42, 42, 43]) m4 = larray(42.0, shape=(4, 5)) + 2 assert_equal(m1, 42.0) assert_equal(m2, 42) assert_not_equal(m3, 42) assert_raises(Exception, m4.__eq__, 44.0) def test_equality_with_array(): m1 = larray(42.0, shape=(4, 5)) target = 42.0 * np.ones((4, 5)) assert_raises(TypeError, m1.__eq__, target) def test_deepcopy(): m1 = 3 * larray(lambda i, j: 5 * i + j, shape=(4, 5)) + 2 m2 = deepcopy(m1) m1.shape = (3, 4) m3 = deepcopy(m1) assert_equal(m1.shape, m3.shape, (3, 4)) assert_equal(m2.shape, (4, 5)) assert_array_equal(m1.evaluate(), m3.evaluate()) def test_deepcopy_with_ufunc(): m1 = sqrt(larray([x ** 2 for x in range(5)])) m2 = deepcopy(m1) m1.base_value[0] = 49 assert_array_equal(m1.evaluate(), np.array([7, 1, 2, 3, 4])) assert_array_equal(m2.evaluate(), np.array([0, 1, 2, 3, 4])) def test_set_shape(): m = larray(42) + larray(lambda i: 3 * i) assert_equal(m.shape, None) m.shape = (5,) assert_array_equal(m.evaluate(), np.array([42, 45, 48, 51, 54])) def test_call(): A = larray(np.array([1, 2, 3]), shape=(3,)) - 1 B = 0.5 * larray(lambda i: 2 * i, shape=(3,)) C = B(A) assert_array_equal(C.evaluate(), np.array([0, 1, 2])) assert_array_equal(A.evaluate(), np.array([0, 1, 2])) # A should be unchanged def test_call2(): positions = np.array( [[0., 2., 4., 6., 8.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]]) def position_generator(i): return positions.T[i] def distances(A, B): d = A - B d **= 2 d = np.sum(d, axis=-1) np.sqrt(d, d) return d def distance_generator(f, g): def distance_map(i, j): return distances(f(i), g(j)) return distance_map distance_map = larray(distance_generator(position_generator, position_generator), shape=(4, 5)) f_delay = 1000 * larray(lambda d: 0.1 * (1 + d), shape=(4, 5)) assert_array_almost_equal( f_delay(distance_map).evaluate(), np.array([[100, 300, 500, 700, 900], [300, 100, 300, 500, 700], [500, 300, 100, 300, 500], [700, 500, 300, 100, 300]], dtype=float), decimal=12) # repeat, should be idempotent assert_array_almost_equal( f_delay(distance_map).evaluate(), np.array([[100, 300, 500, 700, 900], [300, 100, 300, 500, 700], [500, 300, 100, 300, 500], [700, 500, 300, 100, 300]], dtype=float), decimal=12) def test__issue4(): # In order to avoid the errors associated with version changes of numpy, mask1 and mask2 no longer contain boolean values ​​but integer values a = np.arange(12).reshape((4, 3)) b = larray(np.arange(12).reshape((4, 3))) mask1 = (slice(None), int(True)) mask2 = (slice(None), np.array([int(True)])) assert_equal(b[mask1].shape, partial_shape(mask1, b.shape), a[mask1].shape) assert_equal(b[mask2].shape, partial_shape(mask2, b.shape), a[mask2].shape) def test__issue3(): a = np.arange(12).reshape((4, 3)) b = larray(a) c = larray(lambda i, j: 3*i + j, shape=(4, 3)) assert_array_equal(a[(1, 3), :][:, (0, 2)], b[(1, 3), :][:, (0, 2)]) assert_array_equal(b[(1, 3), :][:, (0, 2)], c[(1, 3), :][:, (0, 2)]) assert_array_equal(a[(1, 3), (0, 2)], b[(1, 3), (0, 2)]) assert_array_equal(b[(1, 3), (0, 2)], c[(1, 3), (0, 2)]) def test_partial_shape(): a = np.arange(12).reshape((4, 3)) test_cases = [ (slice(None), (4, 3)), ((slice(None), slice(None)), (4, 3)), (slice(1, None, 2), (2, 3)), (1, (3,)), ((1, slice(None)), (3,)), ([0, 2, 3], (3, 3)), (np.array([0, 2, 3]), (3, 3)), ((np.array([0, 2, 3]), slice(None)), (3, 3)), (np.array([True, False, True, True]), (3, 3)), #(np.array([True, False]), (1, 3)), # not valid with NumPy 1.13 (np.array([[True, False, False], [False, False, False], [True, True, False], [False, True, False]]), (4,)), #(np.array([[True, False, False], [False, False, False], [True, True, False]]), (3,)), # not valid with NumPy 1.13 ((3, 1), tuple()), ((slice(None), 1), (4,)), ((slice(None), slice(1, None, 3)), (4, 1)), ((np.array([0, 3]), 2), (2,)), ((np.array([0, 3]), np.array([1, 2])), (2,)), ((slice(None), np.array([2])), (4, 1)), (((1, 3), (0, 2)), (2,)), (np.array([], bool), (0, 3)), ] for mask, expected_shape in test_cases: assert_equal(partial_shape(mask, a.shape), a[mask].shape) assert_equal(partial_shape(mask, a.shape), expected_shape) b = np.arange(5) test_cases = [ (np.arange(5), (5,)) ] for mask, expected_shape in test_cases: assert_equal(partial_shape(mask, b.shape), b[mask].shape) assert_equal(partial_shape(mask, b.shape), expected_shape) def test_is_homogeneous(): m0 = larray(10, shape=(5,)) m1 = larray(np.arange(1, 6)) m2 = m0 + m1 m3 = 9 + m0 / m1 assert m0.is_homogeneous assert not m1.is_homogeneous assert not m2.is_homogeneous assert not m3.is_homogeneous lazyarray-0.5.0/test/test_ufunc.py000066400000000000000000000032761414246606100172560ustar00rootroot00000000000000""" Unit tests for ``larray``-compatible ufuncs Copyright Andrew P. Davison, 2012-2017 """ from lazyarray import larray, sqrt, cos, power, fmod import numpy as np from numpy.testing import assert_array_equal, assert_array_almost_equal from nose.tools import assert_raises def test_sqrt_from_array(): A = larray(np.array([1, 4, 9, 16, 25])) assert_array_equal(sqrt(A).evaluate(), np.arange(1, 6)) def test_sqrt_from_iterator(): A = larray(iter([1, 4, 9, 16, 25]), shape=(5,)) assert_array_equal(sqrt(A).evaluate(), np.arange(1, 6)) def test_sqrt_from_func(): A = larray(lambda x: (x + 1) ** 2, shape=(5,)) assert_array_equal(sqrt(A).evaluate(), np.arange(1, 6)) def test_sqrt_normal_array(): A = np.array([1, 4, 9, 16, 25]) assert_array_equal(sqrt(A), np.arange(1, 6)) def test_cos_from_generator(): def clock(): for x in np.arange(0, 2 * np.pi, np.pi / 2): yield x A = larray(clock(), shape=(2, 2)) assert_array_almost_equal(cos(A).evaluate(), np.array([[1.0, 0.0], [-1.0, 0.0]]), decimal=15) def test_power_from_array(): A = larray(np.array([1, 4, 9, 16, 25])) assert_array_equal(power(A, 0.5).evaluate(), np.arange(1, 6)) def test_power_with_plain_array(): A = np.array([1, 4, 9, 16, 25]) assert_array_equal(power(A, 0.5), np.arange(1, 6)) def test_fmod_with_array_as_2nd_arg(): A = larray(np.array([1, 4, 9, 16, 25])) B = larray(np.array([1, 4, 9, 16, 25])) assert_raises(TypeError, fmod, A, B)