####################################################################################################

import math

import pytest

import stomasimulator.geom.point as p
import stomasimulator.geom.geom_utils as g

####################################################################################################

class TestMethods( object ):
    ## Test normal vector computations

    def test_scalar_product( self ):
        p1 = p.Point( 1, 2, 3 )
        p2 = p.Point( 2, 3, 4 )

        assert g.scalar_product( p1, p2 ) == 1. * 2. + 2. * 3. + 3. * 4.

    def test_vector_prod( self ):
        # Two vectors corresponding to edges of tetrahedron in the positive octant
        v1 = p.Point( 2, 0, -2 )
        v2 = p.Point( 0, 2, -2 )

        assert g.vector_product( v1, v2 ) == p.Point( 4, 4, 4 )

    def test_vector_prod_unit( self ):
        # Two vectors corresponding to edges of tetrahedron in the positive octant
        v1 = p.Point( 2, 0, -2 )
        v2 = p.Point( 0, 2, -2 )

        k = 4. * math.sqrt( 3. )
        assert g.vector_product( v1, v2 ).unit() == p.Point( 4. / k, 4. / k, 4. / k )

    # can't compute normal if points are collinear
    def test_collinear( self ):
        pts = [ p.Point( 0, 0, 0 ), p.Point( 1, 0, 0 ), p.Point( 2, 0, 0 ) ]

        with pytest.raises( ArithmeticError ):
            g.calc_unit_normal( *pts )

    # normal should be in x direction for points in y-z plane
    def test_normal_x( self ):
        pts = [ p.Point( 0, 2, 0 ), p.Point( 0, 0, 2 ), p.Point( 0, -1, -1 ) ]

        assert g.calc_unit_normal( *pts ) == p.Point( 1, 0, 0 )

    # normal should be in y direction for points in x-z plane
    def test_normal_y( self ):
        pts = [ p.Point( -2, 0, 0 ), p.Point( 0, 0, 4 ), p.Point( 1, 0, -1 ) ]

        assert g.calc_unit_normal( *pts ) == p.Point( 0, 1, 0 )

    # normal should be in z direction for points in x-y plane
    def test_normal_z( self ):
        pts = ( p.Point( 1, 1, 0 ), p.Point( 2, 1, 0 ), p.Point( 4, 4, 0 ) )

        assert g.calc_unit_normal( *pts ) == p.Point( 0, 0, 1 )

    ## Volumes and areas

    def test_tetrahedron_volume( self ):
        pts = ( p.Point( 1, 0, 0 ), p.Point( 0, 1, 0 ), p.Point( 0, 0, 0 ), p.Point( 0, 0, 1 ) )

        assert g.calc_tetrahedron_volume( *pts ) == 1. / 6

    def test_triangle_area( self ):
        pts = ( p.Point( 1, 0, 0 ), p.Point( 0, 1, 0 ), p.Point( 1, 1, 0 ) )

        assert g.calculate_polygon_area( pts ) == 0.5

    def test_triangle_area_1( self ):
        pts = ( p.Point( 1, 0, 0 ), p.Point( 0, 1, 0 ), p.Point( 0, 0, 1 ) )

        assert g.calculate_polygon_area( pts ) == math.sqrt( 3. ) / 2.

    def test_area_sq_open( self ):
        c = ( p.Point( 0, 0, 0 ), p.Point( 2, 0, 0 ), p.Point( 2, 2, 0 ), p.Point( 0, 2, 0 ) )

        assert g.calculate_polygon_area( c ) == 4.

    def test_area_sq_closed( self ):
        c = [ p.Point( -2, -2, 0 ), p.Point( -1, -2, 0 ), p.Point( -1, -1, 0 ), p.Point( -2, -1, 0 ) ]

        c.append( c[ 0 ] )

        assert g.calculate_polygon_area( c ) == 1.

    def test_area_tri_open( self ):
        c = [ p.Point( 1, 1, 0 ), p.Point( 2, 1, 0 ), p.Point( 2, 2, 0 ) ]

        assert g.calculate_polygon_area( c ) == 0.5

    def test_area_tri_closed( self ):
        c = [ p.Point( 1, 1, 0 ), p.Point( 2, 1, 0 ), p.Point( 2, 2, 0 ) ]
        c.append( c[ 0 ] )

        assert g.calculate_polygon_area( c ) == 0.5

    def test_area_normal_towards_x( self ):
        c = [ p.Point( 3, -1, 0 ), p.Point( 3, 1, 0 ), p.Point( 0, 1, 4 ),
              p.Point( 0, -1, 4 ) ]

        assert g.calculate_polygon_area( c ) == 10.

    def test_area_normal_towards_y( self ):
        c = [ p.Point( 1, 3, 0 ), p.Point( -1, 3, 0 ), p.Point( -1, 0, 4 ),
              p.Point( 1, 0, 4 ) ]

        assert g.calculate_polygon_area( c ) == 10.

    def test_area_normal_towards_z( self ):
        c = [ p.Point( 3, -1, 0 ), p.Point( 3, 1, 0 ), p.Point( 0, 1, 1 ),
              p.Point( 0, -1, 1 ) ]

        assert g.calculate_polygon_area( c ) == 2. * math.sqrt( 10. )

    def test_calculate_euler_characteristic_1( self ):
        """
        Square     1--2
                   |  |
                   4--3
        """
        surface = ( (1, 2, 3, 4), )
        chi, d1, d2 = g.calculate_euler_characteristic( surface )

        assert chi == 1

    def test_calculate_euler_characteristic_2( self ):
        """
        Square     1--2--3
                   |  |  |
                   4--5--6
                   |  |  |
                   7--8--9
        """
        surface = ( (1, 2, 5, 4), (2, 3, 6, 5), (4, 5, 8, 7), (5, 6, 9, 8) )
        chi, d1, d2 = g.calculate_euler_characteristic( surface )

        assert chi == 1

    def test_calculate_euler_characteristic_3( self ):
        """
        Cube (hidden vertex is 5)
                  8--7
                 /  /|
                4--3 6
                |  |/
                1--2
        """
        surface = (
        (1, 2, 3, 4), (5, 6, 7, 8), (2, 6, 7, 3), (1, 5, 8, 4), (1, 2, 6, 5), (4, 3, 7, 8) )
        chi, d1, d2 = g.calculate_euler_characteristic( surface )

        assert chi == 2

    def test_calculate_euler_characteristic_4( self ):
        """
        Hole-y cube, i.e. topologically equivalent to a cylinder
        Hidden vertex is 5 - (1,2,3,4) and (5,6,7,8) are not faces)
                  8--7
                 /  /|
                4--3 6
                |  |/
                1--2
        """
        surface = ( (2, 6, 7, 3), (1, 5, 8, 4), (1, 2, 6, 5), (4, 3, 7, 8) )
        chi, d1, d2 = g.calculate_euler_characteristic( surface )

        assert chi == 0

    # v. simple volume calculations

    def test_vol_tetrahedron( self ):
        p1 = p.Point( 1, 0, 0 )
        p2 = p.Point( 0, 1, 0 )
        p3 = p.Point( 0, 0, 0 )
        p4 = p.Point( 0, 0, 1 )

        volume = g.calc_tetrahedron_volume( p1, p2, p3, p4 )

        assert volume == 1.0 / 6.0

    def test_vol_tetrahedron_1( self ):
        # Volume of a tetrahedron but using the general faces and vertices formula

        p1 = p.Point( 1, 0, 0, 1 )
        p2 = p.Point( 0, 1, 0, 2 )
        p3 = p.Point( 0, 0, 0, 3 )
        p4 = p.Point( 0, 0, 1, 4 )

        vertices = { 1: p1, 2: p2, 3: p3, 4: p4 }

        # Need a list of faces: vertices are labelled 1,2,3,4 so faces are 1-2-4, 2-3-4, 1-4-3 and 1-3-2
        # (all traversed anti-clockwise when looking from outside).
        #
        faces = ( (1, 2, 4), (2, 3, 4), (1, 4, 3), (1, 3, 2) )

        volume = g.calculate_volume( vertices, faces )

        assert volume == 1.0 / 6.0

    def test_vol_irregular_cuboid_1( self ):
        # Calculate volume of irregular rectangular cuboid using projection method
        # - node indices are as per FEBio representation of 8-node element

        a, b, c = 2., 3., 4.

        vertices, faces = self.get_cuboid( a, b, c )

        vertices_dict = dict( )
        for vertex in vertices:
            vertices_dict[ vertex.id ] = vertex

        vol = g.calculate_volume( vertices_dict, faces )

        assert vol == a * b * c

    def test_vol_irregular_cuboid_2( self ):
        # Calculate volume of irregular rectangular cuboid using projection method
        # - node indices are as per FEBio representation of 8-node element

        a, b, c = 2., 3., 4.

        vertices, faces = self.get_cuboid( a, b, c )

        vv = g.rotate_about_x( vertices, 45 )

        # id is not preserved so use the one from above
        vertex_dict = dict( )
        for idx in range( len( vertices ) ):
            vertex_dict[ vertices[ idx ].id ] = vv[ idx ]

        vol = g.calculate_volume( vertex_dict, faces )

        assert vol == a * b * c

    def get_cuboid( self, a, b, c ):

        # node indices are as per FEBio representation of 8-node element

        vertices = list()
        vertices.append( p.Point( a, 0, 0 ) )
        vertices.append( p.Point( a, b, 0 ) )
        vertices.append( p.Point( 0, b, 0 ) )
        vertices.append( p.Point( 0, 0, 0 ) )
        vertices.append( p.Point( a, 0, c ) )
        vertices.append( p.Point( a, b, c ) )
        vertices.append( p.Point( 0, b, c ) )
        vertices.append( p.Point( 0, 0, c ) )

        # rely on the id so set it
        p.Point.point_id = 1

        for pt in vertices:
            pt.allocate_id()

        # List of faces
        faces = ( (1, 4, 3, 2), (5, 6, 7, 8), (1, 5, 8, 4), (2, 3, 7, 6), (3, 4, 8, 7), (1, 2, 6, 5) )

        return vertices, faces

####################################################################################################

    class TestRotationMatrix(object):
        def test_rotate_around_x(self):
            p = [ 1, 2, 3 ]
            rm = g.rotation_matrix( [1, 0, 0], math.pi / 2 )

            rot_p = g.rotate_3d( p, rm )

            assert rot_p[0] == p[0] and rot_p[1] == -p[2] and rot_p[2] == p[1]

        def test_rotate_around_y(self):
            p = [ 1, 2, 3 ]
            rm = g.rotation_matrix( [0, 1, 0], math.pi / 2 )

            rot_p = g.rotate_3d( p, rm )

            assert rot_p[0] == p[2] and rot_p[1] == p[1] and rot_p[2] == -p[0]

        def test_rotate_around_z(self):
            p = [ 1, 2, 3 ]
            rm = g.rotation_matrix( [0, 0, 1], math.pi / 2 )

            rot_p = g.rotate_3d( p, rm )

            assert rot_p[0] == -p[1] and rot_p[1] == p[0] and rot_p[2] == p[2]

        def test_rotate_around_x_to_xy_plane(self):
            import numpy as np

            rr = np.linspace( -4, 4, 17 )

            for x in rr:
                for y in rr:
                    for z in rr:
                        xyz = [ x, y, z ]

                        theta = np.arctan2( z, y )
                        rm = g.rotation_matrix( [1, 0, 0], -theta )

                        rot_xyz = g.rotate_3d( xyz, rm )

                        assert ( rot_xyz[1] >= 0.0 or abs( rot_xyz[1] ) < 1e-10 ) and abs( rot_xyz[2] ) < 1e-10

        def test_rotate_around_y_to_xy_plane(self):
            import numpy as np

            rr = np.linspace( -4, 4, 17 )

            for x in rr:
                for y in rr:
                    for z in rr:
                        xyz = [ x, y, z ]

                        theta = np.arctan2( x, z )
                        rm = g.rotation_matrix( [0, 1, 0], np.pi / 2 - theta )

                        rot_xyz = g.rotate_3d( xyz, rm )

                        assert ( rot_xyz[0] >= 0.0 or abs ( rot_xyz[0] ) < 1e-10 ) and abs( rot_xyz[2] ) < 1e-10

        def test_rotate_around_z_to_xz_plane(self):
            import numpy as np

            rr = np.linspace( -4, 4, 17 )

            for x in rr:
                for y in rr:
                    for z in rr:
                        xyz = [ x, y, z ]

                        theta = np.arctan2( y, x )
                        rm = g.rotation_matrix( [0, 0, 1], -theta )

                        rot_xyz = g.rotate_3d( xyz, rm )

                        assert ( rot_xyz[0] >= 0.0 or abs( rot_xyz[0] ) < 1e-10 ) and abs( rot_xyz[1] ) < 1e-10

####################################################################################################


import math
import numpy as np
import tensorflow as tf

def msra_stddev(x, k_h, k_w):
    return 1/math.sqrt(0.5*k_w*k_h*x.get_shape().as_list()[-1])

def mse_ignore_nans(preds, targets, **kwargs):
    #Computes mse, ignores targets which are NANs

    # replace nans in the target with corresponding preds, so that there is no gradient for those
    targets_nonan = tf.where(tf.is_nan(targets), preds, targets)
    return tf.reduce_mean(tf.square(targets_nonan - preds), **kwargs)

def conv2d(input_, output_dim,
        k_h=3, k_w=3, d_h=2, d_w=2, msra_coeff=1,
        name="conv2d"):
    with tf.variable_scope(name):
        w = tf.get_variable('w', [k_h, k_w, input_.get_shape()[-1], output_dim],
                            initializer=tf.truncated_normal_initializer(stddev=msra_coeff * msra_stddev(input_, k_h, k_w)))
        b = tf.get_variable('b', [output_dim], initializer=tf.constant_initializer(0.0))

        return tf.nn.bias_add(tf.nn.conv2d(input_, w, strides=[1, d_h, d_w, 1], padding='SAME'), b)

def lrelu(x, leak=0.2, name="lrelu"):
    with tf.variable_scope(name):
        f1 = 0.5 * (1 + leak)
        f2 = 0.5 * (1 - leak)
        return f1 * x + f2 * abs(x)

def linear(input_, output_size, name='linear', msra_coeff=1):
    shape = input_.get_shape().as_list()

    with tf.variable_scope(name):
        w = tf.get_variable("w", [shape[1], output_size], tf.float32,
                                tf.random_normal_initializer(stddev=msra_coeff * msra_stddev(input_, 1, 1)))
        b = tf.get_variable("b", [output_size], initializer=tf.constant_initializer(0.0))
        return tf.matmul(input_, w) + b

def conv_encoder(data, params, name, msra_coeff=1):
    layers = []
    for nl, param in enumerate(params):
        if len(layers) == 0:
            curr_inp = data
        else:
            curr_inp = layers[-1]

        layers.append(lrelu(conv2d(curr_inp, param['out_channels'], k_h=param['kernel'], k_w=param['kernel'], d_h=param['stride'], d_w=param['stride'], name=name + str(nl), msra_coeff=msra_coeff)))

    return layers[-1]

def fc_net(data, params, name, last_linear = False, return_layers = [-1], msra_coeff=1):
    layers = []
    for nl, param in enumerate(params):
        if len(layers) == 0:
            curr_inp = data
        else:
            curr_inp = layers[-1]

        if nl == len(params) - 1 and last_linear:
            layers.append(linear(curr_inp, param['out_dims'], name=name + str(nl), msra_coeff=msra_coeff))
        else:
            layers.append(lrelu(linear(curr_inp, param['out_dims'], name=name + str(nl), msra_coeff=msra_coeff)))

    if len(return_layers) == 1:
        return layers[return_layers[0]]
    else:
        return [layers[nl] for nl in return_layers]

def flatten(data):
    return tf.reshape(data, [-1, np.prod(data.get_shape().as_list()[1:])])


import numpy as np

# Example1
a = np.matrix([[0, 4, 5, 2],
               [1, 0, 2, -6],
               [4, 1, 0, -2],
               [1, 7, 1, 0]], dtype='float64')
b = np.matrix([9, -3, 1, -3], dtype='float64').T
x = np.matrix([None]*4, dtype='float64').T

# Example2
a = np.matrix([[2, 4, 6],
               [1, -1, 5],
               [4, 1, -2]], dtype='float64')
b = np.matrix([28, 7, 21], dtype='float64').T
x = np.matrix([None]*3, dtype='float64').T

# for check sum
ori_a = a
# define extended coefficient matrix of eq ax=b
a = np.concatenate([a, b], axis=1)
(row, col) = a.shape
for j in range(row):
    # search pivot
    max_idx = j+np.argmax(abs(a[j:, j]))
    # swap!
    a[[j, max_idx]] = a[[max_idx, j]]
    # push forward
    for i in range(j+1, row):
        p = -a[i, j] / a[j, j]
        a[i] += p*a[j]
print('extended coefficient matrix applied gauss method \n a={}'.format(a))
# back forward
for i in range(row)[::-1]:
    x[i] = (a[i, -1]-np.dot(a[i, i+1:row], x[i+1:row]))/a[i, i]
print('x={}'.format(x))
# confirm
print("ax-b\n={}".format(ori_a@x-b))


""" Sigmoid Layer """

import numpy as np
EPS = 1e-11

def sigmoid(y):
	return 1/(1+1/(np.exp(y)+EPS))
def sigmoid_deriv(y):
	return sigmoid(y)*(1-sigmoid(y))
class SigmoidLayer():
	def __init__(self):
		"""
		Applies the element-wise function: f(x) = 1/(1+exp(-x))
		"""
		self.trainable = False

	def forward(self, Input, **kwargs):

		############################################################################
	    # TODO: Put your code here
		# Apply Sigmoid activation function to Input, and return results.
		self.Input=Input
		return sigmoid(Input)
	    ############################################################################

	def backward(self, delta):

		############################################################################
	    # TODO: Put your code here
		# Calculate the gradient using the later layer's gradient: delta

		return np.multiply(delta,sigmoid_deriv(self.Input))
	    ############################################################################


from math import log, sqrt, exp
import numpy as np

eps = 1e-15

def kl_bern(x, y):
    """ Kullback-Leibler divergence for Bernoulli distributions."""
    x = min(max(x, eps), 1-eps)
    y = min(max(y, eps), 1-eps)
    return x*log(x/y) + (1-x)*log((1-x)/(1-y))

def kl_poisson(x, y):
    """ Kullback-Leibler divergence for Poison distributions."""
    x = max(x, eps)
    y = max(y, eps)
    return y-x+x*log(x/y)

def kl_gamma(x, y, a=1):
    """ Kullback-Leibler divergence for gamma distributions."""
    x = max(x, eps)
    y = max(y, eps)
    return a*(x/y - 1 - log(x/y))

def kl_neg_bin(x, y, r=1):
    """ Kullback-Leibler divergence for negative binomial distributions."""
    return r*log((r+x)/(r+y)) - x * log(y*(r+x)/(x*(r+y)))

def kl_gauss(x, y, sig2=1.):
    """ Kullback-Leibler divergence for Gaussian distributions."""
    return (x - y) ** 2 / (2 * sig2)

def klucb(x, d, div, upperbound, lowerbound=-float('inf'), precision=1e-6):
    """The generic klUCB index computation.

    Input args.: x, d, div, upperbound, lowerbound=-float('inf'), precision=1e-6,
    where div is the KL divergence to be used.
    """
    low = max(x, lowerbound)
    up = upperbound
    while up-low > precision:
        m = (low+up)/2
        if div(x, m) > d:
            up = m
        else:
            low = m
    return (low+up)/2

def klucb_gauss(x, d, sig2=1., precision=0.):
    """klUCB index computation for Gaussian distributions.

    Note that it does not require any search.
    """
    return x + sqrt(2*sig2*d)

def klucb_poisson(x, d, precision=1e-6):
    """klUCB index computation for Poisson distributions."""
    upperbound = x+d+sqrt(d*d+2*x*d)  # looks safe, to check: left (Gaussian) tail of Poisson dev
    return klucb(x, d, kl_poisson, upperbound, precision)

def klucb_bern(x, d, precision=1e-6):
    """klUCB index computation for Bernoulli distributions."""
    upperbound = min(1., klucb_gauss(x, d))
    return klucb(x, d, kl_bern, upperbound, precision)

def klucb_exp(x, d, precision=1e-6):
    """klUCB index computation for exponential distributions."""
    if d < 0.77:
        upperbound = x/(1+2./3*d-sqrt(4./9*d*d+2*d))  # safe, klexp(x,y) >= e^2/(2*(1-2e/3)) if x=y(1-e)
    else:
        upperbound = x*exp(d+1)
    if d > 1.61:
        lowerbound = x*exp(d)
    else:
        lowerbound = x/(1+d-sqrt(d*d+2*d))
    return klucb(x, d, kl_gamma, upperbound, lowerbound, precision)


# -*- coding: utf-8 -*-
"""
Created on Wed Dec 15 20:33:06 2021

@author: Oscar
"""

import numpy as np

def linearsolver(A,b):
    n = len(A)

    #Initialise solution vector as an empty array
    x = np.zeros(n)

    # Join A and use concatenate to form an
    # augmented coefficient matrix
    M = np.concatenate((A,b.T), axis=1)

    for k in range(n):
        for i in range(k,n):
            if abs(M[i][k]) > abs(M[k][k]):
                M[[k,i]] = M[[i,k]]
            else:
                pass
                for j in range(k+1,n):
                    q = M[j][k] / M[k][k]
                    for m in range(n+1):
                        M[j][m] += -q * M[k][m]
    #Python starts indexing with 0, so the last
    #element is n-1
    x[n-1] =M[n-1][n]/M[n-1][n-1]

    #We need to start at n-2, because of
    #Python indexing
    for i in range (n-2,-1,-1):
        z = M[i][n]
        for j in range(i+1,n):
            z = z - M[i][j]*x[j]
        x[i] = z/M[i][i]
    return x

#Initialise the matrices to be solved.
A = np.array([[-70., 1., 0],[-60., -1., 1.],[-40, 0, -1]])
b = np.array([[636.7, 518.6, 307.4]])
print(linearsolver(A,b))

Gauss Elimination to a physics problem.py


from dataclasses import dataclass

import numpy as np

@dataclass(frozen=True)
class MinkowskiSize:
    """
    Family of vector size measures of the form
    `(x1**p + x2**p + ... + xm**p)**(1/p)` (if `unrooted = False`), or
    `(x1**p + x2**p + ... + xm**p)` (if `unrooted = True`),
    for `0 < p < ∞`, and their limits in 0 and ∞.

    For `p = 0`, the rooted variant evaluates to ∞ if there is more than one non-zero coefficient,
    to 0 if all coefficients are zero, and to the only non-zero coefficient otherwise.
    The unrooted variant is equal to the number of non-zero coefficients.

    For `p = ∞`, the rooted variant is the maximum of all coefficients.
    The unrooted variant evaluates to ∞ if there is at least one coefficient larger than 1,
    and to the number of coefficients equal to 1 otherwise.

    Parameters
    ----------
    p: float = 1
        Exponent to use. Must be in `[0, ∞]`.

    unrooted: bool = False
        Whether to omit the root `**(1/p)` from the formula.
        For `p = 0`, this gives Hamming size.
        For `p = 2`, this gives squared Euclidean size.

    scale_by_dimensionality: bool = False
        If `True`, values are scaled linearly such that the vector `[1, 1, ..., 1]` has size 1.
        This can be used to ensure that the range of dissimilarity values in the unit hypercube is `[0, 1]`,
        which can be useful when working with features scaled to `[0, 1]`.

    Notes
    -----
    The most used parameter combinations have their own name.

    * Hamming size is unrooted `p = 0`.
    * The Boscovich norm is `p = 1`. Also known as cityblock, Manhattan or Taxicab norm.
    * The Euclidean norm is rooted `p = 2`. Also known as Pythagorean norm.
    * Squared Euclidean size is unrooted `p = 2`.
    * The Chebishev norm is rooted `p = ∞`. Also known as chessboard or maximum norm.
    """

    p: float
    unrooted: bool = False
    scale_by_dimensionality: bool = False

    def __post_init__(self):
        if self.p < 0:
            raise ValueError('`p` must be in `[0, ∞]`')

    def __call__(self, u, axis=-1):
        if self.p == 0:
            if self.unrooted:
                result = np.count_nonzero(u, axis=axis)
            else:
                result = np.where(np.count_nonzero(u, axis=axis) <= 1, np.sum(np.abs(u), axis=axis), np.inf)
        elif self.p == 1:
            result = np.sum(np.abs(u), axis=axis)
        elif self.p == np.inf:
            if self.unrooted:
                result = np.sum(np.where(np.abs(u) < 1, 0, np.where(np.abs(u) > 1, np.inf, 1)), axis=axis)
            else:
                result = np.max(u, axis=axis)
        else:
            result = np.sum(np.abs(u) ** self.p, axis=axis)
            if not self.unrooted:
                result = result**(1/self.p)
        if self.scale_by_dimensionality and self.p < np.inf:
            if self.unrooted:
                result = result / u.shape[axis]
            else:
                result = result / (u.shape[axis]**(1/self.p))
        return result


import numpy as np
import math

class Sigmoid:
    def __init__(self):
        def sig(x):
            return 1 / (1 + math.exp(-x))

        def sig_grad(x):
            sigx = sig(x)
            return sigx*(1-sigx)

        self.func_vec = np.vectorize(sig)
        self.func_vec_grad = np.vectorize(sig_grad)

    def eval(self,z):
        return self.func_vec(z)

    def eval_grad(self,z):
        return self.func_vec_grad(z)

class Tanh:
    def __init__(self):
        def tanh(x):
            return math.tanh(x)

        def tanh_grad(x):
            tanhx = tanh(x)
            return (1- tanhx*tanhx)

        self.func_vec = np.vectorize(tanh)
        self.func_vec_grad = np.vectorize(tanh_grad)

    def eval(self, z):
        return self.func_vec(z)

    def eval_grad(self, z):
        return self.func_vec_grad(z)

#a = np.array([1,2])
#sigmoid = Sigmoid()


import numpy as np
# Logistic Regression class
class LogisticRegression:
    def __init__(self,lr,n_iters) -> None:
        self.lr=lr
        self.n_iters=n_iters
        self.weights=None
        self.bias=None

# fitting the data
    def fit(self,x,y):
        n_samples,n_features=x.shape
        self.weights=np.zeros(n_features)
        self.bias=0
        # gradient descent
        for _ in range(self.n_iters):
            linear_model=np.dot(x,self.weights) + self.bias
            y_predicted=self._sigmoid(linear_model)

            dw=(1/n_samples)*np.dot(x.T,(y_predicted-y))
            db=(1/n_samples)*np.sum((y_predicted-y))
            self.weights-=self.lr*dw
            self.bias-=self.lr*db

# predicting the data
    def predict(self,x):
        linear_model=np.dot(x,self.weights) + self.bias
        y_predicted=self._sigmoid(linear_model)
        y_predicted_cls=[1 if i>0.5 else 0 for i in y_predicted]
        return y_predicted_cls
#sigmoid function
    def _sigmoid(self,x):
        return 1/(1+np.exp(-x))


#polinomio de taylor
import sympy as sym
import math
#funcion que determinará los polinomios de Taylor de cierto grado
#funcionx representa la función en terminos de x
#x0 es el valor alrededor del cual se desarrollara la serie
#n es el grado maximo del polinomio al que se llegará
def taylor(funcionx,x0,n):
    k = 0
    polinomio = 0
    while (k <= n):
        derivada   = funcionx.diff(x,k)
        derivadax0 = derivada.subs(x,x0)
        divisor   = math.factorial(k)
        terminok  = (derivadax0/divisor)*(x-x0)**k
        polinomio = polinomio + terminok
        k = k + 1
    return(polinomio)

#se asigna una x como un valor string para las funciones, no tocar
x = sym.Symbol('x')

#aquí se define la función en terminos de la variable equis anteriormente
#asignada, para poner exponenciales ocupar la función math.exp(n) donde n
#es el exponente al cual se eleva e
funcionx = 1/(2-x)

a = 0        # Valor alrededor del cual se desarrolla la serie
n  = 4       # Grado polinomio Taylor
valor=3

# tabla polinomios
px_tabla = []
for grado in range(0,n,1):
    polinomio = taylor(funcionx,a,grado)
    px_tabla.append(polinomio)

# SALIDA
for grado in range(0,n,1):
    px = px_tabla[grado]
    x=valor
    solucion=eval(str(px))
    solucionreal=eval(str(funcionx))
    error=sym.Abs((solucion-solucionreal)/solucionreal)
    print("{}|{:40}|{:10}|{}".format(str(grado),str(px),str(solucion),str(error)))


from sklearn.base import TransformerMixin
import numpy as np
import pandas as pd
class RSITransformer(TransformerMixin):
    def __init__(self, column,outputname,period=14):
        self.column = column
        self.cols = [column]
        self.outputname = outputname
        self.period = period

    def fit(self, x, y=None):
        return self
#Stolen from https://stackoverflow.com/questions/20526414/relative-strength-index-in-python-pandas
    def transform(self, dataframe):
        delta = dataframe[self.column].diff()
        dUp, dDown = delta.copy(), delta.copy()
        dUp[dUp < 0] = 0
        dDown[dDown > 0] = 0

        RolUp = dUp.rolling(window=self.period,center=False).sum()
        #pd.rolling_mean(dUp, self.period)
        RolDown = abs(dDown.rolling(window=self.period,center=False).sum()) #pd.rolling_mean(dDown, self.period).abs()
        RS = (RolUp / RolDown)
        dataframe[self.outputname]= (1-(1/(1+RS)))
        return dataframe[[self.outputname]]


import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
import copy
import math

set = int(input("Enter 0 -> 1-D EM \n or \n 1 -> 2-D EM"))

if(set == 0):

    #Generating 3 Random Data
    data1 = np.random.normal(loc=-10, scale=3, size=100)
    data2 = np.random.normal(loc=0, scale=2, size=100)
    data3 = np.random.normal(loc=14, scale=5, size=100)

    # Combine the clusters to get the random datapoints from above
    dataAll = np.stack((data1,data2,data3)).flatten()

    plt.figure(1)
    plt.subplot(311)
    plt.title("Sample Data Set-SINGLE CHANNEL")
    base = np.zeros_like(data1)
    plt.plot(data1,base,"rx")
    plt.subplot(312)
    plt.plot(data2,base,"gx")
    plt.subplot(313)
    plt.plot(data3,base,"bx")

    plt.figure(2)
    plt.title("Sample Data Set-THREE CHANNEL")
    base = np.zeros_like(data1)
    plt.plot(data1,base,"rx")
    plt.plot(data2,base,"gx")
    plt.plot(data3,base,"bx")

    plt.figure(3)
    plt.title("PDF FOR EACH GAUSSIAN")
    x = np.linspace(-30,30, num=300, endpoint=True)
    base = np.zeros_like(data1)
    plt.plot(data1,base,"rx")
    plt.plot(x,norm.pdf(x, -12, 4),"r-",linewidth=1)
    base = np.zeros_like(data2)
    plt.plot(data2,base,"gx")
    plt.plot(x,norm.pdf(x, 0, 3),"g-",linewidth=1)
    base = np.zeros_like(data3)
    plt.plot(data3,base,"bx")
    plt.plot(x,norm.pdf(x, 14, 5),"b-",linewidth=1)

    dataAll.sort()
    # initialization
    m = np.array([1/3,1/3,1/3])
    pi = m / np.sum(m)

    mean = np.array([-5,1,2]).astype(np.float64)
    standDev = np.array([5,3,1]).astype(np.float64)

    plt.figure(4)
    plt.title("INITIAL EM RESULTS")
    base = np.zeros_like(data1)
    plt.plot(data1,base,"gx")
    plt.plot(x,norm.pdf(x, mean[0], standDev[0]),"k-",linewidth=1)
    base = np.zeros_like(data2)
    plt.plot(data2,base,"gx")
    plt.plot(x,norm.pdf(x, mean[1], standDev[1]),"k-",linewidth=1)
    base = np.zeros_like(data3)
    plt.plot(data3,base,"gx")
    plt.plot(x,norm.pdf(x, mean[2], standDev[2]),"k-",linewidth=1)

    alpha = np.zeros((dataAll.shape[0],3))
    iterations = 1000
    oldMean =0

    for j in range(iterations):
        if math.sqrt(np.dot((mean - oldMean),(mean - oldMean)))< 0.0000001:
            break

        oldMean = copy.deepcopy(mean)
        #E- step
        for k,mean_,standDev_,pi_ in zip(range(3),mean,standDev,pi):
            alpha[:,k] = pi_*norm.pdf(dataAll,mean_,standDev_)
        alpha = np.divide(alpha.T, np.sum(alpha , axis = -1)).T

        ## M- step
        tempMu = np.zeros_like(mean)
        for i in range(3):
            den2 = alpha[:,i].sum()
            mean[i] = (alpha[:,i]*dataAll).sum() / den2
            Num = (alpha[:,i]*(dataAll-mean[i])**2).sum()
            standDev[i] = math.sqrt(Num / den2)
            pi[i] = den2/ dataAll.shape[0]

    plt.figure(5)
    plt.title("FINAL EM RESULTS")
    base = np.zeros_like(data1)
    plt.plot(data1,base,"gx")
    plt.plot(x,norm.pdf(x, mean[0], standDev[0]),"k-",linewidth=1)
    base = np.zeros_like(data2)
    plt.plot(data2,base,"gx")
    plt.plot(x,norm.pdf(x, mean[1], standDev[1]),"k-",linewidth=1)
    base = np.zeros_like(data3)
    plt.plot(data3,base,"gx")
    plt.plot(x,norm.pdf(x, mean[2], standDev[2]),"k-",linewidth=1)
    plt.show()
    print(mean)
    print(standDev)

else:
    import matplotlib.pyplot as plt
    import numpy as np
    from scipy.stats import norm
    import copy
    import math
    from scipy.stats import multivariate_normal
    import random

    N = 99     # Number of training samples
    K = 3       # Number of groups
    D = 2       # Number of dimensions

    data1 =np.random.normal(10, 10,size=(N//3, D))
    data2 =np.random.normal(50, 10,size=(N//3, D))
    data3 =np.random.normal(80, 10,size=(N//3, D))

    x = np.vstack([data1,data2,data3])

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    for c,m,x_ in zip(['r','g','y'],['+','^','x'],[data1,data2,data3]):
        plt.plot(x_[:,0], x_[:,1], c=c, marker=m,alpha=0.5)
    ax.set_xlabel('xAxis')
    ax.set_ylabel('yAxis')
    ax.set_zlabel('zAxis')
    plt.title("3D PROJECTION OF 3 CHANNELS")
    plt.show()

    # initilizing gaussians
    initMean = np.random.randint(1, high = 70, size = (K,D)).astype(np.float64)
    initCov =  [(np.random.randint(1,20)*np.eye(D)) for i in range(K)]
    regCov = 1e-6 * np.eye(D)
    initGauss = [multivariate_normal(mean, cov) for mean,cov in zip(initMean,initCov)]
    initPi = np.asarray([1/K for i in range(K)])
    alpha = np.zeros((N,K))
    pdf = np.zeros((N,K))
    log_likelihoods = []

    iterations = 100
    for itera in range(iterations):

        for i,gauss in enumerate(initGauss):
           pdf[:,i] = gauss.pdf(x)

        pixPdf = np.dot(pdf, np.diag(initPi))
        pixPdfSum = np.sum(pixPdf , axis = -1)

        alpha = np.divide(pixPdf.T,
                          pixPdfSum).T

        alphaSum = np.sum(alpha.T , axis = -1)

        mean = np.divide((np.dot(alpha.T,x)).T, alphaSum+1e-8).T

        meanTemp = []
        cov = []
        for i in range(K):
            xSubMean = x-initMean[i, :]
            alphaTemp = np.multiply(alpha[:,i],np.ones((N,D)).T)
            cov.append(np.divide(np.dot(xSubMean.T, np.multiply(alphaTemp.T, xSubMean)), np.sum(alpha[:, i])+1e-8))
            cov[-1] += regCov
        pi = alphaSum/N

        if (np.linalg.norm(mean - initMean) < 1e-4):
            stop = True

        initMean = copy.deepcopy(mean)
        initCov = copy.deepcopy(cov)
        initPi = copy.deepcopy(pi)
        initGauss =  [multivariate_normal(mean_, cov_) for mean_,cov_ in zip(initMean,initCov)]
        log_likelihoods.append(np.log(np.sum([k*multivariate_normal(mean[i],cov[j]).pdf(x) for k,i,j in zip(pi,range(len(mean)),range(len(cov)))])))

    gmm = [multivariate_normal(mean, cov) for mean,cov in zip(initMean,initCov)]

    surface = []
    for i in range(0,255,3):
        for j in range(0,255,3):
            surface.append([i,j])

    surface = np.asarray(surface)
    cpdf = np.zeros(surface.shape[0])

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    for gauss,pi,c,m in zip(gmm,initPi,['r','g','y'],['+','^','x']):
        cpdf = np.add(cpdf, pi*gauss.pdf(surface))
        ax.scatter(surface[:,0], surface[:,1], gauss.pdf(surface), c=c, marker=m,alpha=0.1)

    ax.set_xlabel('xAxis')
    ax.set_ylabel('yAxis')
    ax.set_zlabel('zAxis')
    plt.title("2D-EM")
    plt.show()
    #%%
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    for c,m,x_ in zip(['r','g','y'],['+','^','x'],[data1,data2,data3]):
        plt.plot(x_[:,0], x_[:,1], c=c, marker=m,alpha=0.5)
    ax.scatter(surface[:,0], surface[:,1], cpdf, c='k', marker='.',alpha=0.2)
    ax.set_xlabel('xAxis')
    ax.set_ylabel('yAxis')
    ax.set_zlabel('zAxis')
    plt.title("2D-EM")
    plt.show()
    print(mean)
    print(cov)


import numpy as np

__all__ = ['frobenius_norm',
           'kullback_leibler_divergence',
           'itakura_saito_divergence']

def frobenius_norm(Y, Y_hat):
    """
    Calculate the Frobenius Norm between two matrices.

    Parameters
    ----------
    Y : numpy.array
        Original matrix.
    Y_hat : numpy.array
        Estimated matrix.

    Returns
    -------
    error : float
        Calculated error based on the Frobenius Norm
        (Squared Euclidean Distance).
    """
    error = 0.5 * np.power((Y - Y_hat), 2).sum(axis=None)

    return error

def kullback_leibler_divergence(Y, Y_hat):
    """
    Calculate the Kullback-Leibler divergence between two matrices.

    Parameters
    ----------
    Y : numpy.array
        Original matrix.
    Y_hat : numpy.array
        Estimated matrix.

    Returns
    -------
    error : float
        Calculated error using the KL divergence.
    """
    eps = 1e-16
    error = ((Y * np.log((Y/Y_hat) + eps)) - Y + Y_hat).sum(axis=None)

    return error

def itakura_saito_divergence(Y, Y_hat):
    """
    Calculate the Itakura-Saito divergence between two matrices.

    Parameters
    ----------
    Y : numpy.array
        Original matrix.
    Y_hat : numpy.array
        Estimated matrix.

    Returns
    -------
    error : float
        Calculated error using the IS divergence.
    """
    eps = 1e-16
    error = ((Y/(Y_hat + eps)) - np.log((Y/Y_hat + eps) + eps) - 1).sum(axis=None)

    return error


# import numpy and matplotlib
import numpy as np
import matplotlib.pyplot as plt
# load the dataset
train_data = np.genfromtxt('dataset.csv', delimiter=',')
# remove the header row
train_data = np.delete(train_data, 0, 0)
# take SAT Score as X vector
X = train_data[:, 0]
# take GPA as Y vector
Y = train_data[:, 1]

#initialize m and c
m = 0
c = 0
L = 0.0001 # The learning Rate
iterations = 1000 # The number of iterations to perform gradient descent
n = float(len(X)) # Number of elements in X
# Performing Gradient Descent
for i in range(iterations):
 Y_pred = m*X + c # The current predicted value of Y
 D_m = (-2/n) * sum(X * (Y - Y_pred)) # Derivative with respect to m
 D_c = (-2/n) * sum(Y - Y_pred) # Derivative with respect to c
 m = m - L * D_m # Update m
 c = c - L * D_c # Update c
#output m and c
print ("m = ",m)
print ("c = ",c)

#Final predictions
Y_pred = m*X + c
#Draw the best fitting line
plt.scatter(X, Y)
plt.plot([min(X), max(X)], [min(Y_pred), max(Y_pred)], color='red')
plt.show()


from sklearn.linear_model import RANSACRegressor, LinearRegression
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

df = pd.read_csv('housing.data.txt', header=None, sep='\s+')
df.columns = ['CRIM', 'ZN', 'INDUS', 'CHAS',
              'NOX', 'RM', 'AGE', 'DIS', 'RAD',
              'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']

X = df[['RM']].values
y = df['MEDV'].values

# Fitting a robust regression model using RANSAC
ransac = RANSACRegressor(LinearRegression(),
                         max_trials=100,
                         min_samples=50,
                         loss='absolute_loss',
                         residual_threshold=5.0,
                         random_state=0)

ransac.fit(X, y)

inlier_mask = ransac.inlier_mask_
outlier_mask = np.logical_not(inlier_mask)

line_X = np.arange(3, 10, 1)
line_y_ransac = ransac.predict(line_X[:, np.newaxis])
plt.scatter(X[inlier_mask], y[inlier_mask],
            c='steelblue', edgecolor='white',
            marker='o', label='Inliers')
plt.scatter(X[outlier_mask], y[outlier_mask],
            c='limegreen', edgecolor='white',
            marker='s', label='Outliers')
plt.plot(line_X, line_y_ransac, color='black', lw=2)
plt.xlabel('Average number of rooms [RM]')
plt.ylabel('Price in $1000s [MEDV]')
plt.legend(loc='upper left')
plt.show()

print('Slope: %.3f' % ransac.estimator_.coef_[0])
print('Intercept: %.3f' % ransac.estimator_.intercept_)

Chapter 10/Fitting a robust regression model using RANSAC/program1.py


# encoding:utf-8
import numpy as np
from sklearn.preprocessing import OneHotEncoder

class GaussianNB(object):
    def __init__(self):
        self.class_prior = None
        self.feature_mean = None
        self.feature_var = None

    def _cal_class_prior(self, y):
        """
        e.g.
        y = [[1,0]
             [0,1]]
        """
        class_count = np.sum(y, axis=0)
        return class_count / class_count.sum()

    def _cal_feature_mean_and_var(self, X, y):
        """
        e.g.
        y = [[1,0]
             [0,1]]
        """
        feature_mean = []
        feature_var = []
        epsilon = 1e-9 * np.var(X, axis=0).max()

        for i in range(y.shape[1]):
            class_feature = X[y[:, i] == 1]
            mean = np.mean(class_feature, axis=0)
            # prevent numerical errors. e.g when var=0
            var = np.var(class_feature, axis=0, ddof=1) + epsilon
            feature_mean.append(mean)
            feature_var.append(var)

        return feature_mean, feature_var

    def fit(self, X, y):
        y = np.reshape(y, [-1, 1])
        y = OneHotEncoder(sparse=False).fit_transform(y)
        self.class_prior = self._cal_class_prior(y)
        self.feature_mean, self.feature_var = self._cal_feature_mean_and_var(X, y)

        return self

    def predict(self, X):
        """
        log(gaussian) = - 0.5 * [log(2 * π * var) + (x - mean)^2 / var]
        """
        class_proba = []

        for i, cp in enumerate(self.class_prior):
            mean = self.feature_mean[i]
            var = self.feature_var[i]
            log_proba = np.log(cp) + np.sum(-0.5 * (np.log(2 * np.pi * var) + (X - mean) ** 2 / var), axis=1, keepdims=True)
            class_proba.append(log_proba)

        proba = np.concatenate(class_proba, axis=1)
        label = np.argmax(proba, axis=1)

        return label

if __name__ == '__main__':
    from sklearn.datasets import load_iris
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import classification_report

    X, y = load_iris(True)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
    model = GaussianNB()
    model.fit(X_train, y_train)

    print classification_report(y_test, model.predict(X_test))


import numpy as np

def PCA(data):
    '''
    PCA	Principal Component Analysis

    Input:
      data      - Data numpy array. Each row vector of fea is a data point.
    Output:
      eigvector - Each column is an embedding function, for a new
                  data point (row vector) x,  y = x*eigvector
                  will be the embedding result of x.
      eigvalue  - The sorted eigvalue of PCA eigen-problem.
    '''

    # YOUR CODE HERE
    # Hint: you may need to normalize the data before applying PCA
    # begin answer
    # normalize
    data = data - np.mean(data, axis=1, keepdims=True)

    C = np.cov(data.T)
    eig_value, eig_vec = np.linalg.eig(C)
    idx = np.argsort(eig_value)[::-1]

    return eig_value[idx], eig_vec[:, idx]
    # end answer


'''
Description: 使用最小二乘法求解一元线性回归方程 y(x,w) = w0 + w1 * x 系数
Author: xuzf
Date: 2021-03-21 07:56:48
FilePath: \algorithm-toy\Gradient-descent\Least-squares-method.py
'''
import numpy as np

def least_squares_fit(X, Y):
    X_array = np.array(X)
    Y_array = np.array(Y)
    n = len(X)

    w1 = n * np.dot(X_array, Y_array) - np.sum(X_array) * np.sum(Y_array)
    w1 = w1 / (n * np.dot(X_array, X_array) - np.sum(X_array)**2)
    w0 = (np.sum(Y_array) - w1 * np.sum(X_array)) / n

    return w0, w1


import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt

def norm_ball(p):
    step = np.pi/128
    THETA = np.arange(0, 2*np.pi+step, step)
    X = np.mat(np.zeros((2,len(THETA))))
    for i, theta in enumerate(THETA):
        x = (np.cos(theta), np.sin(theta))
        a = (1/(np.abs(x[0])**p + np.abs(x[1])**p ))**(1/p)
        X[:, i] = a*np.mat(x).T

    return X

P = np.arange(0.25,5.25,0.25)
#print(X)
fig = plt.figure(figsize=(10,10))
NumPlotRows = 5
NumPlotCols = 4

for i,p in enumerate(P):

    X = norm_ball(p=p)
    plt.subplot(NumPlotRows, NumPlotCols, i+1)
    plt.plot(X[0,:].T, X[1,:].T,'-',clip_on=False)
    ax = fig.gca()
    ax.set_xlim((-2,2))
    ax.set_ylim((-2,2))
    ax.axis('equal')
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    #plt.plot(X[0,:].tolist())
    for loc, spine in ax.spines.items():
        spine.set_color('none')  # don't draw spine

    plt.title(p)

plt.show()


import numpy as np
from scipy.stats import norm

'''
This script provides basic functions for the Gaussian modeling
'''

## Threshold for 0th MDL
def threshod_MDL(delta, d, h):
    return np.log(1/delta) + (1 + delta + d/2) * np.log(h) + np.log(h)

## Threshold for 1st MDL
def threshod_F_MDL(delta, d, h):
    return np.log(1/delta) + d * np.log(h/2)

## Threshold for 2nd MDL
def threshod_S_MDL(delta, d, h):
    return 2 * (np.log(1/delta) + d * np.log(h/2))

## Calcuate delta for the 1st D-MDL
def delta_First_NML(score, d, h):
    return np.exp(d * np.log(h/2) - score)

## Calcuate delta for the 2nd D-MDL
def delta_Second_NML(score, d, h):
    return np.exp(d * np.log(h/2) - 0.5 * score)

## Normalizer term for maximum likelihood estimation
def normalizer_MDL(d, h):
    return d/2 * np.log(h)

## NML codelength within a window at a given cut-off point
def NML_gaussian(window, h):

    window_size = len(window)
    window = np.array(window)

    parameters_all = norm.fit(window)
    parameters_before = norm.fit(window[:h])
    parameters_after = norm.fit(window[h:])

    std_all = parameters_all[1]
    std_before = parameters_before[1]
    std_after = parameters_after[1]

    change_score = window_size * np.log(std_all) - h * np.log(std_before) - (window_size - h) * np.log(std_after) + normalizer_MDL(2, window_size) - normalizer_MDL(2, h) - normalizer_MDL(2, window_size - h)

    return change_score

## Maximal NML codelength within a window
def MDL_gaussian_0th(window):

    MDL = -10000
    cut_point = -1
    for cut in range(2, len(window)-1):
        score_tmp = NML_gaussian(window, cut)
        if score_tmp > MDL:
            MDL = score_tmp
            cut_point = cut
    return MDL, cut_point

## Maximal first-order NML codelength within a window
def MDL_gaussian_1th(window):

    MDL = -10000
    cut_point = -1
    for cut in range(2, len(window)-2):
        score_tmp = NML_gaussian(window, cut + 1) - NML_gaussian(window, cut)
        if score_tmp > MDL:
            MDL = score_tmp
            cut_point = cut
    return MDL, cut_point

## Maximal second-order NML codelength within a window
def MDL_gaussian_2nd(window):

    MDL = -10000
    cut_point = -1
    for cut in range(3, len(window)-2):
        score_tmp = NML_gaussian(window, cut + 1) - 2 * NML_gaussian(window, cut) + NML_gaussian(window, cut - 1)
        if score_tmp > MDL:
            MDL = score_tmp
            cut_point = cut
    return MDL, cut_point


from pulp import *
import numpy as np

n_items=int(input("No. of Items produced:"))
n_resources=int(input("No. of resources/materials used to make item:"))

print("Write profit per item:")
items = []
for i in range(n_items):
    print("Item",i+1,":")
    items.append(float(input()))
my_array = np.array(items)
print(np.floor(items))

print("Write maximum no. of resources can be used:")
constarints = []
for i in range(n_resources):
    print("Maximum no. of resource",i+1, " can be used :")
    constarints.append(float(input()))
my_array1 = np.array(constarints)
print(np.floor(constarints))

print(("No. of resources used per item:"))
cost_matrix=[]
for i in range(n_resources):
    cost_matrix.append([])
for i in range(n_resources):
    for j in range(n_items):
        print("Resource ",i+1, "for item ",j+1, ":")
        cost_matrix[i].append(float(input()))

my_array3=np.array(cost_matrix)

model = LpProblem("Profit_Maximization", LpMaximize)

if n_items==2 :
    x = LpVariable(name="x", lowBound=0)
    y = LpVariable(name="y", lowBound=0)
    obj_fun=x*my_array[0]+y*my_array[1]

    for i in range(n_resources):

        model+=(x*my_array3[i][0] + y*my_array3[i][1] <= my_array1[i])

    model+=obj_fun
    print(model)
    status = model.solve()

    print("x=",x.value())
    print("y=",y.value())
    print("Maximum Profit",model.objective.value())

elif n_items==3:
    x = LpVariable(name="x", lowBound=0)
    y = LpVariable(name="y", lowBound=0)
    z = LpVariable(name="z",lowBound=0)
    obj_fun=x*my_array[0]+y*my_array[1]+z*my_array[2]

    for i in range(n_resources):

        model+=(x*my_array3[i][0] + y*my_array3[i][1]  + z*my_array3[i][2] <= my_array1[i])

    model+=obj_fun
    print(model)
    status = model.solve()
    print(status)
    print("x=",x.value())
    print("y=",y.value())
    print("z=",z.value())
    print("Maximum Profit",model.objective.value())

elif n_items==4:
    x = LpVariable(name="x", lowBound=0)
    y = LpVariable(name="y", lowBound=0)
    z = LpVariable(name="z",lowBound=0)
    t = LpVariable(name="t",lowBound=0)

    obj_fun = x*my_array[0] + y*my_array[1] + z*my_array[2] + t*my_array[3]

    for i in range(n_resources):

        model+=(x*my_array3[i][0] + y*my_array3[i][1]  + z*my_array3[i][2] + t*my_array3[i][3] <= my_array1[i])

    model+=obj_fun
    print(model)
    status = model.solve()

    print("x=",x.value())
    print("y=",y.value())
    print("z=",z.value())
    print("t=",t.value())
    print("Maximum Profit",model.objective.value())

elif n_items==5:
    p = LpVariable(name="r", lowBound=0)
    q = LpVariable(name="q", lowBound=0)
    r = LpVariable(name="r",lowBound=0)
    s = LpVariable(name="s",lowBound=0)
    t = LpVariable(name="t",lowBound=0)

    obj_fun = p*my_array[0] + q*my_array[1] + r*my_array[2] + s*my_array[3] + t*my_array[4]

    for i in range(n_resources):

        model+=(p*my_array3[i][0] + q*my_array3[i][1]  + r*my_array3[i][2] + s*my_array3[i][3] + t*my_array3[i][4] <= my_array1[i])
        print(model)

    model+=obj_fun
    print(model)
    status = model.solve()
    print(status)
    print("p=",p.value())
    print("q=",q.value())
    print("r=",r.value())
    print("s=",s.value())
    print("t=",t.value())
    print("Maximum Profit",model.objective.value())

else:
    print("System is limited for only 5 variables..")


from math import erf, sqrt
import numpy as np
# for inverse cumulative normal
import scipy.stats

def nstream(u):
  """ Return a vector of n normally distributed pseudorandom variates (mean zero unity variance) """
  return scipy.stats.norm.ppf(u)

def norm_cdf(x):
  """ Compute the normal cumulatve density funtion """
  return (1.0 + erf(x / sqrt(2.0))) / 2.0


import numpy as np

des = 0.0005
mean = [0.2, 0.2]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate(([x], [y]), axis=0).transpose()
label = 1 * np.ones(x.shape[0])

mean = [0.1, 0.9]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 2 * np.ones(x.shape[0])])

mean = [0.4, 0.5]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 3 * np.ones(x.shape[0])])

mean = [0.6, 0.2]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 4 * np.ones(x.shape[0])])

mean = [0.8, 0.5]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 5 * np.ones(x.shape[0])])

title = "Well_separated"
np.savetxt('../Datasets/Simulated/' + title + '.txt', np.concatenate((data, label[:, None]), axis=1) , delimiter=' ', fmt=['%.4e', '%.4e', '%d'])

des = 0.0005
mean = [0.2, 0.2]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate(([x], [y]), axis=0).transpose()
label = 1 * np.ones(x.shape[0])

mean = [0.1, 0.9]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 2 * np.ones(x.shape[0])])

mean = [0.4, 0.5]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 3 * np.ones(x.shape[0])])

mean = [0.6, 0.2]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 4 * np.ones(x.shape[0])])

mean = [0.8, 0.5]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 5 * np.ones(x.shape[0])])

mean = [0.5, 0.5]
cov = [[des * 100, 0], [0, des * 100]]
x, y = np.random.multivariate_normal(mean, cov, 50).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 6 * np.ones(x.shape[0])])

title = "Well_separated noise"
np.savetxt('../Datasets/Simulated/' + title + '.txt', np.concatenate((data, label[:, None]), axis=1) , delimiter=' ', fmt=['%.4e', '%.4e', '%d'])

des = 0.003
mean = [0.5, 0.5]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 100).T
data = np.concatenate(([x], [y]), axis=0).transpose()
label = 1 * np.ones(x.shape[0])

mean = [0.3, 0.3]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 50).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 2 * np.ones(x.shape[0])])

mean = [0.7, 0.3]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 400).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 3 * np.ones(x.shape[0])])

title = "Different Density"
np.savetxt('../Datasets/Simulated/' + title + '.txt', np.concatenate((data, label[:, None]), axis=1) , delimiter=' ', fmt=['%.4e', '%.4e', '%d'])

des = 0.0008
mean = [0.2, 0.2]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate(([x], [y]), axis=0).transpose()
label = 1 * np.ones(x.shape[0])

mean = [0.2, 0.4]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 2 * np.ones(x.shape[0])])

mean = [0.65, 0.8]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 3 * np.ones(x.shape[0])])

mean = [0.8, 0.8]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 4 * np.ones(x.shape[0])])

mean = [0.8, 0.2]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 250).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 5 * np.ones(x.shape[0])])

title = "Sub clusters"
np.savetxt('../Datasets/Simulated/' + title + '.txt', np.concatenate((data, label[:, None]), axis=1) , delimiter=' ', fmt=['%.4e', '%.4e', '%d'])

des = 0.01
mean = [0.5, 0.5]
cov = [[des, 0], [0, des]]
x, y = np.random.multivariate_normal(mean, cov, 1300).T
data = np.concatenate(([x], [y]), axis=0).transpose()
label = 1 * np.ones(x.shape[0])

mean = [0.2, 0.2]
cov = [[des / 10, 0], [0, des / 10]]
x, y = np.random.multivariate_normal(mean, cov, 100).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 2 * np.ones(x.shape[0])])

mean = [0.8, 0.2]
cov = [[des / 10, 0], [0, des / 10]]
x, y = np.random.multivariate_normal(mean, cov, 100).T
data = np.concatenate([data, np.concatenate(([x], [y]), axis=0).transpose()])
label = np.concatenate([label, 3 * np.ones(x.shape[0])])

title = "Skew clusters"
np.savetxt('../Datasets/Simulated/' + title + '.txt', np.concatenate((data, label[:, None]), axis=1) , delimiter=' ', fmt=['%.4e', '%.4e', '%d'])


import pandas as pd
import numpy as np

def rsi_tradingview(ohlc: pd.DataFrame, period: int = 14, round_rsi: bool = True):
    """ Implements the RSI indicator as defined by TradingView on March 15, 2021.
        The TradingView code is as follows:
        //@version=4
        study(title="Relative Strength Index", shorttitle="RSI", format=format.price, precision=2, resolution="")
        len = input(14, minval=1, title="Length")
        src = input(close, "Source", type = input.source)
        up = rma(max(change(src), 0), len)
        down = rma(-min(change(src), 0), len)
        rsi = down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down))
        plot(rsi, "RSI", color=#8E1599)
        band1 = hline(70, "Upper Band", color=#C0C0C0)
        band0 = hline(30, "Lower Band", color=#C0C0C0)
        fill(band1, band0, color=#9915FF, transp=90, title="Background")

    :param ohlc:
    :param period:
    :param round_rsi:
    :return: an array with the RSI indicator values
    """

    delta = ohlc["close"].diff()

    up = delta.copy()
    up[up < 0] = 0
    up = pd.Series.ewm(up, alpha=1/period).mean()

    down = delta.copy()
    down[down > 0] = 0
    down *= -1
    down = pd.Series.ewm(down, alpha=1/period).mean()

    rsi = np.where(up == 0, 0, np.where(down == 0, 100, 100 - (100 / (1 + up / down))))

    return np.round(rsi, 2) if round_rsi else rsi

def rr(ohlc: pd.DataFrame, period: int = 14, round_rsi: bool = True):
    lst = [
        [1245,39494.5],
        [1250,39547.5],
        [1255,39522.0],
        [1300,39495.0],
        [1305,39146.0],
        [1310,39280.5],
        [1315,39380.0],
        [1320,39304.5],
        [1325,39257.0],
        [1330,39316.0],
        [1335,39241.5],
        [1340,39200.0],
        [1345,39235.5],
        [1350,39157.5],
    ]
    N = 14
    close_price0 = float(lst[0][1])
    gain_avg0 = loss_avg0 = close_price0
    for kline in lst[1:]:
        close_price = float(kline[1])
        if close_price > close_price0:
            gain = close_price - close_price0
            loss = 0
        else:
            gain = 0
            loss = close_price0 - close_price
        close_price0 = close_price
        gain_avg = (gain_avg0 * (N - 1) + gain) / N
        loss_avg = (loss_avg0 * (N - 1) + loss) / N
        rsi = 100 - 100 / (1 + gain_avg / loss_avg)
        gain_avg0 = gain_avg
        loss_avg0 = loss_avg
        print (rsi)

def rsi_tradingview2(ohlc: pd.DataFrame, period: int = 14) -> pd.Series:
    """See source https://github.com/peerchemist/finta
    and fix https://www.tradingview.com/wiki/Talk:Relative_Strength_Index_(RSI)
    Relative Strength Index (RSI) is a momentum oscillator that measures the speed and change of price movements.
    RSI oscillates between zero and 100. Traditionally, and according to Wilder, RSI is considered overbought when above 70 and oversold when below 30.
    Signals can also be generated by looking for divergences, failure swings and centerline crossovers.
    RSI can also be used to identify the general trend."""

    delta = ohlc["close"].diff()

    up, down = delta.copy(), delta.copy()
    up[up < 0] = 0
    down[down > 0] = 0

    _gain = up.ewm(com=(period - 1), min_periods=period).mean()
    _loss = down.abs().ewm(com=(period - 1), min_periods=period).mean()

    RS = _gain / _loss
    return pd.Series(100 - (100 / (1 + RS)), name="RSI")


from __future__ import division
import numpy as np
import matplotlib.pyplot as plt

try:
    xrange
except NameError:
    xrange = range

def add_intercept(X_):
    m, n = X_.shape
    X = np.zeros((m, n + 1))
    X[:, 0] = 1
    X[:, 1:] = X_
    return X

def load_data(filename):
    D = np.loadtxt(filename)
    Y = D[:, 0]
    X = D[:, 1:]
    return add_intercept(X), Y

def calc_grad(X, Y, theta):
    m, n = X.shape
    grad = np.zeros(theta.shape)

    margins = Y * X.dot(theta)
    probs = 1. / (1 + np.exp(margins))
    grad = -(1./m) * (X.T.dot(probs * Y))

    return grad

def logistic_regression(X, Y):
    m, n = X.shape

    theta = np.zeros(n)
    learning_rate = 10

    i = 0
    max_training_rounds = 100000
    fig, axes = plt.subplots(2, 2, figsize=(9, 6))
    axes = axes.ravel()
    diff = []
    grad_norm = []
    while True and i <= max_training_rounds:
        i += 1
        prev_theta = theta
        grad = calc_grad(X, Y, theta)
        theta = theta  - learning_rate * (grad)
        norm = np.linalg.norm(prev_theta - theta)
        grad_norm.append(np.linalg.norm(grad))
        diff.append(norm)
        if i % 10000 == 0:
            print('Finished {0} iterations; Diff theta: {1}; theta: {2}; Grad: {3}'.format(
                i, norm, theta, grad))

            for j in range(m):
                if Y[j] == 1:
                    axes[int(i / 10000 - 1)].scatter(X[j][1], X[j][2], color='red')
                else:
                    axes[int(i / 10000 - 1)].scatter(X[j][1], X[j][2], color='blue')
            x = np.arange(0, 1, 0.1)
            axes[int(i/10000-1)].plot(x, -(theta[0] + theta[1] * x) / theta[2])

        if norm < 1e-15:
            print('Converged in %d iterations' % i)
            break
    plt.xlabel('Training Rounds')
    plt.ylabel('Theta Difference')
    plt.plot(np.arange(i)+1,np.array(diff))
    plt.show()
    return

def main():
    print('==== Training model on data set A ====')
    Xa, Ya = load_data('data_a.txt')
    logistic_regression(Xa, Ya)

    print('\n==== Training model on data set B ====')
    Xb, Yb = load_data('data_b.txt')
    logistic_regression(Xb, Yb)

    return

if __name__ == '__main__':
    main()


import numpy as np

def one_hot_encode(labels):
	size = len(labels)
	max_val = max(labels)
	labels = np.array(labels)
	encoded_labels = np.zeros((size, max_val + 1))
	encoded_labels[range(size), labels] = 1
	return encoded_labels

class PCA(object):
	def __init__(self):
		pass

	def calc_covariance_matrix(self, X, Y):
		N = len(X)
		cov_mat = (1 / (N - 1)) * np.dot((X - np.mean(X, axis = 0)).T,  Y - np.mean(Y, axis = 0))
		return cov_mat

	def transform(self, X, num_components):
		cov_mat = self.calc_covariance_matrix(X, X)
		eig_val, eig_vec = np.linalg.eig(cov_mat)

		select_idx = eig_val.argsort()[::-1]
		eig_val = eig_val[select_idx][:num_components]
		eig_vec = np.atleast_1d(eig_vec[:, select_idx])[:, :num_components]

		X_transformed = np.dot(X, eig_vec)
		return X_transformed


import numpy as np

def sigmoid(z):
    """
        :param: z - can be a matrix, vector or scalar

        :return: g - sigmoid de cada valor
    """
    g = 1/(1+np.exp(-z))

    return g


import numpy as np
from sklearn.exceptions import NotFittedError

from mlscratch.models import losses
from mlscratch.models.activations import sigmoid

class LogisticRegression(object):
    """ A simple LogisticRegression implementation with NumPy.

    Args:
        lr (float): Learning rate.
        num_iterations (int): A number of iterations.
        threshold (float): A threshold for classify.
        verbose (bool): A verbosity parameter.
    """

    def __init__(
        self,
        lr=0.001,
        num_iterations=1000,
        threshold=0.5,
        verbose=True,
    ):
        self.lr = lr
        self.num_iterations = num_iterations
        self.threshold = threshold
        self.theta = None  # Parameters to train
        self.verbose = verbose
        self.train_metric_list = []

    @staticmethod
    def _initialize_parameters(X):
        num_features = X.shape[1]
        limit = 1 / np.sqrt(num_features)
        params = np.random.uniform(-limit, limit, (num_features, ))
        return params

    def fit(self, X, y):
        assert X.ndim == 2, f'X.ndim must be 2. But got {X.ndim}.'
        assert y.ndim == 1, f'y.ndim must be 1. But got {y.ndim}.'
        self.theta = self._initialize_parameters(X)
        data_size = len(y)

        for n_iter in np.arange(self.num_iterations):
            # Make a new prediction.
            y_pred = sigmoid(X.dot(self.theta))
            dw = np.dot((y - y_pred), X) / data_size
            # Update parameters.
            self.theta += self.lr * dw
            # Calculate BinaryCrossEntropy.
            bce = losses.binary_cross_entropy(y, y_pred)
            self.train_metric_list.append(bce)
            if self.verbose:
                print(f'num_iterations: {n_iter}\t BCE: {bce}')

        return self

    def predict(self, X):
        assert X.ndim == 2, f'X.ndim must be 2. But got {X.ndim}.'
        if self.theta is None:
            raise NotFittedError(f'{self.__class__.__name__} is not fitted.'
                                 f' You must call fit before call predict.')
        y_pred = sigmoid(X.dot(self.theta)) >= self.threshold
        return (y_pred.astype('int'))

    def predict_proba(self, X):
        assert X.ndim == 2, f'X.ndim must be 2. But got {X.ndim}.'
        if self.theta is None:
            raise NotFittedError(f'{self.__class__.__name__} is not fitted.'
                                 f' You must call fit before call predict.')
        return sigmoid(X.dot(self.theta))


import numpy as np
rng = np.random.default_rng()

a = rng.integers(100, size=(5, 20))

b = np.sort(a, axis=0)  # Sort columns
ind = np.argsort(b, axis=1)  # Indices to sort rows
c = np.take_along_axis(b, ind, axis=1)  # Sort rows using indices ind

if __name__ == '__main__':
    print('\nUnsorted array:\n', a)
    print('\nSorted columns:\n', b)
    print('\nIndices to sort rows:\n', ind)
    print('\nSorted:\n', c)


import numpy as np

def randomization(n):
    """
    Arg:
      n - an integer
    Returns:
      A - a randomly-generated nx1 Numpy array.
    """
    A = np.random.random((n,1))
    return A
    raise NotImplementedError

def operations(h, w):
    """
    Takes two inputs, h and w, and makes two Numpy arrays A and B of size
    h x w, and returns A, B, and s, the sum of A and B.

    Arg:
      h - an integer describing the height of A and B
      w - an integer describing the width of A and B
    Returns (in this order):
      A - a randomly-generated h x w Numpy array.
      B - a randomly-generated h x w Numpy array.
      s - the sum of A and B.
    """
    #Your code here
    A = np.random.random((h,w))
    B = np.random.random((h,w))
    s = np.add(A,B)
    return A,B,s
    raise NotImplementedError

def norm(A, B):
    """
    Takes two Numpy column arrays, A and B, and returns the L2 norm of their
    sum.

    Arg:
      A - a Numpy array
      B - a Numpy array
    Returns:
      s - the L2 norm of A+B.
    """
    #Your code here
    sum = np.add(A,B)
    s = np.linalg.norm(sum)
    return s
    raise NotImplementedError

def neural_network(inputs, weights):
    """
     Takes an input vector and runs it through a 1-layer neural network
     with a given weight matrix and returns the output.

     Arg:
       inputs - 2 x 1 NumPy array
       weights - 2 x 1 NumPy array
     Returns (in this order):
       out - a 1 x 1 NumPy array, representing the output of the neural network
    """
    #Your code here
    p = np.multiply(inputs, weights)
    out1 = np.array(([np.sum(p)]))
    out =  np.array(([np.tanh(out1)]))
    return out
    raise NotImplementedError

def scalar_function(x, y):
    """
    Returns the f(x,y) defined in the problem statement.
    """
    #Your code here
    if x <= y:
        out = x*y
    else:
        out = x/y
    return out
    raise NotImplementedError

def vector_function(x, y):
    """
    Make sure vector_function can deal with vector input x,y
    """
    #Your code here
    v = np.vectorize(scalar_function)
    return v(x,y)
    raise NotImplementedError


# Copyright 2020 (c) Aalto University - All Rights Reserved
# ELEC-E8125 - Reinforcement Learning Course
# AALTO UNIVERSITY
#
#############################################################

import numpy as np
from time import sleep
from sailing import SailingGridworld

epsilon = 10e-4  # TODO: Use this criteria for Task 3

# Set up the environment
env = SailingGridworld(rock_penalty=-2)
value_est = np.zeros((env.w, env.h))
env.draw_values(value_est)

def value_iteration(env, gamma, termination_condition):
    value_est, policy = np.zeros((env.w, env.h)), np.zeros((env.w, env.h))
    previous_values = np.copy(value_est)
    iteration = 0
    done = False

    while not done:
        for x in range(len(env.transitions)):
            for y in range(len(env.transitions[x])):
                # variable used for storing the different values of the value iteration
                values_of_transitions = np.array([])
                for transitions_set in env.transitions[x, y]:
                    value = 0
                    # For each transition the value is calculated
                    for transtion in transitions_set:
                        next_state = transtion[0]
                        # If next state exists
                        if next_state:
                            reward = transtion[1]
                            probability = transtion[3]
                            value += probability * (reward + gamma * previous_values[next_state[0], next_state[1]])
                    values_of_transitions = np.append(values_of_transitions, value)

                # update of the current value grid
                value_est[x, y] = np.max(values_of_transitions)
                policy[x, y] = np.argmax(values_of_transitions)

        # evaluate termination condition
        done = termination_condition(value_est, previous_values, policy, iteration)

        # update necessary values
        iteration += 1
        previous_values = np.copy(value_est)

    return value_est, policy

def convergence_criteria(current_val, prev_val, _, current_iteration, log_results=True):
    max_change = np.max(np.abs(np.subtract(current_val, prev_val)))
    converged = max_change < epsilon
    if converged and log_results:
        print(f'algorithm converged at iteration {current_iteration}')
    return converged

def number_of_executions_convergence(max_number_of_executions, current_val, prev_val, _, current_iteration):
    reached_max = current_iteration >= max_number_of_executions

    if (reached_max):
        converged = convergence_criteria(current_val, prev_val, _, current_iteration, log_results=False)
        print(f"With max of {max_number_of_executions} the algorithm {converged}")

    return reached_max

def discount_return(env, policy, max_iterations, gamma):
    discounts = np.array([])
    for _ in range(max_iterations):
        done = False
        iter = 0
        G = 0
        env.reset()
        while not done:
            # Select a random action
            action = policy[env.state[0], env.state[1]]

            # Step the environment
            state, reward, done, _ = env.step(action)

            G += reward * np.power(gamma, iter)
            iter += 1
            if done:
                discounts = np.append(discounts, G)

    return discounts

if __name__ == "__main__":
    # Reset the environment
    state = env.reset()
    gamma = 0.9

    # Compute state values and the policy
    # Task 1 and 2
    max_n_iterations = 100
    max_number_of_iterations = lambda current_val, prev_val, policy, iterations: iterations >= max_n_iterations
    value_est, policy = value_iteration(env, gamma, max_number_of_iterations)
    # Show the values and the policy
    env.draw_values(value_est)
    env.draw_actions(policy)
    env.render()
    env.save_figure("report/img/final-board-with-policy-2.png")
    sleep(1)

    # # question 4
    number_of_executions = [10, 25, 50, 75, 80]

    for execution in number_of_executions:
        evaluator = lambda current_val, prev_val, _, current_iteration: number_of_executions_convergence(execution,
                                                                                current_val,
                                                                                prev_val, _,
                                                                                current_iteration)
        value_est, policy = value_iteration(env, gamma, evaluator)
    #
    # # Task 3
    value_est, policy = value_iteration(env, gamma, convergence_criteria)

    # Show the values and the policy
    env.draw_values(value_est)
    env.draw_actions(policy)
    env.render()
    env.save_figure("report/img/final-board-with-threshold.png")
    sleep(1)
    #
    # # Task 4
    discounts = discount_return(env, policy, max_iterations=1000, gamma=gamma)

    print(f"mean of Gs -> {np.mean(discounts)}")
    print(f"Std of Gs -> {np.std(discounts)}")

    # Save the state values and the policy
    fnames = "task_1_values.npy", "task_2_policy.npy"
    np.save(fnames[0], value_est)
    np.save(fnames[1], policy)
    print("Saved state values and policy to", *fnames)
    #
    # # Run a single episode
    # # TODO: Run multiple episodes and compute the discounted returns (Task 4)
    done = False
    while not done:
        # Select a random action
        action = policy[env.state[0], env.state[1]]

        # Step the environment
        state, reward, done, _ = env.step(action)

        # Render and sleep
        env.render()
        sleep(0.05)


#！/bin/python
# -*- UTF-8 -*-

"""
Ufuncs expect a set of scalars as input and produce a set of scalars as output.
Universal functions can typically be mapped to mathematical counterparts,
such as, add, subtract, divide, multiply, and likewise.
"""

import numpy as np

a = np.arange(9)
b = np.add.reduce(a)
c = np.add.accumulate(a)
#(indices(i), indices(i+1))] , if i = len - 1, then reduce(indices(i), -1)
d = np.add.reduceat(a, [0, 5, 2, 7])

print("a", a)
print("reduce: ", b)
print("accumu: ", c)
print("reduceat: ", d)
print("Reduceat step I", np.add.reduce(a[0:5]))
print("Reduceat step II", np.add.reduce(a[5]))
print("Reduceat step III", np.add.reduce(a[2:7]))
print("Reduceat step IV", np.add.reduce(a[7:]))
print("Outer", np.add.outer(np.arange(5), a))


#!/usr/bin/env python
# coding: utf-8

# troduction
# -------------------
#
#  this section, we  consider the famous Gauss-Markov problem which will give us an opportunity to use all the material we have so far developed. The Gauss-Markov is the fundamental model for noisy parameter estimation because it estimates unobservable parameters given a noisy indirect measurement.  Incarnations of the same model appear in all studies of Gaussian models. This case is an excellent opportunity to use everything we have so far learned about projection and conditional expectation.

# Following Luenberger (1997), let's  consider the following problem:
#
# $$ \mathbf{y} = \mathbf{W} \boldsymbol{\beta} + \boldsymbol{\epsilon} $$
#
# where $\mathbf{W}$ is a $ n \times m $ matrix, and $\mathbf{y}$ is a $n \times 1$ vector. Also, $\boldsymbol{\epsilon}$ is a $n$-dimensional np.random.random vector with zero-np.mean and covariance
#
# $$ \mathbb{E}( \boldsymbol{\epsilon} \boldsymbol{\epsilon}^T) = \mathbf{Q}$$
#
# Note that real systems usually provide a *calibration mode* where you can estimate $\mathbf{Q}$ so it's not fantastical to assume you have some knowledge of the noise statistics. The problem is to find a matrix $\mathbf{K}$ so that $ \boldsymbol{\hat{\beta}} = \mathbf{K} \mathbf{y}$  approximates $ \boldsymbol{\beta}$.  Note that we only have knowledge of $\boldsymbol{\beta}$ via $ \mathbf{y}$ so we can't measure it directly. Further, note that $\mathbf{K} $ is a matrix, not a vector, so there are $m \times n$ entries to compute.
#
# We can approach this problem the usual way by trying to solve the MMSE problem:
#
# $$ \min_K \mathbb{E}(|| \boldsymbol{\hat{\beta}}- \boldsymbol{\beta} ||^2)$$
#
# which we can write out as
#
# $$ \min_K \mathbb{E}(|| \boldsymbol{\hat{\beta}}- \boldsymbol{\beta} ||^2)
# =  \min_K\mathbb{E}(|| \mathbf{K}\mathbf{y}- \boldsymbol{\beta} ||^2)
# =  \min_K\mathbb{E}(|| \mathbf{K}\mathbf{W}\mathbf{\boldsymbol{\beta}}+\mathbf{K}\boldsymbol{\epsilon}- \boldsymbol{\beta} ||^2)$$
#
# and since $\boldsymbol{\epsilon}$ is the only np.random.random variable here, this simplifies to
#
# $$\min_K || \mathbf{K}\mathbf{W}\mathbf{\boldsymbol{\beta}}- \boldsymbol{\beta} ||^2 + \mathbb{E}(||\mathbf{K}\boldsymbol{\epsilon} ||^2)
# $$
#
# The next step is to compute
#
# $\DeclareMathOperator{\Tr}{Trace}$
# $$ \mathbb{E}(||\mathbf{K}\boldsymbol{\epsilon} ||^2) = \mathbb{E}(\boldsymbol{\epsilon}^T \mathbf{K}^T \mathbf{K}^T \boldsymbol{\epsilon})=\Tr(\mathbf{K \mathbb{E}(\boldsymbol{\epsilon}\boldsymbol{\epsilon}^T) K}^T)=\Tr(\mathbf{K Q K}^T)$$
#
# using the properties of the trace of  a matrix. We can assemble everything as
#
# $$  \min_K || \mathbf{K W} \boldsymbol{\beta} -  \boldsymbol{\beta}||^2 + \Tr(\mathbf{K Q K}^T) $$
#
# Now, if we were to solve this for $\mathbf{K}$, it would be a function of $ \boldsymbol{\beta}$, which is the same thing as saying that the estimator, $ \boldsymbol{\hat{\beta}}$, is a function of what we are trying to estimate, $ \boldsymbol{\beta}$, which makes no sense. However, writing this out tells us that if we had $\mathbf{K W}= \mathbf{I}$, then the first term  vanishes and the problem simplifies to
#
# $$ \min_K \Tr(\mathbf{K Q K}^T) $$
#
# with
#
# $$ \mathbf{KW} = \mathbf{I}$$
#
# This requirement is the same as asserting that the estimator is unbiased,
#
# $$ \mathbb{E}( \boldsymbol{\hat{\beta}}) = \mathbf{KW}  \boldsymbol{\beta} =  \boldsymbol{\beta}  $$
#
# To line this problem up with our earlier work, let's consider  the $i^{th}$ column of $\mathbf{K}$, $\mathbf{k}_i$. Now, we can re-write the problem as
#
# $$ \min_k (\mathbf{k}_i^T \mathbf{Q} \mathbf{k}_i) $$
#
# with
#
# $$ \mathbf{k}_i^T \mathbf{W} = \mathbf{e}_i$$
#
# and from our previous work on contrained optimization, we know the solution to this:
#
# $$ \mathbf{k}_i  = \mathbf{Q}^{-1} \mathbf{W}(\mathbf{W}^T \mathbf{Q^{-1} W})^{-1}\mathbf{e}_i$$
#
# Now all we have to do is stack these together for the general solution:
#
# $$ \mathbf{K}  = (\mathbf{W}^T \mathbf{Q^{-1} W})^{-1} \mathbf{W}^T\mathbf{Q}^{-1} $$
#
# It's easy when you have all of the concepts lined up! For completeness, the covariance of the error is
#
# $$ \mathbb{E}(\hat{\boldsymbol{\beta}}-\boldsymbol{\beta}) (\hat{\boldsymbol{\beta}}-\boldsymbol{\beta})^T
# = \mathbb{E}(\mathbf{K} \boldsymbol{\epsilon} \boldsymbol{\epsilon}^T \mathbf{K}^T)=\mathbf{K}\mathbf{Q}\mathbf{K}^T =(\mathbf{W}^T \mathbf{Q}^{-1} \mathbf{W})^{-1}$$

# The following  simulation  illustrates these results.

# [18]

from mpl_toolkits.mplot3d import proj3d
from numpy.linalg import inv
import matplotlib.pyplot as plt
import numpy as np as np
from numpy import matrix, linalg, ones, np.array

Q = np.eye(3)*.1 # error covariance matrix
#Q[0,0]=1

beta = matrix(ones((2,1))) # this is what we are trying estimate
W = matrix([[1,2],
            [2,3],
            [1,1]])

ntrials = 50
epsilon = np.np.random.random.multivariate_normal((0,0,0),Q,ntrials).T
y=W*beta+epsilon

K=inv(W.T*inv(Q)*W)*matrix(W.T)*inv(Q)
b=K*y #estimated beta from data

fig = plt.figure()
fig.set_size_inches([6,6])

# some convenience definitions for plotting
bb = np.array(b)
bm = bb.np.mean(1)
yy = np.array(y)
ax = fig.add_subplot(111, projection='3d')

ax.plot3D(yy[0,:],yy[1,:],yy[2,:],'mo',label='y',alpha=0.3)
ax.plot3D([beta[0,0],0],[beta[1,0],0],[0,0],'r-',label=r'$\beta$')
ax.plot3D([bm[0],0],[bm[1],0],[0,0],'g-',lw=1,label=r'$\hat{\beta}_m$')
ax.plot3D(bb[0,:],bb[1,:],0*bb[1,:],'.g',alpha=0.5,lw=3,label=r'$\hat{\beta}$')
ax.legend(loc=0,fontsize=18)
plt.show()

# The figure above show the simulated $\mathbf{y}$ data as magenta circles. The green dots show the corresponding estimates, $\boldsymbol{\hat{\beta}}$ for each sample. The red and green lines show the true value of $\boldsymbol{\beta}$ versus the average of the estimated $\boldsymbol{\beta}$-values, $\boldsymbol{\hat{\beta_m}}$. The matrix $\mathbf{K}$ maps the magenta circles in the corresponding green dots. Note there are many possible ways to map the magenta circles to the plane, but the $\mathbf{K}$ is the ones that minimizes the MSE for $\boldsymbol{\beta}$.
#
# The figure below shows more detail in the horizontal *xy*-plane above.

# [19]

from  matplotlib.patches import Ellipse

fig, ax = plt.plt.subplots()
fig.set_size_inches((6,6))
ax.set_aspect(1)
ax.plot(bb[0,:],bb[1,:],'g.')
ax.plot([beta[0,0],0],[beta[1,0],0],'r--',label=r'$\beta$',lw=4.)
ax.plot([bm[0],0],[bm[1],0],'g-',lw=1,label=r'$\hat{\beta}_m$')
ax.legend(loc=0,fontsize=18)
ax.grid()

bm_cov = inv(W.T*Q*W)
U,S,V = linalg.svd(bm_cov)

err = np.np.sqrt((matrix(bm))*(bm_cov)*(matrix(bm).T))
theta = np.arccos(U[0,1])/np.pi*180

ax.add_patch(Ellipse(bm,err*2/np.np.sqrt(S[0]),err*2/np.np.sqrt(S[1])
                       ,angle=theta,color='pink',alpha=0.5))

plt.show()

# The figure above shows the green dots, which are individual estimates of $\boldsymbol{\hat{\beta}}$ from the corresponding simulated $\mathbf{y}$ data. The red dashed line is the true value for $\boldsymbol{\beta}$ and the green line ($\boldsymbol{\hat{\beta_m}}$ ) is the average of all the green dots. Note there is hardly a visual difference between them. The pink ellipse provides some scale as to the covariance of the estimated $\boldsymbol{\beta}$  values. I invite you to download this [IPython notebook](https://github.com/unpingco/Python-for-Signal-Processing/blob/master/Gauss_Markov.ipynb)  and tweak the values for the error covariance or any other parameter. All of the plots should update and scale correctly.
#

# ## Summary

# The Gauss-Markov problem is the cornerstone of all Gaussian modeling and is thus one of the most powerful models used in signal processing. We showed how to estimate the unobservable parameters given noisy measurements using our previous work on projection. We also coded up a short example to illustrate how this works in a simulation. For a much more detailed approach, see Luenberger (1997).
#
# As usual, the corresponding IPython notebook for this post  is available for download [here](https://github.com/unpingco/Python-for-Signal-Processing/blob/master/Gauss_Markov.ipynb).
#
# Comments and corrections welcome!

# References
# ---------------
#
# * Luenberger, David G. *Optimization by vector space methods*. Wiley-Interscience, 1997.


# -*- coding: utf-8 -*-
"""
Created on Mon Dec 7 17:07:32 2020
@author: Mingfen Wang
"""

import numpy as np

def coordinate_descent_method(A, b, guess, N, TOR):
    A_T = A.T
    x = guess.copy()
    k = 1
    while k < N:
        save_x = x.copy()
        for i in range(len(x)):
            b_in_Ax = b - np.dot(A, x)
            foot = np.dot(b_in_Ax, A_T[i]) / np.dot(A_T[i], A_T[i])
            x[i] = x[i] + foot
        diff_x = x - save_x
        if np.dot(diff_x, diff_x) < TOR:
            break
        k = k + 1
    return x, k

A = np.array([[1., -1., 0.],\
              [-1., 2., 1.],\
              [0., 1., 5.]])
b = np.array([3., -3., 4.])
guess = np.array([0., 0., 0.])
x, k = coordinate_descent_method(A, b, guess, 300 * len(b), 1e-6)
print("x = {} in {} iterations.".format(x.round(2), k))


#!/usr/bin/env python
import sys
from scipy.stats import t
from scipy.special import gammaln
import numpy as np
from numpy import pi,log,sqrt
from numpy.linalg import slogdet,inv

################################
### Multivariate Student's t ###
################################

### Multivariate Student's t density (log)
def dmvt(x,mu,Sigma,nu):
	##### IMPORTANT: also the scalars MUST be np arrays
	## Check that mu and Sigma have the appropriate dimension
	if mu.shape[0] != Sigma.shape[0]:
		raise ValueError("The arrays mu and Sigma must have compatible dimension.")
	if Sigma.shape[0] != 1:
		if Sigma.shape[0] != Sigma.shape[1]:
			raise ValueError("Sigma must be a squared matrix.")
	if mu.shape[0] == 1:
		## Exception when the PDF is unidimensional
		return t.logpdf(x,df=nu,loc=mu,scale=sqrt(Sigma))
	else:
		# Calculate the number of dimensions
		d = mu.shape[0]
		# Calculate the ratio of Gamma functions
		gamma_ratio = gammaln(nu/2.0 + d/2.0) - gammaln(nu/2.0)
		# Calculate the logarithm of the determinant
		logdet = -.5 * slogdet(Sigma)[1] - .5 * d * (log(pi) + log(nu))
		# Invert the scale matrix, centre the vector and calculate the main expression
		Sigma_inv = inv(Sigma)
		x_center = x - mu
		main_exp = -.5 * (nu + d) * log(1 + (x_center.dot(Sigma_inv)).dot(x_center) / nu)
		# Return the result
		return gamma_ratio + logdet + main_exp ## log-density of the Student's t

### Multivariate Student's t density (log) -- computed efficiently (determinants and inverses are stored)
def dmvt_efficient(x,mu,Sigma_inv,Sigma_logdet,nu):
	##### IMPORTANT: the scalars MUST be numpy arrays
	## Sigma_inv must be scaled by the degrees of freedom
	## Check that mu and Sigma have the appropriate dimension
	if mu.shape[0] != Sigma_inv.shape[0]:
		raise ValueError("The arrays mu and Sigma must have compatible dimension.")
	if Sigma_inv.shape[0] != 1:
		if Sigma_inv.shape[0] != Sigma_inv.shape[1]:
			raise ValueError("Sigma must be a squared matrix.")
	if mu.shape[0] == 1:
		## Exception when the PDF is unidimensional
		return t.logpdf(x,df=nu,loc=mu,scale=1/sqrt(Sigma_inv))
	else:
		# Calculate the number of dimensions
		d = mu.shape[0]
		# Calculate the ratio of Gamma functions
		gamma_ratio = gammaln(.5*(nu + d)) - gammaln(.5*nu)
		# Calculate the logarithm of the determinant
		logdet = -.5 * Sigma_logdet - .5 * d * (log(pi) + log(nu))
		# Centre the vector and calculate the main expression
		### IMPORTANT: Sigma_inv MUST BE SCALED by the degrees of freedom in input
		x_center = x - mu
		main_exp = -.5 * (nu + d) * log(1 + (x_center.dot(Sigma_inv)).dot(x_center))
		# Return the result
		return gamma_ratio + logdet + main_exp ## log-density of the Student's t


"""
nitrogen.math
-----------------

General mathematical tools and functions

"""

import numpy as np

def cumsimp(y, x, axis = 0):
    """
    Calculate the cumulative integral by Simpson's Rule

    Parameters
    ----------
    y : array_like
        The uniformly spaced samples
    x : array_like or scalar
        The sample points along the integration axis or, if scalar, the sample spacing.
    axis : int, optional
        The integration axis. The default is 0.

    Returns
    -------
    res : ndarray
        The result of cumulative integration.

    Notes
    -----

    The first element of the cumulative integral is
    fixed to zero. The second element is handled in one of two ways.
    If the length of `y` is only 2, then the trapezoid rule is used. Otherwise,
    the next element in `y` is used for quadratic interpolation.
    The remaining elements are evaluated via Simpson's 1/3 rule.

    """

    y = np.array(y)

    if y.shape[axis] < 1:
        raise ValueError('y must be have non-zero length along integration axis')

    if not np.isscalar(x):
        if len(x) != y.shape[axis]:
            raise ValueError('x must have the same integration points as y')
            # This is not strictly necessary because we assume the
            # integration points are uniform, but I will enforce this
            # check because it will probably catch some logical errors
            # or unintentional parameters

    # Move integration axis to the front
    y = np.moveaxis(y, axis, 0)

    res = np.zeros_like(y)

    # First element is always zero
    # res[0] = 0.0

    ylen = y.shape[0] # The integration length

    if ylen > 1:

        # Gather the sample spacing
        if np.isscalar(x):
            dx = x
        else:
            dx = x[1] - x[0]

        # Second element has two cases
        if ylen == 2:
            # 1) The total length is only 2
            # Use trapezoid rule
            res[1] = dx * 0.5 * (y[0] + y[1])
        else:
            # 2) The length is greater than 2
            #    Use quadratic interpolation for the
            #    first semgment ("half-way Simpson's Rule")
            res[1] = (dx/12) * (5*y[0] + 8*y[1] - y[2])

    if ylen > 2:
        for i in range(2,ylen):
            # For the third and later elements,
            # use the standard 1/3 Simpson's rule
            res[i] = res[i-2] + (dx/3) * (y[i-2] + 4*y[i-1] + y[i])

    # Move integration axis back to original position
    res = np.moveaxis(res, 0, axis)
    return res

def spech_fft(C,dt,sample_factor = 1, damping = 0.0):
    """
    Calculate the intensity spectrum from a hermitian
    autocorrelation function C.

    Parameters
    ----------
    C : ndarray
        The autocorrelation function.
    dt : float
        The time step.
    sample_factor : int
        The over-sampling factor. The default is 1.
    damping : float
        The Gaussian damping factor. The default is 0.

    Returns
    -------
    g : ndarray
        The (real) intensity spectrum
    freq : ndarray
        The angular frequency axis

    Notes
    -----
    The autocorrelation function is provided for :math:`t \geq 0`, i.e.
    C[0] is the :math:`t = 0` value. It is assumed that :math:`C(-t) = C(t)^*`.

    The returned spectrum approximates the Fourier transformation

    ..  math::

        g(\\omega) = \\frac{1}{2\\pi}\\, \\int_{-\\infty}^{\\infty} dt e^{i \\omega t} C(t)

    This normalization means that the angular frequency integral of
    :math:`g(\\omega)` equals :math:`C(0)`, which is typically unity.

    The calculated spectrum may be over-sampled by zero-padding the
    autocorrelation function. This sampling factor is controlled by
    `sample_factor`.

    A Gaussian window function is also applied to the autocorrelation
    function, of the form :math:`C(t) \\exp(-a (t/T)^2)`, where
    :math:`T` is length of the correlation function (before any padding).
    The damping factor :math:`a` is controlled by the `damping` keyword.
    At sufficiently large :math:`a`, the line shape becomes Gaussian
    with an angular frequency full-width at half-maximum equal to
    :math:`\\omega_\\text{FWHM} = 4 T^{-1} \\sqrt{a \\ln 2}`.

    """

    # First window and zero-pad as necessary
    tau = np.linspace(0.0, 1.0, len(C))
    C = C * np.exp(-damping * tau**2)
    # zero-pad
    sample_factor = int(sample_factor)
    if sample_factor < 1:
        raise ValueError("sample_factor must be a postive integer")
    Cz = np.zeros(len(C) * sample_factor, dtype = C.dtype)
    Cz[:len(C)] = C[:]

    #
    # Because C is hermitian, we can reconstruct
    # its negative-time values explicitly
    # (We could also just use numpy's hfft, but I will do it this way instead)
    fpos = Cz # t >= 0
    fneg = Cz[:0:-1].conj()  # t < 0

    f = np.concatenate((fneg, fpos)) # [-T,T]
    N = len(f)  # N is odd
    n = (N-1) // 2 # integer

    F = np.fft.fft(f, norm = None) # Use standard normalization fft
    k = np.concatenate((np.arange(0,-(n+1), -1), np.arange(n,0,-1)))

    t0 = -(N-1)/2 * dt

    # Correct for DFT phase
    G = (dt/(2*np.pi)) * np.exp(+1j * 2*np.pi * t0 / (N*dt)  * k) * F

    # Calculate angular frequency values
    dw = (2*np.pi) / (N*dt)
    freq = k * dw

    # Re-sort result in ascending frequency
    IDX = np.argsort(freq)
    freq = freq[IDX]
    g = np.real(G[IDX])

    return g, freq

def gaussianFWHM(x, fwhm, norm = 'area'):
    """
    Calculate a Gaussian function.

    Parameters
    ----------
    x : array_like
        Input array
    fwhm : scalar
        The full-width at half-maximum value.
    norm : {'area', 'amplitude'}
        The normalization convention. The default is 'area'. See Notes for
        definitions.

    Returns
    -------
    y : ndarray
        The result.

    Notes
    -----
    For `norm` = ``\`area\```, the integrated area is equal to
    unity. For `norm` = ``\`amplitude\```, the
    peak amplitude is equal to unity.

    """

    y = np.exp(-4*np.log(2) * (x/fwhm)**2)

    if norm == 'area':
        y *= np.sqrt(np.log(16)/np.pi) / fwhm
    elif norm == 'amplitude':
        pass
    else:
        raise ValueError('unexpected norm keyword')

    return y


#!/usr/bin/env python3
import unittest
import numpy as np
import Medusa.matrix
from Medusa.matrix.multiplication import multiply

class TestMatrixMultiplication(unittest.TestCase):
    def test_mult_if_A_not_2d(self):
        A = np.array([1,2,3])
        B = np.array([[3,4,5],[0,1,0]])
        with self.assertRaises(Exception) as ctx:
            multiply(A, B, 1)
        self.assertTrue('Must provide 2-dimensional matrix. Matrix A has 1 dimensions' in str(ctx.exception))

    def test_mult_if_B_not_2d(self):
        A = np.array([[3,4,5],[0,1,0]])
        B = np.array([1,2,3])
        with self.assertRaises(Exception) as ctx:
            multiply(A, B, 1)
        self.assertTrue('Must provide 2-dimensional matrix. Matrix B has 1 dimensions' in str(ctx.exception))

    def test_mult_improper_sized_inputs(self):
        A = np.array([[1, 2, 3],[4, 5, 6]])
        B = np.array([[1, 2, 3],[4, 5, 6]])
        with self.assertRaises(Exception) as ctx:
            multiply(A, B, 1)
        self.assertTrue('A (3x2) and B (3x2) row number does not match column number' in str(ctx.exception))

    def test_mult_sequential_execution(self):
        A = np.array([[-1, 4], [2, 3]])
        B = np.array([[9, -3], [6, 1]])
        C = multiply(A, B, 1)
        self.assertTrue(np.allclose(C, np.array([[15, 7], [36, -3]])))

    def test_mult_parallel_4_procs(self):
        A = np.array([[1, 2, 3, 4, 5] for i in range(5)])
        B = np.array([[i%2] for i in range(1, 6)])
        C = multiply(A, B, 4)
        print(A, "times", B, "=", C)
        self.assertTrue(np.allclose(C, np.array([[9] for i in range(5)])))

    def test_mult_parallel_8_procs(self):
        A = np.array([[1, -2, 3] for i in range(10)])
        B = np.array([[i+1] for i in range(3)])
        C = multiply(A, B, 4)
        self.assertTrue(np.allclose(C, np.array([[6] for i in range(10)])))

    def test_mult_by_identity_mtx(self):
        A = np.array([[i, i**2, i**3] for i in range(3)])
        B = np.array([[1, 0, 0],[0, 1, 0], [0, 0, 1]])
        C = multiply(A, B, 1)
        self.assertTrue(np.allclose(C, A))


import numpy as np
import random
import pickle

REWARD = -0.1
GAMMA = 0.7
DISCOUNT = 0.99

ACTIONS = [(1, 0), (0, -1), (-1, 0), (0, 1)]  # actions which can be D, L, U, R
N_ACTIONS = len(ACTIONS)

# Grid size based on amount of rows and columns
ROW = 4
COL = 4

# Grid with terminal states
u = [[0, 0, 0, 40],
     [0, 0, 0, 0],
     [0, 0, 0, 0],
     [10, 0, 0, 0]]

def maze_grid(arr, policy=False):
    """This function initializes the maze grid with all of the
    given rewards per state and prints it out nicely.
    The grid has 4 rows and 4 columns which is set in the cell above.
    """
    res = ""
    for r in range(ROW):
        res += "|"
        for c in range(COL):
            if r == 0 and c == 3:
                val = "F"
            elif r == 3 and c == 0:
                val = "F"
            elif r == 3 and c == 2:
                val += " S"

            else:
                if policy:
                    # arrow symbols
                    val = ['\u2193', '\u2190', '\u2191', '\u2192'][arr[r][c]]
                else:
                    val = str(arr[r][c])
            res += " " + val[:5].ljust(5) + " |"
        res += "\n"
    print(res)

def get_utility(rewards, r, c, action):
    """This function gets the utility of the state reached
    by performing the given action from the given state
    """
    dr, dc = ACTIONS[action]
    newR = r + dr
    newC = c + dc
    start = (newR == 3 and newC == 2)
    if newR < 0 or newC < 0 or newR >= ROW or newC >= COL or start:
        return rewards[r][c]
    else:
        return rewards[newR][newC]

def calculate_utility(rewards, r, c, action):
    """This function calculates the utility of a state given
    an action
    """
    if r == 3 and c == 1:
        u = -2
    elif r == 1 and c == 2:
        u = -10
    elif r == 1 and c == 3:
        u = -10
    else:
        u = REWARD

    u += 0.1 * DISCOUNT * get_utility(rewards, r, c, (action-1)%4)
    u += GAMMA * DISCOUNT * get_utility(rewards, r, c, action)
    u += 0.1 * DISCOUNT * get_utility(rewards, r, c, (action+1)%4)

    return u

def value_iteration(rewards):
    """This function initializes the maze grid with all of the
    given rewards per state and prints it out nicely.
    The grid has 4 rows and 4 columns which is set in the cell above.
    """
    print("During the value iteration:\n")
    while True:
        next_utility = [[0, 0, 0, 40],
                        [0, 0, 0, 0],
                        [0, 0, 0, 0],
                        [10, 0, 0, 0]]
        error = 0
        for r in range(ROW):
            for c in range(COL):
                terminal_states = [(r == 0 and c == 3),
                                   (r == 3 and c == 0),
                                   (r == 3 and c == 1)]
                if any(terminal_states):
                    continue
                next_utility[r][c] = max([calculate_utility(rewards, r, c, action) for action in range(N_ACTIONS)]) # Bellman update
                error = max(error, abs(next_utility[r][c]-rewards[r][c]))
        rewards = next_utility
        maze_grid(rewards)
        if error < ((1-GAMMA) / GAMMA):
            break
    return rewards

def optimal_policy(rewards):
    """This function gets the optimal policy from U
    """
    policy = [[-1,-1,-1,-1],
              [-1,-1,-10,-10],
              [-1,-1,-1,-1],
              [-1,-2,-1,-1]]

    for r in range(ROW):
        for c in range(COL):
            terminal_states = [(r == 0 and c == 3),
                               (r == 3 and c == 0),
                               (r == 3 and c == 1)]
            if any(terminal_states):
                continue
            # Choose the action that maximizes the utility
            maxAction, maxU = None, -float("inf")
            for action in range(N_ACTIONS):
                u = calculate_utility(rewards, r, c, action)
                if u > maxU:
                    maxAction, maxU = action, u
            policy[r][c] = maxAction
    return policy

def create_random_policy(row, col, min, max):
    """Creates a random policy.
    """
    pol = np.random.randint(min, max, size=(row, col))
    pol_grid = maze_grid(pol, True)
    return pol, pol_grid

# Print the initial environment
print("The initial maze grid is:\n")
maze_grid(u)

# Value iteration
rewards = value_iteration(u)
# print(rewards)

# Get the optimal policy from U and print it
policy = optimal_policy(rewards)
print("The optimal policy is:\n")
maze_grid(policy, True)