3. Currently available features¶
Numscrypt currently supports:
- ndarray with
- one or two dimensions
- dtype int32, float32, float64, complex32 and complex64
- indexing
- simple and extended slicing
- astype
- tolist
- real
- imag
- __repr__ and __str__
- transpose
- overloaded operators: * / + - @, mixing of ndarray and scalar expressions
- empty, array, copy
- hsplit, vsplit
- hstack, vstack
- zeros, ones, identity
- linalg with
- matrix inversion
- eigenvalue / eigenvector decomposition
- FFT with
- FFT for 2^n complex samples
- IFFT for 2^n complex samples
- FFT2 for 2^n x 2^n complex samples
- IFFT2 for 2^n x 2^n complex samples
Note that all operations where the distinction between row vectors and column vectors matters, only work on ndarrays with two dimensions. Such operations are e.g. @, hstack, vstack, hsplit and vsplit. When used with these operations, a row vector should have shape (1,n) and a column vector should have shape (n,1). Furthermore views and broadcasting are not supported.
4. Systematic code examples: a guided tour of Numscrypt¶
One ready-to-run code example is worth more than ten lengthy descriptions. The autotest and demo suite, that is part of the distribution, is a collection of sourcecode fragments called testlets. These testlets are used for automated regression testing of Numscrypt against NumPy. Since they systematically cover all the library constructs, they are also very effective as a learning tool. The testlets are arranged alphabetically by subject.
import org.transcrypt.autotester
import basics
import module_linalg
import module_fft
autoTester = org.transcrypt.autotester.AutoTester ()
autoTester.run (basics, 'basics')
autoTester.run (module_linalg, 'module_linalg')
autoTester.run (module_fft, 'module_fft')
autoTester.done ()
4.1. Basics: creating and using arrays¶
from org.transcrypt.stubs.browser import *
from org.transcrypt.stubs.browser import __main__, __envir__, __pragma__
# Imports for Transcrypt, skipped runtime by CPython
if __envir__.executor_name == __envir__.transpiler_name:
import numscrypt as num
# Imports for CPython, skipped compile time by Transcrypt
__pragma__ ('skip')
import numpy as num
__pragma__ ('noskip')
def run (autoTester):
z = num.zeros ((4, 3), 'int32')
autoTester.check ('Zeros', z.tolist (), '<br>')
o = num.ones ((4, 5))
autoTester.check ('Ones', o.astype ('int32') .tolist ())
i = num.identity (3, 'int32')
autoTester.check ('Identity', i.tolist (), '<br>')
a = num.array ([
[1, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 10, 12]
])
autoTester.check ('Matrix a', a.tolist (), '<br>')
autoTester.check ('Transpose of a', a.transpose () .tolist (), '<br>')
b = num.array ([
[2, 2, 4, 6],
[8, 10, 12, 14],
[16, 18, 20, 24]
])
bp = b.transpose ()
autoTester.check ('Matrix b', b.tolist (), '<br>')
autoTester.check ('Permutation of b', bp.tolist (), '<br>')
c = num.array ([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
], 'int32')
autoTester.check ('Shape c', tuple (c.shape), '<br>')
autoTester.check ('Matrix c', c.tolist (), '<br>')
ct = c.transpose ()
autoTester.check ('Shape ct', tuple (ct.shape), '<br>')
autoTester.check ('Transpose of c', ct .tolist (), '<br>')
cs0, cs1 = num.hsplit (c, 2)
autoTester.check ('Matrix cs0', cs0.tolist (), '<br>')
autoTester.check ('Matrix cs1', cs1.tolist (), '<br>')
ci = num.hstack ((cs1, cs0))
autoTester.check ('Matrix ci', ci.tolist (), '<br>')
cts0, cts1, cts2 = num.hsplit (ct, 3)
autoTester.check ('Matrix cts0', cts0.tolist (), '<br>')
autoTester.check ('Matrix cts1', cts1.tolist (), '<br>')
autoTester.check ('Matrix cts2', cts2.tolist (), '<br>')
cti = num.hstack ((cts2, cts1, cts0))
autoTester.check ('Matrix ci', cti.tolist (), '<br>')
d = num.array ([
[13, 14],
[15, 16],
[17, 18],
[19, 20]
], 'int32')
autoTester.check ('Matrix d', d.tolist (), '<br>')
dt = d.transpose ()
autoTester.check ('Permutation of d', dt.tolist (), '<br>')
ds0, ds1, ds2, ds3 = num.vsplit (d, 4)
autoTester.check ('Matrix ds0', ds0.tolist (), '<br>')
autoTester.check ('Matrix ds1', ds1.tolist (), '<br>')
autoTester.check ('Matrix ds2', ds2.tolist (), '<br>')
autoTester.check ('Matrix ds3', ds3.tolist (), '<br>')
di = num.vstack ((ds3, ds2, ds1, ds0))
autoTester.check ('Matrix di', di.tolist (), '<br>')
dts0, dts1 = num.vsplit (dt, 2)
autoTester.check ('Matrix dts0', dts0.tolist (), '<br>')
autoTester.check ('Matrix dts1', dts1.tolist (), '<br>')
dti = num.vstack ((dts1, dts0))
autoTester.check ('Matrix dti', dti.tolist (), '<br>')
v0 = num.array (range (10))
v1 = num.array ((1, 2, 3, 1, 2, 3, 1, 2, 3, 1))
__pragma__ ('opov')
a [1, 0] = 177
el = b [1, 2]
bsl0 = b [1, 1 : 3]
bsl1 = b [1 : 2, 1 : 3]
bsl2 = b [1 : 2, 1]
bsl3 = b [1, 1 : 3]
bsl4 = b [ : , 1]
bsl5 = b [1, 1 : 3]
bsl6 = b [1, 1 : 3]
bsl7 = b [1, 2 : 3]
bpsl0 = bp [1, 1 : 3]
bpsl1 = bp [1 : 2, 1 : 3]
bpsl2 = bp [1, 0 : ]
bpsl3 = bp [1, 1 : 3]
bpsl4 = bp [ : , 1]
bpsl5 = bp [3, 1 : 3]
bpsl6 = bp [2 : 4, 1 : 3]
bpsl7 = bp [2 : 4, 2 : 3]
sum = a + b
dif = a - b
prod = a * b
quot = a / b
dot = c @ d
vsum = v0 + v1
vel = vsum [6]
vsum [6] = 70
mul_a3 = a * 3
mul_3a = 3 * a
div_a3 = a / 3.1234567
div_3a = 3.1234567 / a
add_a3 = a + 3
add_3a = 3 + a
sub_a3 = a - 3
sub_3a = 3 - a
neg_a = -a
__pragma__ ('noopov')
autoTester.check ('El a [1, 2, 3] alt', a.tolist (), '<br>')
autoTester.check ('El b [1, 2, 3]', el, '<br>')
autoTester.check ('Sl b0', bsl0.tolist (), '<br>')
autoTester.check ('Sl b1', bsl1.tolist (), '<br>')
autoTester.check ('Sl b2', bsl2.tolist (), '<br>')
autoTester.check ('Sl b3', bsl3.tolist (), '<br>')
autoTester.check ('Sl b4', bsl4.tolist (), '<br>')
autoTester.check ('Sl b5', bsl5.tolist (), '<br>')
autoTester.check ('Sl b6', bsl6.tolist (), '<br>')
autoTester.check ('Sl b7', bsl7.tolist (), '<br>')
autoTester.check ('Sl bp0', bpsl0.tolist (), '<br>')
autoTester.check ('Sl bp1', bpsl1.tolist (), '<br>')
autoTester.check ('Sl bp2', bpsl2.tolist (), '<br>')
autoTester.check ('Sl bp3', bpsl3.tolist (), '<br>')
autoTester.check ('Sl bp4', bpsl4.tolist (), '<br>')
autoTester.check ('Sl bp5', bpsl5.tolist (), '<br>')
autoTester.check ('Sl bp6', bpsl6.tolist (), '<br>')
autoTester.check ('Sl bp7', bpsl7.tolist (), '<br>')
autoTester.check ('Matrix sum', sum.tolist (), '<br>')
autoTester.check ('Matrix difference', dif.tolist (), '<br>')
autoTester.check ('Matrix product', prod.tolist (), '<br>')
autoTester.check ('Matrix quotient', quot.tolist (), '<br>')
autoTester.check ('Matrix dotproduct', dot.tolist (), '<br>')
autoTester.check ('Vector', v0.tolist (), '<br>')
autoTester.check ('Vector', v1.tolist (), '<br>')
autoTester.check ('El sum old', vel, '<br>')
autoTester.check ('Vector sum new', vsum.tolist (), '<br>')
autoTester.check ('mul_a3', mul_a3.tolist (), '<br>')
autoTester.check ('mul_3a', mul_3a.tolist (), '<br>')
autoTester.check ('div_a3', num.round (div_a3, 2).tolist (), '<br>')
autoTester.check ('div_3a', num.round (div_3a, 2).tolist (), '<br>')
autoTester.check ('add_a3', add_a3.tolist (), '<br>')
autoTester.check ('add_3a', add_3a.tolist (), '<br>')
autoTester.check ('sub_a3', sub_a3.tolist (), '<br>')
autoTester.check ('sub_3a', sub_3a.tolist (), '<br>')
autoTester.check ('neg_a', neg_a.tolist (), '<br>')
__pragma__ ('opov')
comp_a = num.array ([
[1 + 2j, 2 - 1j, 3],
[4, 5 + 3j, 7]
], 'complex128')
comp_b = num.array ([
[6, 8 - 1j],
[9 + 3j, 10],
[11, 12 - 6j]
], 'complex128')
comp_c = comp_a @ comp_b
__pragma__ ('noopov')
autoTester.check ('comp_a', comp_a.tolist (), '<br>')
autoTester.check ('comp_b', comp_b.tolist (), '<br>')
autoTester.check ('comp_c', comp_c.tolist (), '<br>')
__pragma__ ('opov')
comp_a_square = comp_a [ : , : 2]
comp_b_square = comp_b [1 : , : ]
comp_c_square = comp_a_square * comp_b_square
comp_d_square = comp_a_square / comp_b_square
comp_e_square = comp_a_square + comp_b_square
comp_f_square = comp_a_square - comp_b_square
__pragma__ ('noopov')
autoTester.check ('comp_a_square', comp_a_square.tolist (), '<br>')
autoTester.check ('comp_b_square', comp_b_square.tolist (), '<br>')
autoTester.check ('comp_c_square', comp_c_square.tolist (), '<br>')
autoTester.check ('comp_d_square', num.round (comp_d_square, 2).tolist (), '<br>')
autoTester.check ('comp_e_square', comp_e_square.tolist (), '<br>')
autoTester.check ('comp_f_square', comp_f_square.tolist (), '<br>')
__pragma__ ('opov')
sliceable_a = num.array ([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
])
autoTester.check ('sliceable_a', sliceable_a.tolist ())
slice_a = sliceable_a [1 : , 1 : ]
autoTester.check ('slice_a')
sliceable_at = sliceable_a.transpose ()
slice_at = sliceable_at [1 : ]
__pragma__ ('noopov')
4.2. Linalg: matrix inversion and eigen decomposition¶
from org.transcrypt.stubs.browser import *
from org.transcrypt.stubs.browser import __main__, __envir__, __pragma__
# Imports for Transcrypt, skipped run time by CPython
if __envir__.executor_name == __envir__.transpiler_name:
import numscrypt as num
import numscrypt.linalg as linalg
# Imports for CPython, skipped compile time by Transcrypt
__pragma__ ('skip')
import numpy as num
import numpy.linalg as linalg
num.set_printoptions (linewidth = 240)
__pragma__ ('noskip')
def run (autoTester):
autoTester.check ('====== inverse ======')
# Real
r = num.array ([
[2.12, -2.11, -1.23],
[2.31, 1.14, 3.15],
[1.13, 1.98, 2.81]
])
autoTester.check ('Matrix r', num.round (r, 2) .tolist (), '<br>')
ri = linalg.inv (r)
autoTester.check ('Matrix ri', num.round (ri, 2) .tolist (), '<br>')
__pragma__ ('opov')
rid = r @ ri
__pragma__ ('noopov')
autoTester.check ('r @ ri', [[int (round (elem)) for elem in row] for row in rid.tolist ()], '<br>')
__pragma__ ('opov')
delta = 0.001
autoTester.check ('r * r', num.round (r * r + delta, 3) .tolist (), '<br>')
autoTester.check ('r / r', num.round (r / r + delta, 3) .tolist (), '<br>')
autoTester.check ('r + r', num.round (r + r + delta, 3) .tolist (), '<br>')
autoTester.check ('r - r', num.round (r - r + delta, 3) .tolist (), '<br>')
__pragma__ ('noopov')
# Complex
__pragma__ ('opov')
c = num.array ([
[2.12 - 3.15j, -2.11, -1.23],
[2.31, 1.14, 3.15 + 2.75j],
[1.13, 1.98 - 4.33j, 2.81]
], 'complex128')
__pragma__ ('noopov')
autoTester.check ('Matrix c', num.round (c, 2) .tolist (), '<br>')
ci = linalg.inv (c)
autoTester.check ('Matrix ci', num.round (ci, 2) .tolist (), '<br>')
__pragma__ ('opov')
cid = c @ ci
__pragma__ ('noopov')
# autoTester.check ('c @ ci', [['{} + j{}'.format (int (round (elem.real)), int (round (elem.imag))) for elem in row] for row in cid.tolist ()], '<br>')
__pragma__ ('opov')
delta = 0.001 + 0.001j
autoTester.check ('c * c', num.round (c * c + delta , 3) .tolist (), '<br>')
autoTester.check ('c / c', num.round (c / c + delta, 3) .tolist (), '<br>')
autoTester.check ('c + c', num.round (c + c + delta, 3) .tolist (), '<br>')
autoTester.check ('c - c', num.round (c - c + delta, 3) .tolist (), '<br>')
__pragma__ ('noopov')
autoTester.check ('====== eigen ======')
__pragma__ ('opov')
for a in (
num.array ([
[0, 1j],
[-1j, 1]
], 'complex128'),
num.array ([
[1, -2, 3, 1],
[5, 8, -1, -5],
[2, 1, 1, 100],
[2, 1, -1, 0]
], 'complex128'),
):
eVals, eVecs = linalg.eig (a)
enumSorted = sorted (
enumerate (eVals.tolist ()),
key = lambda elem: -(elem [1].real + elem [1].imag / 1000) # Order on primarily on real, secundarily on imag, note conjugate vals
)
indicesSorted = [elem [0] for elem in enumSorted]
eValsSorted = [elem [1] for elem in enumSorted]
eValsMat = num.empty (a.shape, a.dtype)
for iRow in range (a.shape [0]):
for iCol in range (a.shape [1]):
eValsMat [iRow, iCol] = eVals [iCol]
eVecsNorms = num.empty ((eVecs.shape [1], ), a.dtype)
for iNorm in range (eVecsNorms.shape [0]):
eVecsNorms [iNorm] = complex (linalg.norm (eVecs [:, iNorm]))
eVecsCanon = num.empty (a.shape, a.dtype)
for iRow in range (a.shape [0]):
for iCol in range (a.shape [1]):
eVecsCanon [iRow, iCol] = eVecs [iRow, iCol] / eVecs [0, iCol]
eVecsSorted = num.empty (a.shape, a.dtype)
for iRow in range (a.shape [0]):
for iCol in range (a.shape [1]):
eVecsSorted [iRow, iCol] = eVecsCanon [iRow, indicesSorted [iCol]]
'''
autoTester.check ('\n---------------- a ----------------------')
autoTester.check (a)
autoTester.check ('\n---------------- eigVals ----------------')
autoTester.check (eVals)
autoTester.check ('\n---------------- eigValsMat--------------')
autoTester.check (eValsMat)
autoTester.check ('\n---------------- eigVecs ----------------')
autoTester.check (eVecs)
autoTester.check ('\n---------------- eigValsMat @ eigVecs ---')
autoTester.check (eValsMat * eVecs)
autoTester.check ('\n---------------- a @ eigVecs-------------')
autoTester.check (a @ eVecs)
autoTester.check ('\n---------------- eigVecsNorms -----------')
autoTester.check (eVecsNorms)
autoTester.check ('\n---------------- eigVecsCanon -----------')
autoTester.check (eVecsCanon)
'''
autoTester.check ('\n---------------- eigVecsSorted ----------')
autoTester.check ([[(round (value.real + 1e-3, 3), round (value.imag + 1e-3, 3)) for value in row] for row in eVecsSorted.tolist ()])
autoTester.check ('\n---------------- eigValsSorted ----------')
autoTester.check ([(round (value.real + 1e-3, 3), round (value.imag + 1e-3, 3)) for value in eValsSorted], '\n')
4.3. Fourier transform: FFT(2) and IFFT(2) for 2^n (x 2^n) samples, using complex arrays¶
from org.transcrypt.stubs.browser import *
from org.transcrypt.stubs.browser import __main__, __envir__, __pragma__
from math import sin, cos, pi
transpiled = __envir__.executor_name == __envir__.transpiler_name
# Imports for Transcrypt, skipped run time by CPython
if __envir__.executor_name == __envir__.transpiler_name:
import numscrypt as num
import numscrypt.fft as fft
# Imports for CPython, skipped compile time by Transcrypt
__pragma__ ('skip')
import numpy as num
import numpy.fft as fft
__pragma__ ('noskip')
fSample = 4096
tTotal = 2
fSin = 30
fCos = 50
def getNow (): # Avoid operator overloading, which would result in the dysfunctional: __new__ __call__ (Date)
return __new__ (Date ())
def tCurrent (iCurrent):
return iCurrent / fSample
def run (autoTester):
__pragma__ ('opov')
delta = 0.001 + 0.001j
__pragma__ ('noopov')
autoTester.check ('<br>------ 1D ------<br>')
cut = 102
autoTester.check ('Samples computed: {}<br>'.format (tTotal * fSample))
autoTester.check ('Samples shown: {}<br>'.format (cut))
orig = num.array ([
complex (0.3 + sin (2 * pi * fSin * t) + 0.5 * cos (2 * pi * fCos * t), 0)
for t in [
iSample / fSample
for iSample in range (tTotal * fSample)
]
], 'complex128')
__pragma__ ('opov')
autoTester.check ('Original samples', num.round (orig + delta, 3) .tolist ()[ : cut], '<br>')
if transpiled:
timeStartFft = getNow ()
freqs = fft.fft (orig)
if transpiled:
timeStopFft = getNow ()
autoTester.check ('Frequencies', num.round (freqs + delta, 3) .tolist ()[ : cut], '<br>')
if transpiled:
timeStartIfft = getNow ()
reconstr = fft.ifft (freqs)
if transpiled:
timeStopIfft = getNow ()
autoTester.check ('Reconstructed samples', num.round (reconstr + delta, 3) .tolist ()[ : cut], '<br>')
__pragma__ ('noopov')
if transpiled:
print ('FFT for {} samples took {} ms'.format (tTotal * fSample, timeStopFft - timeStartFft))
print ('IFFT for {} samples took {} ms'.format (tTotal * fSample, timeStopIfft - timeStartIfft))
autoTester.check ('<br>------ 2D ------<br>')
__pragma__ ('opov')
orig2 = num.zeros ((128, 128), 'complex128')
orig2 [32 : 96, 32 : 96] = num.ones ((64, 64), 'complex128')
autoTester.check ('Original samples', num.round (orig2 + delta, 3) [64 : 68, 16 : 112] .tolist (), '<br>')
if transpiled:
timeStartFft = getNow ()
freqs2 = fft.fft2 (orig2)
if transpiled:
timeStopFft = getNow ()
autoTester.check ('Frequencies', num.round (freqs2 + delta, 3) [64 : 68, 16 : 112] .tolist (), '<br>')
if transpiled:
timeStartIfft = getNow ()
reconstr2 = fft.ifft2 (freqs2)
if transpiled:
timeStopIfft = getNow ()
if transpiled:
print ('FFT2 for {} samples took {} ms'.format (orig2.size, timeStopFft - timeStartFft))
print ('IFFT2 for {} samples took {} ms'.format (orig2.size, timeStopIfft - timeStartIfft))
autoTester.check ('Reconstructed samples', num.round (reconstr2 + delta, 3) [64 : 68, 16 : 112] .tolist (), '<br>')
__pragma__ ('noopov')
5. Some more examples: interactive tests¶
5.1. Benchmark¶
Performance of operations like @ and inv
from org.transcrypt.stubs.browser import *
from org.transcrypt.stubs.browser import __pragma__
import numscrypt as num
import numscrypt.random as num_rand
import numscrypt.linalg as linalg
import random
result = ''
for useComplex in (False, True):
for transpose in (False, True):
if useComplex:
a = num.array ([
[complex (random.random (), random.random ()) for iCol in range (30)]
for iRow in range (30)
], 'complex128')
else:
a = num_rand.rand (30, 30)
timeStartTranspose = __new__ (Date ())
if transpose:
a = a.transpose ()
timeStartInv = __new__ (Date ())
ai = linalg.inv (a)
timeStartMul = __new__ (Date ())
__pragma__ ('opov')
id = a @ ai
__pragma__ ('noopov')
timeStartScalp = __new__ (Date ())
__pragma__ ('opov')
sp = a * a
__pragma__ ('noopov')
timeStartDiv = __new__ (Date ())
__pragma__ ('opov')
sp = a / a
__pragma__ ('noopov')
timeStartAdd = __new__ (Date ())
__pragma__ ('opov')
sp = a + a
__pragma__ ('noopov')
timeStartSub = __new__ (Date ())
__pragma__ ('opov')
sp = a - a
__pragma__ ('noopov')
timeStartEig = __new__ (Date ())
if useComplex:
evals, evecs = linalg.eig (a [:10, :10])
timeEnd = __new__ (Date ())
result += (
'''
<pre>
a @ ai [0:5, 0:5] =
{}
'''
) .format (
str (num.round (id [0:5, 0:5], 2)) .replace (' ', '\t'),
)
if transpose:
result += (
'''
Transpose took: {} ms'''
).format (
timeStartInv - timeStartTranspose
)
result += (
'''
Inverse took: {} ms
Matrix product (@) took: {} ms
Elementwise product (*) took: {} ms
Division took: {} ms
Addition took: {} ms
Subtraction took: {} ms
Eigenvals/vecs took: {} ms
</pre>
'''
) .format (
timeStartMul - timeStartInv,
timeStartScalp - timeStartMul,
timeStartDiv - timeStartScalp,
timeStartAdd - timeStartDiv,
timeStartSub - timeStartAdd,
timeStartEig - timeStartSub,
timeEnd - timeStartEig if useComplex else 'N.A.'
)
document.getElementById ('result') .innerHTML = result