from __future__ import division, print_function
from sympy import *

_inverse_relations = { Ge: Le, Le: Ge, Gt: Lt, Lt: Gt }

def add_equations(equation1,equation2):
    r"""
    Adds each side of two equations and returns the resulting equation.

    Parameters
    ==========
    equation1 : Equality
    equation2 : Equality

    Examples
    ========
    ```python
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x,y = symbols('x,y',real=True)

    >>> eq1 = Eq(x+10, 4*y-2)
    >>> eq2 = Eq(sin(x)+5*x, -y**2)
    >>> add_equations(eq1,eq2)
    Eq(-4*x - sin(x) + 10, y**2 + 4*y - 2)
    ```
    """
    return Eq( equation1.lhs + equation2.lhs, equation1.rhs+equation2.rhs )

def sub_equations(equation1,equation2):
    r"""
    Subtracts each side of two equations and returns the resulting equation.

    Parameters
    ==========
    equation1 : Equality | minuend
    equation2 : Equality | subtrahend

    Example
    =======
    ```python
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x,y = symbols('x,y',real=True)

    >>> eq1 = Eq(x+5, y-3)
    >>> eq2 = Eq(x, 10*y)
    >>> sub_equations(eq1,eq2)
    Eq(5, -9*y - 3)
    ```
    """
    return Eq( equation1.lhs - equation2.lhs, equation1.rhs-equation2.rhs )

def mul_equations(equation1,equation2):
    r"""
    Multiplies each side of two equations and returns the resulting equation.

    Parameters
    ==========
    equation1 : Equality
    equation2 : Equality

    Example
    =======
    ```python
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x,y = symbols('x,y',real=True)

    >>> eq1 = Eq(x+5, x**2)
    >>> eq2 = Eq(y+10*x, 6)
    >>> mul_equations(eq1,eq2)
    Eq((x + 5)*(10*x + y), 6*x**2)
    ```
    """
    return Eq( equation1.lhs * equation2.lhs, equation1.rhs*equation2.rhs )

def div_equations(equation1,equation2):
    r"""
    Divides each side of two equations and returns the resulting equation.

    Parameters
    ==========
    equation1 : Equality | dividend
    equation2 : Equality | divisor

    Example
    =======
    ```python
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x,y,th = symbols(r'x,y,theta',real=True)

    >>> eq1 = Eq(sin(th), y)
    >>> eq2 = Eq(cos(th), x)
    >>> div_equations(eq1,eq2)
    Eq(sin(theta)/cos(theta), y/x)
    ```
    """
    return Eq( equation1.lhs/equation2.lhs, equation1.rhs/equation2.rhs )

def invert_ineq(ineq):
    r"""
    Changes the direction of an inequality

    Parameters
    ==========
    ineq : Relational

    Returns
    =======
    Relational, direction inverted

    Example
    =======
    ```
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x = symbols('x',real=True)

    >>> ineq = Ge(x,3)
    >>> invert_ineq(ineq)
    -x <= -3
    ```
    """
    return _inverse_relations[ineq.func]( -ineq.lhs, -ineq.rhs, evaluate=False)

def _both_sides_add_ineq(relation,argument):
    r"""
    Adds `argument` to each side of `relation`, according to the property
    $ x>y \implies x + c > y + c $, where $c \in \mathbb R$.
    """
    return relation.func( relation.lhs+argument, relation.rhs+argument, evaluate=False )

def _both_sides_mul_ineq(relation,argument,interval,variable):
    r"""
    Multiplies `argument` to each side of `relation`.

    If `interval` is not present, global assumptions are used (properties is_positive, is_negative).

    If `interval` is not present, the function returns the resulting inequality.

    Rules are made according to: $x \le y \implies xc \le yc$ if $c>0$ and $xc \ge yc$ if $c<0$.

    If `interval` is present, `variable` must be too, and `solveset` is used to determine
    the intervals where the inequality must or not change sign (solveset supports only univariable
    inequalities).

    If `interval` is present, the function returns a dictionary with intervals and solutions.
    """
    if interval is None:
        if argument.is_positive == True:
            return relation.func( relation.lhs*argument, relation.rhs*argument, evaluate=False )
        elif argument.is_negative == True:
            return _inverse_relations[relation.func]( relation.lhs*argument, relation.rhs*argument, evaluate=False )
        elif argument == 0:
            return relation.func(0,0)
        else:
            raise ValueError("Couldn't determine sign of the argument")
    else:
        if variable is None:
            raise TypeError("If `interval` is given, a variable must be specified. Only univariable inequalities are supported")
        # Determine where in the given interval the argument is positive, negative and zero:
        positive_interval = solveset(argument>0,variable,domain=interval)
        negative_interval = solveset(argument<0,variable,domain=interval)
        zeros = solveset(argument,variable,domain=interval)
        # The resulting inequalities depend on the interval of the argument
        ans_if_positive = relation.func( relation.lhs*argument, relation.rhs*argument, evaluate=False )
        ans_if_negative = _inverse_relations[relation.func]( relation.lhs*argument, relation.rhs*argument, evaluate=False )
        ans_if_zero = relation.func(0,0)
        return {positive_interval: ans_if_positive, negative_interval: ans_if_negative, zeros: ans_if_zero }

def _both_sides_pow_ineq(relation,argument,interval,variable):
    r"""
    Applies a power to both sides of an inequality. The only implemented power is -1 (reciprocal of both sides).
    If `interval` is not present, global assumptions are used (properties is_positive, is_negative).

    If `interval` is not present, the function returns the resulting inequality.

    The rules implemented are:
    $ x > y \implies 1/x < 1/y$ if $x>0,y>0$ or $x<0,y<0$, and $1/x>1/y$ if $x>0,y<0$ or $x<0,y>0$

    If `interval` is present, `variable` must be too, and `solveset` is used to determine
    the intervals where the inequality must or not change sign (solveset supports only univariable
    inequalities).

    If `interval` is present, the function returns a dictionary with intervals and solutions.

    `solveset` can be useful in order to determine an adequate interval where the original inequality holds.

    Example
    =======
    ```python
    >>> from sympy_relational_tools import *
    >>> x = symbols('x',real=True)
    >>> ie1 = Ge(x,x**2,evaluate=False); ie1
    x >= x**2
    >>> both_sides(ie1,Pow,-1,Interval(-oo,oo),x)
    {Interval.open(0, oo): 1/x <= x**(-2), Interval.open(-oo, 0): 1/x >= x**(-2)}
    ```
    """
    if argument == -1:
        if interval is None:
            # Method 1: global assumptions
            if variable is None:
                raise TypeError("If `interval` is given, a variable must be specified. Only univariable inequalities are supported")
            if (relation.lhs.is_positive == True) and (relation.rhs.is_positive == True):
                return relation.func( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            elif (relation.lhs.is_positive == True) and (relation.rhs.is_negative == True):
                return relation.func( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            elif (relation.lhs.is_negative == True) and (relation.rhs.is_positive == True):
                return relation.func( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            elif (relation.lhs.is_negative == True) and (relation.rhs.is_negative == True):
                return _inverse_relations[relation.func]( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            elif (relation.lhs == 0):
                raise ZeroDivisionError("Can't take reciprocals with one side zero")
            elif (relation.rhs == 0):
                raise ZeroDivisionError("Can't take reciprocals with one side zero")
            else:
                raise ValueError("Couldn't determine sign of the expressions")
        else:
            # Method 2: solveset
            # Determine where are both sides positive or negative
            lhs_positive_interval = solveset(relation.lhs>0,variable,domain=interval)
            rhs_positive_interval = solveset(relation.rhs>0,variable,domain=interval)
            lhs_negative_interval = solveset(relation.lhs<0,variable,domain=interval)
            rhs_negative_interval = solveset(relation.rhs<0,variable,domain=interval)

            if (relation.lhs == 0):
                raise ZeroDivisionError("Can't take reciprocals with one side zero")
            elif (relation.rhs == 0):
                raise ZeroDivisionError("Can't take reciprocals with one side zero")

            # For both sides positive or negative, the relation is inversed
            pp_interval = lhs_positive_interval.intersect(rhs_positive_interval)
            nn_interval = lhs_negative_interval.intersect(rhs_negative_interval)
            eqsigns = _inverse_relations[relation.func]( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            # For both sides of different signs, the relation is not inversed
            pn_interval = lhs_positive_interval.intersect(rhs_negative_interval)
            np_interval = lhs_negative_interval.intersect(rhs_positive_interval)
            difsigns = relation.func( 1/relation.lhs, 1/relation.rhs, evaluate=False )

            result = {}
            if nn_interval != EmptySet():
                result[nn_interval] = eqsigns
            if pp_interval != EmptySet():
                result[pp_interval] = eqsigns
            if pn_interval != EmptySet():
                result[pn_interval] = difsigns
            if np_interval != EmptySet():
                result[np_interval] = difsigns

            return result

    else:
        raise NotImplementedError("Only reciprocals (Pow,-1) are implemented")

def both_sides(relation,function,argument=None,interval=None,variable=None):
    r"""
    Applies a `function` with an `argument` to a `relation` and returns the resulting
    Relational.

    Parameters
    ==========
    relation: Relational
    function: sympy function to be applied
        currently implemented: Add,Mul,Pow,factor,simplify,collect,expand,together,apart
    argument: Sympy expression (optional)
    interval: Interval() (optional)
        interval in which the relation holds (intended to be used with inequalities)
    variable: Sympy expression (optional)
        if `interval` is specified, variable must also be specified. (Intended to be
        used with inequalities; only univariable inequalities are implemented)

    Warnings
    ========
    * Specifying an `interval` causes the fucntion to use `solveset`, which is slow.
    * Differentiation and integration are not implemented for equalities.
    * Unequalities are not supported.

    Rules implemented
    =================
    * Equality: any function applied to both sides preserves equality.
    * Addition to inequalities: $ x>y \implies x + c > y + c $, where $c \in \mathbb R$.
    * Multiplication to inequalities: $x \le y \implies xc \le yc$ if $c>0$ and $xc \ge yc$ if $c<0$.
    * Reciprocal of an inequality: $ x > y \implies 1/x < 1/y$ if $x>0,y>0$
      or $x<0,y<0$, and $1/x>1/y$ if $x>0,y<0$ or $x<0,y>0$
    * Subtraction and division of a quantity are achieved with addition of the inverse
      and multiplication of the reciprocal.

    Examples
    ========
    ```python
    >>> from sympy_relational_tools import *
    >>> x,y = symbols('x,y',real=True)
    >>> a = symbols('a',positive=True)
    >>> equation = Eq(5*x+x**2, x+5)
    >>> both_sides(equation,Add,-5*x)
    Eq(x**2, -4*x + 5)

    >>> equation1 = Eq(sin(x),x+1)
    >>> equation2 = both_sides(equation1,Pow,-2)
    >>> equation1
    Eq(sin(x), x + 1)
    >>> both_sides(equation2,expand)
    Eq(sin(x)**(-2), 1/(x**2 + 2*x + 1))

    >>> inequality = Ge(x+1,0,evaluate=False)
    >>> both_sides(inequality,Mul,a)
    a*(x + 1) >= 0

    >>> inequality = Ge(x+1,x**2,evaluate=False)
    >>> both_sides(inequality,Pow,-1,Interval(-1,1),x)
    {Union(Interval.open(-1, 0), Interval.Lopen(0, 1)): 1/(x + 1) <= x**(-2)}
    ```
    """
    if relation.func == Eq:
        return Eq( function(relation.lhs,argument), function(relation.rhs,argument) )
    else:
        if function == Add:
            return _both_sides_add_ineq(relation,argument)
        elif function == Mul:
            return _both_sides_mul_ineq(relation,argument,interval,variable)
        elif function == Pow:
            return _both_sides_pow_ineq(relation,argument,interval,variable)
        elif function in [factor,simplify,collect,expand,together,apart]:
            return relation.func( function(relation.lhs), function(relation.rhs), evaluate=False )
        else:
            raise NotImplementedError("Function not yet implemented")


#!/usr/bin/python
"""
author: Bhekimpilo Ndhlela
author: 18998712
module: Applied Mathematics(Numerical Analysis) TW324
task  : computer assignment 01
since : Friday-09-02-2018
"""

def question3a(debug=False):
    # get approximation values [J0, J1] respectively
    J0 = get_appr3A(0)
    J1 = get_appr3A(1)

    #get built in values for [J0, J1] respectively
    blt_val0 = ss.jn(0, 1)
    blt_val1 = ss.jn(1, 1)

    #get absolute error [abs_err0, abs_err1] respectively
    abs_err0 = abs(J0 - blt_val0)
    abs_err1 = abs(J1 - blt_val1)

    if debug is True:
        print "(appro_val0, blt_val0, abs_err0) = (" + str(J0) + ", " + str(blt_val0) + ", " + str(abs_err1)+")"
        print "(appro_val1, blt_val1, abs_err1) = (" + str(J1) + ", " + str(blt_val1) + ", " + str(abs_err1)+")"

def get_appr3A(n, x=1):
    J = 0
    for k in xrange(0, 4): # "4" is exclusive
        N = pow(-1, k) * pow(float(x)/2, n + (2 * k))
        D = sm.factorial(k) * sm.factorial(n + k)
        J = J + (N / D)
    return J

def question3b(x=1, debug=True):
    vector_J_A = []     #vector to store approximations
    vector_J_B = []     #vector to store built in value
    vector_J_A.append(get_appr3A(0))
    vector_J_A.append(get_appr3A(1))

    for n in xrange(1, 7):
        vector_J_A.append((((2 * n) / x) * vector_J_A[n]) - vector_J_A[n - 1])
    for n in xrange(0, 8):
        vector_J_B.append(ss.jn(n, 1))

    print len(vector_J_A), len(vector_J_B)
    if debug is True:
        print vector_J_A
        print vector_J_B

import scipy.special as ss
#import this for scipy misc factorial [for efficient factorial]
import scipy.misc as sm

question3a()
question3b()


import numpy as np
import math
import matplotlib.pyplot as plt

x=np.array([10,1,5,6,2,4,6,3,7,6,0,4,2,3,6,4,5,9,1,2,8,7]) #Hours studied
y=np.array([1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,0,1,0,1,1,1]) #Pass=1 and Fail=0

plt.scatter(x,y,color="r",marker="*",s=100)

W=0
B=0
alpha = 0.01

def Sigmoid(z):
    G_Z = float(1.0 / float((1.0 + math.exp(-1.0*z))))
    return G_Z

def Gradient_Descent(a,b,x,y,hours):
    for i in range(10000):
        gradient1=0
        gradient2=0
        for j in range(len(x)):
            gradient1+=((Sigmoid(a+(b*x[j]))-y[j]))
            gradient2+=((Sigmoid(a+(b*x[j]))-y[j])*x[j])
        a-=(alpha*gradient1)
        b-=(alpha*gradient2)
    return a,b,Sigmoid((b*hours)+a)

def graph(a,b):
    y=[]
    x=np.array(range(0,12))
    for i in x:
        y.append(Sigmoid(b*i+a))
    plt.plot(x,y)
    plt.show()

hr=int(input('Enter no. of hours studied: '))
W,B,percentage=Gradient_Descent(W,B,x,y,hr)
print('\nThere is '+str(round(percentage*100,2))+'% chance that you will pass in the exam.')

graph(W,B)


#import our library
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

#Get our dataset
train_data = np.genfromtxt('iris-Train.data', delimiter=',', names=True)
test_data = np.genfromtxt('iris-Test.data', delimiter=',', names=True)

#Define our features and target
x1 = np.transpose(np.asmatrix(train_data['x1']))
x2 = np.transpose(np.asmatrix(train_data['x2']))
x = np.append(x1, x2, 1)
y = np.transpose(np.asmatrix(train_data['y']))

#-----------------------------------------------------------

xtest1 = np.transpose(np.asmatrix(test_data['x1']))
xtest2 = np.transpose(np.asmatrix(test_data['x2']))
xtest = np.append(xtest1, xtest2, 1)
ytest = np.transpose(np.asmatrix(test_data['y']))

#Define theta and x_0
ones = np.ones((x.shape[0],1))
onestest = np.ones((xtest.shape[0],1))
one_x = np.append(ones, x, 1)
one_xtest = np.append(onestest, xtest, 1)

m, n = np.shape(one_x)
theta = np.zeros((1,n))
theta = np.transpose(theta)
theta_new = theta

#main function for binary classification(logistic Regression)

#Define our iteration & Learning Rate
loops= 1000
alpha = 0.002
#--------------------------------

#Define our MSE function with Gradient
mse = np.zeros((loops,2))
r = 0

while r < loops:

    h_theta = 1/(1+np.exp(-1*(one_x*theta)))
    gradient = one_x.T*(h_theta-y)
    theta = theta - alpha*gradient
    y_pred = 1/(1+np.exp(-1*(one_x*theta)))

    loop_mse = 0
    for i in range(0, m):

        loop_mse += -(y.A1[i]*np.log(1/(1+np.exp(-1*(one_x[i]*theta))))) - ((1-y.A1[i])*np.log(1-(1/(1+np.exp(-1*(one_x[i]*theta))))))
    loop_mse = loop_mse/(m)
    mse[r,0] = r
    mse[r,1] = loop_mse

    r += 1

print('Number of iteration = ',loops)
print('Learning Rate = ',alpha)
print('Theta[0] = ', theta.A1[0])
print('Theta[1] = ', theta.A1[1])
print('Theta[2] = ', theta.A1[2])

#Calculate our h(theta)
y_pred = 1/(1+np.exp(-1*(one_x*theta)))
y_pred_test = 1/(1+np.exp(-1*(one_xtest*theta)))

#calculate train MSE
train_mse = 0
m = y.shape[0]
for i in range(m):
    train_mse += ((y.A1[i] - y_pred.A1[i])**2)
train_mse = train_mse/(2*m)
print("Train MSE = ", train_mse)

#calculate test MSE
test_mse = 0
m = ytest.shape[0]
for i in range(m):
    test_mse += ((ytest.A1[i] - y_pred_test.A1[i])**2)
test_mse = test_mse/(2*m)
print("Test MSE = ", test_mse)

#classification our dataset in 2 class
minx1 = np.min(x1)
maxx1 = np.max(x1)
x2_1 = -(theta.A1[0] + theta.A1[1]*minx1)/theta.A1[2]
x2_2 = -(theta.A1[0] + theta.A1[1]*maxx1)/theta.A1[2]

#Plot our dataset and MSE
plt.scatter(train_data['x1'],train_data['x2'], c=train_data['y'], s = 10, label = 'Train data')
plt.scatter(train_data['x1'],train_data['x2'], c=train_data['y'], s = 10, label = 'Train data')
plt.legend(loc = 'upper left')
plt.plot((minx1,maxx1), (x2_1,x2_2))
plt.show()

plt.scatter(test_data['x1'],test_data['x2'], c=test_data['y'], s = 10, label = 'Test data')
plt.scatter(test_data['x1'],test_data['x2'], c=test_data['y'], s = 10, label = 'Test data')
plt.legend(loc = 'upper left')
plt.plot((minx1,maxx1), (x2_1,x2_2))
plt.show()

plt.plot(mse.T[0], mse.T[1], color = "black", label = 'MSE')
plt.legend(loc = 'upper right')
plt.show()

Binary Classification Logistic regression/LogisticRegression (Prart 1(A)).py


import sympy as sy
from sympy import pi, cos, sin, tan

q1 = sy.Symbol("q1")
q2 = sy.Symbol("q2")
q3 = sy.Symbol("q3")

qvec = sy.Matrix([[q1, q2, q3,]]).T

l1 = sy.Symbol("l1")
l2 = sy.Symbol("l2")
l3 = sy.Symbol("l2")
l4 = sy.Symbol("l2")

# l1 = 1.0
# l2 = 1.0
# l3 = 1.0
# l4 = 1.0

D1 = sy.Matrix([
        [cos(q1), -sin(q1), 0.0],
        [sin(q1), cos(q1), 0.0],
        [0.0, 0.0, 1.0],
    ])

D2 = sy.Matrix([
        [1.0, 0.0, l1],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0],
    ])

D3 = sy.Matrix([
        [cos(q2), -sin(q2), 0.0],
        [sin(q2), cos(q2), 0.0],
        [0.0, 0.0, 1.0],
    ])

D4 = sy.Matrix([
        [1.0, 0.0, l2],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0],
    ])

theta = 270/180*pi
D5 = sy.Matrix([
        [cos(theta), -sin(theta), 0.0],
        [sin(theta), cos(theta), 0.0],
        [0.0, 0.0, 1.0],
    ])

D6 = sy.Matrix([
        [1.0, 0.0, l3],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0],
    ])

D7 = sy.Matrix([
        [cos(q3), -sin(q3), 0.0],
        [sin(q3), cos(q3), 0.0],
        [0.0, 0.0, 1.0],
    ])

D8 = sy.Matrix([
        [1.0, 0.0, l4],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0],
    ])

D_end = D1 * D2 * D3 * D4 * D5 * D6 * D7 * D8
D_end = sy.simplify(D_end)

print(D_end[0, 2])
print(D_end[1, 2])
print(D_end[2, 2])

# #x, y, phi = sy.symbols('x, y, phi')

# x = 2.9
# y = -0.9
# phi = float(pi)/2

# fx = x - D_end[0, 2]
# fy = y - D_end[1, 2]
# fq = q1 + q2 + q3 -phi -2*float(pi)

# sol = sy.solve([fx, fy, fq], [q1, q2, q3])
# print(sol)


import numpy as np

def cagr(risk_returns,Rebalancement_frequency):
        """

        Compute the Compound Annual Growth Rate (CAGR)
        Parameters
        ----------
        risk_returns : np.ndarray
        Rebalancement_frequency : np.float64
        Returns
        ----------
        float64, CAGR

        """
        Value_earned = Cumulative_ret(risk_returns)[-1]
        N = risk_returns.size / Rebalancement_frequency
        cagr = Value_earned ** (1/N) - 1
        return cagr

def Cumulative_ret(risk_returns):
        """
        Compute the Cumulative return across time
        Parameters
        ----------
        risk_returns : np.ndarray
        Returns
        ----------
        np.ndarray, CAGR

        """
        return np.cumprod(1 + risk_returns)


# -*- coding: utf-8 -*-
"""
Created on Wed Feb 13 13:31:03 2019

@author: Aditya's HP Omen 15
"""

# this program if for line fitting using OLS (oridnary least squares)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
import pickle
from statistics import mean
from numpy import linalg as LA

style.use('fivethirtyeight')

# DATA1
f = open('data1_new.pkl','rb')
data1 = pickle.load(f)
f.close()
da = np.asarray(data1)
X = da[:,0]
Y = da[:,1]
plt.axis([-150,150,-100,100])

def TLS(X,Y):
    numerator = (np.sum(Y**2)- Y.shape[0]*(mean(Y)**2))-(np.sum(X**2)- X.shape[0]*(mean(X)**2))
    denominator = X.shape[0]*mean(X)*mean(Y) -np.sum(np.multiply(X,Y))
    B = 0.5*numerator/denominator
    m = -B+ np.sqrt(B*B+1)
    c = mean(Y) -m*mean(X)
    return m,c

TLS_m,TLS_c = TLS(X,Y)
TLS_regression_line = [(TLS_m*x)+TLS_c for x in X]
plt.figure(1)
plt.scatter(X,Y, color = 'blue')
plt.plot(X,TLS_regression_line, color = 'red', label = "TLS")

def OLS(X,Y):
    m = (np.multiply(mean(X),mean(Y)) -mean(np.multiply(X,Y) ))/((mean(X))**2-mean((X)**2))
    c = mean(Y) - m*mean(X)
    return m,c
m,c = OLS(X,Y)
regression_line = [(m*x)+c for x in X]
plt.scatter(X,Y, color = 'blue')
plt.plot(X,regression_line, color = 'green', label = "OLS")
plt.title("Line fitting with vertical and orthogonal least squares on data 1")

plt.legend()
plt.axis([-150,150,-100,100])
plt.show()

#DATA2
f = open('data2_new.pkl','rb')
data1 = pickle.load(f)
f.close()
da = np.asarray(data1)
X = da[:,0]
Y = da[:,1]
plt.axis([-150,150,-100,100])

def TLS(X,Y):
    numerator = (np.sum(Y**2)- Y.shape[0]*(mean(Y)**2))-(np.sum(X**2)- X.shape[0]*(mean(X)**2))
    denominator = X.shape[0]*mean(X)*mean(Y) -np.sum(np.multiply(X,Y))
    B = 0.5*numerator/denominator
    m = -B+ np.sqrt(B*B+1)
    c = mean(Y) -m*mean(X)
    return m,c

TLS_m,TLS_c = TLS(X,Y)
TLS_regression_line = [(TLS_m*x)+TLS_c for x in X]
plt.figure(2)
plt.scatter(X,Y, color = 'blue')
plt.plot(X,TLS_regression_line, color = 'red', label = "TLS")

def OLS(X,Y):
    m = (np.multiply(mean(X),mean(Y)) -mean(np.multiply(X,Y) ))/((mean(X))**2-mean((X)**2))
    c = mean(Y) - m*mean(X)
    return m,c
m,c = OLS(X,Y)
regression_line = [(m*x)+c for x in X]
plt.plot(X,regression_line, color = 'green', label = "OLS")
plt.title("Line fitting with vertical and orthogonal least squares on data 1")
plt.legend()
plt.axis([-150,150,-100,100])
plt.show()

#DATA3
f = open('data3_new.pkl','rb')
data1 = pickle.load(f)
f.close()
da = np.asarray(data1)
X = da[:,0]
Y = da[:,1]
plt.axis([-150,150,-100,100])

def TLS(X,Y):
    numerator = (np.sum(Y**2)- Y.shape[0]*(mean(Y)**2))-(np.sum(X**2)- X.shape[0]*(mean(X)**2))
    denominator = X.shape[0]*mean(X)*mean(Y) -np.sum(np.multiply(X,Y))
    B = 0.5*numerator/denominator
    m = -B+ np.sqrt(B*B+1)
    c = mean(Y) -m*mean(X)
    return m,c

TLS_m,TLS_c = TLS(X,Y)
TLS_regression_line = [(TLS_m*x)+TLS_c for x in X]
plt.figure(3)
plt.scatter(X,Y, color = 'blue')
plt.plot(X,TLS_regression_line, color = 'red', label = "TLS")

def OLS(X,Y):
    m = (np.multiply(mean(X),mean(Y)) -mean(np.multiply(X,Y) ))/((mean(X))**2-mean((X)**2))
    c = mean(Y) - m*mean(X)
    return m,c
m,c = OLS(X,Y)
regression_line = [(m*x)+c for x in X]
plt.plot(X,regression_line, color = 'green', label = "OLS")
plt.title("Line fitting with vertical and orthogonal least squares on data 3")
plt.legend()
plt.axis([-150,150,-100,100])
plt.show()


#Imports
import numpy as np
from numpy.fft import rfft2, irfft2 #Importing built in 2D Fast Fourier Transforms: rfft2 is 2D real fast fourier transform, irfft2 is 2D inverse real fast fourier transform

InputArray = np.zeros((100,100)) #Array to be fourier transformed
for i in range(100):
    for j in range(100):
        InputArray[i,j] = i+j
print InputArray[10,10]

FT = rfft2(InputArray) #Fourier transform function takes array as input and outputs an array of 1/2 N +1 size. e.g. If InputArray is 100x100, FT will be 100x51. FT is array of coefficients of sinusoidal functions.
print FT[10,10]

#FT array can be manipulated e.g. Removing coefficients for smoothing

IFT = irfft2(FT) #Inverse fourier transform function takes array as input and outputs an array of 2(N-1) size e.g. If FT is 100x51, IFT will be 100x100
print IFT[10,10]

#An explicit Discrete Fourier Transform code is supplied in the slides, but is more complex and less efficient than the in built fast fourier transforms.


import math
import numpy as np
from itertools import combinations

def factorize(n):
	result = []
	for i in range(1,int(np.sqrt(n)+1)):
		if (n%i==0):
			result.append(i)
			result.append(int(n/i))
		result.sort()
	return result

def sumoffactors(n):
	factors = factorize(n)
	sum = 0
	for factor in factors:
		sum +=factor
	return sum-n

#print(factorize(220))
#print(sumoffactors(220))
#print(factorize(284))
#print(sumoffactors(284))

def amicable(a, b):
	if sumoffactors(a) == b and sumoffactors(b) ==a:
		return True
	else:
		return False
comb = combinations(range(2,10001),2)
sum = 0
for item in comb:
	a,b = item
	#print(item)
	if amicable(a,b):
		print(a,b)
		sum+= a+b
print(sum)


#!/usr/bin/env python
"""
    Artificial Intelligence for Humans
    Volume 1: Fundamental Algorithms
    Python Version
    http://www.aifh.org
    http://www.jeffheaton.com

    Code repository:
    https://github.com/jeffheaton/aifh

    Copyright 2013 by Jeff Heaton

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

    For more information on Heaton Research copyrights, licenses
    and trademarks visit:
    http://www.heatonresearch.com/copyright
============================================================================================================
This example shows how three different distance metrics calculate the same three points.

Euclidean Distance
pos1->pos2: 5.19615242271
pos2->pos3: 5.19615242271
pos3->pos1: 10.3923048454

Manhattan (city block) Distance

pos1->pos2: 9.0
pos2->pos3: 9.0
pos3->pos1: 18.0

Chebyshev Distance

pos1->pos2: 3.0
pos2->pos3: 3.0
pos3->pos1: 6.0
"""
__author__ = 'jheaton'

import os
import sys
from scipy.spatial import distance

# Find the AIFH core files
aifh_dir = os.path.dirname(os.path.abspath(__file__))
aifh_dir = os.path.abspath(aifh_dir + os.sep + ".." + os.sep + "lib" + os.sep + "aifh")
sys.path.append(aifh_dir)

# Create three different positions.
pos1 = [1.0, 2.0, 3.0]
pos2 = [4.0, 5.0, 6.0]
pos3 = [7.0, 8.0, 9.0]

# Calculate the distance between the specified points in 3 metrics.
print("Euclidean Distance")
print("pos1->pos2: " + str(distance.euclidean(pos1, pos2)))
print("pos2->pos3: " + str(distance.euclidean(pos2, pos3)))
print("pos3->pos1: " + str(distance.euclidean(pos3, pos1)))
print("\nManhattan (city block) Distance\n")
print("pos1->pos2: " + str(distance.cityblock(pos1, pos2)))
print("pos2->pos3: " + str(distance.cityblock(pos2, pos3)))
print("pos3->pos1: " + str(distance.cityblock(pos3, pos1)))
print("\nChebyshev Distance\n")
print("pos1->pos2: " + str(distance.chebyshev(pos1, pos2)))
print("pos2->pos3: " + str(distance.chebyshev(pos2, pos3)))
print("pos3->pos1: " + str(distance.chebyshev(pos3, pos1)))


import math
import base64
import sympy
import numpy as np
from matplotlib.figure import Figure
from io import BytesIO

def graphic_method(f, f_str, fp, fp_str, x_a, x_b, step=100):

    x = [x for x in np.linspace(x_a, x_b, step)]
    y = [f(x) for x in np.linspace(x_a, x_b, step)]
    yp = [fp(x) for x in np.linspace(x_a, x_b, step)]

    fig = Figure()
    ax = fig.subplots()

    # ax.title = "Metodo grafico"
    ax.xlabel = "x"
    ax.ylabel = "y"
    ax.plot(x, y, label=f"f(x)={f_str}")
    ax.plot(x, yp, "--", label=f"f'(x)={fp_str}")
    ax.legend()
    ax.grid()

    # Save it to a temporary buffer.
    buf = BytesIO()
    fig.savefig(buf, format="png")

    return base64.b64encode(buf.getbuffer()).decode("ascii")

def bi(x_inicial, f, delta=0.001, n_iter_max=1000):

    x_a = x_inicial
    f_a = f(x_a)

    raiz = None
    message = None
    results = [[x_a, f_a]]

    if f_a == 0:
        message = f"{x_a} es raiz de f(x)"
        raiz = x_a

    x_b = x_inicial + delta
    f_b = f(x_b)

    for _ in range(n_iter_max):
        if f_a * f_b < 0:
            message = f"Existe una raiz de f(x) entre {x_a} y {x_b}"
            raiz = [x_a, x_b]

        x_a = x_b
        f_a = f_b

        results.append([x_a, f_a])

        x_b += delta
        f_b = f(x_b)

        if f_b == 0:
            message = f"{x_b} es raiz de f(x)"
            raiz = x_b

    else:
        print(f"Fracaso en {n_iter_max} iteraciones ")

    return raiz, message, results

def biseccion(x_a, x_b, f, tol=1e-6, err="abs", regla_falsa=False, n_iter_max=1000):

    f_a = f(x_a)
    f_b = f(x_b)

    raiz = None
    message = None
    results = []

    if f_a == 0:
        message = f"{x_a} es raiz de f(x)"
        raiz = x_a

    elif f_b == 0:
        message = f"{x_b} es raiz de f(x)"
        raiz = x_b

    elif f_a * f_b > 0:
        message = f"No existe raiz en el intervalo [{x_a}, {x_b}]"

    if message is not None:
        return raiz, message, results

    x_m = (f_b * x_a - f_a * x_b) / (f_b - f_a) if regla_falsa else (x_a + x_b) / 2
    f_m = f(x_m)
    results = [[x_m, f_m, 100]]

    for _ in range(n_iter_max):
        if f_a * f_m < 0:
            x_b = x_m
            f_b = f(x_b)
        else:
            x_a = x_m
            f_a = f(x_a)

        x_c = (f_b * x_a - f_a * x_b) / (f_b - f_a) if regla_falsa else (x_a + x_b) / 2
        error = abs((x_c - x_m) / (x_m if err == "rel" else 1))
        x_m = x_c
        f_m = f(x_m)

        results.append([x_m, f_m, error])

        if f_m == 0:
            message = f"{x_m} es raiz de f(x)"
            raiz = x_m
            break

        if error < tol:
            message = f"{x_m} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            raiz = x_m
            break
    else:
        message = f"Fracaso en {n_iter_max} iteraciones "

    return raiz, message, results

def n_iter_biseccion(x_a, x_b, x_raiz, x_mf):

    return math.floor(math.log(abs((x_b - x_a) / (x_mf - x_raiz)), 2))

def punto_fijo(x_a, f, g, tol=1e-6, err="abs", n_iter_max=1000):

    f_a = f(x_a)

    # x_a, f_a, err_a
    results = [[x_a, f_a, 100]]

    for _ in range(n_iter_max):
        x_b = g(x_a)
        error = abs((x_b - x_a) / (x_a if err == "rel" else 1))

        x_a = x_b
        f_a = f(x_a)

        results.append([x_a, f_a, error])

        if f_a == 0:
            message = f"{x_a} es raiz de f(x)"
            break
        elif error < tol:
            message = f"{x_a} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            break

    else:
        message = f"Fracaso en {n_iter_max} iteraciones"

    return x_a, message, results

def n_iter_punto_fijo(x_a, x_b, k, x_0, error):

    return math.floor(np.log(error / max(x_0 - x_a, x_b - x_0)) / np.log(k))

def newton_raphson(x_a, f, fp, m=1, tol=1e-6, err="abs", n_iter_max=1000):

    f_a = f(x_a)
    fp_a = fp(x_a)
    results = [[x_a, f_a, fp_a, 100]]
    message = None

    for _ in range(n_iter_max):
        if fp_a == 0:
            message = f"La derivada de la f(x) en {x_a} es cero"
            break

        x_b = x_a - m * f_a / fp_a

        error = abs((x_b - x_a) / (x_a if err == "rel" else 1))

        x_a = x_b
        f_a = f(x_a)
        fp_a = fp(x_a)

        results.append([x_a, f_a, fp_a, error])

        if f_a == 0:
            message = f"{x_a} es raiz de f(x)"
            break

        elif error < tol:
            message = f"{x_a} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            break
    else:
        message = f"Fracaso en {n_iter_max} iteraciones"

    return x_a, message, results

def newton_raphson_multiples_raices(
    x_a, f, fp, fpp, tol=1e-6, err="abs", n_iter_max=1000
):

    f_a = f(x_a)
    fp_a = fp(x_a)
    fpp_a = fpp(x_a)
    results = [[x_a, f_a, fp_a, fpp_a, 100]]
    message = None

    for _ in range(n_iter_max):
        if fp_a == 0:
            message = f"La derivada de la f(x) en {x_a} es cero"
            break

        x_b = x_a - f_a * fp_a / (fp_a ** 2 - f_a * fpp_a)

        error = abs((x_b - x_a) / (x_a if err == "rel" else 1))

        x_a = x_b
        f_a = f(x_a)
        fp_a = fp(x_a)

        results.append([x_a, f_a, fp_a, fpp_a, error])

        if f_a == 0:
            message = f"{x_a} es raiz de f(x)"
            break

        elif error < tol:
            message = f"{x_a} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            break
    else:
        message = f"Fracaso en {n_iter_max} iteraciones"

    return x_a, message, results

def secante(x_a, x_b, f, tol=1e-6, err="abs", n_iter_max=1000):
    # x_a debe ser menor que x_b

    f_a = f(x_a)
    f_b = f(x_b)
    results = [[x_a, f_a, 100], [x_b, f_b, 100]]
    message = None

    for _ in range(n_iter_max):
        x_c = x_b - f_b * (x_b - x_a) / (f_b - f_a)

        error = abs((x_c - x_b) / (x_b if err == "rel" else 1))

        x_a = x_b
        x_b = x_c
        f_a = f_b
        f_b = f(x_c)

        results.append([x_b, f_b, error])

        if f_a == 0:
            message = f"{x_a} es raiz de f(x)"
            break

        elif error < tol:
            message = f"{x_a} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            break
    else:
        message = f"Fracaso en {n_iter_max} iteraciones"

    return x_a, message, results

def check_n_raices(f, x):
    fp = sympy.diff(get_expr(f))

    n_raices = 1
    while parse_function(fp)(x) == 0:
        n_raices += 1
        fp = sympy.diff(fp)

    return n_raices


from IMLearn.learners import UnivariateGaussian, MultivariateGaussian
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

pio.templates.default = "simple_white"
import plotly.express as px

TRUE_VAR = 1
TRUE_EXP = 10

def test_univariate_gaussian():
    # Question 1 - Draw samples and print fitted model
    samples = np.random.normal(10, 1, 1000)
    univar = UnivariateGaussian()
    univar.fit(samples)
    print()
    print("(", univar.mu_, ",", univar.var_, ")\n")

    def estimate_expectancy(size):
        return UnivariateGaussian().fit(samples[0:size]).mu_

    # Question 2 - Empirically showing sample mean is consistent
    sizes = np.arange(10, 1010, 10)
    vector = np.abs(np.vectorize(estimate_expectancy)(sizes) - TRUE_EXP)
    fig_diff = px.scatter(x=sizes, y=vector)
    fig_diff.update_layout(title="The effect of the amount of samples on the expectation accuracy",
                           xaxis_title="Samples amount", yaxis_title="Distant from real expectancy")
    fig_diff.show()

    # Question 3 - Plotting Empirical PDF of fitted model
    pdf = univar.pdf(samples)
    fig_pdf = px.scatter(x=samples, y=pdf)
    fig_pdf.update_layout(title="PDF's", xaxis_title="sample", yaxis_title="pdf")
    fig_pdf.show()

def test_multivariate_gaussian():
    # Question 4 - Draw samples and print fitted model
    mean = np.array([0, 0, 4, 0])
    cov = np.array([[1, 0.2, 0, 0.5], [0.2, 2, 0, 0], [0, 0, 1, 0], [0.5, 0, 0, 1]])
    samples_multi = np.random.multivariate_normal(mean, cov, 1000)
    multivar = MultivariateGaussian()
    multivar.fit(samples_multi)
    print(multivar.mu_, "\n")
    print(multivar.cov_)

    # Question 5 - Likelihood evaluation
    heat_data = np.zeros(shape=(200, 200))
    f_arr = np.linspace(-10, 10, 200)
    i = 0
    for f1 in f_arr:
        j = 0
        for f3 in f_arr:
            new_mu = np.array([f1, 0, f3, 0])
            heat_data[i][j] = MultivariateGaussian.log_likelihood(new_mu, cov, samples_multi)
            j += 1
        i += 1

    fig = go.Figure(go.Heatmap(x=f_arr, y=f_arr, z=heat_data))
    fig.update_layout(title="The log likelihood of different values of expectancy", xaxis_title="f3", yaxis_title="f1")
    fig.show()

    # Question 6 - Maximum likelihood
    max_idx = np.unravel_index(np.argmax(heat_data, axis=None), heat_data.shape)
    print("The maximum log likliehood is: [", round(f_arr[max_idx[0]], 3), "0,", round(f_arr[max_idx[1]], 3), ",0]")

if __name__ == '__main__':
    np.random.seed(0)
    test_univariate_gaussian()
    test_multivariate_gaussian()


# -*- coding: utf-8 -*-
"""
Created on Thu May 23 12:13:20 2019

@author: Tin
"""

import numpy as np
from numpy.linalg import inv

options = " Matrix Addition, Matrix Subtraction, Matrix Multiplication, Matrix Division, Dot Product, Matrix Scalar, Matrix Transpose, Matrix Inverse, Pseudo Inverse Matrix, Quit".split(",")

def input_X_matrix():
    R = int(input("Enter the number of rows: "))
    C = int(input("Enter the number of columns: "))
    print("Input the entries in a single line (separated by space): ")
    x = list(map(int, input().split()))
    X_matrix = np.array(x).reshape(R, C)
    print(X_matrix)
    return X_matrix

def input_Y_matrix():
    R = int(input("Enter the number of rows: "))
    C = int(input("Enter the number of columns: "))
    print("Input the entries in a single line (separated by space): ")
    y = list(map(int, input().split()))
    Y_matrix = np.array(y).reshape(R, C)
    print(Y_matrix)
    return Y_matrix

def matrix_addition():
    add = input_X_matrix() + input_Y_matrix()
    return add

def matrix_subtraction():
    sub = input_X_matrix() - input_Y_matrix()
    return sub

def matrix_multiplication():
    multiply = input_X_matrix() * input_Y_matrix()
    return multiply

def matrix_divison():
    divison = input_X_matrix() / input_Y_matrix()
    return divison

def dot_product():
    A = input_X_matrix()
    B = input_Y_matrix()
    C = A.dot(B)
    print('___Dot Product___')
    print(C)
    return C

def matrix_scalar():
    A = input_X_matrix()
    b = float(input("Enter a number: "))
    c = A*b
    print('__Matrix-Scalar Multiplication___')
    print(c)
    return c

def matrix_transpose():
    mt = input_X_matrix()
    T_m = mt.T
    print('___Matrix Transpose___')
    print(T_m)
    return

def matrix_inverse():
    mt = input_X_matrix()
    I_m = inv(mt)
    print('___Matrix Inverse___')
    print(I_m)
    return

def p_inverse():
    m = input_X_matrix()
    pim = np.linalg.pinv(m)
    print('___Pseudo Inverse Matrix___')
    print(pim)
    return

def main():
    run_program = True
    while run_program:
        print("\n")
        print("Choose Option:")
        print("Mathematics for Machine Learning Linear Algebra")
        for i in range(1, len(options)+1):
            print("{} - {}".format(i, options[i-1]))
        choice = int(input())

        if choice == 1:
             print('___Matrix Addition___')
             matrix_addition()
        elif choice == 2:
             print('___Matrix Subtraction___')
             matrix_subtraction()
        elif choice == 3:
             print('___Matrix Multiplication___')
             matrix_multiplication()
        elif choice == 4:
             print('___Matrix Division___')
             matrix_divison()
        elif choice == 5:
             dot_product()
        elif choice == 6:
             matrix_scalar()
        elif choice == 7:
             matrix_transpose()
        elif choice == 8:
             matrix_inverse()
        elif choice == 9:
             p_inverse()
        elif choice == 10:
             run_program = False

if __name__ == "__main__":
    main()


import numpy as np

def simpson(function, p0, n : int):
    x,y=p0
    dx=np.abs(x-y)/n
    r=0
    for i in range(n):
        r=r+(1/6)*(x+dx-x)*(function(x)+function(x+dx)+4*function(x+dx/2))
        x=x+dx
    return r

# find the value of pi by integrating sin(x)^2 from 0 to 2pi
f = lambda x : np.sin(x)**2
x = simpson(f, (0, 2*np.pi), int(1e6))
print("The value of pi is : {}".format(x))


import numpy as np

def dft(x):
    size = len(x)
    a = np.zeros(size, dtype=np.complex)
    for k in xrange(size):
        for j in xrange(size):
            a[k] += x[j] * np.exp(-1j * 2 * np.pi * k * j / size)
    return a

def idft(x):
    size = len(x)
    a = np.zeros(size, dtype=np.complex)
    for j in xrange(size):
        for i in range(size):
            a[j] += x[i] * np.exp(1j * 2 * np.pi * i * j / size)
    return a / size


import numpy as np
a = np.array([0.3, 2.9, 4.0])

def softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a/ sum_exp_a
    return y

def softmax2(a):
    c = np.max(a)
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a/ sum_exp_a
    return y

y = softmax(a)
y2 = softmax2(a)
print(y)
print(np.sum(y))
print('\n', y2)
print(np.sum(y2))


import numpy as np
import pandas as pd
import matplotlib.pyplot as plot
from scipy import stats
from sklearn.linear_model import LinearRegression

source = ("https://github.com/SerkanKorkusuz/Machine-Learning/blob/master/linear-regression-dataset.csv")
myData = pd.read_csv(source, header=0, prefix="V", sep=";")
print(myData)

att = []
label = []

for i in range(len(myData)):
    att.append(myData.iloc[i, 0])
    label.append(myData.iloc[i, 1])

slope, intercept, r_value, pvalue, std_err = stats.linregress(att, label)
print("Square of r_value is "+ str(r_value**2))

def myPredict(feature):
    labelList = []
    for j in range(len(feature)):
        labelList.append(feature[j] * slope + intercept)
    return labelList

predictLabel = myPredict(att)
plot.scatter(att, label)
plot.plot(att, predictLabel, c="r")
plot.xlabel("Experience")
plot.ylabel("Salary")
plot.title("Thanks to scipy")
plot.show()

#another way to form a model

linear_reg_model = LinearRegression()

attArr = myData.deneyim.values.reshape(-1,1)
labelArr = myData.maas.values.reshape(-1,1)
#print(attArr.shape())

linear_reg_model.fit(attArr, labelArr)
print("y=mx+n =>   n=" + str(linear_reg_model.predict([[0]])))
print("y=mx+n =>   n=" + str(linear_reg_model.intercept_))
print("y=mx+n =>   m=" + str(linear_reg_model.coef_))

newAtt = np.linspace(0, 15, 15).reshape(-1,1)
newLabel = linear_reg_model.predict(newAtt)
plot.scatter(attArr, labelArr)
plot.plot(newAtt, newLabel, c="r")
plot.xlabel("Experience")
plot.ylabel("Salary")
plot.title("Thanks to skicit-learn")
plot.show()


from matplotlib.pyplot import (legend, plot, scatter, show)
from numpy import (array, mean, sqrt, square)

data_set = array([
    [32, 31], [53, 68], [60, 62], [47, 71], [59, 87],
    [55, 78], [52, 79], [39, 59], [48, 75], [52, 71],
    [45, 55], [54, 82], [44, 62], [58, 75], [56, 81],
    [48, 60], [44, 82], [60, 97], [45, 48], [38, 56],
    [66, 83], [65, 11], [47, 57], [41, 51], [51, 75]
])

x_list = data_set[:, 0]
y_list = data_set[:, 1]

count = len(data_set)
intercept = 0
slope = 0

def line_equation(x):
    return slope * x + intercept

learning_rate = 0.0001
iteration_cycles = 1000

for _ in range(iteration_cycles):
    new_y = list(map(line_equation, x_list))
    y_difference = y_list - new_y

    intercept_gradient = sum(-2 / count * y_difference)
    slope_gradient = sum(-2 / count * y_difference * x_list)

    intercept -= learning_rate * intercept_gradient
    slope -= learning_rate * slope_gradient

predicted_y = list(map(line_equation, x_list))

rmse = sqrt(mean(square(y_list - predicted_y)))

print("intercept =", intercept)
print("slope =", slope)
print("rmse =", rmse)

test_x = [44, 32, 63]
test_y = list(map(line_equation, test_x))

scatter(x_list, y_list, label='original')
scatter(test_x, test_y, label='prediction')
plot(x_list, predicted_y, label='line of best fit')
legend()
show()

Gradient Descent for Linear Regression.py


import numpy as np
import matplotlib.pyplot as plt

def signal(sinuses):
    ''' This function creates a function f(x) = sin(a*pi*2)+sin(b*p*2)+...
     plots the graph of f(x) and saves it

    Args:
        sinuses(Float) : The list of constants a,b,c,...

    Returns:
        t (numpy array): X axis
        s (numpy array): Y axis
    '''
    # Create the singal as a sum of different sinuses
    t = np.linspace(0, 0.5, 800)
    s=0
    for i in range(len(sinuses)):
        s += np.sin(sinuses[i] * 2 * np.pi * t)

    # Plot the signal
    plt.style.use('seaborn')
    fig = plt.figure(figsize=(8,4))
    ax = fig.add_subplot(1,1,1)

    ax.plot(t, s, label = r'$y=f(x)$')

    ax.set_title(" Signal ", fontsize = 20)
    ax.set_ylabel("Amplitude")
    ax.set_xlabel("Time [s]")

    ax.legend(loc='best')
    ax.grid(True)
    plt.show()
    fig.savefig('signal.png')
    return s, t

def Fourier(s, t, alg = "False"):
    '''This function performs the FFT in the function f(x) defined in signal function, plots and saves the figure

    Args:
        t (numpy array): X axis
        s (numpy array): Y axis
        alg (string): Variable to determine whether to use my FFT or numpy's
    '''

    #Perform the Fourier Transform
    if alg == "True":
        fft = FFT(s)
    else:
        fft = np.fft.fft(s)

    T = t[1] - t[0]  # sample rate
    N = s.size

    # 1/T = frequency
    f = np.linspace(0, 1 / T, N)

    # Plot the signal Decomposition
    plt.style.use('seaborn')
    fig = plt.figure(figsize=(8,4))
    ax = fig.add_subplot(1,1,1)

    ax.set_title(" Decomposed Signal ", fontsize = 20)
    ax.set_ylabel("Amplitude")
    ax.set_xlabel("Frequency [Hz]")

    ax.bar(f[:N // 2], np.abs(fft)[:N // 2] * 1 / N, width=1.5)  # 1 / N is a normalization factor
    ax.grid(False)
    plt.show()
    fig.savefig("Decomposed_signal.png")

def FFT(x):
    """Compute the discrete Fourier Transform of the signal x

    Args:
        x (numpy array): The function f(x)

    Returns:
        M (numpy array): The FFT
    """

    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    n = np.arange(N)
    k = n.reshape((N, 1))
    M = np.exp(-2j * np.pi * k * n / N)
    return np.dot(M, x)


import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
import numpy.ma as ma
from numpy.random import uniform, seed
from matplotlib import cm

u = np.asarray([0.,0.])
sig = np.asarray(
    [
        [1.8 , 0],
        [0 , 1.8]
    ]
)

def gauss(x,y,Sigma,mu):
    X=np.vstack((x,y)).T
    mat_multi=np.dot((X-mu[None,...]).dot(np.linalg.inv(Sigma)),(X-mu[None,...]).T)
    return  np.diag(np.exp(-1*(mat_multi)))

def plot_countour(x,y,z):
    # define grid.
    xi = np.linspace(-2.1, 2.1, 100)
    yi = np.linspace(-2.1, 2.1, 100)
    ## grid the data.
    zi = griddata((x, y), z, (xi[None,:], yi[:,None]), method='cubic')
    levels = [0.2, 0.4, 0.6, 0.8, 1.0]
    # contour the gridded data, plotting dots at the randomly spaced data points.
    CS = plt.contour(xi,yi,zi,len(levels),linewidths=0.5,colors='k', levels=levels)
    #CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
    CS = plt.contourf(xi,yi,zi,len(levels),cmap=cm.Greys_r, levels=levels)
    plt.colorbar() # draw colorbar
    # plot data points.
    # plt.scatter(x, y, marker='o', c='b', s=5)
    plt.xlim(-2, 2)
    plt.ylim(-2, 2)
    plt.title('griddata test (%d points)' % npts)
    plt.show()

# make up some randomly distributed data
seed(1234)
npts = 1000
x = uniform(-2,2,npts)
y = uniform(-2,2,npts)

z = gauss(x, y, Sigma=sig, mu=u)
plot_countour(x, y, z)


import os
import numpy as np
import cv2
import matplotlib.pyplot as plt
import imutils

class imageTransform:

    def log_transform(self, impath):
      c = 35
      inp = cv2.imread(impath,0)
      opt = np.zeros((inp.shape[0], inp.shape[1]))
      for m in range(0, inp.shape[0]):
          for n in range(0, inp.shape[1]):
              opt[m, n] = c * np.log(1 + inp[m, n])
      return opt

    def power_law_transform(self,impath):
      gama = 1.3
      constant = 1
      inp = cv2.imread(impath,0)
      opt = np.zeros((inp.shape[0], inp.shape[1]))
      for m in range(0, inp.shape[0]):
          for n in range(0, inp.shape[1]):
              opt[m, n] = constant * np.power(inp[m, n], gama)
      return opt

    def histogram_equalization(self,impath):
        inp = cv2.imread(impath,0)
        opt = np.zeros((inp.shape[0], inp.shape[1]))
        hist_vals = np.zeros([1,256])
        for m in range(0,inp.shape[0]):
            for n in range(0,inp.shape[1]):
                hist_vals[0][inp[m,n]] +=1
        normalized_hist = hist_vals/(inp.shape[0]*inp.shape[1])
        for m in range(0,inp.shape[0]):
            for n in range(0,inp.shape[1]):
                 indices = np.arange(inp[m,n])
                 cdf = normalized_hist[0][indices].sum()
                 opt[m,n] = 255*cdf
        return opt

    def negative_image(self,impath):
        inp = cv2.imread(impath,0)
        L = 256
        opt = np.zeros((inp.shape[0], inp.shape[1]));
        for m in range(0, inp.shape[0]):
            for n in range(0, inp.shape[1]):
                opt[m, n] = L - 1 - inp[m, n]
        return opt


import logging
import numpy as np
import pandas as pd

# PCA
from sklearn import decomposition

# THIS IS WHAT WE NEED TO LOOK AT!
from sklearn import preprocessing as preproc
# from sklearn.preprocessing import StandardScaler

logger = logging.getLogger(__name__)

def get_scatter3D(data):

    if(data['view']['settings']['method'] == "PCA"):
        feature_columns = data['view']['settings']['featureColumns']
        target_column = data['view']['settings']['targetColumn']

        df = pd.DataFrame(data['data'])
        df_train = df[feature_columns]
        X = df_train.values
        df_target = df[target_column]
        y = df_target.values

        if "preprocessingEnabled" in data['view']['settings'].keys():
            if "preprocMethod" in data['view']['settings'].keys():
                if(data['view']['settings']['preprocessingEnabled'] == True):
                    if(data['view']['settings']['preprocMethod'] == "StandardScaling"):
                        scaler = preproc.StandardScaler()
                        scaler.fit(X)
                        X = scaler.transform(X)
                    elif(data['view']['settings']['preprocMethod'] == "Normalization"):
                        normalizer = preproc.Normalizer().fit(X)
                        X = normalizer.transform(X)

        pca = decomposition.PCA(n_components=3)
        pca.fit(X)
        X = pca.transform(X)
        evr = pca.explained_variance_ratio_

        pca_x = []; pca_y = []; pca_z = []
        for pcaSet in X:
            pca_x.append(pcaSet[0])
            pca_y.append(pcaSet[1])
            pca_z.append(pcaSet[2])

        data['data']['x'] = pca_x
        data['data']['y'] = pca_y
        data['data']['z'] = pca_z
        data['data']['evr'] = evr
        data['data']['noOfFeat'] = len(feature_columns)

    return data['data']


import numpy as np
import scipy.optimize as opt

from scipy.interpolate import InterpolatedUnivariateSpline
from scipy.special import binom
from typing import Union, List, Tuple, Optional

def cls(
    x: Union[float, List[float], np.ndarray], n1: float, n2: float, norm: bool = True
) -> np.ndarray:
    """
    Compute class function.

    Parameters
    ----------
    x : array_like
        Points to evaluate class function for
    n1, n2 : int
        Class function parameters
    norm : bool, optional
        True (default) if the class function should be normalized

    Returns
    -------
    np.array
        Class function value for each given point

    Notes
    -----
    It is assumed that 0 <= x <= 1.
    """
    c = (x ** n1) * ((1.0 - x) ** n2)
    c /= (
        1.0
        if not norm or n1 == n2 == 0
        else (((n1 / (n1 + n2)) ** n1) * ((n2 / (n1 + n2)) ** n2))
    )
    return c

def bernstein(x: Union[float, List[float], np.ndarray], r: int, n: int) -> np.array:
    """
    Compute Bernstein basis polynomial.

    Parameters
    ----------
    x : array_like
        Points to evaluate the Bernstein polynomial at
    r, n : int
        Bernstein polynomial index and degree

    Returns
    -------
    np.array
        Values of the Bernstein polynomial at the given points

    Notes
    -----
    It is assumed that 0 <= x < 1.
    It is assumed that r <= n.
    """
    return binom(n, r) * (x ** r) * ((1.0 - x) ** (n - r))

def cst(
    x: Union[float, List[float], np.ndarray],
    a: Union[List[float], np.ndarray],
    delta: Tuple[float, float] = (0.0, 0.0),
    n1: float = 0.5,
    n2: float = 1.0,
) -> np.ndarray:
    """Compute coordinates of a CST-decomposed curve.

    This function uses the Class-Shape Transformation (CST) method to compute the y-coordinates as a function of a given
    set of x-coordinates, `x`, and a set of coefficients, `a`. The x-coordinates can be scaled by providing a length
    scale, `c`. The starting and ending points of the curve can be displaced by providing non-zero values for `delta`.
    Finally, the class of shapes generated can be adjusted with the `n1` and `n2` parameters. By default, these are 0.5
    and 1.0 respectively, which are good values for generating airfoil shapes.

    Parameters
    ----------
    x : float or array_like
        x-coordinates.
    a : array_like
        Bernstein coefficients.
    delta : tuple of two floats
        Vertical displacements of the start- and endpoints of the curve. Default is (0., 0.).
    n1, n2 : float
        Class parameters. These determine the general "class" of the shape. They default to n1=0.5 and n2=1.0 for
        airfoil-like shapes.

    Returns
    -------
    y : np.ndarray
        Y-coordinates.

    Notes
    -----
    It is assumed that 0 <= x <= 1.

    References
    ----------
    [1] Brenda M. Kulfan, '"CST" Universal Parametric Geometry Representation Method With Applications to Supersonic
     Aircraft,' Fourth International Conference on Flow Dynamics, Sendai, Japan, September 2007.
    """
    # Ensure x is a numpy array
    x = np.atleast_1d(x)

    # Bernstein polynomial degree
    n = len(a) - 1

    # Compute Class and Shape functions
    _class = cls(x, n1, n2)
    _shape = sum(a[r] * bernstein(x, r, n) for r in range(len(a)))

    # Compute y-coordinates
    y = _class * _shape + (1.0 - x) * delta[0] + x * delta[1]
    return y

def fit(
    x: Union[List[float], np.ndarray],
    y: Union[List[float], np.ndarray],
    n: int,
    delta: Optional[Tuple[float, float]] = None,
    n1: float = 0.5,
    n2: float = 1.0,
) -> Tuple[np.ndarray, Tuple[float, float]]:
    """Fit a set of coordinates to a CST representation.

    Parameters
    ----------
    x, y : array_like
        X- and y-coordinates of a curve.
    n : int
        Number of Bernstein coefficients.
    delta : tuple of two floats, optional
        Manually set the start- and endpoint displacements.
    n1, n2 : float
        Class parameters. Default values are 0.5 and 1.0 respectively.

    Returns
    -------
    A : np.ndarray
        Bernstein coefficients describing the curve.
    delta : tuple of floats
        Displacements of the start- and endpoints of the curve.

    Notes
    -----
    It is assumed that 0 <= x <= 1.
    """
    # Ensure x and y are np.ndarrays
    x = np.atleast_1d(x)
    y = np.atleast_1d(y)

    # Make sure the coordinates are sorted by x-coordinate
    ind = np.argsort(x)
    x = x[ind]
    y = y[ind]

    if delta is None:
        f = InterpolatedUnivariateSpline(x, y)
        delta = (f(0.0), f(1.0))

    def f(_a):
        return np.sqrt(np.mean((y - cst(x, _a, delta=delta, n1=n1, n2=n2)) ** 2))

    # Fit the curve
    res = opt.minimize(f, np.zeros(n))

    return res.x, delta


import numpy as np
from termcolor import colored
import os
import sys
if sys.platform == 'linux':
    sys.path.append(r'../lib')
else:
    sys.path.append(os.path.abspath('../build/x64/Release'))
import NumCpp

####################################################################################
def doTest():
    print(colored('Testing Linalg Module', 'magenta'))

    print(colored('Testing cholesky', 'cyan'))
    shapeInput = np.random.randint(5, 50, [2, ])
    shape = NumCpp.Shape(shapeInput[0].item(), shapeInput[1].item())
    cArray = NumCpp.NdArray(shape)
    a = np.random.randint(1, 100, [shape.rows, shape.cols]).flatten()
    aL = np.tril(a)
    b = aL.dot(aL.transpose())
    cArray.setArray(b)
    if np.array_equal(np.round(NumCpp.cholesky(cArray).getNumpyArray()), np.round(aL)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing det: 2x2', 'cyan'))
    order = 2
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols]).astype(np.double)
    cArray.setArray(data)
    if round(NumCpp.det(cArray)) == round(np.linalg.det(data).item()):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing det: 3x3', 'cyan'))
    order = 3
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols]).astype(np.double)
    cArray.setArray(data)
    if round(NumCpp.det(cArray)) == round(np.linalg.det(data).item()):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing det: NxN', 'cyan'))
    order = np.random.randint(4, 8, [1,]).item()
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols]).astype(np.double)
    cArray.setArray(data)
    if round(NumCpp.det(cArray)) == round(np.linalg.det(data).item()):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing hat', 'cyan'))
    shape = NumCpp.Shape(1, 3)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(0, 100, [shape.rows, shape.cols]).flatten()
    cArray.setArray(data)
    if np.array_equal(NumCpp.hat(cArray), hat(data)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing inv', 'cyan'))
    order = np.random.randint(5, 50, [1,]).item()
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols])
    cArray.setArray(data)
    if np.array_equal(np.round(NumCpp.inv(cArray).getNumpyArray(), 9), np.round(np.linalg.inv(data), 9)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing lstsq', 'cyan'))
    shapeInput = np.random.randint(5, 50, [2,])
    shape = NumCpp.Shape(shapeInput[0].item(), shapeInput[1].item())
    aArray = NumCpp.NdArray(shape)
    bArray = NumCpp.NdArray(1, shape.rows)
    aData = np.random.randint(1, 100, [shape.rows, shape.cols])
    bData = np.random.randint(1, 100, [shape.rows,])
    aArray.setArray(aData)
    bArray.setArray(bData)
    x = NumCpp.lstsq(aArray, bArray, 1e-12).getNumpyArray().flatten()
    if np.array_equal(np.round(x, 9), np.round(np.linalg.lstsq(aData, bData, rcond=None)[0], 9)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing lu_decompostion', 'cyan'))
    sizeInput = np.random.randint(5, 50)
    shape = NumCpp.Shape(sizeInput)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols])
    cArray.setArray(data)
    lu = NumCpp.lu_decomposition(cArray)
    l = lu.first
    u = lu.second
    p = np.round(np.dot(l.getNumpyArray(), u.getNumpyArray())).astype(np.int)
    if np.array_equal(p, data):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing matrix_power: power = 0', 'cyan'))
    order = np.random.randint(5, 50, [1, ]).item()
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols])
    cArray.setArray(data)
    if np.array_equal(NumCpp.matrix_power(cArray, 0).getNumpyArray(), np.linalg.matrix_power(data, 0)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing matrix_power: power = 1', 'cyan'))
    order = np.random.randint(5, 50, [1, ]).item()
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols])
    cArray.setArray(data)
    if np.array_equal(NumCpp.matrix_power(cArray, 1).getNumpyArray(), np.linalg.matrix_power(data, 1)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing matrix_power: power = -1', 'cyan'))
    order = np.random.randint(5, 50, [1, ]).item()
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols])
    cArray.setArray(data)
    if np.array_equal(np.round(NumCpp.matrix_power(cArray, -1).getNumpyArray(), 8), np.round(np.linalg.matrix_power(data, -1), 8)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing matrix_power: power > 1', 'cyan'))
    order = np.random.randint(5, 50, [1, ]).item()
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 5, [shape.rows, shape.cols]).astype(np.double)
    cArray.setArray(data)
    power = np.random.randint(2, 9, [1,]).item()
    if np.array_equal(NumCpp.matrix_power(cArray, power).getNumpyArray(), np.linalg.matrix_power(data, power)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing matrix_power: power < -1', 'cyan'))
    order = np.random.randint(5, 50, [1,]).item()
    shape = NumCpp.Shape(order)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols])
    cArray.setArray(data)
    power = np.random.randint(2, 9, [1, ]).item() * -1
    if np.array_equal(np.round(NumCpp.matrix_power(cArray, power).getNumpyArray(), 9), np.round(np.linalg.matrix_power(data, power), 9)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing multi_dot', 'cyan'))
    shapeInput = np.random.randint(5, 50, [2, ])
    shape1 = NumCpp.Shape(shapeInput[0].item(), shapeInput[1].item())
    shape2 = NumCpp.Shape(shape1.cols, np.random.randint(5, 50, [1, ]).item())
    shape3 = NumCpp.Shape(shape2.cols, np.random.randint(5, 50, [1, ]).item())
    shape4 = NumCpp.Shape(shape3.cols, np.random.randint(5, 50, [1, ]).item())
    cArray1 = NumCpp.NdArray(shape1)
    cArray2 = NumCpp.NdArray(shape2)
    cArray3 = NumCpp.NdArray(shape3)
    cArray4 = NumCpp.NdArray(shape4)
    data1 = np.random.randint(1, 10, [shape1.rows, shape1.cols])
    data2 = np.random.randint(1, 10, [shape2.rows, shape2.cols])
    data3 = np.random.randint(1, 10, [shape3.rows, shape3.cols])
    data4 = np.random.randint(1, 10, [shape4.rows, shape4.cols])
    cArray1.setArray(data1)
    cArray2.setArray(data2)
    cArray3.setArray(data3)
    cArray4.setArray(data4)
    if np.array_equal(np.round(NumCpp.multi_dot(cArray1, cArray2, cArray3, cArray4), 9), np.round(np.linalg.multi_dot([data1, data2, data3, data4]), 9)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing pivotlu_decompostion', 'cyan'))
    sizeInput = np.random.randint(5, 50)
    shape = NumCpp.Shape(sizeInput)
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols])
    cArray.setArray(data)
    l, u, p = NumCpp.pivotLU_decomposition(cArray)
    lhs = p.dot(data)
    rhs = l.dot(u)
    if np.array_equal(np.round(lhs, 10), np.round(rhs, 10)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

    print(colored('Testing svd', 'cyan'))
    shapeInput = np.random.randint(5, 50, [2,])
    shape = NumCpp.Shape(shapeInput[0].item(), shapeInput[1].item())
    cArray = NumCpp.NdArray(shape)
    data = np.random.randint(1, 100, [shape.rows, shape.cols])
    cArray.setArray(data)
    uArray = NumCpp.NdArray()
    sArray = NumCpp.NdArray()
    vArray = NumCpp.NdArray()
    NumCpp.svd(cArray, uArray, sArray, vArray)
    data2 = np.dot(uArray.getNumpyArray(), np.dot(sArray.getNumpyArray(), vArray.getNumpyArray()))
    if np.array_equal(np.round(data, 9), np.round(data2, 9)):
        print(colored('\tPASS', 'green'))
    else:
        print(colored('\tFAIL', 'red'))

####################################################################################
def hat(inVec):
    mat = np.zeros([3,3])
    mat[0, 1] = -inVec[2]
    mat[0, 2] = inVec[1]
    mat[1, 0] = inVec[2]
    mat[1, 2] = -inVec[0]
    mat[2, 0] = -inVec[1]
    mat[2, 1] = inVec[0]

    return mat

####################################################################################
if __name__ == '__main__':
    doTest()


import pandas as pd
import numpy as np

class LinearRegression():
    def __init__(self):
        self.weights = None
        self.residuals = None
        self.feature_names = None
        self.train_preds = None
        self.r_squared = None

    def train_test_split(self,X,y,test_proportion=0.2):

        assert f"Input data needs to be type pandas DataFrame \
                    or numpy matrix, not {type(data)}"

        num_samples = data.shape[0]

        test_ind = np.random.choice(range(num_samples),
                                    int(num_samples*test_proportion),
                                    replace=False)
        train_ind = np.array(list(set(range(num_samples)) - set(test_ind)))

        X_test = X.iloc[test_ind]
        y_test = y.iloc[test_ind]

        X_train = X.iloc[train_ind]
        y_train = y.iloc[train_ind]

        return X_train, y_train, X_test, y_test

    def fit(self,X,y):

        if not isinstance(X,pd.DataFrame) and not isinstance(X, np.matrix):
            assert f"Input X data needs to be type pandas DataFrame \
                    or numpy matrix, not {type(X)}"

        if not isinstance(y,pd.DataFrame) and not isinstance(y, np.matrix) and not isinstance(y, pd.Series):
            assert f"Input y data needs to be type pandas DataFrame \
                    or pandas Series or numpy matrix, not {type(y)}"

        if isinstance(X,pd.DataFrame):
            self.feature_names = list(X.columns)

        # add column of ones for intercept
        X = np.concatenate([np.ones((X.shape[0],1)),X],axis=1)

        #weights = np.linalg.inv(X.transpose() @ X) @ (X.transpose() @ y)
        weights = np.linalg.solve(X.transpose() @ X), (X.transpose() @ y)

        self.weights = weights.reshape(-1,1)

        train_preds = self.predict(X[:,1:])

        self.residuals = train_preds - y.to_numpy().reshape(-1,1)

        # calculate r-squared
        y_bar = np.mean(y_train)
        sst = sum([(y-y_bar)**2 for y in y])
        ssreg = sum([(y-y_bar)**2 for y in train_preds])
        self.r_squared = np.round(float(sst/ssreg),decimals=4)

    def predict(self,X):

        if not isinstance(X,pd.DataFrame) and not isinstance(X, np.matrix):
            assert f"Input data needs to be type pandas DataFrame \
                    or numpy matrix, not {type(X)}"

        # add column of ones for intercept
        X = np.concatenate([np.ones((X.shape[0],1)),X],axis=1)

        return X @ self.weights

    def summary(self):

        print("R-squared:",self.r_squared,'\n')

        if self.feature_names:
            df = pd.DataFrame(lr.weights,
                              columns=['Weight'],
                              index=['Intercept']+self.feature_names)
            print(df)

if __name__ == '__main__':
    data = pd.read_csv('USA_Housing.csv')
    X = data.iloc[:,:-2]
    y = data.Price
    lr = LinearRegression()
    X_train, y_train, X_test, y_test = lr.train_test_split(X,y)
    lr.fit(X_train,y_train)
    preds = lr.predict(X_test)
    lr.summary()


# -*- coding: utf-8 -*-
"""
Created on Thu Feb  4 16:09:56 2016

@author: edouard.duchesnay@cea.fr
"""
import numpy as np
import scipy
import matplotlib.pyplot as plt
import seaborn as sns
#%matplotlib inline

'''
Mahalanobis distance
====================
'''

from matplotlib.patches import Ellipse
def plot_cov_ellipse(cov, pos, nstd=2, ax=None, **kwargs):
    """
    Plots an `nstd` sigma error ellipse based on the specified covariance
    matrix (`cov`). Additional keyword arguments are passed on to the
    ellipse patch artist.

    Parameters
    ----------
        cov : The 2x2 covariance matrix to base the ellipse on
        pos : The location of the center of the ellipse. Expects a 2-element
            sequence of [x0, y0].
        nstd : The radius of the ellipse in numbers of standard deviations.
            Defaults to 2 standard deviations.
        ax : The axis that the ellipse will be plotted on. Defaults to the
            current axis.
        Additional keyword arguments are pass on to the ellipse patch.

    Returns
    -------
        A matplotlib ellipse artist
    """
    def eigsorted(cov):
        vals, vecs = np.linalg.eigh(cov)
        order = vals.argsort()[::-1]
        return vals[order], vecs[:,order]

    if ax is None:
        ax = plt.gca()

    vals, vecs = eigsorted(cov)
    theta = np.degrees(np.arctan2(*vecs[:,0][::-1]))

    # Width and height are "full" widths, not radius
    width, height = 2 * nstd * np.sqrt(vals)
    ellip = Ellipse(xy=pos, width=width, height=height, angle=theta, **kwargs)

    ax.add_artist(ellip)
    return ellip

n_samples, n_features = 100, 2
mean0, mean1 = np.array([0, 0]), np.array([0, 2])
Cov = np.array([[1, .8],[.8, 1]])
np.random.seed(42)
X0 = np.random.multivariate_normal(mean0, Cov, n_samples)
X1 = np.random.multivariate_normal(mean1, Cov, n_samples)

x = np.array([2, 2])

plt.scatter(X0[:, 0], X0[:, 1], color='b')
plt.scatter(X1[:, 0], X1[:, 1], color='r')
plt.scatter(mean0[0], mean0[1], color='b', s=200, label="m0")
plt.scatter(mean1[0], mean1[1], color='r', s=200, label="m2")
plt.scatter(x[0], x[1], color='k', s=200, label="x")
plot_cov_ellipse(Cov, pos=mean0, facecolor='none', linewidth=2, edgecolor='b')
plot_cov_ellipse(Cov, pos=mean1, facecolor='none', linewidth=2, edgecolor='r')
plt.legend(loc='upper left')

#
d2_m0x = scipy.spatial.distance.euclidean(mean0, x)
d2_m0m2 = scipy.spatial.distance.euclidean(mean0, mean1)

Covi = scipy.linalg.inv(Cov)
dm_m0x = scipy.spatial.distance.mahalanobis(mean0, x, Covi)
dm_m0m2 = scipy.spatial.distance.mahalanobis(mean0, mean1, Covi)

print('Euclidean dist(m0, x)=%.2f > dist(m0, m2)=%.2f' % (d2_m0x, d2_m0m2))
print('Mahalanobis dist(m0, x)=%.2f < dist(m0, m2)=%.2f' % (dm_m0x, dm_m0m2))

'''
## Exercise

- Write a function `euclidean(a, b)` that compute the euclidean distance
- Write a function `mahalanobis(a, b, Covi)` that compute the euclidean
  distance, with the inverse of the covariance matrix. Use `scipy.linalg.inv(Cov)`
  to invert your matrix.
'''
def euclidian(a, b):
    return np.sqrt(np.sum((a - b) ** 2))

def mahalanobis(a, b, cov_inv):
    return np.sqrt(np.dot(np.dot((a - b), cov_inv),  (a - b).T))

assert mahalanobis(mean0, mean1, Covi) == dm_m0m2
assert euclidian(mean0, mean1)  == d2_m0m2

mahalanobis(X0, mean0, Covi)
X = X0
mean = mean0
covi= Covi

np.sqrt(np.dot(np.dot((X - mean), covi),  (X - mean).T))

def mahalanobis(X, mean, covi):
    """
    from scipy.spatial.distance import mahalanobis
    d2= np.array([mahalanobis(X[i], mean, covi) for i in range(X.shape[0])])
    np.all(mahalanobis(X, mean, covi) == d2)
    """
    return np.sqrt(np.sum(np.dot((X - mean), covi) *  (X - mean), axis=1))


import numpy as np
from timeit import default_timer as timer

def matrix_prod(A, B, display = False):
    """
    Computes the matrix product of two matrices using array slicing and vector operations.
    """
    if A.shape[1] != B.shape[0]:
        raise ValueError("Dimensions not compatible.")

    # Not allowed!?
    #matrix = A.dot(B)

    # Dotproduct of each A.row*B.clm
    matrix = np.array([[np.sum(A[i,:]*B[:,j]) for j in range(B.shape[1])]
                       for i in range(A.shape[0])])

    if display:
        print(matrix)

    return matrix

def matrix_prod2(A, B, display = False):
    """
    Computes the matrix product of two matrices using python loops.
    """
    if A.shape[1] != B.shape[0]:
        raise ValueError("Dimensions not compatible.")

    matrix = np.zeros((A.shape[0], B.shape[1]))
    for i in range(A.shape[0]):
        for j in range(B.shape[1]):
            for l in range(A.shape[1]):
                matrix[i,j] += A[i,l]*B[l,j]

    if display:
        print(matrix)

    return matrix

def main():
    # Test functions
    A = np.array([[1,2], [3,4]])
    B = np.array([[1,2,3], [4,5,6]])
    print(A.dot(B))
    matrix_prod(A, B, True)
    matrix_prod2(A, B, True)

    #---------------------------------------------------------
    print("### Test performance ###")
    # Generate large random matrices
    np.random.seed(0)
    A = np.random.randint(low=0, high=100, size=(100,500))
    B = np.random.randint(low=0, high=100, size=(500,200))

    # Method #0: numpy.dot()
    tic = timer()
    A.dot(B)
    toc = timer()
    print("Reference - np.dot(): ", toc - tic, "s")

    # Method #1: using slicing
    tic = timer()
    matrix_prod(A, B)
    toc = timer()
    print("Method #1 - Slicing: ", toc - tic, "s")

    # Method #2: using loops
    tic = timer()
    matrix_prod2(A, B)
    toc = timer()
    print("Method #2 - Looping: ", toc - tic, "s")

    """ Result (on my machine)
    Reference - np.dot():       0.0094825 s
    Method #1 - Slicing:        0.1890397 s
    Method #2 - Looping:       11.7723318 s

    Interpretation:
    Slicing > looping through elements. In-built function are best.
    """

if __name__ == "__main__":
    main()


# linear regression
# from statsmodels.api import OLS
# import statsmodels.api as sm

def linreg_model(train_data,test_data):
    # sort
    sorted_train = train_data.sort_values(['x'])
    sorted_test = test_data.sort_values(['x'])

    x_train, x_test, y_train, y_test = sorted_train['x'], sorted_test['x'], sorted_train['y'], sorted_test['y']
    # Add constant to x data
    x_train_ca = sm.add_constant(x_train)
    x_test_ca = sm.add_constant(x_test)

    # Create Linear Regression object
    linreg_model = sm.OLS(y_train, x_train_ca)

    # Fit
    results = linreg_model.fit()

    # predict
    train_preds = results.predict(x_train_ca)
    test_preds = results.predict(x_test_ca)

    # find r^2
    r2_train = metrics.r2_score(y_train, results.predict(x_train_ca))
    r2_test = metrics.r2_score(y_test, results.predict(x_test_ca))

    return train_preds, test_preds, r2_train, r2_test

def plot_predictions2(k,train_data,test_data, knn_train_preds, knn_test_preds, linreg_train_preds, linreg_test_preds):

    # SubPlots
    fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(20,6))
    axes[0].plot(train_data['x'], train_data['y'], 'bo',alpha = 0.5, label = 'Data' )
    axes[0].plot(train_data['x'], knn_train_preds,'m-', linewidth = 2, markersize = 10,  label = 'KNN Preds')
    axes[0].plot(train_data['x'], linreg_train_preds,'k-', linewidth = 2, markersize = 10, label = 'Linreg Preds')
    axes[0].set_xlabel('x')
    axes[0].set_ylabel('y')
    axes[0].set_title("Train Data")
    axes[0].legend()

    axes[1].plot(test_data['x'], test_data['y'], 'r*', alpha = 0.5, label = 'Data' )
    axes[1].plot(test_data['x'], knn_test_preds, 'y-', linewidth = 2, markersize = 10, label = 'KNN Preds')
    axes[1].plot(test_data['x'], linreg_test_preds, 'g-', linewidth = 2, markersize = 10,label = 'Test Preds')
    axes[1].set_xlabel('x')
    axes[1].set_ylabel('y')
    axes[1].set_title("Test Data")
    axes[1].legend()

    fig.suptitle("KNN vs Linear Regression")
    plt.show()

# get predictions
linreg_train_preds,linreg_test_preds, linreg_r2_train, linreg_r2_test = linreg_model(sim_train_data,sim_test_data)

# plot linreg predictions side by side with knn predictions
k=10
plot_predictions2(k, sim_sorted_train, sim_sorted_test, knn_train_preds[1], knn_test_preds[1], linreg_train_preds, linreg_test_preds)

# print r2 scores for knn with k=10 and linreg
print("R^2 Score of kNN on training set with k={}:".format(k), knn_r2_train_scores[1])
print("R^2 Score of kNN on testing set: with k={}".format(k), knn_r2_test_scores[1])
print("R^2 Score of linear regression on training set", linreg_r2_train)
print("R^2 Score of linear regression on testing set", linreg_r2_test)


"""Auxiliary functions for approximation lecture."""
import numpy as np

def spline_basis(x, degree, h, a):
    """Return spline."""
    nu = a + (degree - 1) * h

    if np.abs(x - nu) <= h:
        return 1 - (np.abs(x - nu)) / h
    else:
        return 0.0

    return nu

def get_uniform_nodes(n, a=-1, b=1):
    """Return uniform nodes."""
    return np.linspace(a, b, num=n)

def get_chebyshev_nodes(n, a=-1, b=1):
    """Return Chebyshev nodes."""
    nodes = np.tile(np.nan, n)

    for i in range(1, n + 1):
        nodes[i - 1] = (a + b) / 2 + ((b - a) / 2) * np.cos(((n - i + 0.5) / n) * np.pi)

    return nodes

def compute_interpolation_error(error):
    """Compute interpolation error."""
    return np.log10(np.linalg.norm(error, np.inf))

def compute_interpolation_error_df(df):
    """Compute interpolation error from a pandas.DataFrame."""
    return np.log10(np.linalg.norm(df["Error"], np.inf))


def taylorarctan(indicies,x):
    import math
    sum = 0
    lastIndex = len(indicies) - 1
    for n in range(indicies[0],indicies[lastIndex]):
        middle = ((2*n)+1)
        sum = sum + ((-1)**n)*((x**(middle)) / (middle))
    return sum

import math, time
import numpy as np

precision = 131072
indicies = list(range(precision))
numJobs = 16
Time = 0
nums = (-0.9999999999,0.9999999999,0.0000000001,0.7853194857,-0.82,0.4792378463)
joblist = np.array_split(indicies,numJobs)

for x in nums:
    results = 0
    start_time = time.time_ns()

    for job in joblist:
        results = results + taylorarctan(job,x)

    end_time = time.time_ns()
    Time = ((end_time - start_time)/1000000000)

    print("Arctan of %s" % x)
    print("Taylor Approximation at %s precision: %s" % (precision, results))
    realResults = np.arctan(x)
    print("Function Results: %s" % realResults)
    difference = realResults - results
    dPercentage = (1-abs(difference/realResults)) * 100
    print("Similarity: %s %% " % dPercentage)
    print("Time taken: %s" % Time)
    print("")


import numpy as np
from transforms import decay
import matplotlib.pyplot as plt

plt.rcParams["figure.figsize"]=(8, 8)
plt.rcParams['figure.dpi'] = 80

BETA_INIT = 1
T_MIN = 0
T_MAX = 100

# Plot a single decay trajectory
rho = 0.9
beta = BETA_INIT

Xs = list(range(T_MIN, T_MAX))
Ys = []

for t in range(T_MIN, T_MAX):
    beta = decay(rho, BETA_INIT, t / (T_MAX - 1))
    Ys.append(beta)

plt.plot(Xs, Ys)
plt.title('Rho = {}'.format(rho))
plt.show()

# Plot mutltiple trajectory shapesd (values for rho)
N = 20
RHOs = np.linspace(0, 1, N)

Xs = np.array((range(T_MIN, T_MAX))) / T_MAX
Ys = []
plt.gca().set_prop_cycle(plt.cycler('color', plt.cm.turbo(np.linspace(0, 1, len(RHOs)))))

# Generate values
for RHO in RHOs:

    Y = []
    beta = BETA_INIT

    for t in range(T_MIN, T_MAX):
        beta = decay(RHO, BETA_INIT, t / (T_MAX - 1))
        Y.append(beta)

    Ys.append(Y)

# Plot
for i, Y in enumerate(Ys):
    plt.plot(Xs, Y, label='rho = {}'.format(round(RHOs[i], 2)))

plt.legend(loc='center right')
plt.ylabel('Parameter value')
plt.xlabel('Progress (t/T)')
plt.title('Decay algorithm Rosokha & Younge (2019) for initial value of {} and rho in [0,1]'.format(BETA_INIT))
plt.show()


"""
    Mnożenie macierzy przez wektor:
    Mając wektor n-elementowy x oraz macierz A o wymiarach m×n napisać funkcję obliczającą ich iloczyn:
                                    y = Ax

    1) Za pomocą iteracji po elementach (podwójna pętla)
    2) korzystając z faktu, że każdy element wektora yi jest iloczynem skalarnym i-tego rzędu macierzy A oraz wektora x (pojedyncza pętla)
    3) używając funkcji: np.dot lub np.tensordot (bez pętli)
"""

import numpy as np

MESSAGE_REGARDING_DIFFERENT_DIMENSIONS = "The number of columns in the matrix " \
                                         "should be the same as the length of the vector."

def y1(A, x):
    # liczba kolumn(n) w macierzu A
    n = A.shape[1]

    # Jeżeli liczba kolumn w macierzy A nie jest równa długości wektora x
    # wyrzuć wyjątek
    if n != len(x):
        raise ValueError(MESSAGE_REGARDING_DIFFERENT_DIMENSIONS)

    y = []

    # Przechodzimy przez kolejne wiersze macierzy A
    for row in A:

        # Obliczanie każdego kolejnego elementu wektora y,
        # w którym i-ty element to iloczyn skalarny wektora x i i-tego wiersza macierza A
        scalar_product = 0

        for idx in range(n):
            scalar_product += row[idx] * x[idx]

        y.append(scalar_product)

    return y

def y2(A, x):
    # liczba kolumn(n) w macierzu A
    n = A.shape[1]

    # Jeżeli liczba kolumn w macierzy A nie jest równa długości wektora x
    # wyrzuć wyjątek
    if n != len(x):
        raise ValueError(MESSAGE_REGARDING_DIFFERENT_DIMENSIONS)

    y = []

    # Przechodzimy przez kolejne wiersze macierzy A
    for row in A:
        # Obliczanie każdego kolejnego elementu wektora y ( za pomocą funkcji z pakietu numpy (inner)),
        # w którym i-ty element to iloczyn skalarny wektora x i i-tego wiersza macierza A
        scalar_product = np.inner(row, x)
        y.append(scalar_product)

    return y

def y3(A, x):
    # liczba kolumn(n) w macierzu A
    n = A.shape[1]

    # Jeżeli liczba kolumn w macierzy A nie jest równa długości wektora x
    # wyrzuć wyjątek
    if n != len(x):
        raise ValueError(MESSAGE_REGARDING_DIFFERENT_DIMENSIONS)

    return np.dot(A, x)

# Tests
A = np.arange(9).reshape(3, 3)
x = np.arange(3)
np.testing.assert_almost_equal(y1(A, x), np.array([5, 14, 23]))
np.testing.assert_almost_equal(y2(A, x), np.array([5, 14, 23]))
np.testing.assert_almost_equal(y3(A, x), np.array([5, 14, 23]))

try:
    y1(np.arange(9).reshape(3, 3), np.array([1, 1]))
except (BaseException, Exception) as error:
    if type(error) != ValueError or \
            str(error) != MESSAGE_REGARDING_DIFFERENT_DIMENSIONS:
        raise AssertionError("Second dimension of matrix should "
                             "be the same as dimension of vector!.")

try:
    y2(np.arange(9).reshape(3, 3), np.array([1, 1]))
except (BaseException, Exception) as error:
    if type(error) != ValueError or \
            str(error) != MESSAGE_REGARDING_DIFFERENT_DIMENSIONS:
        raise AssertionError("Second dimension of matrix should "
                             "be the same as dimension of vector!.")

try:
    y3(np.arange(9).reshape(3, 3), np.array([1, 1]))
except (BaseException, Exception) as error:
    if type(error) != ValueError or \
            str(error) != MESSAGE_REGARDING_DIFFERENT_DIMENSIONS:
        raise AssertionError("Second dimension of matrix should "
                             "be the same as dimension of vector!.")

print("All tests were successful.")


# -*- coding: utf-8 -*-
"""
Created on Sat Sep 26 22:11:55 2015

@author: Nathan
"""

from __future__ import print_function
import numpy as np

try:
	from numba import autojit
except:
	def autojit(a):
		return a

@autojit
def EulerX(beta, mode='rad', clockwise=False):
	# Return the Euler matrix for rotation about the X axis
	if mode == 'deg':
		beta = np.deg2rad(beta)

	if not clockwise:
		x_rot_mat = np.array([	[1, 	0, 				0			],
								[0, 	np.cos(beta), 	np.sin(beta)],
								[0, 	-np.sin(beta), 	np.cos(beta)]	])
	elif clockwise:
		x_rot_mat = np.array([	[1, 	0, 				0			],
								[0, 	np.cos(beta),	-np.sin(beta)],
								[0, 	np.sin(beta), 	np.cos(beta)]	])

	return x_rot_mat

@autojit
def EulerY(theta, mode='rad', clockwise=False):
	# Return the Euler matrix for rotation about the Y axis
	if mode == 'deg':
		theta = np.deg2rad(theta)

	if not clockwise:
		y_rot_mat = np.array([	[np.cos(theta), 0, 	-np.sin(theta)	],
								[0, 			1,	0				],
								[np.sin(theta), 0, 	np.cos(theta)	]	])
	elif clockwise:
		y_rot_mat = np.array([	[np.cos(theta), 0, 	np.sin(theta)	],
								[0, 			1,	0				],
								[-np.sin(theta), 0, 	np.cos(theta)]	])

	return y_rot_mat

@autojit
def EulerZ(alpha, mode='rad', clockwise=False):
	# Return the Euler matrix for rotation about the Z axis
	if mode == 'deg':
		theta = np.deg2rad(alpha)

	if not clockwise:
		z_rot_mat = np.array([	[np.cos(alpha),		np.sin(alpha),	0],
								[-np.sin(alpha),	np.cos(alpha),	0],
								[0,					0,				1]	])
	elif clockwise:
		z_rot_mat = np.array([	[np.cos(alpha),		-np.sin(alpha),	0],
								[np.sin(alpha),		np.cos(alpha),	0],
								[0,					0,				1]	])

	return z_rot_mat

@autojit
def rotx(points, beta, mode='deg'):
	"""
	Rotate points about the global X axis by the angles beta (roll).  Rotation
	matrices are sourced from http://mathworld.wolfram.com/RotationMatrix.html
	"""

	if mode == 'rad':
		pass
	elif mode == 'deg':
		beta	=	np.deg2rad(beta)
	else:
		print('ERROR: Incorrect angle type specified.  Assuming degrees.')

	# Rotation about X axis
	x_rot_mat = np.array([	[1, 	0, 				0			],
							[0, 	np.cos(beta), 	np.sin(beta)],
							[0, 	-np.sin(beta), 	np.cos(beta)]	])

	# Sequentially rotate input points about X, Y and then Z axes
	if np.size(points) == 3:
		rot_x 	= np.dot(points, x_rot_mat)
	else:
		rot_x 	= np.dot(points, x_rot_mat)

	return rot_x

@autojit
def roty(points, theta, mode='deg'):
	"""
	Rotate points about the global Y axis by the angles theta (yaw).  Rotation
	matrices are sourced from http://mathworld.wolfram.com/RotationMatrix.html
	"""

	if mode == 'rad':
		pass
	elif mode == 'deg':
		theta	=	np.deg2rad(theta)
	else:
		print('ERROR: Incorrect angle type specified.  Assuming degrees.')

	# Rotation about Y axis
	y_rot_mat = np.array([	[np.cos(theta), 0, 	-np.sin(theta)	],
							[0, 			1,	0				],
							[np.sin(theta), 0, 	np.cos(theta)	]	])

	# Sequentially rotate input points about X, Y and then Z axes
	if np.size(points) == 3:
		rot_y 	= np.dot(points, y_rot_mat)
	else:
		rot_y 	= np.dot(points, y_rot_mat)

	return rot_y

@autojit
def rotz(points, alpha, mode='deg'):
	"""
	Rotate points about the global Z axis by the angles theta (pitch).  Rotation
	matrices are sourced from http://mathworld.wolfram.com/RotationMatrix.html
	"""

	if mode == 'rad':
		pass
	elif mode == 'deg':
		alpha	=	np.deg2rad(alpha)
	else:
		print('ERROR: Incorrect angle type specified.  Assuming degrees.')

	# Rotation about Z axis
	z_rot_mat = np.array([	[np.cos(alpha),		np.sin(alpha),	0],
							[-np.sin(alpha),	np.cos(alpha),	0],
							[0,					0,				1]	])

	# Sequentially rotate input points about X, Y and then Z axes
	if np.size(points) == 3:
		rot_z 	= np.dot(points, z_rot_mat)
	else:
		rot_z 	= np.dot(points, z_rot_mat)

	return rot_z

@autojit
def rotxyz(points, beta, theta, alpha, mode='deg', clockwise=False):
	"""
	=== triPy.rotxyz ===
	Rotates points about the global X, Y and Z axes by the angles beta, theta,
	and alpha (roll, yaw, and pitch, respectively) in that order.  Rotation
	matrices are sourced from http://mathworld.wolfram.com/RotationMatrix.html

	=== Inputs ===
	'points'	XYZ coordinates of the points to be rotated
	'beta'		Angle of rotation about the X axis (roll)
	'theta'		Angle of rotation about the Y axis (yaw)
	'alpha'		Angle of rotation about the Z axis (pitch)
	'mode'		Defines whether angles are expressed in radians or degrees
				(degrees are the default)
	'clockwise' Defines direction of rotation about axes
				(may be either True or False - the default is False: anticlockwise)

	=== Usage ===
	import triPy
	rot_x, rot_y, rot_z = rotMatrix(x, y, z, beta, theta, alpha)
	"""

	if mode == 'rad':
		pass
	elif mode == 'deg':
		beta		=	np.deg2rad(beta)
		theta	=	np.deg2rad(theta)
		alpha	=	np.deg2rad(alpha)
	else:
		print('ERROR: Incorrect angle type specified.  Assuming degrees.')

	if clockwise == False:
		# Rotation about X axis
		x_rot_mat = np.array([	[1, 	0, 				0			],
								[0, 	np.cos(beta), 	np.sin(beta)],
								[0, 	-np.sin(beta), 	np.cos(beta)]	])

		# Rotation about Y axis
		y_rot_mat = np.array([	[np.cos(theta), 0, 	-np.sin(theta)	],
								[0, 			1,	0				],
								[np.sin(theta), 0, 	np.cos(theta)	]	])

		# Rotation about Z axis
		z_rot_mat = np.array([	[np.cos(alpha),		np.sin(alpha),	0],
								[-np.sin(alpha),		np.cos(alpha),	0],
								[0,					0,				1]	])

	else:
		# Rotation about X axis
		x_rot_mat = np.array([	[1, 	0, 				0			],
								[0, 	np.cos(beta),	-np.sin(beta)],
								[0, 	np.sin(beta), 	np.cos(beta)]	])

		# Rotation about Y axis
		y_rot_mat = np.array([	[np.cos(theta), 0, 	np.sin(theta)	],
								[0, 			1,	0				],
								[-np.sin(theta), 0, 	np.cos(theta)	]	])

		# Rotation about Z axis
		z_rot_mat = np.array([	[np.cos(alpha),		-np.sin(alpha),	0],
								[np.sin(alpha),	np.cos(alpha),	0],
								[0,					0,				1]	])

	# Sequentially rotate input points about X, Y and then Z axes
	if np.size(points) == 3:
		rot_x 	= np.dot(points, x_rot_mat)
		rot_xy 	= np.dot(rot_x, y_rot_mat)
		rot_xyz = np.dot(rot_xy, z_rot_mat)
	else:
		rot_x 	= np.dot(points, x_rot_mat)
		rot_xy 	= np.dot(rot_x, y_rot_mat)
		rot_xyz = np.dot(rot_xy, z_rot_mat)

	return rot_xyz

@autojit
def rotzyx(points, beta, theta, alpha, mode='deg', clockwise=False):
	"""
	=== triPy.rotxyz ===
	Rotates points about the global X, Y and Z axes by the angles beta, theta,
	and alpha (roll, yaw, and pitch, respectively) in reverse order.  Rotation
	matrices are sourced from http://mathworld.wolfram.com/RotationMatrix.html

	=== Inputs ===
	'points'	XYZ coordinates of the points to be rotated
	'beta'		Angle of rotation about the X axis (roll)
	'theta'		Angle of rotation about the Y axis (pitch)
	'alpha'		Angle of rotation about the Z axis (yaw)
	'mode'		Defines whether angles are expressed in radians or degrees
				(degrees are the default)
	'clockwise' Defines direction of rotation about axes
				(may be either True or False - the default is False: anticlockwise)

	=== Usage ===
	import triPy
	rot_x, rot_y, rot_z = rotMatrix(x, y, z, beta, theta, alpha)
	"""

	if mode == 'rad':
		pass
	elif mode == 'deg':
		beta		=	np.deg2rad(beta)
		theta	=	np.deg2rad(theta)
		alpha	=	np.deg2rad(alpha)
	else:
		print('ERROR: Incorrect angle type specified.  Assuming degrees.')

	if clockwise == False:
		# Rotation about X axis
		x_rot_mat = np.array([	[1, 	0, 				0			],
								[0, 	np.cos(beta), 	np.sin(beta)],
								[0, 	-np.sin(beta), 	np.cos(beta)]	])

		# Rotation about Y axis
		y_rot_mat = np.array([	[np.cos(theta), 0, 	-np.sin(theta)	],
								[0, 			1,	0				],
								[np.sin(theta), 0, 	np.cos(theta)	]	])

		# Rotation about Z axis
		z_rot_mat = np.array([	[np.cos(alpha),		np.sin(alpha),	0],
								[-np.sin(alpha),	np.cos(alpha),	0],
								[0,					0,				1]	])

	else:
		# Rotation about X axis
		x_rot_mat = np.array([	[1, 	0, 				0			],
								[0, 	np.cos(beta),	-np.sin(beta)],
								[0, 	np.sin(beta), 	np.cos(beta)]	])

		# Rotation about Y axis
		y_rot_mat = np.array([	[np.cos(theta), 0, 	np.sin(theta)	],
								[0, 			1,	0				],
								[-np.sin(theta), 0, 	np.cos(theta)	]	])

		# Rotation about Z axis
		z_rot_mat = np.array([	[np.cos(alpha),		-np.sin(alpha),	0],
								[np.sin(alpha),	np.cos(alpha),	0],
								[0,					0,				1]	])

	# Sequentially rotate input points about X, Y and then Z axes
	if np.size(points) == 3:
		rot_z 	= np.dot(points, z_rot_mat)
		rot_zy 	= np.dot(rot_z, y_rot_mat)
		rot_zyx = np.dot(rot_zy, x_rot_mat)
	else:
		rot_z 	= np.dot(points, z_rot_mat)
		rot_zy 	= np.dot(rot_z, y_rot_mat)
		rot_zyx = np.dot(rot_zy, x_rot_mat)

	return rot_zyx

@autojit
def eulerToVector(alpha, theta):
	"""
	Convert Euler angles (pitch and yaw; alpha and theta) to vector components
	in XYZ reference frame.
	"""

	x = np.sin(alpha) * np.cos(theta)
	y = np.sin(alpha) * np.sin(theta)
	z = np.cos(alpha)

	return [x, y, z]

@autojit
def vectorToEuler(x, y, z):
	"""
	Convert XYZ vector components to Euler angles (pitch and yaw; alpha and
	theta)
	"""

	# Trig relations
	r = np.linalg.norm([x, y, z])
	theta = np.arctan(y / x)
	alpha = np.arccos(z / r)

#	1st quad: 0
#	2nd quad: 180-
#	3rd quad: +180
#	4th quad: 360-

	# Identify quadrants in XY plane and adjust returned angle accordingly
	# Rotation about Z axis (yaw, theta)
	if (x > 0) & (y > 0):
		# 1st quadrant
		pass
	elif (x < 0) & (y > 0):
		# 2nd quadrant
		theta = np.pi - np.abs(theta)
	elif (x < 0) & (y < 0):
		# 3rd quadrant
		theta = np.pi + np.abs(theta)
	elif (x > 0) & (y < 0):
		# 4th quadrant
		theta = (2 * np.pi) - np.abs(theta)
	else:
		# Quadrant borders i.e. x = 0 or y = 0
		pass

	# Identify quadrants in XZ plane and adjust returned angle accordingly
	# Rotation about Y axis (pitch, alpha)
	if (x > 0) & (z > 0):
		# 1st quadrant
		pass
	elif (x < 0) & (z > 0):
		# 2nd quadrant
		alpha = np.pi - np.abs(alpha)
	elif (x < 0) & (z < 0):
		# 3rd quadrant
		alpha = np.pi + np.abs(alpha)
	elif (x > 0) & (z < 0):
		# 4th quadrant
		alpha = (2 * np.pi) - np.abs(alpha)
	else:
		# Quadrant borders i.e. x = 0 or y = 0
		pass

	return [alpha, theta]

#@autojit
def localPitchRoll(pitch, yaw, roll, clockwise=False):
	"""
	Calculate pitch and roll of an object in its local vetical plane
	NB: pitch, yaw, and roll denote rotations about the Y, Z, and X axes,
	respectively.
	"""

	# Define initial pointing vectors
	vecX = np.array([1, 0, 0])
	vecZ = np.array([0, 0, 1])

	# Transform vectors using provided Euler angles
	vecX1 = rotxyz(vecX, roll, pitch, yaw, mode='rad', clockwise=clockwise)
	vecZ1 = rotxyz(vecZ, roll, pitch, yaw, mode='rad', clockwise=clockwise)

	# Project vectors onto local YZ plane
	vecX1_proj = np.array([0, vecX1[1], vecX1[2]])
	vecZ_proj = np.array([0, 0, np.linalg.norm(vecX1_proj)])

	# Calculate angle to roll vectors back into XY plane
#	planeRoll = np.arccos(np.dot(vecX1, vecZ_proj) / (np.linalg.norm(vecX1_proj)**2))
	planeRoll = vectorAngle(vecX1_proj, vecZ_proj)

	# Calculate local pitch angle (about local Y axis)
	vecZ2 = rotxyz(vecZ1, planeRoll, 0, 0, mode='rad', clockwise=clockwise)
#	localPitch = np.arccos(np.dot(vecX1, [1, 0, 0]) / (np.linalg.norm(vecX1)))
	localPitch = vectorAngle(vecX1, vecX)

	# Transform normal vector into ZY plane (pitch) to find local roll
	vecZ3 = rotxyz(vecZ2, 0, -localPitch, 0, mode='rad', clockwise=clockwise)
#	localRoll = np.arccos(np.dot(vecZ3, [0, 0, 1]) / (np.linalg.norm(vecZ3)))
	localRoll = vectorAngle(vecZ3, vecZ)

	return localPitch, localRoll, planeRoll

@autojit
def vectorAngle(a, b):
	"""
	Return angle between two vectors
	"""
	return np.arccos(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))


import numpy as np

class LOWESS:

    def __init__(self, sigma=1., frac=1., eps=1e-8):
        self.sigma = sigma
        self.frac = frac
        self.eps = eps
        self.X_ = None
        self.y_ = None

    def _compute_weights(self, x):
        distances = np.linalg.norm(self.X_ - x[:, None], axis=-1)

        # gaussian kernel where sigma define the brandwidth
        weights = np.exp(-(distances ** 2) / self.sigma)

        # take weights of the closest points only
        weights = weights * (distances <= np.quantile(distances, q=self.frac))

        # clip weights close to zero
        weights = np.where(np.abs(weights) >= self.eps, weights, 0.)

        return weights

    def fit(self, X, y):
        self.X_ = X
        self.y_ = y

    def predict(self, X):
        n_samples = X.shape[0]
        y_hat = np.zeros(n_samples)
        for i in range(n_samples):
            y_hat[i] = np.average(
                self.y_,
                weights=self._compute_weights(X[i, :]),
            )

        return y_hat


import matplotlib.pyplot as plt
import numpy as np

def PolynomialFit(x, y):
    # Fit using Vandermonde Matrix, Condition number can explode
    assert len(x) == len(y)
    N = len(x)
    A = np.ones((N,N))
    for i in range(1, N):
        A[:, i] = x
        x = x * x
    return np.linalg.solve(A, y), np.linalg.cond(A)

def PolynomialEval(p, x):
    # Evaluate Polynomial Fit, via Horners Rule
    # p - polynomial coefficients, x - xvalues
    i, v = len(p) - 1, None
    while i > 0:
        if v is None :
            v = p[i]*x
        else:
            v = x*(p[i] + v)
            i = i -1
    v += p[i]
    return v

def CubicFit(x, y):
    # cubic spline with continuity conditions
    d = len(x) -1
    a = np.zeros((4*d,4*d))
    s = np.zeros(4*d)
    r = 0
    for i in range(d):
        b=4*i
        a[r,b] = 1
        s[r] = y[r]
        r += 1
    b = (d-1)*4
    dk = x[d] - x[d-1]
    a[r, b:]= [1, dk, dk**2, dk**3]
    s[r] = y[r]

    r += 1
    for i in range(1,d):
        b = 4*(i-1)
        dx = x[i] - x[i-1]
        a[r,b:b+8] = [1, dx, dx**2, dx**3, -1, 0, 0, 0]
        a[r+1,b:b+8] = [0, 1, 2*dx, 3*dx**2, 0, -1, 0, 0]
        a[r+2,b:b+8] = [0, 0, 2, 6*dx, 0, 0, -2, 0]
        r += 3
    a[r,0:4] = [0, 0, 2, 0]
    b = 4*(d-1)
    a[r+1,b:b+4] = [0, 0, 2, 0]
    return np.linalg.solve(a,s), np.linalg.cond(a)

def CubicEval(x, c, xi):
    # Evaluate the cubic spline
    #
    #    x - original x knot points
    #    c - vector of spline coefficits
    #   xi - x value being evaluated
    def leval(x, c, xi, i):
        dx = xi - x[i-1]
        b = 4*(i-1)
        return c[b] + c[b+1]*dx + c[b+2]*dx**2 + c[b+3]*dx**3

    d = len(x) - 1
    for i in range(1,d):
        if xi < x[i]:
            return leval(x, c, xi, i)
    return leval(x, c, xi, d)