## TOC »

## 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

`gen`constant`c128`constant`c64`constant`f64`constant`f32`constant`s64`constant`u64`constant`s32`constant`u32`constant`s16`constant`u16`constant`s8`constant`u8`constant`u1`constantFairly 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-copy`parameterUnless 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 ...)`syntaxAllows you to temporarily change the mode of operation for

`body ...`to SRFI-like.

`(with-copy body ...)`syntaxAllows 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``dtype`procedureConverts an array-like nested list to an array. See

`array`.

`array``lst``#!key``dtype`procedureMain array creation procedure. Currently synonymous to

`list->array*`.Similar: numpy.array

`domain``arg1``#!optional``arg2`procedureLike SRFI-179's

`make-interval`, but interval bounds can be specified as vectors, lists, or pairs.

#### Array attributes

`array-shape``a`procedureReturn 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``a`procedureReturn the number of array dimensions, synonymous to SRFI-179's

`array-dimension`.Similar: ndarray.ndim

`array-data``a`procedureThe same as SRFI-179's

`array-body`.Similar: ndarray.data

`array-size``a`procedureReturns the total number of elements in the array.

Similar: ndarray.size

`array-dtype``a`procedureReturns the dtype of the array or

`#f`if`a`is not a specialized array.Similar: ndarray.dtype

#### Array procedures

`array->list*``a`procedureConverts an array to an array-like nested list.

Similar: ndarray.tolist

`array-T``a``#!key``axes`procedureReturns 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``a`procedureReturns the real part of

`a`.Similar: ndarray.real

`array-imag``a`procedureReturns the imaginary part of

`a`.Similar: ndarray.imag

`array-astype``a``dtype`procedureCopy the array, cast to the specified

`dtype`.Similar: ndarray.astype

- dtype
- A storage class

`array-copy*``a``#!optional``dtype``new-domain``mutable?``safe?`procedureLike SRFI-179's

`array-copy`,`new-domain`can be specified as for the`domain`procedure.Similar: ndarray.copy

`array-map*``f``a``#!rest``as`procedureLike 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``as`procedureLike SRFI-179's

`array-map`, but stores the results in`a`, the first passed array.

`array-fill!``a``value`procedureFill

`a`with`value`.Similar: ndarray.fill

`array-reshape``a``shape`procedureReturns 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``axes`procedureSynonymous to

`array-T`.Similar: ndarray.transpose

`array-flatten``array`procedureReturns 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`)procedureReturn 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`)procedureReturn 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`)procedureReturn 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``max`procedureReturn an array whose minimum values are

`min`and maximum values are`max`.Similar: ndarray.clip

`array-clip!``a``#!key``min``max`procedureLike

`array-clip`, but modifies the original array.

`array-conj``a`procedureComplex conjugate all elements of

`a`.Similar: ndarray.conj

`array-round``a``#!optional``n`procedureRound 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`)procedureReturn the sum along the diagonals of the array.

**DIFF**: See`array-diagonal`Similar: ndarray.trace

`array-sum``a``#!key``axis`(`dtype``generic-storage-class`)procedureReturn 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`)procedureReturns 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`)procedureReturns 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`)procedureReturns the standard deviation of the array elements along given axis.

Similar: ndarray.std

`array-prod``a``#!key``axis`(`dtype``generic-storage-class`)procedureReturn 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`)procedureReturns

`#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`)procedureReturns

`#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)`procedureWrites 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-maxsize`constantSets the maximum size that an array can be beyond which its printed form is summarized.

`array-summary-num-edges`constantSets the number of edges to display for a summarized array.

`array-summary-flonum-precision`constantSets the array flounm print precision for a summarized array.

### Constants

`inf`constantAn alias for +inf.0

`ninf`constantAn alias for -inf.0

`nan`constantAn alias for +nan.0

`nzero`constantAn alias for -0.0

`e`constantEuler's number

`euler-gamma`constantEuler-Mascheroni constant

`pi`constant

### Mathematical functions

`conj``a`procedureReturns the complex conjugate of a number or element-wise complex conjugate of an array.

Similar: numpy.conj

`round``a``#!optional``b`procedureEvenly 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``n`procedureThe same as

`round`.

`add``a``b``#!key``dtype`procedureAdds 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.