import numpy as np
import time

def create_tridiag(a, b, c, n):
    A = np.zeros((n, n))
    for i in range(n):
        A[i][i] = b
        if i < n - 1:
            A[i + 1][i] = a
            A[i][i + 1] = c
    return A

def solve_gauss(A, B):
    n = len(A)
    det = 1
    for i in range(n):
        # Find Maximum Row
        maxRow = i
        for j in range(i + 1, n):
            if abs(A[j][i]) > abs(A[maxRow][i]):
                maxRow = j
            # Swap Rows
        if maxRow != i:
            det *= -1
            for k in range(0, n):
                temp = A[i][k]
                A[i][k] = A[maxRow][k]
                A[maxRow][k] = temp
            temp = B[maxRow]
            B[maxRow] = B[i]
            B[i] = temp

        # Make upper diagonal
        for k in range(i + 1, n):
            t = -A[k][i] / A[i][i]
            for c in range(i, n):
                A[k][c] += t * A[i][c]
            B[k] += t * B[i]

    # Solve equation Ax=b for an upper triangular matrix A
    X = [0 for i in range(n)]
    for i in range(n - 1, -1, -1):
        X[i] = B[i] / A[i][i]
        for k in range(i - 1, -1, -1):
            B[k] -= A[k][i] * X[i]
        det *= A[i][i]
    return det, X

'''___Main___'''

n = 50
a = create_tridiag(1, 4, -1, n)
b = [1 for i in range(n)]
start = time.time()
d, result = solve_gauss(a, b)
end = time.time()
print("Determinant: ", d)
print("Result: ", result)
print("NumPy Result: ", np.linalg.solve(a, b))
print("Time: ", end - start)


#---------------------------------------------------------------------------------------------------#
Chapter 1 - Graphical exploratory data analysis

#---------------------------------------------------------------------------------------------------#
#Axis labels!

# Plot histogram of versicolor petal lengths
_ = plt.hist(versicolor_petal_length)

# Label axes
plt.xlabel('petal length (cm)')
plt.ylabel('count')

# Show histogram

plt.show()

#---------------------------------------------------------------------------------------------------#
#Adjusting the number of bins in a histogram

# Import numpy
import numpy as np

# Compute number of data points: n_data
n_data = len(versicolor_petal_length)

# Number of bins is the square root of number of data points: n_bins
n_bins = np.sqrt(n_data)

# Convert number of bins to integer: n_bins
n_bins = int(n_bins)

# Plot the histogram
_ = plt.hist(versicolor_petal_length, bins=n_bins)

# Label axes
_ = plt.xlabel('petal length (cm)')
_ = plt.ylabel('count')

# Show histogram
plt.show()

#---------------------------------------------------------------------------------------------------#
#Plotting the ECDF

# Compute ECDF for versicolor data: x_vers, y_vers
x_vers, y_vers = ecdf(versicolor_petal_length)

# Generate plot
_ = plt.plot(x_vers, y_vers, marker='.', linestyle='none')

# Make the margins nice
plt.margins(0.02)

# Label the axes
_ = plt.xlabel('petal length (cm)')
_ = plt.ylabel('ECDF')

# Display the plot
plt.show()

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

#Computing the ECDF
def ecdf(data):
    """Compute ECDF for a one-dimensional array of measurements."""

    # Number of data points: n
    n = len(data)

    # x-data for the ECDF: x
    x = np.sort(data)

    # y-data for the ECDF: y
    y = np.arange(1, n+1) / n

    return x, y

#---------------------------------------------------------------------------------------------------#
#Comparison of ECDFs

# Compute ECDF for versicolor data: x_vers, y_vers
x_vers, y_vers = ecdf(versicolor_petal_length)
x_set, y_set = ecdf(setosa_petal_length)
x_virg, y_virg = ecdf(virginica_petal_length)
# Generate plot
_ = plt.plot(x_vers, y_vers, marker='.', linestyle='none')
_ = plt.plot(x_set, y_set, marker='.', linestyle='none')
_ = plt.plot(x_virg, y_virg, marker='.', linestyle='none')
# Make the margins nice
plt.margins(0.02)

# Annotate the plot
plt.legend(('setosa', 'versicolor', 'virginica'), loc='lower right')
_ = plt.xlabel('petal length (cm)')
_ = plt.ylabel('ECDF')

# Display the plot
plt.show()

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

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

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

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

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

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

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

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

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

Statistical Thinking in Python (Part 1)/Chapter 1 - Graphical exploratory data analysis.py


# coding: utf-8

# # What to Input
#
# Graph: Your implementation can expect your graph to be submitted in adjacency matrix format, through a text file (call it graph.txt). Here is an example text filePreview the document to show you can provide adjacency matrix as input. This document will contain graph structure in matrix format, with three columns (I,j,k). Each row will denote information for a cell. e.g. a row of the form "i j k" denotes that the matrix contains a value k at the row i column j.  The value k=1 denotes that there is a link from document (node) i to document (node) j, else the value of k =0.
#
# Parameters: In your implementations use beta=0.85 as the dampening factor.
#
#
# # What to output:
#
#  Your code should give out the Rank Vector (after convergence),
#     and number of iterations it took to converge.
#     The rank scores should be rounded to 4 decimal places.

# In[29]:

get_ipython().run_line_magic('pylab', 'inline')
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img=mpimg.imread('graph.png')
imgplot = plt.imshow(img)
plt.show()

# In[30]:

# import library
import numpy as np, pandas as pd
import warnings
warnings.filterwarnings("ignore")

# In[31]:

# Read graph text file form disk
graph = open('./graph.txt', 'r')
data = graph.read() # process graph.txt
graph.close()# closing file
data = data.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').split() # split data and put in one row
# data

# In[32]:

def split(theList):
    for i in range(0, len(theList), 3):
        yield theList[i:i+3]
data = split(data)
data = list(data)
data

# In[33]:

dic = {} #a dictionary object
dicdup = {} #duplicates
for i in data:
    node = i[0]
    if node not in dic:
        dic[node] = 0
    if i[1] not in dic: # for the graph.txt files where only 1 is shown
        dic[i[1]] = 0
    if i[2] == "1": #is there an dic edge?

        if node + "---->" + i[1] not in dicdup: #k=1 dictionary
            dic[node] += 1
            dicdup[node+"---->"+i[1]] = 1
dicdup

# # Matrix M

# In[34]:

m_matrix = pd.DataFrame(columns=dic.keys(), index=dic.keys(), dtype="double").fillna(0)
for i in data:
    if i[2] == "1":
        m_matrix.at[i[1],i[0]] = 1 / dic[i[0]]
print("What is the output for Matrix M? Give (print) the matrix:","\n\n", m_matrix,"\n")
m_matrix = m_matrix.as_matrix() #create a numpy matrix

# # Matrix A

# In[35]:

# A = beta * M + (1-beta)*T
beta = 0.85 #BETA

T = np.matrix([[1/6,1/6,1/6,1/6,1/6,1/6],
               [1/6,1/6,1/6,1/6,1/6,1/6],
               [1/6,1/6,1/6,1/6,1/6,1/6],
               [1/6,1/6,1/6,1/6,1/6,1/6],
               [1/6,1/6,1/6,1/6,1/6,1/6],
               [1/6,1/6,1/6,1/6,1/6,1/6]])
reuslt1= beta * m_matrix
reuslt2=(1-beta)*T
print("What is the output of Matrix A? After applying teleportation. Give (print) the matrix.")
# Matrix_A= reuslt1+reuslt2
Matrix_A=np.add(reuslt1,reuslt2)
print(Matrix_A)

# # Original Rank Vector R

# In[36]:

rj = []
for i in range(0, len(dic)):
    rj.append(1 / len(dic))
original_rank_vector_R = np.matrix(rj).T
print("What is the original rank vector (R)?:","\n\n", original_rank_vector_R, "\n")

# # Converged Rank Vector R

# In[37]:

iterations = 0; #iterations
lastrvec = original_rank_vector_R
currentrvec = np.empty(shape=(len(dic),1))  #(n,1) format for r vectors: n rows, one col
currentrvec.fill(-1) #start while loop with currentrvec values of -1
while(np.allclose(lastrvec, currentrvec, rtol=1e-06, atol=1e-06) == 0):
    lastrvec = original_rank_vector_R
    iterations += 1
#     print(iterations)
    currentrvec =  m_matrix * original_rank_vector_R  # Formula for M :         rank_vector_t_plus_1 = m_matrix * rank_vector_t
    original_rank_vector_R = currentrvec
print(" When you use Matrix M")
print(currentrvec)
print("\n iterations is", iterations)

# In[38]:

iterations = 0; #exponent for iterations
lastrvec = original_rank_vector_R
currentrvec = np.empty(shape=(len(dic),1))
currentrvec.fill(0)
teleport_factor = (1- beta) / len(dic)  #probability that the user will jump
# teleport_factor= round(teleport_factor)
# print("teleport_factor:" ,teleport_factor)
# Formula for A:  rank_vector_t_plus_1 = damp_factor * m_matrix * rank_vector_t + teleport_factor
while(np.allclose(lastrvec, currentrvec, rtol=1e-06, atol=1e-06) == 0):
#     to 6 decimal places
    lastrvec = original_rank_vector_R
    iterations += 1
#     print(iterations , "\t", lastrvec)
    currentrvec = beta * m_matrix * original_rank_vector_R +  teleport_factor
    original_rank_vector_R = currentrvec
print(" When you use Matrix A")
print(currentrvec )
print("\n iterations is", iterations)


import numpy as np

def calculate_probability(values):
    probabilities={}
    unique_values=list(set(values))
    for unique_value in unique_values:
        probabilities[str(unique_value)]=len(values[values==unique_value])/len(values)
    return probabilities

def calculate_entropy(values):
    probabilities=calculate_probability(values)
    entropy=0
    for probability_key in probabilities.keys():
        probability_value=probabilities[probability_key]
        entropy=entropy-1*probability_value*np.log(probability_value)
        entropy=entropy/np.log(np.exp(1))
    return entropy

def calculate_information_gain(x,y):
    information_gain=0
    unique_x_values=set(x)
    for unique_x_value in unique_x_values:
        sub_y=y[x==unique_x_value]
        sub_ratio=len(sub_y)/len(y)
        information_gain=information_gain+sub_ratio*calculate_entropy(sub_y)
    return information_gain

def create_decision_tree(x,y,dimension_level):
    unique_rows=np.unique(x,axis=0)
    #print(x,y)
    if np.shape(unique_rows)[0]<2 or dimension_level==0:
        return y[0]
    entropy=calculate_entropy(y)
    base_information_gain=-1
    selected_dimension=-1
    for i in range(np.shape(x)[1]):
        current_information_gain=entropy-calculate_information_gain(x[:,i],y)
        if base_information_gain<current_information_gain:
            base_information_gain=current_information_gain
            selected_dimension=i
    #print('entropy:',entropy,'information gain:',base_information_gain)
    #print('splitting based on dimension',selected_dimension)
    unique_x_values=set(x[:,selected_dimension])
    #sub-tree routine
    decision_tree={}
    for unique_x_value in unique_x_values:
        #print('unique_x_value:',unique_x_value,'out of',unique_x_values)
        decision_tree['dimension']=selected_dimension
        sub_mask=x[:,selected_dimension]==unique_x_value
        sub_x=x[sub_mask]
        sub_y=y[sub_mask]
        decision_sub_tree=create_decision_tree(sub_x,sub_y,dimension_level-1)
        decision_tree[str(unique_x_value)]=decision_sub_tree
    return decision_tree

def check_fit(x,y,decision_tree):
    matches=0
    for i in range(np.shape(x)[0]):
        current_x=x[i,:]
        decision_sub_tree=decision_tree
        for j in range(0,np.shape(x)[1]):
            if type(decision_sub_tree) is dict:
                current_dimension=int(decision_sub_tree['dimension'])
                decision_sub_tree=decision_sub_tree[str(x[i,current_dimension])]
            else:
                break
        prediction=decision_sub_tree
        if prediction==y[i]:
            matches=matches+1
    accuracy=matches/len(y)
    return accuracy

if __name__=='__main__':
    for i in range(10):
        #generate random dataset
        x=np.floor(np.random.rand(100,32)/0.2)
        y=np.floor(np.random.rand(100)/0.2)

        print(x,y)

        decision_tree=create_decision_tree(x,y,np.shape(x)[1])
        print('decision tree:',decision_tree)

        print('training accuracy:',check_fit(x,y,decision_tree))


'''
This performs some statistical manipulations on distributions given in series
It has no state
'''

from scipy import stats

def compareDistributionsMannWhitney(distA, distB, sig_level, htmlise=False):
    distA.sort()
    distB.sort()
    u_statistic, p_value = stats.mannwhitneyu(distA,distB)

    diver = "\n"
    if htmlise:
        diver = "
\n"

    test_string = "-- Mann-Whitney U Test --" + diver
    test_string +=  "Hypothesis:The distributions are identical" + diver
    test_string += "Method: If the p-value < " + str(round(sig_level,2)) + " we will reject" + diver
    test_string += "Evidence: The p-value is " + str(round(p_value,4)) + diver
    test_string += "(U Statistic=" + str(round(u_statistic,2)) + ")" + diver
    if p_value < sig_level:
        test_string += "Conclusion: We reject the hypothesis, they are not the same."
    else:
        test_string += "Conclusion: We have no evidence to reject the hypothesis."

    return p_value, test_string

def normalityShapiroWilk(distA, sig_level, htmlise=False):
    distA.sort()
    statistic,pvalue = stats.shapiro(distA)

    diver = "\n"
    if htmlise:
        diver = "
\n"

    test_string = "-- Shapiro-Wilk Normality Test --" + diver
    test_string +=  "Hypothesis:The distribution is normal" + diver
    test_string += "Method: If the p-value < " + str(round(sig_level,2)) + " we will reject" + diver
    test_string += "Evidence: The p-value is " + str(round(pvalue,4)) + diver
    test_string += "(Statistic=" + str(round(statistic,2)) + ")" + diver
    if pvalue < sig_level:
        test_string += "Conclusion: Not normal, we reject the hypothesis."
    else:
        test_string += "Conclusion: Normal, we have no evidence to reject the hypothesis."

    return pvalue, test_string


import numpy as np
import matplotlib.pyplot as plt
import skfuzzy as fuzz

x_qual = np.arange(0,11,1)
x_serv = np.arange(0,11,1)
x_tip = np.arange(0,26,1)

qual_lo = fuzz.trimf(x_qual, [0, 0, 5])
qual_md = fuzz.trimf(x_qual, [0, 5, 10])
qual_hi = fuzz.trimf(x_qual, [5, 10, 10])
serv_lo = fuzz.trimf(x_serv, [0, 0, 5])
serv_md = fuzz.trimf(x_serv, [0, 5, 10])
serv_hi = fuzz.trimf(x_serv, [5, 10, 10])
tip_lo = fuzz.trimf(x_tip, [0, 0, 13])
tip_md = fuzz.trimf(x_tip, [0, 13, 25])
tip_hi = fuzz.trimf(x_tip, [13, 25, 25])

fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, figsize=(8, 9))
ax0.plot(x_qual, qual_lo, 'b', linewidth=1.5, label='Bad')
ax0.plot(x_qual, qual_md, 'g', linewidth=1.5, label='Decent')
ax0.plot(x_qual, qual_hi, 'r', linewidth=1.5, label='Great')
ax0.set_title('Food quality')
ax0.legend()

ax1.plot(x_serv, serv_lo, 'b', linewidth=1.5, label='Poor')
ax1.plot(x_serv, serv_md, 'g', linewidth=1.5, label='Acceptable')
ax1.plot(x_serv, serv_hi, 'r', linewidth=1.5, label='Amazing')
ax1.set_title('Service quality')
ax1.legend()

ax2.plot(x_tip, tip_lo, 'b', linewidth=1.5, label='Low')
ax2.plot(x_tip, tip_md, 'g', linewidth=1.5, label='Medium')
ax2.plot(x_tip, tip_hi, 'r', linewidth=1.5, label='High')
ax2.set_title('Tip amount')
ax2.legend()

# Turn off top/right axes
for ax in (ax0, ax1, ax2):
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.get_xaxis().tick_bottom()
    ax.get_yaxis().tick_left()

plt.tight_layout()
plt.show()

qual_level_lo = fuzz.interp_membership(x_qual, qual_lo, 6.5)
qual_level_md = fuzz.interp_membership(x_qual, qual_md, 6.5)
qual_level_hi = fuzz.interp_membership(x_qual, qual_hi, 6.5)

serv_level_lo = fuzz.interp_membership(x_serv, serv_lo, 9.8)
serv_level_md = fuzz.interp_membership(x_serv, serv_md, 9.8)
serv_level_hi = fuzz.interp_membership(x_serv, serv_hi, 9.8)

active_rule1 = np.fmax(qual_level_lo, serv_level_lo)
tip_activation_lo = np.fmin(active_rule1, tip_lo)

tip_activation_md = np.fmin(serv_level_md, tip_md)

active_rule3 = np.fmax(qual_level_hi, serv_level_hi)
tip_activation_hi = np.fmin(active_rule3, tip_hi)

#tip0 = np.zeros_like(x_tip)
# fig, ax0 = plt.subplots(figsize=(8, 3))
# ax0.fill_between(x_tip, tip0, tip_activation_lo, facecolor='b', alpha=0.7)
# ax0.plot(x_tip, tip_lo, 'b', linewidth=0.5, linestyle='--', )
# ax0.fill_between(x_tip, tip0, tip_activation_md, facecolor='g', alpha=0.7)
# ax0.plot(x_tip, tip_md, 'g', linewidth=0.5, linestyle='--')
# ax0.fill_between(x_tip, tip0, tip_activation_hi, facecolor='r', alpha=0.7)
# ax0.plot(x_tip, tip_hi, 'r', linewidth=0.5, linestyle='--')
# ax0.set_title('Output membership activity')
# # Turn off top/right axes
# for ax in (ax0,):
#     ax.spines['top'].set_visible(False)
#     ax.spines['right'].set_visible(False)
#     ax.get_xaxis().tick_bottom()
#     ax.get_yaxis().tick_left()
#     plt.tight_layout()
#
# plt.show()

aggregated = np.fmax(tip_activation_lo, np.fmax(tip_activation_md, tip_activation_hi))
tip = fuzz.defuzz(x_tip, aggregated, 'centroid')

#
# tip_activation = fuzz.interp_membership(x_tip, aggregated, tip)
# fig, ax0 = plt.subplots(figsize=(8, 3))
# ax0.plot(x_tip, tip_lo, 'b', linewidth=0.5, linestyle='--', )
# ax0.plot(x_tip, tip_md, 'g', linewidth=0.5, linestyle='--')
# ax0.plot(x_tip, tip_hi, 'r', linewidth=0.5, linestyle='--')
# ax0.fill_between(x_tip, tip0, aggregated, facecolor='Orange', alpha=0.7)
# ax0.plot([tip, tip], [0, tip_activation], 'k', linewidth=1.5, alpha=0.9)
# ax0.set_title('Aggregated membership and result (line)')
# for ax in (ax0,):
#     ax.spines['top'].set_visible(False)
#     ax.spines['right'].set_visible(False)
#     ax.get_xaxis().tick_bottom()
#     ax.get_yaxis().tick_left()
#     plt.tight_layout()
# plt.show()


import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# making life easier
def l2hnorm(x, h):
    return np.linalg.norm(x)*np.sqrt(h)

# Colelctors for convergence analysis
err = []
hs = []

#Choose here if you want to see the plots for h = 0.2
plot = False

#Choose here if you want to use the asymetrical domain
asym = True

# Creating a matrix for the symetrical case
def make_A(n):
    L = np.diag((n-1)*[2],0)
    L += np.diag((n-2)*[-1],1)
    L += np.diag((n-2)*[-1],-1)
    return L

# Creating a similar matrix for the asymetrical case
def make_A_asym(n):
    L = np.diag((n-1)*[2.0],0)
    L += np.diag((n-2)*[-1.0],1)
    L += np.diag((n-2)*[-1.0],-1)
    L[0,0] = 1
    L[0,1] = -(2/3)
    return L

for k in range(0,4):
    # k refinement
    h = 0.2/(2**k)
    hs.append(h)
    n = int(1/h)

    # creating the domain and a finer domain for the exactg solution
    omega = np.linspace(0,1, n+1)
    omega2 = np.linspace(0,1, 100)
    if asym:
        omega = np.delete(omega, 1)

    # Calling the proper matrix creation
    A = make_A(n) if not asym else make_A_asym(n-1)

    L = A/(h**2)

    # exact solutions
    fn1 = lambda x: -1/2*np.power(x,2)+3/2*x+1
    fn2 = lambda x: -1*np.exp(x)+np.e*x+2

    # inner points of the computational domain
    wh = np.linspace(0+h, 1-h, n-1)
    if asym:
        wh = wh[1:]

    # creating the boundary conditions
    f1 = np.ones(wh.shape[0])
    f1[0] += 1/h/h if not asym else 1/3/h/h
    f1[-1] += 2/h/h

    f2 = np.exp(wh)
    f2[0] += 1/h/h if not asym else 1/3/h/h
    f2[-1] += 2/h/h

    # Solving the system
    u1 = np.linalg.solve(L.copy(),f1)
    u2 = np.linalg.solve(L.copy(),f2)

    # inserting boundary conditions
    u1 = np.insert(u1, 0, 1)
    u1 = np.append(u1, 2)

    u2 = np.insert(u2, 0, 1)
    u2 = np.append(u2, 2)

    # Plotting of the solution
    if k==0 and plot:

        print(L)
        print(f1)
        print(f2)
        plt.plot(omega, u1, '-x', label="numerical")
        plt.plot(omega2, fn1(omega2), label="analytical")
        plt.legend()
        plt.show()

        plt.plot(omega, u2, '-x', label="numerical")
        plt.plot(omega2, fn2(omega2), label="analytical")
        plt.legend()
        plt.show()
        print(u1)
        print(fn1(omega))
        print(u2)
        print(fn2(omega))

    #Convergence analysis of u2
    print(l2hnorm(u1-fn1(omega), h))
    err.append(l2hnorm(u2-fn2(omega),h))
    print(err[-1])

#FInding alphha and C
to_fit = lambda x,a,c: c*np.power(x,a)

fitted = curve_fit(to_fit, hs, err)[0]
print(fitted)

# Plotting Convergence analysis
plt.loglog(hs,err,'-x', label="Global Error")
plt.loglog(hs, to_fit(hs, *fitted), label="Fitted logarithmic function")
plt.legend()
plt.axis('equal')
plt.show()


import numpy as np
from loss import CategoricalCrossEntropy

class ActivationReLU:
    def forward(self, inputs):
        self.output = np.maximum(0, inputs)
        # store the inputs for the backwars pass
        self.inputs = inputs

    def backward(self, dvalues):
        self.dinputs = dvalues.copy()
        # zero gradient where input values are negative
        self.dinputs[self.inputs <= 0] = 0

class ActivationSoftmax:
    def forward(self, inputs):
        # get non-normalized probabilities
        # subtract the map value of the inputs to prevent exploding values
        # when dealing with really large numbers. it does not take much for
        # exponential values to get very large (ie. e^1000 will overflow)
        # since e^-inf == 0 and e^0 = 1, we subtract the largest value to put
        # the input values in the range of -inf to 0 (since largest itself == 0)
        exp_values = np.exp(inputs - np.max(inputs, axis=1, keepdims=True))

        # normalize
        # axis=1 means sum the rows, keepdims=True will keep it in the same dims
        # as the exp_values (ie. wont flatten to a list)
        probabilities = exp_values / np.sum(exp_values, axis=1, keepdims=True)

        self.output = probabilities

    def backward(self, dvalues):
        self.dinputs = np.empty_like(dvalues)

        for index, (single_output, single_dvalues) in enumerate(zip(self.output, dvalues)):
            # flatten out array
            single_output = single_output.reshape(-1, 1)

            # compute jacobian matrix of the output
            jacobian_matrix = np.diagflat(single_output) - np.dot(single_output, single_output.T)

            # calculate the samplewise gradient and add it to the array of sample gradients
            self.dinputs[index] = np.dot(jacobian_matrix, single_dvalues)

# Softmax Classifier - combined Softmax activation and cross-entropy loss
#                      for faster backwards step
class ActivationSoftmaxLossCategoricalCrossentropy:
    def __init__(self):
        self.activation = ActivationSoftmax()
        self.loss = CategoricalCrossEntropy()

    def forward(self, inputs, y_true):
        # compute output
        self.activation.forward(inputs)
        self.output = self.activation.output

        # compute loss
        return self.loss.calculate(self.output, y_true)

    def backward(self, dvalues, y_true):
        # number of samples
        samples = len(dvalues)

        # if labels are one-hot encoded, turn them
        # into discrete values
        if len(y_true.shape) == 2:
            y_true = np.argmax(y_true, axis=1)

        # compute gradient
        self.dinputs = dvalues.copy()
        self.dinputs[range(samples), y_true] -= 1

        # normalize
        self.dinputs = self.dinputs / samples

class ActivationSigmoid:
    def forward(self, inputs):
        self.inputs = inputs
        self.output = 1 / (1 + np.exp(-inputs))

    def backward(self, dvalues):
        self.dinputs = dvalues * (1 - self.output) * self.output


import numpy as np

def halfcos(N: int) -> np.ndarray:
    """
    Create a half-cosine function of length N samples.

    Parameters:
        N: Usually odd.
    Returns:
        h: discrete samples of the half-cosine
    """
    # The amplitude gives unit gain at zero frequency.
    h = np.cos((np.array(range(1, N+1))/(N+1) - 0.5) * np.pi)
    h = h / sum(h)
    return h

'''
Use the np.convolve function in a for loop to convolve a 15-sample half-cosine
with each row of the test image, Lighthouse.
https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.convolve.html

Observe the resulting image Xf and note the increased width and the gradual
fade to black at the edges, caused by conv assuming the signal is zero
outside the range of the input vectors.
'''

def convse(X: np.ndarray, h: np.ndarray) -> np.ndarray:
    """
    Filter rows of image X using filter h with symmetric extension of X.

    Parameters:
        X: Image matrix (Usually 256x256)
        h: Filter coefficients
    Returns:
        Y: Row - Filtered image

    PS: Need to apply `convse` twice to filter rows and columns.
    """
    r, c = X.shape
    m = len(h)
    m2 = m // 2
    if m % 2:
        X = np.pad(X, [(0, 0), (m2, m2)], mode='reflect')
    else:
        X = np.pad(X, [(0, 0), (m2, m2)], mode='symmetric')

    Y = np.zeros((r, c))
    # Loop for each term in h. This loop is the convolution.
    for i in range(m):
        Y = Y + h[i] * X[:, i:i+c]
    return Y


import os
import sys
import numpy
import matplotlib.animation
import matplotlib.pyplot as plt
import scipy.integrate

def E_0(n):
    return n + 0.5

def phi(N, x):
    coef = tuple(0 if i < N else 1 for i in range(N+1))
    return numpy.polynomial.hermite.hermval(x, c=coef) * numpy.exp(-(x**2) / 2) / (numpy.power(numpy.pi, 0.25) * numpy.sqrt(2**N * numpy.math.factorial(N)))

def calculate_H(size, lam, x):
    H = numpy.diag([E_0(n) for n in range(size)])

    Phi = [None for _ in range(size)]
    for i in range(size):
        Phi[i] = phi(i, x)

    for i in range(size):
        for j in range(size):
            H[i,j] += lam * numpy.trapz(Phi[i]*x*x*x*x*Phi[j], x)

    return H, numpy.array(Phi)

def psi(c, x):
    psi = numpy.zeros(len(x), dtype=complex)
    for i in range(len(c)):
        coef = tuple(0 if j < i else 1 for j in range(i+1))
        psi += c[i] / (numpy.power(numpy.pi, 0.25) * numpy.sqrt(2**i * numpy.math.factorial(i))) * \
            numpy.polynomial.hermite.hermval(x, c=coef) * numpy.exp(-(x**2) / 2)
    return psi

def calculate_psi(ncalc, nret, lam, x):
    H, Phi = calculate_H(ncalc, lam, x)
    eigval, eigvec = numpy.linalg.eigh(H)
    psis = []
    for i in range(nret):
        psis.append(psi(eigvec[:,i], x))
    return eigval[:nret], psis

def plot_energies(x, Es, psis, lam):
    fig = plt.figure(figsize=(16,9))
    plt.title("Excited eigenstates of anharmonic oscillator $V(x) = \\frac{{1}}{{2}}x^2 + \\lambda x^4$ for $\\lambda = {}$".format(numpy.round(lam, 2)), fontsize=18)

    plots = []
    for psi in psis:
        plots += plt.plot(x, numpy.abs(psi)**2, linewidth=2)

    plt.legend(plots, Es, title="$E$", loc="upper right")
    plt.grid(linestyle="dotted", alpha=0.5)
    plt.xlabel("$x$", fontsize=16)
    plt.ylabel("$||\\psi(x)||^2$", fontsize=16)

    fig.savefig("task1_lam={}.pdf".format(int(lam*10)), bbox_inches="tight")
    # plt.show()

def plot_per_lam(x, lam_to_Epsi):
    fig, axes = plt.subplots(2, 3, figsize=(16,9))
    fig.suptitle("Excited eigenstates of anharmonic oscillator $V(x) = \\frac{{1}}{{2}}x^2 + \\lambda x^4$")

    n = 0
    axes_to_plots = [[] for _ in range(6)]
    axes_to_energies = [[] for _ in range(6)]
    for lam in lam_to_Epsi:
        n = len(lam_to_Epsi[lam][1])
        for i in range(len(lam_to_Epsi[lam][0])):
            axes_to_plots[i] += axes[int(i/3), i%3].plot(x, numpy.abs(lam_to_Epsi[lam][1][i])**2, linewidth=0.5)
            axes_to_energies[i].append(lam_to_Epsi[lam][0][i])
            print(lam_to_Epsi[lam][0][i])

    print(n)
    for i in range(n):
        axes[int(i/3), i%3].set_title("$n$-th excited eigenstate for $n={}$".format(i))
        axes[int(i/3), i%3].set_xlabel("$x$")
        axes[int(i/3), i%3].set_ylabel("$||\\psi(x)||^2$")
        axes[int(i/3), i%3].grid(linestyle="dotted", alpha=0.5)
        axes[int(i/3), i%3].legend(axes_to_plots[i], numpy.round(axes_to_energies[i], 2), title="$E_{}$".format(i))

    fig.legend(axes_to_plots[0], numpy.round(sorted(lam_to_Epsi.keys()), 2), title="$\\lambda$", loc="right")
    fig.savefig("task1_states_per_n.pdf", bbox_inches="tight")

def task1():
    L = 5
    lams = [0.2*i for i in range(6)]
    ncalc = 16
    nret = 6
    h = 0.01
    x = numpy.linspace(-L, L, int(2*L/h) + 1)

    lam_to_Epsi = {}

    for lam in lams:
        print(lam)
        Es, psis = calculate_psi(ncalc, nret, lam, x)
        lam_to_Epsi[lam] = [Es, psis]
        plot_energies(x, Es, psis, lam)

    plot_per_lam(x, lam_to_Epsi)

def animate(x, states, filename, lam, h, interval_base=10):
    print("Rendering animation ...")
    fig, ax = plt.subplots()
    plt.suptitle("($\\lambda = {0}$)".format(lam))
    plt.xlabel(r"$x$")
    plt.ylabel(r"$|\psi|^2$")

    def animate_func(frame):
        print("{0} | {1}".format(len(states), frame))
        ax.clear()
        ax.set_ylim(bottom=0, top=1)
        ax.plot(x, numpy.abs(states[frame])**2/(h**2))

    animation = matplotlib.animation.FuncAnimation(fig, animate_func, frames=len(states), interval=interval_base)
    animation.save(filename+".mp4")

def plot_animation(x, lam_psits, lams, tau, h):
    axes_to_plots = [[] for _ in range(6)]
    ts = numpy.array([20*i for i in range(6)])
    alphas = numpy.linspace(0.1, 1, len(ts))

    fig, axes = plt.subplots(2,3, figsize=(16,9))
    fig.suptitle("Time evolution of anharmonic oscillator $V(x) = \\frac{{1}}{{2}} x^2 + \\lambda x^4$", fontsize=16)

    for i in range(len(lams)):
        for ti, t in enumerate(ts):
            axes_to_plots[i] += axes[int(i/3), i%3].plot(x, numpy.abs(lam_psits[i][t])**2 *(h**2), 'b-', alpha=alphas[ti])

        axes[int(i/3), i%3].set_title("$\\lambda={}$".format(numpy.round(lams[i], 2)))
        axes[int(i/3), i%3].set_xlabel("$x$")
        axes[int(i/3), i%3].set_ylabel("$||\\psi(x)||^2$")
        axes[int(i/3), i%3].set_ylim(bottom=0, top=1.4)
        axes[int(i/3), i%3].grid(linestyle="dotted", alpha=0.5)

    fig.legend(axes_to_plots[0], ts*tau, title="$t = n\\tau$", loc="right")
    fig.savefig("task2_timeevolution.pdf", bbox_inches="tight")

def task2():
    L = 5
    lams = [0.2*i for i in range(6)]
    ncalc = 15
    nret = 6
    h = 0.01
    x = numpy.linspace(-L, L, int(2*L/h) + 1)
    tau = 0.01
    t1 = 10
    ts = numpy.linspace(0, t1, int(t1/tau) + 1)

    psi0 = phi(0, x)
    lam_psits = []

    for lam in lams:
        print(lam)
        lam_psits.append([])
        Es, psis = calculate_psi(ncalc, nret, lam, x)

        for t in ts:
            lam_psits[-1].append(numpy.zeros(len(x), dtype=complex))
            for n in range(nret):
                lam_psits[-1][-1] += numpy.dot(psis[n], psi0) * numpy.exp(-1j * Es[n] * t) * psis[n]

        lam_psits[-1] = numpy.array(lam_psits[-1])
    # plt.plot(x, numpy.abs(lam_psits[0][0])**2)
    # plt.show()

    # animate(x, lam_psits[-1], "animacija", lams[-1], h)
    plot_animation(x, lam_psits, lams, tau, h)

if __name__ == "__main__":
    os.chdir("hw2_stationary-problem-and-spectral-methods/images/")

    # task1()
    task2()


import copy
import numpy as np

def rowmul(matrix, index, k):
    """
    Multiplies index row with k
    """
    matrix[index, :] = k * matrix[index, :]
    return matrix

def rowadd(matrix, index1, index2, k):
    """
    Adds the index1 row with index2 row which in turn is multiplied by k
    """
    matrix[index1, :] += k * matrix[index2, :]
    return matrix

def update_mat(mat, row):
    '''
    Put the pivots to the first line
    '''
    mat_this = abs(mat)
    max_index = np.argmax(mat_this[row:, row]) + row
    if max_index == row:
        return mat
    else:
        change = copy.deepcopy(mat[max_index, :])
        mat[max_index, :] = mat[row, :]
        mat[row, :] = change
    return mat

def rref(matrix):
    """
    Returns the reduced row echelon form of a Matrix.

    Examples
    ========

    >>>from factorization.matrixtools import rref
    >>>import numpy as np
    >>> a = np.array([[3,7,4],[2,4,5],[6,2,3]])
    >>> rref(a)

    array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

    """
    result_matrix = copy.deepcopy(matrix)
    result_matrix = result_matrix.astype(np.float)
    nrow, ncol = result_matrix.shape
    for i in range(min(nrow, ncol)):
        result_matrix = update_mat(result_matrix, i)
        if np.all(np.round(result_matrix[i, :], decimals=4) == 0):
            continue
        frist_nzero = np.where(np.round(result_matrix[i, :], decimals=4) != 0)[0][0]
        if result_matrix[i, frist_nzero] != 1:
            rowmul(result_matrix, i, 1 / result_matrix[i, frist_nzero])
        rows = [k for k in range(nrow)]
        rows.remove(i)
        for j in rows:
            if (np.round(result_matrix[j, frist_nzero], decimals=4) != 0):
                rowadd(result_matrix, j, i, -result_matrix[j, frist_nzero])
    return result_matrix

def rank(matrix):
    '''
    Return the rank of the matrix

    Examples
    ========

    >>>from factorization.matrixtools import rank
    >>>import numpy as np
    >>> a = np.array([[3,7,4],[2,4,5],[6,2,3]])
    >>> rank(a)

    3
    '''
    result_matrix = copy.deepcopy(matrix)
    rref_state = rref(result_matrix)
    rank_num = 0
    for i in range(rref_state.shape[0]):
        if np.any(np.round(rref_state[i, :], decimals=4) != 0):
            rank_num += 1
    return rank_num

class Mattools():
    '''
    Some basic tools of matrix , contain " rref , rank , columnspace and nullspace"
    '''

    def __init__(self, mat):
        self.mat = copy.deepcopy(mat)
        self.nrow, self.ncol = self.mat.shape
        self.stables = []
        self.pivots = []

    def rref(self):
        '''
        Returns the reduced row echelon form of a Matrix.

        Example
        ========

        >>>from factorization import Matrix
        >>>import numpy as np
        >>>a = np.array([[0,0,0],[3,0,-4],[4,11,-2]])
        >>>test_object = Matrix(a)
        >>>test_object.rank()

        array([[ 1.        ,  0.        , -1.33333333],
       [-0.        ,  1.        ,  0.3030303 ],
       [ 0.        ,  0.        ,  0.        ]])

        See also
        ========

        def rref(matrix)
        '''
        return rref(self.mat)

    def rank(self):
        '''
        Return the rank of the matrix

        Example
        ========

        >>>from factorization import Matrix
        >>>import numpy as np
        >>>a = np.array([[0,0,0],[3,0,-4],[4,11,-2]])
        >>>test_object = Matrix(a)
        >>>test_object.rank()

        2

        See also
        ========

        def rank(matrix)
        '''
        return rank(self.mat)

    def columnspace(self):
        '''
        columnspace is also 值空间

        Example
        ========

        >>>from factorization import Matrix
        >>>import numpy as np
        >>>a = np.array([[0,0,0],[3,0,-4],[4,11,-2]])
        >>>test_object = Matrix(a)
        >>>test_object.columnspace()

        array([[ 0.,  0.],
       [ 3.,  0.],
       [ 4., 11.]])
        '''
        if self.rank() == self.ncol:
            return self.mat
        pivots = []
        result_matrix = copy.deepcopy(self.mat)
        rref_state = rref(result_matrix)
        for i in range(rref_state.shape[0]):
            if np.any(np.round(rref_state[i, :], decimals=4) != 0):
                pivots.append(i)
        columspace_mat = np.zeros([self.nrow, len(pivots)])
        for i in range(len(pivots)):
            pivots_row = rref_state[pivots[i], :]
            for j in range(pivots_row.size):
                if pivots_row[j] == 1:
                    columspace_mat[:, i] = result_matrix[:, j]
                    continue
        return columspace_mat

    def nullspace(self):
        '''
        For nullspace of matrix

        Example
        ========

        >>>from factorization import Matrix
        >>>import numpy as np
        >>>a = np.array([[0,0,0],[3,0,-4],[4,11,-2]])
        >>>test_object = Matrix(a)
        >>>test_object.nullspace()

        array([[ 1.33333333],
       [-0.3030303 ],
       [ 1.        ]])
        '''
        if self.rank() == self.ncol:
            return np.zeros([self.ncol, 1])
        vars = [i for i in range(self.ncol)]
        result_matrix = copy.deepcopy(self.mat)
        rref_state = rref(result_matrix)
        for i in range(rref_state.shape[0]):
            if np.any(np.round(rref_state[i, :], decimals=4) != 0):
                self.pivots.append(i)
        for i in range(len(self.pivots)):
            pivots_row = rref_state[self.pivots[i], :]
            for j in range(pivots_row.size):
                if pivots_row[j] == 1:
                    self.stables.append(j)
                    vars.remove(j)
                    break
        nullspace_mat = np.zeros([self.ncol, self.ncol - len(self.pivots)])
        for i in range(len(self.pivots)):
            pivots_row = rref_state[self.pivots[i], :]
            for k in range(len(vars)):
                nullspace_mat[self.stables[i], k] = -pivots_row[vars[k]]
        for i in range(len(vars)):
            nullspace_mat[vars[i], i] = 1
        return nullspace_mat

if __name__ == "__main__":
    pass


import numpy as np
import scipy.sparse as sps
import scipy.sparse.linalg as spla
from scipy.special import *

def make_matrix_square_root_applier(A, check_error=True):
    A = A.tocsr()
    weights, poles, _ = matrix_inverse_square_root_rational_weights_and_poles(A)
    N = len(weights)

    AA = [A - poles[k]*sps.eye(A.shape[0]) for k in range(N)]
    AA_solvers = [spla.factorized(A) for A in AA]

    def apply_isqrtA(v):
        u = np.zeros(v.shape)
        for k in range(N):
            u = u + weights[k] * AA_solvers[k](v)
        return u

    def apply_sqrtA(v):
        return A * apply_isqrtA(v)

    if check_error:
        v = np.random.randn(A.shape[1])
        u1 = apply_sqrtA(apply_sqrtA(v))
        u2 = A * v
        sqrt_err = np.linalg.norm(u1-u2)/np.linalg.norm(u2)
        print('sqrt_err=', sqrt_err)

    return apply_sqrtA

def matrix_inverse_square_root_rational_weights_and_poles(A, tol=1e-8, N_max=30, verbose=False):
    # Computes the weights and poles for a rational approximation to the inverse square root of a
    # symmetric positive definite matrix.
    max_lambda = spla.eigsh(A, 1, return_eigenvectors=False, which='LM')[0]
    min_lambda = spla.eigsh(A, 1, return_eigenvectors=False, which='SM')[0]
    if verbose:
        print('min_lambda=', min_lambda, ', max_lambda=', max_lambda)

    xx = np.logspace(np.log10(min_lambda), np.log10(max_lambda), 1000)
    for N in range(1,N_max+1):
        weights, poles, rational_function = inverse_square_root_rational_weights_and_poles(min_lambda, max_lambda, N)
        yy = rational_function(xx)
        yy_true = 1./np.sqrt(xx)
        err = np.linalg.norm(yy_true - yy)/np.linalg.norm(yy_true)
        if verbose:
            print('N=', N, ', err=', err)
        if err < tol:
            break
    return weights, poles, rational_function

def inverse_square_root_rational_weights_and_poles(lower_limit_m, upper_limit_M, number_of_rational_terms_N):
    # Computes weights and poles for the rational approximation
    #   1/sqrt(z) = weights[0]/(z - poles[0]) + weights[1]/(z - poles[1]) + ... + weights[N]/(z - poles[N])
    # designed to be accurate on the positive interval [m,M].
    #
    # Adapted version of method 3 from:
    #   Hale, Nicholas, Nicholas J. Higham, and Lloyd N. Trefethen.
    #   "Computing A^α,\log(A), and related matrix functions by contour integrals."
    #   SIAM Journal on Numerical Analysis 46.5 (2008): 2505-2523.
    m = lower_limit_m
    M = upper_limit_M
    N = number_of_rational_terms_N
    k2 = m/M
    Kp = ellipk(1-k2)
    t = 1j * np.arange(0.5, N) * Kp/N
    sn, cn, dn, ph = ellipj(t.imag,1-k2)
    cn = 1./cn
    dn = dn * cn
    sn = 1j * sn * cn
    w = np.sqrt(m) * sn
    dzdt = cn * dn

    poles = (w**2).real
    weights = (2 * Kp * np.sqrt(m) / (np.pi*N)) * dzdt
    rational_function = lambda zz: np.dot(1. / (zz.reshape((-1,1)) - poles), weights)
    return weights, poles, rational_function


"""
Harris Corner Detection
functions:
cv2.
    cornerHarris()
    cornerSubPix()

corners - regions in image with large variation in intensity in all directions
detect by:
    find difference in intensity for a displacement of (u, v) in all directions
    E(u, v) = sum(wrt x,y) w(x, y) [I(x+u, y+v) - I(x, y)]^2
        w is window function, , I(x+u, y+v) shifted intensity, I(x, y) intensity
        window function either rect window or gaussian window (gives weights to underneath pixels)
have to maximize E(u, v) for corner detection
    -> have to maximize 2nd term
        apply taylor expansion, get

        E(u, v) ~= [u v] M [u v]'
        M = sum(wrt x, y) w(x, y) [I_x I_y] x [I_x; I_y]
        I_x and I_y are image derivs in x and y dirs, respectively
            can be found with cv2.Sobel()
After, they created a score (an eq'n) that determines if window contains corner or not
    R = det(M) - k(trace(M))^2
    det(M) = \lambda_1 * \lambda_2
    trace(M) = \lambda_1 + \lambda_2
    \lambda_1 & \lambda_2 are eigenvals of M
        values of eigenvals determine whether region is corner, edge, or flat
            when |R| small, when \lambda_1 & \lambda_2 small, region flat
            when R < 0, when \lambda_1 >> \lambda_2 or vv, region is edge
            when R large, when \lambda_1 & \lambda_2 large and \lambda_1 ~ \lambda_2, region is a corner
            see: http://docs.opencv.org/3.1.0/harris_region.jpg
Result of Harris corner detection: grayscale image with these scores
    thresholding for suitable gives corners of image
"""

# Harris Corner Detector in OpenCV
# function: cv2.cornerHarris()
# arguments:
    # img - input image; grayscale and float32 type
    # blockSize - size of nbhd considered for corner detection
    # ksize - aperture parameter of Sobel deriv used
    # k - Harris detector free parameter in eq'n

# ex.
import cv2
import numpy as np

filename = 'chessboard.png'
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)

# result is dilated for marking the corners, not important
dst = cv2.dilate(dst, None)

# Threshold for an optimal value, it may vary depending on the image
img[dst>0.01*dst.max()] = [0, 0, 255]

cv2.imshow('dst', img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows


import numpy as np
import sys, os

sys.path.append(os.pardir)
from dataset.mnist import load_mnist

# 损失函数 loss function 是表示神经网络性能的'恶劣程度'的指标
# 最有名的损失函数 均方误差 mean squared error

# 均方误差的实现
def mean_squared_error(y, t):
    return 0.5 * np.sum((y - t) ** 2)

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
# 设 2 为正确解
y = np.array([0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0])
print(np.ndim(y))
print(y)
y = y.reshape(1, y.size)
print(y)
M = mean_squared_error(np.array(y), np.array(t))
print(M)

# 交叉熵误差 cross entropy error
def cross_entropy_error(y, t):
    delta = 1e-7  # .0000001
    return -np.sum(t * np.log(y + delta))

# mini-batch
# (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
# train_size = x_train.shape[0]
# batch_size = 10
# batch_mask = np.random.choice(train_size, batch_size)
# x_batch = x_train[batch_mask]
# t_batch = t_train[batch_mask]
#
# print(np.random.choice(6000, 10))

# mini-batch版交叉熵误差的实现
def batch_cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    return -np.sum(t * np.log(y + 1e-7)) / batch_size

#
def batch_cross_entropy_error_normalize(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

# 导数的实现
# 不好的实现 数值微分

def numerical_diff(f, x):
    h = 10e-50
    return (f(x + h) - f(x - h)) / (2 * h)

# 梯度的实现
def numerical_gradient(f, x):
    h = 1e-4  # 0.0001
    grad = np.zeros_like(x)  # 生成和x形状相同的数组

    for idx in range(x.size):
        tmp_val = x[idx]
        # f(x+h)的计算
        x[idx] = tmp_val + h
        fxh1 = f(x)

        # f(x-h)的计算
        x[idx] = tmp_val - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2 * h)
        x[idx] = tmp_val  # 还原值
    return grad

def function_2(x):
    return x[0] ** 2 + x[1] ** 2

n1 = numerical_gradient(function_2, np.array([3.0, 4.0]))
n2 = numerical_gradient(function_2, np.array([0.0, 2.0]))
n3 = numerical_gradient(function_2, np.array([3.0, 0.0]))
print(n1, n2, n3)

# 梯度下降法
def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
    return x

init_x = np.array([-3.0, 4.0])
G = gradient_descent(function_2, init_x=init_x, lr=0.1, step_num=100)
print(G)

# 神经网络的梯度


import numpy as np

def unitStep(v):
	if v >= 0:
		return 1
	else:
		return 0

def perceptronModel(x, w, b):
	v = np.dot(w, x) + b
	y = unitStep(v)
	return y

# w1 = 1, w2 = 1, b = -1.5
def AND(x):
	w = np.array([1, 1])
	b = -1.5
	return perceptronModel(x, w, b)

test1 = np.array([0, 1])
test2 = np.array([1, 1])
test3 = np.array([0, 0])
test4 = np.array([1, 0])

print("AND({}, {}) = {}".format(0, 1, AND(test1)))
print("AND({}, {}) = {}".format(1, 1, AND(test2)))
print("AND({}, {}) = {}".format(0, 0, AND(test3)))
print("AND({}, {}) = {}".format(1, 0, AND(test4)))


"""
This python code demonstrates an edge-based active contour model as an application of the
Distance Regularized Level Set Evolution (DRLSE) formulation in the following paper:

  C. Li, C. Xu, C. Gui, M. D. Fox, "Distance Regularized Level Set Evolution and Its Application to Image Segmentation",
     IEEE Trans. Image Processing, vol. 19 (12), pp. 3243-3254, 2010.

Author: Ramesh Pramuditha Rathnayake
E-mail: rsoft.ramesh@gmail.com

Released Under MIT License
"""

import numpy as np
from scipy.ndimage import laplace

from lv_set.potential_func import SINGLE_WELL, DOUBLE_WELL

def drlse_edge(phi_0, g, lmda, mu, alfa, epsilon, timestep, iters, potential_function):  # Updated Level Set Function
    """

    :param phi_0: level set function to be updated by level set evolution
    :param g: edge indicator function
    :param lmda: weight of the weighted length term
    :param mu: weight of distance regularization term
    :param alfa: weight of the weighted area term
    :param epsilon: width of Dirac Delta function
    :param timestep: time step
    :param iters: number of iterations
    :param potential_function: choice of potential function in distance regularization term.
%              As mentioned in the above paper, two choices are provided: potentialFunction='single-well' or
%              potentialFunction='double-well', which correspond to the potential functions p1 (single-well)
%              and p2 (double-well), respectively.
    """
    phi = phi_0.copy()
    [vy, vx] = np.gradient(g)
    for k in range(iters):
        phi = neumann_bound_cond(phi)
        [phi_y, phi_x] = np.gradient(phi)
        s = np.sqrt(np.square(phi_x) + np.square(phi_y))
        delta = 1e-10
        n_x = phi_x / (s + delta)  # add a small positive number to avoid division by zero
        n_y = phi_y / (s + delta)
        curvature = div(n_x, n_y)

        if potential_function == SINGLE_WELL:
            dist_reg_term = laplace(phi, mode='nearest') - curvature  # compute distance regularization term in equation (13) with the single-well potential p1.
        elif potential_function == DOUBLE_WELL:
            dist_reg_term = dist_reg_p2(phi)  # compute the distance regularization term in eqaution (13) with the double-well potential p2.
        else:
            raise Exception('Error: Wrong choice of potential function. Please input the string "single-well" or "double-well" in the drlse_edge function.')
        dirac_phi = dirac(phi, epsilon)
        area_term = dirac_phi * g  # balloon/pressure force
        edge_term = dirac_phi * (vx * n_x + vy * n_y) + dirac_phi * g * curvature
        phi += timestep * (mu * dist_reg_term + lmda * edge_term + alfa * area_term)
    return phi

def dist_reg_p2(phi):
    """
        compute the distance regularization term with the double-well potential p2 in equation (16)
    """
    [phi_y, phi_x] = np.gradient(phi)
    s = np.sqrt(np.square(phi_x) + np.square(phi_y))
    a = (s >= 0) & (s <= 1)
    b = (s > 1)
    ps = a * np.sin(2 * np.pi * s) / (2 * np.pi) + b * (s - 1)  # compute first order derivative of the double-well potential p2 in equation (16)
    dps = ((ps != 0) * ps + (ps == 0)) / ((s != 0) * s + (s == 0))  # compute d_p(s)=p'(s)/s in equation (10). As s-->0, we have d_p(s)-->1 according to equation (18)
    return div(dps * phi_x - phi_x, dps * phi_y - phi_y) + laplace(phi, mode='nearest')

def div(nx: np.ndarray, ny: np.ndarray) -> np.ndarray:
    [_, nxx] = np.gradient(nx)
    [nyy, _] = np.gradient(ny)
    return nxx + nyy

def dirac(x: np.ndarray, sigma: np.ndarray) -> np.ndarray:
    f = (1 / 2 / sigma) * (1 + np.cos(np.pi * x / sigma))
    b = (x <= sigma) & (x >= -sigma)
    return f * b

def neumann_bound_cond(f):
    """
        Make a function satisfy Neumann boundary condition
    """
    g = f.copy()

    g[np.ix_([0, -1], [0, -1])] = g[np.ix_([2, -3], [2, -3])]
    g[np.ix_([0, -1]), 1:-1] = g[np.ix_([2, -3]), 1:-1]
    g[1:-1, np.ix_([0, -1])] = g[1:-1, np.ix_([2, -3])]
    return g


__all__ = ['mvee']
import math
import numpy as np
try:
    from scipy.spatial import ConvexHull
except ImportError:
    def _getConvexHull(points):
        return points
else:
    def _getConvexHull(points):
        hull = ConvexHull(points)
        return points[np.unique(hull.simplices)]

def mvee(points, tol=1.e-4, limits=10000):
    """
    Finds the minimum volume enclosing ellipsoid (MVEE) of a set of data points
    in the M-dimentional space.

    Parameters
    ----------
    points : (N, M) array_like
        A array of N points in the M-dimentional space. N must be larger than M.
    tol : float, optional
        Error in the solution with respect to the optimal value.
    limits : int, optional
        Maximal number of iteration.

    Returns
    -------
    A : (M,M) ndarray
        The matrix of the ellipse equation in the 'center form':
        (x-c)^{T} A^{-1} (x-c) = 1,
        where the eigenvalues of A are the squares of the semiaxes.
    c : (M,) ndarray
        The center of the ellipse.

    Notes
    -----
    This function is ported from the MATLAB routine
    ``Minimum Volume Enclosing Ellipsoid'' (see [1]_ and [2]_)
    by Nima Moshtagh (nima@seas.upenn.edu) at University of Pennsylvania.
    Note that the output matrix A here is different from the original MATLAB
    routine, where it returns A^{-1} instead.

    References
    ----------
    .. [1] http://www.mathworks.com/matlabcentral/fileexchange/9542-minimum-volume-enclosing-ellipsoid/content/MinVolEllipse.m
    .. [2] http://stackoverflow.com/questions/14016898/port-matlab-bounding-ellipsoid-code-to-python

"""
    P = _getConvexHull(points)
    N, d = P.shape
    if N <= d:
        raise ValueError("The number of points must be larger than the number of dimensions.")
    dp1_inv = 1./float(d+1)
    Q = np.vstack((P.T, np.ones(N)))
    err = tol + 1.
    u = np.ones(N)/float(N)
    while err > tol and limits > 0:
        X_inv = np.linalg.inv(np.einsum('ij,j,kj', Q, u, Q))
        M = np.einsum('ji,jk,ki->i', Q, X_inv, Q)
        j = np.argmax(M)
        step_size = (1.-d/(M[j]-1.))*dp1_inv
        u[j] -= 1.
        err = math.sqrt((u*u).sum())*math.fabs(step_size)
        u *= (1.-step_size)
        u[j] += 1.
        u /= u.sum()
        limits -= 1
    c = np.dot(u, P)
    A = (np.einsum('ji,j,jk', P, u, P) - np.outer(c,c)) * float(d)
    return A, c


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A Port of some of the functionality of the "chebpade" function in the "chebfun" library.

Copyright 2017 by The University of Oxford and The Chebfun Developers.
All rights reserved. See http://www.chebfun.org/ for Chebfun information.

This file is part of Fieldosophy, a toolkit for random fields.

"""

import numpy as np
from scipy import linalg
from scipy import signal

# %% Define functions

def ClenshawLord( f, N, domain, m, n ):
    """
    Function for computing Clenshaw-Lord approximation of chebychev polynomial on -1,1

    A port of the function "chabpade" from the Matlab-library "chebfun".
    """

    # Acquire Chebyshev approximation of f
    chebPoly = np.polynomial.chebyshev.Chebyshev.interpolate(f, deg = N, domain = domain )

    # Get chebyshev coefficients
    c = chebPoly.coef

    # Zero pad if needed
    if c.size < m + 2*n:
        c = np.concatenate( (c, np.zeros(m+2*n-c.size)) )

    # Setup system of linear equations to solve for beta
    c[0] = 2*c[0]
    top = c[ np.abs( np.arange(m-n+1,m+1) ) ]
    bottom = c[ np.abs( np.arange(m,m+n) ) ]
    rhs = c[ np.arange(m+1,m+n+1) ]

    beta = np.array([1])
    if n > 0:
        beta = linalg.hankel(top, bottom)
        beta = linalg.solve( beta, rhs )
        beta = np.flipud( np.concatenate( (-beta , np.array([1])) ) )

    # Compute alpha
    l = np.max((m,n))
    c[0] = c[0]/2
    alpha = signal.convolve(c[:l+1], beta)
    alpha = alpha[:l+2]

    # Compute numerator polynomial
    pk = np.zeros( (m+1) )
    D = np.outer( alpha, beta )
    pk[0] = np.sum(D.diagonal())
    for k in range(m):
        pk[k+1] = np.sum( np.concatenate( ( D.diagonal(k+1), D.diagonal(-k-1) ) ) )

    # Compute denominator
    qk = np.zeros( (n+1) )
    qk[0] = np.dot(beta, beta)
    for k in range(n):
        u = beta[:-(k+1)]
        v = beta[(k+1):]
        qk[k+1] = np.dot(u,v)

    # # Normalize the coefficients
    pk = pk/qk[0]
    qk = 2*qk/qk[0]
    qk[0] = 1

    # Get coefficients of corresponding power series
    pk = np.polynomial.chebyshev.cheb2poly(pk)
    qk = np.polynomial.chebyshev.cheb2poly(qk)

    # Return polynomial objects
    pk = np.polynomial.polynomial.Polynomial( pk, domain = domain )
    qk = np.polynomial.polynomial.Polynomial( qk, domain = domain )

    return (pk, qk)


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

'''
This is a collection of functions implemented in python that replicate
their implementation in GrADS.
Content:
1. Centered Differences (cdifof)
2. Horizontal Divergence (hdivg)
3. Vertical component of the relative vorticity (hcurl)
4. Horizontal Advection (hadv)

Only requires Numpy.
'''

import numpy as np

def cdiff(scalar,axis=0):
    '''
    Performs the same as GrADS function cdiff()
    http://cola.gmu.edu/grads/gadoc/gradfunccdiff.html
    The scalar quantity must by 2D.
    The finite differences calculation ignores the borders, where np.nan is returned.
    '''
    # Check if 2D
    dimScalar=np.size(np.shape(scalar))
    if dimScalar != 2:
        print("Pystuff Error: scalar must have only 2 dimensions, but it has %d." %dimScalar)
        return

    # Length of each dimension
    lendim0=np.shape(scalar)[0]
    lendim1=np.shape(scalar)[1]

    # Initialize output var
    out=np.zeros(np.shape(scalar))
    out.fill(np.nan)

    # Centered finite differences
    for x in np.arange(1,lendim0-1):
        for y in np.arange(1,lendim1-1):
            if axis==0:
                out[x,y]=scalar[x+1,y]-scalar[x-1,y]
            elif axis==1:
                out[x,y]=scalar[x,y+1]-scalar[x,y-1]
            else:
                print("Pystuff Error: Invalid axis option. Must be either 0 or 1.")
                return
    return out

def hdivg(u,v,lat,lon):
    '''
    Calculates the horizontal divergence (du/dx+dv/dy) exactly like GrADS.
    lat and lon are 1D arrays.
    http://cola.gmu.edu/grads/gadoc/gradfunccdiff.html
    '''
    latv, lonv = np.meshgrid(lat, lon, indexing='ij')
    r  = 6.371*(10**6)
    dtr = np.pi/180
    dudx = cdiff(u, axis=1)/cdiff(lonv*dtr, axis=1)
    dvdy = cdiff(v*np.cos(latv*dtr), axis=0)/cdiff(latv*dtr, axis=0)
    out  = (dudx + dvdy)/(r*np.cos(latv*dtr))
    return out

def hcurl(u,v,lat,lon):
    '''
    Calculates the vertical component of the relative vorticity (dv/dx-du/dy) exactly like GrADS.
    lat and lon are 1D arrays.
    http://cola.gmu.edu/grads/gadoc/gradfunccdiff.html
    '''
    latv, lonv = np.meshgrid(lat, lon, indexing='ij')
    r  = 6.371*(10**6)
    dtr = np.pi/180
    dvdx = cdiff(v, axis=1)/cdiff(lonv*dtr, axis=1)
    dudy = cdiff(u*np.cos(latv*dtr), axis=0)/cdiff(latv*dtr, axis=0)
    out  = (dvdx - dudy)/(r*np.cos(latv*dtr))
    return out

def hadv(u,v,t,lat,lon):
    '''
    Calculates the advection of a scalar variable.
    lat and lon are 1D arrays.
    http://cola.gmu.edu/grads/gadoc/gradfunccdiff.html
    '''
    latv, lonv = np.meshgrid(lat, lon, indexing='ij')
    dtr = np.pi/180
    r   = 6.371*(10**6)
    dtx = cdiff (t, axis=1)
    dty = cdiff (t, axis=0)
    dx  = cdiff(lonv, axis=1)*dtr
    dy  = cdiff(latv, axis=0)*dtr
    out = -( (u*dtx)/(np.cos(latv*dtr)*dx) + v*dty/dy )/r
    return out


import numpy as np
import matplotlib.pyplot as plt

N = 100
D = 2

X = np.random.randn(N, D)
# center first 50 points at (-2, -2)
X[:50, :] = X[:50, :] - 2 * np.ones((50, D))
# center last 50 points at (2, 2)
X[50:, :] = X[50:, :] + 2 * np.ones((50, D))
# labels: first 50 = 0, last 50 = 1
T = np.array([0] * 50 + [1] * 50)

ones = np.array([[1] * N]).T
Xb = np.concatenate((ones, X), axis=1)

W = np.random.random(D + 1)
z = Xb.dot(W)

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

Y = sigmoid(z)

def cross_entropy(T, Y):
    E = 0
    for i in range(N):
        if T[i] == 1:
            E -= np.log(Y[i])
        else:
            E -= np.log(1 - Y[i])
    return E

print(cross_entropy(T, Y))

# Closed form
W = np.array([0, 4, 4])
z = Xb.dot(W)
Y = sigmoid(z)
print(cross_entropy(T, Y))

# y = -x
plt.scatter(X[:, 0], X[:, 1], c=T, s=100, alpha=0.5)
x_axis = np.linspace(-6, 6, 100)
y_axis = -x_axis
plt.plot(x_axis, y_axis)
plt.show()


import numpy as np
import editdistance

from numba import jit

#TODO add more

def makeLn(n):
    return lambda a, b: np.linalg.norm(a - b, ord=n)

def editDistance(a, b):
    return editdistance.eval(a, b)

@jit(nopython=True)
def earthMoverDistance(u, v):
    """
    Compute the first Wasserstein distance between two 1D distributions.
    :param u, v: (Array-like) Values observed in the (empirical) distribution.
    :return: (float) The computed distance between the distributions.
    """
    # Calculate the CDFs of u and v
    u_cumweights = np.concatenate((np.array([0.0]), np.cumsum(u)))
    u_cdf = u_cumweights / u_cumweights[-1]

    v_cumweights = np.concatenate((np.array([0.0]), np.cumsum(v)))
    v_cdf = v_cumweights / v_cumweights[-1]

    return np.sum(np.abs(u_cdf - v_cdf))


import numpy as np

# Piratiado: https://kaushikghose.wordpress.com/2013/10/24/computing-mutual-information-and-other-scary-things/

def calc_MI(X, Y, bins=100, sparse=False):
    if sparse:
        X = X.toarray()[0]
        Y = Y.toarray()[0]

    c_XY = np.histogram2d(X,Y,bins)[0]
    c_X = np.histogram(X,bins)[0]
    c_Y = np.histogram(Y,bins)[0]

    H_X = shan_entropy(c_X)
    H_Y = shan_entropy(c_Y)
    H_XY = shan_entropy(c_XY)

    MI = H_X + H_Y - H_XY
    return MI

def shan_entropy(c):
    c_normalized = c / float(np.sum(c))
    c_normalized = c_normalized[np.nonzero(c_normalized)]
    H = -sum(c_normalized* np.log2(c_normalized))
    return H


import numpy as np

# Cria um Vetor com a função array do NumPy.
v = np.array([2, 1])

# Tira a Magnitude do Vetor.
vMag = np.linalg.norm(v)
print("Magnitude (distância) do meu vetor {0} é {1}".format(v, vMag))


from numpy import array, cos
from benchmarks.benchmark import Benchmark

class Shubert(Benchmark):
    """dim: 2"""
    def __init__(self, lower=-10, upper=10, dimension=2):
        super(Shubert, self).__init__(lower, upper, dimension)

    def get_optimum(self):
        return array([[]]), -186.7309  # has 18 global minima. May be restricted to the square xi /in [-5.12, 5.12]

    @staticmethod
    def eval(sol):
        sum1 = 0
        sum2 = 0
        for i in range(1, 6):
            new1 = i * cos((i + 1) * sol[0] + i)
            new2 = i * cos((i + 1) * sol[1] + i)
            sum1 += new1
            sum2 += new2

        return sum1 * sum2


import numpy as np
from scipy.optimize import linprog

def wasserstein_distance(p, q, D):
    """Wasserstein-distance
    p.shape=[m], q.shape=[n], D.shape=[m, n]
    p.sum()=1, q.sum()=1, p∈[0,1], q∈[0,1]
    """
    A_eq = []
    for i in range(len(p)):
        A = np.zeros_like(D)
        A[i, :] = 1
        A_eq.append(A.reshape(-1))
    for i in range(len(q)):
        A = np.zeros_like(D)
        A[:, i] = 1
        A_eq.append(A.reshape(-1))
    A_eq = np.array(A_eq)
    b_eq = np.concatenate([p, q])
    D = D.reshape(-1)
    result = linprog(D, A_eq=A_eq[:-1], b_eq=b_eq[:-1])
    return result.fun

def word_mover_distance(x, y):
    """WMD（Word Mover's Distance）
    x.shape=[m,d], y.shape=[n,d]
    """
    p = np.ones(x.shape[0]) / x.shape[0]
    q = np.ones(y.shape[0]) / y.shape[0]
    D = np.sqrt(np.square(x[:, None] - y[None, :]).mean(axis=2))
    return wasserstein_distance(p, q, D)

def word_rotator_distance(x, y):
    """WRD（Word Rotator's Distance
    x.shape=[m,d], y.shape=[n,d]
    """
    x_norm = (x**2).sum(axis=1, keepdims=True)**0.5
    y_norm = (y**2).sum(axis=1, keepdims=True)**0.5
    p = x_norm[:, 0] / x_norm.sum()
    q = y_norm[:, 0] / y_norm.sum()
    D = 1 - np.dot(x / x_norm, (y / y_norm).T)
    return wasserstein_distance(p, q, D)

def word_rotator_similarity(x, y):
    """1 - WRD
    x.shape=[m,d], y.shape=[n,d]
    """
    return 1 - word_rotator_distance(x, y)


import numpy as np
import scipy

def truncNorm( a=0, b=1, mu=0, sigma=1, size=1 ):
    return scipy.stats.truncnorm.rvs( (a-mu)/sigma, (b-mu)/sigma, loc=mu, scale=sigma, size=size )

def truncNormCdf( x, a=0, b=1, mu=0, sigma=1 ):
    return scipy.stats.truncnorm.cdf( x, (a-mu)/sigma, (b-mu)/sigma, loc=mu, scale=sigma )

def truncNormPdf( x, a=0, b=1, mu=0, sigma=1 ):
    return scipy.stats.truncnorm.pdf( x, (a-mu)/sigma, (b-mu)/sigma, loc=mu, scale=sigma )

def truncPowerLawRVs( k, minX, maxX, size=1, skew='negative', algorithm='custom' ):
    if algorithm == 'custom':
        u = np.random.random( size )
        if skew == 'negative':
            vals = maxX - (maxX - minX)*np.power(1-u,1/(1-k))
        elif skew == 'positive':
            k = -k
            vals = np.power( (np.power(maxX,k+1) - np.power(minX,k+1))*u + np.power(minX,k+1), 1/(k+1) )
    elif algorithm == 'scipy':
        vals = scipy.stats.powerlaw.rvs( k+1, loc=minX, scale=(maxX-minX), size=size )

    return vals

def truncPowerLawPdf( x, k, minX, maxX ):
    if not isinstance(x,np.ndarray):
        x = np.array( x )

    pdf = np.zeros_like( x )
    pdf[x < minX] = 0
    pdf[x > maxX] = 0
    remaining = (x > minX ) & ( x < maxX )
    pdf[remaining] = np.power(1/(maxX-x[remaining]),k)

    return pdf

def truncPowerLawMean( k, minX, maxX ):
    m = maxX - ((1-k)/(2-k))*(maxX - minX)
    return m

def truncPowerLawVar( k, minX, maxX ):
    eDiff = maxX - minX
    secondmoment = (1 - k) * ( eDiff*eDiff/(3-k) + maxX*maxX/(1-k) - 2*maxX*eDiff/(2-k) )
    #eDiff = maxX
    #print( (1- k) * ( eDiff*eDiff/(3-k) + maxX*maxX/(1-k) - 2*maxX*eDiff/(2-k) )- truncPowerLawMean( k, 0, maxX )**2  )
    #print( maxX * maxX * (1 + (k-4)*(1-k)/((3-k)*(2-k))) - truncPowerLawMean( k, 0, maxX )**2 )
    return secondmoment - truncPowerLawMean( k, minX, maxX )**2


import numpy
import scipy.io.wavfile
import matplotlib.pyplot as plot
from scipy.fftpack import dct

sample_rate, signal = scipy.io.wavfile.read('./1234.wav')  # File assumed to be in the same directory
#signal = signal[0:int(3.5 * sample_rate)]  # Keep the only first 3.5 seconds
Time = numpy.linspace(0, len(signal) / sample_rate, len(signal)) #Time axis

#--Pre-Emphasis--
pre_emphasis = 0.97 #typical values for the filter coefficient (α) are 0.95 or 0.97,
emphasized_signal = numpy.append(signal[0], signal[1:] - pre_emphasis * signal[:-1])

#--Framing--
#Typical frame sizes in speech processing range from 20 ms to 40 ms with 50% (+/-10%) overlap between consecutive frames.
frame_size = 0.025 #Popular setting 25 ms for the frame size
frame_stride = 0.01 #10 ms stride (15 ms overlap)

frame_length, frame_step = frame_size * sample_rate, frame_stride * sample_rate  # Convert from seconds to samples
signal_length = len(emphasized_signal)
frame_length = int(round(frame_length))
frame_step = int(round(frame_step))
num_frames = int(numpy.ceil(float(numpy.abs(signal_length - frame_length)) / frame_step))  # Make sure that we have at least 1 frame

pad_signal_length = num_frames * frame_step + frame_length
z = numpy.zeros((pad_signal_length - signal_length))
pad_signal = numpy.append(emphasized_signal, z) # Pad Signal to make sure that all frames have equal number of samples without truncating any samples from the original signal

indices = numpy.tile(numpy.arange(0, frame_length), (num_frames, 1)) + numpy.tile(numpy.arange(0, num_frames * frame_step, frame_step), (frame_length, 1)).T #construct array
frames = pad_signal[indices.astype(numpy.int32, copy=False)] #just for plotting
ham_frames = pad_signal[indices.astype(numpy.int32, copy=False)]

#--Apply Hamming Window--
ham_frames *= numpy.hamming(frame_length)
# frames *= 0.54 - 0.46 * numpy.cos((2 * numpy.pi * n) / (frame_length - 1))  # Explicit Implementation **

#Fourier-Transform and Power Spectrum
#We can now do an N-point FFT on each frame to calculate the frequency spectrum. aka Short-Time Fourier-Transform (STFT)
#NFFT = 256 #N is typically 256 or 512
NFFT = frame_length
mag_frames = numpy.absolute(numpy.fft.rfft(ham_frames, NFFT))  # Magnitude of the FFT
freq_scale = numpy.fft.rfftfreq(frame_length, 1./sample_rate)  # Frequency scale
pow_frames = ((1.0 / NFFT) * ((mag_frames) ** 2))  # Power Spectrum

#--Filter Banks--
nfilt = 40 #number of filter banks

low_freq_mel = 0
high_freq_mel = (2595 * numpy.log10(1 + (sample_rate / 2) / 700))  # Convert Hz to Mel
mel_points = numpy.linspace(low_freq_mel, high_freq_mel, nfilt + 2)  # Equally spaced in Mel scale
hz_points = (700 * (10**(mel_points / 2595) - 1))  # Convert Mel to Hz
bin = numpy.floor((NFFT + 1) * hz_points / sample_rate)

fbank = numpy.zeros((nfilt, int(numpy.floor(NFFT / 2 + 1))))
for m in range(1, nfilt + 1):
    f_m_minus = int(bin[m - 1])   # left
    f_m = int(bin[m])             # center
    f_m_plus = int(bin[m + 1])    # right

    for k in range(f_m_minus, f_m):
        fbank[m - 1, k] = (k - bin[m - 1]) / (bin[m] - bin[m - 1])
    for k in range(f_m, f_m_plus):
        fbank[m - 1, k] = (bin[m + 1] - k) / (bin[m + 1] - bin[m])
filter_banks = numpy.dot(pow_frames, fbank.T)
filter_banks = numpy.where(filter_banks == 0, numpy.finfo(float).eps, filter_banks)  # Numerical Stability
filter_banks = 20 * numpy.log10(filter_banks)  # dB

# -- Mel-frequency Cepstral Coefficients (MFCCs) --
num_ceps = 12 #Typically, for Automatic Speech Recognition (ASR), the resulting cepstral coefficients 2-13 are retained and the rest are discarded

mfcc = dct(filter_banks, type=2, axis=1, norm='ortho')[:, 1 : (num_ceps + 1)] # Keep 2-13 (num_ceps+1)

#One may apply sinusoidal liftering1 to the MFCCs to de-emphasize higher MFCCs
#(nframes, ncoeff) = mfcc.shape
#n = numpy.arange(ncoeff)
#lift = 1 + (cep_lifter / 2) * numpy.sin(numpy.pi * n / cep_lifter)
#mfcc *= lift  #*

#-- Print some details
print("Sound file duration " + str(len(signal) / sample_rate) + "(secs), " + str(len(signal)) + " samples")
print("Sampling rate " + str(sample_rate) + "Hz ("+ str(sample_rate) +" samples per sec)" )
print("-Framing-")
print("Frame length: " +str(frame_length) + " samples, " + str(round(frame_size*1000)) + 'ms')
print("Frame overlapp: " +str(round((frame_size-frame_stride)*1000)) + "ms")
print("No. of frames: " +str(num_frames))
print("-Filter Banks-")
print("No. of filter banks: " + str(nfilt) + " (spectogram array size)")
print("-MCC's-")
print("No. of coeffecients: " + str(num_ceps))

#-- Plots
plot.figure(1,[12, 6]) #plot 1st frame

plot.subplot(321)
plot.title("(1) Frame #1")
plot.xlabel("Time ")
plot.tight_layout(2)
plot.plot(frames[0])

plot.subplot(322)
plot.title("(2) Hamming applied")
plot.plot(ham_frames[0])

plot.subplot(323)
plot.title("(3) FFT spectrum")
plot.xlabel("Freq (Hz)")
plot.bar(freq_scale,mag_frames[0],freq_scale[1])

plot.subplot(324)
plot.title("(4) Power spectrum")
plot.xlabel("Freq (Hz)")
plot.bar(freq_scale,pow_frames[0],freq_scale[1])

plot.subplot(325)
plot.title("(5) Mel Filters applied ")
plot.xlabel("Freq (Hz)")
plot.bar(numpy.arange(freq_scale[-1]/nfilt,freq_scale[-1]+(freq_scale[-1]/nfilt),freq_scale[-1]/nfilt), filter_banks[0], (freq_scale[-1]/nfilt))

plot.subplot(326)
plot.title("(6) MFCC's ")
plot.xlabel(" Coefficient # ")
plot.bar(numpy.arange(1,num_ceps+1,1), mfcc[0], 1)

plot.figure(2, [12, 6])#plot full signal

plot.subplot(321)
plot.title("(1) Raw .wav signal")
plot.tight_layout(2)
plot.plot(Time,signal)

plot.subplot(322)
plot.title("(2) Pre-emphasised signal")
plot.plot(emphasized_signal)

plot.subplot(323)
plot.title("(3) FFT")
plot.imshow(mag_frames.T,plot.cm.jet, aspect='auto')
plot.yticks([0,50,100],[int(freq_scale[0]),int(freq_scale[50]),int(freq_scale[100])])
plot.gca().invert_yaxis()
plot.ylabel("Freq (Hz)")

plot.subplot(324)
plot.title("(4) Power Spectrum (periodogram) ")
plot.imshow(pow_frames.T,plot.cm.jet, aspect='auto')
plot.yticks([0,50,100],[int(freq_scale[0]),int(freq_scale[50]),int(freq_scale[100])])
plot.gca().invert_yaxis()
plot.ylabel("Freq (Hz)")

plot.subplot(325)
plot.title("(5) Mel Filter banks applied (spectogram)")
plot.imshow(filter_banks.T,plot.cm.jet, aspect='auto')
plot.yticks([0,int(nfilt/2),int(nfilt)],[int(freq_scale[0]),int(freq_scale[50]),int(freq_scale[100])])
plot.gca().invert_yaxis()
plot.ylabel("Freq (Hz)")
plot.xlabel("Time")

plot.subplot(326)
plot.title("(6) MFCC's")
plot.imshow(mfcc.T,plot.cm.jet, aspect='auto')
#plot.yticks([0,int(nfilt/2),int(nfilt)],[int(freq_scale[0]),int(freq_scale[50]),int(freq_scale[100])])
plot.gca().invert_yaxis()
plot.ylabel("MFCC Coeffecients")
plot.xlabel("Time")

plot.show()


"""

Write a program to implement the naïve Bayesian classifier for a sample training data set stored as a .CSV file. Compute the accuracy of the classifier, considering few test data sets.

"""

print("\nNaive Bayes Classifier for concept learning problem")
import numpy as np
import csv
import random
import math
import operator
def safe_div(x,y):
    if y == 0:
        return 0
    return x / y

def loadCsv(filename):
	lines = csv.reader(open(filename))
	dataset = list(lines)
	for i in range(len(dataset)):
		dataset[i] = [float(x) for x in dataset[i]]
	return dataset

def splitDataset(dataset, splitRatio):
	trainSize = int(len(dataset) * splitRatio)
	trainSet = []
	copy = list(dataset)
	i=0
	while len(trainSet) < trainSize:
		trainSet.append(copy.pop(i))
	return [trainSet, copy]

def separateByClass(dataset):
	separated = {}
	for i in range(len(dataset)):
		vector = dataset[i]
		if (vector[-1] not in separated):
			separated[vector[-1]] = []
		separated[vector[-1]].append(vector)
	return separated

def mean(numbers):
	return safe_div(sum(numbers),float(len(numbers)))

def stdev(numbers):
	avg = mean(numbers)
	variance = safe_div(sum([pow(x-avg,2) for x in numbers]),float(len(numbers)-1))
	return math.sqrt(variance)

def summarize(dataset):
	summaries = [(mean(attribute), stdev(attribute)) for attribute in zip(*dataset)]
	del summaries[-1]
	return summaries

def summarizeByClass(dataset):
	separated = separateByClass(dataset)
	summaries = {}
	for classValue, instances in separated.items():
		summaries[classValue] = summarize(instances)
	return summaries

def calculateProbability(x, mean, stdev):
	exponent = math.exp(-safe_div(math.pow(x-mean,2),(2*math.pow(stdev,2))))
	final = safe_div(1 , (math.sqrt(2*math.pi) * stdev)) * exponent
	return final

def calculateClassProbabilities(summaries, inputVector):
	probabilities = {}
	for classValue, classSummaries in summaries.items():
		probabilities[classValue] = 1
		for i in range(len(classSummaries)):
			mean, stdev = classSummaries[i]
			x = inputVector[i]
			probabilities[classValue] *= calculateProbability(x, mean, stdev)
	return probabilities

def predict(summaries, inputVector):
	probabilities = calculateClassProbabilities(summaries, inputVector)
	bestLabel, bestProb = None, -1
	for classValue, probability in probabilities.items():
		if bestLabel is None or probability > bestProb:
			bestProb = probability
			bestLabel = classValue
	return bestLabel

def getPredictions(summaries, testSet):
	predictions = []
	for i in range(len(testSet)):
		result = predict(summaries, testSet[i])
		predictions.append(result)
	return predictions

def getAccuracy(testSet, predictions):
	correct = 0
	for i in range(len(testSet)):
		if testSet[i][-1] == predictions[i]:
			correct += 1
	accuracy = safe_div(correct,float(len(testSet))) * 100.0
	return accuracy

def main():
	filename = 'ConceptLearning.csv'
	splitRatio = 0.9
	dataset = loadCsv(filename)
	trainingSet, testSet = splitDataset(dataset, splitRatio)
	print('Split {0} rows into'.format(len(dataset)))
	print('Number of Training data: ' + (repr(len(trainingSet))))
	print('Number of Test Data: ' + (repr(len(testSet))))
	print("\nThe values assumed for the concept learning attributes are\n")
	print("OUTLOOK=> Sunny=1 Overcast=2 Rain=3\nTEMPERATURE=> Hot=1 Mild=2 Cool=3\nHUMIDITY=> High=1 Normal=2\nWIND=> Weak=1 Strong=2")
	print("TARGET CONCEPT:PLAY TENNIS=> Yes=10 No=5")
	print("\nThe Training set are:")
	for x in trainingSet:
		print(x)
	print("\nThe Test data set are:")
	for x in testSet:
		print(x)
	print("\n")
	# prepare model
	summaries = summarizeByClass(trainingSet)
	# test model
	predictions = getPredictions(summaries, testSet)
	actual = []
	for i in range(len(testSet)):
		vector = testSet[i]
		actual.append(vector[-1])
	# Since there are five attribute values, each attribute constitutes to 20% accuracy. So if all attributes match with predictions then 100% accuracy
	print('Actual values: {0}%'.format(actual))
	print('Predictions: {0}%'.format(predictions))
	accuracy = getAccuracy(testSet, predictions)
	print('Accuracy: {0}%'.format(accuracy))
main()


# Calculate terminal velocity, ut, from drag coefficient, Cd, and particle sphericity
# from Kunii1991 book, pg 80, Eqs 28-29

# use Python 3 print function and division
from __future__ import print_function
from __future__ import division

# libraries
import numpy as np

#--- INPUTS

# air properties at T = 300K, P = 1 atm
rhog = 1.17     # density (kg/m^3)
ug = 1.85e-5    # dynamic viscosity (kg/ms)
g = 9.81        # gravity (m/s^2)

# particle properties
dp = 0.000207   # diameter of particle (m)
rhos = 2500     # density of particle (kg/m^3)
sp = 0.8        # sphericity of the particle, perfect sphere = 1.0

#--- OUTPUTS

# guess range for terminal velocity, ut (m/s)
ut = np.arange(0.001, 20, 0.001)

# Re, Reynolds number (-)
# Cd, drag coefficient as function of Re and sphericity (-)
# Cdd, drag coefficient as function of ut, etc. (-)
Re = (dp*rhog*ut)/ug
Cd = (24/Re)*(1+(8.1716*np.exp(-4.0655*sp))*Re**(0.0964+0.5565*sp)) + (73.69*np.exp(-5.0748*sp)*Re)/(Re+5.378*np.exp(6.2122*sp))
Cdd = (4*g*dp*(rhos-rhog))/(3*(ut**2)*rhog)

delta = np.abs(Cd-Cdd)  # compare difference between Cd and Cdd
idx = np.argmin(delta)  # find index of minimum value in delta

# print results to console
print('--- Kunii1991 ---')
print('ut =', ut[idx])
print('Re =', Re[idx])
print('Cd =', Cd[idx])
print('Cdd =', Cdd[idx])
print('delta =', delta[idx])


#Importing Libraries
import os
import numpy as np
from matplotlib import pyplot
from scipy import optimize
import utils

#Loading the data
data = np.loadtxt(os.path.join('Data', 'ex2data1.txt'), delimiter=',')
X, y = data[:, 0:2], data[:, 2]

#Visualising the data
def plotdata(X, y):
    fig = pyplot.figure()
    pyplot.plot(X[pos, 0], X[pos, 1], 'k*', lw=2, ms=10)
    pyplot.plot(X[neg, 0], X[neg, 1], 'ko', mfc='y', ms=8, mec='k', mew=1)

plotData(X, y)

#Defining the Logistic Sigmoid Function 𝜎(𝑧)
def sigmoid(z):
    return 1/(1 + np.exp(-z))

#Cost and Gradient at Initialization
A = sigmoid(np.dot(w.T, X) + b)
cost = (- 1 / m) * np.sum(Y * np.log(A) + (1 - Y) * (np.log(1 - A)))

#Implement Gradient Descent from Scratch

#Plotting the Convergence of 𝐽(𝜃)

#Plotting the Decision Boundary

#Predictions Using the Optimized 𝜃 Values


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

eps = np.finfo(float).eps
vala = []
valb = []
valx = []

def f(x):
    return ((x-1)*math.exp((x-2)**2))-1

def printar(n,a,b,x):
    list = [n+1,a,b,x,f(a),f(b),f(x)]
    print("Iteracao {}: \nValor de a: {} \nValor de b: {} \nValor de x: {} \nValor de função de a:{} \nValor de função de b: {} \nValor de função de x: {}".format(*list))
    vala.append(a)
    valb.append(b)
    valx.append(x)

def bisseccao(a,b,eps,it,iteracoes):
    while(it<iteracoes):
        if(b-a<eps):
            return (random.uniform(a,b))
        else:
            x = (a+b)/2
            if(f(x) == 0):
                return x
            if(f(a)<0 and f(b)>0):
                if(f(x) < 0):
                    printar(it,a,b,x)
                    a=x
                    it=it+1
                else:
                    printar(it,a,b,x)
                    b=x
                    it=it+1
            elif(f(a)>0 and f(b)<0):
                if(f(x) > 0):
                    printar(it,a,b,x)
                    b=x
                    it=it+1
                else:
                    printar(it,a,b,x)
                    a=x
                    it=it+1
            else:
                print("meu amigo, queria dizer não, mas vai ter que usar algo que ainda não aprendi")
    return None

def relerror(x,apr):
    return abs(x-aprox)/abs(x)

#Exemplo da Tabela 3.1 realizado do livro da UFRGS
a = float(0)
b = float(3)
bi = bisseccao(a,b,(1e-6),0,300)

if bi == None:
    print("Passou do limite, bro, fez sol!")
else:
    print("Caralho, mermão, toma uma raiz aproximada: {}".format(bi))

valerr = []

for aprox in valx:
    valerr.append(relerror(bi,aprox))

plt.plot(valerr, color="#00FF00")
plt.show()


import numpy as np

inputs = [[1.0, 2.0, 3.0, 2.5],
          [2.0, 5.0, -1.0, 2.0],
          [-1.5, 2.7, 3.3, -0.8]]
weights = [[0.2, 0.8, -0.5, 1.0],
           [0.5, -0.91, 0.26, -0.5],
           [-0.26, -0.27, 0.17, 0.87]]
biases = [2.0, 3.0, 0.5]

layer_outputs = np.dot(inputs, np.array(weights).T) + biases

print(layer_outputs)

# a = [[2,3,4,5],
#      [3,4,5,7]]
# print(a)
# b = np.array(a)
# print(b)

'''
>>>
array([[ 4.8    1.21   2.385],
       [ 8.9   -1.81   0.2  ],
       [ 1.41   1.051  0.026]])
'''


# 05 Confidence intervals

'''
If we assume that a particular distribution is normal, then
the Student's t-distribution can be used to compute
the confidence interval for the mean of the distribution.

A n% confidence interval means that the
true mean of a distribution falls within the
computed confidence interval with an estimated 95%
probability.

Confidence intervals are often used to describe the
uncertainty in sampled values such as survey data
or simulation results.
'''

import numpy as np
import scipy.stats as st

def ci(data):
    return st.t.interval(alpha=0.95, df=len(data)-1, loc=np.mean(data), scale=st.sem(data))

def between(x,ci):
    return x >= ci[0] and x <= ci[1]

def test(mu,sigma):
    # mu and sigma are the true mean and sample deviation

    # empirical means
    m = np.empty(10)

    # generate distributions
    for i in range(10):
        s = np.random.normal(mu, sigma, 10)
        m[i] = np.mean(s)

    # Is the true mean within the C.I. of the empirical means (m)?
    return between(mu, ci(m))

if __name__ == "__main__":
    count = 0
    nTests = 10000

    for x in range(nTests):
        if test(0,1):
            count += 1
    print('Proportion of tests where the 95% confidence interval includes the true mean:', count/nTests)


# Import libraries
###############################
import numpy # numerics
from matplotlib import pyplot # plotting
from matplotlib import cm # colormap
from mpl_toolkits.mplot3d import Axes3D # 3d plot

######################################################################
# FUNCTION DEFINITIONS                                               #
######################################################################
def laplace2d(p, y, dx, dy, l1norm_target):
    l1norm = 1
    pp = numpy.empty_like(p) # p at previous step

    while l1norm > l1norm_target:
        pp = p.copy()
        p[1:-1,1:-1] = (dy**2*(pp[2:,1:-1]+pp[0:-2,1:-1])+\
                        dx**2*(pp[1:-1,2:]+pp[1:-1,0:-2]))/(2*(dx**2+dy**2))

        p[0,:] = 0  # p = 0 @ x = 0
        p[-1,:] = y # p = y @ x = 2
        p[:,0] = p[:,1] # dp/dy = 0 @ y = 0
        p[:,-1] = p[:,-2]   # dp/dy = 0 @ y = 1
        l1norm = (numpy.sum(numpy.abs(p[:])-numpy.abs(pp[:])))\
                 /numpy.sum(numpy.abs(pp[:]))

    return p

def plot2d(x, y, p, title):
    fig = pyplot.figure(figsize=(11, 7), dpi=100)
    ax = fig.gca(projection='3d')
    X, Y = numpy.meshgrid(x, y, indexing='ij')
    surf = ax.plot_surface(X, Y, p, rstride=1, cstride=1, cmap=cm.coolwarm, \
            linewidth=0, antialiased=False)
    ax.set_xlim(0,2)
    ax.set_ylim(0,1)
    ax.view_init(30,225)
    pyplot.title(title)

######################################################################
# USER INPUT                                                         #
######################################################################

# Physical parameters
##############################

# Geometry and spatial discretization
##############################
nx = 31 # Remark: Must not be too large --> small dx --> instability?
ny = 31
xmin = 0
xmax = 2
ymin = 0
ymax = 1
dx = (xmax-xmin)/(nx-1)
dy = (ymax-ymin)/(ny-1)
x = numpy.linspace(xmin, xmax, nx)
y = numpy.linspace(ymin, ymax, ny)

# Temporal discretization
##############################
#nt = 201 # Remark: nt had to be increased compared to 07 again

# Initial condition
##############################
# Initialize dependent variables
p = numpy.zeros((nx, ny))
p[0,:] = 0  # p = 0 @ x = 0
p[-1,:] = y # p = y @ x = 2
p[:,0] = p[:,1] # dp/dy = 0 @ y = 0
p[:,-1] = p[:,-2]   # dp/dy = 0 @ y = 1

######################################################################
# SOME FUN                                                           #
######################################################################
plot2d(x, y, p, 'initial')
p = laplace2d(p, y, dx, dy, 1e-4)
plot2d(x, y, p, 'L1=1e-4')
pyplot.show()


''' Principal component analysis (PCA) is a dimensionality reduction
technique that can used in Unsupervised Learning. It is important to
minimise the number of dimensions when working with a data set that has
a large number of features.

'''
#https://deepblade.com/artificial-intelligence/machine-learning/principal-component-analysis-pca/

import numpy as np
import matplotlib.pyplot as plt

# create dataset as numpy array
data = np.array([[40, 20],
                [55, 30],
                [70, 60],
                [50, 35],
                [45, 40],
                [62, 75],
                [45, 30],
                [68, 80],
                [80, 70],
                [75, 90]])
n=len(data)
# feature scaling

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaled_data = scaler.fit_transform(data)

#for i in range(n):
#    print(data[i][0], scaled_data[i][0], data[i][1], scaled_data[i][1] )

print(scaled_data)

''' create a covariance matrix to measure the relationships between the
features. Then we can identify the principal components (PCs) by
calculating eigenvalues and eigenvectors using the covariance matrix.

the number of principal components equal to the number of dimensions of
the problem. this example gives a maximum of two
principal components.
'''

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(data)

print(pca.explained_variance_)
print(pca.explained_variance_ratio_)

#number of dimensions in the data set has been reduced from 2 to 1.

data_pca = pca.transform(data)
print("Original shape:", data.shape)
print("Transformed shape:", data_pca.shape)

new_data = pca.inverse_transform(data_pca)
plt.scatter(data[:, 0], data[:, 1], alpha=0.2)
plt.scatter(new_data[:, 0], new_data[:, 1])
plt.show()


# Hierarchical Clustering
# This chapter focuses on a popular clustering algorithm - hierarchical clustering - and its implementation in SciPy. In addition to the procedure to perform hierarchical clustering, it attempts to help you answer an important question - how many clusters are present in your data? The chapter concludes with a discussion on the limitations of hierarchical clustering and discusses considerations while using hierarchical clustering.

# Hierarchical clustering: ward method
# It is time for Comic-Con! Comic-Con is an annual comic-based convention held in major cities in the world. You have the data of last year's footfall, the number of people at the convention ground at a given time. You would like to decide the location of your stall to maximize sales. Using the ward method, apply hierarchical clustering to find the two points of attraction in the area.

# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time.

# Import the fcluster and linkage functions
from scipy.cluster.hierarchy import fcluster, linkage

# Use the linkage() function
distance_matrix = linkage(comic_con[['x_scaled', 'y_scaled']], method = 'ward', metric = 'euclidean')

# Assign cluster labels
comic_con['cluster_labels'] = fcluster(distance_matrix, 2, criterion='maxclust')

# Plot clusters
sns.scatterplot(x='x_scaled', y='y_scaled',
                hue='cluster_labels', data = comic_con)
plt.show()

# Hierarchical clustering: single method
# Let us use the same footfall dataset and check if any changes are seen if we use a different method for clustering.

# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time.

# Import the fcluster and linkage functions
from    scipy.cluster.hierarchy import fcluster, linkage

# Use the linkage() function
distance_matrix = linkage(comic_con[['x_scaled', 'y_scaled']], method = 'single', metric = 'euclidean')

# Assign cluster labels
comic_con['cluster_labels'] = fcluster(distance_matrix, 2, criterion ='maxclust')

# Plot clusters
sns.scatterplot(x='x_scaled', y='y_scaled',
                hue='cluster_labels', data = comic_con)
plt.show()

# Hierarchical clustering: complete method
# For the third and final time, let us use the same footfall dataset and check if any changes are seen if we use a different method for clustering.

# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time.

# Import the fcluster and linkage functions
from scipy.cluster.hierarchy import fcluster, linkage

# Use the linkage() function
distance_matrix = linkage(comic_con[['x_scaled','y_scaled']], method ='complete', metric = 'euclidean')

# Assign cluster labels
comic_con['cluster_labels'] = fcluster(distance_matrix, 2, criterion='maxclust')

# Plot clusters
sns.scatterplot(x='x_scaled', y='y_scaled',
                hue='cluster_labels', data = comic_con)
plt.show()

# Visualize clusters with matplotlib
# We have discussed that visualizations are necessary to assess the clusters that are formed and spot trends in your data. Let us now focus on visualizing the footfall dataset from Comic-Con using the matplotlib module.

# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time. cluster_labels has the cluster labels. A linkage object is stored in the variable distance_matrix.

# Import the pyplot class
from matplotlib import pyplot as plt

# Define a colors dictionary for clusters
colors = {1:'red', 2:'blue'}

# Plot a scatter plot
comic_con.plot.scatter(x='x_scaled',
                	   y='y_scaled',
                	   c=comic_con['cluster_labels'].apply(lambda x: colors[x]))
plt.show()

# Visualize clusters with seaborn
# Let us now visualize the footfall dataset from Comic Con using the seaborn module. Visualizing clusters using seaborn is easier with the inbuild hue function for cluster labels.

# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time. cluster_labels has the cluster labels. A linkage object is stored in the variable distance_matrix.

# Import the seaborn module
import seaborn as sns

# Plot a scatter plot using seaborn
sns.scatterplot(x='x_scaled',
                y='y_scaled',
                hue='cluster_labels',
                data = comic_con)
plt.show()

# Create a dendrogram
# Dendrograms are branching diagrams that show the merging of clusters as we move through the distance matrix. Let us use the Comic Con footfall data to create a dendrogram.

# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time. cluster_labels has the cluster labels. A linkage object is stored in the variable distance_matrix.

# Import the dendrogram function
from scipy.cluster.hierarchy import dendrogram

# Create a dendrogram
dn = dendrogram(distance_matrix)

# Display the dendogram
plt.show()

# Exercise
# Exercise
# FIFA 18: exploring defenders
# In the FIFA 18 dataset, various attributes of players are present. Two such attributes are:

# sliding tackle: a number between 0-99 which signifies how accurate a player is able to perform sliding tackles
# aggression: a number between 0-99 which signifies the commitment and will of a player
# These are typically high in defense-minded players. In this exercise, you will perform clustering based on these attributes in the data.

# This data consists of 5000 rows, and is considerably larger than earlier datasets. Running hierarchical clustering on this data can take up to 10 seconds.

# The following modules are pre-loaded: dendrogram, linkage, fcluster from scipy.cluster.hierarchy, matplotlib.pyplot as plt, seaborn as sns. The data is stored in a Pandas dataframe, fifa.

# Fit the data into a hierarchical clustering algorithm
distance_matrix = linkage(fifa[['scaled_sliding_tackle', 'scaled_aggression']], 'ward')

Cluster Analysis in Python/Hierarchical_Clustering.py


"""
Power spectrum related functions.
"""
import numpy as np

def growth_func(z, Om, Ol, Ok=0):
    """
    The growth function.

    .. math::

       P(k,z) = 2*\\pi^{2} * norm_z0 * pfunc * growth_func(z)^{2}
    """
    D0 = 2.5*Om / (Om**(4./7) - Ol + (1 + 0.5*Om)*(1 + Ol/70.))

    zp1 = 1 + z
    zp1_2 = zp1**2
    zp1_3 = zp1**3
    temp =  Ol + Ok*zp1_2 + Om*zp1_3
    omegaz = Om * zp1_3 / temp
    omegalz = Ol / temp

    temp = omegaz**(4./7) - omegalz + (1 + 0.5*omegaz)*(1 + omegalz/70.)
    Dz = 1./zp1 * 2.5 * omegaz / temp
    return Dz / D0

def cdm_corr_z2p5(rvals, fudge=4.9, sigma8 = 0.809, omegam = 0.272,
                  omegal = 0.728):
    """
    Return the dark matter correlation function at separations given by rvals at z=2.5

    rvals in h^-1 Mpc
    """
    rvals = np.asarray(rvals)

    assert rvals.max() < 180
    assert rvals.min() > 1e-3
    import astro
    prefix = astro.__path__[0]
    r,xi0 = np.loadtxt(prefix + '/data/slosar09_corr.txt', unpack=1)
    xivals = np.interp(rvals, r, xi0)

    # just normalisation, don't worry too much about it
    norm = (sigma8 / 0.00039453207450944561)**2
    xi1 = 1. / (2*np.pi)**3 * xivals * norm * growth_func(2.5, omegam, omegal)

    return fudge * xi1

def powerspec(y, dt, fig=None):
    """ Find the power spectrum using an FFT.

    The samples y must be equally spaced with spacing dt. It is
    assumed y is real.

    return the coords in fourier transform space (e.g. 1/t) and the power
    spectrum values, 2 * abs(fft)**2
    """
    fft = np.fft.fft(y)
    nu = np.fft.fftfreq(len(y), dt)
    pspec = (2*np.abs(fft)**2)[:len(y)//2 + 1]
    pnu = np.abs(nu)[:len(y)//2 + 1]
    if fig is not None:
        import pylab as pl
        ax = fig.add_subplot(211)
        ax.plot(np.arange(len(y))*dt, y)
        ax.set_ylabel('$y$')
        ax.set_xlabel('T')
        ax = fig.add_subplot(212)

        ax.semilogy(pnu, pspec)
        ax.set_ylabel(r'$2*abs(fft(y))^2$')
        ax.set_xlabel('f = 1/T')

    return pnu, pspec


import cvxpy as cp
import fire
import numpy as np

rng = np.random.Generator(np.random.MT19937(seed=12345))
n = 20
# _A and _C are internal - you don't need them.
_A = rng.standard_normal((2 * n, n))
_C = np.diag(0.5 * np.exp(rng.standard_normal((n,))))

Sigma = _C @ _A.T @ _A @ _C
Sigma = 0.5 * (Sigma + Sigma.T)
M = np.ones(n) * 0.2
sigma = np.sqrt(np.diag(Sigma))

def main():
    def compute_dr(_x):
        return sigma @ _x / ((Sigma @ _x @ _x) ** 0.5)

    x = cp.Variable(n)
    cons = [x >= 0., x - M * cp.sum(x) <= 0., sigma @ x == 1., ]
    obj = cp.Minimize(cp.quad_form(x, Sigma))
    cp.Problem(objective=obj, constraints=cons).solve()

    x_star = x.value / (np.sum(x.value))
    d_x_star = compute_dr(x_star)
    print(f'x_star: {x_star}, d_x_star: {d_x_star:.6f}')

    x = cp.Variable(n)
    cons = [x >= 0., x <= M, cp.sum(x) == 1]
    obj = cp.Minimize(cp.quad_form(x, Sigma))
    cp.Problem(objective=obj, constraints=cons).solve()
    x_mv = x.value
    d_x_mv = compute_dr(x_mv)
    print(f'x_mv: {x_mv}, d_x_mv: {d_x_mv:.6f}')

    import matplotlib.pyplot as plt
    plt.bar(np.arange(0, 20), x_star, width=0.5, label="Max diversification")
    plt.bar((np.arange(0, 20) * 2 + 1) / 2, x_mv, width=0.5, label="Min variance")
    plt.legend()
    plt.show()

if __name__ == "__main__":
    fire.Fire(main)


from typing import Union, Optional

import gin
import jax.numpy as jnp
import matplotlib.pyplot as plt
import seaborn as sns

from myriad.custom_types import Params
from myriad.systems import IndirectFHCS

@gin.configurable
class MouldFungicide(IndirectFHCS):
  """
    Taken from: Optimal Control Applied to Biological Models, Lenhart & Workman (Chapter 6, Lab 2)
    This environment models the concentration level of a mould population that we try to control by
    applying a fungicide. The state ( \\(x\\) ) is the population concentration, while the control ( \\(u\\) ) is
    the amount of fungicide added. We are trying to minimize:

    .. math::

      \\begin{align}
      & \\min_u \\quad &&\\int_0^T Ax^2(t) + u^2(t) dt \\\\
      &\\; \\mathrm{s.t.}\\quad && x'(t) = r(M - x(t)) - u(t)x(t) \\\\
      & && x(0)=x_0 \\;
      \\end{align}
  """

  def __init__(self, r=0.3, M=10., A=10., x_0=1.0, T=5):
    super().__init__(
      x_0=jnp.array([x_0]),  # Starting state
      x_T=None,  # Terminal state, if any
      T=T,  # Duration of experiment
      bounds=jnp.array([  # Bounds over the states (x_0, x_1, ...) are given first,
        [0., 5.],  # followed by bounds over controls (u_0, u_1, ...)
        [0., 5.],  # nh: I replaced [-inf, inf] with [0, 5] in both of the bounds
      ]),
      terminal_cost=False,
      discrete=False,
    )

    self.adj_T = None  # Final condition over the adjoint, if any
    self.r = r
    """Growth rate"""
    self.M = M
    """Carrying capacity"""
    self.A = A
    """Weight parameter, balancing between controlling the population and limiting the fungicide use"""

  def dynamics(self, x_t: jnp.ndarray, u_t: Union[float, jnp.ndarray],
               v_t: Optional[Union[float, jnp.ndarray]] = None, t: Optional[jnp.ndarray] = None) -> jnp.ndarray:
    d_x = self.r * (self.M - x_t) - u_t * x_t

    return d_x

  def parametrized_dynamics(self, params: Params, x_t: jnp.ndarray, u_t: Union[float, jnp.ndarray],
                            v_t: Optional[Union[float, jnp.ndarray]] = None,
                            t: Optional[jnp.ndarray] = None) -> jnp.ndarray:
    r = params['r']
    M = params['M']
    d_x = r * (M - x_t) - u_t * x_t

    return d_x

  def cost(self, x_t: jnp.ndarray, u_t: Union[float, jnp.ndarray], t: Optional[jnp.ndarray] = None) -> float:
    return self.A * x_t ** 2 + u_t ** 2

  def parametrized_cost(self, params: Params, x_t: jnp.ndarray, u_t: Union[float, jnp.ndarray],
                        t: Optional[jnp.ndarray] = None) -> float:
    return self.A * x_t ** 2 + u_t ** 2  # don't learn the cost for now

  def adj_ODE(self, adj_t: jnp.ndarray, x_t: Optional[jnp.ndarray], u_t: Optional[jnp.ndarray],
              t: Optional[jnp.ndarray]) -> jnp.ndarray:
    return adj_t * (self.r + u_t) - 2 * self.A * x_t

  def optim_characterization(self, adj_t: jnp.ndarray, x_t: Optional[jnp.ndarray],
                             t: Optional[jnp.ndarray]) -> jnp.ndarray:
    char = 0.5 * adj_t * x_t
    return jnp.minimum(self.bounds[-1, 1], jnp.maximum(self.bounds[-1, 0], char))