source: project/wiki/eggref/5/numchi @ 39687

Last change on this file since 39687 was 39687, checked in by Diego, 2 months ago

numchi: add links to performance experiments

File size: 20.6 KB
Line 
1[[tags: egg]]
2[[toc:]]
3== numchi
4A mathematical library inspired by NumPy
5
6=== Introduction
7
8NumChi attempts to provide a subset of a similar API to Python's NumPy based
9on SRFI-179's arrays. All of SRFI-179's procedures are reexported. Because this
10library defines many procedures, some of which have the same names as scheme
11procedures, it's strongly recommended to prefix the module: {{(import (prefix
12numchi nc))}} or similar.
13
14=== Disclaimers
15
16This egg is not yet released, but you can find the sources at the
17following link:
18
19[[https://code.dieggsy.com/numchi]].
20
21This library should still be considered alpha software. There may be breaking
22changes in the future. Help and advice are very welcome at {{(string-append
23"dieggsy" "@" "pm" "." "me")}} or {{dieggsy}} on the {{#chicken}} IRC
24channel.
25
26Currently, the code is written for correct results rather than
27performance, and tested against NumPy itself using the pyffi egg. It's possible
28that the library could be made faster in the future by specializing for lower
29dimensions (like SRFI-179 does) and/or dropping into the Chicken C interface.
30Ideas for performance can be found in the
31[[https://code.dieggsy.com/numchi/tree/bits/matrix-multiply.scm|bits/matrix-multiply.scm]]
32file (blas/Eigen integration) as well as in the
33[[https://code.dieggsy.com/numchi/?h=fast-math|fast-math]] branch (fast generic
34math routines with dispatching in C/C++)
35
36Most optional arguments are implemented as keyword arguments (except in
37certain cases like {{round}}) as these convey the purpose of the argument more
38clearly than optional arguments, and can also be used similarly to python's
39optional arguments (in any order).
40
41The library procedures should work on arrays with domains with non-zero
42lower bounds, but this is currently not well-tested.
43
44=== Mode of operation
45
46This library sets {{(specialize-array-default-safe? #t)}}. It's possible
47that setting this to {{#f}} might give you a small performance benefit.
48
49There are two basic modes of operation, determined by the value of the
50{{array-default-copy}} parameter. If its value is {{#t}} (the default), the
51library behaves more like NumPy in that array operations return specialized
52arrays. This is referred to as NumPy-like.
53
54If its value is {{#f}}, the library behaves more like SRFI-179 in that the
55results of array operations are not specialized arrays (meaning the resulting
56arrays behave more like functions defined on the input arrays). This is refered
57to as SRFI-like. This may be sort of unfamiliar since e.g. the array returned
58by {{(array-sum a #:axis 0)}} will later change if {{a}} changes. To avoid
59unexpected behavior, setting {{(array-default-copy #f)}} also sets
60{{(specialized-array-default-mutable? #f)}}, which can of course be unset
61manually if you don't want this behavior.
62
63Procedures that behave in the same way regardless of the "mode" should
64be documented as such.
65
66=== Dtypes and storage-class aliases
67
68For the purposes of this library, a "dtype" is simply an SRFI-179
69storage-class. The following aliases are provided for convenience.
70
71==== Dtype aliases
72
73<constant>gen</constant>
74<constant>c128</constant>
75<constant>c64</constant>
76<constant>f64</constant>
77<constant>f32</constant>
78<constant>s64</constant>
79<constant>u64</constant>
80<constant>s32</constant>
81<constant>u32</constant>
82<constant>s16</constant>
83<constant>u16</constant>
84<constant>s8</constant>
85<constant>u8</constant>
86<constant>u1</constant>
87Fairly self-explanatory aliases for SRFI-179 storage classes. {{gen}} is
88{{generic-storage-class}}, the remaining are the same as
89{{[dtype]-storage-class}}, where {{[dtype]}} is one of the idetifiers above.
90
91==== Dtype conversion table
92
93For generic operations on multiple arrays like add, multiply, etc., the
94resulting dtype is determined according to the following table, which is based
95on numpy's default type conversion rules:
96
97<enscript>
98      gen  c128   c64   f64   f32   s64   u64   s32   u32   s16   u16    s8    u8
99 gen  gen   gen   gen   gen   gen   gen   gen   gen   gen   gen   gen   gen   gen
100c128  gen  c128  c128  c128  c128  c128  c128  c128  c128  c128  c128  c128  c128
101 c64  gen  c128   c64  c128   c64  c128  c128  c128  c128   c64   c64   c64   c64
102 f64  gen  c128  c128   f64   f64   f64   f64   f64   f64   f64   f64   f64   f64
103 f32  gen  c128   c64   f64   f32   f64   f64   f64   f64   f32   f32   f32   f32
104 s64  gen  c128  c128   f64   f64   s64   f64   s64   s64   s64   s64   s64   s64
105 u64  gen  c128  c128   f64   f64   f64   u64   f64   u64   f64   u64   f64   u64
106 s32  gen  c128  c128   f64   f64   s64   f64   s32   s64   s32   s32   s32   s32
107 u32  gen  c128  c128   f64   f64   s64   u64   s64   u32   s64   u32   s64   u32
108 s16  gen  c128   c64   f64   f32   s64   f64   s32   s64   s16   s32   s16   s16
109 u16  gen  c128   c64   f64   f32   s64   u64   s32   u32   s32   u16   s32   u16
110  s8  gen  c128   c64   f64   f32   s64   f64   s32   s64   s16   s32    s8   s16
111  u8  gen  c128   c64   f64   f32   s64   u64   s32   u32   s16   u16   s16    u8
112</enscript>
113
114=== Basic array operations
115
116==== Mode of operation
117
118<parameter>array-default-copy</parameter>
119Unless otherwise specified by procedure documentation, if set to
120{{#t}}, the library is NumPy-like (returned arrays are specialized). If set to
121{{#f}}, the library is SRFI-like (returned arrays are functional).
122
123<syntax>(without-copy body ...)</syntax>
124Allows you to temporarily change the mode of operation for {{body ...}} to
125SRFI-like.
126
127<syntax>(with-copy body ...)</syntax>
128Allows you to temporarily change the mode of operation for {{body ...}} to
129NumPy-like.
130
131==== Constructors
132
133There are a few ways to create arrays with numchi:
134
135<enscript highlight="scheme">
136;; Using the array procedure
137(array '((1 2) ;; dtype inferred
138         (3 4)))
139(array '((1 2) ;; explicit dtype
140         (3 4))
141       dtype: s64)
142;; Using the special read syntax
143'#!a((1 2)    ;; dtype inferred
144     (3 4))
145'#!s64a((1 2) ;; explicit dtype
146        (3 4))
147</enscript>
148
149You may also prefer srfi-179's {{list->array}}:
150
151<enscript highlight="scheme">
152;; You don't have to import srfi-179 since numchi reexports it.
153(list->array '(1 2
154               3 4)
155             (domain #(2 2))
156             s64)
157</enscript>
158
159<procedure>(list->array* lst #!key dtype) → array</procedure>
160Converts an array-like nested list to an array. See {{array}}.
161
162<procedure>(array lst #!key dtype) → array</procedure>
163Main array creation procedure. Currently synonymous to {{list->array*}}.
164
165Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.array.html|numpy.array]]
166
167; lst : An array-like list, with dimensions specified by nesting.
168; 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.
169
170<procedure>(domain arg1 #!optional arg2) → interval</procedure>
171Like SRFI-179's {{make-interval}}, but interval bounds can be specified
172as vectors, lists, or pairs.
173
174==== Array attributes
175
176<procedure>(array-shape a) → vector</procedure>
177Return the shape of {{a}} as a vector. This equivalent to the upper bounds
178of the array domain if the domain is normalized to 0 lower bounds.
179
180Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.shape.html#numpy.ndarray.shape|ndarray.shape]]
181
182<procedure>(array-ndim a) → integer</procedure>
183Return the number of array dimensions, synonymous to SRFI-179's
184{{array-dimension}}.
185
186Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.ndim.html#numpy.ndarray.ndim|ndarray.ndim]]
187
188<procedure>(array-data a) → (or vector homogeneous-vector)</procedure>
189The same as SRFI-179's {{array-body}}.
190
191Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.data.html#numpy.ndarray.data|ndarray.data]]
192
193<procedure>(array-size a) → integer</procedure>
194Returns the total number of elements in the array.
195
196Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.size.html#numpy.ndarray.size|ndarray.size]]
197
198<procedure>(array-dtype a) → symbol</procedure>
199Returns the dtype of the array or {{#f}} if {{a}} is not a specialized array.
200
201Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.dtype.html#numpy.ndarray.dtype|ndarray.dtype]]
202
203==== Array procedures
204
205<procedure>(array->list* a) → list</procedure>
206Converts an array to an array-like nested list.
207
208Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tolist.html|ndarray.tolist]]
209
210<procedure>(array-T a #!key axes) → array</procedure>
211Returns a transposed shared array. Equivalent to SRFI-179's
212{{array-permute}} but with an optional permutation argument with a default
213value.
214
215Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.T.html#numpy.ndarray.T|ndarray.T]]
216
217; 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.
218
219<procedure>(array-real a) → array</procedure>
220Returns the real part of {{a}}.
221
222Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.real.html#numpy.ndarray.real|ndarray.real]]
223
224<procedure>(array-imag a) → array</procedure>
225Returns the imaginary part of {{a}}.
226
227Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.imag.html#numpy.ndarray.imag|ndarray.imag]]
228
229<procedure>(array-astype a dtype) → array</procedure>
230Copy the array, cast to the specified {{dtype}}.
231
232Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.astype.html#numpy.ndarray.astype|ndarray.astype]]
233
234; dtype : A storage class
235
236<procedure>(array-copy* a #!optional dtype new-domain mutable? safe?) → array</procedure>
237Like SRFI-179's {{array-copy}}, {{new-domain}} can be specified as for the
238{{domain}} procedure.
239
240Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.copy.html#numpy.ndarray.copy|ndarray.copy]]
241
242<procedure>(array-map* f a . as) → array</procedure>
243Like SRFI-179's {{array-map}}, but behaves differently on
244(array-default-copy #t): namely, it returns a copy of the map with the first
245argument's storage class.
246
247<procedure>(array-map! f a . as)</procedure>
248Like SRFI-179's {{array-map}}, but stores the results in {{a}}, the first
249passed array.
250
251<procedure>(array-fill! a value)</procedure>
252Fill {{a}} with {{value}}.
253
254Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.fill.html#numpy.ndarray.fill|ndarray.fill]]
255
256<procedure>(array-reshape a shape) → array</procedure>
257Returns an array containing the same data with new shape. Unaffected by
258{{array-default-copy}}.
259
260Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.reshape.html#numpy.ndarray.reshape|ndarray.reshape]]
261
262; a : A specialized array
263; shape : A domain in the same format as specified by {{domain}}
264
265<procedure>(array-transpose a #!key axes) → array</procedure>
266Synonymous to {{array-T}}.
267
268Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.transpose.html#numpy.ndarray.transpose|ndarray.transpose]]
269
270<procedure>(array-flatten array) → array</procedure>
271Returns a copy of the array collapsed into one dimension. Unaffected by
272{{array-default-copy}}.
273
274Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.flatten.html#numpy.ndarray.flatten|ndarray.flatten]]
275
276<procedure>(array-diagonal a #!key (offset 0) (axis1 0) (axis2 1)) → array</procedure>
277Return diagonals of {{a}} as a shared array. Unaffected by
278{{array-default-copy}}. Currently only works on specialized arrays.
279
280'''NOTE:''' Part of the implementation of this procedure is based on
281NumPy's C sources (translated to scheme), and is pretty gross if not
282inefficient.
283
284Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.diagonal.html#numpy.ndarray.diagonal|ndarray.diagonal]]
285
286'''DIFF''': Offsets must be within {{a}}'s bounds, since SRFI-179 does not
287support zero sized arrays/dimensions.
288
289<procedure>(array-max a #!key axis (initial -inf.0)) → (or number array)</procedure>
290Return the maximum of the array or the maximum along the given axis.
291
292Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.max.html#numpy.ndarray.max|ndarray.max]]
293
294; initial : Minimum value of an input element.
295
296<procedure>(array-min a #!key axis (initial +inf.0)) → (or number array)</procedure>
297Return the minimum of the array or the minimum along the given axis.
298
299Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.min.html#numpy.ndarray.min|ndarray.min]]
300
301; initial : Maximum value of an input element.
302
303<procedure>(array-clip a #!key min max) → array</procedure>
304Return an array whose minimum values are {{min}} and maximum values are {{max}}.
305
306Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.clip.html#numpy.ndarray.clip|ndarray.clip]]
307
308<procedure>(array-clip! a #!key min max)</procedure>
309Like {{array-clip}}, but modifies the original array.
310
311<procedure>(array-conj a) → array</procedure>
312Complex conjugate all elements of {{a}}.
313
314Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.conj.html#numpy.ndarray.conj|ndarray.conj]]
315
316<procedure>(array-round a #!optional n) → array</procedure>
317Round all elements of {{a}}.
318
319Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.round.html|ndarray.round]]
320
321; n : Decimal places to round to, can be negative to round the tens, hundreds, etc. place.
322
323<procedure>(array-trace a #!key (offset 0) (axis1 0) (axis2 1) (dtype generic-storage-class))</procedure>
324Return the sum along the diagonals of the array.
325
326'''DIFF''': See {{array-diagonal}}
327
328Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.trace.html#numpy.ndarray.trace|ndarray.trace]]
329
330<procedure>(array-sum a #!key axis (dtype generic-storage-class))</procedure>
331Return the sum of the array elements over the given axis.
332
333'''DIFF''': Because the result may overflow, on {{(array-default-copy #t)}}
334the returned array has the generic storage class by default.
335
336Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.sum.html#numpy.ndarray.sum|ndarray.sum]]
337
338<procedure>(array-mean a #!key axis (dtype generic-storage-class))</procedure>
339Returns the average of the array elements along given axis.
340
341'''DIFF''': This procedure will return averages as exact numbers where
342possible, so on {{(array-default-copy #t)}} the returned array has the generic
343storage class by default.
344
345Similar:  [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.mean.html#numpy.ndarray.mean|mean]]
346
347<procedure>(array-var a #!key axis (dtype generic-storage-class))</procedure>
348Returns the variance of the array elements, along given axis.
349
350'''DIFF''': This procedure will return variance as exact numbers where
351possible, so on {{(array-default-copy #t)}} the returned array has the generic
352storage class by default.
353
354Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.var.html#numpy.ndarray.var|ndarray.var]]
355
356<procedure>(array-std a #!key axis (dtype generic-storage-class))</procedure>
357Returns the standard deviation of the array elements along given axis.
358
359Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.std.html#numpy.ndarray.std|ndarray.std]]
360
361<procedure>(array-prod a #!key axis (dtype generic-storage-class))</procedure>
362Return the product of the array elements over the given axis
363
364'''DIFF''': Because the result may overflow, on {{(array-default-copy
365#t)}} the returned array has the generic storage class by default.
366
367Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.prod.html#numpy.ndarray.prod|ndarray.prod]]
368
369<procedure>(array-all a #!key axis (dtype generic-storage-class))</procedure>
370Returns {{#t}} if all elements are not {{#f}}.
371
372'''DIFF''': This will return slightly different results than numpy's array-all
373because while scheme and python both have "truthiness" (non #t or True
374objects evaluate as true for boolean operations), scheme does not have Python's
375"falsiness" for values like {{0}}, {{[]}}, {{""}}, etc.
376
377Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.all.html#numpy.ndarray.all|ndarray.all]]
378
379<procedure>(array-any* a #!key axis (dtype generic-storage-class))</procedure>
380Returns {{#t}} if any of the elements is not {{#f}}. Note the {{*}} in the
381name to avoid shadowing srfi-179's {{array-any}}.
382
383'''DIFF''': This will return slightly different results than numpy's
384array-all because while scheme and python both have "truthiness" (objects
385that are not #t or True evaluate as true for boolean operations), scheme does
386not have Python's "falsiness" for values like {{0}}, {{[]}}, {{""}}, etc.
387
388Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.ndarray.any.html#numpy.ndarray.any|ndarray.any]]
389
390=== Array I/O
391
392==== Read/write syntax
393
394The special read syntax {{'#!a(...)}} is an alias for {{(array '(...))}}.
395You can also specify a dtype, like so: {{'#![dtype]a(...)}} (e.g.
396{{'#!f64a(...)}}). Arrays are by default printed in the dtype syntax, with
397non-specialized arrays printed as {{'#!*a(...)}}, which currently does not have
398an equivalent read syntax.
399
400==== Procedures
401
402<procedure>(write-array a #!optional (port (current-output-port)) #!key mode summarize)</procedure>
403Writes an array to {{port}}. If {{#:summarize}} is {{#t}}, prints a
404summary of the array according to the {{array-summary-*}} parameters. The
405keyword argument {{#:mode}} is currently a no-op, but there are plans to use it
406for writing arrays in e.g. binary or ascii format.
407
408==== Parameters
409
410<constant>array-summary-maxsize</constant>
411Sets the maximum size that an array can be beyond which its printed form
412is summarized.
413
414<constant>array-summary-num-edges</constant>
415Sets the number of edges to display for a summarized array.
416
417<constant>array-summary-flonum-precision</constant>
418Sets the array flounm print precision for a summarized array.
419
420=== Constants
421
422<constant>inf</constant>
423An alias for +inf.0
424
425<constant>ninf</constant>
426An alias for -inf.0
427
428<constant>nan</constant>
429An alias for +nan.0
430
431<constant>nzero</constant>
432An alias for -0.0
433
434<constant>e</constant>
435Euler's number
436
437<constant>euler-gamma</constant>
438Euler-Mascheroni constant
439
440<constant>pi</constant>
441=== Mathematical functions
442
443<procedure>(conj a) → (or array number)</procedure>
444Returns the complex conjugate of a number or element-wise complex conjugate of
445an array.
446
447Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.conj.html|numpy.conj]]
448
449<procedure>(round a #!optional b) → (or array number)</procedure>
450Evenly rounds the number (or array, element-wise) to {{n}} decimals. If {{n}}
451is negative, rounds to nearest tens, hundreds, etc.
452
453Similar: [[https://numpy.org/doc/stable/reference/generated/numpy.around.html|numpy.around]]
454
455<procedure>(around a #!optional n)</procedure>
456The same as {{round}}.
457
458<procedure>(add a b #!key dtype) → (or array number)</procedure>
459Adds arguments element-wise. Arguments can be arrays or numbers. A
460resulting array's dtype is determined by the dtype conversion table if
461{{dtype}} is not given.
462
463=== Repository
464
465[[https://code.dieggsy.com/numchi]]
466
467=== Author
468Diego A. Mundo
469
470=== License
471BSD
472
473 Copyright (c) 2021 Diego A. Mundo
474 All rights reserved.
475 
476 Redistribution and use in source and binary forms, with or without
477 modification, are permitted (subject to the limitations in the disclaimer
478 below) provided that the following conditions are met:
479 
480      * Redistributions of source code must retain the above copyright notice,
481        this list of conditions and the following disclaimer.
482 
483      * Redistributions in binary form must reproduce the above copyright
484        notice, this list of conditions and the following disclaimer in the
485        documentation and/or other materials provided with the distribution.
486 
487      * Neither the name of the copyright holder nor the names of its
488        contributors may be used to endorse or promote products derived from
489        this software without specific prior written permission.
490 
491 NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS
492 LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
493 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
494 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
495 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
496 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
497 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
498 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
499 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
500 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
501 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Note: See TracBrowser for help on using the repository browser.