import numpy as np
from scipy.special import erf, erfinv

# =============================================================================
# Define variables
# =============================================================================
sqrt2 = np.sqrt(2)
sqrt2pi = np.sqrt(2 * np.pi)

# =============================================================================
# Define functions
# =============================================================================
def gaussian1d_variant1(x, *p):
    """
    Create a normalised gaussian (Area == 1) from mean and variance

    :param x: [numpy array]    x axis data (array/list)
    :param p: [Tuple]         (mean , variance)

    Area of gaussian is 1  --> A = 1/(c*sqrt(2pi))

    Returns a Gaussian array one value for each x value
    """
    mu, sigma = p
    A = 1.0 / (sigma * sqrt2pi)
    return A * np.exp(-0.5 * (x - mu) ** 2 / (sigma ** 2))

def gaussian1d_variant2(x, *p):
    """
    Create a gaussian from amplitude, mean and variance

    :param x: [numpy array]    x axis data (array/list)
    :param p: [Tuple]         (a = amplitude, b = mean , c = variance)

    Returns a Gaussian array one value for each x value
    """
    a, b, c = p
    return a * np.exp(-0.5 * (x - b) ** 2 / (c ** 2))

def gaussian2d_variant1(x, y, *p):
    """
    Create a 2D gaussian from amplitude, mean x, variance x, mean y and
    variance y

    :param x: [numpy array]    x axis data (array/list)
    :param y: [numpy array]    y axis data (array/list)
    :param p: [Tuple]         (amplitude, mean x, variance x,
                               mean y, variance y)

    Returns a Gaussian array one value for each x value
    """
    A, x0, sigmax, y0, sigmay = p
    part1 = (x - x0) ** 2 / (2. * sigmax ** 2)
    part2 = (y - y0) ** 2 / (2. * sigmay ** 2)
    # return 2d gaussian
    return A * np.exp(-(part1 + part2))

def gaussian2d_variant2(x, y, *p, **kwargs):
    """
    Create a 2D gaussian from amplitude, mean x, variance x, mean y and
    variance y and a rotation angle theta (0.0 == x-axis) where theta is in
    degrees unless keyword units = radians or rad

    :param x: [numpy array]    x axis data (array/list)
    :param y: [numpy array]    y axis data (array/list)
    :param p: [Tuple]         (amplitude, mean x, variance x,
                               mean y, variance y, theta)
    :param kwargs:             keyword arguments i.e. units = "deg"
    keywords args are as follows:
        - units:               string either deg or rad

    Returns a Gaussian array one value for each x value
    """
    # extract values from p
    A, x0, sigmax, y0, sigmay, theta = p
    # sort out units
    units = kwargs.get('units', 'deg')
    if units == 'deg' or 'deg' in units:
        theta = np.deg2rad(theta)
    else:
        theta = theta
    # calculate gaussian
    a = np.cos(theta) ** 2 / (2. * sigmax ** 2) + np.sin(theta) ** 2 / (
    2. * sigmay ** 2)
    b = -np.sin(2 * theta) / (4. * sigmax ** 2) + np.sin(2 * theta) / (
    4. * sigmay ** 2)
    c = np.cos(theta) ** 2 / (2. * sigmay ** 2) + np.sin(theta) ** 2 / (
    2. * sigmax ** 2)
    part1 = a * (x - x0) ** 2
    part2 = -2 * b * (x - x0) * (y - y0)
    part3 = c * (y - y0) ** 2
    # return 2d gaussian
    return A * np.exp(-(part1 + part2 + part3))

def gaussian2d_variant3(x, y, *p):
    """
    Standard Gaussian function Creator
    :param x: [numpy array]    x axis data (array/list)
    :param y: [numpy array]    y axis data (array/list)
    :param p: [Tuple]         (amplitude, mean x, mean y, a, b, c)

    Returns a Gaussian array one value for each x value
    """
    A, x0, y0, a, b, c = p
    part1 = a * (x - x0) ** 2
    part2 = -2 * b * (x - x0) * (y - y0)
    part3 = c * (y - y0) ** 2
    # return 2d gaussian
    return A * np.exp(-(part1 + part2 + part3))

def sigma2percentile(sigma):
    """
    Percentile calculation from sigma
    i.e. 1 sigma == 0.68268949213708585 (68.27%)
    :param sigma: [float]     sigma value
    :return percentile:  [float] the percentile (i.e. between 0.00 and 1.00)
    """
    # percentile = integral of exp{-0.5x**2}
    percentile = erf(sigma / sqrt2)
    return percentile

def percentile2sigma(percentile):
    """
    Sigma calcualtion from percentile
    i.e. 0.68268949213708585 (68.27%) == 1 sigma
    :param percentile: [float]     percentile value (i.e. between 0.00 and 1.00)
    :return sigma:  [float] the sigma value (i.e. 1, 2, 3, 1.5)
    """
    # area = integral of exp{-0.5x**2}
    sigma = sqrt2 * erfinv(percentile)
    return sigma


# Find eigenvalue and eigenvectors of matrix A.
#Ax = lx
#l = lamda

# Rewrite (A - lI)x = 0
# det(A - lI) = 0

# Find lamda first.

import numpy as np
import scipy.linalg

A = np.array([[3, 1],
              [1, 3]])

(l, eigv) = np.linalg.eig(A)

# More by hand
# Another solution det(A - lI) = 0
# Lamda in diagonal.

# (3-l)² = 1
# Solve for lamda. l1 = 4 and l2 = 2

lamda1 = l[0]
lamda2 = l[1]

lamda1Vector = lamda1 * (np.identity(2))
sol1 = A - lamda1Vector

lamda2Vector = lamda2 * (np.identity(2))
sol2 = A - lamda2Vector

# We will solve a type of Bx = 0. That is solve for N(B), The Nullspace of B.

X1 = scipy.linalg.null_space(sol1)
X2 = scipy.linalg.null_space(sol2)

# Need to reshape to compare.
X1 = X1.reshape((2,))
X2 = X2.reshape((2,))

assert np.allclose(X1, eigv[:,0])
assert np.allclose(X2, eigv[:,1])

print(lamda1)


# Import
import numpy as np  # Matrix
from sklearn.datasets import make_regression  # Random dataset
import matplotlib.pyplot as plt  # Graphic

# Linear regression multiple
# https://github.com/MachineLearnia/Regression-lineaire-numpy/blob/master/R%C3%A9gression%20Lin%C3%A9aire%20Multiple.ipynb
class LrMultiple:
    # Hyper parameter
    learning_rate = 0.1
    n_iteration = 100

    # Configuration
    n_features = 2
    n_param = 2

    def run(self):
        x, y = make_regression(n_samples=100, n_features=self.n_features, noise=10)  # x -> inputs  y -> outputs
        y = y.reshape(y.shape[0], 1)

        X = np.hstack((x, np.ones((x.shape[0], 1))))  # X -> arguments input [[a, 1],[b, 1]...]
        for i in range(2, self.n_param):
            X = np.hstack((X * x, np.ones((x.shape[0], 1))))
            # print(X)

        theta = np.random.randn(X.shape[1], 1)  # Matrix with the arguments of the function

        # Learn
        theta_final, cost_history = self.gradient_descent(X, y, theta, self.learning_rate, self.n_iteration)

        predictions = self.model(X, theta_final)

        print("Stats: Linear regression multiple")  # Show Stats
        print("Iteration: " + str(self.n_iteration))
        print("Learning rate: " + str(self.learning_rate))
        print("Features: " + str(self.n_features))
        print("Parameters: " + str(self.n_param))
        print("x: " + str(x.shape))
        print("y: " + str(y.shape))
        print("X: " + str(X.shape))
        print("Theta: " + str(theta.shape))
        print("Theta final: " + str(theta_final))

        # Show coef /1
        coef = self.coef_determination(y, predictions)
        print("\nCoef: {0:9.3f}/1 ({0})".format(coef))

        # Show graphs
        for i in range(0, self.n_features):
            plt.scatter(x[:, i], y)
            plt.scatter(x[:, i], predictions, c='r')
            plt.show()

        #  Show cost evolution
        plt.plot(range(self.n_iteration), cost_history)
        plt.show()

    def model(self, X, theta):
        return X.dot(theta)

    def cost(self, X, y, theta):
        m = len(y)
        return 1 / (2 * m) * np.sum((self.model(X, theta) - y) ** 2)

    def grad(self, X, y, theta):
        m = len(y)
        return 1 / m * X.T.dot(self.model(X, theta) - y)

    def gradient_descent(self, X, y, theta, learning_rate, n_iteration):
        cost_history = np.zeros(n_iteration)
        for i in range(0, n_iteration):
            theta = theta - learning_rate * self.grad(X, y, theta)
            cost_history[i] = self.cost(X, y, theta)
        return theta, cost_history

    def coef_determination(self, y, pred):
        u = ((y - pred) ** 2).sum()
        v = ((y - y.mean()) ** 2).sum()
        return 1 - u / v


#!/usr/bin/env python3
"""
Clustering Module
"""
import numpy as np

def pdf(X, m, S):
    """
    Calculates the probability density function of a Gaussian distribution:

    X is a numpy.ndarray of shape (n, d) containing the data points whose PDF
    should be evaluated
    m is a numpy.ndarray of shape (d,) containing the mean of the distribution
    S is a numpy.ndarray of shape (d, d) containing the covariance of the
    distribution
    You are not allowed to use any loops
    You are not allowed to use the function numpy.diag or the method
    numpy.ndarray.diagonal

    Returns: P, or None on failure
        P is a numpy.ndarray of shape (n,) containing the PDF values for each
        data point
    All values in P should have a minimum value of 1e-300
    """
    if not isinstance(X, np.ndarray) or len(X.shape) != 2:
        return None
    if not isinstance(m, np.ndarray) or len(m.shape) != 1:
        return None
    if not isinstance(S, np.ndarray) or len(S.shape) != 2:
        return None
    if X.shape[1] != m.shape[0] or X.shape[1] != S.shape[0]:
        return None
    if S.shape[0] != S.shape[1] or X.shape[1] != S.shape[1]:
        return None
    if S.shape[0] != S.shape[1]:
        return None

    n, d = X.shape
    Xmm = X - m
    Sinv = np.linalg.inv(S)

    p = 1. / (np.sqrt(((2 * np.pi)**d * np.linalg.det(S))))
    fac = np.einsum('...k,kl,...l->...', Xmm, Sinv, Xmm)
    q = np.exp(-fac / 2)
    return (np.maximum(p*q, 1e-300))


import numpy as np
import math

def linear(m0,m1,u,domain=None):
    """For completeness: linear interpolation between m0 and m1 at parameter u in [0,1].

    Alternatively, if `domain` != None, then this will use domain=(a,b)
    """
    if domain is not None:
        return linear(m0,m1,(u-domain[0])/(domain[1]-domain[0]))
    return (1.0-u)*m0 + u*m1

def piecewise_linear(ms,u,times=None):
    """Evaluate a piecewise linear spline at interpolation parameter u in [0,1]

    Milestones are given by the array `ms`.  `ms` is assumed to be a list of
    n Numpy arrays, or an n x d Numpy array.

    If `times` != None, this will be a list of n non-decreasing time indices.
    """
    if times is not None:
        raise NotImplementedError("Not done with timed paths")
    n = len(ms)
    s = u*n
    i = int(math.floor(s))
    u = s - i
    if i < 0: return ms[0]
    elif i+1 >= n: return ms[-1]
    return linear(ms[i],ms[i+1],u)

def hermite(m0,m1,t0,t1,u,domain=None):
    """Evaluate a cubic hermite curve at interpolation parameter u in [0,1].

    Endpoints are m0 and m1, with derivatives t0 and t1.  These are assumed to be Numpy arrays.

    Alternatively, if `domain` != None, then this will use domain=(a,b)
    as the interpolation domain
    """
    if domain is not None:
        assert isinstance(domain,(list,tuple)) and len(domain) == 2,"Need to provide a pair as a domain"
        scale = (domain[1]-domain[0])
        t = (u - domain[0])/scale
        return hermite(m0,m1,t0*scale,t1*scale,t)
    u2 = u**2
    u3 = u**3
    cm0 = 2*u3 - 3*u2 + 1
    cm1 = -2*u3 + 3*u2
    ct0 = u3 - 2*u2 + u
    ct1 = u3 - u2
    return cm0*m0 + cm1*m1 + ct0*t0 + ct1*t1

def hermite_deriv(m0,m1,t0,t1,u,domain=None):
    """Evaluate the derivative of a cubic hermite curve at interpolation parameter u in [0,1].

    Endpoints are m0 and m1, with derivatives t0 and t1.  These are assumed to be numpy arrays.

    Alternatively, if `domain` != None, then this will use domain=(a,b)
    as the interpolation domain
    """
    if domain is not None:
        assert isinstance(domain,(list,tuple)) and len(domain) == 2,"Need to provide a pair as a domain"
        scale = (domain[1]-domain[0])
        t = (u - domain[0])/scale
        return hermite_deriv(m0,m1,t0*scale,t1*scale,t)
    u2 = u**2
    cm0 = 6*u2 - 6*u
    cm1 = -6*u2 + 6*u
    ct0 = 3*u2 - 4*u + 1
    ct1 = 3*u2 - 2*u
    return cm0*m0 + cm1*m1 + ct0*t0 + ct1*t1

def hermite_spline(ms,ts,u,times=None):
    """Evaluate a cubic hermite spline at interpolation parameter u in [0,1].

    Milestones are given in `ms`, with derivatives in `ts`.  These are assumed to be
    lists of n Numpy arrays, or n x d Numpy arrays.

    If `times` != None, this will be a list of n non-decreasing time indices.
    """
    if times is not None:
        raise NotImplementedError("Not done with timed paths")

    n = len(ms)
    s = u*n
    i = int(math.floor(s))
    u = s - i
    if i < 0: return ms[0]
    elif i+1 >= n: return ms[-1]
    return hermite(ms[i],ms[i+1],ts[i],ts[i+1],u)


#%%
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
import matplotlib.gridspec as gridspec
from scipy.stats import multivariate_normal as mn
# %%
plt.rcParams['grid.color'] = '#A8BDB7'
plt.rcParams['grid.linestyle'] = '--'
plt.rcParams['text.usetex'] = True
plt.rcParams['text.latex.preamble'] = r'\usepackage{{amsmath}}'

class UpdateFigure:
    def __init__(self, ax1, ax2, ax3):

        self.colors = dict(
            blue        = '#375492',
            green       = '#88E685',
            dark_green  = '#00683B',
            red         = '#93391E',
            pink        = '#E374B7',
            purple      = '#A268B4',
            black       = '#000000',
        )
        self.cm = plt.cm.turbo
        # ====================
        # config data
        # ====================
        self.mean = np.zeros(2)
        self.cov = np.eye(2)
        self.xx, self.yy = np.meshgrid(np.linspace(-4,4,101), np.linspace(-4,4,101))
        xysurf = mn.pdf(np.dstack((self.xx,self.yy)), self.mean, self.cov)
        self.vmin, self.vmax = 0, np.max(xysurf)

        # ====================
        # draw LaTeX formula
        # ====================
        ax1.axis('off')
        self.tex = ax1.text(0.5,0.5,
            self.gen_text(self.mean, self.cov),
            ha='center', va='center',
            color='k', fontsize=25)

        # ====================
        # plot 3d surface
        # ====================
        self.surf = ax2.plot_surface(self.xx, self.yy, xysurf, cmap=self.cm,
                        rstride=1, cstride=1, vmin=self.vmin, vmax=self.vmax)
        ax2.view_init(10, None)

        ax2.set_xlabel(r'$x$', fontsize=20)
        ax2.set_ylabel(r'$y$', fontsize=20)
        ax2.zaxis.set_rotate_label(False)
        # ax2.set_zlabel(r'$f(y)$', rotation=0, fontsize=20)
        xticks=[-4,-2,0,2,4]
        yticks=[-4,-2,0,2,4]
        zticks=[0,.1,.2]
        ax2.set_xticks(xticks)
        ax2.set_yticks(yticks)
        ax2.set_zticks(zticks)
        ax2.set_xlim(xticks[0], xticks[-1])
        ax2.set_ylim(yticks[0], yticks[-1])
        ax2.set_zlim(zticks[0], zticks[-1])
        # ax2.invert_xaxis()
        ax2.xaxis.pane.fill = False
        ax2.yaxis.pane.fill = False
        ax2.zaxis.pane.fill = False
        ax2.xaxis.pane.set_edgecolor('w')
        ax2.yaxis.pane.set_edgecolor('w')
        ax2.zaxis.pane.set_edgecolor('w')
        self.ax2 = ax2

        # ====================
        # draw 2d pcolor
        # ====================
        self.mesh = ax3.pcolormesh(self.xx, self.yy, xysurf, cmap=self.cm)
        ax3.axis('scaled')
        ax3.set_xticks([-4,-2,0,2,4])
        ax3.set_yticks([-4,-2,0,2,4])

    def set_target(self, trans_type, diff, nframe):
        self.trans_type = trans_type
        if trans_type == 'stretch':
            self.diff = (diff-np.eye(2))*1.0/(nframe-1)
        else:
            self.diff = diff*1.0/(nframe-1)

    @staticmethod
    def rot(mat, theta):
        rot_mat =  np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]])
        return rot_mat.T@mat@rot_mat

    @staticmethod
    def stretch(mat, diff):
        stretch_mat = np.sqrt(np.eye(2) + diff)
        return stretch_mat@mat@stretch_mat

    @staticmethod
    def gen_text(_mean, _cov):
        return r"$\boldsymbol{\mu}=\begin{bmatrix}%.1f\\%.1f\end{bmatrix},\boldsymbol{\Sigma}=\begin{bmatrix}%.1f & %.1f \\ %.1f & %.1f\end{bmatrix}$"%(*_mean, *_cov.flatten())

    def __call__(self, i):
        if self.trans_type == 'rotation':
            mean_ = self.mean.copy()
            cov_ = self.rot(self.cov, self.diff*i)
        elif self.trans_type == 'stretch':
            mean_ = self.mean.copy()
            cov_ = self.stretch(self.cov, self.diff*i)
        elif self.trans_type == 'translation':
            mean_ = self.mean+self.diff*i
            cov_ = self.cov.copy()
        xysurf = mn.pdf(np.dstack((self.xx,self.yy)), mean_, cov_)

        self.surf.remove()
        self.surf = self.ax2.plot_surface(self.xx, self.yy, xysurf, cmap=self.cm,
                        rstride=1, cstride=1, vmin=self.vmin, vmax=self.vmax)
        self.mesh.set_array(xysurf)
        self.tex.set_text(self.gen_text(mean_, cov_), )
        return [self.surf,]

# %%
if __name__ == '__main__':
    # %%
    fig = plt.figure(figsize=(5,10),dpi=400,)
    spec = gridspec.GridSpec(3, 1,
        left=0.10, right=0.90, top=1.00, bottom=0.05,
        hspace=0.0,
        height_ratios=[1,5,4],
        figure=fig)
    ax1 = fig.add_subplot(spec[0])
    ax2 = fig.add_subplot(spec[1], projection='3d')
    ax3 = fig.add_subplot(spec[2], )
    # create a figure updater
    nframes=100
    ud = UpdateFigure(ax1, ax2, ax3)
    ud.cov = np.array([[1,-0.8],[-0.8,1]])
    ud.set_target('stretch', np.diag([1,4]),nframes)
    # user FuncAnimation to generate frames of animation
    anim = FuncAnimation(fig, ud, frames=nframes, blit=True)
    # save animation as *.mp4
    anim.save('2d_gaussian_2.mp4', fps=20, dpi=400, codec='libx264', bitrate=-1, extra_args=['-pix_fmt', 'yuv420p'])
# %%


import matplotlib.pyplot as plt
import numpy as np

e = np.e
pi = np.pi

def VolumeNSphere(N, R = 1):
    '''Computes the volume of a N Sphere (using Stearling aproximation) in a N dimensionl space as a function od N
    (dimension) and R (radius).'''
    term1 = 1 / (np.sqrt(N * pi))
    term2 = np.power( (2 * pi * e)/N, N/2 )
    expresion = term1 * term2 * np.power(R, N)
    return expresion

x_axis = [x for x in range(1, 50)]
Radious = [(0.9, 'orange'),
           (1, 'red'),
           (1.1, 'purple'),
           #(1.2, 'green'),
           #(1.5,'indigo'),
          # (1.75, 'black')
           ]

def DimensionRadious():
    plt.figure(figsize = (20, 14))
    for Rad in Radious:
        plt.plot(x_axis,
                 [VolumeNSphere(x, R=Rad[0]) for x in x_axis],
                 'ro',
                 linestyle='-',
                 label = 'Radius: ' + str(Rad[0]),
                 c = Rad[1]
                 )

    plt.title('Evolution of the volume of the Sphere as dimension grows for different radious')
    plt.ylabel('Volume')
    plt.xlabel('Dimension')
    plt.legend()
    plt.savefig('VolumeNSphere')
    plt.show()

def Radious1():
    plt.figure(figsize=(20, 14))
    plt.plot(x_axis,
             [VolumeNSphere(N = x, R = 1) for x in x_axis],
             'ro',
             linestyle='-',
             )
    plt.title('Volume of a N-Sphere of radius 1')
    plt.xlabel('Dimension')
    plt.ylabel('Volumen')
    plt.savefig('Volume_N_Sphere_R1')
    plt.show()

DimensionRadious()

Images and auxiliar graphics/SphereVolumeExploration.py


import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hamming, triang, blackmanharris
from scipy.fftpack import fft, ifft
import math
import sys, os, functools, time

sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../software/models/'))

import stft as STFT
import sineModel as SM
import utilFunctions as UF

M = 256
N = 256
hN = N//2
hM = M//2
fs = 44100
f0 = 5000.0
A0 = 1
ph = 1.5
t = np.arange(-hM,hM)/float(fs)
x = A0 * np.cos(2*np.pi*f0*t+ph)

w = hamming(M)
xw = x*w
fftbuffer = np.zeros(N)
fftbuffer[0:M] = xw
X = fft(fftbuffer)
mX = abs(X)
pX = np.angle(X[0:hN])

powerX = sum(2*mX[0:hN]**2)/N

mask = np.zeros(N//2)
mask[int(N*f0/fs-2*N/float(M)):int(N*f0/fs+3*N/float(M))] = 1.0
mY = mask*mX[0:hN]
powerY = sum(2*mY[0:hN]**2)/N

Y = np.zeros(N, dtype = complex)
Y[:hN] = mY * np.exp(1j*pX)
Y[hN+1:] = mY[:0:-1] * np.exp(-1j*pX[:0:-1])

y = ifft(Y)
SNR1 = -10*np.log10((powerX-powerY)/(powerX))

freqaxis = fs*np.arange(0,N/2)/float(N)
taxis = np.arange(N)/float(fs)

plt.figure(1, figsize=(9, 6))
plt.subplot(3,2,1)
plt.plot(20*np.log10(mY[:hN])-max(20*np.log10(mY[:hN])), 'r', lw=1.5)
plt.title ('mX, mY (main lobe); Hamming')
plt.plot(20*np.log10(mX[:hN])-max(20*np.log10(mX[:hN])), 'r', lw=1.5, alpha=.2)
plt.axis([0,hN,-120,0])

plt.subplot(3,2,3)
plt.plot(y[0:M], 'b', lw=1.5)
plt.axis([0,M,-1,1])
plt.title ('y (synthesis of main lobe)')

plt.subplot(3,2,5)
yerror = xw - y
plt.plot(yerror, 'k', lw=1.5)
plt.axis([0,M,-.003,.003])
plt.title ("error function: x-y; SNR = ${%d}$ dB" %(SNR1))

w = blackmanharris(M)
xw = x*w
fftbuffer = np.zeros(N)
fftbuffer[0:M] = xw
X = fft(fftbuffer)
mX = abs(X)
pX = np.angle(X[0:hN])

powerX = sum(2*mX[0:hN]**2)/N

mask = np.zeros(N//2)
mask[int(N*f0/fs-4*N/float(M)):int(N*f0/fs+5*N/float(M))] = 1.0
mY = mask*mX[0:hN]
powerY = sum(2*mY[0:hN]**2)/N

Y = np.zeros(N, dtype = complex)
Y[:hN] = mY * np.exp(1j*pX)
Y[hN+1:] = mY[:0:-1] * np.exp(-1j*pX[:0:-1])

y = ifft(Y)
SNR2 = -10*np.log10((powerX-powerY)/(powerX))

plt.subplot(3,2,2)
plt.plot(20*np.log10(mY[:hN])-max(20*np.log10(mY[:hN])), 'r', lw=1.5)
plt.title ('mX, mY (main lobe); Blackman Harris')
plt.plot(20*np.log10(mX[:hN])-max(20*np.log10(mX[:hN])), 'r', lw=1.5, alpha=.2)
plt.axis([0,hN,-120,0])

plt.subplot(3,2,4)
plt.plot(y[0:M], 'b', lw=1.5)
plt.axis([0,M,-1,1])
plt.title ('y (synthesis of main lobe)')

plt.subplot(3,2,6)
yerror2 = xw - y
plt.plot(yerror2, 'k', lw=1.5)
plt.axis([0,M,-.003,.003])
plt.title ("error function: x-y; SNR = ${%d}$ dB" %(SNR2))

plt.tight_layout()
plt.savefig('spec-sine-synthesis-lobe.png')
plt.show()


"""
Good to use when the number of features are less than 1000.
Extremely efficient than gradient decent when above condition is satisfied
"""

import numpy as np

np.random.seed(111)

'''
The data is generated adding noise to the values from  y = 1 + 0.5x1 + 3x2 -2x3 equation
The expectation of the auto encoder is to get the values  w0, w1, w2 and w3  closer to 1, 0.5, 3 and -2  respectively
'''

'''generate random x1, x2, x3 values'''
x1 = np.random.random((1, 50))[0]
x2 = np.random.random((1, 50))[0]
x3 = np.random.random((1, 50))[0]

'''define x0 as 1. To be multiplied with w0 values'''
x0 = np.ones((1, 50))[0]

'''create X_train which is has x1, x2, x3 as columns and 50 rows'''
X = np.c_[x0, x1, x2, x3]

'''get the reference y value'''
y_reference = 1 + 0.5*x1 + 3*x2 -2*x3

'''add noise to the reference y value'''
y = y_reference + np.sqrt(0.01) * np.random.random((1, 50))[0]

'''Get X transpose'''
X_t = X.transpose()

'''Normal Equation'''
W = np.linalg.pinv(X_t.dot(X)).dot(X_t).dot(y)

print('\nNormal Equation optimization completed')
print('W Expected : [1, 0.5, 3, -2]' + '  Learned : ' + str(W))


import numpy as np
from math import *

def f_of_x(func_str, endpoints):
    ''' User-entered Function

    Takes a mathematical expression as string and computes its result

    Args:
        func_str (string)     : the function to be analized [f(x)]
        endpoints (float list): coordinates of the partitions

    Returns:
        A new list of endpoints
    '''

    #result = [ (pow(1+(cos(i)), 1./3)) for i in endpoints]
    result = [ eval(func_str) for x in endpoints]

    return result

def mid_rule(interval, delta_x, f_of_x, func_str):
    ''' Midpoint Rule of Integration

    Approximates the area between a given curve f(x) and the line x=0 within a closed interval [a,b] using the Midpiont Rule

    Args:
        interval (int list): [a,b]
        my_func (function) : the function to integrate
        delta_x (float)    : length of partitions

    Returns:
        Approximate are under f(x) within the interval [a,b]

    '''
    endpoints = [ (.5* ( (i-delta_x) + i)) for i in np.arange(interval[0]+delta_x, interval[1]+delta_x, delta_x)]

    endpoints = f_of_x(func_str, endpoints)
    result = np.sum(endpoints)
    result *= delta_x

    return result

def trap_rule(interval, delta_x, endpoints):
    ''' Trapezoidal Rule of Integration

    Approximates the area between a given curve f(x) and the line x=0 within a closed interval [a,b] using the Trapezoidal Rule

    Args:
        interval  (int list): [a,b]
        my_func   (function): the function to integrate
        delta_x   (float): length of partitions
        endpoints (float list): enpoints of the partitions

    Returns:
        Approximate are under f(x) within the interval [a,b]

    '''

    # the first and last term stay unchanged. This is why the for loop only goes through indices 1 to (and including) len(endpoints)-2
    for i in range(1,len(endpoints)-1):
        endpoints[i] = endpoints[i]*2

    #endpoints = [endpoints[i]*2 for i in range(1,len(endpoints)-1)]

    result = np.sum(endpoints)
    result *= (delta_x/2)

    return result

def simpsons_rule(interval, delta_x, endpoints):
    ''' Simpson's Rule of Integration

    Approximates the area between a given curve f(x) and the line x=0 within a closed interval [a,b] using Simpson's Rule

    Args:
        interval  (int list): [a,b]
        my_func   (function): the function to integrate
        delta_x   (float): length of partitions
        endpoints (float list): enpoints of the partitions

    Returns:
        Approximate are under f(x) within the interval [a,b]

    '''

    # the first and last terms stay unchanged. This is why the for loop only goes through indices 1 to endpoints-2
    for i in range(1,len(endpoints)-1):

        # if my index is odd, multiply by four, otherwise multiply by two.
        if (i%2)==1:
            endpoints[i] = endpoints[i]*4
        else:
            endpoints[i] = endpoints[i]*2

    result = sum(endpoints)
    result *= (delta_x/3)

    return result

def compute(n, interval, func_str):
    ''' Main function

    Controls the flow of information on the file; helps minimize redundant functionalities.

    Args:
        n (int)            : number of partitions
        interval (int list): [a,b]
        func_str (str)     : string version of the formula entered by the user.

    Returns:
        The area under the cruve and within the interval using all three methods of approximate integration
    '''

    delta_x = (interval[1] - interval[0])/n

    # Bescause two different rules use the same set on endpoints, delegate this computation, so it only happens once.
    endpts = [ i for i in np.arange(interval[0], interval[1] + delta_x, delta_x)]

    #enpoints_max = fofx2(my_func, endpts)
    endpts = f_of_x(func_str, endpts)
    endpts_copy = endpts.copy()

    by_midpoint = round(mid_rule(interval, delta_x, f_of_x, func_str), 6)
    by_trapezoidal = round(trap_rule(interval, delta_x, endpts), 6)
    by_simpsons = round(simpsons_rule(interval, delta_x, endpts_copy), 6)

    return by_midpoint, by_trapezoidal, by_simpsons


import numpy as np
import timeit

MATRIX = np.array([[1, 1],
                   [1, 0]])

def log_fibonacci(num):
    result = np.copy(MATRIX)

    # We create a stack, floor dividing by 2 each time.
    # The length of the stack determines how many matrix multiplications to do
    stack = [num]
    while stack[len(stack) - 1] != 1:
        last_ele = stack[len(stack) - 1]
        stack.append(last_ele // 2 )

    # The last element, which will be one, does not provide any useful info.
    stack.pop()

    while len(stack) != 0:
        result = np.matmul(result, result)

        # If smallest integer is odd, then we multiply by MATRIX once more
        # to match the necessary power on result
        if stack.pop() % 2 == 1:
        # Do one final multiplication in this cycle if we have an odd number
            result = np.matmul(result, MATRIX)

    return result[0][1]

def linear_fibonacci(num):
    # The matrix above to the nth power will have the nth fibonacci
    # number in its second and third entries.
    result = np.copy(MATRIX)

    for _ in range(num - 1):
        result = np.matmul(result, MATRIX)

    # Could've used: 'return result[1][0]'
    return result[0][1]

def main():
    print('Input a number to find that numbered entry in the Fibonacci sequence!')
    num = int(input())
    fib = linear_fibonacci(num)
    print(f'Fibonacci number {num} is {fib}')
    log_fig = log_fibonacci(num)
    print(f'Log(n) time fibonacci number {num} is {log_fig}')

if __name__ == '__main__':
    main()


import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate

from functions import *

##### Plot basic spline ######
x = np.array([0, 0.1, 0.2, 0.5, 0.7, 1.0])

spline = cubic_spline( x, x**3, 0, 3 )

x = np.linspace(0,1,50)
y = x**3 + 0.1

yhat = spline(x)

plt.plot(x, y, label='x^3 + 0.1')
plt.plot(x, yhat, label='Spline')

plt.legend()
plt.gca().set_title('Spline approximation of cubic')
plt.savefig('spline_vs_cubic.png')
plt.close()

##### Plot periodic spline ######
f = lambda x: np.sin(2*np.pi * (x + 0.2))
interval = [0,1]

d = 0.01

# Evaluation mesh
x = np.linspace(0, 1, 10)

# Spline construction
spline = periodic_cubic_spline( x, f(x) )
# Ensure periodic
ps = lambda x: spline( x % (interval[1] - interval[0]) )
# Create this sd_ps thingy
sd_ps = lambda x: d**-2 * (ps(x-d) - 2*ps(x) + ps(x+d))

x = np.linspace( 0, 1, 100)

fig, axs = plt.subplots(2, figsize=(7,5))

axs[0].plot( x, ps(x), label='ps')
axs[1].plot( x, sd_ps(x), label='sd_ps')

for ax in axs:
    ax.legend()
axs[0].set_title('Periodic spline PS vs SD_PS')
plt.savefig('sd_vs_ps.png')
plt.close()

####### Plot Error metrics on spline ########

# Our functions
fs = [ np.sin, lambda x: x**0.5,]
f_names = ['sin(x)', 'sqrt(x)']

# Function domains
f_intervals = [[0,np.pi/2.0], [1,4]]

# Function endpoint derivatives
f_derivs = [[1, 0], [0.5, 1]]

# Subintervals for sampling
n_intervals = [4, 8, 16, 32]

fig, axs = plt.subplots(2, figsize=(7,5), sharex=True)

for i, f, derivs, interval in zip(range(len(fs)), fs, f_derivs, f_intervals):

    abs_error = []

    for n in n_intervals:

        # Create mesh
        x_spline = np.linspace(interval[0], interval[1], n)

        # Spline construction
        spline = cubic_spline(
            x_spline,
            f(x_spline),
            derivs[0],
            derivs[1]
        )

        # Spline error sample
        # We evaluate on 10x as many points as the construction mesh
        x_sample = np.linspace(interval[0], interval[1], 10*n)

        abs_error.append(
            # Take the highest value
            np.max(
                # of the absolute error
                np.abs(spline(x_sample) - f(x_sample))
            )
        )

    # Plot that error
    axs[i].plot(
        np.log10(n_intervals),
        np.log10(abs_error),
        label=f_names[i],
    )
    # Make sure name is present
    axs[i].legend()

axs[0].set_title('Log(number of intervals) vs Log(maximum error)')
fig.tight_layout()
plt.savefig('spline_error.png')


import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import ode

# using scipy.integrate.ode
def ode_sol(n0, decay_const, t_final, n_t_steps):
    intermediate_points = n_t_steps
    t3 = np.linspace(0,t_final, intermediate_points)
    n3 = np.zeros(t3.shape, float)
    def f(t, y, decay_const):
        return  - decay_const * y
    solver = ode(f).set_integrator('dopri5') # runge-kutta of order (4)5
    y0 = n0
    t0 = 0
    solver.set_initial_value(y0, t0)
    solver.set_f_params(decay_const)
    k=1
    n3[0] = n0
    while solver.successful() and solver.t < t_final:
        n3[k] = solver.integrate(t3[k])[0]
        k += 1  # k = k + 1
    n3r = n3 / n0
    return n3, n3r, t3

# Analytical solution
na, nar, ta = analytical_solution(n0=10000, decay_const=1.54e-1, t_final=20, n_t_steps=10)
# Euler method
ne, ner, te = euler_method(n0=10000, decay_const=1.54e-1, t_final=20, n_t_steps=10)
nuler_rel_error = 100*(ne-na)/na
# runge-kutta of order (4)5
n_ode, n_oder, tode = ode_sol(n0=10000, decay_const=1.54e-1, t_final=20, n_t_steps=10)
ode_rel_error = 100*(n_ode - na) / na

# Make the plot
fig = plt.figure(figsize=(8,5))
ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(ta, nar, linestyle="-", linewidth=2, label='Analytical Solution', c='#ff464a')
ax1.plot(te, ner, linestyle="--", linewidth=2, label='Euler method', c='#4881e9')
ax1.plot(tode, n_oder, linestyle="--", linewidth=2, label='Runge-Kutta of order (4)5', c='#342a77')
ax1.set_ylabel('Relative Number of $^{238}$U atoms')
ax1.set_xlabel('Time in bilion years')
ax1.legend()

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(te, euler_rel_error, linestyle="-", linewidth=2, c='#4881e9', label='Euler method')
ax2.plot(tode, ode_rel_error, linestyle="-", linewidth=2, c='#342a77', label='Runge-Kutta of order (4)5')
ax2.set_ylabel('Relative Error, in %')
ax2.set_xlabel('Time in bilion years')
ax2.legend()

fig.tight_layout()


#!/usr/bin/env python
import sys

##########################################################################
## Can you prove that De Morgan's laws work in Python?
##########################################################################

a = set(["A","B","C","D"])
b = set(["C","D","E","F"])
sample_space = set(["A","B","C","D","E","F","G"])

## The complement of the union of two sets is the same as the intersection of their complements
part1 = sample_space.difference(a.union(b))
part2 = sample_space.difference(b).intersection(sample_space.difference(a))
print(part1==part2)

## The complement of the intersection of two sets is the same as the union of their complements
part1 = sample_space.difference(b).union(sample_space.difference(a))
part2 = sample_space.difference(a.intersection(b))
print(part1==part2)

## A = (X < 90) and B  is between 90 and 95

# The union or  (A \cap C) = (250 \leq chol \leq 280)

# `P(250 \leq chol \leq 299) + P(chol \geq 300) = 0.2 + 0.1 = 0.3`. chol \leq 280)

##########################################################################
## combinations and permutations
##########################################################################
from math import factorial
from itertools import combinations,permutations
from scipy.misc import comb

## We have sampler plates that hold 4 beers.  How many different ways can we combine these beers?
lefthand_beers = ["Milk Stout", "Good Juju", "Fade to Black", "Polestar Pilsner"]
lefthand_beers += ["Black Jack Porter", "Wake Up Dead Imperial Stout","Warrior IPA"]
n = len(lefthand_beers)
k = 4

def comb(n, k):
    return factorial(n) / (factorial(k) * factorial(n - k))

print("There are %s combinations"%comb(n,k))

## Print a list of these pairs so we can identify the bad ones?
for c in combinations(lefthand_beers,4):
    print(c)

## on a team of 12 baseball players how many batting orders?
def permu(n,k):
    return factorial(n) / factorial(n - k)

print(permu(12,9))

##########################################################################
## probability
##########################################################################

## probability of a queen
p_queen = 1.0/52 + 1.0/52 + 1.0/52 + 1.0/52
p_queen_or_spade = 4.0/52 + 13/52 - 1.0/52

## conditional probability problem
#print("prob of tails: %s"%1 - ((1./3 + (1./3 * 1./4)) / (1. / 2)))
print("prob of heads: %s"%((1./3 + (1./3 * 1./4)) / (1. / 2)))

import random
import pandas as pd

coins = ['HH', 'HT', 'TT']
results = []
for i in range(10):
    coin = random.choice(coins)
    results.append([random.choice(coin) for j in [1,2]])

print(results)

df = pd.DataFrame(results, columns=['first', 'second']) == 'H'
#print df
df.groupby('first').mean()

sys.exit()


#########################
##                     ##
## Irving Gomez Mendez ##
##     May 09, 2021    ##
##                     ##
#########################
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

import sympy as sym
from sympy.functions import exp
from scipy.optimize import least_squares
from scipy.stats import norm, t

alpha = 0.05

dat = pd.read_csv('ChickWeight.csv')
dat = dat.drop(dat.columns[0], axis=1)
dat['Diet'] = dat['Diet'].astype('category')

plt.figure(figsize=(10,7.5))
sns.scatterplot(x=dat['Time'], y=dat['weight'], hue=dat['Diet'], s=50)

plt.figure(figsize=(10,7.5))
sns.lineplot(
    x=dat['Time'], y=dat['weight'],
    hue=dat['Diet'], style=dat['Diet']
)
## Let's select just the diet=3
dat_example = dat.loc[dat['Chick']==3, ['weight','Time']].reset_index(drop=True)

plt.figure(figsize=(10,7.5))
sns.lineplot(x=dat_example['Time'], y=dat_example['weight'])
sns.scatterplot(x=dat_example['Time'], y=dat_example['weight'])
# We are going to model using a logistic growth
M, r, b, t = sym.symbols('M r b t')
f = M/(1+exp(-r*(t-b)))

sym.diff(f, M)
sym.diff(f, r)
sym.diff(f, b)

def f(t, M, r, b):
    return(M/(1+np.exp(-r*(t-b))))

def F_1(t,M,r,b):
    return(1/(1+np.exp(-r*(t-b))))

def F_2(t,M,r,b):
    return((M*(t-b)*np.exp(-r*(t-b)))/(1+np.exp(-r*(t-b)))**2)

def F_3(t,M,r,b):
    return((-M*r*np.exp(-r*(t-b)))/(1+np.exp(-r*(t-b)))**2)

y = dat_example['weight']
X = dat_example['Time']
n = len(y)
p = 3

# Initialize
M0 = y.max()
r0 = 0.1
b0 = 15

tt = [M0,r0,b0]

tolm   = 1e-6       # tolerance (minimum norm of the difference of the betas)
iterm  = 100        # maximum number of iterations
tolera = 1          # initialize tolera
itera  = 0          # initialize ittera
histo  = tt          # initialize beta upgrade

while((tolera > tolm) and (itera < iterm)):
    F_matrix = np.vstack([
        F_1(dat_example['Time'], tt[0], tt[1], tt[2]),
        F_2(dat_example['Time'], tt[0], tt[1], tt[2]),
        F_3(dat_example['Time'], tt[0], tt[1], tt[2])
    ])
    F_matrix = F_matrix.T
    y_hat = f(dat_example['Time'], tt[0], tt[1], tt[2])
    delta = np.linalg.solve(F_matrix.T @ F_matrix, F_matrix.T @ (y-y_hat))
    tt = tt + delta
    tolera = np.sqrt(sum(delta**2))
    histo  = np.vstack([histo, tt])
    itera  = itera+1

histo

## Using scipy least squares
def logistic_gowth(theta, t):
    return theta[0] / (1 + np.exp(- theta[1] * (t - theta[2])))

def fun(theta):
    return logistic_gowth(theta, X) - y

theta0 = [M0,r0,b0]
log_growth = least_squares(fun, theta0)
log_growth.x

F_matrix

log_growth.jac

# Getting confidence intervals
hat_sigma2 = sum((y-y_hat)**2/(n-p))
var_params = np.diag(hat_sigma2 * np.linalg.inv(F_matrix.T @ F_matrix))
var_params

# Let's get the significance of the estimators
se_params = np.sqrt(var_params)
z_score = tt/se_params
p_value = 1-norm.cdf(np.abs(z_score))

z_score
p_value

# predictions
y_hat = logistic_gowth(theta=log_growth.x, t=X)

plt.figure(figsize=(10,7.5))
plt.plot(X, y, 'o-', label='data')
plt.plot(X, y_hat, 'o-', label='estimated')
plt.xlabel('time')
plt.ylabel('weight')
plt.title("Weight of a chicken over time")
plt.legend(loc='upper left')

low_pred = y_hat - np.sqrt(hat_sigma2) * t.ppf(1-alpha/2, n-p)
upp_pred = y_hat + np.sqrt(hat_sigma2) * t.ppf(1-alpha/2, n-p)

plt.figure(figsize=(10,7.5))
plt.fill_between(X, low_pred, upp_pred, facecolor='green', alpha=0.5, label='Prediction interval')
plt.plot(X, y, 'o-', label='data')
plt.plot(X, y_hat, 'o-', label='estimated')
plt.xlabel('time')
plt.ylabel('weight')
plt.title("Weight of a chicken over time")
plt.legend(loc='upper left')

###

import numpy,from scipy,import sympy,from sympy


# -*- coding: utf-8 -*-
"""
Created on Wed Jun  7 14:17:38 2017

@author: picku
"""

import numpy as np
from scipy import linalg
from collections import OrderedDict
import matplotlib.pyplot as plt
from matplotlib import style
style.use('ggplot')

class PolynomialRegression(object):
        """PolynomialRegression

        Parameters
        ------------
        x_pts : 1-d numpy array, shape = [n_samples,]
        y_pts : 1-d numpy array, shape = [n_samples,]

        Attributes
        ------------
        theta : 1-d numpy array, shape = [polynomial order + 1,]
            Ceofficients of fitted polynomial, with theta[0] corresponding
            to the intercept term

        method : str , values = 'normal_equation' | 'gradient_descent'
            Method used for finding optimal values of theta

        If gradient descent method is chosen:

            costs : 1-d numpy array,
                Cost function values for every iteration of gradient descent

            numIters: int
                Number of iterations of gradient descent to be performed

        References
        ------------
        https://en.wikipedia.org/wiki/Polynomial_regression
        """

    def __init__(self, x, y):

        self.x = x
        self.y = y

    def standardize(self,data):
        """ Peform feature scaling
        Parameters:
        ------------
        data : numpy-array, shape = [n_samples,]

        Returns:
        ---------
        Standardized data
        """

        return (data - np.mean(data))/(np.max(data) - np.min(data))

    def hypothesis(self, theta, x):
        """ Compute hypothesis, h, where
        h(x) = theta_0*(x_1**0) + theta_1*(x_1**1) + ...+ theta_n*(x_1 ** n)

        Parameters:
        ------------
        theta : numpy-array, shape = [polynomial order + 1,]
        x : numpy-array, shape = [n_samples,]

        Returns:
        ---------
        h(x) given theta values and the training data

        """
        h = theta[0]
        for i in np.arange(1, len(theta)):
            h += theta[i]*x ** i
        return h

    def computeCost(self, x, y, theta):
        """ Compute value of cost function J

        Parameters:
        ------------
        x : numpy array, shape = [n_samples,]
        y : numpy array, shape = [n_samples,]

        Returns:
        ---------
        Value of cost function J at value theta given the training data

        """
        m = len(y)
        h = self.hypothesis(theta, x)
        errors = h-y

        return (1/(2*m))*np.sum(errors**2)

    def fit(self, method = 'normal_equation', order = 1, tol = 10**-3, numIters = 20, learningRate = 0.01):

        """Fit theta to the training data

        Parameters
        -----------
        method: string, values = 'normal_equation' | 'gradient_descent'
             Indicates method for which polynomial regression will be performed

        order: int, optional
             Order of polynomial fit. Defaults to 1 (linear fit)

        numIters: int, optional
             Number of iterations of gradient descent to be performed

        learningRate: float, optional

        tol : float, optional
            Value indicating the cost value (J(theta)) at which
            gradient descent should terminated. Defaults to 10 ** -3

        Returns:
        -----------
        self : object

        """

        if method == 'normal_equation':
            d = {}
            d['x' + str(0)] = np.ones([1,len(x_pts)])[0]
            for i in np.arange(1, order+1):
                d['x' + str(i)] = self.x ** (i)

            d = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
            X = np.column_stack(d.values())

            theta = np.matmul(np.matmul(linalg.pinv(np.matmul(np.transpose(X),X)), np.transpose(X)), self.y)

        elif method == 'gradient_descent':

            d = {}
            d['x' + str(0)] = np.ones([1,len(x_pts)])[0]
            for i in np.arange(1, order+1):
                d['x' + str(i)] = self.standardize(self.x ** (i))

            d = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
            X = np.column_stack(d.values())

            m = len(self.x)
            theta = np.zeros(order + 1)
            costs = []
            for i in range(numIters):

                h = self.hypothesis(theta, self.x)
                errors = h-self.y
                theta += -learningRate * (1/m)*np.dot(errors, X)
                cost = self.computeCost(self.x, self.y, theta)
                costs.append(cost)
                #tolerance check
                if cost < tol:
                    break

            self.costs = costs
            self.numIters = numIters

        self.method = method
        self.theta = theta

        return self

    def plot_predictedPolyLine(self):
        """Plot predicted polynomial line using values of theta found
        using normal equation or gradient descent method

        Returns
        -----------
        matploblib figure
        """
        plt.figure()
        plt.scatter(self.x, self.y, s = 30, c = 'b')
        line = self.theta[0] #y-intercept
        label_holder = []
        label_holder.append('%.*f' % (2, self.theta[0]))
        for i in np.arange(1, len(self.theta)):
            line += self.theta[i] * self.x ** i
            label_holder.append(' + ' +'%.*f' % (2, self.theta[i]) + r'$x^' + str(i) + '$')

        plt.plot(self.x, line, label = ''.join(label_holder))
        plt.title('Polynomial Fit: Order ' + str(len(self.theta)-1))
        plt.xlabel('x')
        plt.ylabel('y')
        plt.legend(loc = 'best')

    def plotCost(self):
        """Plot number of gradient descent iterations verus cost function, J,
        values at values of theta

        Returns
        -----------
        matploblib figure
        """
        if self.method == 'gradient_descent':
            plt.figure()
            plt.plot(np.arange(1, self.numIters+1), self.costs, label = r'$J(\theta)$')
            plt.xlabel('Iterations')
            plt.ylabel(r'$J(\theta)$')
            plt.title('Cost vs Iterations of Gradient Descent')
            plt.legend(loc = 'best')
        else:
            print('plotCost method can only be called when using gradient descent method')


import math

# Taylor expansion at n=1 2x
# Taylor expansion at n=3 2x - 4(x**3)/3
# Taylor expansion at n=5 2x - 4(x**3)/3 + 4(x**5)/15

import sympy as sy
import numpy as np
from sympy.functions import sin, cos
import matplotlib.pyplot as plt
from sympy.parsing.sympy_parser import *

plt.style.use("ggplot")

# Define the variable and the function to approximate
x = sy.Symbol('x')
f = sin(2 * x)

# Factorial function
def factorial(n):
    if n <= 0:
        return 1
    else:
        return n * factorial(n - 1)

# Taylor approximation at x0 of the function 'function'
def taylor(function, x0, n):
    i = 0
    p = 0
    while i <= n:
        p = p + (function.diff(x, i).subs(x, x0)) / (factorial(i)) * (x - x0) ** i
        i += 1
    return p

# Plot results
def plot():
    x_lims = [0, math.pi]
    x1 = np.linspace(x_lims[0], x_lims[1], 800)
    y1 = []
    # Approximate up until 5 starting from 1 and using steps of 2
    lastFunc = 0
    for j in range(1, 6, 2):
        func = taylor(f, 0, j)
        lastFunc = func
        print('Taylor expansion at n=' + str(j), func)
        for k in x1:
            y1.append(func.subs(x, k))
        plt.plot(x1, y1, label='order ' + str(j))
        y1 = []
    # Plot the function to approximate (sine, in this case)
    expr = parse_expr(str(lastFunc))
    realValue = np.sin(math.pi / 2)  # sin(2x) => x=pi/2
    taylorValue = expr.subs(x, math.pi / 4)

    print('\n\nTaylor expansion sin of 2x=', lastFunc)
    print('Taylor expansion result sin of 2x ~=', taylorValue)
    print('Real value sin of 2x ~=', realValue)

    absoluteError = abs(realValue - taylorValue)
    realtiveError = absoluteError / realValue
    percentageError = realtiveError * 100

    print('\n\nAbsolute Error: ', absoluteError)
    print('Realtive Error: ', realtiveError)
    print('Percentage Error: "%"', percentageError)

    plt.plot(x1, np.sin(x1), label='sin of 2x')
    plt.xlim(x_lims)
    plt.ylim([-math.pi, math.pi])
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()
    plt.grid(True)
    plt.title('Taylor series approximation')
    plt.show()

plot()


import scipy as sp
import numpy as np
import scipy.linalg as la
import numpy.linalg as la
import matplotlib.pyplot as plt
'''
A = sp.array([[2,1],[4,2.01]])
la.norm(A)*la.norm(la.pinv(A))
np.linalg.cond(A)
'''
#problem 2

def hilbertCond(n):
	'''
	I'm just going to construct teh Hilbert Matrx and calculate the
	condition from there. If there's a better way, I don't know it.
	'''
	def hilbert(n):
		return sp.array([[1./(x+y-1) for x in range(1,n+1)] for y in range(1,n+1)])
	return np.linalg.cond (hilbert(n))
'''
I don't understand the growth curve. When I plot n in ranges like 1 to 21 and 1 to 100 I get strange peaks, but the condition is still obviously growing rapidly. When I plot from 1 to 10 though, I get no peaks. Is this what I'm supposed to get?
'''
if __name__ == "__main__":
	N = sp.arange(1,21)
	plt.plot(N,sp.vectorize(hilbertCond)(N))
	plt.show()


'''
Computing the covariance
100xp
The covariance may be computed using the Numpy function np.cov(). For example,
we have two sets of data x and y, np.cov(x, y) returns a 2D array where entries
[0,1] and [1,0] are the covariances. Entry [0,0] is the variance of the data in x,
and entry [1,1] is the variance of the data in y. This 2D output array is called
the covariance matrix, since it organizes the self- and covariance.

To remind you how the I. versicolor petal length and width are related, we include
the scatter plot you generated in a previous exercise.

Instructions
-Use np.cov() to compute the covariance matrix for the petal length
(versicolor_petal_length) and width (versicolor_petal_width) of I. versicolor.
-Print the covariance matrix.
-Extract the covariance from entry [0,1] of the covariance matrix. Note that by symmetry,
entry [1,0] is the same as entry [0,1].
-Print the covariance.
'''
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

versicolor_petal_length = np.array([4.7,  4.5,  4.9,  4.,  4.6,  4.5,  4.7,  3.3,  4.6,  3.9,  3.5,
                                    4.2,  4.,  4.7,  3.6,  4.4,  4.5,  4.1,  4.5,  3.9,  4.8,  4.,
                                    4.9,  4.7,  4.3,  4.4,  4.8,  5.,  4.5,  3.5,  3.8,  3.7,  3.9,
                                    5.1,  4.5,  4.5,  4.7,  4.4,  4.1,  4.,  4.4,  4.6,  4.,  3.3,
                                    4.2,  4.2,  4.2,  4.3,  3.,  4.1])

versicolor_petal_width = np.array([1.4,  1.5,  1.5,  1.3,  1.5,  1.3,  1.6,  1.,  1.3,  1.4,  1.,
                                   1.5,  1.,  1.4,  1.3,  1.4,  1.5,  1.,  1.5,  1.1,  1.8,  1.3,
                                   1.5,  1.2,  1.3,  1.4,  1.4,  1.7,  1.5,  1.,  1.1,  1.,  1.2,
                                   1.6,  1.5,  1.6,  1.5,  1.3,  1.3,  1.3,  1.2,  1.4,  1.2,  1.,
                                   1.3,  1.2,  1.3,  1.3,  1.1,  1.3])

# Compute the covariance matrix: covariance_matrix
covariance_matrix = np.cov(versicolor_petal_length, versicolor_petal_width)

# Print covariance matrix
print(covariance_matrix)

# Extract covariance of length and width of petals: petal_cov
petal_cov = covariance_matrix[0, 1]

# Print the length/width covariance
print(petal_cov)


import numpy as np

def MAE(X, T):
    """Mean Absolute Error

    ## Output
        Loss of the whole mini-batch (one value will be returned)
    """
    return np.sum(np.absolute(X-T))/X.shape[0]

def MSE(X, T):
    """Mean Square Error

    ## Output
        Loss of the whole mini-batch (one value will be returned)
    """
    return np.sum((X-T)**2)/X.shape[0]

def RMSE(X, T):
    """Root Mean Square Error

    ## Output
        Loss of the whole mini-batch (one value will be returned)
    """
    return np.sqrt(np.sum((X-T)**2)/X.shape[0])

def CEL(X, T):
    """Cross Entropy Loss
    X: output of softmax function
    T: training data in the form of one-hot vector

    ## Output
        Loss of the whole mini-batch (one value will be returned)
    """
    return -np.sum(T*np.log(np.absolute(X+10e-7)))/X.shape[0]


from scipy import optimize
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from random import uniform
import evaluation_functions as ef

def rosenbrock(x, y):
    return (x-1)**2 + 100*(y-x**2)**2;

def beale(x, y):
    return (x*y + 1.5 - x)**2 + (x * (y**2) + 2.5 - x)**2 + (x * (y**3) + 2.625 - x)

def booth(x, y):
    return (x + 2 * y - 7)**2 + (2*x  + y - 5)**2

def matyas(x, y):
    return 0.26*(x**2 + y**2) - 0.48*(x*y)

def plot_rosenbrock():
    step = .15
    X = np.arange(-2, 2, step)
    Y = np.arange(-1, 3, step)
    X, Y = np.meshgrid(X, Y)

    Z = rosenbrock(X, Y)

    fig = plt.figure(figsize=(15, 10))
    ax = fig.gca(projection='3d')
    surface = ax.plot_surface(X, Y, Z, cmap=cm.gist_heat_r, linewidth=0)
    ax.set_zlim(0, 2000)
    fig.colorbar(surface, shrink=0.5, aspect=10)
    plt.show()

def bfgs_rosenbrock2d(minValue, maxValue):
    answerList = []
    answerValuesList = []
    for i in range(30):
        print('\nInteration:',i)
        x0 = [uniform(minValue, maxValue), uniform(minValue, maxValue)]
        print('Initial Point:', x0)
        answer = optimize.minimize(optimize.rosen, x0, method='BFGS', jac = optimize.rosen_der,
                                   options={'disp':True, 'return_all':True})
        print('Rosenbrock Minimized Value:',answer.x)
        answerList.append(answer)
        answerValuesList.append(answer.x)
    return answerList, answerValuesList

def bfgs_rosenbrock30d(minValue, maxValue):
    answerList = []
    answerValuesList = []
    for i in range(30):
        print('\nInteration:',i)
        x0 = []
        for j in range(30):
            x0.append(uniform(minValue, maxValue))
        print('Initial Point:', x0)
        answer = optimize.minimize(optimize.rosen, x0, method='BFGS', jac = optimize.rosen_der,
                                   options={'disp':True, 'return_all':True})
        print('Rosenbrock Minimized Value:',answer.x)
        answerList.append(answer)
        answerValuesList.append(answer.x)
    return answerList, answerValuesList

#plot_rosenbrock()
result2dList, result2dValueList = bfgs_rosenbrock2d(-1,1)
minimum2dInterationResult = ef.minimum_iterations(result2dList)
minimum2dInterationConvergeResult = ef.minimum_iterations_converge(result2dList)
ef.evaluate_method(result2dValueList, 'Rosenbrock 2D')

result30dList, result30dValueList = bfgs_rosenbrock30d(-1, 1)
minimum30dInterationResult = ef.minimum_iterations(result30dList)
minimum30dInterationConvergeResult = ef.minimum_iterations_converge(result30dList)
ef.evaluate_method(result30dValueList , 'Rosenbrock 30D')


import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(0, 20, 1001)
noise = np.random.randn(1001)

T = np.pi  # period

# Fourier series coefficients
a_0 = 1.

a_1 = 1.
a_2 = 2.
a_3 = 3.

b_1 = 4.
b_2 = 5.
b_3 = 6.

timeseries = a_0 \
    + a_1*np.cos(1*np.pi*t/T) \
    + a_2*np.cos(2*np.pi*t/T) \
    + a_3*np.cos(3*np.pi*t/T) \
    + b_1*np.sin(1*np.pi*t/T) \
    + b_2*np.sin(2*np.pi*t/T) \
    + b_3*np.sin(3*np.pi*t/T)

noisy_timeseries = timeseries + noise

plt.plot(t, timeseries)
plt.scatter(t, noisy_timeseries)
plt.show()

# data = np.vstack((t, timeseries)).T
# noisy_data = np.vstack((t, noisy_timeseries)).T

# np.savetxt("test_timeseries.csv", data, delimiter=",")
# np.savetxt("test_timeseries_noisy.csv", noisy_data, delimiter=",")


import numpy as np
import random

from numpy.core.fromnumeric import transpose

# 1.Matrix Creation
dim = int(input("Enter Dimension of matrix :"))
matrix = np.random.randint(10, size=(dim, dim))

# 2.Matrix inversion
inverse = np.linalg.inv(matrix)
print ("Inverse matrix : %s" %inverse)

# 3.dot Product won't exactly yield I
I = np.dot(matrix, inverse)
print(" Dot product of matrix and its inverse is I: %s" %np.dot(matrix, inverse))

# 4.Verification of the identity matrix
EPS = 1e-8
r = np.all(np.abs(I - np.eye(dim)) < EPS)
print(r)

# 5.Trace of matrix
print(" Trace of matrix : %s" %matrix.trace())

# Random matrices with fixed dimension
A = np.random.randint(10, size=(2, 3))
B = np.random.randint(10, size=(3, 2))

# 6.cross product
print("cross product of %s and %s is %s" %(A, B, np.outer(A, B)))

# 7.transpose of matrix
print("Transpose of matris is %s" %np.transpose(matrix))

# 8.norm of matrix
print("Norm of matrix is : %s" %np.linalg.norm(matrix))

# 9. Range of matrix
print('Range of the matrix is : %s' %np.ptp(matrix))

# 9. Rank of matrix
print('Rank of the matrix is : %s' %np.linalg.matrix_rank(matrix))


#
# Points and weights for Gaussian quadrature
#
# by Alberto Costa Nogueira Jr. (Matlab and Python versions)
#    Renato Cantao (Python version)
#
import numpy
from math import gamma
from jacobi_p import jacobi_p
from djacobi_p import djacobi_p
from jacobi_roots import jacobi_roots
from simulation_data import QuadratureNodes

class JacobiGaussQuad:
    """Points and weights for Gaussian quadrature based on Jacobi polynomials"""
    def __init__(self, sim_data):
        # Sets parameters to obtain quadrature points from Legendre polynomials
        # Legendre == Jacobi(0,0)
        alpha = 0.0
        beta  = 0.0
        nip = sim_data.nip()

        # Case 1: Gauss-Legendre quadrature
        if sim_data.node_dist == QuadratureNodes.GL:
            self.xi, self.w = self._gl(nip, alpha, beta)
        # Case 2: Gauss-Legendre-Lobato quadrature
        elif sim_data.node_dist == QuadratureNodes.GLL:
            self.xi, self.w = self._gll(nip, alpha, beta)
        else:
            raise AssertionError("Unknown quadrature type!")

    # Case 1: Gauss-Legendre quadrature
    def _gl(self, nip, alpha, beta):
        xi = jacobi_roots(nip, alpha, beta)

        C1 = (2.0**(alpha+beta+1.0))*gamma(alpha+nip+1.0)*gamma(beta+nip+1.0)
        C2 = gamma(nip+1.0)*gamma(alpha+beta+nip+1.0)*(1.0-xi**2 )

        DPm = djacobi_p(xi, nip, alpha, beta)

        w = C1*DPm**(-2)/C2

        return xi, w

    # Case 2: Gauss-Legendre-Lobato quadrature
    def _gll(self, nip, alpha, beta):
        r = jacobi_roots(nip-2, alpha+1.0, beta+1.0)
        xi = numpy.empty(r.shape[0]+2)
        xi[0] = -1.0
        xi[1:-1] = r
        xi[-1] = 1.0

        C1 = (2.0**(alpha+beta+1.0))*gamma(alpha+nip)*gamma(beta+nip)
        C2 = (nip-1)*gamma(nip)*gamma(alpha+beta+nip+1.0)

        Pm = jacobi_p(xi, nip-1, alpha, beta)

        w = C1*Pm**(-2)/C2

        w[ 0] = w[ 0]*(beta+1.0)
        w[-1] = w[-1]*(alpha+1.0)

        return xi, w

    def n(self):
        """Number of integration points / weights."""
        return self.xi.shape[0]

#-- jacobi_gauss_quad.py -------------------------------------------------------


"""Functions for working with vectors and polar coordinates."""

import numpy as np

def cart_to_pol(x, y):
    """
    Convert vector from cartesian/xy notation to polar notation.

    Parameters
    ------
    x: int or float
        The x component of a vector
    y: int or float
        The y component of a vector

    Returns
    -------
    phi: float
        The direction of the vector
    rho: float
        The magnitude of the vector
    """
    rho = np.sqrt(x ** 2 + y ** 2)
    phi = np.arctan2(y, x)
    return (phi, rho)

def pol_to_cart(phi, rho=1):
    """
    Convert vector from polar notation to cartesian/xy notation.

    Parameters
    -------
    phi: float
        The direction of the vector
    rho: float
        The magnitude of the vector.
        If ommitted, the default value is 1 (unit vector).

    Returns
    ------
    x: int or float
        The x component of a vector
    y: int or float
        The y component of a vector
    """
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)
    return (x, y)

def points_to_angle(p, q):
    """
    Finds the polar angle (phi) between the vector from the origin
    along the positive x-axis and the line defined by two points p,q.

    Parameters
    -----
    p: tuple of ints/floats. (x,y)
    q: tuple of ints/floats. (x,y)

    Returns
    ------
    phi: float
        the angle of the line in radians, between -pi and pi.
    """
    phi = np.arctan2(q[0] - p[0], q[1] - p[1])
    return phi

def wrap_to_pi(x):
    """
    Wrap a value between -pi and pi in polar coordinate space.
        e.g. if x = 2*pi, x_wrapped = 0
        e.g. if x = 3*pi, x_wrapped = pi

    Parameters
    ------
    x: an int or float

    Returns
    -------
    x_wrapped: float between -pi and pi
    """
    x_wrapped = (x + np.pi) % (2 * np.pi) - np.pi
    return x_wrapped


"""
When you roll two regular six-sided dice, the total number of pips that can come up ranges from 2 (if both dice show 1)
to 12 (if both dice show 6), but as all experienced gamblers know, some numbers are more likely than others. In fact,
the most likely number to come up is 7, with a probability of 1/6. By contrast, the probability of 12 showing is only
1/36, so it is six times more likely that the dice will show 7 than it is that they will show 12.

The reason for this is of course that there are more ways that two dice can sum to 7. In fact, there are exactly six
ways two dice can sum to 7: the first die can show 1 and the second 6, the first 2 and the second 5, the first 3 and
the second 4, the first 4 and the second 3, the first 5 and the second 2, and finally the first die can show 6 and the
second 1. Given that there are a total of 6*6 = 36 different ways the dice can land, this gives us the probability:
6/36 = 1/6. In contrast, there is only one way two dice can form 12, by throwing two sixes.

Define a function f(d, n) that gives the number of ways d six-sided dice can be thrown to show the number n. So, in the
previous example, f(2,7) = 6. Here are a few other values of that function:
f(1,n) = 1 (for 1≤n≤6, 0 otherwise)
f(2,7) = 6
f(2,10) = 3
f(2,12) = 1
f(3,10) = 27
f(5,20) = 651
f(7,30) = 12117
f(10,50) = 85228
Find f(20, 100)

Note: the answer fits into a 64-bit integer

Bonus: Find f(1100, 5000) mod 107
"""

import itertools
from scipy import convolve

def f(d, n):
    """
    my itertools solution. too slow for final solution
    """
    dice = '123456'
    comb = itertools.product(dice, repeat=d)
    perm_list = []

    def sum_check(x, n):
        return sum(x) == n

    for c in comb:
        intd = list(map(int, c))
        if sum_check(intd, n):
            perm_list.append(intd)
    return len(perm_list)

def crawphish(d, n):
    """
    Recursive solution from /u/crawphish ported to python by me. too slow for final solution
    """
    out = 0
    for i in range(1, 6+1):
        if d:
            out += crawphish(d - 1, n - i)
        else:
            if n:
                return 0
            else:
                return 1
    return out

def ttl(d, n, m=10**7):
    """
    /u/ttl's solution. Mathematical solution, no idea of how it works but it returns the solution and bonus solution
    very quickly.
    """
    p = q = [1]*6+[0]
    i = 1
    while i < d:
        q, i = (convolve(q, q) % m, i*2) if 2*i < d else (convolve(q, p) % m, i+1)
    return q[-n-1]

def main():
    # print(f(20,100))
    # print(crawphish(20, 100))
    print(ttl(1100, 5000))

if __name__ == "__main__":
    main()


"""
Content under Creative Commons Attribution license CC-BY 4.0,
code under MIT license (c)2018 Sergio Rojas (srojas@usb.ve)

http://en.wikipedia.org/wiki/MIT_License
http://creativecommons.org/licenses/by/4.0/

Created on april, 2018
Last Modified on: may 15, 2018

  This program finds the solution of the equation
     7 = x + 9
"""
from sympy import symbols, Eq, solveset
x  = symbols('x')

LHS = 7
RHS = x + 9

thesol = list( solveset( Eq(LHS, RHS), x) )
print('thesol =', thesol)

newLHS = LHS - RHS #rearrange the equation to read: LHS - RHS = 0
print('newLHS =', newLHS)

newLHS = newLHS.subs(x, thesol[0])
if newLHS.simplify() == 0:
    print('The solution of {0} = {1}, is x = {2}'.format(LHS,RHS,thesol[0]))


import numpy as np
import time

#implementation of Gauss-Jordan Elimination for square matrices
test1 = [[ 0,  2,  1],
         [ 1, -2, -3],
         [-1,  1,  2]]

test2 = [[1, -2, 1],
        [0, 2, -8],
        [-4, 5, 9]]

test3 = [[1, 1, -2, 1, 3, -1],
         [2, -1, 1, 2, 1, -3],
         [1, 3, -3, -1, 2, 1],
         [5, 2, -1, -1, 2, 1],
         [-3, -1, 2, 3, 1, 3],
         [4, 3, 1, -6, -3, -2]]

another = [[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]]

test_vector1 = [-8, 0, 3]
test2_vector = [0, 8, -9]
test3_vector = [4, 20, -15, -3, 16, -27]

numpy_matrix = np.array(test3)
numpy_vector = np.array([test3_vector])
numpy_vector = np.transpose(numpy_vector)

start1 = time.time()
solution = np.linalg.solve(numpy_matrix, numpy_vector)
end1 = time.time()
numpy_time = end1 - start1

#switches the rows in the case when there is a vector with zero in the first place
def switch_the_rows(mat, vector):
    for i in range(len(list_)):
        if mat[i][0] != mat[0][0] and mat[i][0] != 0:
            temp1 = mat[i]
            temp2 = vector[i]
            mat[i] = mat[0]
            vector[i] = vector[0]
            mat[0] = temp1
            vector[0] = temp2
            break
    return mat, vector

#row reduces the matrix, approaches to lower triangular matrix
#one row operation each time
def lower_row_reduction(mat, pivot_index, vector):
    pivot = mat[pivot_index]
    for i in range(pivot_index, len(mat)):
        if i+1 == len(mat): break
        if mat[i+1][i] != 0:
            piv_multiple = [-x * mat[i+1][pivot_index]/pivot[pivot_index] for x in pivot]
            vect_multiple = -vector[pivot_index]*mat[i+1][pivot_index]/pivot[pivot_index]
            mat[i+1][:] = [x+y for x, y in zip(piv_multiple, mat[i+1])]
            vector[i+1] = vect_multiple+vector[i+1]
    return mat, vector

#creation of lower triangular matrix
def lower_triangular(mat, vector):
    for i in range(len(mat)):
        mat, vector = lower_row_reduction(mat, i, vector)
    return mat, vector

#row reduces the matrix, approaches to identity matrix
#one row operation each time
def upper_row_reduction(mat, pivot_index, vector):
    pivot = mat[pivot_index]
    vector[pivot_index] /= pivot[pivot_index]
    mat[pivot_index][:] = [x/pivot[pivot_index] for x in pivot]
    pivot = mat[pivot_index]
    for i in range(pivot_index, -1, -1):
        if i == 0: break
        if mat[i][i] != 0:
            temp = [-x*mat[i-1][pivot_index] for x in pivot]
            temp_vector = -vector[pivot_index]*mat[i-1][pivot_index]
            mat[i-1][:] = [y+x for x, y in zip(mat[i-1], temp)]
            vector[i-1] = temp_vector+vector[i-1]
    return mat, vector

#creation of upper triangular matrix
def upper_triangular(mat, vector):
    for i in range(len(mat)-1, -1, -1):
        mat, vector = upper_row_reduction(mat, i, vector)
    return mat, vector

#merge the functions
def gauss_jordan(mat, vector):
    if mat[0][0] == 0: mat, vector = switch_the_rows(mat, vector)
    mat, vector = lower_triangular(mat, vector)
    mat, vector = upper_triangular(mat, vector)
    return mat, vector

start = time.time()
M, sol = gauss_jordan(test3, test3_vector)
end = time.time()

my_time = end - start

sol = [round(x, 2) for x in sol]                #round each element in the vector up to 2 decimal places
print(sol)

print("Numpy implementation: " + str(numpy_time))
print("My implementation: " + str(my_time))
print("Difference: " + str(my_time - numpy_time))


# -*- coding: utf-8 -*-

# Raivo Laanemets
# rlaanemt@ut.ee

# A note: getA1() is used to transform matrix row into array.

import sys
import random
import numpy
import time
import math

# Solves linear system Kx = b.
# when is_unit is set to True then the
# procedure expects lower triangular matrix as L
# otherwise it expects lower unit triangular matrix as L.
# Might destructively update input matrix!

def solveL(L, b, is_unit=False):
    # If unit triangular matrix is assumed, set 1's in
    # the matrix main diagonal.
    if is_unit:
        L += numpy.diag(numpy.matrix(-L.diagonal() + 1).getA1())
    # Solve with forward subsitution.
    x = numpy.array(numpy.zeros(len(b), dtype=float))
    x[0] = b[0] / L[0, 0]
    for i in xrange(1, len(b)):
        x[i] = (b[i] - sum((L[i, 0:i]).getA1() * x[0:i])) / L[i, i]
    return x

# Solves linear system Kx = b.
# Expects upper triangular matrix as L.

def solveU(L, b):
    # Transforms the matrix L into suitable form to use with solveL.
    # Might be inefficient.
    # [::-1] reverses the array.
    return solveL(numpy.fliplr(numpy.flipud(L)), b[::-1])[::-1]

# Taken from pseudoalgorithm:
# http://www.math.vt.edu/people/wapperom/class_home/4445/alg_ludoolittle.pdf

# Compared to one on our lecture slides:
# 1. It gives name of the algorithm (Doolittle's) so
# I can ask someone about it.
# 2. Checks if factorization is possible at all
# instead of quitely producing bogus results.
# 3. Gives L and U matrixes I actually know how to use.

# Returns a three tuple (L, U) as the decomposition result.
# Modifies right side vector b as well.

def gauss_decomp(a, b, pivoting=False):
    n = len(a)
    l = numpy.matrix(numpy.zeros((n, n), dtype=float))
    u = numpy.matrix(numpy.zeros((n, n), dtype=float))
    for i in xrange(n): # iterates over rows

        max_a = abs(a[i, i])
        max_i = i

        # Row pivoting.
        # Find best pivot (manually).
        if pivoting:
            for j in xrange(i, n):
                if (abs(a[j, i]) > max_a):
                    max_a = abs(a[j, i])
                    max_i = j

        if max_i != i:

            # Swaps rows manually.
            # Tracking row permutations was too complex for me.
            # This might be inefficient but could be easily
            # implemented in C or Fortran and then use
            # that implementation through some Python
            # native interface.

            interchange(a, i, max_i);
            interchange(l, i, max_i);

            # Also does swapping for right-hand side vector.

            tmp = b[i]
            b[i] = b[max_i]
            b[max_i] = tmp

        if a[i, i] == 0:
            raise Exception("Factorization not possible");
        l[i, i] = 1.0
        for j in xrange(i, n):
            u[i, j] = a[i, j]
            for k in xrange(i):
                u[i, j] = u[i, j] - l[i, k] * u[k, j]
        for j in xrange(i + 1, n):
            l[j, i] = a[j, i]
            for k in xrange(i):
                l[j, i] = l[j, i] - l[j, k] * u[k, i]
            l[j, i] = l[j, i] / float(u[i, i])

    return (l, u)

def interchange(M, r1, r2):
    for j in xrange(len(M)):
        tmp = M[r1, j]
        M[r1, j] = M[r2, j]
        M[r2, j] = tmp

def solve_single(A, b, pivoting=True):
    (L, U) = gauss_decomp(A, b, pivoting)
    return solve_lu(L, U, b);

def solve_lu(L, U, b):
    y = solveL(L, b)
    x = numpy.matrix(solveU(U, y))
    # Consistent with numpy.linalg.solve output.
    x.shape = (len(b), 1)
    return numpy.matrix(x)

def main(argv):
    # Testing with the example from http://en.wikipedia.org/wiki/System_of_linear_equations
    A = numpy.matrix([[2, -2, 4], [3, 2, -1], [-1, 0.5, -1]], dtype=float)
    b = numpy.array([-2, 1, 0], dtype=float)
    print "A\n", A
    print "b\n", b
    print "solve\n", solve_single(A, b, True)

    # Calculating errors for large matrices.
    m = 40
    size = 25
    errors = []
    errors_pivoting = []
    for i in xrange(m):
        Aarr = (numpy.random.rand(size * size) - 0.5) * 1000000000
        Aarr.shape = (size, size)
        A = numpy.matrix(Aarr)
        b = numpy.random.random(size)
        A1 = A.copy()
        b1 = b.copy()
        x1 = solve_single(A1, b1, False)
        errors.append(numpy.sum(numpy.square(b1 - (A1 * x1).getA1())))
        A2 = A.copy()
        b2 = b.copy()
        x2 = solve_single(A2, b2, True)
        errors_pivoting.append(numpy.sum(numpy.square(b2 - (A2 * x2).getA1())))

    print "Average error without pivoting :", numpy.average(errors)
    print "Average error with pivoting :", numpy.average(errors_pivoting)

if __name__ == "__main__":
    main(sys.argv[1:])


#ZADANIE 1
#Napisz funkcje tsin(x), tcos(x), texp(x) i ttan(x) realizujace takie same operacje jak funkcje biblioteczne:
#numpy.sin(x), numpy.cos(x), numpy.exp(x) i numpy.tan(x).
#We wlasnych funkcjach wykorzystaj rozwiniecie w szereg Taylora (https://en.wikipedia.org/wiki/Taylor_series).
#W celu poprawnego rozwiazania zadania nalezy dodatkowo napisac funkcje realizujaca n! (silnia).

import numpy as np

def tbernoulli(n):
    if n<0 and n>20:
        return 0
    else:
        temp=[1, 1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0, 5/66, 0, -691/2730, 0, 7/6, 0, -3617/510, 0, 43867/798, 0, -174611/330]
        return temp[n]

def silnia(x):
    sil=1
    while x>0:
        sil=sil*x
        x =x-1
    return sil

def tsin(x):
    sum_sin=0
    for i in range(0,6):
        sum_sin=sum_sin+((-1)**i)*(x**(2*i+1))/float(silnia(2*i+1))
    return sum_sin

def tcos(x):
    sum_cos=0
    for i in range(0,6):
        sum_cos=sum_cos+((-1)**i)*(x**(2*i))/float(silnia(2*i))
    return sum_cos

def texp(x):
    sum_exp=0
    for i in range(0,6):
        sum_exp=sum_exp+(x**i)/float(silnia(i))
    return sum_exp

def ttan(x):
    sum_tan=0
    for i in range(0,6):
        sum_tan=sum_tan+tbernoulli(2*i)*((-4)**i)*(1-(4**i))*(x**(2*i-1))/float(silnia(2*i))
    return sum_tan

x=np.pi/3

print('sin(%.2f)=%.12f' % (x,tsin(x)))
print('cos(%.2f)=%.12f' % (x,tcos(x)))
print('exp(%.2f)=%.12f' % (x,texp(x)))
print('tan(%.2f)=%.12f' % (x,ttan(x)))


# -*- coding: utf-8 -*-
"""
Created on Tue Sep 21 00:58:09 2021

@author: Mahfuz_Shazol
"""

import numpy as np

X=np.array([
            [1,2,4],
            [2,-1,3],
            [0,5,1]
          ])

det_x_result=np.linalg.det(X)
print(det_x_result)

lambdas,v=np.linalg.eig(X)
product_of_eigon_result=np.product(lambdas)
print(product_of_eigon_result)


import numpy as np
import math

# Compute the intersection distance between histograms x and y
# Return 1 - hist_intersection, so smaller values correspond to more similar histograms
# Check that the distance range in [0,1]

def dist_intersect(x,y):

    #... (your code here)

# Compute the L2 distance between x and y histograms
# Check that the distance range in [0,sqrt(2)]

def dist_l2(x,y):

    #... (your code here)

# Compute chi2 distance between x and y
# Check that the distance range in [0,Inf]
# Add a minimum score to each cell of the histograms (e.g. 1) to avoid division by 0

def dist_chi2(x,y):

    #... (your code here)

def get_dist_by_name(x, y, dist_name):
  if dist_name == 'chi2':
    return dist_chi2(x,y)
  elif dist_name == 'intersect':
    return dist_intersect(x,y)
  elif dist_name == 'l2':
    return dist_l2(x,y)
  else:
    assert False, 'unknown distance: %s'%dist_name


## 2. The Mean ##

distribution = [0,2,3,3,3,4,13]

mean = 4
center =False
equal_distances = True

## 3. The Mean as a Balance Point ##

from numpy.random import randint, seed

equal_distances = 0

for i in range(5000):
    seed(i)
    distribution = randint(0,1000,10)
    mean = sum(distribution)/len(distribution)

    above_mean = []
    below_mean = []
    for value in distribution:
        if value == mean:
            continue
        if value < mean:
            below_mean.append(mean - value)
        if value > mean:
            above_mean.append(value -mean)

    sum_above = round(sum(above_mean),1)
    sum_below =round(sum(below_mean),1)
    if (sum_above==sum_below):
        equal_distances+=1

print(equal_distances)

## 4. Defining the Mean Algebraically ##

one = False

two = False

three =  False

## 5. An Alternative Definition ##

distribution_1 = [42, 24, 32, 11]
distribution_2 = [102, 32, 74, 15, 38, 45, 22]
distribution_3 = [3, 12, 7, 2, 15, 1, 21]

def mean(distribution):
    sum_distribution = 0
    for value in distribution:
        sum_distribution += value

    return sum_distribution / len(distribution)

mean_1 = mean(distribution_1)
mean_2 = mean(distribution_2)
mean_3 =  mean(distribution_3)

print(mean_1)
print(mean_2)
print(mean_3)

## 6. Introducing the Data ##

import pandas as pd

houses = pd.read_table('AmesHousing_1.txt', sep = '\t')
print(houses.head())

one =  True

two = False

three = True

## 7. Mean House Prices ##

def mean(distribution):
    sum_distribution = 0
    for value in distribution:
        sum_distribution += value

    return sum_distribution / len(distribution)

function_mean = mean(houses['SalePrice'])

pandas_mean =  houses['SalePrice'].mean()
means_are_equal = function_mean ==  pandas_mean

print(function_mean ,pandas_mean, means_are_equal)

## 8. Estimating the Population Mean ##

parameter = houses['SalePrice'].mean()

sample_size = 5

sample_sizes = []
sampling_errors = []

for i in range(101):
    sample = houses['SalePrice'].sample(sample_size, random_state = i)

    statistic = sample.mean()
    sampling_error = parameter - statistic
    sampling_errors.append(sampling_error)
    sample_sizes.append(sample_size)
    sample_size +=29

import matplotlib.pyplot as plt

plt.scatter(sample_sizes, sampling_errors)
plt.axhline(0)
plt.axvline(2930)
plt.xlabel('Sample size')
plt.ylabel('Sampling error')

## 9. Estimates from Low-Sized Samples ##

means = []

for i in range(10000):
    sample = houses['SalePrice'].sample(100, random_state = i)
    means.append(sample.mean())

plt.hist(means)
plt.axvline(houses['SalePrice'].mean())
plt.xlabel('Sample mean')
plt.ylabel('Frequency')
plt.xlim(0,500000)

## 11. The Sample Mean as an Unbiased Estimator ##

population = [3, 7, 2]
samples = [[3, 7], [3, 2],
           [7, 2], [7, 3],
           [2, 3], [2, 7]
          ]

sample_means = []
for sample in samples:
    sample_means.append(sum(sample) / len(sample))

population_mean = sum(population) / len(population)
mean_of_sample_means = sum(sample_means) / len(sample_means)

unbiased = (population_mean == mean_of_sample_means)
print(population_mean)
print(sample_means)
print(mean_of_sample_means)
print(unbiased)

5. Probability and Statistics/statistics-intermediate/The Mean-305.py


# -*- coding: utf-8 -*-
"""
-----------------------------------------------------------------------------
Ejercicio 2: Integración - Fórmulas de cuadratura compuestas.
-----------------------------------------------------------------------------
"""
import numpy as np
import sympy as sym

# Cálculo de la integral exacta de la función f(x) = exp(x) en [0,3]
#---------------------------------------------------------------------
x = sym.Symbol('x', real=True)            # definimos la variable x simbólica
f = sym.exp(x)            # definimos la función f simbólica
I_exacta = sym.integrate(f,(x,0,3))
I_exacta = float(I_exacta)
print ('El valor exacto es: ',I_exacta)

"""
Ejercicio 2a: Integración - Fórmula del punto medio compuesta.
-----------------------------------------------------------------------------
Función punto_medio_comp: Halla la integral aproximada utilizando la fórmula del
punto medio compuesta para una función f en un intervalo [a,b].

Argumentos de entrada:
    f:  función integrando (función lambda).
    a:  extremo inferior del intervalo de integración (número real).
    b:  extremo superior del intervalo de integración (número real).
    n:  número de subintervalos (número entero).

Argumentos de salida:
    I:  integral aproximada con la fórmula del punto medio compuesta de la función
        f en [a,b] (número real).

Ejemplos:
    f = lambda x : x**2
    I = punto_medio_comp(f,0,2,4)
    print('Ejemplo de prueba con punto medio compuesta =', I)
    Salida:
        Ejemplo de prueba con punto medio compuesta = 2.625
"""

# Función punto_medio_comp
#--------------------------------------
def punto_medio_comp(f,a,b,n):
    h = (b-a)/float(n)              # longitud de los subintervalos
    x = np.arange(a,b+h,h) # vector que contiene los puntos medios de los subintervalos
    I = 0.0
    for i in range(1,n+1):
        I += f((x[i-1]+x[i])/2.0)
    I = h*I       # fórmula del punto medio compuesta
    return I

#--------------------------------------
# Ejemplos
#--------------------------------------

# Ejemplo de prueba
f = lambda x : x**2
I = punto_medio_comp(f,0,2,4)
print('Ejemplo de prueba con punto medio compuesta =', I)

#--------------------------------------
# Ejercicio 2a
#--------------------------------------

# Cálculo de la integral aproximada
#---------------------------------------
f = lambda x :np.exp(x)
a = 0
b = 3
n = 5
Ipmc = punto_medio_comp(f,a,b,n)

print ('El valor aproximado con punto medio compuesta es:', Ipmc)

"""
Ejercicio 2b: Integración - Fórmula del trapecio compuesta.
-----------------------------------------------------------------------------
Función trapecio_comp: Halla la integral aproximada utilizando la fórmula del
trapecio compuesta para una función f en un intervalo [a,b].

Argumentos de entrada:
    f:  función integrando (función lambda).
    a:  extremo inferior del intervalo de integración (número real).
    b:  extremo superior del intervalo de integración (número real).
    n:  número de subintervalos (número entero).

Argumentos de salida:
    I:  integral aproximada con la fórmula del trapecio compuesta de la función
        f en [a,b] (número real).

Ejemplos:
    f = lambda x : x**2
    I = trapecio_comp(f,0,2,4)
    print('Ejemplo de prueba con trapecio compuesta =', I)
    Salida:
        Ejemplo de prueba con trapecio compuesta = 2.75
"""

# Función trapecio_comp
#--------------------------------------
def trapecio_comp(f,a,b,n):
    h = (b-a)/float(n)            # longitud del intervalo
    x = np.arange(a,b+h,h) # vector que contiene los nodos intermedios
    I = 0.0
    for i in range(1,n):
        I += f(x[i])
    I = (h/2.0)*(f(a)+f(b)) + h*I           # fórmula del trapecio compuesta
    return I

#--------------------------------------
# Ejemplos
#--------------------------------------

# Ejemplo de prueba
f = lambda x : x**2
I = trapecio_comp(f,0,2,4)
print('Ejemplo de prueba con trapecio compuesta =', I)

#--------------------------------------
# Ejercicio 2b
#--------------------------------------

# Cálculo de la integral aproximada
#---------------------------------------
f = lambda x :np.exp(x)
a = 0
b = 3
n = 4
Itc = trapecio_comp(f,a,b,n)

print ('El valor aproximado con trapecio compuesta es:', Itc)

"""
Ejercicio 2c: Integración - Fórmula de Simpson compuesta.
-----------------------------------------------------------------------------
Función simpson_comp: Halla la integral aproximada utilizando la fórmula de
Simpson compuesta para una función f en un intervalo [a,b].

Argumentos de entrada:
    f:  función integrando (función lambda).
    a:  extremo inferior del intervalo de integración (número real).
    b:  extremo superior del intervalo de integración (número real).
    n:  número de subintervalos (número entero).

Argumentos de salida:
    I:  integral aproximada con la fórmula de Simpson compuesta de la función
        f en [a,b] (número real).

Ejemplos:
    f = lambda x : x**2
    I = simpson_comp(f,0,2,6)
    print('Ejemplo de prueba con Simpson compuesta =', I)
    Salida:
        Ejemplo de prueba con Simpson compuesta = 2.6666666666666665
"""

# Función simpson_comp
#--------------------------------------
def simpson_comp(f,a,b,n):
    h = (b-a)/float(n)                                 # longitud de los subintervalos
    x1 = np.arange(a,b+h,h) # nodos que separan los subintervalos
    I = 0.0                                 # fórmula de Simpson compuesta
    for i in range(1,n+1,1):
        I += f(x1[i-1]) + 4.0*f((x1[i-1]+x1[i])/2.0) + f(x1[i])
    I = (h/6.0)*I
    return I

#--------------------------------------
# Ejemplos
#--------------------------------------

# Ejemplo de prueba
f = lambda x : x**2
I = simpson_comp(f,0,2,6)
print('Ejemplo de prueba con Simpson compuesta =', I)

#--------------------------------------
# Ejercicio 2c
#--------------------------------------

# Cálculo de la integral aproximada
#---------------------------------------
f = lambda x :np.exp(x)
a = 0
b = 3
n = 4
Its = simpson_comp(f,a,b,n)

print ('El valor aproximado con Simpson compuesta es:', Its)


import numpy as np
from numpy import linalg as linAlg
import math
import pandas as pd
from random import shuffle
from matplotlib import pyplot as plt
import matplotlib.pyplot as plt
import matplotlib.pyplot as pause
from mpl_toolkits.mplot3d import Axes3D
from time import sleep
import matplotlib.animation as animation
import sys

def plotPoints(plotOutFunction, fy=[], fx=[],tau=-1):
	x = []
	y = []

	for i in range(len(xyPoints)):
		x.append(xyPoints[i][0])
		y.append(xyPoints[i][len(xyPoints[0])-1])

	if tau == -1:
		plt.title("XY points and predicted function for Linear Regression")
	else:
		plt.title("XY points and predicted function for weighted Regression with tau = " + str(tau))
	plt.scatter(x,y,color='blue',label='Original Plot')
	if (plotOutFunction == 1):
		plt.scatter(fx,fy,color='red',label='Predicted Plot')

	plt.xlabel("Input Data")
	plt.ylabel("Output Data")
	plt.legend()

	plt.show()

def readPoints(fileX,fileY):
	noOfPoints = 0
	xyPoints = []

	f1 = open(fileX, "r")
	f2 = open(fileY, "r")

	xPoint = f1.read().splitlines()
	yPoint = f2.read().splitlines()

	for i in range(len(xPoint)):
		xy1Point = []
		xy1Point.append(float(xPoint[i]))
		xy1Point.append(1.0)			# This is the value corresponding to theta0 (since theta0 is the intercept)
		xy1Point.append(float(yPoint[i]))

		xyPoints.append(xy1Point)
		noOfPoints = noOfPoints + 1

	f1.close()
	f2.close()

	return xyPoints, noOfPoints

def normalize(xyPoints, mean, var):
	for i in range(len(xyPoints[0])):
		if (i == len(xyPoints[0])-2):		# Ignore the second last col and last col. Second last col is 1.0 that denotes intercept
			break

		x = []
		for j in range(len(xyPoints)):
			x.append(xyPoints[j][i])

		mean[i]=(np.mean(x))
		var[i]=(np.std(x))

		for j in range(len(xyPoints)):
			xyPoints[j][i] = (xyPoints[j][i] - mean[i]) / var[i]

	return xyPoints,mean,var

def getPredictedY(xyPoints, thetaVec):
	fy = []
	y = [0] * len(xyPoints)

	for k in range(len(xyPoints)):
		fy1 = 0.0
		for i in range(len(thetaVec)):
			fy1 = fy1 + thetaVec[i] * xyPoints[k][i]

		fy.append(fy1)
		y[k] = xyPoints[k][len(xyPoints[0])-1]

	return fy

def unormalize(xyPoints, mean, var, pfx=[], isFxAvailable=False):
	fx = pfx
	if isFxAvailable == False:
		fx = [0] * len(xyPoints)

	for i in range(len(xyPoints[0])):
		if (i == len(xyPoints[0])-2):
			break

		for j in range(len(xyPoints)):
			xyPoints[j][i] = (xyPoints[j][i] * var[i]) + mean[i]

			if isFxAvailable == True:
				fx[j] = (fx[j] * var[i]) + mean[i]
			else:
				fx[j] = xyPoints[j][i]

	return xyPoints, fx

def calculateTheta(xyPoints, thetaVec, weights, isWeightedLinearReg):
	X = np.zeros((len(xyPoints), 2))
	Xt = np.zeros((2, len(xyPoints)))
	Y = np.zeros((len(xyPoints), 1))
	W = np.zeros((len(xyPoints), len(xyPoints)))

	for i in range(len(xyPoints)):
		for j in range(len(xyPoints[i])-1):
			X[i][j] = xyPoints[i][j]
			Xt[j][i] = xyPoints[i][j]

	for i in range(len(weights)):
		W[i][i] = weights[i]

	for i in range(len(xyPoints)):
		Y[i][0] = xyPoints[i][len(xyPoints[0])-1]

	thetaVec = linAlg.inv(Xt @ X) @ Xt @ Y
	if isWeightedLinearReg == True:
		thetaVec = linAlg.inv(Xt @ W @ X) @ Xt @ W @ Y

	thetaVecRet = [0] * (len(thetaVec))
	for i in range(len(thetaVec)):
		thetaVecRet[i] = thetaVec[i][0]

	return thetaVecRet

def getWeights(xyPoints, noOfPoints, pointToProcess, weights, tau):
	for i in range(noOfPoints):
		diff = 0.0
		for j in range(len(xyPoints[i])-1):
			diff = diff + (pointToProcess[j] - xyPoints[i][j]) * (pointToProcess[j] - xyPoints[i][j])

		weights[i] = math.exp(-1 * (diff / (2 * tau * tau)))

	return weights

def linearRegression(xyPoints, noOfPoints, pointToProcess, isWeightedLinearReg, tau = 0):
	thetaVec = [0] * (len(xyPoints[0]) - 1)		# Last col is y so we will subtract that col in thetaVec and last theta is O0 which we will add a column in theta Vec
	weights = [1] * (noOfPoints)

	if isWeightedLinearReg == True:
		weights = getWeights(xyPoints, noOfPoints, pointToProcess, weights, tau)

	thetaVec = calculateTheta(xyPoints, thetaVec, weights, isWeightedLinearReg)
	fy = getPredictedY(xyPoints, thetaVec)

	return fy,thetaVec

def weightedLinearRegression(xyPoints, noOfPoints, tau):
	fyFinal = [1] * (noOfPoints)
	xFinal = [1] * (noOfPoints)
	cntr = 0

	minEle = 100000
	maxEle = -100000
	for i in range(len(xyPoints)):
		if minEle > xyPoints[i][0]:
			minEle = xyPoints[i][0]
		if maxEle < xyPoints[i][0]:
			maxEle = xyPoints[i][0]
	points = np.linspace(minEle, maxEle, noOfPoints)
	#for i in range(len(xyPoints)):
	#	points[i] = xyPoints[i][0]

	while cntr < noOfPoints:
		pointToProcess = [1] * (len(xyPoints[0])-1)
		pointToProcess[0] = points[cntr]

		fy,thetaVec = linearRegression(xyPoints, noOfPoints, pointToProcess, True, tau)		# True - Is weighted linear regression

		predictedPointToProcess = 0.0
		for j in range(len(thetaVec)):
			predictedPointToProcess = predictedPointToProcess + thetaVec[j] * pointToProcess[j]

		fyFinal[cntr] = predictedPointToProcess
		xFinal[cntr] = pointToProcess[0]

		cntr = cntr + 1

	return fyFinal,xFinal

fileX = sys.argv[1] # prints python_script.py
fileY = sys.argv[2] # prints var1
tau = float(sys.argv[3]) # prints var2

xyPoints,noOfPoints = readPoints(fileX, fileY)

mean = [0] * len(xyPoints[0])
var = [0] * len(xyPoints[0])

######### (a)
xyPoints,mean,var = normalize(xyPoints, mean, var)

fy,thetaVec = linearRegression(xyPoints, noOfPoints, [], False)					# False - Is not weighted linear regression

xyPoints,fx = unormalize(xyPoints, mean, var, [], False)
plotPoints(1,fy,fx)

######### (b)
xyPoints,mean,var = normalize(xyPoints, mean, var)

#tau =0.8
fy,fx = weightedLinearRegression(xyPoints, noOfPoints, tau)

xyPoints,fx = unormalize(xyPoints, mean, var, fx, True)
plotPoints(1,fy, fx,tau)

######### (c)
tauList = [0.1, 0.3, 2, 10]
for i in range(len(tauList)):
	xyPoints,mean,var = normalize(xyPoints, mean, var)

	tau = tauList[i]
	fy,fx = weightedLinearRegression(xyPoints, noOfPoints, tau)

	xyPoints,fx = unormalize(xyPoints, mean, var, fx, True)
	plotPoints(1,fy,fx,tau)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Jérôme Eberhardt 2016-2020
# Unrolr
#
# Principal Component Analysis
# Author: Jérôme Eberhardt <qksoneo@gmail.com>
#
# License: MIT

from __future__ import division
from __future__ import print_function
from __future__ import absolute_import

import numpy as np
from scipy import linalg

__author__ = "Jérôme Eberhardt"
__copyright__ = "Copyright 2020, Jérôme Eberhardt"

__lience__ = "MIT"
__maintainer__ = "Jérôme Eberhardt"
__email__ = "qksoneo@gmail.com"

class PCA:

    def __init__(self, n_components=None):
        """Create DihedralPCa object

        Args:
            n_components (int, None): Number of components to keep. if n_components is not set all components are kept.

        """
        self._n_components = n_components
        self.components = None
        self.singular_values = None

    def fit_transform(self, X):
        """Fit the model with X and apply the dimensionality reduction on X.

        Args:
            X (ndarray): array-like, shape (n_samples, n_features)

        Returns:
            ndarray: final embedding (n_samples, n_components)

        """
        # Centering the data
        X -= np.mean(X, axis=0)
        # Compute covariance matrix
        cov = np.cov(X, rowvar=False)
        # PCA!!!
        singular_values , components = linalg.eigh(cov)

        # Sort by singular values
        idx = np.argsort(singular_values)[::-1]
        self.components = components[:, idx].T
        self.singular_values = singular_values[idx]

        if self._n_components is None:
            embedding = np.dot(X, self.components)
        else:
            embedding = np.dot(X, self.components[:int(self._n_components)].T)

        return embedding


"""
This file represents an abstract activation function and many well-known activation functions
"""

from abc import ABC, abstractmethod
import math

import numpy as np

class AbstractActivation(ABC):
    @abstractmethod
    def get_value(self, x: np.array):
        """
        The value of the activation on the array (element wise operation)
        """

    @abstractmethod
    def get_derivative(self, x: np.array, value_at_x: np.array = None):
        """
        Get the derivative of the activation function on x (element wise operation)
        """

    def get_approximate_derivative(self, x: np.array, eps: float = math.pow(10, -7)):
        """
        :param x: the array on which we want to compute the approximate derivative (element wise)
        :param eps: the epsilon used for the approximation (we recommend to not change it)
        :return: the approximate derivative
        """
        return (self.get_value(x + eps) - self.get_value(x - eps)) / (2 * eps)

class Sigmoid(AbstractActivation):
    def get_value(self, x: np.array) -> np.array:

        return 1 / (1 + np.exp(-x))

    def get_derivative(self, x: np.array, value_at_x: np.array = None) -> np.array:

        # The derivative on x is equal to sigmoid(x) * (1 - sigmoid(x))
        if value_at_x is None:
            sigmoid_value = self.get_value(x)
        else:
            sigmoid_value = value_at_x

        return sigmoid_value * (1 - sigmoid_value)

class Relu(AbstractActivation):
    def get_value(self, x: np.array) -> np.array:

        return np.maximum(x, 0)

    def get_derivative(self, x: np.array, value_at_x: np.array = None) -> np.array:

        # The derivative of relu is 0 when value is less than 0 else 1
        # Mathematically speaking, there is no derivative on 0, but we will consider that it is
        # equal to 1

        return x >= 0

    def get_approximate_derivative(self, x: np.array, eps: float = math.pow(10, -7)):

        # We highly do not recommend to use the approxilate derivative when using a relu function
        raise Exception("Do not use derivative approximate on Relu")

class Tanh(AbstractActivation):
    def get_value(self, x: np.array) -> np.array:

        return np.tanh(x)

    def get_derivative(self, x: np.array, value_at_x: np.array = None) -> np.array:

        # The derivative on x is equal to 1 + tanh^2
        if value_at_x is None:
            tanh_value = self.get_value(x)
        else:
            tanh_value = value_at_x
        return 1 - np.power(tanh_value, 2)