# 3. Currently available features

Numscrypt currently supports:

- ns_settings.optim_space setting added, default is False. Setting it to True is DISADVISED, since it will result in slow code.

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

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

Autotest: Numcrypt autotest demo suite

    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

Testlet: basics

    from org.transcrypt.stubs.browser import *
    from org.transcrypt.stubs.browser import __main__, __envir__, __pragma__

    # Imports for Transcrypt, resolved run time
    if __envir__.executor_name == __envir__.transpiler_name:
        import numscrypt as num

    # Imports for CPython, resolved compile time
    __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>')

        # shape: 2 blocks x 3 rows x 4 columns

        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

Testlet: module_linalg

    from org.transcrypt.stubs.browser import *
    from org.transcrypt.stubs.browser import __main__, __envir__, __pragma__

    # Imports for Transcrypt, resolved run time
    if __envir__.executor_name == __envir__.transpiler_name:
        import numscrypt as num
        import numscrypt.linalg as linalg

    # Imports for CPython, resolved compile time
    __pragma__ ('skip')
    import numpy as num
    import numpy.linalg as linalg
    __pragma__ ('noskip')

    def run (autoTester):

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

## 4.3. Fourier transform: FFT(2) and IFFT(2) for 2^n (x 2^n) samples, using complex arrays

Testlet: module_fft

    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, resolved run time
    if __envir__.executor_name == __envir__.transpiler_name:
        import numscrypt as num
        import numscrypt.fft as fft

    # Imports for CPython, resolved compile time
    __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. ns_settings.optimize_space

For time critical operations like @ and inv, slicing operations are avoided. For @ this happens by copying arrays to ‘natural stride order’. Setting ns_settings.optimize_space to True will avoid this copying to save memory space. In general this is DISADVISED, since it will considerably slow down execution of the @ operator, which is O (n^3).

Benchmark: slicing_optimization

    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')

            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
    </pre>
    '''
            ) .format (
                timeStartMul - timeStartInv,
                timeStartScalp - timeStartMul,
                timeStartDiv - timeStartScalp,
                timeStartAdd - timeStartDiv,
                timeStartSub - timeStartAdd,
                timeEnd - timeStartSub
            )

    document.getElementById ('result') .innerHTML = result