"""
=======================================================
Comparison of LDA and PCA 2D projection of Siri dataset
=======================================================

The Siri dataset represents 3 kind of Siri classes (Type 1, Type 2, Type 3)
with 4 attributes: attr_SL, attr_SQ, attr_PL, attr_PW

Principal Component Analysis (PCA) applied to this data identifies the
combination of attributes (principal components, or directions in the
feature space) that account for the most variance in the data. Here we
plot the different samples on the 2 first principal components.

Linear Discriminant Analysis (LDA) tries to identify attributes that
account for the most variance *between classes*. In particular,
LDA, in contrast to PCA, is a supervised method, using known class labels.
"""

print(__doc__)

import matplotlib.pyplot as plt
import numpy as np

from sklearn import datasets
from sklearn.decomposition import PCA
from sklearn.lda import LDA

# read in csv dataset
siri = np.loadtxt('Siri_data_with_class.csv', delimiter=',',skiprows=1, dtype={'names':('Attr_SL','Attr_SQ','Attr_PL','Attr_PW','class'),'formats':('f2','f2','f2','f2','S6')})
X = np.array([list(i)[:4] for i in siri])
y = np.array([int(list(i)[4][5:]) for i in siri])
target_names = np.array(['Type 1','Type 2','Type 3'])

# choose the top 2 best attributes by PCA algo
pca = PCA(n_components=2)
X_r = pca.fit(X).transform(X)

#lda is a supervised method, use target y
## choose the top 2 best attributes by LDA algo
lda = LDA(n_components=2)
X_r2 = lda.fit(X, y).transform(X)

# Percentage of variance explained for each components
print('explained variance ratio (first two components): %s'
      % str(pca.explained_variance_ratio_))

# draw scatterplot
plt.figure()
for c, i, target_name in zip("rgb", [1, 2, 3], target_names):
    plt.scatter(X_r[y == i, 0], X_r[y == i, 1], c=c, label=target_name)
plt.legend()
plt.title('PCA of SIRI dataset')

plt.figure()
for c, i, target_name in zip("rgb", [1, 2, 3], target_names):
    plt.scatter(X_r2[y == i, 0], X_r2[y == i, 1], c=c, label=target_name)
plt.legend()
plt.title('LDA of SIRI dataset')

plt.show()

dimension reduction/dimension reduction_PCA & LDA_siri.py


import matplotlib.pyplot as plt
import numpy as np
from distributions import Binomial_distribution, Discrete_uniform_distribution

def plotDistributeBar(ax, data, label='', width=0.3, offset=0, title='Probability Distribution of true'):
    ax.bar(np.arange(len(data))+offset,data,width=width,label=label)

    fontSize = 12
    ax.set_title(title,fontsize=fontSize)

    plt.xlabel('Different Teeth Bins',fontsize=fontSize)
    plt.ylabel('Probability',fontsize=fontSize)
    plt.xticks(np.arange(len(data)))

def plorDataDis(true_data,uniform,bino):
    ax = plt.subplot(1,1,1)
    offset = 0
    width=0.2
    plotDistributeBar(ax, true_data, label='True data', offset=offset, width=width)

    offset += width
    plotDistributeBar(ax, uniform, label='Uniform data', offset=offset, width=width)

    offset += width
    print(bino)
    plotDistributeBar(ax, bino, label='Binomial data', offset=offset, width=width)
    plt.legend()
    plt.show()

def get_klpq_div(p_probs, q_probs):
    kl_div = 0.0
    for pi, qi in zip(p_probs, q_probs):
        kl_div += pi*np.log(pi/qi)

    return kl_div

def get_klqp_div(p_probs, q_probs):
    kl_div = 0.0
    for pi, qi in zip(p_probs, q_probs):
        kl_div += qi*np.log(qi/pi)

    return kl_div

def plotKLResult(true_data,minP):
    ax = plt.subplot(1,1,1)
    offset = 0
    width=0.2
    plotDistributeBar(ax, true_data, label='True data', offset=offset, width=width)

    pAll = [0.02,0.1,minP,0.8]
    for p in pAll:
        offset += width
        dis = Binomial_distribution(N=len(true_data),p=p)
        plotDistributeBar(ax, dis, label='Binomial '+ str(p), offset=offset, width=width)

    plt.legend()
    plt.show()

def testDiscretKL():
    true_data = [0.02, 0.03, 0.15, 0.14, 0.13, 0.12, 0.09, 0.08, 0.1, 0.08, 0.06]
    print('sum=', sum(true_data))
    assert sum(true_data)==1.0
    unif_data = Discrete_uniform_distribution(true_data,N=len(true_data))
    bino_data = Binomial_distribution(N=len(true_data),p=0.3)

    plorDataDis(true_data,unif_data,bino_data)

    print('KL(True||Uniform): ', get_klpq_div(true_data,unif_data))
    print('KL(True||Binomial): ', get_klpq_div(true_data,bino_data))

    p = np.arange(0.02, 1.0, 0.02) #np.linspace(0, 1.0, 50)
    klpq = [get_klpq_div(true_data,Binomial_distribution(N=len(true_data),p=i)) for i in p]
    klqp = [get_klqp_div(true_data,Binomial_distribution(N=len(true_data),p=i)) for i in p]

    print('minimal klpq,', np.argmin(klpq), np.min(klpq))

    ax = plt.subplot(1,1,1)
    plotDistribute(ax,p,klpq,label='KL(P||Q)')
    plotDistribute(ax,p,klqp,label='KL(Q||P)')
    plotDistribute(ax,p,np.array(klpq)-np.array(klqp),label='KL(P||Q)-KL(Q||P)')
    plt.show()

    plotKLResult(true_data, np.min(klpq))

def plotDistribute(ax,x,y,label='', title='Binomial P vs KL'):
    ax.plot(x,y,label=label)

    fontSize = 12
    ax.set_title(title,fontsize=fontSize)
    ax.legend()
    plt.xlabel('Binomial P',fontsize=fontSize)
    plt.ylabel('KL(P||Q) divergence',fontsize=fontSize)
    #plt.show()

def main():
    testDiscretKL()

if __name__=='__main__':
    main()


#!/usr/bin/env python3
"""
Bayesian Probability
"""
import numpy as np

def marginal(x, n, P, Pr):
    """
    Calculates the marginal probability of obtaining the data:

    x is the number of patients that develop severe side effects
    n is the total number of patients observed
    P is a 1D numpy.ndarray containing the various hypothetical probabilities
    of patients developing severe side effects
    Pr is a 1D numpy.ndarray containing the prior beliefs about P
    If n is not a positive integer, raise a ValueError with the message n must
    be a positive integer
    If x is not an integer that is greater than or equal to 0, raise a
    ValueError with the message x must be an integer that is greater than or
    equal to 0
    If x is greater than n, raise a ValueError with the message x cannot be
    greater than n
    If P is not a 1D numpy.ndarray, raise a TypeError with the message P must
    be a 1D numpy.ndarray
    If Pr is not a numpy.ndarray with the same shape as P, raise a TypeError
    with the message Pr must be a numpy.ndarray with the same shape as P
    If any value in P or Pr is not in the range [0, 1], raise a ValueError
    with the message All values in {P} must be in the range [0, 1] where {P}
    is the incorrect variable
    If Pr does not sum to 1, raise a ValueError with the message Pr must sum
    to 1
    All exceptions should be raised in the above order

    Returns: the marginal probability of obtaining x and n
    """
    if not isinstance(n, int) or n < 1:
        raise ValueError("n must be a positive integer")
    if not isinstance(x, (int, float)) or (x < 0):
        mg = "x must be an integer that is greater than or equal to 0"
        raise ValueError(mg)
    if x > n:
        raise ValueError("x cannot be greater than n")
    if not isinstance(P, np.ndarray) or len(P.shape) != 1:
        raise TypeError("P must be a 1D numpy.ndarray")
    if not isinstance(Pr, np.ndarray) or P.shape != Pr.shape:
        raise TypeError("Pr must be a numpy.ndarray with the same shape as P")
    if np.any(P > 1) or np.any(P < 0):
        raise ValueError("All values in P must be in the range [0, 1]")
    if np.any(Pr > 1) or np.any(Pr < 0):
        raise ValueError("All values in Pr must be in the range [0, 1]")
    if not np.isclose([np.sum(Pr)], [1])[0]:
        raise ValueError("Pr must sum to 1")

    num = (np.math.factorial(n))
    den = (np.math.factorial(x) * np.math.factorial(n - x))
    fct = num / den
    D = fct * (P**x) * ((1 - P)**(n - x))
    its = D * Pr

    return np.sum(its)


# Basic functions on polynomials

import numpy as np
import matplotlib.pyplot as plt

# Polynomials as lists of coefficients

# P+Q: max(deg(P),deg(Q)) operations

def add_polynomials(P, Q):
    somme = []
    n = len(P)
    m = len(Q)
    if n < m:
        P += (m - n) * [0]
    elif n < m:
        Q += (n - m) * [0]
    deg_max = max(n, m)
    for k in range(0, deg_max):
        somme += [P[k] + Q[k]]
    return somme

# aP: deg(P) operations
def multiply_polynomial(P, a):
    n = len(P)
    for k in range(0, n):
        P[k] *= a
    return P

# PQ: (deg(P)+deg(Q))**2 operations
def multiply_polynomials(P, Q):
    n = len(P)
    m = len(Q)
    P += m * [0]
    Q += n * [0]
    p = n + m
    # thus deg(PQ)=n+m
    res = []
    for k in range(0, p - 1):
        a_k = 0
        for i in range(0, p - 1):
            a_k += P[i] * Q[k - i]
        res += [a_k]
    return res

# P(x): deg(P) operations
def polynomial_image(P, x):
    n = len(P)
    sigma = 0
    for k in range(0, n):
        sigma += P[k] * x ** k
    return sigma

# int(P): 3deg(P) operations
def polynomial_integral(P, a, b):
    n = len(P)
    Q = []
    for k in range(0, n):
        Q += [P[k] / (k + 1)]
    Q = [0] + Q
    I = polynomial_image(Q, b) - polynomial_image(Q, a)
    return I

# P: deg(P) operations
def polynomial_derivative(P):
    n = len(P)
    dP = []
    for k in range(1, n):
        dP += [P[k] * (k)]
    return dP

def polynomial_graph(P, a, b):
    coordsx = np.linspace(a, b, 1000)
    coordsy = [polynomial_image(P, x) for x in coordsx]
    plt.clf()
    plt.plot(coordsx, coordsy, "r-", linewidth=2)

# Two useful utils to compute power series : factorial and binomial coeffs

# log(n) operations
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

# 3*log(n) operations
def binomial_coefficients(k, n):
    u = factorial(n) / (factorial(k) * factorial(n - k))
    return u


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

def func(X):
    F = (X[0]+X[1])**2 + (X[1]+X[2])**2
    return F
def f_con(X):
    G = X[0] + 2.0*X[1] + 3.0*X[2] - 1.0
    return G
x0 = np.array([-4.0, 1.0, 2.0])
res = optimize.fmin_slsqp(func, x0, f_eqcons=f_con, iter=1000, acc=1e-06, disp=True, full_output=True)

# conz = ({'type': 'eq', 'fun': f_con})
# res2 = optimize.minimize(func, x0, method='L-BFGS-B', constraints=conz, options={'disp': True})


import numpy as np

def retroactive_resolution(coefficients: np.matrix, vector: np.array) -> np.array:
    """
    This function performs a retroactive linear system resolution
        for triangular matrix
    Examples:
        2x1 + 2x2 - 1x3 = 5         2x1 + 2x2 = -1
        0x1 - 2x2 - 1x3 = -7        0x1 - 2x2 = -1
        0x1 + 0x2 + 5x3 = 15
    >>> gaussian_elimination([[2, 2, -1], [0, -2, -1], [0, 0, 5]], [[5], [-7], [15]])
    array([[2.],
           [2.],
           [3.]])
    >>> gaussian_elimination([[2, 2], [0, -2]], [[-1], [-1]])
    array([[-1. ],
           [ 0.5]])
    """

    rows, columns = np.shape(coefficients)

    x = np.zeros((rows, 1), dtype=float)
    for row in reversed(range(rows)):
        sum = 0
        for col in range(row + 1, columns):
            sum += coefficients[row, col] * x[col]

        x[row, 0] = (vector[row] - sum) / coefficients[row, row]

    return x

def gaussian_elimination(coefficients: np.matrix, vector: np.array) -> np.array:
    """
    This function performs Gaussian elimination method
    Examples:
        1x1 - 4x2 - 2x3 = -2        1x1 + 2x2 = 5
        5x1 + 2x2 - 2x3 = -3        5x1 + 2x2 = 5
        1x1 - 1x2 + 0x3 = 4
    >>> gaussian_elimination([[1, -4, -2], [5, 2, -2], [1, -1, 0]], [[-2], [-3], [4]])
    array([[ 2.3 ],
           [-1.7 ],
           [ 5.55]])
    >>> gaussian_elimination([[1, 2], [5, 2]], [[5], [5]])
    array([[0. ],
           [2.5]])
    """
    # coefficients must to be a square matrix so we need to check first
    rows, columns = np.shape(coefficients)
    if rows != columns:
        return []

    # augmented matrix
    augmented_mat = np.concatenate((coefficients, vector), axis=1)
    augmented_mat = augmented_mat.astype("float64")

    # scale the matrix leaving it triangular
    for row in range(rows - 1):
        pivot = augmented_mat[row, row]
        for col in range(row + 1, columns):
            factor = augmented_mat[col, row] / pivot
            augmented_mat[col, :] -= factor * augmented_mat[row, :]

    x = retroactive_resolution(
        augmented_mat[:, 0:columns], augmented_mat[:, columns : columns + 1]
    )

    return x

if __name__ == "__main__":
    import doctest

    doctest.testmod()


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

# Initial setup
xx0 = np.array([.25,1.5])
mu =0.1
xmin,xmax = -2,2
ymin,ymax = -2,2
plt.close()
fig,ax0 = plt.subplots(1,1,figsize=(8,8))
ax0.set_facecolor(plt.cm.gray(.95))
#ax.axis('equal')

#Vector field
x1,y1 = np.linspace(xmin,xmax,200),np.linspace(ymin,ymax,200)
X,Y = np.meshgrid(x1,y1 )
U = mu*X - Y +X*Y**2
V = X + mu*Y + Y**3
start = [[0.3,0],[.6,.5]]

#Plotting
strm = ax0.streamplot( X,Y,U, V,linewidth=.2)
try:
    strmS = ax0.streamplot(x1,y1, U, V, start_points=start, color="crimson", linewidth=.5)
except: # IndexError:
    print('Unable to plot initial value trajectories')
fig.suptitle('Hopf Bifurcation', size=16)
ax0.set_title( 'Strogatz 8.2.1, p.256', size=12)
ax0.text(xmin+.25,ymax-.25, r'$\mu = {0}$'.format(mu),size=14)
ax0.plot(y1/(mu+y1**2),x1, 'k-.',lw=.8, label=r'x-nullcline: $x = \frac{y}{(\mu+y^2)}$')
ax0.plot(-mu*y1+y1**3,x1, 'k:', lw=.8, label=r'y-nullcline: $x =  -\mu y + y^3}$')
ax0.set_xlim([xmin,xmax])
ax0.set_ylim([ymin,ymax])
ax0.set_xlabel(r"$ x$",size=14)
ax0.set_ylabel(r"$ y$",size=14)
plt.legend()
plt.grid(True)
plt.legend()
plt.grid(True)
plt.show()


"""
Day 3 Solutions
"""

import itertools as it

import numpy as np

def part1(input_lines):
    """
    You come across an experimental new kind of memory stored on an infinite two-dimensional grid.

    Each square on the grid is allocated in a spiral pattern starting at a location marked 1
    and then counting up while spiraling outward. For example, the first few squares are
    allocated like this:

    17  16  15  14  13
    18   5   4   3  12
    19   6   1   2  11
    20   7   8   9  10
    21  22  23---> ...

    While this is very space-efficient (no squares are skipped), requested data must be
    carried back to square 1 (the location of the only access port for this memory system)
    by programs that can only move up, down, left, or right. They always take the shortest path:
    the Manhattan Distance between the location of the data and square 1.

    For example:

        - Data from square 1 is carried 0 steps, since it's at the access port.
        - Data from square 12 is carried 3 steps, such as: down, left, left.
        - Data from square 23 is carried only 2 steps: up twice.
        - Data from square 1024 must be carried 31 steps.

    How many steps are required to carry the data from the square identified in your puzzle
    input all the way to the access port?
    """
    # Note: Rotation is right -> top -> left -> bottom; starting one cell away from the corner.
    # The size of the array is the smallest square-of-an-odd-int that fits our count
    # 1, 9, 25, etc.
    cell = int(input_lines[0])
    # (Special base case for 1)
    if cell == 1:
        return 0
    diam = 1
    while diam ** 2 < cell:
        diam += 2
    radius = diam // 2
    inner_area = (diam - 2) ** 2
    # Consider our cell as being on the "perimeter". We basically need to determine
    # distance-to-center of the side we're on (corners being the farthest away)
    # For the 3x3 case, this goes like so as we walk the spiral: 0, 1, 0, 1, 0, 1, 0, 1
    # The 5x5 case goes: 1, 0, 1, 2, 1, 0, 1, 2, ...
    # The 7x7 case goes: 2, 1, 0, 1, 2, 3, ...
    # Note that it's (decreasing, 0, increasing), shifted left by 1 due to where we start the spiral.
    # We use a modulus of diameter - 1 to rotate through that array, finding that delta.
    mod = diam - 1
    deltas = abs(np.array(range(mod)) - radius + 1)
    delta_to_center_of_side = deltas[(cell - inner_area) % mod - 1]
    # Now our distance is that delta + the radius!
    return delta_to_center_of_side + radius

def part2(input_lines):
    """
    As a stress test on the system, the programs here clear the grid and then store the value 1 in square 1.
    Then, in the same allocation order as shown above, they store the sum of the values in all adjacent squares,
    including diagonals.

    So, the first few squares' values are chosen as follows:

        - Square 1 starts with the value 1.
        - Square 2 has only one adjacent filled square (with value 1), so it also stores 1.
        - Square 3 has both of the above squares as neighbors and stores the sum of their values, 2.
        - Square 4 has all three of the aforementioned squares as neighbors and stores the sum of their values, 4.
        - Square 5 only has the first and fourth squares as neighbors, so it gets the value 5.

    Once a square is written, its value does not change. Therefore, the first few squares would receive
    the following values:

    147  142  133  122   59
    304    5    4    2   57
    330   10    1    1   54
    351   11   23   25   26
    362  747  806--->   ...

    What is the first value written that is larger than your puzzle input?
    """
    target = int(input_lines[0])
    g = SpiralGrid()
    return g.locate_target_sum(target)

class SpiralGrid:
    """
    It was easier for me to conceptualize this as a class, so I did.

    This implements the spiral grid plus the debug storage method above.
    It knows how to run one layer of the spiral at a time, and how to
    reverse-search its data store for the right target value.
    """
    def __init__(self):
        self.x = 0
        self.y = 0
        self.cell = 2
        self.layer = 1
        self.map = {(0,0): 1}
        self.data = [1]

    def store(self):
        sum_ = 0
        for (dx, dy) in it.product((-1, 0, 1), repeats=2):
                neighbor = (self.x + dx, self.y + dy)
                if neighbor in self.map:
                    nval = self.data[self.map[neighbor] - 1]
                    sum_ += nval
        self.map[(self.x, self.y)] = self.cell
        self.data.append(sum_)
        self.cell += 1

    def up(self):
        self.y += 1
        self.store()

    def down(self):
        self.y -= 1
        self.store()

    def left(self):
        self.x -= 1
        self.store()

    def right(self):
        self.x += 1
        self.store()

    def fill_layer(self):
        side_len = (2 * self.layer)
        # Right to start
        self.right()
        # Up to the corner
        for _ in range(side_len - 1):
            self.up()
        # Left, then down, then right
        for _ in range(side_len):
            self.left()
        for _ in range(side_len):
            self.down()
        for _ in range(side_len):
            self.right()
        # Increment working layer
        self.layer += 1

    def get_cell_number(self, x, y):
        while self.layer <= max(abs(x), abs(y)):
            self.fill_layer()
        return self.map[(x, y)]

    def locate_target_sum(self, target):
        # We just do this one layer at a time,
        # then scan backwards once we're in the right layer.
        while self.data[-1] <= target:
            self.fill_layer()
        i = -1
        while self.data[i] > target:
            i -= 1
        return self.data[i + 1]


# Communities formation using Girvan-Newman algorithm

import networkx as nx

# Which edge to remove from the graph to form two communities ?
# This is done by calculating the betweenness centrality for each edge, and higher the betweenness centrality of an edge, sooner it is removed.
def edge_to_remove(G):
	dict1 = nx.edge_betweenness_centrality(G) # Returns dictionary, with 'edge' as key and 'edge betweenness centrality' as value
	# Now we have to sort edges according to their betweenness centrality measures.
	list_of_tuples = dict1.items()
	list_of_tuples.sort(key = lambda x: x[1], reverse = True)
	return list_of_tuples[0][0] # Returns an edge (a,b) as a tuple ----> ((a,b))

# Function to implement Girvan-Newman algorithm.
def girvan(G):
	c = list(nx.connected_component_subgraphs(G))
	l = len(c)
	print 'The number of connected components in the graph : ', l

	while (l==1):
		G.remove_edge(*edge_to_remove(G))  # ((a,b)) ----> (a,b). That's why we use *
		c = list(nx.connected_component_subgraphs(G))
		l = len(c)
		print 'The number of connected components in the graph : ', l

	return c

G = nx.karate_club_graph() # Creating Karate Club network using builtin function
c = girvan(G) # Detecting communities in the network
print 'The communities formed are as follows :\n'
for i in c :
	print i.nodes()
	print 'Size of the community : ', i.number_of_nodes()
	print '\n'


import numpy as np

def score_game(game_core, predict_func):
  '''Запускаем игру 1000 раз, чтобы узнать, как быстро игра угадывает число'''
  count_ls = []
  np.random.seed(1)  # фиксируем RANDOM SEED, чтобы ваш эксперимент был воспроизводим!
  random_array = np.random.randint(1,101, size=(1000))
  for number in random_array:
    count_ls.append(game_core(number, predict_func))
  score = int(np.mean(count_ls))
  print(f"Ваш алгоритм угадывает число в среднем за {score} попыток")
  return(score)

def game_core(number, predict_func):
  '''Угадываем число, сокращая диапазон для угадывания'''
  r_min = 1
  r_max = 100
  count = 1
  predict = np.random.randint(r_min, r_max + 1)

  while predict != number:
    count += 1
    if number > predict:
      r_min = predict + 1
      predict = predict_func(r_min, r_max)
    else:
      r_max = predict - 1
      predict = predict_func(r_min, r_max)

  return(count)

def gen_predict_v1(r_min, r_max):
  '''Угадываем число через случайную функцию'''
  return np.random.randint(r_min, r_max+1)

def gen_predict_v2(r_min, r_max):
  '''Угадываем число бинарным способом'''
  return (r_max + r_min) // 2

# Запускаем программу
print('Угадываем случайно:')
score_game(game_core, gen_predict_v1)

print()

print('Угадываем бинарно:')
score_game(game_core, gen_predict_v2)


#########################################################################
######  Name: Kenneth Emeka Odoh
######  Purpose: An implementation of Recursive Linear Regression
#########################################################################
from __future__ import division
import pandas as pd
import numpy as np

class OnlineLinearRegression:
    """
    This is based on the Recursive Linear regression classifier

    [1] Galanos, K. Recursive least squares. Retrieved 06/15, 2017, from http://saba.kntu.ac.ir/eecd/people/aliyari/NN%20%20files/rls.pdf

    """

    def __init__(self):
        '''
            accept the data in a matrix format
        '''
        self.error = None
        self.theta = None
        self.cov = None
        self.isTrained = False

    def update(self, x, y):
        '''
            accept in same format and dimension as data in constructor
        '''

        #set the prior
        self.priorY = y

        self.priorX = x

        xm = np.mat (x)
        ym = np.mat (y)

        all_zeros = not np.any(self.error)
        if all_zeros:
            self.error = np.zeros(y.shape)

        all_zeros = not np.any(self.theta)
        if all_zeros:
            #self.theta = ( (xm.T * xm ).I ) * xm.T *  ym

            tmat = np.linalg.pinv( np.nan_to_num(xm.T * xm) ) #ensure stability
            self.theta = ( tmat ) * xm.T *  ym
            self.isTrained = True

        all_zeros = not np.any(self.cov)
        if all_zeros:
            self.cov = np.dot ( x.T, x)

        if not self.isTrained:

            cov = np.mat ( self.cov )

            theta = np.mat ( xm * self.theta )

            self.error = ym - ( theta  )

            Im = np.mat ( np.eye (x.shape[1]) )

            self.cov = cov * np.mat( Im - ( ( xm.T * xm * cov ) / (1 + ( xm * cov * xm.T )  ) )  )

            self.theta = theta + ( self.cov * xm.T *  self.error  )

            self.isTrained = False

    def getA(self):
        """
            outputs the A vector
        """
        return self.theta

    def getB(self):
        """
            outputs the noise or bias
        """
        return self.error

    def getCovarianceMatrix(self):
        """
            outputs the covariance matrix
        """
        #ypost = np.dot ( self.getA().T, self.priorX )

        theta = np.mat ( self.getA() )
        Xm = np.mat ( self.priorX )

        ypost = Xm * theta
        yprior = self.priorY
        error = ypost - yprior
        #error = error - np.mean ( error, axis = 0 )
        return np.dot ( error.T, error )

    def getCovarianceNoiseMatrix(self):
        """
            outputs the noise covariance matrix, R
        """
        return np.dot ( self.getB().T, self.getB() )


import numpy as np
import matplotlib.pyplot as plt
from numpy.fft import fft,fftfreq, ifft

def main():
	#setup domain
	#number of points
	n=int(input("Enter sampling rate: "))

	#time length in secs
	l= int(input("Enter time lenght in seconds, the time for which you want to perform the fft"))

	#angular frequency
	omg=2*np.pi/l

	#creating signals
	x=np.linspace(0,l,n);
	y1=1*np.cos(5*omg*x)
	y2=1*np.cos(10*omg*x)
	y3=0.5*np.cos(20*omg*x)

	y=y1+y2+y3

	freq=fftfreq(n)

	mask=freq>0

	fft_vals=fft(y)

	fft_theo=2*np.abs(fft_vals/n)

	plt.figure(1)
	plt.title('Original Image')
	plt.plot(x,y,color='xkcd:salmon',label='Original')
	plt.legend()

	plt.figure(2)
	plt.title('FFT')
	#plt.plot(freq,fft_vals,label='fft')
	#plt.title('True FFT')
	plt.plot(freq[mask],fft_theo[mask],label='true fft values')
	plt.show()


import sys
import math
from array import *
import numpy as np

n = int(sys.argv[1])
k = int(sys.argv[2])

P=[[0 for i in range(k+1)] for j in range(n+1)] # initialize

for i in range(n+1):
    for j in range(k+1):
        if j == 0 or i == j:
            P[i][j] = 1
        else:
            P[i][j] = P[i-1][j-1] + P[i-1][j]

r = P[n][k]

print(str(n) + " choose " + str(k) + " = " + str(r))


import scipy as sp
import scipy.linalg as linalg
from timeit import timeit

def DoolittleLU(matrix: sp.ndarray, dtype=float):
    n, m = matrix.shape
    l = sp.zeros((n, n), dtype=dtype)
    u = sp.zeros((n, m), dtype=dtype)

    for i in range(n):
        for k in range(i, m):
            s = 0
            for j in range(i):
                s += l[i][j] * u[j][k]

            u[i][k] = matrix[i][k] - s

        l[i][i] = 1
        for k in range(i+1, n):
            s = 0
            for j in range(i):
                s += l[k][j] * u[j][i]
            l[k][i] = (matrix[k][i] - s) / u[i][i]

    return l, u

def CroutLU(matrix: sp.ndarray, dtype=float):
    n, m = matrix.shape
    l = sp.zeros((n, m))
    u = sp.zeros((m, m))

    for j in range(m):
        for i in range(j, n):
            s = 0
            for k in range(j):
                s += l[i][k] * u[k][j]
            l[i][j] = matrix[i][j] - s

        u[j][j] = 1
        for i in range(j+1, m):
            s = 0
            for k in range(j):
                s += l[j][k] * u[k][i]
            u[j][i] = (matrix[j][i] - s) / l[j][j]

    return l, u

def LUDecomposition(matrix: sp.ndarray, dtype=float):
    n, m = matrix.shape
    L = sp.identity(n, dtype)
    U = sp.array(matrix, dtype=dtype)

    for i in range(1, n):
        for j in range(i):
            L[i][j], U[i][j] = U[i][j]/U[j][j], dtype(0)
            for k in range(j+1, m):
                U[i][k] -= U[j][k]*L[i][j]

    return L, U

def LUPPDecomposition(matrix: sp.ndarray, dtype=float):
    n, _ = matrix.shape
    absmatrix = sp.absolute(matrix)
    scales = sp.amax(absmatrix, axis=1)
    p = sp.arange(n)
    p = sorted(p, key=lambda i: absmatrix[i][0]/scales[i], reverse=True)

    P = sp.zeros((n, n))
    for idx, pivot in enumerate(p):
        P[idx][pivot] = 1

    L, U = LUDecomposition(P@matrix, dtype)
    return P.transpose(), L, U

def LUCPDecomposition(matrix: sp.ndarray, dtype=float):
    n, m = matrix.shape
    absmatrix = sp.absolute(matrix)
    rowscales = sp.amax(absmatrix, axis=1)
    colscales = sp.amax(absmatrix, axis=0)
    p = sp.arange(n)
    p = sorted(p, key=lambda i: absmatrix[i][0]/rowscales[i], reverse=True)
    q = sp.arange(m)
    q = sorted(q, key=lambda j: absmatrix[0][j]/colscales[j], reverse=True)

    P = sp.zeros((n, n))
    for idx, pivot in enumerate(p):
        P[idx][pivot] = 1
    Q = sp.zeros((m, m))
    for idx, pivot in enumerate(q):
        Q[idx][pivot] = 1

    L, U = LUDecomposition(P@matrix@Q, dtype)

    return P.transpose(), L, U, Q.transpose()

def CholeskyDecomposition(matrix: sp.ndarray, dtype=float):
    n, _ = matrix.shape
    L = sp.zeros((n, n))

    for i in range(n):
        for j in range(i):
            L[i][j] = matrix[i][j]
            for k in range(j):
                L[i][j] -= L[i][k] * L[j][k]
            L[i][j] /= L[j][j]
        L[i][i] = matrix[i][i]
        for k in range(i):
            L[i][i] -= L[i][k] ** 2
        L[i][i] **= 0.5

    return L

def LDLTDecomposition(matrix: sp.ndarray, dtype=float):
    L, U = LUDecomposition(matrix, dtype)

    return L, sp.diag(sp.diag(U))

def AssertLU(matrix: sp.ndarray, msg=''):
    print(msg)
    P, L, U = linalg.lu(matrix)
    if sp.allclose(matrix, P@L@U, rtol=0):
        print('PASS: linalg.lu')
        print(
            f'time: {timeit(lambda: linalg.lu(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: linalg.lu')

    L, U = DoolittleLU(matrix)
    if sp.allclose(matrix, L@U, rtol=0):
        print('PASS: DoolittleLU')
        print(
            f'time: {timeit(lambda: DoolittleLU(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: DoolittleLU')

    L, U = CroutLU(matrix)
    if sp.allclose(matrix, L@U, rtol=0):
        print('PASS: CroutLU')
        print(
            f'time: {timeit(lambda: CroutLU(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: CroutLU')

    L, U = LUDecomposition(matrix)
    if sp.allclose(matrix, L@U, rtol=0):
        print('PASS: LU decomposition')
        print(
            f'time: {timeit(lambda: LUDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: LU decomposition')

    P, L, U = LUPPDecomposition(matrix)
    if sp.allclose(matrix, P@L@U, rtol=0):
        print('PASS: LUPP decomposition')
        print(
            f'time: {timeit(lambda: LUPPDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: LUPP decomposition')

    P, L, U, Q = LUCPDecomposition(matrix)
    if sp.allclose(matrix, P@L@U@Q, rtol=0):
        print('PASS: LUCP decomposition')
        print(
            f'time: {timeit(lambda: LUCPDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: LUCP decomposition')

    # Symmetric matrix only
    if matrix.shape[0] == matrix.shape[1] and sp.allclose(matrix, sp.transpose(matrix), rtol=0):
        L, D = LDLTDecomposition(matrix)
        if sp.allclose(matrix, L@D@L.transpose(), rtol=0):
            print('PASS: LDLT decomposition')
            print(
                f'time: {timeit(lambda: LDLTDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
        else:
            print('FAIL: LDLT decomposition')

        L = CholeskyDecomposition(matrix)
        if sp.allclose(matrix, L@L.transpose(), rtol=0):
            print('PASS: Cholesky decomposition')
            print(
                f'time: {timeit(lambda: CholeskyDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
        else:
            print('FAIL: Cholesky decomposition')

    print()

def main():
    A = sp.array([[1, 1, 1],
                  [1, 2, 4],
                  [1, 3, 9]])
    AssertLU(A, 'Low rank(3) Vandermonde matrix')

    A = sp.array([[-1]])
    AssertLU(A, 'Single element matrix')

    A = sp.array([[3, 2, 4],
                  [2, 4, 3]])
    AssertLU(A, 'Non-square(2, 3) matrix')

    A = sp.array([[2, 4],
                  [3, 3],
                  [4, 2]])
    AssertLU(A, 'Non-square(3, 2) matrix')

    A = sp.array([[1, -2, 0],
                  [-2, 1, -2],
                  [0, -2, 1]])
    AssertLU(A, 'Symmetric matrix')

    A = sp.array([[4, 12, -16],
                  [12, 37, -43],
                  [-16, -43, 98]])
    AssertLU(A, 'Positive-definite matrix')

    A = sp.array([[1, 2, 3],
                  [2, 4, 7],
                  [3, 3, 3]])
    AssertLU(A, 'Bad condition of naive LU decomposition')

    A = sp.array([[1, 9, 3],
                  [2, 2, 7],
                  [3, 3, 3]])
    AssertLU(A, 'Bad condition of LUPP decomposition')

    A = sp.zeros((16, 16))
    for i in range(A.shape[0]):
        for j in range(A.shape[1]):
            A[i][j] = (i+1)**j
    AssertLU(A, 'High rank(16) Vandermonde matrix')

    A = sp.zeros((17, 17))
    for i in range(A.shape[0]):
        for j in range(A.shape[1]):
            A[i][j] = (i+1)**j
    AssertLU(A, 'High rank(17) Vandermode matrix')

    A = sp.array([[1, 1, 1],
                  [2, 2, 2],
                  [3, 3, 3]])
    AssertLU(A, 'Singular matrix')

    A = sp.rand(50, 50)
    AssertLU(A, 'Big(50, 50) matrix')

if __name__ == '__main__':
    main()


import numpy as np

class SoftmaxClassifier:
    def __init__(self):
        # Random initialization of W
        C, D = 10, 3073  # C :number of classes, D: dimension of each flattened image
        self.W = np.random.randn(C, D) * 0.001

    def train(self, x, y, lr=1e-5, reg=1e-3, num_iters=1000, batch_size=200):
        """
        Train the model using stochastic gradient descent.
        Arguments:
            x: D * N numpy array as the training data, where D is the dimension and N the training sample size
            y: 1D numpy array with length N as the labels for the training data
            lr: learning rate
            reg: regularization factor
            num_iters: number of iterations for training
            batch_size: batch size for the calculation of the mini-batch gradient descent
        Output:
            loss_record: Array containing the cross entropy loss history during the training process
        """
        _, N = x.shape  # N is the sample size

        # Train the model using mini-batch stochastic gradient descent.
        loss_record = []
        for it in range(num_iters):
            # Randomly chosen mini-batch
            indices = np.random.choice(N, batch_size, replace=True)
            x_batch = x[:, indices]
            y_batch = y[indices]

            # Calculate loss and gradient for the iteration
            loss, grad = self.cross_entropy_loss(x_batch, y_batch, reg)
            loss_record.append(loss)

            # Update W
            self.W -= lr * grad

        return loss_record

    def predict(self, x):
        """
        Predict labels using the trained model.
        Arguments:
            x: D * N numpy array as the test data, where D is the dimension and N the test sample size
        Output:
            y_pred: 1D numpy array with length N as the predicted labels for the test data
        """
        y = self.W.dot(x)
        y_pred = np.argmax(y, axis=0)
        return y_pred

    def cross_entropy_loss(self, x, y, reg):
        """
        Calculate the cross-entropy loss and the gradient for each iteration of training.
        Arguments:
            x: D * N numpy array as the training data, where D is the dimension and N the training sample size
            y: 1D numpy array with length N as the labels for the training data
        Output:
            loss: a float number of calculated cross-entropy loss
            dW: C * D numpy array as the calculated gradient for W, where C is the number of classes, and 10 for this model
        """

        # Calculation of loss
        z = np.dot(self.W, x)
        z -= np.max(z, axis=0)  # Max trick for the softmax, preventing infinite values
        p = np.exp(z) / np.sum(np.exp(z), axis=0)  # Softmax function
        L = -1 / len(y) * np.sum(np.log(p[y, range(len(y))]))  # Cross-entropy loss
        R = 0.5 * np.sum(np.multiply(self.W, self.W))  # Regularization term
        loss = L + R * reg  # Total loss

        # Calculation of dW
        p[y, range(len(y))] -= 1
        dW = 1 / len(y) * p.dot(x.T) + reg * self.W
        return loss, dW


# coding=utf-8
"""
If p is the perimeter of a right angle triangle with integral length sides, {a,b,c}, there are exactly three solutions for p = 120.

{20,48,52}, {24,45,51}, {30,40,50}

For which value of p ≤ 1000, is the number of solutions maximised?

Solution comment:
Assume a < b, and know b < c. Then a <= p/2, a < b <= (p-a)/2.

Then simply check if c^2 = a^2 + b^2 is possible and makes the
total perimeter equal to p.
"""
from math import sqrt
from gmpy2 import is_square
from numpy import zeros, argmax

p_all = xrange(3, 1000 + 1)
solutions = zeros(len(p_all))

for i, p in enumerate(p_all):
    for a in xrange(1, p//2+1):
        for b in xrange(a+1, (p - a)//2 + 1):
            c2 = a**2 + b**2
            if is_square(c2) and int(sqrt(c2)) + a + b == p:
                solutions[i] += 1

print p_all[argmax(solutions)]


import numpy as np
from distance import euclidean
from kmeans import KMeans

def elbow_method(X, k_range = range(1,9), init_centroid = "random", distance="euclidean"):
  X = np.array(X)
  distortion_scores = []
  silhouete_scores = []
  for k in k_range:
    model = KMeans(k = k, init_centroid=init_centroid, distance=distance)
    model.train(X)
    distortion_scores.append(model.inertia)
    silhouete_scores.append(silhouete_score(X, model.centroids))
  return distortion_scores, silhouete_scores

def silhouete_coef(x, centroids):
  distances = []
  if len(centroids) == 1 :
    return 0
  for centroid in centroids:
    distances.append(euclidean(x, centroid))
  distances.sort()
  return (distances[1] - distances[0]) / max(distances[0], distances[1])

def silhouete_score(X, centroids):
  X=np.array(X)
  silhouete_coefs = []
  for point in X:
    silhouete_coefs.append(silhouete_coef(point, centroids))
  return sum(silhouete_coefs) / len(silhouete_coefs)


from sympy.core import S, C, sympify, cacheit
from sympy.core.function import Function, ArgumentIndexError
from sympy.functions.elementary.miscellaneous import sqrt

###############################################################################
################################ ERROR FUNCTION ###############################
###############################################################################

class erf(Function):

    nargs = 1

    def fdiff(self, argindex=1):
        if argindex == 1:
            return 2*C.exp(-self.args[0]**2)/sqrt(S.Pi)
        else:
            raise ArgumentIndexError(self, argindex)

    @classmethod
    def eval(cls, arg):
        if arg.is_Number:
            if arg is S.NaN:
                return S.NaN
            elif arg is S.Infinity:
                return S.One
            elif arg is S.NegativeInfinity:
                return S.NegativeOne
            elif arg is S.Zero:
                return S.Zero
            elif arg.is_negative:
                return -cls(-arg)
        elif arg.is_Mul:
            if arg.as_coeff_mul()[0].is_negative:
                return -cls(-arg)

    @staticmethod
    @cacheit
    def taylor_term(n, x, *previous_terms):
        if n < 0 or n % 2 == 0:
            return S.Zero
        else:
            x = sympify(x)

            k = (n - 1)//2

            if len(previous_terms) > 2:
                return -previous_terms[-2] * x**2 * (n-2)/(n*k)
            else:
                return 2*(-1)**k * x**n/(n*C.Factorial(k)*sqrt(S.Pi))

    def _eval_as_leading_term(self, x):
        arg = self.args[0].as_leading_term(x)

        if C.Order(1,x).contains(arg):
            return arg
        else:
            return self.func(arg)

    def _eval_is_real(self):
        return self.args[0].is_real


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Blackbody functions for wavelength and frequency.

In wavelength space, the blackbody function assumes that the wavelength
is given in units of Angstroms.
"""

import numpy as np
from numba import jit

from pypython.constants import (ANGSTROM, BOLTZMANN, VLIGHT, WIEN_FREQUENCY, WIEN_WAVELENGTH, H, PI, STEFAN_BOLTZMANN)

@jit
def planck_lambda(temperature, lamda):
    """Calculate the monochromatic intensity for a black body given a
    temperature and frequency of interest.

    Parameters
    ----------
    temperature: float
        The temperature of the blackbody.
    lamda: np.ndarray or float
        The wavelength points to calculate the value at, in Angstroms.

    Returns
    -------
    b_lamda: float
        The value of the Planck function with the provided temperature and
        wavelength. Has units ergs s^-1 cm^-2 A^-1.
    """

    lcm = lamda * ANGSTROM
    x = H * VLIGHT / lcm / BOLTZMANN / temperature
    y = 2 * H * VLIGHT**2 / lcm**5
    b_lamda = y / (np.exp(x) - 1)

    return b_lamda

@jit
def planck_nu(temperature, frequency):
    """Calculate the monochromatic intensity for a black body given a
    temperature and frequency of interest.

    Parameters
    ----------
    temperature: float
        The temperature of the blackbody.
    frequency: np.ndarray or float
        The frequency points to calculate the vale at, in units of Hz.

    Returns
    -------
    b_nu: float
        The value of the Planck function with the provided temperature and
        frequency. Has units ergs s^-1 cm^-2 Hz^-1.
    """

    x = H * frequency / (BOLTZMANN * temperature)
    b_nu = (2 * H * frequency**3) / (VLIGHT**2 * (np.exp(x) - 1))

    return b_nu

def stefan_boltzmann(radius, temperature):
    """Calculate the luminosity for a spherical blackbody following from
    Stefan-Boltzmann.

    Parameters
    ----------
    radius: float
        The radius of the sphere.
    temperature: float
        The temperature of the blackbody.

    Returns
    -------
    lum: float
        The luminosity of the sphere.
    """
    return 4 * PI * radius**2 * STEFAN_BOLTZMANN * temperature**4

def wien_law(temperature, freq_space=False):
    """Calculate the peak wavelength of a blackbody curve.

    Parameters
    ----------
    temperature: float
        The temperature of the blackbody.
    freq_space: bool [optional]
        Return the peak in frequency space.

    Returns
    -------
    The wavelength (in Angstrom) or frequency where the blackbody curve is
    at maximum.
    """

    if freq_space:
        return WIEN_FREQUENCY * temperature
    else:
        return WIEN_WAVELENGTH / temperature / 1e-10


#This file contains implementation of Principal Component Analysis
import numpy as np

class PCA:
    def __init__(self, n_components):
    """Function used to initialize variables
    Args:
        n_components: Input parameter
    Returns:
        None
    """
        self.n_components = n_components
        self.components = None
        self.mean = None

    def fit(self, X):
        #Compute mean
        self.mean = np.mean(X, axis = 0)
        X = X- self.mean
        #Compute Covariance
        cov  = np.cov(X.T)
        #Eigen vectors and eigenvalues
        eigenvalues, eigenvectors = np. linalg.eig(cov)
        #Sort Eigenvectors
        eigenvectors = eigenvectors.T
        idxs = np.argsort(eigenvalues)[::-1]
        eigenvalues = eigenvalues[idxs]
        eigenvectors = eigenvectors[idxs]
        #Store first n in eigenvectors
        self.components = eigenvectors[0:self.n_components]

    def transform(self, X):
        X = X - self.mean
        return np.dot(X, self.components.T)

if __name__ == "__main__":
    # Imports
    import matplotlib.pyplot as plt
    from sklearn import datasets

    # data = datasets.load_digits()
    data = datasets.load_iris()
    X = data.data
    y = data.target

    # Project the data onto the 2 primary principal components
    pca = PCA(2)
    pca.fit(X)
    X_projected = pca.transform(X)

    print("Shape of X:", X.shape)
    print("Shape of transformed X:", X_projected.shape)

    x1 = X_projected[:, 0]
    x2 = X_projected[:, 1]

    plt.scatter(
        x1, x2, c=y, edgecolor="none", alpha=0.8, cmap=plt.cm.get_cmap("viridis", 3)
    )

    plt.xlabel("Principal Component 1")
    plt.ylabel("Principal Component 2")
    plt.colorbar()
    plt.show(


from scipy.spatial.distance import minkowski
import numpy as np

import matplotlib
import matplotlib.pyplot as plt

from numpy import linalg as LA
from math import sqrt

def euclidean_distance(spectrum_A,spectrum_B,power_value):
    spectrum_A=np.array(spectrum_A).flatten()
    spectrum_B=np.array(spectrum_B).flatten()

    #normalize according to the maximum value
    #max_A=max(spectrum_A)
    #max_B=max(spectrum_B)
    #print max_A,max_B
    #spectrum_A=np.array([x/max_A for x in spectrum_A.flatten()]).flatten()
    #spectrum_B=np.array([x/max_B for x in spectrum_B.flatten()]).flatten()

    #Normalize according to modulus of spectrum vectors
    norm_A=LA.norm(spectrum_A)
    norm_B=LA.norm(spectrum_B)
    spectrum_A=np.array([x/norm_A for x in spectrum_A.flatten()]).flatten()
    spectrum_B=np.array([x/norm_B for x in spectrum_B.flatten()]).flatten()

    eucl_metric=LA.norm(np.subtract(spectrum_A,spectrum_B))

    eucl_metric=np.power(eucl_metric,power_value)

    return 1.-eucl_metric

def cosine_sim_given_spectra(spectrum_A,spectrum_B,power_value=1.):

    spectrum_A=np.array(spectrum_A).flatten()
    spectrum_B=np.array(spectrum_B).flatten()

    dot_product=np.power(np.dot(spectrum_A,spectrum_B),power_value)

    norm_A=sqrt(np.power(np.dot(spectrum_A,spectrum_A),power_value))
    norm_B=sqrt(np.power(np.dot(spectrum_B,spectrum_B),power_value))
    """
    norm_A=LA.norm(spectrum_A)
    norm_B=LA.norm(spectrum_B)
    """
    kernel=dot_product/(norm_A*norm_B)
    return kernel

def plot_2D_matrix_with_axis_labels(matrix, x_axis_labels, y_axis_labels, title, savefig_name, save_fig_type,vmax,vmin,cmap=matplotlib.cm.get_cmap('Purples'), show_sim_values=True, fontsize=5):

    matrix=np.array(matrix)

    if vmax==None:
        vmax=max(np.array(matrix).flatten())
    if vmin==None:
        vmin=min(np.array(matrix).flatten())

    x_pos=np.arange(len(x_axis_labels))
    y_pos=np.arange(len(y_axis_labels))

    #plt.tight_layout()
    #fig, ax = plt.subplots()

    plt.imshow(matrix,vmax=vmax,vmin=vmin,cmap=cmap)
    plt.colorbar()

    #ax.xaxis.set_ticks_position('bottom') # need to call this after matshow, otherwise error
    plt.xticks(x_pos,x_axis_labels, rotation=90, fontsize=fontsize)
    plt.yticks(y_pos,y_axis_labels, fontsize=fontsize)
    plt.title(title, y=1.1)

    #Add text
    if show_sim_values:
        fmt = '.2f'
        thresh = max(matrix.flatten()) * 0.5
        for i, j in itertools.product(range(matrix.shape[0]), range(matrix.shape[1])):
            plt.text(j, i, format(matrix[i, j], fmt),
                     horizontalalignment="center",
                     color="white" if matrix[i, j] > thresh else "black", fontsize=fontsize)

    plt.tight_layout()
    plt.show()
    plt.savefig(savefig_name+save_fig_type)
    plt.close()

def similarity_metric(spectrum_A,spectrum_B,metric,power_value,p=3):
    """
    ARGUMENTS: spectrum_A,spectrum_B: signals/spectra to be compared
               metric: similarity metric to be used (possible choices: 'cosine_sim','euclidean','cross_correlation' (TODO last one incomplete))
               power_value: power to which result of similarity metric is raised

    RETURNS:   value of similarity metric

    """

    spectrum_A=np.array(spectrum_A).flatten()
    spectrum_B=np.array(spectrum_B).flatten()

    if metric=='cosine_sim':

        return cosine_sim_given_spectra(spectrum_A,spectrum_B,power_value)

    elif metric=='euclidean':

        return euclidean_distance(spectrum_A,spectrum_B,power_value)

    elif metric=='minkowski':
        zero_vector=np.zeros(spectrum_A.size)

        norm_A=minkowski(u=spectrum_A,v=zero_vector,p=p,w=None)
        norm_B=minkowski(u=spectrum_B,v=zero_vector,p=p,w=None)

        spectrum_A_normed=spectrum_A/norm_A
        spectrum_B_normed=spectrum_B/norm_B

        minkowski_AB=minkowski(u=spectrum_A_normed,v=spectrum_B_normed,p=p,w=None)
        return 1.-minkowski_AB

    elif metric=='cross_correlation':
        #Not sure if this doesnt give the same as skp since automatic mode is 'valid' .... https://docs.scipy.org/doc/numpy/reference/generated/numpy.correlate.html
        corr_A_B=np.correlate(spectrum_A,spectrum_B)
        corr_A_A=np.correlate(spectrum_A,spectrum_A)
        corr_B_B=np.correlate(spectrum_B,spectrum_B)

        normalized_corr=corr_A_B/(corr_A_A*corr_B_B)

        return np.power(normalized_corr,power_value)

    else:

        raise NotImplementedError("The chosen metric is not implemented.")

def compute_and_plot_cross_similarity(descriptors_A,descriptors_B,sim_metric,power_value,p,x_axis_labels,y_axis_labels,
                                      title,savefig_name,save_fig_type='.png',
                                      vmax=None, vmin=None,
                                      cmap=matplotlib.cm.get_cmap('Purples'), show_sim_values=True,
                                      fontsize=5):

    sim_matrix=[]

    for descriptor_1 in descriptors_A:

        sim_matrix_row=[]

        for descriptor_2 in descriptors_B:

            #coherence_matrix_row.append(coherence_kernel(descriptor_1,descriptor_2))
            sim_matrix_row.append(similarity_metric(descriptor_1,descriptor_2,sim_metric,power_value,p))

        #coherence_matrix.append(coherence_matrix_row)
        sim_matrix.append(sim_matrix_row)

    plot_2D_matrix_with_axis_labels(sim_matrix, x_axis_labels, y_axis_labels, title, savefig_name, save_fig_type, vmax, vmin, cmap, show_sim_values, fontsize)

    return sim_matrix


import tensorflow as tf
import numpy as np

sess = tf.Session()
sess.run(tf.global_variables_initializer())

a_2x3 = tf.constant([[1, 2, 3], [4, 5, 6]])
b_vec3 = tf.constant([10, 20, 30])

# This makes no sense mathematically
sum = a_2x3 + b_vec3

# In order to work around this, TensorFlow performs "broadcasting"
# turning the 3-vector into a matrix of two rows each with the elements
# of the 3-vector and applying the sum to that.

print(sess.run(sum, {}))

# [[11 22 33]
# [14 25 36]]

# Broadcasting with two matrices with different number of rows
# Like for the vector example above, the rows of the smaller one are replicated to the height of the other

b_1x3 = tf.constant([[10, 20, 30]])
print(sess.run(a_2x3 + b_1x3, {}))

# [[11 22 33]
# [14 25 36]]


"""
Author      :   Muhammad Reza Fahlevi
Dated       :   June 7th, 2021
Affiliation :   Departemen Ilmu Komputer,
                Fakultas Ilmu Komputer - Teknologi Informasi,
                Universitas Sumatera Utara, Indonesia
References  :   Benjamin Baka. Python Data Structures and Algorithms. 2017. Packt Publishing.
                García-Martínez C., Rodriguez F.J., Lozano M. (2018) Genetic Algorithms. In: Martí R., Pardalos P., Resende M. (eds) Handbook of Heuristics. Springer, Cham. https://doi.org/10.1007/978-3-319-07124-4_28
                https://docs.python.org/3.8/tutorial/index.html
                J M García et al 2020 J. Phys.: Conf. Ser. 1448 012020
                Stuart Russell and Peter Norvig. 2020. Artificial Intelligence: A Modern Approach, 4th Edition. University of California at Berkeley. Pearson.
"""

# Prepare the tools
import math
import pandas as pd
from numpy import random

# phi = lambda x,y: math.exp(-0.5 * ((x ** 2) + (y ** 2)))

def phi(x, y):
    first_denominator = 1 + ((x - 1) ** 2) + ((y - 1) ** 2)
    second_denominator = 1 + (1/4) * (((x + 0.5) ** 2)) + (1/36) * ((y - 1) ** 2)
    third_denominator = 1 + ((x - 2) ** 2) + ((y - 2) ** 2)
    fourth_denominator = 1 + ((x - 1) ** 2) + ((y + 1) ** 2)
    exp_term = (3 / 2) * math.exp(1 / first_denominator) + (-5 / 2) * math.exp(1 / second_denominator) + 2 * math.exp(1 / third_denominator) + 2 * math.exp(1 / fourth_denominator)
    return exp_term

xvect, yvect = random.random(10), random.random(10)
print(xvect, yvect)
print([phi(x, y) for x, y in zip(xvect, yvect)])

def competition(df_pop):
    st_candidate = df_pop.iloc[random.randint(9), 0:3]
    nd_candidate = df_pop.iloc[random.randint(9), 0:3]
    winner = st_candidate if st_candidate['fitness'] > nd_candidate['fitness'] else nd_candidate
    return winner

def pbxalpha(domain_interval, p1, p2, alpha):
    constant = abs(p1['xgenotype'] - p2['xgenotype'])
    p1_lower_bound = max(domain_interval[0], p1['xgenotype'] - (alpha * constant))
    p1_upper_bound = min(domain_interval[1], p1['xgenotype'] + (alpha* constant))

    constant = abs(p1['ygenotype'] - p2['ygenotype'])
    p2_lower_bound = max(domain_interval[0], p2['ygenotype'] - (alpha * constant))
    p2_upper_bound = min(domain_interval[1], p2['ygenotype'] + (alpha* constant))

    p1_offspring = [p1_lower_bound, p1_upper_bound]
    p2_offspring = [p2_lower_bound, p2_upper_bound]

    return p1_offspring, p2_offspring

gamma = lambda t, tmax, beta: (1 - (t / tmax)) ** beta

def delta(domain_interval, genotype, computed_gamma):
    get_probability = random.random()
    probability_criteria = 1 / 2
    normvar = random.random()
    if get_probability == probability_criteria:
        delta_value = (domain_interval[1] - genotype) * ((1 - normvar) ** computed_gamma)
    else:
        delta_value = (domain_interval[0] - genotype) * ((1 - normvar) ** computed_gamma)
    return delta_value

# Define the constant
NP = 10
Pm = 0.3
MAX_GENERATION = 50
problem_interval = [-5, 5]

def config_df(x_vect, y_vect):
    dframe = pd.DataFrame(
        {
            "xgenotype": x_vect,
            "ygenotype": y_vect,
            "fitness": [phi(x, y) for x, y in zip(x_vect, y_vect)]
        }, index = [(idx + 1) for idx in range(len(x_vect))]
    )
    data_frame = dframe.sort_values('fitness')
    data_frame = data_frame.iloc[1:10]
    return data_frame

def steady_state_genetic_algorithm(population_size, mutation_probability, max_generation):
    generations = []
    x_vector = random.random(population_size)
    y_vector = random.random(population_size)

    for generation in range(max_generation):
        df_population = config_df(x_vector, y_vector)
        generations.append(df_population)
        x_vector = [] # start a new population
        y_vector = []
        x_vector.append(df_population.iloc[population_size - 2, 0]) # add the fittest to the next generation
        y_vector.append(df_population.iloc[population_size - 2, 1])
        vect_size = len(x_vector)
        while vect_size != population_size:
            p1, p2 = competition(df_population), competition(df_population) # Selection
            offsprings = pbxalpha(problem_interval, p1, p2, alpha=0.5) # Crossover, return tuple of list
            current_mutation_rate = random.random()
            if current_mutation_rate < mutation_probability:
                p11 = offsprings[0][0] + delta(problem_interval, offsprings[0][0], gamma(generation, max_generation, beta=9))
                p12 = offsprings[0][1] + delta(problem_interval, offsprings[0][1], gamma(generation, max_generation, beta=9))
                p21 = offsprings[1][0] + delta(problem_interval, offsprings[1][0], gamma(generation, max_generation, beta=9))
                p22 = offsprings[1][1] + delta(problem_interval, offsprings[1][1], gamma(generation, max_generation, beta=9))
                offsprings = tuple([[p11, p12], [p21, p22]]) # mutation occur, return tuple of list
            else:
                offsprings = offsprings # no mutation occurs
            # Evaluation
            first_value = phi(offsprings[0][0], offsprings[0][1])
            second_value = phi(offsprings[1][0], offsprings[1][1])
            survivor = [offsprings[0][0], offsprings[0][1]] if first_value > second_value else [offsprings[1][0], offsprings[1][1]]
            x_vector.append(survivor[0])
            y_vector.append(survivor[1])
            vect_size += 1
    return generations

for i in range(25):
    print(steady_state_genetic_algorithm(NP, Pm, MAX_GENERATION)[MAX_GENERATION - 1])


import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np

def model(inputs, W, b):
    """
    This is a linear classifier model.
    :param inputs:
    :param W:
    :param b:
    :return:
    """

    outputs = tf.matmul(inputs, W) + b
    return outputs

def square_loss(true_labels, predictions):
    """
    This function gets the loss of model based on predictions and labels.
    :param true_labels:
    :param predictions:
    :return:
    """

    loss_per_sample = tf.square(predictions - true_labels)
    return tf.reduce_mean(loss_per_sample)

def fit(X, y, W, b, learning_rate=0.1):
    """
    Fit the model.
    :param X:
    :param y:
    :param W:
    :param b:
    :param learning_rate:
    :return:
    """

    with tf.GradientTape() as tape:
        predictions = model(inputs=X, W=W, b=b)
        loss = square_loss(true_labels=y, predictions=predictions)

    gradients_dW, gradients_db = tape.gradient(target=loss, sources=[W, b])  # Get gradients
    W.assign_sub(gradients_dW * learning_rate)  # Update W
    b.assign_sub(gradients_db * learning_rate)  # Update b

    return loss, W, b

# Generate random data
num_samples_per_class = 1000
negative_samples = np.random.multivariate_normal(mean=[0, 3], cov=[[1, 0.5], [0.5, 1]],
                                                 size=num_samples_per_class)
negative_labels = np.zeros(shape=(num_samples_per_class, 1))

positive_samples = np.random.multivariate_normal(mean=[3, 0], cov=[[1, 0.5], [0.5, 1]],
                                                 size=num_samples_per_class)
positive_labels = np.ones(shape=(num_samples_per_class, 1))

X = np.vstack((negative_samples, positive_samples)).astype('float32')
y = np.vstack((negative_labels, positive_labels)).astype('float32')

# Initialize the model parameters
input_dims = 2
output_dims = 1
W = tf.Variable(initial_value=tf.random.uniform(shape=(input_dims, output_dims)))
b = tf.Variable(initial_value=tf.zeros(shape=(output_dims,)))

# Train model
epochs = 50
loss = np.inf

for i_epoch in range(epochs):
    loss, W, b = fit(X=X, y=y, W=W, b=b)

    print(f'Loss at step {i_epoch}: {loss:.4f}')

# Get predictions
predictions = model(inputs=X, W=W, b=b)

# Plot results
x = np.linspace(-1, 4, 100)

weights = W.numpy()
bias = b.numpy()
hyper_line = -weights[0] / weights[1] * x + (0.5 - bias) / weights[1]
plt.plot(x, hyper_line, '-r')
plt.scatter(X[:, 0], X[:, 1], c=predictions[:, 0] >= 0.5)
plt.show()


"""
Purpose: To show an example of principal component analysis (PCA)
for doing linear dimension reduction on a collection of image
patches.  Each patch can be thought of as a point in high dimensional
Euclidean space, where each dimension in that space is a pixel, and
the value along that axis is the brightness of that pixel.
White pixels have 255 as their value, black pixels have 0 as their value,
and gray pixels are somewhere in between.

When you do the dimension reduction with numbers that are very different,
you should see them clustering into two distinct regions.  In this example,
we show that 3s and 0s end up in different parts of the space
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.metrics import pairwise_distances
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import skimage
from sklearn.decomposition import PCA

def imscatter(X, P, dim, zoom=1):
    """
    Plot patches in specified locations in 2D

    Parameters
    ----------
    Y : ndarray (N, 2)
        The positions of each patch in 2D
    P : ndarray (N, dim*dim)
        An array of all of the patches
    dim : int
        The dimension of each patch

    """
    #https://stackoverflow.com/questions/22566284/matplotlib-how-to-plot-images-instead-of-points
    ax = plt.gca()
    for i in range(P.shape[0]):
        patch = np.reshape(P[i, :], (dim, dim))
        x, y = Y[i, :]
        im = OffsetImage(patch, zoom=zoom, cmap = 'gray')
        ab = AnnotationBbox(im, (x, y), xycoords='data', frameon=False)
        ax.add_artist(ab)
    ax.update_datalim(X)
    ax.autoscale()
    ax.set_xticks([])
    ax.set_yticks([])

# Steup an array that will hold 200 images in 784 dimensions (28x28 pixels)
X = np.zeros((200, 784))

# Load in all of the zeros into the first 100 rows
for i in range(100):
    x = skimage.io.imread("Digits/0/{}.png".format(i))
    x = x.flatten()
    X[i, :] = x # Put image i into row i

# Load in all of the threes into the next 100 rows
for i in range(100):
    x = skimage.io.imread("Digits/3/{}.png".format(i))
    x = x.flatten()
    X[100+i, :] = x # Put image i into row i

# This is an example of what pairwise_distances is actually
# doing to compute distances between each pair of points in X.
# In this case, we're computing Euclidean distance between
# the point at index 10 and the point at index 20
d_10_20 = np.sqrt(np.sum(X[10, :] - X[20, :])**2)

# But it is much faster to use the pairwise_distances function
# to do all of them
# When you run this code, you should see that most of the
# 0s are closer to each other than they are to the 3s, and
# vice versa
D = pairwise_distances(X)
plt.figure()
plt.imshow(D, cmap='magma_r')
plt.xticks([50, 150], ["0s", "3s"])
plt.yticks([50, 150], ["0s", "3s"])
plt.ylim([200, 0])
plt.title("Distance Matrix for Digits")
plt.colorbar()

# When you run this code, you'll see a plot of the images
# at their projected locations
plt.figure(figsize=(12, 12))
pca = PCA(n_components=2)
Y = pca.fit_transform(X)
imscatter(Y, X, 28)
plt.show()


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

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 二维目标函数

def Rastrigrin(x: np.ndarray):
    return 20 + x[0] ** 2 - 10 * np.cos(2 * np.pi * x[0]) + x[1] ** 2 - 10 * np.cos(2 * np.pi * x[1])

def obj_fun(x: np.ndarray):
    return abs(0.2 * x[0]) + 10 * np.sin(5 * x[0]) + 7 * np.cos(4 * x[0])

def obj_fun2(x: np.ndarray):
    return (1 - x[0]) ** 2 + 100 * (x[0] - x[1] ** 2) ** 2

def Easom(x: np.ndarray):
    return -1 * np.cos(x[0]) * np.cos(x[1]) * np.exp(-1 * (x[0] - np.pi) ** 2 - (x[0] - np.pi) ** 2)

def obj_fun_test1(x: np.ndarray):
    return -1 * (10 + np.sin(1 / x) / ((x - 0.16) ** 2 + 0.1))

def Bohachevsky(x: np.ndarray):
    return x[0] ** 2 + x[1] ** 2 - 0.3 * np.cos(3 * np.pi * x[0]) + 0.3 * np.cos(4 * np.pi * x[1]) + 0.3

def Schaffersf6(x: np.ndarray):
    return 0.5 + (np.sin(x[0] ** 2 + x[1] ** 2) ** 2 - 0.5) / (1 + 0.001 * (x[0] ** 2 + x[1] ** 2) ** 2) ** 2

def Shubert(x: np.ndarray):
    s1 = 0
    s2 = 0
    for i in range(1, 6):
        s1 += i * np.cos((i + 1) * x[0] + i)
        s2 += i * np.cos((i + 1) * x[1] + i)
    return s1 + s2

# 一维目标函数
def fun1(x):
    return -(x + 10 * np.sin(5 * x) + 7 * np.cos(4 * x))

class GSA():
    def __init__(self,T_max=400,T_min=10,pop=10,new_pop=10,cur_g=1,p=0.9,tour_n=10,func=fun1,shape=1, **kwargs):
        """
        @param T_max: 最大温度
        @param T_min: 最小温度
        @param pop: 种群的个体数量
        @param new_pop: 每个个体生成的新解的数量
        @param cur_g: 当前迭代代数
        @param p: 锦标赛中的概率p
        @param tour_n: 一次锦标赛的选手数
        @param func: 函数
        @param x_best: 最优点
        @param y_best: 最优点的函数值
        @param shape: x的维度
        """
        self.T_max = T_max
        self.T = T_max  # 当前温度
        self.T_min = T_min
        self.pop = pop
        self.new_pop = new_pop
        self.cur_g = cur_g
        self.p = p
        self.tour_n = tour_n
        self.func = func
        self.shape = shape
        self.x_best = [0]*shape
        self.y_best = 0
        self.x_history = []
        self.T_history = [self.T]
        self.m, self.n, self.quench = kwargs.get('m', 1), kwargs.get('n', 1), kwargs.get('quench', 1)
        self.lower, self.upper = kwargs.get('lower', -10), kwargs.get('upper', 10)
        self.c = self.m * np.exp(-self.n * self.quench)

    def xrange(self, xmin: np.ndarray, xmax: np.ndarray):  # 输入x范围，尚未考虑多维情形
        self.xmin = xmin
        print('x的下界是'+str(xmin))
        self.xmax = xmax
        print('x的上界是'+str(xmax))

    def init_pop(self):
        pop1 = []
        x_init = [10,10]
        for i in range(self.pop):
            pop1.append(self.get_new(x_init))
        return pop1

    def judge(self, df):
        if df < 0:  # 新解 < 原解 ---> 直接接受
            return True
        else:
            pp = np.exp(-1 * (df / self.T))  # 新解 > 原解 ---> 计算接受概率p
            rand_p = random.random()  # 产生 rand_p ~ Uniform(0, 1)
            if pp > rand_p:
                return True
            else:
                return False

    def get_new(self,x):
        u = np.random.uniform(-1, 1, size=self.shape)  # 产生均匀分布的[Xi,……] u ~ Uniform(0, 1, size = d)
        x_new = x + 20 * np.sign(u) * self.T * ((1 + 1.0 / self.T) ** np.abs(u) - 1.0)
        return x_new

    def generate(self,old_pop):
        x_new = []

        for i in range(len(old_pop)):
            while len(x_new) < (i+1)*self.new_pop:
                xnew = self.get_new(old_pop[i])

                # Metropolis准则
                df = self.func(xnew) - self.func(old_pop[i])  # 计算函数值差值
                if self.judge(df):
                    x_new.append(xnew)

        return x_new

    def tournament(self,x):
        """
        计算每个点的适应值，选择N个点作为生存集（GA）锦标赛。
        如果适应值最大的点被舍弃了，那么就随机舍弃一个点，又把它加回来。
        处理为先把适应值最大的点加进去
        """
        survive = []
        x_cur = x
        y_cur = [self.func(xx) for xx in x]
        best_index = y_cur.index(min(y_cur))
        self.x_best = x_cur[best_index]
        self.y_best = y_cur[best_index]
        survive.append(self.x_best)  # 先把最好的点放进去
        del(x_cur[best_index])  # 把最好的点删了，其他的进行锦标赛，最后选出self.pop-1个点

        #     进行选择（锦标赛方法）
        #     choose k (the tournament size) individuals from the population at random
        #     choose the best individual from the tournament with probability p
        #     choose the second best individual with probability p*(1-p)
        #     choose the third best individual with probability p*((1-p)^2)
        #     and so on

        fitness = [self.y_best - self.func(xx) for xx in x_cur]
        num_individuals = len(fitness)
        indices = list(range(len(fitness)))
        selected_indices = []
        selection_size = self.pop - 1

        while (len(selected_indices) < selection_size):
            np.random.shuffle(indices)
            idx_tournament = indices[0:self.tour_n]
            fit = []
            for i in range(len(idx_tournament)):
                fit.append(fitness[idx_tournament[i]])
            maxindex = fit.index(max(fit))
            winner = idx_tournament[maxindex]
            r = random.random()
            if r < self.p:
                selected_indices.append(winner)
            selected_indices = list(set(selected_indices))

        for i in range(len(selected_indices)):
            survive.append(x_cur[selected_indices[i]])

        return survive

    def cool(self):
        self.T = self.T * 0.7
        self.T_history.append(self.T)

    def disp(self):
        if self.shape == 1:
            pass

        elif self.shape == 2:
            fig = plt.figure()
            ax = plt.axes(projection='3d')
            x0_list = [[0 for col in range(len(self.x_history[0]))] for row in range(len(self.x_history))]
            x1_list = [[0 for col in range(len(self.x_history[0]))] for row in range(len(self.x_history))]
            for i in range(len(self.x_history)):
                for j in range(len(self.x_history[i])):
                    x0_list[i][j] = self.x_history[i][j][0]
                    x1_list[i][j] = self.x_history[i][j][1]
            x_min, x_max = min(min(row) for row in x0_list), max(max(row) for row in x0_list)
            y_min, y_max = min(min(row) for row in x1_list), max(max(row) for row in x1_list)
            x = np.arange(x_min, x_max, 0.01)
            y = np.arange(y_min, y_max, 0.01)
            X, Y = np.meshgrid(x, y)
            Z = self.func(np.array([X, Y]))
            ax.plot_surface(X, Y, Z, cmap='Blues', alpha=0.3)

            for i in range(len(self.x_history)):
                x0, x1, t = x0_list[i], x1_list[i], self.T_history[i]
                print(x0)
                y = [self.func(x) for x in self.x_history[i]]
                plt.title("当前温度 ："+str(t))
                p = ax.scatter(x0, x1, y, c='b')
                plt.pause(0.5)
                if i != len(self.x_history) - 1:
                    p.remove()
            plt.scatter(self.x_best[0], self.x_best[1], self.y_best, c='r', marker='*')
            plt.xlabel("x0")
            plt.ylabel("x1")
            plt.show()

    def main(self):
        pop1 = self.init_pop()
        self.x_history.append(pop1)
        while self.T > self.T_min:

            print('----------------当前迭代代数是' + str(self.cur_g) + '-------------------')
            new_pop = self.generate(pop1)
            pop1 = self.tournament(new_pop)
            self.x_history.append(pop1)
            self.cool()
            print('当前温度'+str(self.T))
            self.cur_g = self.cur_g + 1

            sur_pop_y = [self.func(xx) for xx in pop1]
            best_index = sur_pop_y.index(min(sur_pop_y))
            self.x_best = pop1[best_index]
            self.y_best = sur_pop_y[best_index]

class fastGSA(GSA):
    def __init__(self, T_max=400, T_min=10, pop=10, new_pop=10, cur_g=1, p=0.9, tour_n=10, func=fun1, shape=1, **kwargs):
        super().__init__(T_max, T_min, pop, new_pop, cur_g, p, tour_n, func, shape, **kwargs)
        self.name = 'SAFast'
        self.m, self.n, self.quench = kwargs.get('m', 1), kwargs.get('n', 1), kwargs.get('quench', 1)
        self.lower, self.upper = kwargs.get('lower', -10), kwargs.get('upper', 10)
        self.c = self.m * np.exp(-self.n * self.quench)

    def get_new(self, x):
        r = np.random.uniform(-1, 1, size=self.shape)
        xc = np.sign(r) * self.T * ((1 + 1.0 / self.T) ** np.abs(r) - 1.0)
        x_new = x + xc * (self.upper - self.lower)
        return x_new

    def cool(self):
        self.T = self.T_max * np.exp(-self.c * (self.cur_g * 10) ** self.quench)
        self.T_history.append(self.T)

class cauchyGSA(GSA):
    def __init__(self, T_max=400, T_min=10, pop=10, new_pop=10, cur_g=1, p=0.9, tour_n=10, func=fun1, shape=1, **kwargs):
        super().__init__(T_max, T_min, pop, new_pop, cur_g, p, tour_n, func, shape, **kwargs)
        self.name = 'SACauchy'
        self.learn_rate = kwargs.get('learn_rate', 0.5)

    def get_new_x(self, x):
        u = np.random.uniform(-np.pi / 2, np.pi / 2, size=self.shape)
        xc = self.learn_rate * self.T * np.tan(u)
        x_new = x + xc
        return x_new

    def cool_down(self):
        self.T = self.T * 0.7
        self.T_history.append(self.T)

class BoltzmannGSA(GSA):
    def __init__(self, T_max=400, T_min=10, pop=10, new_pop=10, cur_g=1, p=0.9, tour_n=10, func=fun1, shape=1, **kwargs):
        super().__init__(T_max, T_min, pop, new_pop, cur_g, p, tour_n, func, shape, **kwargs)
        self.name = 'SABoltzmann'
        self.lower, self.upper = kwargs.get('lower', -10), kwargs.get('upper', 10)
        self.learn_rate = kwargs.get('learn_rate', 0.5)

    def get_new_x(self, x):
        std = min(np.sqrt(self.T), (self.upper - self.lower) / 3.0 / self.learn_rate) * np.ones(self.shape)
        xc = np.random.normal(0, 1.0, size=self.shape)
        x_new = x + xc * std * self.learn_rate
        return x_new

    def cool_down(self):
        self.T = self.T * 0.7
        self.T_history.append(self.T)

if __name__ == "__main__":
    start = time.time()  # 开始计时
    x_init = np.array([10, 10])  # 设置初始点（行向量）
    x_min = np.array([-10, -10])
    x_max = np.array([10, 10])
    # demo = GSA(func=Rastrigrin,T_max=1000,T_min=10,pop=30,new_pop=15,cur_g=1,p=0.9,tour_n=15,shape=2)
    demo = fastGSA(func=Rastrigrin, T_max=1, T_min=1e-5, pop=30, new_pop=15, cur_g=1, p=0.9, tour_n=15, shape=2)
    # demo = cauchyGSA(func=Rastrigrin, T_max=1000, T_min=100, pop=30, new_pop=15, cur_g=1, p=0.9, tour_n=15, shape=2)
    # demo = BoltzmannGSA(func=Rastrigrin, T_max=1000, T_min=1, pop=30, new_pop=15, cur_g=1, p=0.9, tour_n=15, shape=2)
    demo.xrange(x_min, x_max)
    demo.main()
    demo.disp()
    end = time.time()  # 结束计时
    print("Duration time: %0.3f" % (end - start))  # 打印运行时间
    print('iterations:' + str(demo.cur_g))
    print('x_best:'+str(demo.x_best))
    print('y_best:'+str(demo.y_best))


import numpy as np

def rotationMatrix(inc, dec, normal=True):
    """
        Take an inclination and declination angle and return a rotation matrix

    """

    phi = -np.deg2rad(np.asarray(inc))
    theta = -np.deg2rad(np.asarray(dec))

    Rx = np.asarray(
        [[1, 0, 0], [0, np.cos(phi), -np.sin(phi)], [0, np.sin(phi), np.cos(phi)]]
    )

    Rz = np.asarray(
        [
            [np.cos(theta), -np.sin(theta), 0],
            [np.sin(theta), np.cos(theta), 0],
            [0, 0, 1],
        ]
    )

    if normal:
        R = Rz.dot(Rx)
    else:
        R = Rx.dot(Rz)

    return R

def dipazm_2_xyz(dip, azm_N):
    """
    dipazm_2_xyz(dip,azm_N)

    Function converting degree angles for dip and azimuth from north to a
    3-components in cartesian coordinates.

    INPUT
    dip     : Value or vector of dip from horizontal in DEGREE
    azm_N   : Value or vector of azimuth from north in DEGREE

    OUTPUT
    M       : [n-by-3] Array of xyz components of a unit vector in cartesian

    Created on Dec, 20th 2015

    @author: dominiquef
    """

    # Modify azimuth from North to Cartesian-X
    azm_X = (450.0 - azm_N) % 360.0

    D = np.deg2rad(np.asarray(dip))
    I = np.deg2rad(azm_X)

    M = np.zeros(3)
    M[0] = np.cos(D) * np.cos(I)
    M[1] = np.cos(D) * np.sin(I)
    M[2] = np.sin(D)

    return M


import numpy as np
from scipy.signal import resample

def _cov(x, y):
    x = x - np.mean(x)
    y = y - np.mean(y)
    return np.mean(np.multiply(x,y))

def _pcc(x, y):
    s_x = np.std(x)
    s_y = np.std(y)
    return _cov(x,y)/(s_y * s_x)

def _remove_unvoiced(x):
    y = np.zeros(len(x))
    i = 0
    for v in x:
        if v > 0:
            y[i] = v
            i += 1
    return y[:i]

def pcc_on_2_pitch_curve(ref, dut, remove_unvoiced=True):
    """ This function computes the Pearsons correlation ceofficient (PCC) between 2 pitch curves.

    Args:
        ref (numpy array): sequence of an arbitrary length (assumes unvoiced segments are indicated by numbers <= 0)
        dut (numpy array): sequence of an arbitrary length (assumes unvoiced segments are indicated by numbers <= 0)
        remove_unvoiced (bool, optional):if True, all unvoiced segments will be removed from the pitch curves. Defaults to True.

    Returns:
        float: the PCC between both sequences
    """

    # will remove all parts of the signal that are unvoiced
    if remove_unvoiced:
        ref = _remove_unvoiced(ref)
        dut = _remove_unvoiced(dut)

    # resample the second signal to get the same lenght
    dut_rs = resample(dut, len(ref))

    # compute the Pearson's correlation
    return _pcc(ref, dut_rs)


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

import numpy as np
import matplotlib.pyplot as plt

curve_data = np.genfromtxt('data_1.csv', delimiter = ',')
# Extract x and y coordinates from the curve data
x = curve_data[1:,0]
y = curve_data[1:,1]

x_squared = np.power(x,2)
x_matrix = np.transpose([x_squared, x, np.ones(np.shape(x))])

#Least Square Implementation
def least_square_curvefitting():
    '''
    Function to fit a curve using Least squares given minimum number of points (x,y) from the data.
    Y = XB
    E = (Y - XB)T(Y - XB)
    dE/dB = 0
    B = inv(XTX)*XT*Y
    '''

    B = np.matmul(np.linalg.inv(np.matmul(np.transpose(x_matrix), x_matrix)),
                  np.matmul(np.transpose(x_matrix), y))
    y_estimate = np.matmul(x_matrix, np.transpose(B))
    plt.figure()
    plt.xlabel('x axis')
    plt.ylabel('y axis')
    plt.title('Data_1')
    plt.scatter(x, y, label = 'Scattered data')
    plt.plot(x, y_estimate, 'r', label = 'Curve Fitting')
    plt.legend()
    plt.savefig('least_square_curvefitting_data_1.png')

#Least Square with Regularization
def least_square_regularization():
    '''
    Function to fit a curve using Least squares given minimum number of points (x,y) from the data.
    Y = XB
    E = (Y - XB)T(Y - XB)
    dE/dB = 0
    B = (inv(XTX)+L)*(XT*Y)
    '''
    L=np.dot(np.identity(3),5)
    Breg = np.matmul(np.linalg.inv((np.matmul(np.transpose(x_matrix), x_matrix))+L), np.matmul(np.transpose(x_matrix), y))
    y_estimatereg = np.matmul(x_matrix, np.transpose(Breg))
    plt.figure()
    plt.xlabel('x axis')
    plt.ylabel('y axis')
    plt.title('Data_1')
    plt.scatter(x, y, label = 'Scattered data')
    plt.plot(x, y_estimatereg , 'r', label = 'Curve Fitting')
    plt.legend()
    plt.savefig('least_square_regularization_data_1.png')

#Total Least Square
def total_least_square():
    yn=np.reshape(y,(y.shape[0],1))
    a=np.concatenate((x_matrix,yn),axis=1)
    u, s, vh = np.linalg.svd(a, full_matrices=True)
    V = vh.T
    Vxy = V[:3,3]
    Vyy = V[3,3]
    a_tls = - Vxy / Vyy
    VV=V[:,3].reshape(4,1)
    Xtyt=np.matmul(np.matmul(a,VV),VV.T)
    Xtyt=Xtyt[:,0:3]
    y_tls=np.matmul((Xtyt+x_matrix),a_tls)
    plt.figure()
    plt.xlabel('x axis')
    plt.ylabel('y axis')
    plt.title('Data_1')
    plt.scatter(x, y, label = 'Scattered data')
    plt.plot(x, y+y_tls , 'r', label = 'Curve Fitting')
    plt.legend()
    plt.savefig('total_least_square_data_1.png')

def main():
    least_square_curvefitting()
    least_square_regularization()
    total_least_square()

if __name__=='__main__':
    main()


from numpy import genfromtxt
import numpy as np

temp = genfromtxt('Q2Q3_input.csv', delimiter=',') #Generate the numpy array
inp = temp[1:, 1:]
mean = inp.mean(axis = 0)
std = inp.std(axis = 0)
#normed = (inp - inp.mean(axis = 0)) / inp.std(axis = 0)
normed = inp

def sq_dist(a, b):
	return np.sum((a - b) ** 2, axis = 1) #will return squared distance

c1 = np.array([1,1,1,1,1,1])
c2 = np.array([0,0,0,0,0,0])

for i in range(50):
	d_a = sq_dist(c1, normed) #Distance from cluster to points
	d_b = sq_dist(c2, normed)

	w1 = np.divide(d_b, d_a + d_b) #Find the fuzzy weights
	w2 = np.divide(d_a, d_a + d_b)

	sse = np.sum(np.multiply(d_a, w1) + np.multiply(d_b, w2)) #Calculate SSE

	w_1 = w1.reshape(695, 1)
	w_2 = w2.reshape(695, 1)

	w1_sq = np.multiply(w_1**2, np.ones((695, 6))) #Broadcasting
	w2_sq = np.multiply(w_2**2, np.ones((695, 6)))

	w1_m = np.multiply(w1_sq, normed) #M step starts here
	w2_m = np.multiply(w2_sq, normed) #This is the numerator

	new_c1 = np.sum(w1_m, axis = 0) / np.sum(w1**2) #Sum up the numerator and divide it by weights
	new_c2 = np.sum(w2_m, axis = 0) / np.sum(w2**2)
	dist = np.sum(np.abs(new_c1 - c1)) + np.sum(np.abs(new_c2 - c2))

	print(sse)
	print(new_c1)
	print(new_c2)

	if dist < 0.001: #break conditions
		break

	c1 = new_c1
	c2 = new_c2


##module LUdecomp
'''
a= LUdecomp(a)
LUdecompsition: [L][U]=[a]

x= LUsolve(a,b)

solution phase: solves [L][U]{x}={b}
'''

import numpy as np

def LUdecomp(a):
    n = len(a)
    for k in range(0,n-1):
        for i in range(k+1,n):
            if a[i,k] != 0:
                lam = a[i,k]/a[k,k]
                a[i,k+1:n]=a[i,k+1:n]-lam*a[k,k+1:n]
                a[i,k]=lam
    return a

def LUsolve(a,b):
    n = len(a)
    for k in range(1,n):
        b[k] = b[k]-np.dot(a[k,0:k],b[0:k])
    b[n-1] = b[n-1]/a[n-1,n-1]

    for k in range(n-2,-1,-1):
        b[k] = (b[k]-np.dot(a[k,k+1:n],b[k+1:n]))/a[k,k]

    return b


# Calculates an estimation for 1/pi
# with various convergences

import numpy as np

def Borwein_2(k):			# Quadratic convergence
	a = np.sqrt(2)
	b = 0
	p = 2 + np.sqrt(2)
	for i in range(k):
		b = (1+b)*np.sqrt(a)/(a+b)
		a = (np.sqrt(a)+1/np.sqrt(a))/2
		p = ((1+a)*p*b)/(1+b)
	return p

def Borwein_3(k):			# Cubic convergence
	a = 1./3
	s = (np.sqrt(3)-1)/2
	for i in range(k):
		r = 3/(1+2*(1-s**3)**(1./3))
		s = (r-1)/2
		a = r**2*a-3**i*(r**2-1)
	return a

def Borwein_4(k):			# Quartic convergence
	a = 6-4*np.sqrt(2)
	y = np.sqrt(2)-1
	for i in range(k):
		y = (1-(1-y**4)**(1./4))/(1+(1-y**4)**(1./4))
		a = a*(1+y)**4 -2**(2*i+3)*y*(1+y+y**2)
	return a


import numpy as np

N = np.array([[ 3.35774, -1.68663, -0.62988, -1.04123],
              [-1.68663,  3.15898, -0.90703, -0.56532],
              [-0.62988, -0.90703,  4.05644, -2.51953],
              [-1.04123, -0.56532, -2.51953, 4.12608]])
# Moore-Penrose pseudo inverse
print('Determinant: {:.3f}'.format(np.linalg.det(N)))
inv1 = np.linalg.pinv(N)
print('Moore-Penrose pseudo inverse')
print(inv1)
print('N == N * inv1 * N ? {}'.format(np.allclose(N, N.dot(inv1).dot(N))))
# Singular value decomposition (SVD)
U, S, VT = np.linalg.svd(N)     # attention V transpose is returned!
S1 = np.array([ 1.0/s if abs(s) > 1e-6 else 0 for s in S])
inv2 = VT.T.dot(np.diag(S1)).dot(U.T)
print('SVD general inverse')
print(inv2)
print('inv1 == inv2 ? {}'.format(np.allclose(inv1, inv2)))
# regularize matrix with eigenvalues
lamb, E = np.linalg.eig(N)
N1 = np.zeros(N.shape)
for i in range(lamb.size):
    if abs(lamb[i]) < 1e-6:
        N1 = N1 + np.outer(E[:,i], E[:,i])  # diad product
inv3 = np.linalg.inv(N + N1) - N1
print('General inverse from regularization')
print(inv3)
print('inv1 == inv3 ? {}'.format(np.allclose(inv1, inv3)))


import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from os import path

def plotData(x, y, h,title):
    plt.subplots()
    plt.plot(x, h)
    plt.scatter(x, y, c='red')
    plt.grid(color='gray', linestyle='--', linewidth=.6,
             axis='both', which='both', alpha=.4)
    plt.title(title)
    plt.show()

def calcR2(actual_y, predicted_y, avg_y):
    return np.square(predicted_y - avg_y).sum()/np.square(actual_y - avg_y).sum()

def calcSlope(x, y, mean_x, mean_y):
    numerator = np.sum((x - mean_x) * (y - mean_y))
    denominator = np.sum(np.square(x - mean_x))
    return numerator/denominator

def run():
    data_file = path.join(path.dirname(__file__),'../../data/food-cart.csv')
    data = np.genfromtxt(data_file, delimiter=',')
    X = data[:,0]
    Y = data[:,1]
    mean_x = np.mean(X)
    mean_y = np.mean(Y)
    slope_m = calcSlope(X, Y, mean_x, mean_y)
    intercept_c = mean_y - mean_x * slope_m
    m = len(X)
    H = []
    for i in range(0, m):
        H.append(slope_m * X[i] + intercept_c)
    title = "Slope : {}, Y Intercept : {}".format(slope_m, intercept_c)
    print("Slope : {}, Y Intercept : {}, Prediction Accuracy (R2 Error) : {}".format(
        slope_m, intercept_c, calcR2(Y, H, mean_y)))
    plotData(X, Y, H,title)


#!/usr/local/bin/python3

import numpy as np

def gompertz_func_fit(x,a,b,c):
    return c*np.exp(-a*np.exp(-b*x))

def gompertz_func(x, pars):
    a = pars[0]
    b = pars[1]
    c = pars[2]
    return c*np.exp(-a*np.exp(-b*x))
################################################
###############################################
##############################################
def gompertz_der_func_fit(x,a,b,c):
    return a*b*c*np.exp(-a*np.exp(-b*x) - b*x)

def gompertz_der_func(x, pars):
    a = pars[0]
    b = pars[1]
    c = pars[2]
    return a*b*c*np.exp(-a*np.exp(-b*x) - b*x)
################################################
###############################################
##############################################
def logistic_func_fit(x,N,xmax,b):
    return N/(1+np.exp(-(x-xmax)/b))

def logistic_func(x, pars):
    N = pars[0]
    xmax = pars[1]
    b = pars[2]
    return N/(1+np.exp(-(x-xmax)/b))
################################################
###############################################
##############################################
def logistic_der_func_fit(x,N,xmax,b):
    return ((N/b)*np.exp(-(x-xmax)/b))/((1+np.exp(-(x-xmax)/b))**2)

def logistic_der_func(x, pars):
    N = pars[0]
    xmax = pars[1]
    b = pars[2]
    return ((N/b)*np.exp(-(x-xmax)/b))/((1+np.exp(-(x-xmax)/b))**2)
################################################
###############################################
##############################################

def logistic_gen_func_fit(x,a,m,n,tau):
    return a*( (1+m*np.exp(-x/tau))/ (1+n*np.exp(-x/tau)) )

def logistic_gen_func(x, pars):
    a = pars[0]
    m = pars[1]
    n = pars[2]
    tau = pars[3]
    return a*( (1+m*np.exp(-x/tau))/ (1+n*np.exp(-x/tau)) )

def logistic_gen_der_func_fit(x,a,m,n,tau):
    return (a/(1+n*np.exp(-x/tau))**2) * \
                    ( (n/tau)*np.exp(-x/tau) - (m/tau)*np.exp(-x/tau) )

def logistic_gen_der_func(x, pars):
    a = pars[0]
    m = pars[1]
    n = pars[2]
    tau = pars[3]
    return (a/(1+n*np.exp(-x/tau))**2) * \
                    ( (n/tau)*np.exp(-x/tau) - (m/tau)*np.exp(-x/tau) )
################################################
###############################################
##############################################
def lin_func_fit(x, a,b):
    return a + b * x

def lin_func(x, pars):
    intercept = pars[0]
    coeff = pars[1]
    return intercept + coeff * x
################################################
###############################################
##############################################
def exp_func_fit(x, a,b):
    return a*np.exp(b*x)

def exp_func(x, pars):
    a = pars[0]
    b = pars[1]
    return a*np.exp(b*x)
################################################
###############################################
##############################################
map_func = {'linear':{'get_fit':lin_func_fit,
                     'get_values':lin_func,
                     'p0':None},
            'exponential':{'get_fit':exp_func_fit,
                     'get_values':exp_func,
                     'p0':None},

            'gompertz':{'get_fit':gompertz_func_fit,
                     'get_values':gompertz_func,
                       'p0':[10,1,100]},
            'gompertz_der':{'get_fit':gompertz_der_func_fit,
                     'get_values':gompertz_der_func,
                       'p0':[10,1,100]},

            'logistic':{'get_fit':logistic_func_fit,
                     'get_values':logistic_func,
                       'p0':None},
            'logistic_der':{'get_fit':logistic_der_func_fit,
                     'get_values':logistic_der_func,
                       'p0':None},

            'logistic_gen':{'get_fit':logistic_gen_func_fit,
                     'get_values':logistic_gen_func,
                       'p0':None},
            'logistic_gen_der':{'get_fit':logistic_gen_der_func_fit,
                     'get_values':logistic_gen_der_func,
                       'p0':None},
                 }


import numpy

'''
Gradient descent is an optimization algorithm for finding the local minimum of a
differentiable function. We continue running the algorithm until our parameters
converge to an approximate solution.
'''

# these are our training examples
# size is our input, so that is a "feature"
# # of bedrooms is an input, so that is also a feature
# price is our output, so that is the "target"
sizes = [2104, 1416, 1534, 852, 1234]
bedrooms = [3, 2, 3, 2, 4]
prices = [400, 232, 315, 178, 231]

# `m` is the number of training examples, meaning the number of rows
# `n` is the number of features
m, n = len(prices), 2

# initialize our parameters at 0
params = [0, 0, 0]

# our learning rate
alpha = 1E-8

# let's express our features as one list
features = [[1 for _ in range(m)], sizes, bedrooms]

# our hypothesis function
h = lambda x, i: sum([params[j] * x[j][i] for j in range(n+1)])

# distance between hypothesis and targets
# iterate
while d := numpy.sqrt(sum([numpy.square(h(features, i) - prices[i]) for i in range(m)])) > 50:
    for i in range(m):
        for j in range(n+1):
            # this is stochastic gradient descent
            params[j] -= alpha / m * (h(features, i) - prices[i]) * features[j][i]

print(params)
print([h(features, i) for i in range(m)])

# let's reset...
params = [0, 0, 0]

# distance between hypothesis and targets
# iterate
while d := numpy.sqrt(sum([numpy.square(h(features, i) - prices[i]) for i in range(m)])) > 50:
    for j in range(n+1):
        # this is batch gradient descent
        pd = 0
        for i in range(m):
            pd += (h(features, i) - prices[i]) * features[j][i]
        params[j] -= alpha / m * pd

print(params)
print([h(features, i) for i in range(m)])

'''
As you can see above, these are the applications of the same algorithm but in different
loops. One is a batch, and the other is stochastic. They produce very similar results.
However, stochastic is much faster when working with a large number of training examples.

DISCLAIMER:
Note that these algorithms are pythonic interpretations of the pseudo-code, and there are other
methods of optimizing this. These samples are just to represent the algorithm at play.
'''


import numpy as np

def E(x):
    u = x[0]
    v = x[1]
    return (u*np.exp(v) - 2*v*np.exp(-u))**2

def grad_E(x):
    grad = np.zeros(2)
    u = x[0]
    v = x[1]
    grad[0] = 2*(u*np.exp(v) - 2*v*np.exp(-u))*(np.exp(v) + 2*v*np.exp(-u))
    grad[1] = 2*(u*np.exp(v) - 2*v*np.exp(-u))*(u*np.exp(v) - 2*np.exp(-u))
    return grad

def coord_descent(E, grad_E, init=np.ones(2), eta=0.1, tol=1e-6, max_iter=10**5):
    iters=0
    u = init
    error = E(u)
    while error > tol and iters < max_iter:
        u[0] = u[0] - eta*grad_E(u)[0]
        u[1] = u[1] - eta*grad_E(u)[1]
        error = E(u)
        iters = iters+1
        print(grad_E(u))
        print(u)
    return u, iters, error

u, iters, error = coord_descent(E, grad_E, init=np.ones(2), tol=1e-30, max_iter=15)
print(u, iters, error)


import scipy
import numpy as np
from scipy.ndimage import zoom
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.ticker import LinearLocator, FormatStrFormatter
from matplotlib import cm
# Changing figure's font to Times New Roman
plt.rcParams["font.family"] = "serif"
plt.rcParams["font.serif"] = "Times New Roman"

'''
NAC 3-D Plot Code
'''
# 3-D axis creation
fig = plt.figure(figsize=plt.figaspect(0.5))
ax = fig.add_subplot(1, 2, 1, projection='3d')

# Two functions for multiplication, and creating transformation matrix W
sigmoid = lambda y: 1 / (1 + np.exp(-y))
tanh = lambda x: ((np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x)))

# Make data
X = np.arange(-10, 10, 0.25)
Y = np.arange(-10, 10, 0.25)
X, Y = np.meshgrid(X, Y)
# Final function for Transformation Matrix W
Z = tanh(X)*sigmoid(Y)

# Axis Labelling
ax.set_xlabel('tanh(x)', fontsize=14)
ax.set_ylabel('sigmoid(y)', fontsize=14)
ax.set_zlabel('W', fontsize=16)
ax.set_title('(a) NAC surface plot', fontsize=18, y=-0.04)

# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False)

# Customize the z axis.
ax.set_zlim(-1.11, 1.11)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.4, aspect=6)

'''
NALU 3-D Plot Code
'''

ax = fig.add_subplot(1, 2, 2, projection='3d')

# Sub-function decleration for NALU units
sigmoid = lambda y: 1 / (1 + np.exp(-y))
tanh = lambda x: ((np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x)))
# learned gate
g_x = lambda x: 1 / (1 + np.exp(-x))
# small epsilon value
eps = 10e-2
# log-exponential space
m_y = lambda y: np.exp(np.log(np.abs(y)+eps))

# Make data
X = np.arange(-10, 10, 0.25)
Y = np.arange(-10, 10, 0.25)
X, Y = np.meshgrid(X, Y)

# Final function for Transformation Matrix W
W_nac = tanh(X)*sigmoid(Y)
# Final function for NALU
Z = ( (g_x(Y)*W_nac) + ((1-g_x(Y))*m_y(Y)) )

# Axis Labelling
ax.set_xlabel('tanh(x)', fontsize=14)
ax.set_ylabel('sig-M/G(y), exp(log(|y|+eps))', fontsize=14)
ax.set_zlabel('Y', fontsize=16)
ax.set_title('(b) NALU surface plot',  fontsize=18, y=-0.04)

# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False)

# Customize the z axis.
ax.set_zlim(-1.1, 10.1)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

fig.colorbar(surf, shrink=0.4, aspect=6)

# show plot
plt.show()
# save plot in given directory
fig.savefig('final_assets/nac-nalu-comparison.png')


import numpy as np
#np.random.seed(0)
def sigmoid (x):
    return 1/(1 + np.exp(-x))
def sigmoid_derivative(x):
    return x * (1 - x)
#Input datasets
inputs = np.array([[0,0],[0,1],[1,0],[1,1]])
expected_output = np.array([[0],[1],[1],[0]])
epochs = 10000
lr = 0.1
inputLayerNeurons, hiddenLayerNeurons, outputLayerNeurons = 2,2,1
#Random weights and bias initialization
hidden_weights = np.random.uniform(size=(inputLayerNeurons,hiddenLayerNeurons))
hidden_bias =np.random.uniform(size=(1,hiddenLayerNeurons))
output_weights = np.random.uniform(size=(hiddenLayerNeurons,outputLayerNeurons))
output_bias = np.random.uniform(size=(1,outputLayerNeurons))
print("Initial hidden weights: ",end='')
print(*hidden_weights)
print("Initial hidden biases: ",end='')
print(*hidden_bias)
print("Initial output weights: ",end='')
print(*output_weights)
print("Initial output biases: ",end='')
print(*output_bias)
#Training algorithm
for _ in range(epochs):
#Forward Propagation
    hidden_layer_activation = np.dot(inputs,hidden_weights)
    hidden_layer_activation += hidden_bias
    hidden_layer_output = sigmoid(hidden_layer_activation)
    output_layer_activation =np.dot(hidden_layer_output,output_weights)
    output_layer_activation += output_bias
    predicted_output = sigmoid(output_layer_activation)
#Backpropagation
    error = expected_output - predicted_output
    d_predicted_output = error * sigmoid_derivative(predicted_output)
    error_hidden_layer = d_predicted_output.dot(output_weights.T)
    d_hidden_layer = error_hidden_layer * sigmoid_derivative(hidden_layer_output)
#Updating Weights and Biases
    output_weights +=hidden_layer_output.T.dot(d_predicted_output) * lr
    output_bias += np.sum(d_predicted_output,axis=0,keepdims=True)* lr
    hidden_weights += inputs.T.dot(d_hidden_layer) * lr
    hidden_bias += np.sum(d_hidden_layer,axis=0,keepdims=True) *lr
print("Final hidden weights: ",end='')
print(*hidden_weights)
print("Final hidden bias: ",end='')
print(*hidden_bias)
print("Final output weights: ",end='')
print(*output_weights)
print("Final output bias: ",end='')
print(*output_bias)
print("\nOutput from neural network after 10,000 epochs: ",end='')
print(*predicted_output)


#!/usr/bin/env python
import numpy as np
from typing import Callable

def rectangle(a: float, b: float, f: Callable[[np.array], np.array],
        h: float) -> float:
    return h * np.sum(f(np.arange(a + h / 2, b + h / 2, h)))

def trapezoid(a: float, b: float, f: Callable[[np.array], np.array],
        h: float) -> float:
    return h / 2 * (f(a) + 2 * np.sum(f(np.arange(a + h, b, h))) + f(b))

def simpson(a: float, b: float, f: Callable[[np.array], np.array],
        h: float) -> float:
    return h / 6 * (f(a) + 2 * np.sum(f(np.arange(a + h, b, h))) +
        4 * np.sum(f(np.arange(a + h / 2, b + h / 2, h))) + f(b))


"""
Code to explore operations on numpy arrays.
"""

# imports
import numpy as np

print('\nCreate a vector (a 1-D array) from a [list]')
a = np.array([1,2,3,4,5,6])
print(a)

print('\nIndexing is just like you learned for lists')
print('e.g. a[::2] gets every other item')
print(a[::2])

print('\nThe index of the last element is -1')
print('so a[-1] gives:')
print(a[-1])

# create 2-D arrays using arange and reshape
# - arange makes an array of integers, starting at 0
# - reshape reshapes an array according to the "size"
# in the tuple you pass to it.
aa = np.array(np.arange(12)).reshape((6,2))
bb = np.array(np.arange(12)).reshape((2,6))
print('\n2-D arrays of different shapes:')
print('\naa =')
print(aa)
print('\nbb =')
print(bb)

# indexing: for 2-D arrays:
# - axis 0 is the rows and
# - axis 1 is the columns,
# and you address each axis in turn in the [], separated by commas
print('\nIndexing a 2-D array: aa[:3, :]')
print(aa[:3, :]) # the second : means "all items of that axis"

print('\nElement-by-element operation (like .* in MATLAB): ')
#
print('\n - exponentiation')
print(aa**2.7) # exponentiation is **
#
print('\n - square root')
print(np.sqrt(aa)) # square root
#
print('\n - Error from aa + bb')
try:
    print(aa + bb) # addition (throws an error because aa and bb are different shapes)
except ValueError as err:
    print(err)
print('\n - aa + bb.T works')
print(aa + bb.T) # need to take transpose of bb so it has the same shape as aa

print('\nMatrix multiplication using @: aa @ bb')
print(aa @ bb)

print('\nMatrix multiplication using @: bb @ aa')
print(bb @ aa)

print('\nConcatenate two arrays')
cc = np.concatenate((aa,aa), axis=1)
print(cc)

print('\nFlatten an array')
print(aa.flatten())

print('\n Make a Boolean mask of aa >= 5')
mask = aa >= 5
print(mask)

print('\n Use the mask to get just those elements of aa: aa[mask]')
print(aa[mask])
print('Note that this returns just a 1-D arrray (flattened)')

"""
Exercises to try on your own:

What happens if you make a copy of a by typing b = a,
and then change an element in a by hand?  Does b change as well?
Now try it again using the copy method b = a.copy() and see
what happens.

To change an element by hand just assign it: a[3] = 17

You can convert an array of integers to an array of floats this way:
af = a.astype(float)

Figure out how to find the mean, standard deviation, max, and index of
the max of an array.

Missing data is represented as the number np.nan.  What happens if
you put a nan in your array and then calculate the mean?  You have
to convert the array to floats first before you can put a nan in.
Then also try the method np.nanmean() on the array with a nan.

Look around in other sub-modules using dir() and help():
* np.random (quickly generate arrays of random numbers, e.g. np.random.randn())
* np.linalg (linear algebra stuff like the inverse of a matrix: np.linalg.inv())
* np.ma (masked arrays)
* scipy.stats (have to import scipy first)

"""