././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1639749314.562119 tinyarray-1.2.4/0000775000175000017500000000000000000000000011665 5ustar00cwgcwg././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1495457967.0 tinyarray-1.2.4/LICENSE.rst0000664000175000017500000000264600000000000013511 0ustar00cwgcwg================= Tinyarray License ================= Copyright 2012-2015 Christoph Groth (CEA) and others. All rights reserved. (CEA = Commissariat à l'énergie atomique et aux énergies alternatives) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1495457967.0 tinyarray-1.2.4/MANIFEST.in0000664000175000017500000000044200000000000013423 0ustar00cwgcwg# This file specifies the files to be included in the source distribution # in addition to the default ones. include MANIFEST.in include LICENSE.rst include README_WINDOWS.txt include test_tinyarray.py include benchmark.py include version recursive-include src *.hh exclude src/version.hh ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1639749314.558119 tinyarray-1.2.4/PKG-INFO0000664000175000017500000000430400000000000012763 0ustar00cwgcwgMetadata-Version: 1.1 Name: tinyarray Version: 1.2.4 Summary: Arrays of numbers for Python, optimized for small sizes Home-page: https://gitlab.kwant-project.org/kwant/tinyarray Author: Christoph Groth (CEA) and others Author-email: christoph.groth@cea.fr License: Simplified BSD license Download-URL: http://downloads.kwant-project.org/tinyarray/ Description: Tinyarrays are similar to NumPy arrays, but optimized for small sizes. Common operations on very small arrays are to 3-7 times faster than with NumPy (with NumPy 1.6 it used to be up to 35 times), and 3 times less memory is used to store them. Tinyarrays are useful if you need many small arrays of numbers, and cannot combine them into a few large ones. (The resulting code is still much slower than C, but it may now be fast enough.) Unlike Python's built-in tuples, Tinyarrays support mathematical operations like element-wise addition and matrix multiplication. Unlike Numpy arrays, Tinyarrays can be used as dictionary keys because they are hashable and immutable. What is more, tinyarrays are equivalent to tuples with regard to hashing and comparisons: a dictionary or set with tinyarray keys may by transparently indexed by tuples. The module's interface is a subset of that of NumPy and thus should be familiar to many. Whenever an operation is missing from Tinyarray, NumPy functions can be used directly with Tinyarrays. Platform: Unix Platform: Linux Platform: Mac OS-X Platform: Windows Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Science/Research Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: C++ Classifier: Topic :: Software Development Classifier: Topic :: Scientific/Engineering Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1600436894.0 tinyarray-1.2.4/README.rst0000664000175000017500000001234200000000000013356 0ustar00cwgcwgTinyarray ========= Tinyarrays are similar to NumPy arrays, but optimized for small sizes. Common operations on very small arrays are to 3-7 times faster than with NumPy (with NumPy 1.6 it used to be up to 35 times), and 3 times less memory is used to store them. Tinyarrays are useful if you need many small arrays of numbers, and cannot combine them into a few large ones. (The resulting code is still much slower than C, but it may now be fast enough.) Unlike Python's built-in tuples, Tinyarrays support mathematical operations like element-wise addition and matrix multiplication. Unlike Numpy arrays, Tinyarrays can be used as dictionary keys because they are hashable and immutable. What is more, tinyarrays are equivalent to tuples with regard to hashing and comparisons: a dictionary or set with tinyarray keys may by transparently indexed by tuples. The module's interface is a subset of that of NumPy and thus should be familiar to many. Whenever an operation is missing from Tinyarray, NumPy functions can be used directly with Tinyarrays. Tinyarray is licensed under the "simplified BSD License". See ``_. Website: https://gitlab.kwant-project.org/kwant/tinyarray Please report bugs here: https://gitlab.kwant-project.org/kwant/tinyarray/issues Source code ----------- Source tarballs are available at http://downloads.kwant-project.org/tinyarray/ Clone the Git repository with :: git clone https://gitlab.kwant-project.org/kwant/tinyarray.git Installation ------------ Tinyarray can be built from source with the usual :: pip install tinyarray One can also download the source tarball (or clone it from git) and use :: python setup.py install Prepared packages exist for * Windows Use `Christoph Gohlke's installer `_. * Ubuntu and derivatives :: sudo apt-add-repository ppa:kwant-project/ppa sudo apt-get update sudo apt-get install python-tinyarray * Debian and derivatives 1. Add the following lines to ``/etc/apt/sources.list``:: deb http://downloads.kwant-project.org/debian/ stable main deb-src http://downloads.kwant-project.org/debian/ stable main 2. (Optional) Add the OpenPGP key used to sign the repositories by executing:: sudo apt-key adv --keyserver pgp.mit.edu --recv-key C3F147F5980F3535 3. Update the package data, and install:: sudo apt-get update sudo apt-get install python-tinyarray * Mac OS X Follow the `instructions for installing "Kwant" `_ but install ``py27-tinyarray`` instead of ``py27-kwant`` etc. Build configuration ------------------- If necessary, the compilation and linking of tinyarray can be configured with a build configuration file. By default, this file is ``build.conf`` in the root directory of the tinyarray distribution. A different path may be provided using the ``--configfile=PATH`` option. The configuration file consists of sections, one for each extension module (currently there is only one: ``tinyarray``), led by a ``[section name]`` header and followed by ``key = value`` lines. Possible keys are the keyword arguments for ``distutils.core.Extension`` (For a complete list, see its `documentation `_). The corresponding values are whitespace-separated lists of strings. Example ``build.conf`` for compiling Tinyarray with C assertions:: [tinyarray] undef_macros = NDEBUG Usage example ------------- The following example shows that in simple cases Tinyarray works just as NumPy. :: from math import sin, cos, sqrt import tinyarray as ta # Make a vector. v = ta.array([1.0, 2.0, 3.0]) # Make a rotation matrix. alpha = 0.77 c, s = cos(alpha), sin(alpha) rot_z = ta.array([[c, -s, 0], [s, c, 0], [0, 0, 1]]) # Rotate the vector, normalize, and print it. v = ta.dot(rot_z, v) v /= sqrt(ta.dot(v, v)) print v Documentation ------------- The module's interface is a basic subset of NumPy and hence should be familiar to many Python programmers. All functions are simplified versions of their NumPy counterparts. The module's docstring serves as main documentation. To see it, run in Python:: import tinyarray as ta help(ta) Or in the system shell:: pydoc tinyarray Contributing ------------ Contributions to tinyarray are most welcome. Patches may be sent by email, or a merge request may be opened on the Project's website. Please add tests for any new functionality and make sure that all existing tests still run. To run the tests, execute:: python setup.py test It is a good idea to enable C assertions as shown above under `Build configuration`_. Authors ------- The principal developer of Tinyarray is Christoph Groth (CEA Grenoble). His contributions are part of his work at `CEA `_, the French Commissariat à l'énergie atomique et aux énergies alternatives. The author can be reached at christoph.groth@cea.fr. Other people that have contributed to Tinyarray include * Michael Wimmer (Leiden University, TU Delft) * Joseph Weston (CEA Grenoble, TU Delft) * Jörg Behrmann (FU Berlin) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1495457967.0 tinyarray-1.2.4/README_WINDOWS.txt0000664000175000017500000000040300000000000014572 0ustar00cwgcwgA note for users of Microsoft Windows ------------------------------------- To read the text files in this directory (README.rst, LICENSE.rst, etc.), right-click, choose "Open", and open with "WordPad". ("Notepad" will not display the files properly.) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1495457967.0 tinyarray-1.2.4/benchmark.py0000664000175000017500000000374200000000000014177 0ustar00cwgcwgfrom __future__ import print_function import tinyarray import numpy from time import time ################################################################ # Mock a numpy like "module" using Python tuples. def tuple_array(seq): return tuple(seq) def tuple_zeros(shape, dtype): return (dtype(0),) * shape def tuple_dot(a, b): return a[0] * b[0] + a[1] * b[1] class Empty: pass tuples = Empty() tuples.__name__ = 'tuples' tuples.array = tuple_array tuples.zeros = tuple_zeros tuples.dot = tuple_dot ################################################################ def zeros(module, dtype, n=100000): zeros = module.zeros return list(zeros(2, dtype) for i in range(n)) def make_from_list(module, dtype, n=100000): array = module.array l = [dtype(e) for e in range(3)] return list(array(l) for i in range(n)) def dot(module, dtype, n=1000000): dot = module.dot a = module.zeros(2, dtype) b = module.zeros(2, dtype) for i in range(n): c = dot(a, b) def dot_tuple(module, dtype, n=100000): dot = module.dot a = module.zeros(2, dtype) b = (dtype(0),) * 2 for i in range(n): c = dot(a, b) def compare(function, modules): print('{}:'.format(function.__name__)) for module in modules: # Execute the function once to make the following timings more # accurate. function(module, int) print(" {:15} ".format(module.__name__), end='') for dtype in (int, float, complex): t = time() try: function(module, dtype) except: print(" failed ", end='') else: print(' {:.4f} s '.format(time() - t), end='') print() def main(): print(' int float complex') modules = [tuples, tinyarray, numpy] compare(zeros, modules) compare(make_from_list, modules) compare(dot, modules) compare(dot_tuple, modules) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1639749314.562119 tinyarray-1.2.4/setup.cfg0000664000175000017500000000007300000000000013506 0ustar00cwgcwg[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1600258536.0 tinyarray-1.2.4/setup.py0000775000175000017500000002427700000000000013416 0ustar00cwgcwg#!/usr/bin/env python # Copyright 2012-2016 Tinyarray authors. # # This file is part of Tinyarray. It is subject to the license terms in the # file LICENSE.rst found in the top-level directory of this distribution and # at https://gitlab.kwant-project.org/kwant/tinyarray/blob/master/LICENSE.rst. # A list of Tinyarray authors can be found in the README.rst file at the # top-level directory of this distribution and at # https://gitlab.kwant-project.org/kwant/tinyarray. from __future__ import print_function import subprocess import os import sys import collections from setuptools import setup, Extension, Command from sysconfig import get_platform from distutils.errors import DistutilsError, DistutilsModuleError from setuptools.command.build_ext import build_ext as build_ext_orig from setuptools.command.sdist import sdist as sdist_orig from setuptools.command.test import test as test_orig try: import configparser except ImportError: import ConfigParser as configparser try: from os.path import samefile except ImportError: # This code path will be taken on Windows for Python < 3.2. # TODO: remove this once we require Python 3.2. def _getfinalpathname(f): return os.path.normcase(os.path.abspath(f)) # This simple mockup should do in practice. def samefile(f1, f2): return _getfinalpathname(f1) == _getfinalpathname(f2) SAVED_VERSION_FILE = 'version' distr_root = os.path.dirname(os.path.abspath(__file__)) def configure_extensions(exts, aliases=(), build_summary=None): """Modify extension configuration according to the configuration file `exts` must be a dict of (name, kwargs) tuples that can be used like this: `Extension(name, **kwargs). This function modifies the kwargs according to the configuration file. This function modifies `sys.argv`. """ global config_file, config_file_present #### Determine the name of the configuration file. config_file_option = '--configfile' # Handle command line option for i, opt in enumerate(sys.argv): if not opt.startswith(config_file_option): continue l, _, config_file = opt.partition('=') if l != config_file_option or not config_file: print('error: Expecting {}=PATH'.format(config_file_option), file=sys.stderr) sys.exit(1) sys.argv.pop(i) break else: config_file = 'build.conf' #### Read build configuration file. configs = configparser.ConfigParser() try: with open(config_file) as f: configs.read_file(f) except IOError: config_file_present = False else: config_file_present = True #### Handle section aliases. for short, long in aliases: if short in configs: if long in configs: print('Error: both {} and {} sections present in {}.'.format( short, long, config_file)) sys.exit(1) configs[long] = configs[short] del configs[short] #### Apply config from file. Use [DEFAULT] section for missing sections. defaultconfig = configs.defaults() for name, kwargs in exts.items(): try: items = configs.items(name) except configparser.NoSectionError: items = defaultconfig.items() else: configs.remove_section(name) for key, value in items: # Most, but not all, keys are lists of strings if key == 'language': pass elif key == 'optional': value = bool(int(value)) else: value = value.split() if key == 'define_macros': value = [tuple(entry.split('=', 1)) for entry in value] value = [(entry[0], None) if len(entry) == 1 else entry for entry in value] if key in kwargs: msg = 'Caution: user config in file {} shadows {}.{}.' if build_summary is not None: build_summary.append(msg.format(config_file, name, key)) kwargs[key] = value kwargs.setdefault('depends', []).append(config_file) unknown_sections = configs.sections() if unknown_sections: print('Error: Unknown sections in file {}: {}'.format( config_file, ', '.join(unknown_sections))) sys.exit(1) return exts def get_version_from_git(): try: p = subprocess.Popen(['git', 'rev-parse', '--show-toplevel'], cwd=distr_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError: return if p.wait() != 0: return if not samefile(p.communicate()[0].decode().rstrip('\n'), distr_root): # The top-level directory of the current Git repository is not the same # as the root directory of the source distribution: do not extract the # version from Git. return # git describe --first-parent does not take into account tags from branches # that were merged-in. for opts in [['--first-parent'], []]: try: p = subprocess.Popen(['git', 'describe', '--long'] + opts, cwd=distr_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError: return if p.wait() == 0: break else: return description = p.communicate()[0].decode().strip('v').rstrip('\n') release, dev, git = description.rsplit('-', 2) version = [release] labels = [] if dev != "0": version.append(".dev{}".format(dev)) labels.append(git) try: p = subprocess.Popen(['git', 'diff', '--quiet'], cwd=distr_root) except OSError: labels.append('confused') # This should never happen. else: if p.wait() == 1: labels.append('dirty') if labels: version.append('+') version.append(".".join(labels)) return "".join(version) with open(os.path.join(SAVED_VERSION_FILE), 'r') as f: for line in f: line = line.strip() if line.startswith('#'): continue else: version = line break else: raise RuntimeError("Saved version file does not contain version.") version_is_from_git = (version == "__use_git__") if version_is_from_git: version = get_version_from_git() if not version: version = "unknown" def long_description(): text = [] skip = True try: with open('README.rst') as f: for line in f: if line == "\n": if skip: skip = False continue elif text[-1] == '\n': text.pop() break if not skip: text.append(line) except: return '' text[-1] = text[-1].rstrip() return ''.join(text) class build_ext(build_ext_orig): def run(self): with open(os.path.join('src', 'version.hh'), 'w') as f: f.write("// This file has been generated by setup.py.\n") f.write("// It is not included in source distributions.\n") f.write('#define VERSION "{}"\n'.format(version)) build_ext_orig.run(self) class sdist(sdist_orig): def make_release_tree(self, base_dir, files): sdist_orig.make_release_tree(self, base_dir, files) fname = os.path.join(base_dir, SAVED_VERSION_FILE) # This could be a hard link, so try to delete it first. Is there any # way to do this atomically together with opening? try: os.remove(fname) except OSError: pass with open(fname, 'w') as f: f.write("# This file has been generated by setup.py.\n{}\n" .format(version)) # The following class is based on a recipe in # http://doc.pytest.org/en/latest/goodpractices.html#manual-integration. class test(test_orig): user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")] def initialize_options(self): test_orig.initialize_options(self) self.pytest_args = '' def run_tests(self): import shlex try: import pytest except: print('The Python package "pytest" is required to run tests.', file=sys.stderr) sys.exit(1) errno = pytest.main(shlex.split(self.pytest_args)) sys.exit(errno) def main(): exts = collections.OrderedDict([ ('tinyarray', dict(language='c++', sources=['src/arithmetic.cc', 'src/array.cc', 'src/functions.cc'], depends=['src/arithmetic.hh', 'src/array.hh', 'src/conversion.hh', 'src/functions.hh']))]) exts = configure_extensions(exts) classifiers = """\ Development Status :: 5 - Production/Stable Intended Audience :: Science/Research Intended Audience :: Developers License :: OSI Approved :: BSD License Programming Language :: Python :: 2 Programming Language :: Python :: 3 Programming Language :: C++ Topic :: Software Development Topic :: Scientific/Engineering Operating System :: POSIX Operating System :: Unix Operating System :: MacOS Operating System :: Microsoft :: Windows""" setup(name='tinyarray', version=version, author='Christoph Groth (CEA) and others', author_email='christoph.groth@cea.fr', description="Arrays of numbers for Python, optimized for small sizes", long_description=long_description(), url="https://gitlab.kwant-project.org/kwant/tinyarray", download_url="http://downloads.kwant-project.org/tinyarray/", license="Simplified BSD license", platforms=["Unix", "Linux", "Mac OS-X", "Windows"], classifiers=classifiers.split('\n'), cmdclass={'build_ext': build_ext, 'sdist': sdist, 'test': test}, ext_modules=[Extension(name, **kwargs) for name, kwargs in exts.items()]) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1639749314.558119 tinyarray-1.2.4/src/0000775000175000017500000000000000000000000012454 5ustar00cwgcwg././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1495457967.0 tinyarray-1.2.4/src/arithmetic.cc0000664000175000017500000005410700000000000015123 0ustar00cwgcwg// Copyright 2012-2013 Tinyarray authors. // // This file is part of Tinyarray. It is subject to the license terms in the // file LICENSE.rst found in the top-level directory of this distribution and // at https://gitlab.kwant-project.org/kwant/tinyarray/blob/master/LICENSE.rst. // A list of Tinyarray authors can be found in the README.rst file at the // top-level directory of this distribution and at // https://gitlab.kwant-project.org/kwant/tinyarray. #include #include #include #include #include #include #include "array.hh" #include "arithmetic.hh" #include "conversion.hh" // This module assumes C99 behavior of division: // int(-3) / int(2) == -1 namespace { template PyObject *array_scalar_product(PyObject *a_, PyObject *b_) { assert(Array::check_exact(a_)); Array *a = (Array*)a_; assert(Array::check_exact(b_)); Array *b = (Array*)b_; int ndim_a, ndim_b; size_t *shape_a, *shape_b; a->ndim_shape(&ndim_a, &shape_a); b->ndim_shape(&ndim_b, &shape_b); assert(ndim_a == 1); assert(ndim_b == 1); size_t n = shape_a[0]; if (n != shape_b[0]) { PyErr_SetString(PyExc_ValueError, "Both arguments must have same length."); return 0; } T *data_a = a->data(), *data_b = b->data(); // It's important not to start with result = 0. This leads to wrong // results with regard to the sign of zero as 0.0 + -0.0 is 0.0. if (n == 0) return pyobject_from_number(T(0)); assert(n > 0); T result = data_a[0] * data_b[0]; for (size_t i = 1; i < n; ++i) { result += data_a[i] * data_b[i]; } return pyobject_from_number(result); } PyObject *(*array_scalar_product_dtable[])(PyObject*, PyObject*) = DTYPE_DISPATCH(array_scalar_product); // This routine is not heavily optimized. It's performance has been measured // to be adequate, given that it will be called from Python. The actual // calculation of the matrix product typically uses less than half of the // execution time of tinyarray.dot for two 3 by 3 matrices. template PyObject *array_matrix_product(PyObject *a_, PyObject *b_) { assert(Array::check_exact(a_)); Array *a = (Array*)a_; assert(Array::check_exact(b_)); Array *b = (Array*)b_; int ndim_a, ndim_b; size_t *shape_a, *shape_b; a->ndim_shape(&ndim_a, &shape_a); b->ndim_shape(&ndim_b, &shape_b); assert(ndim_a > 0); assert(ndim_b > 0); int ndim = ndim_a + ndim_b - 2; assert(ndim > 0); if (ndim > max_ndim) { PyErr_SetString(PyExc_ValueError, "Result would have too many dimensions."); return 0; } const size_t n = shape_a[ndim_a - 1]; size_t shape[max_ndim]; size_t d = 0, a0 = 1; for (int id = 0, e = ndim_a - 1; id < e; ++id) a0 *= shape[d++] = shape_a[id]; size_t b0 = 1; for (int id = 0, e = ndim_b - 2; id < e; ++id) b0 *= shape[d++] = shape_b[id]; size_t b1, n2; if (ndim_b == 1) { n2 = shape_b[0]; b1 = 1; } else { n2 = shape_b[ndim_b - 2]; b1 = shape[d++] = shape_b[ndim_b - 1]; } if (n2 != n) { PyErr_SetString(PyExc_ValueError, "Matrices are not aligned."); return 0; } size_t size; Array *result = Array::make(ndim, shape, &size); if (!result) return 0; T *dest = result->data(); if (n == 0) { for (size_t i = 0; i < size; ++i) dest[i] = 0; } else { assert(n > 0); const T *data_a = a->data(), *data_b = b->data(); const T *src_a = data_a; for (size_t i = 0; i < a0; ++i, src_a += n) { const T *src_b = data_b; for (size_t j = 0; j < b0; ++j, src_b += (n - 1) * b1) { for (size_t k = 0; k < b1; ++k, ++src_b) { // It's important not to start with sum = 0. This leads to // wrong results with regard to the sign of zero as 0.0 + // -0.0 is 0.0. T sum = src_a[0] * src_b[0]; for (size_t l = 1; l < n; ++l) sum += src_a[l] * src_b[l * b1]; *dest++ = sum; } } } } return (PyObject*)result; } PyObject *(*array_matrix_product_dtable[])(PyObject*, PyObject*) = DTYPE_DISPATCH(array_matrix_product); PyObject *apply_binary_ufunc(Binary_ufunc **ufunc_dtable, PyObject *a, PyObject *b) { Dtype dtype; if (coerce_to_arrays(&a, &b, &dtype) < 0) return 0; int ndim_a, ndim_b; size_t *shape_a, *shape_b; reinterpret_cast(a)->ndim_shape(&ndim_a, &shape_a); reinterpret_cast(b)->ndim_shape(&ndim_b, &shape_b); PyObject *result = 0; int ndim = std::max(ndim_a, ndim_b); size_t stride_a = 1, stride_b = 1, shape[max_ndim];; ptrdiff_t hops_a[max_ndim], hops_b[max_ndim]; for (int d = ndim - 1, d_a = ndim_a - 1, d_b = ndim_b - 1; d >= 0; --d, --d_a, --d_b) { size_t ext_a = d_a >= 0 ? shape_a[d_a] : 1; size_t ext_b = d_b >= 0 ? shape_b[d_b] : 1; if (ext_a == ext_b) { hops_a[d] = stride_a; hops_b[d] = stride_b; shape[d] = ext_a; stride_a *= ext_a; stride_b *= ext_b; } else if (ext_a == 1) { hops_a[d] = 0; hops_b[d] = stride_b; stride_b *= shape[d] = ext_b; } else if (ext_b == 1) { hops_a[d] = stride_a; hops_b[d] = 0; stride_a *= shape[d] = ext_a; } else { std::ostringstream s; s << "Operands could not be broadcast together with shapes ("; for (int d = 0; d < ndim_a; ++d) { s << shape_a[d]; if (d + 1 < ndim_a) s << ", "; } s << ") and ("; for (int d = 0; d < ndim_b; ++d) { s << shape_b[d]; if (d + 1 < ndim_b) s << ", "; } s << ")."; PyErr_SetString(PyExc_ValueError, s.str().c_str()); goto end; } } for (int d = 1; d < ndim; ++d) { hops_a[d - 1] -= hops_a[d] * shape[d]; hops_b[d - 1] -= hops_b[d] * shape[d]; } result = ufunc_dtable[int(dtype)](ndim, shape, a, hops_a, b, hops_b); end: Py_DECREF(a); Py_DECREF(b); return result; } } // Anonymous namespace template