# -*- coding: utf-8 -*-
"""
Created on Sat Oct 30 10:07:52 2021

@author: Uday Talwar
"""

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

mpl.rcParams['figure.dpi'] = 4800

def mandelbrotset(steps,threshold,msize,nsize,degree):
    '''
    The underlying function is of the form z_n+1 = z_n^k + c - z_0 (initial value) = 0.

    steps = number of iterations

    threshold = upper bound to estimate divergence/convergence of given C value

    msize = number of rows in np.mgrid

    nsize = number of columns in np.mgrid

    degree = the degree/power of z_n, corresponds to k in the underlying function
    '''

    steps = int(steps)
    threshold = int(threshold)
    msize, nsize = int(msize), int(nsize)

    xmin, xmax, resox = -2,1,complex(0,msize) #the x-axis bounds of the grid and column size
    ymin, ymax, resoy = -1.5,1.5,complex(0,nsize) #y-axis bounds of the grid and row size

    x,y = np.mgrid[xmin:xmax:resox, ymin:ymax:resoy] #initiate mgrid

    c = x+1j*y #define c

    z = 0 #initialize z

    for j in range(steps): #iterate z as z_n+1 = (z_n)^2 + c, where z_0 = 0

        z = np.where(abs(z)<threshold,z, threshold+1)**(degree) + c #computes only those elements that are < 50

    req_set = (abs(z) < threshold) #filter grid for absolute values of z lower than theshold

    return req_set #return filtered grid

mbrot = mandelbrotset(100,50,1000,1000,2) #run iteration

#plot iteration
plt.clf()

plt.imshow(mbrot.T, extent = [-2, 1, -1.5, 1.5])
plt.gray()
plt.show()

#some other interesting renders with varying function forms

def mandelbrotset2(steps,threshold,msize,nsize,degree):

    '''Underlying function of the form z_n+1 = z_n^k + z_n^k-1 + c'''

    xmin, xmax, resox = -2,1,complex(0,msize)
    ymin, ymax, resoy = -1.5,1.5,complex(0,nsize)

    x,y = np.mgrid[xmin:xmax:resox, ymin:ymax:resoy]

    c = x+1j*y

    z = c

    for j in range(steps): #of the form, z_n+1 = z_n^k + z_n^k-1 + c

        z = np.where(abs(z)<threshold,z, threshold+1)**degree + \
            np.where(abs(z)<threshold,z, threshold+1)**(degree-1) + c

    req_set = (abs(z) < threshold)

    return req_set

test2 = mandelbrotset2(100,50,1000,1000,3)

plt.clf()

plt.imshow(test2.T, extent = [-2, 1, -1.5, 1.5])
plt.gray()
plt.show()

def mandelbrotset3(steps,threshold,msize,nsize,degree):

    '''Underlying function of the form z_n+1 = z_n^k + z_n^k-1 + z_n^k-2 + c'''

    xmin, xmax, resox = -2,1,complex(0,msize)
    ymin, ymax, resoy = -1.5,1.5,complex(0,nsize)

    x,y = np.mgrid[xmin:xmax:resox, ymin:ymax:resoy]

    c = x+1j*y

    z = c

    for j in range(steps): #of the form, z_n+1 = z_n^k + z_n^k-1 + z_n^k-2 + c

        z = np.where(abs(z)<threshold,z, threshold+1)**degree + \
            np.where(abs(z)<threshold,z, threshold+1)**(degree-1) + \
                np.where(abs(z)<threshold,z, threshold+1)**(degree-2) + c

    req_set = (abs(z) < threshold)

    return req_set

test3 = mandelbrotset3(100,50,1000,1000,3)

plt.clf()

plt.imshow(test3.T, extent = [-2, 1, -1.5, 1.5])
plt.gray()
plt.show()


'''
Program to QR decomposition using the Gram-Schmidt method
Developed by: SRIJITH R
RegisterNumber: 21004191
'''
import numpy as np
def qr(A):
    n,m=A.shape
    Q=np.empty((n,n))
    u=np.empty((n,n))

    u[:,0]=A[::,0]
    Q[:,0]=u[:,0]/np.linalg.norm(u[:,0])

    for i in range (1,n):
        u[:,i]=A[:,i]
        for j in range(i):
            u[:,i]-=(A[:,i]@Q[:,j])*Q[:,j]
            Q[:,i]=u[:,i]/np.linalg.norm(u[:,i])

    R = np.zeros((n,m))
    for i in range(n):
        for j in range(i,m):
            R[i,j]=A[:,j]@Q[:,i]

    print(Q)
    print(R)
a=np.array(eval(input()))
qr(a)


import numpy as np#导入一个数据处理模块
import pylab as pl#导入一个绘图模块，matplotlib下的模块
from scipy import signal
sampling_rate = 8000#采样频率为8000Hz
fft_size = 4096 #FFT处理的取样长度
t = np.arange(0, 1.0, 1.0/sampling_rate)#np.arange(起点，终点，间隔)产生1s长的取样时间
x = 100*np.sin(2*np.pi*156.25*t) + 200*np.sin(2*np.pi*234.375*t)#两个正弦波叠加，156.25HZ和234.375HZ
# N点FFT进行精确频谱分析的要求是N个取样点包含整数个取样对象的波形。因此N点FFT能够完美计算频谱对取样对象的要求是n*Fs/N（n*采样频率/FFT长度），
# 因此对8KHZ和512点而言，完美采样对象的周期最小要求是8000/512=15.625HZ,所以156.25的n为10,234.375的n为15。
xs = x[:fft_size]# 从波形数据中取样fft_size个点进行运算
xf = np.fft.rfft(xs)/fft_size# 利用np.fft.rfft()进行FFT计算，rfft()是为了更方便对实数信号进行变换，由公式可知/fft_size为了正确显示波形能量
# rfft函数的返回值是N/2+1个复数，分别表示从0(Hz)到sampling_rate/2(Hz)的分。
#于是可以通过下面的np.linspace计算出返回值中每个下标对应的真正的频率：
freqs = np.linspace(0, sampling_rate/2, fft_size/2+1)
# np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
#在指定的间隔内返回均匀间隔的数字
xfp = 20*np.log10(np.clip(np.abs(xf), 1e-20, 1e100))
#最后我们计算每个频率分量的幅值，并通过 20*np.log10()将其转换为以db单位的值。为了防止0幅值的成分造成log10无法计算，我们调用np.clip对xf的幅值进行上下限处理

#绘图显示结果
pl.figure(figsize=(8,4))
pl.subplot(211)
pl.plot(t[:fft_size], xs)
pl.xlabel(u"Time(S)")
pl.title(u"156.25Hz and 234.375Hz WaveForm And Freq")
pl.subplot(212)
pl.plot(freqs, xfp)
pl.xlabel(u"Freq(Hz)")
pl.subplots_adjust(hspace=0.4)
pl.show()

sample_frequencies,segment_times,spectrogram=signal.spectrogram(xs,nperseg=512)
pl.imshow(spectrogram)
pl.show()


import numpy as np

class ellipsoid:
  def __init__(self,a=0,denf=0):
    self.a=a
    self.denf=denf
    self.setGRS80()

  def setGRS80(self):
    self.a=6378137
    self.denf=298.2572221
    self.prepare()
  def prepare(self):
    self.b=self.a*(1-1/self.denf)
    self.e=np.sqrt(1- self.b**2/(self.a**2))
    self.e2=np.sqrt(self.a**2/self.b**2- 1)
  def f(self):
    return 1/denf

  def N(self,lat):
    aux=self.e*np.sin(lat)
    N=self.a/np.sqrt(1-aux*aux)
    return N
  def M(self,lat):
    e=self.e
    M=a*(1-e**2)/(1-e**2 *np.sin(lat)*np.sin(lat))**1.5
    return M
  def xyz2lonlath(self,xyz):
    xyz=np.array(xyz)
    lon=np.arctan(xyz[1]/xyz[0]);
    p=np.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1]);
    e=self.e;
    e2=self.e2;
    b=self.b;
    u=np.arctan(xyz[2]*self.a/(p*b));
    lat=np.arctan((xyz[2]+e2*e2*b*np.sin(u)**3)/(p-e*e*self.a*np.cos(u)**3));
    N0=self.a/np.sqrt(1-e*e*np.sin(lat)*np.sin(lat));
    h=p/np.cos(lat)-N0;
    return np.array([lon*180/np.pi,lat*180/np.pi, h])
  def xyz2lonlathIter(self, xyz):
    lon=np.arctan(xyz[1]/xyz[0]);
    p=np.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1]);
    e=self.e;
    error=1;
    h=0; N0=1; lat0=1000;
    while (error>1e-10):
      lat1=np.arctan(xyz[2]/p/(1-e*e*N0/(N0+h)));
      N0=self.a/np.sqrt(1-e*e*np.sin(lat1)*np.sin(lat1));
      h=p/np.cos(lat1)-N0;
      error=abs(lat1-lat0);
      lat0=lat1;
    return np.array([lon*180/np.pi,lat0*180/np.pi, h])
  def lonlath2xyz(self,lonlath):
    lat=lonlath[1]/180*np.pi
    lon=lonlath[0]/180*np.pi
    h=lonlath[2]
    e=self.e;
    N=self.N(lat);
    x=(N+h)*np.cos(lat)*np.cos(lon);
    y=(N+h)*np.cos(lat)*np.sin(lon);
    z=(N*(1-e*e)+h)*np.sin(lat);
    return np.array([x,y,z])

if __name__=="__main__":
    grs80=ellipsoid()
    xyz=[5176588.653, -3618162.163, -887363.920 ]
    lonlath=grs80.xyz2lonlath(xyz)
    print(xyz)
    print(lonlath)
    print(grs80.xyz2lonlathIter(xyz))
    print(grs80.lonlath2xyz(lonlath))


import scipy.constants as constants
import numpy as np
import math

class QHOGas():

    """Generates the statistics of a gas of N quantum harmonic oscillators
    at thermal equilibrium at temperature T and frequency w """
    def __init__(self, N, T=0.00001, w=1000000):
        self.N = N
        self.T = T
        self.w = w

    def p_n(self, n):
        """
        Calculates the probabilities of finding a quantum harmonic
        oscillator with quantum numbers n
        """
        bhw = (constants.hbar * self.w)/(constants.k * self.T)
        Z_1 = 1/( 2*np.sinh(bhw/2) )
        return np.exp( -bhw*(n + 1/2) )/Z_1

    def calculate_expected_n(self, ns = None):
        """
        Calculates the expected quantum number.
        Returns the theoretical value if no samples are provided
        """
        if ns is None:
            bhw = (constants.hbar * self.w)/(constants.k * self.T)
            return 1/( math.exp(bhw) - 1 )
        else:
            return np.average(ns)

    def calculate_expected_e(self, ns = None):
        """
        Calculates the expected energy per oscillator.
        Returns the theoretical value if no samples are provided
        """
        if ns is None:
            n = self.calculate_expected_n()
            return constants.hbar*self.w*( n + 1/2 )
        else:
            # Compute the energy of each oscillator and then average
            return np.average( constants.hbar*self.w*( ns + 1/2 ) )

    def calculate_internal_energy(self, ns = None):
        """
        Calculates the expected energy (which equals to the internal energy).
        Returns the theoretical value if no samples are provided
        """
        if ns is None:
            bhw = (constants.hbar * self.w)/(constants.k * self.T)
            return self.N*constants.hbar*self.w*( 1/2 + 1/( math.exp(bhw) - 1 ) )
        else:
            # Compute the total energy of each batch and then average
            total_energy_per_gas = np.sum(constants.hbar*self.w*( ns + 1/2 ), axis=1 )
            return np.average(total_energy_per_gas)

    def generate(self, amount, normalize=True, n_max=10):
        """
        Generates 'amount' states of this gas where the quantum numbers are
        limited to a max of 'n_max'.
        The states can be normalized so that the quantum numbers returned
        range from 0 to 1 where one corresponds to n=n_max.
        """
        quantum_numbers = self.sample_quantum_numbers(amount, n_max)
        return quantum_numbers/n_max if normalize else quantum_numbers

    def sample_quantum_numbers(self, batchsize, n_max):
        """
        Generates the quantum numbers of 'amount' systems of N quantum harmonic
        oscillators each.
        The result is a 2-D tensor of shape (amount, N)
        """
        # Calculate the probability for the first n_max quantum numbers and normalize
        p = self.p_n( np.arange(0, n_max) )
        accumulated = np.cumsum(p)
        accumulated /= max(accumulated)
        # Get a uniform distribution to do the usual trick
        j = np.random.uniform(size=(batchsize, self.N))
        # Sample with this auxiliar distribution
        quantum_numbers = np.zeros( j.shape )
        prev = 0
        for i, val in enumerate(accumulated):
            # Set this quantum number i if the random number j
            # is in the range of probabilities of this quantum number
            indexes = np.logical_and(j>prev, j<val)
            quantum_numbers[indexes] = i
            prev = val
        return quantum_numbers


from math import sqrt, exp, log, sin, pi
from sympy import Symbol, Limit, Derivative, solve, sympify, S, simplify, lambdify, pprint, init_printing, symbols
from fractions import Fraction

init_printing( order = 'rev-lex', use_unicode = True )

# 4.1.1

# Suppose a particle’s position is given by f (t) = t 4,
# where t is measured in seconds and f (t) is given in centimeters. What is the velocity of the particle when t = 3?

Ft = t**4
t = Symbol( 't' )
Dt = Derivative( Ft, t ).doit()
Dt.subs( { t: 3 } )

# 4.1.2

# Suppose f(x) = x^-3. What is f'(x) ?

Fx = x**-3
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.1.3

# Suppose f(x) = x^0. What is f'(x) ?

Fx = x**0.1
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.1.6

# Suppose a particle's position is given by f(t) = t^4,
# where t is measurd in seconds and f(t) is given in meters.
# At what time is the velocity of the particle equal to 0 ?

Ft = t**4
t = Symbol( 't' )
Dt = Derivative( Ft, t ).doit()
sln = Dt.subs( { t: 1/2 } )

# 4.1.8

# Find the derivitve: -pi/3 ( sqrt(3) )

Fq = -1 * pi/3 * ( ( q ** 3 ) ** 1/2 )
q = Symbol( 'q' )
Derivative( Fq, q ).doit()

# 4.1.9

# Suppose f(x) = x^9. What is f'(x) ?

Fx = x**9
x = Symbol( 'x' )
Derivative( Fx, x ).doit()


import numpy as np

from simple_object_detection.typing import Point2D

def euclidean_norm(p: Point2D, q: Point2D) -> float:
    """Calcula la distancia euclídea entre 2 puntos.

    :param p: punto 1.
    :param q: punto 2.
    :return: distancia euclídea.
    """
    return np.linalg.norm(np.array(p) - np.array(q))


"""
A tetrahedral number[https://en.wikipedia.org/wiki/Tetrahedral_number] is is a figurate number that represents a
pyramid with a triangular base and three sides.
Write a program to find the base of the tetrahedron that contains an input number of balls.
example: 169179692512835000 balls
taken from programmingpraxis.com
"""

import sympy
# import math

solve = sympy.solvers.solve
x = sympy.Symbol('x')

def get_tetrahydral_level(num):
    return int(solve((x**3 + (3 * x**2) + (2 * x))/6 - num, x)[0])

def calc_triangular_number(num):
    return int(num * (num + 1) / 2)

if __name__ == '__main__':
    while True:
        inp = int(input('Enter number: '))
        try:
            n = get_tetrahydral_level(inp)
            tri = calc_triangular_number(n)
            print(tri)
        except TypeError:
            print('Non-valid tetrahydral number')

# Super slow method
# def nCr(n,r):
#     f = math.factorial
#     return f(n) / (f(r) * f(n-r))
#
# if __name__ == '__main__':
#     n = 1
#     inp = int(input('Enter number: '))
#     tri = nCr(n+1,2)
#     while tri != inp and inp > 0:
#         print(inp)
#         inp -= tri
#         n += 1
#         tri = nCr(n+1,2)
#
#     if inp >= 0:
#         print('Base: {}'.format(inp))
#     else:
#         print('Non tetrahedral number')


import numpy as np

from mygrad import Tensor
from mygrad.operation_base import Operation

__all__ = ["selu"]

_ALPHA = 1.6732632423543772848170429916717
_SCALE = 1.0507009873554804934193349852946

class SELU(Operation):
    """ Returns the scaled exponential linear activation (SELU) elementwise along x. The SELU is
    given by  λɑ(exp(x) - 1) for x < 0 and λx for x ≥ 0.

    Notes
    -----
    The SELU activation was proposed in the paper
        Self-Normalizing Neural Networks
        Günter Klambauer, Thomas Unterthiner, Andreas Mayr, Sepp Hochreiter
    at https://arxiv.org/abs/1706.02515
    """

    def __call__(self, x):
        """
        Parameters
        ----------
        x : mygrad.Tensor
            Input data.

        Returns
        -------
        numpy.ndarray
            The SELU function applied to `x` elementwise.
        """
        self.variables = (x,)

        x = x.data
        self.exp = _ALPHA * (np.exp(x) - 1)
        return _SCALE * np.where(x < 0, self.exp, x)

    def backward_var(self, grad, index, **kwargs):
        x = self.variables[index]
        return grad * _SCALE * np.where(x.data < 0, self.exp + _ALPHA, 1)

def selu(x, constant=False):
    """ Returns the scaled exponential linear activation (SELU) elementwise along x.

    The SELU is given by  λɑ(exp(x) - 1) for x < 0 and λx for x ≥ 0.

    Parameters
    ----------
    x : mygrad.Tensor
        Input data.

    constant : bool, optional(default=False)
        If ``True``, the returned tensor is a constant (it
        does not back-propagate a gradient)

    Returns
    -------
    mygrad.Tensor
        The SELU function applied to `x` elementwise.

    Notes
    -----
    The SELU activation was proposed in the paper
        Self-Normalizing Neural Networks
        Günter Klambauer, Thomas Unterthiner, Andreas Mayr, Sepp Hochreiter
    at https://arxiv.org/abs/1706.02515

    Examples
    --------
    >>> import mygrad as mg
    >>> from mygrad.nnet.activations import selu
    >>> x = mg.arange(-5, 6)
    >>> x
    Tensor([-5, -4, -3, -2, -1,  0,  1,  2,  3,  4,  5])
    >>> y = elu(x, alpha=0.1); y
    Tensor([-1.74625336, -1.72589863, -1.67056873, -1.52016647, -1.11133074,
         0.        ,  1.05070099,  2.10140197,  3.15210296,  4.20280395,
         5.25350494])
    """
    return Tensor._op(SELU, x, constant=constant)


import numpy as np

'''
softmaxOutputs = [[0.7, 0.1, 0.2],
                  [0.1, 0.5, 0.4],
                  [0.02, 0.9, 0.08]]

classTargets = [0, 1, 1]

for targ_idx, distribution in zip(classTargets, softmaxOutputs):
    print(distribution[targ_idx])
'''

softmaxOutputs = np.array([[0.7, 0.1, 0.2],
                           [0.1, 0.5, 0.4],
                           [0.002, 0.9, 0.08]])
classTargets = [0, 1, 1]

# print(softmaxOutputs[[0, 1, 2], classTargets])
print(softmaxOutputs[range(len(softmaxOutputs)), classTargets])

# Loss of distribution
Loss = -np.log(softmaxOutputs[
    range(len(softmaxOutputs)), classTargets
])

print('-log: ', Loss)

averageLoss = np.mean(Loss)
print('Average -log: ', averageLoss)

Chapter 5 - Calculating Loss/retrive-values-in-distribution.py


import matplotlib.pyplot as plt
import numpy as np
from numba import jit
from timeit import default_timer as timer
import random
start = timer()

# PARAMETERS TO CHANGE THE FRACTAL GENERATED
seq = "AB"                     # sequence to alternate r values
probability = 75               # probability that a given value in the sequence will switch
a_lb = 2                       # a lower bound
a_ub = 4                       # a upper bound
b_lb = 2                       # b lower bound
b_ub = 4                       # b upper bound

# PARAMETERS REFINING ACCURACY OF FRACTAL PICTURE GENERATED
num_warmups = 1200             # number of "warmups" or throwaway iterations before computing lyapunov exponent
num_lyap_iterations = 300      # number of iterations used to compute the lyapunov exp
steps = 500                    # steps between b1 and b2 values on axes -- higher it is, the better the picture

# CREATING RANDOM SEQUENCE WITH A LENGTH OF THE TOTAL NUMBER OF ITERATIONS
# EACH ITERATION THE PROBABILITY WILL BE JUDGED AGAINST THIS LIST
@jit
def getrandomseq():
    problist = list()
    for i in range(num_lyap_iterations + num_warmups):
        problist.append(random.randint(0,99))
    return problist

# LOGISTIC MAP THAT GIVES US THE NEXT X
@jit
def F(x, curr_r):
    return (curr_r * x) * (1 - x)

# DERIVATIVE OF F -- USED TO COMPUTE THE LYAPUNOV EXPONENT
@jit
def Fprime(x, curr_r):
    ans = curr_r * (1 - (2 *x))
    ans[ans == 0] = 0.0001
    ans[ans == -np.inf] = -1000
    ans[ans == np.inf] = 1000
    return ans

# RETURNS THE CORRECT B-VALUE BASED ON THE CURRENT ITERATION
@jit
def getseqval(curr_iteration, a, b, probability, problist):
    randnum = problist[curr_iteration]
    index = np.mod(curr_iteration, len(seq))
    if (seq[index] == 'A'):
        if (probability <= randnum):
            return a
        else:
            return b
    else:
        if (probability <= randnum):
            return b
        else:
            return a

# RETURNS THE LYAPUNOV EXPONENT BASED ON THE SPECIFIED B1 AND B2 VALUES
@jit
def getlyapexponent(time_scale, probability, problist):
    b1, b2 = time_scale
    lyap_prob = probability

    x = .5          # initial value of x
    lyapsum = 0     # initializing lyapunov sum for use later

    # do warmups, to discard the early values of the iteration to allow the orbit to settle down
    for i in range(num_warmups):
        x = F(x, getseqval(i, b1, b2, lyap_prob, problist))

    for i in range(num_warmups, num_lyap_iterations + num_warmups):
        lyapsum += np.log( np.abs(Fprime(x, getseqval(i, b1, b2, lyap_prob, problist) ) ) )
        # get next x
        x = F(x, getseqval(i, b1, b2, lyap_prob, problist))

    return (lyapsum / num_lyap_iterations)

# CREATING FRACTAL IMAGE
a = np.linspace(a_lb, a_ub, steps)   #range of b1 values
b = np.linspace(b_lb, b_ub, steps)   #range of b2 values
aa, bb = np.meshgrid(a, b)

# COMPUTING AVERAGE IMAGE FROM MULTIPLE RANDOM SEQUENCES
lyap_exponents = []
for i in range(10):
    problist = getrandomseq()
    lyap_exponents.append(getlyapexponent( (bb, aa), probability, problist ))

fractal_grid = np.average(lyap_exponents, axis = 0)

 # CREATING AND ADJUSTING GRAPH
plt.figure()       # creating new window for each graph that is run

plt.subplots_adjust(top = 0.825, bottom = 0.1, left = 0.11, right = 0.9, hspace = 0.2, wspace = 0.2)
plt.suptitle("Probabilistic Lyanpuov fractal for logistic map")
plt.title("Pattern: " + seq + "\n Probability that a given value\n in the pattern will switch: " + str(probability) + "%")

plt.xlabel("a")
plt.ylabel("b")

im = plt.imshow(fractal_grid, cmap = lyap_cmap, vmin = -2, vmax = 0, origin = "lower", extent = (a_lb, a_ub, b_lb, b_ub))
plt.colorbar(im)

end = timer()

print("elapsed time: " + str(end - start))


import numpy as np

# 计算sigmoid函数值
def sigmoid(z):
	g = 1/(1+np.exp(-z))

	return g

def sigmoid_gradient(z):
	grad = sigmoid(z) * (1-sigmoid(z))

	return grad


import numpy as np
import sys

'''
Function:  Normalization
Description: Normalize input data. For vector x, the normalization process is given by
            normalization(x) = (x - min(x))/(max(x) - min(x))
Input:  data        dataType: ndarray   description: input data
Output: normdata    dataType: ndarray   description: output data after normalization
'''

def Normalization(data):
    # get the max and min value of each column
    minValue = data.min(axis=0)
    maxValue = data.max(axis=0)
    diff = maxValue - minValue
    # normalization
    mindata = np.tile(minValue, (data.shape[0], 1))
    normdata = (data - mindata) / np.tile(diff, (data.shape[0], 1))
    return normdata

'''
Function:  Standardization
Description: Standardize input data. For vector x, the normalization process is given by
             Standardization(x) = x - mean(x)/std(x)
Input:  data            dataType: ndarray   description: input data
Output: standarddata    dataType: ndarray   description: output data after standardization
'''

def Standardization(data):
    # get the mean and the variance of each column
    meanValue = data.mean(axis=0)
    varValue = data.std(axis=0)
    standarddata = (data - np.tile(meanValue, (data.shape[0], 1))) / np.tile(varValue, (data.shape[0], 1))
    return standarddata

'''
Function:  calcuateDistance
Description: calcuate the distance between input vector and train data
Input:  x1      dataType: ndarray   description: input vector
        x2      dataType: ndarray   description: input vector
Output: d       dataType: float     description: distance between input vectors
'''
def calculateDistance(distance_type, x1, x2):
    if distance_type == "Euclidean":
        d = np.sqrt(np.sum(np.power(x1 - x2, 2), axis=1))
    elif distance_type == "Cosine":
        d = np.dot(x1, x2)/(np.linalg.norm(x1)*np.linalg.norm(x2))
    elif distance_type == "Manhattan":
        d = np.sum(x1 - x2)
    else:
        print("Error Type!")
        sys.exit()
    return d

'''
Function:  calcuateDistance
Description: calcuate the distance between input vector and train data
Input:  input       dataType: ndarray   description: input vector
        traind_ata  dataType: ndarray   description: data for training
        train_label dataType: ndarray   description: labels of train data
        k           dataType: int       description: select the first k distances
Output: prob        dataType: float     description: max probability of prediction
        label       dataType: int       description: prediction label of input vector
'''
# def calculateDistance(input, train_data, train_label, k):
#     train_num = train_data.shape[0]
#     # calcuate the distances
#     distances = np.tile(input, (train_num, 1)) - train_data
#     distances = distances**2
#     distances = distances.sum(axis=1)
#     distances = distances**0.5

#     # get the labels of the first k distances
#     disIndex = distances.argsort()
#     labelCount = {}
#     for i in range(k):
#         label = train_label[disIndex[i]]
#         labelCount[label] = labelCount.get(label, 0) + 1

#     prediction = sorted(labelCount.items(), key=op.itemgetter(1), reverse=True)
#     label = prediction[0][0]
#     prob = prediction[0][1]/k
#     return label, prob

'''
Function:  calculateAccuracy
Description: show detection result
Input:  test_data  dataType: ndarray   description: data for test
        test_label dataType: ndarray   description: labels of test data
Output: accuracy   dataType: float     description: detection accuarcy
'''
def calculateAccuracy(test_label, prediction):
    test_label = np.expand_dims(test_label, axis=1)
    # prediction = self.prediction
    accuracy = sum(prediction == test_label)/len(test_label)
    return accuracy


import sympy as sym
import scipy as sci
import numpy as np
from math import *
import sys

from lib_newton import *

A             = np.zeros((1,0));
n             = 0;
eps           = 0;
wholeSize     = 1;

# Load ma trận từ file
while(n * n != wholeSize):
#{
    print("Đang tải ma trận.....");
    A = np.loadtxt("input.txt");
    A = np.ravel(A);

    # Tự sinh kích thước ma trận
    wholeSize = np.shape(A)[0];
    n = int(sqrt(wholeSize));

    if (n * n != wholeSize):
    #{
        c = input("Kích cỡ ma trận không hợp lệ. Vui lòng chỉnh sửa file input.txt gõ \"Y\" để tải lại chương trình hoặc gõ \"N\" để thoát chương trình:");
        if(c == "N"):
            exit();
    #}
    else:
        A = np.reshape(A, (n,n));
        print("Tải ma trận thành công.")
#}

# Nhập sai số eps
eps           = float(input("Nhập sai số eps: "));

# In ra ma trận A
print("--------------------------------------------------------------------");
print("Ma trận A:");
print(A);

# Nghịch đảo ma trận
uu = newton_mat_inversion(A, n, eps);
A1 = uu.improved_newton();

# In ra ma trận nghịch đảo
print("--------------------------------------------------------------------");
print("Ma trận nghịch đảo của A:");
print(A1);

# Kiểm tra nhân ngược
print("--------------------------------------------------------------------");
print("Kiểm tra nhân ngược:");
print(A1 @ A);

Topic 2.5 - Matrix Inversion/14.1.Newton/newton_fromfile.py


#! /usr/bin/python

"""
This programme solves an online coding challenge of manupulating arrays based on prime numbers

Version 1.0

Author: Billy Shi
Date: Dec 24, 2021
"""

### Problem Statement (paraphased)
"""
One row of lights start with a current state, each can be turned on (1) or off (0). This is provided
in an array called /states/. Another integer is provided called /number/.
For this number, we change the state of the light that correspond to the multiples of its prime factors.

For example: if initial /states/ is [1, 1, 1, 1, 1, 1]
Number is 6 with prime factors 2 and 3
We change the states of lights 2, 4, 6 because they are multiples of 2
We then change states of lights 3, 6 because that is multiple of 3

For a more advanced version, the variable /numbers/ can contain an array of numbers.
Each number carries out the above steps.

Output the final states of lights in an array.
"""

### Solution Overview
"""
Approach this solution by breaking down steps:
1. For each number in /numbers/, get its prime factors
2. For each prime factor, work out the lights that need state change
3. Change light state and store it for next iteration
"""

import numpy as np

def PrimeLight(states, numbers):
    # function to obtain all prime factors
    def primefactor(n):
        res = []
        # if n is even
        while n % 2 == 0:
            if 2 not in res:
                res.append(2)
            n = n / 2
        # now n must be even
        for i in range(3,int(np.sqrt(n))+1,2):
            while n % i == 0:
                if i not in res:
                    res.append(i)
                n = n / i
        # finally include n itself
        if n > 2 and n not in res:
            res.append(int(n))
        return res
    # obtain prime factors for numbers
    for i in range(len(numbers)):
        numbers[i] = primefactor(numbers[i])
        temp = []
        for prime in numbers[i]:
            temp = temp + list(np.arange(prime, prime*(len(states)//prime)+1, prime))
        numbers[i] = temp
        # now the variable numbers contain lights needing state change
        for l in numbers[i]:
            if states[l-1] == 1:
                states[l-1] = 0
            elif states[l-1] == 0:
                states[l-1] = 1

    return states

# Run program
states = [1,1,0,0,1,1,0,1,1,1]
numbers = [3,4,15]
assert PrimeLight(states, numbers) == [1,0,0,1,0,0,0,0,1,1]


from warnings import warn
import numpy as np
import numpy.linalg as la

from modpy.optimize._optim_util import OptimizeResult
from modpy.optimize._constraints import prepare_bounds

TERMINATION_MESSAGES = {
    -1: 'LinAlgError due to indeterminate system.',
    0: 'maximum number of iterations reached.',
    1: 'not implemented yet.',
    2: 'not implemented yet.',
    3: 'the unconstrained solution is optimal.'
}

def lsq_linear(A, b, W=None, bounds=None, method='', tol=1e-10, max_iter=100):
    """
    Solves a weighted linear least squares problem with bounds on the variables.

    Given an m-by-n system matrix, A, and a target vector with m elements,
    solves the least squares formulation::

        min 1/2 * ||A x - b|| ** 2
        s.t. lb <= x <= ub

    with unconstrained solution (ordinary)::

        x = (A'A) **(-1) A'b

    with unconstrained solution (weighted)::

        x = (A'WA) **(-1) A'Wb

    Parameters
    ----------
    A : array_like, shape (n, m)
        System matrix
    b : array_like, shape (n,)
        Target vector
    W : array_like, shape (n, n), optional
        Weight matrix
    bounds : 2-tuple of array_like, optional
        Lower and upper bounds. Both lower and upper bound must
        have shape (n,) or be a scalar. Use np.inf for no bounds
    method : string, optional
        TODO: allow Trust Region Reflective algorithm
    tol : float, optional
        Tolerance parameter related to convergence of iterative bounded solver
    max_iter : None or int, optional
        Maximum number of iterations during iterative bounded solver

    Returns
    -------
    OptimizeResult with the following fields:
    x : array_like, shape (m,)
        Solution vector.
    success : bool,
        True if algorithm converged within its optimality conditions.
    status : int
        Reason for algorithm termination:

            * -1 : LinAlgError due to indeterminate system matrix (or weights)
            *  0 : Maximum number of iterations reached (TODO: for trust-region)
            *  1 : TODO: first order optimality reached (trust-region)
            *  2 : TODO : relative change of cost function (trust-region)
            *  3 : Unconstrained solution is optimal

    message : str
        Description of the termination reason.
    nit : int
        Number of iterations used to converge. 0 in the unconstrained case (no iterations required).
    """

    n, m = A.shape

    b = np.atleast_1d(b)
    if b.ndim != 1:
        if (b.ndim == 2) and (np.prod(b.shape) == b.size):
            b = np.reshape(b, (b.size,))
        else:
            raise ValueError('`b` must have at most 1 dimension')

    if b.size != n:
        raise ValueError('Inconsistent shapes between `A` and `b`.')

    if (W is not None) and ((W.shape[0] != n) or (W.shape[1] != n)):
        raise ValueError('Inconsistent shapes between `A` and `W`.')

    if bounds is not None:
        lb, ub = prepare_bounds(bounds, n)

    try:

        if W is None:
            x, cond = _ordinary_lsq(A, b)
        else:
            x, cond = _weighted_lsq(A, b, W)

        res = OptimizeResult(x, success=True, status=3, nit=0, cond=cond)

    except la.LinAlgError:
        res = OptimizeResult(None, success=False, status=-1, nit=0)

    res.message = TERMINATION_MESSAGES[res.status]

    return res

def _weighted_lsq(A, b, W):
    ATW = A.T @ W
    Q, R = la.qr(ATW @ A)
    return la.solve(R, Q.T @ (ATW @ b)), _condition(R)

def _ordinary_lsq(A, b):
    Q, R = la.qr(A.T @ A)
    return la.solve(R, Q.T @ (A.T @ b)), _condition(R)

def _condition(R):
    cond = la.cond(R)
    if cond > 1e5:
        warn('The condition number of R in the LSQ solver is: {}'.format(cond))

    return cond


# Run a test to evaluate parameters of 2d gaussian through FFT/PSD/ACovF.
# This is useful, because a Gaussian should be analytically predictable through each of these transformations.
# Summary:
# The FFT of a gaussian is a Gaussian, with sigma_fft (in frequency space) = 1/(2*pi*sigma_x).
#  Note that a gaussian also translates to a real-only FFT (imaginary part = 0), if using analytic, perfect transform.
# Then the 2d PSD is created by squaring the (real+imaginary) FFT - and a gaussian squared is a gaussian with sigma
#   divided by sqrt 2. So the sigma_psd2d = sigma_fft / sqrt(2) (note this is still in frequency).
# The 2d ACovF is calculated by taking the FFT of the PSD, thus translating back to spatial coordinates .. so
#  sigma_acovf = 1/(2*pi*sigma_psd2d) = 1/(2*pi*sigma_FFT)*sqrt(2) = sigma_x*sqrt(2)
# Looking at the 1d version of the ACovF, we should still find a gaussian with same sigma, and same for 1d PSD in frequency.
#  But, if we want to translate the PSD into 'spatial' scales related to pixels, we can look at the width of the gaussian
#  and try to translate that to pixel scales. The result is that sigma_psd(pix) = sqrt(nx*ny)/(sigma_psd(freq)*2*pi)

import numpy
import pylab
import testImage as ti
from scipy.optimize import curve_fit
from scipy import fftpack

# Gaussian function (for fitting).
def gaussian(x, *p):
    A, mu, sigma = p
    return A*numpy.exp(-(x-mu)**2/(2.*sigma**2))
# Simple function to generate expected and fit data values.
def dofit(x, d, mean, sigma):
    p = (d.max().real, mean, sigma)
    coeff, var_matrix = curve_fit(gaussian, x, d, p0=p)
    fitval = gaussian(x, *coeff)
    expval = gaussian(x, *p)
    print '  Fit:', coeff,  ' Exp:', p
    return fitval, expval
def doplot(x, d, fitval, expval, title):
    pylab.figure()
    pylab.plot(x, d, 'b.', label='data')
    pylab.plot(x, fitval, 'r-', label='fit')
    pylab.plot(x, expval, 'k:', label='expected')
    pylab.legend(numpoints=1, fancybox=True, fontsize='small')
    pylab.title(title)
    return

# Step 1 - generate 2d gaussian image.
# Generate 2d gaussian image of size 'size' and with sigma 'sigma_x'.
size = 750
sigma_x = 5.0
gauss = ti.TestImage(shift=True, nx = size, ny=size)
gauss.addGaussian(xwidth=sigma_x, ywidth=sigma_x, value=1.0)
gauss.zeroPad()
#gauss.hanningFilter()  # not really necessary for a gaussian which doesn't touch the edges of the image.
# and calculate FFT/PSD2d/PSD1d/ACovF2d/ACovF1d.
gauss.calcAll(min_npix=2, min_dr=1.0)

print 'Using image size of %.0f (but with zeroPadding to get to %.0f) and sigma of %.2f' %(size, gauss.nx, sigma_x)

# Step 2 - analyze the width of the gaussians for each of the original image/FFT/PSD/ACovF.

# Look at original image, and slice through center.
print 'Plotting Image'
gauss.showImage()
# Get x data for 1-d gaussian fit to data from the center of the image
x = numpy.arange(0, gauss.nx, 1.0)
# Get 'y' (image) data for 1-d gaussian fit
d = gauss.image[round(gauss.ny/2.0)][:]
# Set expected values for fit ...
mean = gauss.nx/2.0
sigma = sigma_x
fitval, expval = dofit(x, d, mean, sigma)
doplot(x, d, fitval, expval, 'Image slice')

# Move on to FFT
print 'Plotting FFT'
gauss.showFft(real=True, imag=True)
# Slice through FFT ... note that we're now dealing with FREQUENCY space
#   w (freq) = 1/(2*PI*x)
x = gauss.xfreq
d = fftpack.ifftshift(gauss.fimage)[0][:].real
d = numpy.abs(d)
idx = numpy.argsort(x)
d = d[idx]
x = x[idx]
# So expect gaussian to FT to another gaussian, but now with mu=0 and sigma=1/(2*PI*sigma_x)
mean = 0
sigma_fft = 1/(2.0*numpy.pi*sigma_x)
fitval, expval = dofit(x, d, mean, sigma_fft)
doplot(x, d, fitval, expval, 'FFT slice')
pylab.xlim(-.2, .2)

# Now look at PSD - in frequency space first.
print 'Plotting PSD frequency scale'
gauss.showPsd2d()
d = fftpack.ifftshift(gauss.psd2d)[0][:].real
d = d[idx]
# because PSD is square of FFT (which was a gaussian), expect mu = 0 but
#  sigma = sigma_fft / sqrt(2) == (1/sigma_x*2*PI)*1/sqrt(2)
mean = 0
sigma_psd_freq = sigma_fft/numpy.sqrt(2)
fitval, expval = dofit(x, d, mean, sigma_psd_freq)
doplot(x, d, fitval, expval, 'PSD2d slice')
pylab.xlim(-.2, .2)

# And now look at 2d psd in 'pixel' scales
print 'Plotting PSD2d spatial scale'
x = numpy.arange(-gauss.xcen, gauss.nx-gauss.xcen, 1.0)
d = gauss.psd2d[round(gauss.ny/2.0)][:].real
# Because of pixel 'scaling' (and is tied to total size of image) expect sigma = sqrt(nx*ny)/(sigma_x*sqrt(2)*2*PI)
mean = 0
sigma_psd_pix = 1/(sigma_x*numpy.sqrt(2))*numpy.sqrt(gauss.nx*gauss.ny)/(2.0*numpy.pi)
fitval, expval = dofit(x, d, mean, sigma_psd_pix)
doplot(x, d, fitval, expval, 'PSD2d slice, spatial scale')
pylab.xlim(-200, 200)

# Plot 1d PSD (checked this with frequency space, and it matches. However, in 'pixel space', it just doesn't really.
# This may be just a problem with my interpretation, but it does seem like there is something more fundamental here.
# For example, the distribution in pixel space is not really quite a gaussian (even fitting 1/x) - althouhg it is close.
"""
print 'plotting 1d PSD'
gauss.showPsd1d(linear=True)
x = gauss.psdx
d = gauss.psd1d.real
r = 1/x
sigma = 1/(sigma_psd_pix)
#r =x
#sigma = sigma_psd_freq
fitval, expval = dofit(r, d, 0., sigma)
doplot(x, d, fitval, expval, 'PSD1d')
"""

# Now on to the ACovF
print 'Plotting ACovF'
gauss.showAcovf2d()
x = numpy.arange(-gauss.xcen, gauss.nx-gauss.xcen, 1.)
d = gauss.acovf.real[round(gauss.ny/2.0)][:]
# Okay, ACovF is actual inverse FFT of PSD, so in pixel scales, expect sigma_acovf = 1/(sigma_psd_freq*2*PI)
#   ... or = sigma_x*sqrt(2) because of squaring the gaussian during the PSD creation.
#sigma_acovf = 1/(sigma_psd_freq*2.0*numpy.pi)
mean = 0
sigma_acovf = sigma_x*numpy.sqrt(2)
fitval, expval = dofit(x, d, mean, sigma_acovf)
doplot(x, d, fitval, expval, 'ACovF2d slice')

# And look at the 1d ACovF.
print 'Plotting acovf 1d.'
x = gauss.acovfx
d = gauss.acovf1d.real
sigma_acovf = sigma_x*numpy.sqrt(2)
fitval, expval = dofit(x, d, mean, sigma_acovf)
doplot(x, d, fitval, expval, 'ACovF1d')

pylab.show()


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

# Author: Daniel Yang <daniel.yj.yang@gmail.com>
#
# License: BSD 3 clause

import inspect
import numpy as np
import sympy
import torch
import matplotlib.pylab as plt

class calculus(object):
    def __init__(self):
        super().__init__()

    @staticmethod
    def derivative_using_sympy():
        x = sympy.symbols('x')
        for expr in [x, sympy.sin(x), sympy.exp(x), sympy.log(x), x**x]:
            for i in range(4):
                expr = sympy.Derivative(expr, x)
                print(f"{expr} = {expr.doit()}, when x=1.0, expression = {expr.doit().evalf(subs={x:1,})}")

    @staticmethod
    def derivative_plot_using_sympy():
        x = sympy.symbols('x')
        expr = x ** 3
        deriv = sympy.Derivative(expr, x).doit()
        x_values = np.linspace(-5, 5, 1000)
        expr_values  = np.array([expr.evalf(subs={x:x_value})  for x_value in x_values])
        deriv_values = np.array([deriv.evalf(subs={x:x_value}) for x_value in x_values])
        plt.plot(x_values, expr_values,  label = 'x ** 3')
        plt.plot(x_values, deriv_values, label='derivative (3*x**2)')
        plt.xlabel('x')
        plt.legend()
        plt.title(f"{inspect.getframeinfo(inspect.currentframe()).function}()")
        plt.show()

    @staticmethod
    def integrate_using_sympy():
        x = sympy.symbols('x')
        for expr in [x, sympy.sin(x), sympy.exp(x), sympy.log(x), x**x]:
            for i in range(4):
                expr = sympy.Integral(expr, x)
                print(f"{expr} = {expr.doit()}, when x=1.0, expression = {expr.doit().evalf(subs={x:1,})}")

    @staticmethod
    def derivative_using_torch():
        x = torch.tensor(1.0, requires_grad=True)
        expr = torch.exp(x)
        expr.backward()
        print("The dervative of exp(x) at x = 1.0: ", x.grad)

    @staticmethod
    def derivative_plot_using_torch():
        x = torch.linspace(-5, 5, 1000, requires_grad=True)
        expr = x ** 3
        sum_trick = torch.sum(expr) # a sum trick
        sum_trick.backward()
        plt.plot(x.detach().numpy(),   expr.detach().numpy(), label='x ** 3')
        plt.plot(x.detach().numpy(), x.grad.detach().numpy(), label='derivative (3*x**2)')
        plt.xlabel('x')
        plt.legend()
        plt.title(inspect.getframeinfo(inspect.currentframe()).function)
        plt.show()

def math_demo():
    calculus.derivative_plot_using_sympy()
    calculus.derivative_plot_using_torch()

    calculus.derivative_using_sympy()
    calculus.integrate_using_sympy()
    calculus.derivative_using_torch()


# -*- coding: utf-8 -*-
import numpy as np
import cvxpy as cvx
import matplotlib.pyplot as plt
import scipy as sp

def multilevelPWM(S=[-2,-1,0,1,2], sineharmonicnums=[1,3,5,7], sinevals=[1.5,0,0,0], cosineharmonicnums=[1,3,5,7], cosinevals=[0,0,0,0], N=2048, DCval=0, solver='ECOS', plots='OFF'):
    """
    :param S: List consisting of possible voltage levels (floats).
    :param sineharmonicnums: List of sine harmonic numbers (integers).
    :param sinevals: List of sine harmonic values (floats).
    :param cosineharmonicnums: List of cosine harmonic numbers (integers).
    :param cosinevals: List of cosine harmonic values (floats).
    :param N: Time discretization (integer, default is 2048). Must be much larger than max(sineharmonicnums, sosineharmonicnums).
    :param DCval: Desired DC value (floats).
    :param solver: One of the CVXPY solvers, (default: ECOS).
    :param plots: Set it to either 'ON' or 'OFF',  (default: 'OFF).
    :return: The waveform as a list, the switching angles as a list (degrees) and the norm of the difference of
    actual and desired as a percentage of the absolute sum of desired harmonics
    """
    Fs = np.zeros([len(sineharmonicnums), N])
    Fc = np.zeros([len(cosineharmonicnums), N])

    for k in range(len(sineharmonicnums)):
        Fs[k, :] = np.sin(2 * np.pi * (np.linspace(0, N - 1, N) / N) * sineharmonicnums[k])
    for k in range(len(cosineharmonicnums)):
        Fc[k, :] = np.cos(2 * np.pi * (np.linspace(0, N - 1, N) / N) * cosineharmonicnums[k])

    Z = cvx.Variable([N, len(S)])
    s = np.array(S)

    constraints = [np.ones(N) @ (Z @ s) / N == DCval,
                   2*Fc @ (Z @ s) / N == cosinevals,
                   2*Fs @ (Z @ s) / N == sinevals,
                   Z >= 0,
                   Z * np.ones([5, 1]) == 1]

    prob = cvx.Problem(cvx.Minimize(np.ones([1, N]) * (Z @ (s ** 2)) / N), constraints)
    prob.solve(solver=solver, verbose=False)

    x = np.matmul(Z.value, s)
    xc = np.zeros(len(x))

    for k in range(len(x)):
        idx = np.where(np.abs(np.array(s)-x[k])==np.min(np.abs(np.array(s)-x[k])))[0][0]
        xc[k] = s[idx]

    if prob.status=='infeasible':
        print('A multilevel PWM with the given constraints does not exist!')
        return -1
    elif(prob.status=='optimal_inaccurate'):
        print('Solution is inaccurate. Try another solver!')
    else:
        print('An optimal solution was found!')

    angles = 360 * (np.where(np.abs(np.diff(xc)) > 0)[0] + 1) / N
    y = np.matmul(sp.linalg.dft(N), xc) / N

    if(plots=='ON'):
        plt.figure()
        plt.title('The Multievel PWM Waveform', fontsize=30)
        plt.plot(np.linspace(0, N - 1, N), xc)

        plt.figure()
        plt.title('The Fourier Spectrum', fontsize=30)
        plt.stem(np.linspace(0,N-1,N), np.abs(y))

    diff_norm_percentage = 100*(np.linalg.norm(2*Fc@xc/N-cosinevals) + np.linalg.norm(2*Fs@xc/N-sinevals))/(np.sum(np.abs(cosinevals)) + np.sum(np.abs(sinevals)))

    return xc, angles, diff_norm_percentage


import numpy as np
import csv
import sys
import matplotlib.pyplot as plt
from scipy.special import erf

A = -np.pi           # random offset lower bound
B =  np.pi           # random offset higher bound
SEED = 112358      # numpy.random seed for reproducibility

def unif(a,b):
    return a + np.random.rand()*(b-a)

def random_jump_location():
    return unif(A,B),unif(A,B)

def initial_condition(x,y):
    # return np.sin(x)*np.cos(y)
    return -np.cos(x)*np.sin(y)

def exact_avg(x,y):
    # return ( np.cos(x-B) - np.cos(x-A) )*( np.sin(y-A) - np.sin(y-B) )/(B-A)/(B-A)
    return -( np.cos(y-B) - np.cos(y-A) )*( np.sin(x-A) - np.sin(x-B) )/(B-A)/(B-A)

def exact_avg_square(x,y):
    # return (np.sin(2*(x-B))-np.sin(2*(x-A)) +2*(B-A) )*\
    #         (np.sin(2*(y-A))-np.sin(2*(y-B)) +2*(B-A))/(B-A)/(B-A)/16
    return (np.sin(2*(y-B))-np.sin(2*(y-A)) +2*(B-A) )*\
            (np.sin(2*(x-A))-np.sin(2*(x-B)) +2*(B-A))/(B-A)/(B-A)/16

def exact_var(x,y):
    return exact_avg_square(x,y) - np.square(exact_avg(x,y))

def make_meanvar(N,M):
    dx = 2*np.pi/N
    x,y = np.mgrid[0:2*np.pi:dx, 0:2*np.pi:dx]
    average = np.zeros((N,N))
    M2 = np.zeros((N,N))
    delta = np.zeros((N,N))
    for m in range(M):
        X,Y = random_jump_location()
        U = initial_condition(x-X,y-Y)
        delta = U - average
        average += delta/(m+1)
        delta2 = U - average
        M2 += delta*delta2
    return (average, M2/(M-1))

M = 1024
N = 64

average,var = make_meanvar(N,M)

dx = 2*np.pi/N
x,y = np.mgrid[0:2*np.pi:dx, 0:2*np.pi:dx]
plt.subplot(321)
plt.title("s mean")
plt.contourf(x,y,average)
plt.colorbar()
plt.subplot(323)
plt.title("e mean")
plt.contourf(x,y,exact_avg(x,y))
plt.colorbar()
plt.subplot(325)
plt.contourf(x,y,average- exact_avg(x,y))
plt.colorbar()

plt.subplot(322)
plt.title("s var")
plt.contourf(x,y,var)
plt.colorbar()
plt.subplot(324)
plt.title("e var")
plt.contourf(x,y,exact_var(x,y))
plt.colorbar()
plt.subplot(326)
plt.contourf(x,y,var- exact_var(x,y))
plt.colorbar()

plt.show()

plt.close('all')
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x,y,average)
plt.show()

plt.close('all')
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x,y,var)
plt.show()


#!/usr/bin/python
"""Module for Cholesky Methods"""
import numpy as np
import scipy.sparse as sp

def CholeskyFactorization(A):
    """
    Runs a Cholesky Factorization on matrix A.

    The matrix A is expected to be:
    - Square
    - Tridiagonal
    - Symmetric
    - Positive Definite
    In the event the matrix fails one of these condition, a RuntimeError is raised.
    """

    # Check if the matrix is in dia_matrix format, and convert otherwise
    if sp.isspmatrix_dia(A):
        T = A
    else:
        T = sp.dia_matrix(A)

    # Check the dimensions
    (m, n) = T.shape
    if m != n:
        raise RuntimeError("Matrix must be square")

    # Check if the matrix is tridiagonal
    if not _isTridiagonal(T):
        raise RuntimeError("Matrix must be tridiagonal")

    # Check for symmetry - we already know it's tridiagonal, only 1 diag above and below to check
    if not all(T.diagonal(-1) == T.diagonal(1)):
        raise RuntimeError("Matrix must be symmetric")

    # Make copies of the necessary diagonals to prevent changing the original matrix
    L_main_diag = T.diagonal(0).copy()
    L_sub_diag = T.diagonal(-1).copy()

    # Build L column by column
    for i in range(n):
        # The first column doesn't need anything subtracted
        if i > 0:
            L_main_diag[i] -= L_sub_diag[i-1]*L_sub_diag[i-1]

        # Check if matrix is indefinite here to prevent complex arithmetic cost
        if L_main_diag[i] < 0:
            raise RuntimeError("Matrix must be positive definite")

        # Now that we know it's positive, can take the square root
        L_main_diag[i] = np.sqrt(L_main_diag[i])

        # Subdiagonal is one element shorter than main diagonal so can't assign at i == n-1
        if i < n-1:
            L_sub_diag[i] /= L_main_diag[i]

    L = sp.diags([L_main_diag, L_sub_diag], [0, -1], dtype=np.double, format="dia")
    return L

def _isTridiagonal(T):
    """
    Checks to see whether L is tridiagonal.
    L must be in scipy.sparse.dia_matrix form
    """
    # Make sure the matrix is in dia_matrix format
    if not sp.isspmatrix_dia(T):
        raise RuntimeError("Input to _isTridiagonal must be dia_matrix")

    # First check if there's 3 diagonals
    if len(T.offsets) != 3:
        return False

    # Check for the main diagonal
    if 0 not in T.offsets:
        return False

    # Check for the super diagonal
    if 1 not in T.offsets:
        return False

    # Check for the sub diagonal
    if -1 not in T.offsets:
        return False

    return True


import numpy as np

def Ent_Samp20200723(data, m, r):
  """
  function SE = Ent_Samp20200723(data,m,r)
  SE = Ent_Samp20200723(data,m,R) Returns the sample entropy value.
  inputs - data, single column time seres
          - m, length of vectors to be compared
          - r, radius for accepting matches (as a proportion of the
            standard deviation)

  output - SE, sample entropy
  Remarks
  - This code finds the sample entropy of a data series using the method
    described by - Richman, J.S., Moorman, J.R., 2000. "Physiological
    time-series analysis using approximate entropy and sample entropy."
    Am. J. Physiol. Heart Circ. Physiol. 278, H2039–H2049.
  - m is generally recommendation as 2
  - R is generally recommendation as 0.2
  May 2016 - Modified by John McCamley, unonbcf@unomaha.edu
           - This is a faster version of the previous code.
  May 2019 - Modified by Will Denton
           - Added code to check version number in relation to a server
             and to automatically update the code.
  Jul 2020 - Modified by Ben Senderling, bmchnonan@unomaha.edu
           - Removed the code that automatically checks for updates and
             keeps a version history.
  Define r as R times the standard deviation
  Copyright 2020 Nonlinear Analysis Core, Center for Human Movement
  Variability, University of Nebraska at Omaha

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

  1. Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  3. Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived from
      this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  """
  R = r * np.std(data)
  N = len(data)

  data = np.array(data)

  dij = np.zeros((N-m,m+1))
  dj =  np.zeros((N-m,1))
  dj1 = np.zeros((N-m,1))
  Bm = np.zeros((N-m,1))
  Am = np.zeros((N-m,1))

  for i in range(N-m):
    for k in range(m+1):
      dij[:,k] = np.abs(data[k:N-m+k]-data[i+k])
    dj = np.max(dij[:,0:m],axis=1)
    dj1 = np.max(dij,axis=1)
    d = np.where(dj <= R)
    d1 = np.where(dj1 <= R)
    nm = d[0].shape[0]-1 # subtract the self match
    Bm[i] = nm/(N-m)
    nm1 = d1[0].shape[0]-1 # subtract the self match
    Am[i] = nm1/(N-m)

  Bmr = np.sum(Bm)/(N-m)
  Amr = np.sum(Am)/(N-m)

  return -np.log(Amr/Bmr)


import numpy as np
from itertools import product

def count_matching_determinant_filled(x, y, d):
    """xとyのうち重複を許して埋めた3×3正方行列の行列式と与えられた行列式とが一致する総数を返す
    x : 任意の値
    y : 任意の値
    d : 行列式
    """
    repeated_permutation_square = fill_squares([x, y], 3)
    return count_matching_determinant(repeated_permutation_square, d)

def fill_squares(data, n):
    """"与えられたリストの数値から重複を許して埋めた正方行列を返す
    data : 任意を値を持つリスト
    n : 正方行列の行数(列数)
    """
    repeated_permutation = np.array(list(product(data, repeat=n*n)))
    return repeated_permutation.reshape(-1, n, n)

def count_matching_determinant(array, d):
    """配列の行列式と一致する数を返す
    array : 正方行列からなる2次元以上の配列
    d : 配列の行列式との一致を確認したい値
    """
    determinant = np.round(np.linalg.det(array))  # 微小な誤差が発生するため丸めた
    return np.sum(determinant == d)

if __name__ == "__main__":
    import os
    abs_path = os.path.dirname(os.path.abspath(__file__))

    with open(abs_path + "/q1_in.txt") as f:
        rows = f.readlines()

    outputs = [None] * len(rows)

    for i, row in enumerate(rows):
        x, y, d = map(int, row.split(" "))
        outputs[i] = count_matching_determinant_filled(x, y, d)

    with open(abs_path + "/q1_out.txt", "w") as f:
        f.write("\n".join(map(str, outputs)))


######
###BACKGROUND
#Below Section: Imports necessary functions
import numpy as np
import matplotlib.pyplot as graph
import random as rand
import time as watch
pi = np.pi

#FUNCTION: ishit
#PURPOSE: This function is meant to test whether or not a given 2d point is within the unit circle.
#INPUTS: x = x-axis location, y = y-axis location
#OUTPUT: Boolean
def ishit(x, y):
	#Below Section: Calculates (x, y) vector distance from origin using Pythagorean theorem
	hyplength = np.sqrt(x**2 + y**2)

	#Below Section: Returns whether or not length within unit circle or not
	if hyplength >= 1:
		return True
	else:
		return False

#FUNCTION: simdarts
#PURPOSE: This function is meant to simulate a dart game, with the goal of hitting the inner circle.
#INPUTS: Number of times to 'throw' a dart
#OUTPUTS: Arrays of x and y locations where the dart falls each time; estimated pi value
def simdarts(num):
	#Below Section: Simulates given number of dart throws
	xlocs = np.zeros(num) #X-axis locations
	ylocs = np.zeros(num) #Y-axis locations

	#Below generates the random numbers in [0, 1)
	for a in range(0, num):
		xlocs[a] = rand.random()
		ylocs[a] = rand.random()

	#Below Section: Estimates the value of pi using the results
	count = 0
	for b in range(0, num):
		if ishit(x=xlocs[b], y=ylocs[b]):
			count = count + 1

	#Below Section: Returns the calculated values
	return {'xs':xlocs, 'ys':ylocs, 'estpi':(count/1.0/num)}

#FUNCTION: histdarts
#PURPOSE: This function is meant to plot a histogram of the estimated pi value of several dart simulations.
#INPUTS: Number of histogram points; number of darts per simulation
def histdarts(numdarts, numsims):
	#Below Section: Generates several simulations of given number of darts each
	estpis = np.zeros(numsims)
	for c in range(0, numsims):
		estpis[c] = simdarts(num=numdarts)['estpi']

	#Below Section: Graphs results in histogram
	meanstring = 'Mean: {:.4f}, '.format(np.mean(estpis))
	stdstring = 'St. Dev: {.4f}'.format(np.std(estpis))
	graph.hist(estpis, histtype='step', alpha=.6)
	graph.title('Histogram of Pi Estimations')
	graph.suptitle(meanstring+stdstring)
	graph.xlabel('Estimated Pi Values')
	graph.ylabel('Frequencies')
	#graph.savefig('EstPiHist-'+numdarts'Darts-'+numsims+'Sims')
	graph.show()
	graph.close()

#FUNCTION: drawdarts
#PURPOSE: This function is meant to draw the darts for a given simulation within the unit square.
#INPUTS: X- and Y- locations of the simulated darts
def drawdarts(x, y):
	#Below Section: Scatters the simulated darts
	graph.scatter(x, y, color='blue', alpha=.4)

	#Below Section: Plots a circle as a guideline to show boundaries of unit circle
	xcircle = np.linspace(0, 1, 1000)
	ycircle = np.sqrt(1 - xcircle**2)
	graph.plot(xcircle, ycircle, color='cyan', alpha=.2, linestyle='--')
	#graph.savefig('EstPiDraw-'+str(len(x))+'Darts')
	graph.show()
	graph.close()

#FUNCTION: plottime
#PURPOSE: This function is meant to plot the dependence of time upon the number of darts.
#INPUTS: An array containing the number of darts for each simulation
def plottime(numdartsarray):
	#Below Section: Times the dart simulation for each number of darts given
	simtimes = np.zeros(len(numdartsarray))
	for d in range(0, len(numdartsarray)):
		starthere = watch.time() #Start time
		simhere = simdarts(num=numdartsarray[d]) #Simulation
		endhere = watch.time() #End time

		#Below records time taken for current simulation
		simtimes[d] = endhere - starthere

	#Below Section: Plots the time taken
	graph.plot(numdartsarray, simtimes, alpha=.4, color='purple')
	graph.title('Time Dependence of the Number of Darts')
	graph.xlabel('Number of Darts')
	graph.ylabel('Time Dependence')
	graph.show()
	graph.close()

#FUNCTION: plotacc
#PURPOSE: This function is meant to plot the dependence of accuracy upon the number of darts.
#INPUT: An array containing number of darts for each simulation
def plotacc(numdartsarray):
	#Below Section: Determines accuracy for each number of darts given
	simacc = np.zeros(len(numdartsarray))
	for e in range(0, len(numdartsarray)):
		esthere = simdarts(num=numdartsarray[e])['estpi']

		#Below calculates and records current accuracy
		simacc[e] = abs(esthere - pi)

	#Below Section: Graphs the accuracy of pi estimations
	graph.plot(numdartsarray, simacc, alpha=.4, color='orange')
	graph.title('Estimation Accuracy of the Number of Darts')
	graph.xlabel('Number of Darts')
	graph.ylabel('Accuracy of Pi Estimation')
	graph.show()
	graph.close()


import numpy as np
import pandas as pd
import warnings
import scipy
import iqplot
import scipy.stats as st

def log_like_iid_gamma_log_params(params, t):
    """Log likelihood for i.i.d. Gamma measurements with
    input being parameters and data

    Parameters
    ----------
    params : array
        parameters alpha and b.
    t : array
        Array of input data (times)

    Returns
    -------
    output : float
        Log-likelihood.
    """
    alpha, b = params
    if(alpha <= 0 or b <= 0):
        return -np.inf
    return np.sum(st.gamma.logpdf(t, alpha, scale = 1/b))

rg = np.random.default_rng(3252)
def mle_iid_gamma(t):
    """Perform maximum likelihood estimates for parameters for i.i.d.
    gamma measurements, parametrized by alpha, b=1/beta

    Parameters
    __________
    t : array
        data array

    Returns
    __________
    output : tuple
        Parameter estimates for alpha and beta

    """
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")

        res = scipy.optimize.minimize(
            fun=lambda params, t: -log_like_iid_gamma_log_params(params, t),
            x0=np.array([3, 3]),
            args=(t,),
            method='Powell'
        )
    if res.success:
        return res.x
    else:
        raise RuntimeError('Convergence failed with message', res.message)

def gen_gamma(alpha, b, size):
    """
    Parameters
    __________
    alpha : float
        parameter value for alpha

    b : float
        parameter value for b = 1/beta

    size : int
        number of points to sample from the distribution

    Returns
    __________
    output : array
        distribution based on input parameters"""
    return rg.gamma(alpha, 1 / b, size=size)

def log_like(t, b_1, delta_b):
    """Log likelihood for exponential distribution

    Parameters
    __________
    t : array
        data array
    b_1 : parameter for first arrival time
        delta_b : parameter for difference between two arrival times

    Returns
    __________
    output : float
        Log-likelihood.
    """
    res = -b_1 * t + np.log(1 - np.exp(-delta_b*t)) + np.log(b_1 + delta_b) + np.log(b_1) - np.log(delta_b)
    return res
vec_log_like = np.vectorize(log_like)

def log_like_iid_exp_log_params(params, t):
    """Log likelihood for i.i.d. exponential measurements with
    input being parameters.

    Parameters
    ----------
    params : array
        Parameters beta and delta_beta
    t : array
        Array of times.

    Returns
    -------
    output : float
        Log-likelihood.
    """
    b_1, delta_b = params
    # For the calculation, we need to take the values of b and delta_b and sum
    # the log PDF for each value in the data set
    result = 0
    if(b_1 <= 0 or delta_b <= 0):
        return -np.inf
    result = vec_log_like(t, b_1, delta_b)
    return np.sum(result)

def gen_exponential(b, delta_b, size):
    '''Generates exponential values given b and delta_b
    Parameters
    __________
    b : float
        parameter for first arrival time
    delta_b : float
        parameter for difference between two arrival times

    Returns
    __________
    output : float
        Log-likelihood.
    '''
    return (rg.exponential(1/b, size=size) + rg.exponential(1/(b + delta_b), size = size))

def mle_iid_exp(t):
    """Perform maximum likelihood estimates for parameters for i.i.d.
   exponentially distributed measurements, parametrized by beta, delta_beta

   Parameters
   ___________
   t : array
       input array of times

   Returns
   ___________
   output : tuple
       parameters
   """

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")

        res = scipy.optimize.minimize(
            fun=lambda params, t: -log_like_iid_exp_log_params(params, t),
            x0=np.array([1, 1]),
            args=(t,),
            method='Powell'
        )
    if res.success:
        return res.x
    else:
        raise RuntimeError('Convergence failed with message', res.message)

def gamma_cdf(t, alpha, beta, loc = 0):
    """Calculate the cdf for a gamma distribution
    Parameters
    _________
    t : array
        input array of points to calculate cdf for
    alpha : float
        alpha parameter
    beta : float
        1/b for gamma distribution
    loc : float (optional), default = 0
        center of distribution

    Returns
    _________
    output : array
        cdf for each point in t
    """

    cdf = st.gamma.cdf(t, alpha, loc=0, scale=1/beta)
    return cdf

def model_cdf(t, beta_1, delta_beta):
    """calculates cdf for our custom exponential distribution
    Parameters
    _________
    t : array
        input array of points to calculate cdf for
    beta_1 : float
        parameter for first arrival time
    beta_2 : float
        parameter for second arrival time

    Returns
    _________
    output : array
        cdf for each point in t
    """
    beta_2 = beta_1 + delta_beta
    if np.isclose(beta_1, beta_2):
        return st.gamma.cdf(2, loc=0, scale=1 / beta_1)

    cdf = (1 - np.exp(-beta_1 * t)) / beta_1 - (1 - np.exp(-beta_2 * t)) / beta_2

    return beta_1 * beta_2 * cdf / (beta_2 - beta_1)

def overlay_models(data, q, mle_params, cdf_fun = gamma_cdf, exp_color = 'green', theor_color = 'gray'):
    """plots a comparison between experimental ECDF and theoretical
    Parameters
    _________
    data : array
        input data array
    q : string
        quantiative axis label for plot
    mle_params : tuple
        parameter estimates to be used for given model
    cdf_fun : function (optional), default = gamma_cdf
        function to use to calculate CDFs
    exp_color : string (optional), default = 'green'
        color to use for experimental ECDF
    theor_color : string (optional), default = 'gray'
        color to use for theoretical CDF using our model

    Returns
    _________
    output : bokeh figure
        figure containing the ECDFs overlaid
    """

    p = iqplot.ecdf(data, q=q, conf_int=True, palette = [exp_color])

    t_theor = np.linspace(0, 2000, 200)
    cdf = cdf_fun(t_theor, *mle_params)
    p.line(t_theor, cdf, line_width=1, color= theor_color)
    return p


# -*- coding: utf-8 -*-
import numpy as np

def flip_signs(A, B):
    """
    utility function for resolving the sign ambiguity in SVD
    http://stats.stackexchange.com/q/34396/115202
    """
    signs = np.sign(A) * np.sign(B)
    return A, B * signs

class PCA(object):
    """
    Principal Component Analysis

    Parameters
    ----------
    n_components : int
        Number of components to keep.
        if n_components is not set all components are kept

    Attributes
    ----------
    explained_variance : array, [n_components]
        The amount of variance explained by each of the selected components.

    explained_variance_cumsum : array, [n_components]
        Cumulative sum of explained variance for every component.

    total_var : float
        Total variance explained. Equal to `sum(explained_variance)`

    mean_ : array, [n_features]
        Per-feature empirical mean, estimated from the training set.
        Equal to `X.mean(axis=0)`.

    n_components_ : int
        The estimated number of components.

    Example usage
    -------------
    X = np.random.rand(14,88)  # 2D ndarray
    model = PCA(2)
    eig_projection = model.eigen(X)
    svd_projection = model.svd(X)
    svd_projection2D = model(X)  # callable class
    eig_projection2D = model(X, method='eig')  # eigenvector method
    svd_projection2D[0]  # first component with max variance
    svd_projection2D[-1]  # last component with least variance
    """
    def __init__(self, n_components=None):
        self.n_components = n_components

    def __call__(self, X, method=None):
        if method is None:
            return self.svd(X)
        return self.eigen(X)

    @staticmethod
    def scale(X):
        """
        Standardize features by removing the mean and scaling to unit variance
        """
        X_std = (X - np.mean(X, axis=0)) / np.std(X, axis=0)
        return X_std

    def eigen(self, X):
        """
        Compute the eigenvalues and right eigenvectors of a square array.
        Project them to new feature space
        """
        assert X.ndim == 2
        n_samples, n_features = X.shape
        # Estimate if n_components not specified
        self.n_components_ = self.n_components or n_features
        self.mean_ = np.mean(X, axis=0)

        k = self.n_components

        # Standardizing the data
        X = self.scale(X)

        # Eigendecomposition - Computing Eigenvectors and Eigenvalues

        # Covariance matrix after standardizing the data
        # equals to correlation matrix of raw or std data
        cov_mat = np.corrcoef(X, rowvar=False)

        print('Covariance matrix: \n%s' % cov_mat)

        eig_vals, eig_vecs = np.linalg.eig(cov_mat)

        # Sorting eigenpairs wrt. eigenvalues
        idx = eig_vals.argsort()[::-1]
        eig_vals, eig_vecs = eig_vals[idx], eig_vecs[:, idx]

        # the eigenvalues in decreasing order
        print("Eigenvalues:\n", eig_vals[:k])
        # a matrix of eigenvectors (each column is an eigenvector)
        print("Eigenvectors:\n", eig_vecs[:k])

        # Explained Variance
        self.explained_variance = (eig_vals / np.sum(eig_vals))[:k]
        self.explained_variance_cumsum = np.cumsum(self.explained_variance)
        self.total_var = np.sum(self.explained_variance)

        print('Total variance explained:\n', self.total_var)
        print('Explained variance:\n', self.explained_variance)
        print('Explained variance cumsum:\n', self.explained_variance_cumsum)

        # Projection matrix and dimensionality reduction
        W = eig_vecs[:, 0:k]
        assert W.shape == (n_features, self.n_components_)

        # projections of X on the principal axes are called principal components
        PC_k = X.dot(W)
        assert PC_k.shape == (n_samples, self.n_components_)

        # expose variables as attributes for unit testing
        self.X = X
        self.cov_mat = cov_mat
        self.eig_vals = eig_vals
        self.eig_vecs = eig_vecs

        return PC_k

    def svd(self, X):
        """
        Singular Value Decomposition
        """
        assert X.ndim == 2
        n_samples, n_features = X.shape
        # Estimate if n_components not specified
        self.n_components_ = self.n_components or n_features
        self.mean_ = np.mean(X, axis=0)

        k = self.n_components

        # Standardizing the data
        X = self.scale(X)

        # we now perform singular value decomposition of X
        # "economy size" (or "thin") SVD
        U, s, Vt = np.linalg.svd(X, full_matrices=False)
        V = Vt.T
        S = np.diag(s)

        # 1) columns of V are principal directions/axes.
        # np.testing.assert_allclose(*flip_signs(V, principal_axes))

        # 2) columns of US are principal components
        # np.testing.assert_allclose(*flip_signs(U.dot(S), principal_components), rtol=1e-04)

        # 3) singular values are related to the eigenvalues of covariance matrix
        # np.testing.assert_allclose((s ** 2) / (n - 1), eig_vals)

        eig_vals = (s ** 2) / (n_features - 1)

        # Explained Variance
        self.explained_variance = (eig_vals / np.sum(eig_vals))[:k]
        self.explained_variance_cumsum = np.cumsum(self.explained_variance)
        self.total_var = np.sum(self.explained_variance)

        # dimensionality reduction
        US_k = U[:, 0:k].dot(S[0:k, 0:k])
        assert US_k.shape == (n_samples, self.n_components_)

        # expose variables as attributes for unit testing
        self.X = X
        self.U = U
        self.s = s
        self.Vt = Vt
        self.V = V
        self.S = S

        return US_k


import numpy as np
from scipy.optimize import minimize
from collections import namedtuple

def coordinate_descent_minimize(f, x0, max_iters=10, max_tol=1e-9, **kw_minimize_args):
    """
    coordinate descent minimization, using scipy.minimize for each coordinate in
    consecutive order while fixing the rest.

    f,x0: just like scipy.minimize
    max_iter: maximum number of iterations. each iteration will loop over all coordinates
              calling scipy.minimize each time.
    max_tol:  maximum solution vector change under which iterations will continue until
              reaching max_iter. if the change of the solution vector in consecutive
              iterations is <max_tol optimization will finish.

    returns:  a dict object with optimization information. "x" will contain the final solution.

    """
    x = np.r_[x0].copy()
    x_hist = [x.copy()]
    message = "reached max_iters=%d"%max_iters
    for t in range(max_iters):
        last_x = x.copy()
        rset = []
        for i in range(len(x)):
            def fi(xi):
                x[i] = xi
                return f(x)
            r = minimize(fi, x[i], **kw_minimize_args)
            x[i] = r.x
            rset.append(r)
        x_hist.append(x.copy())
        print (np.linalg.norm(x-last_x) )
        if np.linalg.norm(x-last_x) < max_tol:
            message = "finished after %s iterations and convergence reached at %1.e tol"%(t+1,max_tol)
            break
    ret = {"coord_success": [r.success for r in rset],
           "coord_messages": [r.message for r in rset],
           "x": x,
           "x_history": np.r_[x_hist],
           "success": np.alltrue([r.success for r in rset]),
           "message": message}

    rs = namedtuple("coordinate_descent_result",  " ".join(list(ret.keys())))
    ret = rs(**ret)
    return ret


import numpy as np

def normalize_transform(z, match_array=None, total=1.):
    """
    Takes an array and turns them into positive weights.  This transform is scale invariant.
    """
    if match_array is not None:
        z, _ = np.broadcast_arrays(z, match_array)
    w = z**2.
    return total*w/w.sum()

def normalize_alternate_transform(z, total=1., add_element=False):
    """
    Takes an array and turns them into positive weights.  This transform is sensitive to scale.
    Due to this, the last element is determined by the rest of the array as the sum must be 1.
    add_element=False -- ignores the last given value and overwrites it with the remainder
    add_element=True -- adds an element to the end of the array for the remainder
    """
    s = np.sin(z)**2.
    if add_element:
        s = np.append(s, 1.)
    else:
        s[-1] = 1.
    c = np.cumprod(np.cos(z)**2.)
    if add_element:
        c = np.append(1., c)
    else:
        c = np.roll(c, 1)
        c[0] = 1.
    return total*s*c

def squared_transform(z, offset=0.):
    """
    Takes an array and makes it non-negative (or >= offset) via squaring.
    """
    return z**2. + offset

def ascending_nonnegative_transform(z, offset=0., nonnegative_transform=squared_transform):
    """
    Takes an array and makes it ascend in value by nonnegative_transform and incrementing by the previous element.
    """
    x = nonnegative_transform(z)
    y = np.roll(x, 1)
    y[0] = offset
    return x + y.cumsum()

def sine_bounded_transform(z, lower=-1., upper=1.):
    """
    Takes an array and makes it bound by lower and upper limits via sine:
    z = -1 corresponds to the lower and z = 1 the upper.
    """
    center = (upper+lower) / 2.
    full_width = upper - lower
    return full_width/2.*np.sin(np.pi/2.*z) + center

def sigmoid_bounded_transform(z, lower=-1., upper=1.):
    """
    Takes an array and makes it bound by lower and upper limits via sigmoid:
    z = -1 corresponds to the lower and z = 1 the upper.
    """
    center = (upper+lower) / 2.
    full_width = upper - lower
    return full_width*(1./(1.+np.exp(-z))-0.5) + center

def reciprocal_quadratic_bounded_transform(z, lower=-1., upper=1.):
    center = (upper+lower) / 2.
    full_width = upper - lower
    return full_width*(1./(z**2+1)-0.5) + center

def ascending_bounded_box_transform(z, lower=-1., upper=1., bounded_transform=sine_bounded_transform):
    """
    Takes an array and makes each element succesively lower bounded by the previous value via sine (by default).
    lower and upper correspond to the global lower and upper limit on the vector, i.e. lower affects the
    first element and upper bounds all of them.
    """
    x = np.zeros_like(z, dtype=np.float)
    for i, val in enumerate(z):
        if i == 0:
            prev = lower
        else:
            prev = x[i-1]
        x[i] = bounded_transform(val, lower=prev, upper=upper)
    return x

# TODO write unit tests


# import the important modules.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import skimage.io

# This script will simulate diffusion of a particle over time. While we can
# solve the diffusion equation analytically, this will serve as a fun way to
# learn how to write stochastic simulations.

# We'll begin by considering the diffusion of a single particle in one
# dimension. We will say that at any point in time, the particle has an equal
# probability of taking a step to the left as it can take a step to the right.
# Let's takea  look at the trajectory of a single particle.

# We'll seed the random number generator such that our results will be
# the same each time we run it.
#np.random.seed(42)

# Define the nubmer of steps the simulation will run.
n_steps = 1000

# Define the stepping probability.
p = 0.5  # Equal probability of stepping right or left.

# We can simulate the random walk by flipping a coin at each step and testing
# its value with the probability.

position = 0
displacement = np.zeros(n_steps)

for i in range(n_steps):
    # Flip a coin.
    flip = np.random.rand()

    # Test what the value of this flip is.
    if flip < p:
        position = position + 1
    else:
        position = position - 1

    # We want some way to store the position of our walker at this step.
    displacement[i] = position

# And that's it! Let's plot eh trajectory as a function of time (step number)
# and see where it went.

plt.figure()
time = np.arange(0, n_steps, 1)
plt.plot(time, displacement)
plt.xlabel('time (number of steps)')
plt.ylabel('position')
plt.show()

# It's not very useful to see where a single particle diffused. Let's up our
# number of walkers to something huge. That will be representative of the
# behavior of our system. Let's modify our above loop to do this.
n_simulations = 1000
displacement = np.zeros((n_simulations, n_steps))
for i in range(n_simulations):

    # Reset the position for each simulation
    position = 0

    # Loop through each step.
    for j in range(n_steps):

        # Flip a coin.
        flip = np.random.rand()
        if flip < p:
            position = position + 1
        else:
            position = position - 1

        # Keep track of the displacement for each walker.
        displacement[i, j] = position

# Now, let's loop through each simulation and plot the trajectory.
plt.figure()
for i in range(n_simulations):
    plt.plot(time, displacement[i, :])

plt.xlabel('time (number of steps)')
plt.ylabel('position')
plt.show()

# Now we can see a bunch of different trajectories. Let's look at the
# distribution of ending positions. We can make a new array using the
# sotred displacements from the previous simulation.
ending_position = np.zeros(n_simulations)
for i in range(n_simulations):
    ending_position[i] = displacement[i, -1]

# Plot the distribution.
plt.figure()
plt.hist(ending_position, bins=100)
plt.xlabel('ending position')
plt.ylabel('counts')
plt.show()

# Let's see how the displacemtn would change as a function of the step number.
# In our simulation, we are storing positions that are both positive and
# netative. If we wanted to get an idea of the average distance traveled, we
# can simply square the final position and take the average of all of the
# simulations.
n_steps = [10, 100, 500, 1000, 5000, 10000]
final_position = np.zeros(n_simulations)
msd = np.zeros_like(n_steps)
for i in range(len(n_steps)):
    for j in range(n_simulations):
        position = 0
        for k in range(n_steps[i]):
            # Perorm the coin flip.
            flip = np.random.rand()
            if flip < p:
                position = position + 1
            else:
                position = position - 1

        # Save the final position.
        final_position[j] = position

    # Compute and store the mean square displacement.
    msd[i] = np.mean(final_position**2)

# Let's look at how it scales with time.
plt.figure()
plt.loglog(n_steps, np.sqrt(msd), 'o', label='simulation')

# We predict the root mean squared displacement would scale linear with the
# root of the number of steps we've taken
n_vec = np.logspace(-1, 5, 500)
plt.loglog(n_vec, np.sqrt(n_vec), '-', label='data')
plt.xlabel('time (number of steps)')
plt.ylabel('mean squared displacement')
plt.show()


import numpy as np

def eq2ang(ra, dec):
    """
    convert equatorial ra,dec in degrees to angular theta, phi in radians
    parameters

    ----------
    ra: scalar or array
        Right ascension in degrees
    dec: scalar or array
        Declination in degrees
    returns
    -------
    theta,phi: tuple
        theta = pi/2-dec*D2R # in [0,pi]
        phi   = ra*D2R       # in [0,2*pi]
    """
    dec = dec*np.pi/180
    ra  = ra*np.pi/180
    theta = np.pi/2 - dec
    phi  = ra
    return theta, phi

def radec2xy(ra0, dec0, ra, dec, radians=False):
    """
    Gnomonic projection of (ra, dec) coordinates to tangent plane about
    the point (ra0, dec0).

    Parameters
    ----------
    ra0 : float
        RA coordinate of projection origin [degrees]
    dec0 : float
        DEC coordinate of projection origin [degrees]
    ra : array_like
        RA value(s) to project [degrees]
    dec : array_like
        DEC value(s) to project [degrees]
    radians : bool, optional
        If True, return radian values with origin at (0,0) instead of RADEC

    Returns
    -------
    x : array_like
        Projected RA value(s) [degrees]
    y : array_like
        Projected DEC value(s) [degrees]
    """
    ra = np.atleast_1d(ra)
    dec = np.atleast_1d(dec)

    # Convert the input coordinates to radians
    x0 = np.deg2rad(ra0)
    y0 = np.deg2rad(dec0)
    alpha = np.deg2rad(ra)
    delta = np.deg2rad(dec)

    # Compute projected values
    denom = (np.cos(y0) * np.cos(delta) * np.cos(alpha - x0) +
             np.sin(y0) * np.sin(delta))
    x = np.cos(delta) * np.sin(alpha - x0) / denom
    y = ((np.cos(y0) * np.sin(delta) -
          np.sin(y0) * np.cos(delta) * np.cos(alpha - x0)) / denom)

    if radians:
        return x, y

    return np.rad2deg(x), np.rad2deg(y)

def xy2radec(center_ra, center_dec, x, y):
    """
    Inverse projection from tangent plane (x, y) coordinates to the sphere
    about the point (center_ra, center_dec)

    Parameters
    ----------
    center_ra : float
        RA coordinate of projection center [degrees]
    center_dec : float
        DEC coordinate of projection center [degrees]
    x : float array_like
        RA value(s) to deproject [degrees]
    y : float array_like
        DEC value(s) to deproject [degrees]

    Returns
    -------
    ra : float array_like
        Deprojected RA value(s) [degrees]
    dec : float array_like
        Deprojected DEC value(s) [degrees]
    """
    # Convert projection center to radians
    x0 = np.deg2rad(center_ra)
    y0 = np.deg2rad(center_dec)

    x = np.atleast_1d(np.deg2rad(x))
    y = np.atleast_1d(np.deg2rad(y))

    # Compute deprojected coordinates
    z = np.sqrt(x * x + y * y)
    c = np.arctan(z)

    # Prevent division by zero
    factor = np.ones(len(z))
    inds = (z != 0)
    factor[inds] = y[inds] / z[inds]

    delta = np.arcsin(np.cos(c) * np.sin(y0) + factor * np.cos(y0) * np.sin(c))
    denom = z * np.cos(y0) * np.cos(c) - y * np.sin(y0) * np.sin(c)
    alpha = x0 + np.arctan2(x * np.sin(c), denom)

    return np.rad2deg(alpha), np.rad2deg(delta)


import numpy as np
from scipy import special

def psilog(x: np.ndarray) -> np.ndarray:
    """
    psi(x) - log(x)
    needed when calculating E[ln[x]] when x is a Gamma variable
    """
    return special.digamma(x) - np.log(x)

def grad_psilog(x: np.ndarray) -> np.ndarray:
    """d_x (psi(x) - log(x)) = psi^1(x) - 1/x

    needed when calculating the inverse of psilog(x)
    by using Newton-Raphson

    see:
    invpsilog(c)
    """
    return special.polygamma(1, x) - 1 / x

def invpsilog(c: np.ndarray) -> np.ndarray:
    """
    Solves the equation

    psi(x) - log(x) = c

    where psi is the digamma function. c must be negative.
    The function calculates an approximate inverse which it uses as
    a starting point to 4 iterations of the Newton-Raphson algorithm.
    """
    c = np.asanyarray(c)

    if not np.all(c < 0):
        raise ValueError("values passed must be negative")

    # approximate starting guess
    # -1/x < psilog(x) < -1/(2x)
    A, beta, gamma = 0.38648347, 0.89486989, 0.78578843
    x0 = -(1 - 0.5 * (1 + A * (-c) ** beta) ** -gamma) / c

    # do 4 iterations of Newton Raphson to refine estimate
    for _ in range(4):
        f0 = psilog(x0) - c
        x0 = x0 - f0 / grad_psilog(x0)

    return x0


import matplotlib.pyplot as plt
import numpy as np

def tunediagram(order=range(1,4),integer=[0,0],lines=[1,1,1,1],colors='ordered',linestyle='-',fig=plt.gcf()):
    '''
    plot resonance diagram up to specified order
    mx + ny = p
    x = (p-ny)/m
    x = 1 where y = (p-m)/n

    EXAMPLE:
       tunediagram(order=[1,2,3])
       tunediagram([1,2,3],integer=[6,8],lines=[0,0,1,1])
       tunediagram([1,2,3],integer=[6,8],lines=[0,0,1,1], colors='black3', linestyle='--')

    INPUT:
      1. order - array of tune orders to plot. e.g. up to 3rd order, [1,2,3]
      2. integers - integer part of the tune to plot in x,y, default is [0,0]. e.g. plot from 6-7 and 9-10, integer=[6,9]
      2. lines - a boolean of which resonance lines to plot. e.g. [vertical,horizontal,sum,diff]. e.g. plot only vert/horz lines, lines = [1,1,0,0]
      4. colors - option to plot lines in different colors. default is ordered. color options only go up to 10th order
        ordered - each order resonance is a different color
        black - all lines are black
        blackX - X here is a number. all resonances X+ will be in black. e.g. black3 means plot resonances 1-2 in color and 3,4,5,... in black
      6. linestyle - linestyle option from matplotlib
      7. fig - pass in a handle to a figure, otherwise things will be plotted in the current figure.

    Written by Levon Dovlatyan
    University of Maryland, Department of Physics
    Oct 2018
    '''
    # define some variables
    pval = 40 # increase for more/higher order lines
    p = np.linspace(0,pval,pval+1)

    qxmin,qymin = integer[0],integer[1]
    qxmax,qymax = qxmin+1,qymin+1

    # define different colors, up to 10th order
    color = ['C0','C1','C2','C3','C4','C5','C6','C7','C8','C9']
    if colors == 'black':
        color = ['k']*10
    elif colors[0:-1] == 'black':
        idx = int(colors[-1])
        color = color[0:idx-1] + (['k']*10)[idx-1:]

    # adjust plot limits
    plt.xlim((qxmin-0.01, qxmax+0.01))
    plt.ylim((qymin-0.01, qymax+0.01))

    # Plotting formula
    # we plot resonances in reverse order
    for i in order[::-1]:
        m = np.linspace(-i,i,2*i+1)
        n1 = (i-np.abs(m))
        n2 = -1*n1
        for j in range(0,m.size,1):
            # check to see equation is divided by 0 or not
            # ver & hor res lines
            if ((n1[j] == 0 and lines[1]) or (m[j] == 0 and lines[0])):
                # vertical lines
                if n1[j] == 0 and lines[1]:
                    plt.vlines(p/m[j],qymin,qymax,color=color[i-1],linestyle=linestyle);
                # horizontal lines
                if m[j] == 0 and lines[0]:
                    plt.hlines(p/n1[j],qxmin,qxmax,color=color[i-1],linestyle=linestyle);
                    plt.hlines(p/n2[j],qxmin,qxmax,color=color[i-1],linestyle=linestyle);
            # sum and dif res lines
            elif not(n1[j] == 0) and not(m[j] == 0):
                # resonance sum lines
                if lines[2]:
                    if np.sign(m[j]) > 0:
                        plt.plot([[qxmin]*p.size,[qxmax]*p.size],[p/n2[j] - np.array(m[j]*qxmin/n2[j]), p/n2[j] - np.array(m[j]*qxmax/n2[j])],color=color[i-1],linestyle=linestyle);
                    else:
                        plt.plot([[qxmin]*p.size,[qxmax]*p.size],[p/n1[j] - np.array(m[j]*qxmin/n1[j]), p/n1[j] - np.array(m[j]*qxmax/n1[j])],color=color[i-1],linestyle=linestyle);
                # resonance dif lines
                if lines[3]:
                    if np.sign(m[j]) > 0:
                        plt.plot([[qxmin]*p.size,[qxmax]*p.size],[p/n1[j] - np.array(m[j]*qxmin/n1[j]), p/n1[j] - np.array(m[j]*qxmax/n1[j])],color=color[i-1],linestyle=linestyle);
                    else:
                        plt.plot([[qxmin]*p.size,[qxmax]*p.size],[p/n2[j] - np.array(m[j]*qxmin/n2[j]), p/n2[j] - np.array(m[j]*qxmax/n2[j])],color=color[i-1],linestyle=linestyle);


import pickle
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import style
import math
from statistics import mean
style.use('fivethirtyeight')

#please change the dataset here
f = open('data1_new.pkl','rb')
#f = open('data2_new.pkl','rb')
#f = open('data3_new.pkl','rb')
data1 = pickle.load(f)
f.close()
da = np.asarray(data1)
X = da[:,0]
Y = da[:,1]

def best_fit_slope_and_intercept(X,Y):
    m = (np.multiply(mean(X),mean(Y)) -mean(np.multiply(X,Y) ))/((mean(X))**2-mean((X)**2))
    b = mean(Y) - m*mean(X)
    return m,b
m,b = best_fit_slope_and_intercept(X,Y)
#print(m,b)
regression_line = [(m*x)+b for x in X]
plt.scatter(X,Y)
plt.plot(X,regression_line)

matrix_x = np.ones((X.shape[0],2))
matrix_x[:,0] = np.ones(X.shape[0])
matrix_x[:,1] = X
L = 5
LI = [[0,0],[0,L]]
model_parameters = np.matmul(np.matmul(np.linalg.inv(np.matmul(matrix_x.T,matrix_x) + LI),matrix_x.T),Y)
print(model_parameters)

plt.plot(X, model_parameters[0] + model_parameters[1]*X, color = 'black')

plt.title("line fitting with Regularization")
#plt.legend(loc = 2)
plt.axis([-150,150,-100,100])
plt.show()


import random
import numpy as np
import pandas as pd
import numpy.matlib
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D
np.set_printoptions(threshold=np.inf)

# Initialize the data distribution
N_labels = 4
N = 100000
priors = np.array([[0.25,0.25,0.25,0.25]])

# Create mean and covariance matrices
mean_matrix = np.zeros((4,3))
mean_matrix[0, :] = [0, 0, 0]
mean_matrix[1, :] = [0, 0, 3]
mean_matrix[2, :] = [0, 3, 0]
mean_matrix[3, :] = [3, 0, 0]
covariance_matrix = np.zeros((4, 3, 3))
covariance_matrix[0,:,:] = np.array([[1, 0, -3], [0, 1, 0], [-3, 0, 15]])
covariance_matrix[1,:,:] = np.array([[8, 0, 0], [0, .5, 0], [0, 0, .5]])
covariance_matrix[2,:,:] = np.array([[1, 0, -3], [0, .5, 0], [-3, 0, 15]])
covariance_matrix[3,:,:] = np.array([[8, 0, 0], [0, 1, 0], [3, 0, 15]])

# Import dataset
dataset = pd.read_csv('Dtest_100000.csv')
# Split features and labels
X = dataset.iloc[:, 0:3].values
y = dataset.iloc[:, 3].values

# Define 0-1 Loss Matrix
loss_matrix = np.ones(shape = [N_labels, N_labels]) - np.eye(N_labels)

# Compute Class conditional PDF
P_x_given_L = np.zeros(shape = [N_labels, N])
for i in range(N_labels):
    P_x_given_L[i, :] = multivariate_normal.pdf(X,mean = mean_matrix[i, :], cov = covariance_matrix[i, :,:])

# Compute Class Posteriors using priors and class conditional PDF
P_x = np.matmul(priors, P_x_given_L)
ClassPosteriors = (P_x_given_L * (np.matlib.repmat(np.transpose(priors), 1, N))) / np.matlib.repmat(P_x, N_labels, 1)

# Evaluate Expected risk and decisions based on minimum risk
ExpectedRisk = np.matmul(loss_matrix, ClassPosteriors)
Decision = np.argmin(ExpectedRisk, axis = 0)
print("Average Expected Risk", np.sum(np.min(ExpectedRisk, axis = 0)) / N)

# Estimate Confusion Matrix
ConfusionMatrix = np.zeros(shape = [N_labels, N_labels])

for d in range(N_labels):
    for l in range(N_labels):
        ConfusionMatrix[d, l] = (np.size(np.where((d == Decision) & (l == y)))) / np.size(np.where(y == l))

print("Confusion Matrix-")
print(ConfusionMatrix)


import numpy as np
import pandas as pd
from math import log

def entropia(df_dados: pd.DataFrame, nom_col_classe: str) -> float:

    ser_count_col = df_dados[nom_col_classe].value_counts()
    num_total = len(df_dados)
    entropia = 0

    for val_atr, count_atr in ser_count_col.iteritems():
        val_prob = count_atr/num_total
        entropia -= val_prob * (log(val_prob, 2))

    return entropia

def ganho_informacao_condicional(
    df_dados: pd.DataFrame,
    val_entropia_y: float,
    nom_col_classe: str,
    nom_atributo: str,
    val_atributo: float
) -> float:

    """
    Calcula o GI(Y|nom_atributo=val_atributo), ou seja,
    calcula o ganho de informação do atributo 'nom_atributo'
    quando ele assume o valor 'val_atributo'.
    O valor de Entropia(Y) já foi
     e está armazenado em val_entropia_y.
    Dica: A entropia condicional pode ser calculada filtrando
    o DataFrame df_dados.

    df_dados: Dataframe com os dados a serem analisados.
    val_entropia_y: Entropia(Y) (ver slides)
    nom_col_classe: nome da coluna que representa a classe
    nom_atributo: atributo a ser calculado o ganho de informação
    val_atributo: valor do atributo a ser considerado para este calculo
    """
    # substitua os "None"/0 quando necessario para completar o código
    # .em df_dados_filtrado, filtre o df_dados da forma correta -
    # pensando quais
    # elementos considerar na entropia condicional
    # (Y|nom_atributo=val_atributo).
    df_dados_filtrado = df_dados[df_dados[nom_atributo] == val_atributo]

    # use df_dados_filtrado para obter o valor de
    # Entropia(Y|nom_atributo=val_atributo)
    val_ent_condicional = entropia(df_dados_filtrado, nom_col_classe)
    # use val_ent_condicional para calcular o GI(Y|nom_atributo=val_atributo)
    val_gi = val_entropia_y - val_ent_condicional

    # para testes:
    # print("GI({classe}| {atr}={val}) =
    # {val_gi}".format(classe=nom_col_classe,atr=nom_atributo,val=val_atributo,val_gi=val_gi))

    return val_gi

def ganho_informacao(
    df_dados: pd.DataFrame,
    nom_col_classe: str,
    nom_atributo: str
) -> float:

    """
        Calcula GI(Y| nom_atributo), ou seja, o ganho de informação do
        atributo nom_atributo.

        df_dados: DataFrame com os dados a serem analisados.
        nom_col_classe: nome da coluna que representa a classe
        nom_atributo: atributo a ser calculado o ganho de informação
        val_atributo: valor do atributo a ser considerado para este calculo
    """
    # Muito similar ao codigo da entropia, mas aqui você deverá navegar sobre
    # os possiveis valores do atributo nom_atributo para calcular o infoGain
    # (usando a função ganho_informacao_condicional)
    # substitua os "None"/0 quando necessario para completar o código
    # atenção nessa linha abaixo, qual é o valor que temos que colocar em None?
    # o que precisamos contabilizar dessa vez?
    # media ponderada do ganho de informação de cada valor de um atributo
    ser_count_col = df_dados[nom_atributo].value_counts()
    val_entropia_y = entropia(df_dados, nom_col_classe)
    num_total = len(df_dados)
    val_info_gain = 0
    for val_atr, count_atr in ser_count_col.iteritems():
        val_prob = count_atr/num_total
        val_info_gain += val_prob * (ganho_informacao_condicional(
            df_dados,
            val_entropia_y,
            nom_col_classe,
            nom_atributo,
            val_atr
            ))
        # print("GI("+nom_col_classe+"| "+nom_atributo+"="+val_atr+") =
        # "+str(val_gi_val))

    return val_info_gain


# Dynamic programming
# maximize 16a + 19b + 23c + 28d
# subject to 2a + 3b + 4c + 5d <= 7
# a,b,c,d in {0, 1}
import numpy as np
import time

def DP(k, j, v, w):
    DP.counter += 1
    if j == 0:
        return 0
    elif w[j-1] <= k:
        return max(DP(k, j - 1, v, w), v[j-1] + DP(k - w[j-1], j - 1, v, w))
    else:
        return DP(k, j - 1, v, w)

DP.counter = 0
values = np.array([16, 19, 23, 28, 18, 24, 5, 15, 17, 37, 21, 14, 9, 24, 11, 58, 28, 22, 18,
                   13, 33,16, 19, 23, 28, 18, 24, 5, 15, 17, 37, 21, 14, 9, 24, 11, 58, 28, 22, 18, 13, 33,
                   16, 19, 23, 28, 18, 24, 5, 15, 17, 37, 21,  13, 33,16, 19, 23, 28, 18, 24, 5, 15, 17])
weights = np.array([2, 3, 4, 5, 3, 4, 1, 2, 2, 6, 4, 1, 1, 4, 1, 7, 5, 4, 3, 2, 6, 2, 3, 4, 5, 3, 4, 1, 2, 2, 6,
                    4, 1, 1, 4, 1, 7, 5, 4, 3, 2, 6,
                    2, 3, 4, 5, 3, 4, 1, 2, 2, 6, 4, 1, 1, 4, 1, 7, 5, 4, 3, 2, 6, 2, 3, 4, 5, 3, 4, 1, 2, 2])

K = 7
N = values.size
selection = np.zeros(N)

start_time = time.time()
x = DP(K, N, values, weights)

time_taken = (time.time()*1000 - start_time*1000)
print("The optimal value is: %i." % x)
print("The function DP(k, j, v, w) has been called: %i times." % DP.counter)
print("Time taken: %s milliseconds" % time_taken)


#!/usr/bin/python

from sympy import *
init_printing()

import matplotlib.pyplot as plt

t, s = symbols('t, s')
a = symbols('a', real=True, positive=True)
f = exp(-a*t)

def lt(f):
	return laplace_transform(f, t, s, noconds=True)

def inv_lt(F):
	return inverse_laplace_transform(F, s, t)

res_lt = laplace_transform(f, t, s, noconds=True)
print(res_lt)

res_invlt = inv_lt(res_lt).subs({a: 2})
print(res_invlt)

res0 = res_invlt.subs({t: 0.0})
res1 = res_invlt.subs({t: 0.1})
res2 = res_invlt.subs({t: 0.2})
res3 = res_invlt.subs({t: 0.3})

print(res0)
print(res1)
print(res2)
print(res3)


from __future__ import division
from math import *
import numpy as np

class LinearSolver():
    methods = ['GE', 'GE_pivoted', 'doolittle', 'crout', 'cholesky']

    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.L = None
        self.U = None
        self.x = None

    @staticmethod
    def pivot(a, b, i):
        '''Partial Pivioting at i, i'''

        m = max(a[i:, i])
        for j in range(i + 1, a[0].size):
            if m == a[j, i]:
                a[..., i:][[i, j]] = a[..., i:][[j, i]]
                b[[i, j]] = b[[j, i]]
                break

    @staticmethod
    def solve(a, b):
        '''Expected a to be upper triangular square matrix'''

        n = b.size
        x = np.ndarray(shape=b.shape, dtype=b.dtype)
        for i in range(n - 1, -1, -1):
            x[i] = (b[i] - a[i, i + 1:].dot(x[i + 1:])) / a[i, i]
        return x

    @staticmethod
    def LU(a, l, u):
        '''LU decomposition by diag(1) as ones'''

        n = a[0].size
        for i in range(n):
            u[i, i:] = a[i, i:] - l[i, :i].dot(u[:i, i:])
            l[i + 1:, i] = a[i + 1:, i] - l[i + 1:, :i + 1].dot(u[:i + 1, i])
            l[i + 1:, i] /= u[i, i]

    @staticmethod
    def solveLU(l, u, b):
        '''Solves for x when L & U are given
        Depends on LinearSolver.solve'''

        tempx = LinearSolver.solve(l[::-1, ::-1], b[::-1])
        return LinearSolver.solve(u, tempx[::-1])

    def GE(self, pivot=0):
        a = self.a.copy()
        b = self.b.copy()
        n = b.size
        for i in range(0, n - 1):
            if pivot == 1:
                LinearSolver.pivot(a, b, i)

            for j in range(i + 1, n):
                b[j] -= b[i] / a[i, i] * a[j, i]
                a[j] -= a[i] / a[i, i] * a[j, i]

        self.x = LinearSolver.solve(a, b)

    def GE_pivoted(self):
        self.GE(1)

    def doolittle(self):
        self.L, self.U = np.identity(self.b.size), np.identity(self.b.size)

        LinearSolver.LU(self.a, self.L, self.U)
        self.x = LinearSolver.solveLU(self.L, self.U, self.b)

    def crout(self):
        self.L, self.U = np.identity(self.b.size), np.identity(self.b.size)

        LinearSolver.LU(self.a.transpose(), self.L, self.U)
        self.L, self.U = self.U.transpose(), self.L.transpose()
        self.x = LinearSolver.solveLU(self.L, self.U, self.b)

    def cholesky(self):
        n = self.b.size
        self.L = np.identity(n)
        a = self.a
        l, u = self.L, self.L.transpose()
        for i in range(n):
            l[i, i] = sqrt(a[i, i] - np.sum(l[i, :i]**2))
            l[i + 1:, i] = a[i + 1:, i] / l[i, i]
        self.x = LinearSolver.solveLU(l, u, self.b)

def main():
    method_names = ['GE; without pivoting', 'GE; with patial pivoting',
                    'LU decomposition by Doolittle method',
                    'LU decomposition by Crout method',
                    'Cholesky decomposition (for symmetric positive definite matrix)']

    with open("q1.txt", "r") as f:
        n = int(f.readline())
        a, b = np.ndarray(shape=(n, n)), np.ndarray(shape=(n))
        for i, line in enumerate(f):
            a[i] = map(float, line.split()[:-1])
            b[i] = float(line.split()[-1])

    for i, name in enumerate(method_names):
        print 'press ', i, 'for ', name
    choice = int(raw_input())

    solver = LinearSolver(a, b)
    getattr(solver, LinearSolver.methods[choice])()

    with open("out.txt", "w") as fout:
        if solver.x is not None:
            np.savetxt(fout, solver.x, fmt='%f',
                       header='x', footer='\n', comments='')

        if solver.L is not None:
            np.savetxt(fout, solver.L, fmt='%f',
                       header='L', footer='\n', comments='')

        if solver.U is not None:
            np.savetxt(fout, solver.U, fmt='%f',
                       header='U', footer='\n', comments='')

if __name__ == '__main__':
    main()