A Streamlined Linear Algebra Library for Python
Total Page:16
File Type:pdf, Size:1020Kb
PyArmadillo: a streamlined linear algebra library for Python Jason Rumengan yz, Terry Yue Zhuo , Conrad Sanderson †∗ y Data61/CSIRO, Australia z Queensland University of Technology, Australia University of New South Wales, Australia ∗ Griffith University, Australia Abstract While the Python language has extension frameworks such as NumPy and SciPy for providing functionality involving numerical arrays and scientific computing, these frameworks can be cumbersome to use in contrast to the well-established Matlab language. The issues include: a focus on operations involving multi-dimensional arrays rather than matrices, nested organisation of functions which increases code verbosity (leading to reduced user productivity), and syntax that significantly differs from Matlab. To address these shortcomings, we propose PyArmadillo, a streamlined linear algebra library for Python, with an emphasis on ease of use. PyArmadillo aims to provide a high-level syntax and functionality deliberately similar to Matlab/Octave, allowing mathematical operations to be expressed in a familiar and natural manner. Objects for matrices and cubes are provided, as well as over 200 associated functions for manipulating data stored in the objects. Integer, floating point and complex numbers are supported. Various matrix factorisations are provided through integration with LAPACK, or one of its high performance drop-in replacements such as Intel MKL or OpenBLAS. PyArmadillo is open-source software, distributed under the Apache 2.0 license; it can be obtained at https://pyarma.sourceforge.io or via the Python Package Index in precompiled form. Background Matlab is often considered to be the de facto standard for experimentation and prototyping involving numerical linear algebra, particularly in educational settings [9]. This stems from its ease of use, allowing mathematical expressions to be written in a concise and natural manner, especially in comparison to directly using low-level libraries such as LAPACK [2]. However, Matlab is proprietary and has license-based restrictions on use. This in turn may impose significant financial costs dependent on the maximum number of allowed users, and/or application area (eg. industry, academia), and/or proprietary add-on toolboxes. Furthermore, due to its focus on prototyping and linear algebra, Matlab is less suitable for the development of general programs and/or integration into larger systems. Octave, an open-source analog of Matlab, ameliorates the licensing costs and restrictions associated with Matlab. However, Octave still shares the remaining downsides and may not be fully compatible with Matlab; its associated add-on toolboxes tend to be less developed than their proprietary counterparts. Recently the Python language has gained traction for scientific computing through add-on frameworks such as NumPy [5] and SciPy [13]. In contrast to Matlab, Python is suitable for general programming and is available under a permissive open-source license. However, for scientists and engineers looking to transition from Matlab to Python, the programming interface provided by the NumPy and SciPy frameworks tends to be verbose and cumbersome to use in comparison to Matlab. From a linear algebra point of view, these arXiv:2104.11120v2 [cs.MS] 28 Apr 2021 frameworks have issues which include: (i) a focus on operations involving multi-dimensional arrays rather than matrices, (ii) nested organisation of functions which increases code verbosity (which in turn can lead to reduced user productivity), and (iii) syntax that notably differs from Matlab. With the aim to facilitate the transition from Matlab to the Python ecosystem, we have devised and implemented the PyArmadillo library, which addresses the above issues. Specifically, PyArmadillo primarily focuses on linear algebra (operations on matrices), has all functions accessible in one flat structure, and provides an API that closely follows Matlab wherever possible. Functionality PyArmadillo provides matrix objects for several distinct element types: integers, single- and double-precision floating point numbers, as well as complex numbers. In addition to matrices, PyArmadillo also has support for cubes (3 dimensional arrays), where each cube can be treated as an ordered set of matrices. PyArmadillo matrices and cubes are convertible to/from NumPy arrays, allowing users to tap into the wider Python data science ecosystem, including plotting tools such as Matplotlib [6]. Over 200 functions are provided for manipulating data stored in the objects, covering the following areas: fundamental arithmetic operations, contiguous and non-contiguous submatrix views, diagonal views, element-wise functions, scalar/vector/matrix valued functions of matrices, statistics, signal processing, storage of matrices in files, matrix decompositions/factorisations, matrix inverses, and equation solvers. An overview of the available functionality in PyArmadillo (as of version 0.500) is given in Tables1 to8. Table1 briefly describes the member functions and variables of the main matrix class; Table2 lists the main subset of overloaded Python operators; Table3 outlines matrix decompositions and equation solvers; Table4 overviews functions for generating vectors with various sequences; Table5 lists the main forms of general functions of matrices; Table6 lists various element-wise functions of matrices; Table7 summarises the set of functions and classes focused on statistics; Table8 lists functions specific to signal and image processing. Lastly, Table9 provides examples of Matlab/Octave syntax and conceptually corresponding PyArmadillo syntax. See the online documentation at https://pyarma.sourceforge.io/docs.html for more details. Figure1 shows a short example program that demonstrates the streamlined nature of PyArmadillo syntax and draws comparison with NumPy syntax. To generate a matrix with all elements set to one, PyArmadillo simply requires ones(m, n), where m and n indicate the number of rows and columns, respectively. In contrast, NumPy requires the use of double brackets in ones((m, n)), which is non-intuitive and notably differs from Matlab. To generate a matrix filled with values from a uniform random distribution, PyArmadillo has the randu(m,n) function. In comparison, NumPy uses np.random.rand(m, n), which shows the nested organisation of functions, and reveals an inconsistency in the specification of the matrix size: use of single brackets versus double brackets. To multiply two matrices, PyArmadillo uses the standard * operator, as per Matlab. In contrast, NumPy uses the non-intuitive @ character for matrix multiplication, and uses the * operator for element-wise multiplication, which in turn demonstrates a focus on arrays rather than matrices. # PyArmadillo example # corresponding NumPy code import pyarma as pa import numpy as np A = pa.ones(4, 5) A = np.ones((4,5)) B = pa.randu(4, 5) B = np.random.rand(4, 5) C = A*B.t() C = [email protected] C.print() print(C) Figure 1: A short example program using PyArmadillo, and corresponding NumPy implementation. Implementation PyArmadillo relies on Armadillo [10, 11] for the underlying C++ implementation of matrix objects and associated functions, as well as on pybind11 [8] for interfacing C++ and Python. Due to its expressiveness and relatively straightforward use, pybind11 was selected over other interfacing approaches such as Boost.Python [1] and manually writing C++ extensions for Python. Armadillo was selected due to its API having close similarity to Matlab, as well as being battle-tested in the context of RcppArmadillo, a widely used bridge between the R language and C++ [4]. In turn, Armadillo interfaces with low-level routines in BLAS and LAPACK [2], where BLAS is used for matrix multiplication, and LAPACK is used for various matrix decompositions/factorisations and equation solvers. As the low-level routines in BLAS and LAPACK are considered as a de facto standard for numerical linear algebra, it is possible to use high performance drop-in replacements such as Intel MKL [7] and OpenBLAS [14]. PyArmadillo is open-source software available under the permissive Apache License 2.0 [3], making it useful in both open-source and proprietary (closed-source) contexts [12]. Table 1: Subset of member functions and variables of the mat class, the main matrix object in PyArmadillo. Function/Variable Description :n rows number of rows (read only) :n cols number of columns (read only) :n elem total number of elements (read only) [i] access the i-th element, assuming a column-by-column layout [r, c] access the element at row r and column c :in range(i) test whether the i-th element can be accessed :in range(r, c) test whether the element at row r and column c can be accessed :reset() set the number of elements to zero :copy size(A) set the size to be the same as matrix A :set size(n rows, n cols) change size to specified dimensions, without preserving data (fast) .reshape(n rows, n cols) change size to specified dimensions, with elements copied column-wise (slow) .resize(n rows, n cols) change size to specified dimensions, while preserving elements & their layout (slow) :ones(n rows, n cols) set all elements to one, optionally first resizing to specified dimensions :zeros(n rows, n cols) as above, but set all elements to zero :randu(n rows, n cols) as above, but set elements to uniformly distributed random values in [0,1] interval :randn(n rows, n cols) as above, but use a Gaussian/normal distribution with µ = 0 and σ = 1 :fill(k) set all elements to be equal to k :for each(lambda