chickadee » numchi

numchi

A mathematical library inspired by NumPy

Introduction

NumChi attempts to provide a subset of a similar API to Python's NumPy based on SRFI-179's arrays. All of SRFI-179's procedures are reexported. Because this library defines many procedures, some of which have the same names as scheme procedures, it's strongly recommended to prefix the module: (import (prefix numchi nc)) or similar.

Disclaimers

This egg is not yet released, but you can find the sources at the following link:

https://code.dieggsy.com/numchi.

This library should still be considered alpha software. There may be breaking changes in the future. Help and advice are very welcome at (string-append "dieggsy" "@" "pm" "." "me") or dieggsy on the #chicken IRC channel.

Currently, the code is written for correct results rather than performance, and tested against NumPy itself using the pyffi egg. It's possible that the library could be made faster in the future by specializing for lower dimensions (like SRFI-179 does) and/or dropping into the Chicken C interface. Ideas for performance can be found in the bits/matrix-multiply.scm file (blas/Eigen integration) as well as in the fast-math branch (fast generic math routines with dispatching in C/C++)

Most optional arguments are implemented as keyword arguments (except in certain cases like round) as these convey the purpose of the argument more clearly than optional arguments, and can also be used similarly to python's optional arguments (in any order).

The library procedures should work on arrays with domains with non-zero lower bounds, but this is currently not well-tested.

Mode of operation

This library sets (specialize-array-default-safe? #t). It's possible that setting this to #f might give you a small performance benefit.

There are two basic modes of operation, determined by the value of the array-default-copy parameter. If its value is #t (the default), the library behaves more like NumPy in that array operations return specialized arrays. This is referred to as NumPy-like.

If its value is #f, the library behaves more like SRFI-179 in that the results of array operations are not specialized arrays (meaning the resulting arrays behave more like functions defined on the input arrays). This is refered to as SRFI-like. This may be sort of unfamiliar since e.g. the array returned by (array-sum a #:axis 0) will later change if a changes. To avoid unexpected behavior, setting (array-default-copy #f) also sets (specialized-array-default-mutable? #f), which can of course be unset manually if you don't want this behavior.

Procedures that behave in the same way regardless of the "mode" should be documented as such.

Dtypes and storage-class aliases

For the purposes of this library, a "dtype" is simply an SRFI-179 storage-class. The following aliases are provided for convenience.

Dtype aliases

genconstant
c128constant
c64constant
f64constant
f32constant
s64constant
u64constant
s32constant
u32constant
s16constant
u16constant
s8constant
u8constant
u1constant

Fairly self-explanatory aliases for SRFI-179 storage classes. gen is generic-storage-class, the remaining are the same as [dtype]-storage-class, where [dtype] is one of the idetifiers above.

Dtype conversion table

For generic operations on multiple arrays like add, multiply, etc., the resulting dtype is determined according to the following table, which is based on numpy's default type conversion rules:

      gen  c128   c64   f64   f32   s64   u64   s32   u32   s16   u16    s8    u8
 gen  gen   gen   gen   gen   gen   gen   gen   gen   gen   gen   gen   gen   gen
c128  gen  c128  c128  c128  c128  c128  c128  c128  c128  c128  c128  c128  c128
 c64  gen  c128   c64  c128   c64  c128  c128  c128  c128   c64   c64   c64   c64
 f64  gen  c128  c128   f64   f64   f64   f64   f64   f64   f64   f64   f64   f64
 f32  gen  c128   c64   f64   f32   f64   f64   f64   f64   f32   f32   f32   f32
 s64  gen  c128  c128   f64   f64   s64   f64   s64   s64   s64   s64   s64   s64
 u64  gen  c128  c128   f64   f64   f64   u64   f64   u64   f64   u64   f64   u64
 s32  gen  c128  c128   f64   f64   s64   f64   s32   s64   s32   s32   s32   s32
 u32  gen  c128  c128   f64   f64   s64   u64   s64   u32   s64   u32   s64   u32
 s16  gen  c128   c64   f64   f32   s64   f64   s32   s64   s16   s32   s16   s16
 u16  gen  c128   c64   f64   f32   s64   u64   s32   u32   s32   u16   s32   u16
  s8  gen  c128   c64   f64   f32   s64   f64   s32   s64   s16   s32    s8   s16
  u8  gen  c128   c64   f64   f32   s64   u64   s32   u32   s16   u16   s16    u8

Basic array operations

Mode of operation

array-default-copyparameter

Unless otherwise specified by procedure documentation, if set to #t, the library is NumPy-like (returned arrays are specialized). If set to #f, the library is SRFI-like (returned arrays are functional).

(without-copy body ...)syntax

Allows you to temporarily change the mode of operation for body ... to SRFI-like.

(with-copy body ...)syntax

Allows you to temporarily change the mode of operation for body ... to NumPy-like.

Constructors

There are a few ways to create arrays with numchi:

;; Using the array procedure
(array '((1 2) ;; dtype inferred
         (3 4)))
(array '((1 2) ;; explicit dtype
         (3 4))
       dtype: s64)
;; Using the special read syntax
'#!a((1 2)    ;; dtype inferred
     (3 4))
'#!s64a((1 2) ;; explicit dtype
        (3 4))

You may also prefer srfi-179's list->array:

;; You don't have to import srfi-179 since numchi reexports it.
(list->array '(1 2
               3 4)
             (domain #(2 2))
             s64)
list->array* lst #!key dtypeprocedure

Converts an array-like nested list to an array. See array.

array lst #!key dtypeprocedure

Main array creation procedure. Currently synonymous to list->array*.

Similar: numpy.array

lst
An array-like list, with dimensions specified by nesting.
dtype
An SRFI-179 storage class. Any list containing bignums, ratnums, or non-numerical scheme objects is converted into a gen array. Otherwise, the most appropriate of s64, f64, or c128 is chosen.
domain arg1 #!optional arg2procedure

Like SRFI-179's make-interval, but interval bounds can be specified as vectors, lists, or pairs.

Array attributes

array-shape aprocedure

Return the shape of a as a vector. This equivalent to the upper bounds of the array domain if the domain is normalized to 0 lower bounds.

Similar: ndarray.shape

array-ndim aprocedure

Return the number of array dimensions, synonymous to SRFI-179's array-dimension.

Similar: ndarray.ndim

array-data aprocedure

The same as SRFI-179's array-body.

Similar: ndarray.data

array-size aprocedure

Returns the total number of elements in the array.

Similar: ndarray.size

array-dtype aprocedure

Returns the dtype of the array or #f if a is not a specialized array.

Similar: ndarray.dtype

Array procedures

array->list* aprocedure

Converts an array to an array-like nested list.

Similar: ndarray.tolist

array-T a #!key axesprocedure

Returns a transposed shared array. Equivalent to SRFI-179's array-permute but with an optional permutation argument with a default value.

Similar: ndarray.T

axes
Reverses the order of a's axes if not given. Otherwise, a vector of integers: i in the j-th place in the tuple means a’s i-th axis becomes (array-transpose a)’s j-th axis.
array-real aprocedure

Returns the real part of a.

Similar: ndarray.real

array-imag aprocedure

Returns the imaginary part of a.

Similar: ndarray.imag

array-astype a dtypeprocedure

Copy the array, cast to the specified dtype.

Similar: ndarray.astype

dtype
A storage class
array-copy* a #!optional dtype new-domain mutable? safe?procedure

Like SRFI-179's array-copy, new-domain can be specified as for the domain procedure.

Similar: ndarray.copy

array-map* f a #!rest asprocedure

Like SRFI-179's array-map, but behaves differently on (array-default-copy #t): namely, it returns a copy of the map with the first argument's storage class.

array-map! f a #!rest asprocedure

Like SRFI-179's array-map, but stores the results in a, the first passed array.

array-fill! a valueprocedure

Fill a with value.

Similar: ndarray.fill

array-reshape a shapeprocedure

Returns an array containing the same data with new shape. Unaffected by array-default-copy.

Similar: ndarray.reshape

a
A specialized array
shape
A domain in the same format as specified by domain
array-transpose a #!key axesprocedure

Synonymous to array-T.

Similar: ndarray.transpose

array-flatten arrayprocedure

Returns a copy of the array collapsed into one dimension. Unaffected by array-default-copy.

Similar: ndarray.flatten

array-diagonal a #!key (offset 0) (axis1 0) (axis2 1)procedure

Return diagonals of a as a shared array. Unaffected by array-default-copy. Currently only works on specialized arrays.

NOTE: Part of the implementation of this procedure is based on NumPy's C sources (translated to scheme), and is pretty gross if not inefficient.

Similar: ndarray.diagonal

DIFF: Offsets must be within a's bounds, since SRFI-179 does not support zero sized arrays/dimensions.

array-max a #!key axis (initial -inf.0)procedure

Return the maximum of the array or the maximum along the given axis.

Similar: ndarray.max

initial
Minimum value of an input element.
array-min a #!key axis (initial +inf.0)procedure

Return the minimum of the array or the minimum along the given axis.

Similar: ndarray.min

initial
Maximum value of an input element.
array-clip a #!key min maxprocedure

Return an array whose minimum values are min and maximum values are max.

Similar: ndarray.clip

array-clip! a #!key min maxprocedure

Like array-clip, but modifies the original array.

array-conj aprocedure

Complex conjugate all elements of a.

Similar: ndarray.conj

array-round a #!optional nprocedure

Round all elements of a.

Similar: ndarray.round

n
Decimal places to round to, can be negative to round the tens, hundreds, etc. place.
array-trace a #!key (offset 0) (axis1 0) (axis2 1) (dtype generic-storage-class)procedure

Return the sum along the diagonals of the array.

DIFF: See array-diagonal

Similar: ndarray.trace

array-sum a #!key axis (dtype generic-storage-class)procedure

Return the sum of the array elements over the given axis.

DIFF: Because the result may overflow, on (array-default-copy #t) the returned array has the generic storage class by default.

Similar: ndarray.sum

array-mean a #!key axis (dtype generic-storage-class)procedure

Returns the average of the array elements along given axis.

DIFF: This procedure will return averages as exact numbers where possible, so on (array-default-copy #t) the returned array has the generic storage class by default.

Similar: mean

array-var a #!key axis (dtype generic-storage-class)procedure

Returns the variance of the array elements, along given axis.

DIFF: This procedure will return variance as exact numbers where possible, so on (array-default-copy #t) the returned array has the generic storage class by default.

Similar: ndarray.var

array-std a #!key axis (dtype generic-storage-class)procedure

Returns the standard deviation of the array elements along given axis.

Similar: ndarray.std

array-prod a #!key axis (dtype generic-storage-class)procedure

Return the product of the array elements over the given axis

DIFF: Because the result may overflow, on (array-default-copy #t) the returned array has the generic storage class by default.

Similar: ndarray.prod

array-all a #!key axis (dtype generic-storage-class)procedure

Returns #t if all elements are not #f.

DIFF: This will return slightly different results than numpy's array-all because while scheme and python both have "truthiness" (non #t or True objects evaluate as true for boolean operations), scheme does not have Python's "falsiness" for values like 0, [], "", etc.

Similar: ndarray.all

array-any* a #!key axis (dtype generic-storage-class)procedure

Returns #t if any of the elements is not #f. Note the * in the name to avoid shadowing srfi-179's array-any.

DIFF: This will return slightly different results than numpy's array-all because while scheme and python both have "truthiness" (objects that are not #t or True evaluate as true for boolean operations), scheme does not have Python's "falsiness" for values like 0, [], "", etc.

Similar: ndarray.any

Array I/O

Read/write syntax

The special read syntax '#!a(...) is an alias for (array '(...)). You can also specify a dtype, like so: '#![dtype]a(...) (e.g. '#!f64a(...)). Arrays are by default printed in the dtype syntax, with non-specialized arrays printed as '#!*a(...), which currently does not have an equivalent read syntax.

Procedures

(write-array a #!optional (port (current-output-port)) #!key mode summarize)procedure

Writes an array to port. If #:summarize is #t, prints a summary of the array according to the array-summary-* parameters. The keyword argument #:mode is currently a no-op, but there are plans to use it for writing arrays in e.g. binary or ascii format.

Parameters

array-summary-maxsizeconstant

Sets the maximum size that an array can be beyond which its printed form is summarized.

array-summary-num-edgesconstant

Sets the number of edges to display for a summarized array.

array-summary-flonum-precisionconstant

Sets the array flounm print precision for a summarized array.

Constants

infconstant

An alias for +inf.0

ninfconstant

An alias for -inf.0

nanconstant

An alias for +nan.0

nzeroconstant

An alias for -0.0

econstant

Euler's number

euler-gammaconstant

Euler-Mascheroni constant

piconstant

Mathematical functions

conj aprocedure

Returns the complex conjugate of a number or element-wise complex conjugate of an array.

Similar: numpy.conj

round a #!optional bprocedure

Evenly rounds the number (or array, element-wise) to n decimals. If n is negative, rounds to nearest tens, hundreds, etc.

Similar: numpy.around

around a #!optional nprocedure

The same as round.

add a b #!key dtypeprocedure

Adds arguments element-wise. Arguments can be arrays or numbers. A resulting array's dtype is determined by the dtype conversion table if dtype is not given.

Repository

https://code.dieggsy.com/numchi

Author

Diego A. Mundo

License

BSD

Copyright (c) 2021 Diego A. Mundo
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted (subject to the limitations in the disclaimer
below) provided that the following conditions are met:

     * Redistributions of source code must retain the above copyright notice,
       this list of conditions and the following disclaimer.

     * Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.

     * Neither the name of the copyright holder nor the names of its
       contributors may be used to endorse or promote products derived from
       this software without specific prior written permission.

NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS
LICENSE. 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.

Contents »