"""Known matrices related to physics"""

from sympy import Matrix, I

def msigma(i):
    """Returns a Pauli matrix sigma_i. i=1,2,3
    See also:
    http://en.wikipedia.org/wiki/Pauli_matrices
    """
    if i==1:
        mat=( (
            (0, 1),
            (1, 0)
            ) )
    elif i==2:
        mat=( (
            (0, -I),
            (I, 0)
            ) )
    elif i==3:
        mat=( (
            (1, 0),
            (0, -1)
            ) )
    else:
        raise IndexError("Invalid Pauli index")
    return Matrix(mat)

def mgamma(mu,lower=False):
    """Returns a Dirac gamma matrix gamma^mu in the standard
    (Dirac) representation.

    If you want gamma_mu, use gamma(mu, True).

    We use a convention:

    gamma^5 = I * gamma^0 * gamma^1 * gamma^2 * gamma^3
    gamma_5 = I * gamma_0 * gamma_1 * gamma_2 * gamma_3 = - gamma^5

    See also:

    http://en.wikipedia.org/wiki/Gamma_matrices

    """
    if not mu in [0,1,2,3,5]:
        raise IndexError("Invalid Dirac index")
    if mu == 0:
        mat = (
                (1,0,0,0),
                (0,1,0,0),
                (0,0,-1,0),
                (0,0,0,-1)
                )
    elif mu == 1:
        mat = (
                (0,0,0,1),
                (0,0,1,0),
                (0,-1,0,0),
                (-1,0,0,0)
                )
    elif mu == 2:
        mat = (
                (0,0,0,-I),
                (0,0,I,0),
                (0,I,0,0),
                (-I,0,0,0)
                )
    elif mu == 3:
        mat = (
                (0,0,1,0),
                (0,0,0,-1),
                (-1,0,0,0),
                (0,1,0,0)
                )
    elif mu == 5:
        mat = (
                (0,0,1,0),
                (0,0,0,1),
                (1,0,0,0),
                (0,1,0,0)
                )
    m= Matrix(mat)
    if lower:
        if mu in [1,2,3,5]:
            m = - m
    return m

#Minkowski tensor using the convention (+,-,-,-) used in the Quantum Field
#Theory
minkowski_tensor = Matrix( (
    (1,0,0,0),
    (0,-1,0,0),
    (0,0,-1,0),
    (0,0,0,-1)
    ))


import matplotlib.pyplot as plt
from scipy.stats import poisson
from functools import reduce
from numpy.polynomial import Polynomial

def compose(fn, x, n):
    return reduce(lambda res, func: func(res), [fn] * n, x)

def equal_probability():
    number_of_surnames = 10
    number_of_surviving_sons = 5
    number_of_generations = 10

    fn = Polynomial([1 / (number_of_surviving_sons + 1)] * (number_of_surviving_sons + 1))

    plot_graph(fn,
               number_of_surnames,
               number_of_generations,
               "Equal Probability with Number of Surnames = {0} and Number of Surviving Sons = {1}".format(
                   number_of_surnames, number_of_surviving_sons)
               )

def varying_likelyhood():
    number_of_surnames = 10
    number_of_surviving_sons = 5
    number_of_generations = 10

    chance_of_i_surviving_sons = [1 / (number_of_surviving_sons + i) for i in range(1, number_of_surviving_sons + 1)]
    chance_of_0_surviving_sons = 1 - sum(chance_of_i_surviving_sons)

    fn = Polynomial([chance_of_0_surviving_sons] + chance_of_i_surviving_sons)

    plot_graph(fn,
               number_of_surnames,
               number_of_generations,
               "Varying Likelyhood with Number of Surnames = {0} and Number of Surviving Sons = {1}".format(
                   number_of_surnames, number_of_surviving_sons)
               )

def poisson_distribution():
    number_of_surnames = 10
    number_of_surviving_sons = 5
    mean_number_of_surviving_sons = 1
    number_of_generations = 10

    chance_of_i_surviving_sons = [
        poisson.pmf(i, mean_number_of_surviving_sons) for i in range(0, number_of_surviving_sons + 1)
    ]

    fn = Polynomial(chance_of_i_surviving_sons)

    plot_graph(fn,
               number_of_surnames,
               number_of_generations,
               "Poisson Distribution with Number of Surnames = {0} and Number of Surviving Sons = {1}".format(
                   number_of_surnames, number_of_surviving_sons)
               )

def plot_graph(fn, number_of_surnames, number_of_generations, title):
    generating_functions = [compose(fn, 0, generation) for generation in range(0, number_of_generations)]

    points = [number_of_surnames - number_of_surnames * func for func in generating_functions]

    plt.plot(points)
    plt.xlabel('Generation')
    plt.ylabel('Number of Surnames')
    plt.title(title)
    plt.axis([0, number_of_generations, 0, number_of_surnames])
    plt.show()

if __name__ == "__main__":
    equal_probability()
    varying_likelyhood()
    poisson_distribution()


#!/usr/bin/env python
'''
| Filename    : polyinterp.py
| Description : Polynomial interpolation of data points.
| Author      : Pushpendre Rastogi
| Created     : Tue Sep  6 00:10:47 2016 (-0400)
| Last-Updated: Tue Sep  6 00:11:35 2016 (-0400)
|           By: Pushpendre Rastogi
|     Update #: 1
Copied from http://hsakamoto.com/docs/numerical/ch3/301.html
September 6 2016
'''
import numpy as np
import matplotlib.pyplot as plt

def PI(x_data, y_data):
    """ return the polynomially interpolated function
    for a given set of data points by Newton's method
    """
    a = coeff(x_data, y_data)
    n = len(x_data)

    def f(xi):
        """ polynomially interpolated function
        """
        v = a[n - 1]  # initial value
        for k in range(1, n):
            # compute backwardly
            v = a[-k - 1] + (xi - x_data[-k - 1]) * v
        return v
    return f

def coeff(x_data, y_data):
    """ return the coefficients for Newton's polynomial
    """
    x = np.array(x_data)
    y = np.array(y_data)
    m = len(y)
    a = y.copy()  # initial value (a[0] = y[0])
    for k in range(m - 1):
        for i in range(k, m - 1):
            # compute divided difference of k-th order for all i >= k
            a[i + 1] = (a[i + 1] - a[k]) / (x[i + 1] - x[k])
    return a  # coefficient


import math
import numpy as np
import matplotlib.pyplot as plt
import timeit
"""
Constants for movement, subject to change.
May also make hor_offset a non-constant to allow matplotlib to
"animate" the wave moving from left to right.
"""
AMPLITUDE = 140
WAVELENGTH = 840
HOR_OFFSET = 0
VER_OFFSET = 0

SEG_LEN = 280
HEAD_LEN = 270
NUM_JOINTS = 6

def sine(x : float) -> float:
    """
    Trig Sine Function in the form f(x) = (a * sin((2pi/wv)*x + c)) + d
    :param x: angle in radians
    :return: sin value given the constants
    """

    return (AMPLITUDE * np.sin((2 * np.pi / WAVELENGTH) * x + HOR_OFFSET)) + VER_OFFSET

def find_joint_coords(sine_func) -> list:
    """
    Function using euclidean distance formula and sine function to solve the
    positions of each joint according to the given sine function. Assuming the
    snake is oriented with its head on the left hand side at x = 0, and all "tail"
    to the right of the head in positive x-axis.
    :param: sine function callable
    :return: array of tuples in format [(0, y_0), (x_1, y_1)...]
        representing position of each joint. y_0 representing the leftmost tip
        of the head of the snake, which will be x = 0.
    """

    result = [[0.0], [sine_func(0)]]
    # result = [[0.0], [0.0]]

    for i in range(0, NUM_JOINTS):
        guessed_dist = 0.0
        min_x = 0.0 + result[0][i]
        max_x = SEG_LEN + result[0][i]
        guessed_x = 0.0
        # essentially estimate the x coordinate of the next joint in the sequence
        # getting down to +/- 0.5mm in precision to the ideal distance.
        while not ((guessed_dist <= SEG_LEN + 0.5) and (guessed_dist >= SEG_LEN - 0.5)
                   or (max_x - min_x <= 1.0)):
            guessed_x = ((max_x - min_x) / 3.0) + min_x
            # guessed_x = ((max_x + min_x) / 2.0)
            guessed_dist = math.sqrt((guessed_x - result[0][i])**2
                                     + (sine_func(guessed_x) - result[1][i])**2)
            if guessed_dist < SEG_LEN:
                min_x = guessed_x
            else:
                max_x = guessed_x
        result[0].append(guessed_x)
        result[1].append(sine_func(guessed_x))

    return result

if __name__ == "__main__":
    found_coords = find_joint_coords(sine)
    print(found_coords)
    print("Time taken: " + str(timeit.timeit(lambda: find_joint_coords(sine), number=1)))

    plt.plot(found_coords[0], found_coords[1])

    sine_x = range(0, WAVELENGTH * 2)
    sine_y = []
    for i in sine_x:
        sine_y.append(sine(i))
    plt.plot(sine_x, sine_y)
    plt.xlim([0, 4 * AMPLITUDE])
    plt.ylim([-2 * AMPLITUDE, 2 * AMPLITUDE])
    plt.show()


import numpy as np
import scipy.stats as ss

def dnorm(x, mu, sigma):
    sigma += np.eye(sigma.shape[0]) * 1e-8
    return ss.multivariate_normal.logpdf(x, mu, sigma)

class GMM_EM:
    """
    GMM by EM.

    Methods:
        fit(data, max_iter, threshold): Fit the model to data.
        predict(x): Predict cluster labels for x.
    """

    def __init__(self, n_clusters):
        """
        Constructor Methods:

        Args:
            n_clusters(int): number of clusters.
        """

        self.n_clusters = n_clusters

        self.pi = None
        self.mus = [None] * self.n_clusters
        self.sigmas = [None] * self.n_clusters

    def fit(self, data, max_iter=200, threshold=1e-8):
        """
        Fit the model to data.

        Args:
            data: Array-like, shape (n_samples, n_dim)
            max_iter: maximum number of EM steps.
            threshold: threshold to step iteration.
        """

        assert data.ndim == 2
        n_data = data.shape[0]

        # Initialize
        z = self._initialization(data)
        self.pi = np.array([np.mean(z == cluster) for cluster in range(self.n_clusters)])
        self.mus = [np.mean(data[z == cluster, :], axis=0) for cluster in range(self.n_clusters)]
        self.sigmas = [np.cov(data[z == cluster, :].T) for cluster in range(self.n_clusters)]

        old_ll = 0
        for iter in range(max_iter):

            # E-step
            log_p = np.array([dnorm(data, mu=self.mus[cluster], sigma=self.sigmas[cluster]) + np.log(self.pi[cluster])
                          for cluster in range(self.n_clusters)])
            max_p = np.max(log_p, axis=0)
            sum_p = np.log(np.sum(np.exp(log_p - max_p), axis=0)) + max_p
            log_p -= sum_p
            p = np.exp(log_p)

            # M-step
            self.pi = np.sum(p, axis=1) / n_data
            for cluster in range(self.n_clusters):
                effective_size = np.sum(p, axis=1)[cluster]
                self.mus[cluster] = np.sum(p[cluster].reshape(-1, 1) * data, axis=0) / effective_size
                self.sigmas[cluster] = ((data - self.mus[cluster]).T * p[cluster]) @ (data - self.mus[cluster]) / effective_size

            # Calculate (negative) log_likelihood
            new_ll = -np.sum(sum_p)
            if abs(new_ll-old_ll) <= threshold:
                break
            else:
                old_ll = new_ll

        return self

    def predict(self, x):
        """
        Predict cluster labels for x.

        Args:
            x: Array-like, shape (n_samples, n_dim)
        Return:
            Array-like, shape (n_samples, )
        """
        log_prob = [dnorm(x, self.mus[cluster], self.sigmas[cluster]) + np.log(self.pi[cluster])
                     for cluster in range(self.n_clusters)]
        log_prob = np.vstack(log_prob)
        z = np.argmax(log_prob, axis=0)
        return z

    def _initialization(self, data, max_iter=50):
        """
        Initialization by K-Means.
        """
        means = data[np.random.choice(data.shape[0], self.n_clusters, replace=False)]    # pick random samples as center
        z = np.zeros(data.shape[0])

        for iter in range(max_iter):
            dist = [np.sum((data - means[cluster]) ** 2, axis=1) for cluster in range(self.n_clusters)]
            dist = np.vstack(dist)
            z = np.argmin(dist, axis=0)
            means = [np.mean(data[z == cluster], axis=0) for cluster in range(self.n_clusters)]

        return z


import numpy as np
from numpy import pi as pi
from numpy import cos as cos
from numpy import sin as sin
from numpy import linalg
from matplotlib import pyplot as plt

# input units and values
units = input('units: ')

sigX = float(input("sigma-x: "))
sigY = float(input("sigma-y: "))
tauXY = float(input("tau-XY: "))

#calculate principal normal stresses and absolute maximum shear stress
Cauchy = np.matrix([[sigX,tauXY],[tauXY,sigY]])

sig_prncpl = linalg.eigvals(Cauchy)
sig_prncpl = sorted(sig_prncpl,reverse=True)
tau_abs = abs(sig_prncpl[0]-sig_prncpl[1])/2

#print value outputs
print("\nPrincipal normal stresses:\n" +
	str(round(sig_prncpl[0],2)) + units + "\n" +
	str(round(sig_prncpl[1],2)) + units + "\n")
print("Absolute maximum shear stress:\n" +
	str(round(tau_abs,2))  + units + "\n")

#calculations for plot of Mohr's circle
#center of circle, corresponding to average in-plane normal stresses
sig_avg = (sig_prncpl[0] + sig_prncpl[1])/2

#initialize theta
theta = np.linspace(0,2*pi,256)

#create plot
plt.figure(figsize=(10,10),dpi=80)
plt.subplot(1,1,1)

#plot Mohr's circle
X = tau_abs*cos(theta)+sig_avg
Y = tau_abs*sin(theta)
plt.plot(X,Y,color="blue")

#notable points on graph
#absolute maximum shear stress
plt.plot(sig_avg,tau_abs,marker='o',markersize=6,color="black",
	label="Absolute maximum shear stress\n" + "(" + str(round(tau_abs,2)) + units + ")")
#principal normal stresses
#maximum
plt.plot(sig_prncpl[0],0,marker='o',markersize=6,color="black",
	label="Principal normal stresses\n" + "(" + str(round(sig_prncpl[0],2)) + units +
	", " + str(round(sig_prncpl[1],2)) + units + ")")
#minimum
plt.plot(sig_prncpl[1],0,marker='o',markersize=6,color="black")
#current stress state
#X-axis
plt.plot(sigX,tauXY,marker='o',markersize=4,color="red",
	label="Current stress state\n" + "(" + str(round(sigX,2)) + units +
	", " + str(round(sigY,2)) + units + ")")
#Y-axis
plt.plot(sigY,-tauXY,marker='o',markersize=6,color="red")
plt.grid(True)

#format axes and grid
ax = plt.gca()  # gca stands for 'get current axis'
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',sig_avg))
plt.gca().invert_yaxis()
plt.title("Mohr's Circles")
plt.legend(loc='upper left')
plt.show()


import math
import copy
import numpy as np
import prettymatrix
from prettytable import PrettyTable
from sympy import *
# Métodos diretos
## Método de Cholesky
def Determinantes(A, ordem):
    k = 1
    tempA = [[]]
    while k <= ordem:
        for i in range(k):
            for j in range(k):
                tempA[i].append(A[i][j])
        if(round(np.linalg.det(tempA), 8) == 0): return (k,round(np.linalg.det(tempA), 8))
        if k < ordem:
            for i in range(k):
                for j in range(k):
                    tempA[i].pop()
            tempA.append([])
        k+=1
    return (0,round(np.linalg.det(tempA), 8))

def Transposta(A, ordem):
    k = ordem
    mT = []
    for c in range(k):
        mT.append([])
    for i in range(k):
        for j in range(k):
            mT[i].append( A[j][i] )
    soma = 0
    for i in range(k):
        for j in range(k):
            if(mT[i][j] != A[i][j]):
                soma += 1
    if soma == 0:
        return 1

def valoresG(A, G, ordem):
    k = ordem
    for i in range(k):
        for j in range(k):
            if(i == 0 and j == 0):
                G[0][0] = A[0][0] ** (1/2)
            elif(i == j):
                soma = 0
                for c in range(0, i):
                    soma += G[i][c]**2
                G[i][j] = (A[i][j] - soma)**(1/2)
            elif(j == 0 and i >= 1):
                G[i][0] = A[i][0]/G[0][0]
            elif(j == 1 and i >= 2):
                G[i][1] = (A[i][1] - G[i][0]*G[1][0])/G[1][1]
                G[i][1] = round(G[i][1], 8)
            elif(1 <= j and j < i):
                soma = 0
                for c in range(i):
                    soma += G[i][c]*G[j][c]
                G[i][j] = (A[i][j] - soma)/G[j][j]

    G = np.asarray(G)
    return G

def sistLinear(G, B, ordem):
    y = symbols('y:'+str(ordem))
    mY = []
    for i in range(len(y)):
        mY.append(y[i])
    D = np.linalg.det(G)
    tempG = G.copy()
    for j in range(ordem):
        for i in range(ordem):
            tempG[i][j] = B[i]
        tempD = np.linalg.det(tempG)
        tempG = G.copy()
        mY[j] = round(tempD/D, 8)
    mTemp = []
    for i in range(len(mY)):
        mTemp.append([mY[i]])
    mY = mTemp.copy()
    mY = np.asarray(mY)
    return mY

def Cholesky(A, B):
    ordem = np.shape(A)
    if ordem[0] == 0: return print("Isso não é uma matriz")
    if ordem[0] == ordem[1]:
        det = Determinantes(A, ordem[0])
        if( det[1] <= 0):
            print("Determinante <= 0 para k = " + str(det[0]))
            return
        trans = Transposta(A, ordem[0])
        if( not trans ):
            return print("Matriz não é igual sua transposta")

        mG = np.zeros((ordem[0],ordem[1]))
        mG = valoresG(A, mG, ordem[0])
        print(prettymatrix.matrix_to_string(mG, name='G = '))

        mY = sistLinear(mG, B, ordem[0])
        print(prettymatrix.matrix_to_string(mY, name='Y = '))

        transpG = np.zeros((ordem[0],ordem[1]))
        for i in range(ordem[0]):
            for j in range(ordem[0]):
                transpG[i][j] = mG[j][i]

        transpG = np.asarray(transpG)

        mX = sistLinear(transpG, mY, ordem[0])
        print(prettymatrix.matrix_to_string(mX, name='X = '))

# A = [[4,2,-4],
#      [2,10,4],
#      [-4,4,9]]
# B = [0,
#      6,
#      5]
# Cholesky(A,B)

## Método LU
def valoresLU(L, U, A, ordem):
    for i in range(ordem):
        for j in range(ordem):
            if i == 0:
                U[0][j] = A[0][j]
            elif i == 1 and j >= 1:
                U[1][j] =  A[1][j] - L[1][0]*U[0][j]
            elif i <= j:
                soma = 0
                for c in range(i):
                    soma += L[i][c]*U[c][j]
                U[i][j] = A[i][j] - soma

        for j in range(ordem):
            if i == 0 and j >= 1:
                L[j][0] = A[j][0]/U[0][0]
            elif i == 1 and j >= 2:
                L[j][1] = (A[j][1] - L[j][0]*U[0][1])/U[1][1]
            elif j > i:
                soma = 0
                for c in range(i):
                    soma += L[j][c]*U[c][i]
                L[j][i] = (A[j][i] - soma)/U[i][i]

    return L, U

def LU(A,B):
    ordem = np.shape(A)
    if ordem[0] == 0: return print("Isso não é uma matriz")
    if ordem[0] != ordem[1]: return print("Esta matriz não é quadrada")
    det = Determinantes(A, ordem[0]-1)
    if( det[1] == 0):
        return print("Determinante = 0 para k = " + str(det[0]))

    L = np.eye(ordem[0])
    U = np.zeros((ordem[0],ordem[1]))

    mLU = valoresLU(L, U, A, ordem[0])

    mY = sistLinear(L, B, ordem[0])
    mX = sistLinear(U, mY, ordem[0])

    print(prettymatrix.matrices_to_string(L, U, names=['L','U']))
    print(prettymatrix.matrix_to_string(mY, name='Y = '))
    print(prettymatrix.matrix_to_string(mX, name='X = '))

# A = [[5,2,1],[-1,4,2],[2,-3,10]]
# B = [-12, 20, 3]
# LU(A,B)

## Método de Eliminação de Gauss
def pivotParcial(A,B,MA,MB,ordem,k):
    if A[k][k] == 0:
        for i in range(k+1,ordem):
            if A[i][k] != 0:
                temp1 = copy.copy(A[i])
                temp2 = copy.copy(B[i])
                temp3 = copy.copy(MA[i])
                temp4 = copy.copy(MB[i])
                A[i] = copy.copy(A[k])
                B[i] = copy.copy(B[k])
                MA[i] = copy.copy(MA[k])
                MB[i] = copy.copy(MB[k])
                A[k] = copy.copy(temp1)
                B[k] = copy.copy(temp2)
                MA[k] = copy.copy(temp3)
                MB[k] = copy.copy(temp4)
                break
    save = 0
    maior = -math.inf
    for i in range(ordem):
        if abs(A[i][k]) > maior and i >= k:
            maior = copy.copy(abs(A[i][k]))
            save = copy.copy(i)
    temp1 = copy.copy(A[save])
    temp2 = copy.copy(B[save])
    temp3 = copy.copy(MA[save])
    temp4 = copy.copy(MB[save])
    A[save] = copy.copy(A[k])
    B[save] = copy.copy(B[k])
    MA[save] = copy.copy(MA[k])
    MB[save] = copy.copy(MB[k])
    A[k] = copy.copy(temp1)
    B[k] = copy.copy(temp2)
    MA[k] = copy.copy(temp3)
    MB[k] = copy.copy(temp4)

def MA(A, B, ordem):
    MA = np.zeros((ordem,ordem))
    MB = np.zeros(ordem)

    for i in range(ordem):
        MB[i] = B[i]
        for j in range(ordem):
            MA[i][j] = A[i][j]

    mAum = np.zeros((ordem,ordem+1))
    for k in range(ordem-1):
        pivotParcial(A,B,MA,MB,ordem,k)
        for i in range(k+1,ordem):
            MB[i] = round(B[i] - (B[k] * A[i][k])/A[k][k], 8)
            for j in range(ordem):
                MA[i][j] = round(A[i][j] - (A[k][j]*A[i][k])/A[k][k], 8)

        for i in range(ordem):
            for j in range(ordem+1):
                if j == ordem:
                    mAum[i][j] = MB[i]
                else:
                    mAum[i][j] = MA[i][j]
        mAum = np.asarray(mAum)
        print(prettymatrix.matrix_to_string(mAum, name='Matriz aumentada = '))

        for i in range(ordem):
            B[i] = MB[i]
            for j in range(ordem):
                A[i][j] = MA[i][j]
    return MA, MB

def Gauss(A, B):
    ordem = np.shape(A)
    if ordem[0] == 0: return print("Isso não é uma matriz")
    if ordem[0] != ordem[1]: return print("Esta matriz não é quadrada")
    mA, mB = MA(A, B, ordem[0])
    mX = sistLinear(mA, mB, ordem[0])
    print(prettymatrix.matrix_to_string(mX, name='X = '))

A = [[2,2,-1],
     [3,3,1],
     [1,-1,5]]
B = [3, 7, 5]
Gauss(A,B)

# Métodos iterativos
## Norma Linha

def norma_linha(A):
    ordem = np.shape(A)
    if ordem[0] == 0 or ordem[1] == 0: return print("Isso não é uma matriz")

    somaMaior = 0
    for i in range(ordem[0]):
        soma = 0
        for j in range(ordem[0]):
            soma += abs(A[i][j])
        if soma > somaMaior:
            somaMaior = soma
        print("Soma dos elementos da linha ", i+1, " = ", round(soma, 8))

    return somaMaior

def norma_coluna(A):
    ordem = np.shape(A)
    if ordem[0] == 0 or ordem[1] == 0: return print("Isso não é uma matriz")

    somaMaior = 0
    for j in range(ordem[0]):
        soma = 0
        for i in range(ordem[0]):
            soma += abs(A[i][j])
        if soma > somaMaior:
            somaMaior = soma
        print("Soma dos elementos da coluna ", j+1, " = ", soma)

    return somaMaior

def norma_euclidiana(A):
    ordem = np.shape(A)
    if ordem[0] == 0 or ordem[1] == 0: return print("Isso não é uma matriz")
    soma = 0
    for i in range(ordem[0]):
        for j in range(ordem[0]):
            soma += A[i][j]**2

    soma = soma ** (1/2)
    return soma

def norma_inf(A):
    somaMaior = 0
    for i in range(len(A)):
        if abs(A[i]) > somaMaior:
            somaMaior = abs(A[i])

    return somaMaior

# A = [[3,-5,7],
#      [1,-2,4],
#      [-8,1,-7]]

# print("||A||_inf = ", norma_linha(A))
# print("||A||_1 = ", norma_coluna(A))
# print("||A||_E = ", norma_euclidiana(A))

## Método Gauss Jacobi
def condicaoEDD(A, ordem):
    for i in range(ordem):
        for j in range(ordem):
            if A[i][j] > A[i][i]:
              return false

def condicaoF(A, ordem, tipo):
    F = np.zeros((ordem,ordem))
    for i in range(ordem):
        for j in range(ordem):
            if i != j:
                F[i][j] = -(A[i][j]/A[i][i])
    print(prettymatrix.matrix_to_string(F, name='Matriz F = '))
    print("Cálculo da soma em valor absoluto de cada linha da matriz F:")
    if norma_linha(F) < 1:
        return true
    elif tipo == 1:
        print("Cálculo da soma em valor absoluto de cada coluna da matriz F:")
        if norma_coluna(F) < 1:
            return true
    else:
        return false

def GaussJacobi(A, B, X0, e):
    flag = 0
    ordem = np.shape(A)
    if ordem[0] == 0 or ordem[1] == 0: return print("Isso não é uma matriz")
    if condicaoEDD(A,ordem[0]) == false:
        flag += 1
        print("A matriz não é E.D.D.")
    if condicaoF(A, ordem[0], 1) == false:
        flag += 1
    if flag != 2:
        xk = []
        x_x = []
        xk.append(X0)
        x_x.append('-')
        controle = 0
        end_condition = 0
        while not end_condition:
            array = []
            for i in range(len(X0)):
                array2 = []
                temp = 0
                for j in range(len(X0)):
                    if i != j:
                        temp += A[i][j]*X0[j]
                array.append(round((1/A[i][i]) * (B[i] - temp),8))

            for i in range(len(array)):
                array2.append(array[i]-xk[controle][i])

            xk.append(array)
            x_x.append(norma_inf(array2)/norma_inf(array))
            X0 = array
            controle += 1
            if norma_inf(array2)/norma_inf(array) < e:
                end_condition = 1

        Table = PrettyTable(["k", "xk", "||x^(k+1) - x^(k)||/||x^(k+1)||"])
        for k in range(0, len(xk)):
            Table.add_row([k, xk[k], x_x[k]])

        print(Table)
    else:
        print("A matriz não contém os requisitos quando feita a matriz F")

# A = [[10,2,1],[1,5,1],[2,3,10]]
# B = [14, 11, 8]
# X0 = [0,0,0]
# GaussJacobi(A,B,X0,0.01)

## Método Gauss Seidel
def Sanssenfeld(A, ordem):
    B = []
    maior = -math.inf
    for i in range(ordem):
        soma = 0
        for j in range(i):
            if i == 0: continue
            soma += abs(A[i][j]/A[i][i]) * B[j]
        temp = copy.copy(soma)
        soma = 0
        for j in range(i+1,ordem):
            soma += abs(A[i][j]/A[i][i])
        print("\u03B2", i, " = ", round(temp+soma,8))
        B.append(temp+soma)
        if B[len(B)-1] > maior:
            maior = B[len(B)-1]
    if maior < 1:
        return true
    else:
        return false

def GaussSeidel(A, B, X0, e):
    flag = 0
    ordem = np.shape(A)
    if ordem[0] == 0 or ordem[1] == 0: return print("Isso não é uma matriz")
    if condicaoEDD(A,ordem[0]) == false:
        flag += 1
        print("Matriz não é E.D.D.")
    if condicaoF(A, ordem[0], 2) == false:
        flag += 1
        print("Não atende aos requisitos quando transformada na matriz F")
    print("Critério de Sanssenfeld:")
    if Sanssenfeld(A, ordem[0]) == false:
        flag += 1
        print("A matriz não atende ao critério de Sanssenfeld")
    if flag != 3:
        xk = []
        x_x = []
        xk.append(X0)
        x_x.append('-')
        controle = 0
        end_condition = 0
        while not end_condition:
            array = []
            array2 = []
            for i in range(len(X0)):
                temp = 0
                for j in range(len(X0)):
                    if i != j:
                        if j < i:
                            temp += A[i][j]*array[j]
                        else:
                            temp += A[i][j]*X0[j]
                array.append(round((1/A[i][i]) * (B[i] - temp),8))

            for i in range(len(array)):
                array2.append(array[i]-xk[controle][i])

            xk.append(array)
            x_x.append(norma_inf(array2)/norma_inf(array))
            X0 = array
            controle += 1
            if norma_inf(array2)/norma_inf(array) < e:
                end_condition = 1

        Table = PrettyTable(["k", "xk", "||x^(k+1) - x^(k)||/||x^(k+1)||"])
        for k in range(0, len(xk)):
            Table.add_row([k, xk[k], x_x[k]])

        print(Table)

    else:
        return print("A matriz não atende aos requisitos do método")

# A = [[5,1,1],[3,4,1],[3,3,6]]
# B = [5, 6, 0]
# X0 = [0,0,0]
# GaussSeidel(A, B, X0, 0.01)


import logging
logger = logging.getLogger(__name__)

import itertools
import random

from collections import OrderedDict

import functools

from fractions import Fraction

from math import comb

import numpy as np
import pandas as pd

class ProbabilityPredictor:

  @staticmethod
  @functools.lru_cache(maxsize=256)
  def probExactlyXDice(numInstances, numDice, numDieFaces):
    """
    # Probability exactly <numInstances> occurrences of a dice value are
    # shown when <numDice> are thrown, each die having <numDieFaces> sides
    """

    # probability die value will be shown
    probOccur = Fraction(1, numDieFaces)

    # probability exactly <numInstances> of this die value appearing is:
    #  -total number of ways this can occur given the number of dice we have
    # *
    #  -probability this value appears <numInstances> times
    # *
    #  -probability this value DOESN'T appear the remaining times
    #
    return comb(numDice, numInstances) * (probOccur ** numInstances) * ((1-probOccur) ** (numDice-numInstances))

  @staticmethod
  @functools.lru_cache(maxsize=256)
  def probAtLeastXDice(numInstances, numDice, numDieFaces):
    """
    # Probability at least <numInstances> occurrences of a dice value are
    # shown when <numDice> are thrown, each die having <numDieFaces> sides
    """

    # add up the probabilities that:
    #  -exactly <numInstances> values are shown
    # +
    #  -exactly <numInstances>-1 values are shown
    # +
    #  -exactly <numInstances>-2 values are shown
    # +
    # ....
    return sum([ProbabilityPredictor.probExactlyXDice(i, numDice, numDieFaces)
                  for i in range(numInstances,numDice+1)])

  @staticmethod
  def DEPrangeOfAKind(numDice, kind, numDieFaces=6):
    """
    # Probability of getting 1-<numOccurrences> of <kind> dice values
    #  -e.g., when rolling 5 dice, probability of getting 1, 2, 3, 4 or 5 "4"s
    #
    #
    """

    # current number of <kind> we have
    currentNum = diceCount.get(kind, 0)

    # calculate the chance of getting X of <kind>
    #  -e.g., for kind=3 with 5 dice, probability of getting 1x3, 2x3, 3x3, 4x3, 5x3
    for numOccurrences in range(1, numDice + 1):

      # if we already have this number of <kind> then probability is 1.0
      if currentNum >= numOccurrences:
        prob[numOccurrences] = Fraction(1)

      # else, calculate the probability of rolling the number of <kind> needed
      # with the dice we have available
      else:

        # if we can hold the current <kind> dice we have, then we only need to
        # roll for the remaining number of <kind> we need
        numKindNeeded    = numOccurrences - currentNum
        numAvailableDice = numDice - currentNum

        # probability of getting the number of <kind> we need, given the dice we have
        prob[numOccurrences] = ProbabilityPredictor.probAtLeastXDice(numKindNeeded,
                                                                     numAvailableDice,
                                                                     numDieFaces)

    return prob

  @staticmethod
  def ofAKind(kind, diceValues, turnsRemaining, numDieFaces=6, canHold=True):
    """  """

    numDice = len(diceValues)

    diceCount = pd.Series(diceValues).value_counts()

    # current number of <kind> we have
    currentNum = diceCount.get(kind, 0)

    """
    prob 5-of-a-kind of 1s:
    -prob(getting 5 | got exactly 4 before) + prob(getting 5 | got exactly 3 before) +

    p(5|0) + p(4|1) + p(3|2) + p(2|3) + p(1|4) + p(5)

    """

    probSum = 0
    for numOccurrences in range(numDice):

      pA = ProbabilityPredictor.probAtLeastXDice(numDice-numOccurrences, numDice-numOccurrences, numDieFaces=6)
      pB = ProbabilityPredictor.probExactlyXDice(numOccurrences, numDice, numDieFaces=6)
      print("{}: {} * {}".format(numOccurrences, pA, pB))
      probSum += pA * pB

    return probSum

    prob = {}

    # prob getting X of a kind next turn
    #prob[1] = 1.0 if currentNum >= 1 else ProbabilityPredictor.probAtLeastXDice(1, numDice, numDieFaces)

    # calculate the chance of getting X of <kind>
    #  -e.g., for kind=3 with 5 dice, probability of getting 1x3, 2x3, 3x3, 4x3, 5x3
    for numOccurrences in range(1, numDice+1):

      # if we already have this number of <kind> then probability is 1.0
      if currentNum >= numOccurrences:
        prob[numOccurrences] = Fraction(1)

      # else, calculate the probability of rolling the number of <kind> needed
      # with the dice we have available
      else:

        # if we can hold the current <kind> dice we have, then we only need to
        # roll for the remaining number of <kind> we need
        numKindNeeded    = numOccurrences - currentNum if canHold else numOccurrences
        numAvailableDice = numDice - currentNum        if canHold else numDice

        # probability of getting the number of <kind> we need, given the dice we have
        prob[numOccurrences] = ProbabilityPredictor.probAtLeastXDice(numKindNeeded,
                                                                     numAvailableDice,
                                                                     numDieFaces)

    return prob

    # probability for each turn
    probs = {}

    for iTurn in range(1, turnsRemaining+1):

      # prob getting X of a kind this turn

      probs[iTurn] = {}

      for numOccurences in range(1, numDice + 1):
        probs[iTurn][numOccurences] = ProbabilityPredictor.probAtLeastXDice(numOccurences, numDice, numDieFaces)

  @staticmethod
  def OLDofAKind(kind, diceValues, numDieFaces=6, numTurns=3, canHold=True):
    """  """

    numDice = len(diceValues)

    # probability for each turn
    probs = {}

    for iTurn in range(numTurns):

      probs[iTurn] = {}

      for numOccurences in range(1, numDice+1):
        probs[iTurn][numOccurences] = ProbabilityPredictor.probAtLeastXDice(numOccurences, numDice, numDieFaces)

    #probKind = Fraction(1,6)

    #diceCounts = pd.Series(diceValues).value_counts()

    #currNum = diceCounts.get(kind, 0)
    #diceRemaining = numDice - currNum

    return probs


import numpy as np
import math
import copy
import scipy.stats

def log_poisson_likelihood(y, y_obs):
    """
    Calculate the naural logarithm of the Poisson likelihood for
    modeled number of detections y and the observed number of
    detections y_obs. The Poisson PMF is

    pmf(y_obs | y) = (y^(y_obs) * e^(-y))/ y_obs!

    Parameters
    ----------
    y : numpy array
        The population counts, also called lambda.
    y_obs : numpy array
        The observed counts, also called k.

    Returns
    -------
    log_likelihood : float
        The sum of of the Poisson log-likelihoods.

    Notes
    ------
    This code can be condesned down to
    log_likelihood = np.sum(scipy.stats.poisson.logpmf(k=y_obs, mu=y))
    but I chose to be explicit for the learning opportunity.
    """
    assert len(y) == len(y_obs), ('The modeled and observed count'
                                ' lengths must be identical.')

    # Drop 0 y values because they diverge. The probability of
    # any y_obs other than 0, given y is 0 is 0.
    y_ind = np.where(np.array(y) != 0)[0]
    y = y[y_ind]
    y_obs_copy = copy.copy(np.array(y_obs))
    y_obs_copy = y_obs_copy[y_ind]

    # Take the factorial and apply Stirling's approximation
    # for y_obs > 100.
    log_factorial = [math.log(math.factorial(y_i)) if y_i < 100
                    else y_i*np.log(y_i) - y_i for y_i in y_obs_copy]
    # Analytic derivation of the Poisson log-likelihood for a product
    # of Poisson random variables
    log_likelihood = np.sum(y_obs_copy*np.log(y) - y - log_factorial)
    return log_likelihood

def log_normal_likelihood(y_obs, p):
    """
    Calculate the naural logarithm of the Gaussian (normal) likelihood
    for the observation y_obs and Gaussian parameter array p. p[0] is
    the offset, and p[1] is the standard deviation. The norm PDF is

    pdf(x|p) = exp(-((x-p[0])/sqrt(2)*p[1])**2)/(sqrt(2*pi)*p[1])

    Parameters
    ----------
    x : numpy array
        The observation.
    p : numpy array
        An array of length 2 with the norm offset and standard deviation.

    Returns
    -------
    log_likelihood : float
        The sum of of the norm log-likelihoods.
    """
    log_pdf = scipy.stats.norm.logpdf(y_obs, loc=p[0], scale=p[1])
    log_likelihood = np.nansum(log_pdf)
    return log_likelihood


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

x = np.array([i for i in range(-7, 7)])
y = np.array([sin(i) for i in x])

# Sine Function
# f = sin(x)
plt.plot(x, y, label='sin(x)')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.title('The Graph of Trigonometric Functions')
plt.show()

# Cosine Function
y = np.array([cos(i) for i in x])
plt.plot(x, y, label='cos(x)')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()

yx = lambda x: pow(sin(x),2) + pow(cos(x),2)
y = np.array([yx(i) for i in x])
plt.plot(x, y, label='sin(x)**2 + cos(x)**2')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()

# Tangent Function
y = np.array([tan(i) for i in x])
plt.plot(x, y, label='tan(x)')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()


"""Change coordinates"""
from numpy import arccos, arctan2, hypot, sqrt

def to_polar(coo_x, coo_y):
    """
    r, θ = to_polar(x, y)

    Change Cartesian coordinates to Polar coordinates.

    Parameters
    ----------
    x : array_like
        first Cartesian coordinate
    y : array_like
        seconde Cartesian coordinate

    Returns
    -------
    r : ndarray
        radial coordinate
    θ : ndarray
        angular coordinate
    """

    return (hypot(coo_x, coo_y), arctan2(coo_y, coo_x))

def to_spheric(coo_x, coo_y, coo_z):
    """
    r, θ, φ = to_spheric(x, y, z)

    Change Cartesian coordinates to Spherical coordinates.

    Parameters
    ----------
    x : array_like
        first Cartesian coordinate
    y : array_like
        seconde Cartesian coordinate
    z : array_like
        third Cartesian coordinate

    Returns
    -------
    r : ndarray
        radial distance
    θ : ndarray
        azimuthal angle
    φ : ndarray
        polar angle
    """

    coo_r = sqrt(coo_x * coo_x + coo_y * coo_y + coo_z * coo_z)
    return (coo_r, arctan2(coo_y, coo_x), arccos(coo_z / coo_r))


import numpy as np

def ExpRegression(xval, yval):
    """Takes an array of x-values, and y-values and perform a origin exponential regression, with least square method. Returns the function with corresponding constants, and the constants.
        One notes that f(x) = a* e^(-x/b) and thus returns the constants on the form (a,b)."""
    if not isinstance((xval, yval), (np.generic, np.ndarray)):
        if isinstance((xval, yval), (list, tuple)):
            xval, yval = np.array(xval), np.array(yval)
        else:
            raise TypeError("[ExpRegression]: Need a iterable as input.")
    if len(xval) != len(yval):
        raise KeyError("[ExpRegression]: Input xval and yval are not of the same size")
    try:
        A_Matrix = np.array([xval, -np.ones(len(xval))]).T
        A_Matrix_Transposed = A_Matrix.T
        A_AT_Inverse = np.linalg.inv(A_Matrix_Transposed.dot(A_Matrix))
        #Computation to find constants.
        Corrected_yval = np.log(yval)
        ylist = A_Matrix_Transposed.dot(Corrected_yval)
        Constants = A_AT_Inverse.dot(ylist)
        a = np.exp(Constants[1])
        b = Constants[0]
        Expfunc = lambda x: b[0] * np.exp(x/a[0])
        if np.isnan(Constants[0]) or np.isnan(Constants[1]):
            raise TypeError("[ExpRegression]: Performing edited version")
        return Expfunc, (b[0], a[0])
    except Exception as e:
        try:
            print("[ExpRegression]: Performing edited version")
            A_Matrix = np.array([xval, -np.ones(len(xval))]).T
            A_Matrix_Transposed = A_Matrix.T
            A_AT_Inverse = np.linalg.inv(A_Matrix_Transposed.dot(A_Matrix))
            #Computation to find constants.
            ylist1 = np.array([[arg + 1] for arg in yval])
            Corrected_yval = np.log(ylist1)
            ylist = A_Matrix_Transposed.dot(Corrected_yval)
            Constants = A_AT_Inverse.dot(ylist)
            a = np.exp(Constants[1])
            b = Constants[0]
            Expfunc = lambda x: b[0] * np.exp(x/a[0])
            return Expfunc, (b[0],a[0])
        except Exception as e:
            raise e
"""
xlist = (1,2,3,4,5,6,7,8,9)
ylist = (0,0,0,0,1,2,4,8,16)
print(ExpRegression(xlist, ylist))
"""


import numpy as np

from tensor import Tensor
from graph import Node

class Operation(Node):
	base_name:str=None
	output:Tensor = None
	inputs:list = []
	def __init__(self):
		super().__init__()
		self.inputs=[]
		self.output=None

	def forward(self):
		raise NotImplementedError
	def backward(self, gradOutput: Tensor):
		raise NotImplementedError

class Negative(Operation):
	base_name="Negative"
	def __init__(self, A: Node, Name:str=None):
		super().__init__()
		if Name is not None:
			self.name = Name

		self.inputs.append(A)

	def forward(self):
		A = self.inputs[0]
		self.output = -A.output

class Add(Operation):
	base_name = "Add"
	def __init__(self, A: Node,B:Node, Name:str=None):
		super().__init__()
		if Name is not None:
			self.name = Name
		self.inputs.append(A)
		self.inputs.append(B)

	def forward(self):
		A = self.inputs[0]
		B = self.inputs[1]
		self.output = A.output + B.output

class Substract(Operation):
	base_name = "Substract"
	def __init__(self, A: Node,B:Node, Name:str=None):
		super().__init__()
		if Name is not None:
			self.name = Name

		self.inputs.append(A)
		self.inputs.append(B)

	def forward(self):
		A = self.inputs[0]
		B = self.inputs[1]
		self.output = A.output - B.output

class Divide(Operation):
	base_name = "Divide"
	def __init__(self, A: Node,B:Node, Name:str=None):
		super().__init__()
		if Name is not None:
			self.name = Name

		self.inputs.append(A)
		self.inputs.append(B)

	def forward(self):
		A = self.inputs[0]
		B = self.inputs[1]
		self.output = A.output/B.output

class Multiply(Operation):
	base_name = "Multiply"
	def __init__(self, A: Node,B:Node, Name:str=None):
		super().__init__()
		if Name is not None:
			self.name = Name
		self.inputs.append(A)
		self.inputs.append(B)
	def forward(self):
		A = self.inputs[0]
		B = self.inputs[1]
		self.output = A.output * B.output

class Sum(Operation):
	base_name = "Sum"
	def __init__(self, A: Node,axis:int = -1, Name:str=None):
		super().__init__()
		if Name is not None:
			self.name = Name

		self.inputs.append(A)
		self.axis = axis

	def forward(self):
		A = self.inputs[0]
		self.output = np.sum(A.output,self.axis)

class MatMul(Operation):
	base_name = "MatMul"
	def __init__(self, A: Node, B:Node, Name:str=None):
		super().__init__()
		if Name is not None:
			self.name = Name

		self.inputs.append(A)
		self.inputs.append(B)

	def forward(self):
		A = self.inputs[0]
		B = self.inputs[1]
		self.output = np.matmul( A.output , B.output )

class MatMulNaive(Operation):
	base_name = "MatMulNaive"
	def __init__(self, A: Node,B:Node, Name:str=None):
		super().__init__()
		if Name is not None:
			self.name = Name

		self.inputs.append(A)
		self.inputs.append(B)

	def forward(self):
		A = self.inputs[0]
		B = self.inputs[1]
		cshape=(A.shape[0],B.shape[1])
		C=Tensor([x for x in range(np.prod(cshape))]).reshape(cshape)
		for i in range(0, A.shape[0]):
			for j in range(0, B.shape[1]):
				C[i,j]=0
				for k in range(0, A.shape[1]):
					C[i,j]+= A[i,k]*B[k,j]
		self.output = C

class sin(Operation):
	base_name = "Sin"

	def __init__(self, A: Node, Name: str = None):
		super().__init__()
		if Name is not None:
			self.name = Name

		self.inputs.append(A)

	def forward(self):
		A = self.inputs[0]
		self.output = np.sin(A.output)


import numpy
import torch

def hermgauss(n, dtype=torch.float32):
    x, w = numpy.polynomial.hermite.hermgauss(n)
    x, w = torch.as_tensor(x, dtype=dtype), torch.as_tensor(w, dtype=dtype)
    return x, w

def mvhermgauss(H, D, dtype=torch.float32):
    """
    Return the evaluation locations 'xn', and weights 'wn' for a multivariate
    Gauss-Hermite quadrature.

    The outputs can be used to approximate the following type of integral:
    int exp(-x)*f(x) dx ~ sum_i w[i,:]*f(x[i,:])

    :param H: Number of Gauss-Hermite evaluation points.
    :param D: Number of input dimensions. Needs to be known at call-time.
    :return: eval_locations 'x' ((H**D)xD), weights 'w' (H**D)
    """
    gh_x, gh_w = hermgauss(H)
    x = numpy.array(numpy.meshgrid(*(D*(gh_x,))))
    w = numpy.array(numpy.meshgrid(*(D*(gh_w,)))).prod(1)
    x, w = torch.as_tensor(x, dtype=dtype), torch.as_tensor(w, dtype=dtype)
    return x, w

def mvnquad(f, means, covs, H, Din, Dout=()):
    """
    Computes N Gaussian expectation integrals of a single function 'f'
    using Gauss-Hermite quadrature.
    :param f: integrand function. Takes one input of shape ?xD.
    :param means: NxD
    :param covs: NxDxD
    :param H: Number of Gauss-Hermite evaluation points.
    :param Din: Number of input dimensions. Needs to be known at call-time.
    :param Dout: Number of output dimensions. Defaults to (). Dout is assumed
    to leave out the item index, i.e. f actually maps (?xD)->(?x*Dout).
    :return: quadratures (N,*Dout)
    """
    xn, wn = mvhermgauss(H, Din)
    N = means.size(0)

    # transform points based on Gaussian parameters
    Xt = []
    for c in covs:
        chol_cov = torch.potrf(c, upper=False) # DxD each
        Xt.append(torch.matmul(chol_cov, xn.t()))
    Xt = torch.stack(Xt, dim=0) # NxDx(H**D)
    X = 2.0 ** 0.5 * Xt + means.unsqueeze(2)  # NxDx(H**D)
    Xr = X.permute(2, 0, 1).view(-1, Din)  # (H**D*N)xD

    # perform quadrature
    fX = f(Xr).view(*((H ** Din, N,) + Dout))
    wr = (wn * float(numpi.pi) ** (-Din * 0.5)).view(*((-1,) + (1,) * (1 + len(Dout))))
    return (fX * wr).sum(0)


import numpy as np
import os
import fnmatch
import scipy.linalg as sp

# PCA
def makeTwoDim(monsters):
    i,j,k=monsters.shape
    tmp=np.zeros((i*j,k))
    for x in range(k):
        vec=np.reshape(monsters[:,:,x],(i*j),order='F')
        tmp[:,x]=vec
    return tmp

def calcMean(vec):
    i,j=vec.shape
    meanVec=np.zeros(i)
    for x in range(j):
        meanVec+=vec[:,x]/float(j)
    return meanVec

def calcCov(vec):
    i,j=vec.shape
    meanVec=calcMean(vec)
    covMatrix=np.zeros((i,i))
    for x in range(j):
        t1=((vec[:,x]-meanVec).T).reshape(i,1)
        t2=(vec[:,x]-meanVec).reshape(1,i)
        covMatrix+=np.dot(t1,t2)/float(j)
    return covMatrix

def sortEig(eVal, eVec):
    i,j=eVec.shape
    ePair=list(range(j))
    for y in range(j):
        ePair[y]=[(np.abs(eVal[y]), eVec[:,y])]
        ePair[y].sort()
        ePair[y].reverse()
    return ePair

def pairEig(eVal, eVec):
    i,j=eVec.shape
    ePair=list(range(j))
    for y in range(j):
        ePair[y]=[(np.abs(eVal[y]), eVec[:,y])]
    return ePair

def makeTransformMatrix(ePair,pcA,pcB):
    tmp=ePair[0][0][1]
    i=tmp.shape
    i=i[0]
    vec1=ePair[pcA][0][1]
    vec2=ePair[pcB][0][1]

    transform=np.zeros((i,2))
    transform[:,0]=vec1.reshape(i).real
    transform[:,1]=vec2.reshape(i).real
    return transform

def plotTanProj(monsters,pcA,pcB):
    i, j, k = monsters.shape
    twoDim=makeTwoDim(monsters)
    mShape=calcMean(twoDim)
    covMatrix=calcCov(twoDim)
    if k > i * j:  # limit results returned if sample number is less than observations
        self.val, self.vec = sp.eigh(covMatrix)
    else:
        self.val, self.vec = sp.eigh(covMatrix, eigvals=(i * j - k, i * j - 1))
    eigVal=eigVal[::-1]
    eigVec=eigVec[:, ::-1]
    eigPair=pairEig(eigVal,eigVec)
    transform=makeTransformMatrix(eigPair,pcA,pcB)
    transform=np.transpose(transform)
    coords=np.dot(transform,twoDim)
    tmp=np.column_stack((coords[0,:],coords[1,:]))
    return tmp

def plotTanProj(monsters,eigSort,pcA,pcB):
    twoDim=makeTwoDim(monsters)
    transform=makeTransformMatrix(eigSort,pcA,pcB)
    transform=np.transpose(transform)
    coords=np.dot(transform,twoDim)
    tmp=np.column_stack((coords[0,:],coords[1,:]))
    return tmp

############## GPA original
#center shape by removing the mean of each column
def centerShape(shape):
    shape=shape-shape.mean(axis=0)
    return shape

#scale shape by divinding by frobinius norm
def scaleShape(shape):
    shape=shape/np.linalg.norm(shape)
    return shape

#align one shape to a reference shape, soley by rotation
def alignShape(refShape,shape):
    """
    Align the shape to the mean.
    """
    u,s,v=sp.svd(np.dot(np.transpose(refShape),shape), full_matrices=True)
    rotationMatrix=np.dot(np.transpose(v), np.transpose(u))
    shape=np.dot(shape,rotationMatrix)
    return shape

def meanShape(monsters):
    return monsters.mean(axis=2)

def procDist(monsters,mshape):
    i,j,k=monsters.shape
    procDists=np.zeros(k)
    for x in range(k):
        tmp=monsters[:,:,x]-mshape
        procDists[x]=np.linalg.norm(tmp,'fro')
    return procDists

################# GPA update
def runGPA(allLandmarkSets):
  i,j,k=allLandmarkSets.shape
  for index in range(k):
    landmarkSet=allLandmarkSets[:,:,index]
    tempSet = applyCenterScale(landmarkSet)
    allLandmarkSets[:,:,index]=tempSet
  allLandmarkSets = procrustesAlign(allLandmarkSets[:,:,0],allLandmarkSets)
  initialMeanShape=meanShape(allLandmarkSets)
  initialMeanShape = scaleShape(initialMeanShape)
  diff=1
  tries=0
  while diff>0.0001 and tries<5:
    allLandmarkSets = procrustesAlign(initialMeanShape,allLandmarkSets)
    currentMeanShape=meanShape(allLandmarkSets)
    currentMeanShape= scaleShape(currentMeanShape)
    diff=np.linalg.norm(initialMeanShape-currentMeanShape)
    initialMeanShape=currentMeanShape
    tries=tries+1
  return allLandmarkSets, currentMeanShape

def procrustesAlign(mean, allLandmarkSets):
  mean = scaleShape(mean)
  i,j,k=allLandmarkSets.shape
  for index in range(k):
    allLandmarkSets[:,:,index] = alignShape(mean, allLandmarkSets[:,:,index])
  return allLandmarkSets

def applyCenterScale(landmarkSet):
  landmarkSet=centerShape(landmarkSet)
  landmarkSet=scaleShape(landmarkSet)
  return landmarkSet

def runGPANoScale(allLandmarkSets):
  i,j,k=allLandmarkSets.shape
  for index in range(k):
    landmarkSet=allLandmarkSets[:,:,index]
    tempSet = applyCenter(landmarkSet)
    allLandmarkSets[:,:,index]=tempSet
  allLandmarkSets = procrustesAlignNoScale(allLandmarkSets[:,:,0],allLandmarkSets)
  initialMeanShape=meanShape(allLandmarkSets)
  diff=1
  tries=0
  while diff>0.0001 and tries<5:
    allLandmarkSets = procrustesAlignNoScale(initialMeanShape,allLandmarkSets)
    currentMeanShape=meanShape(allLandmarkSets)
    diff=np.linalg.norm(initialMeanShape-currentMeanShape)
    initialMeanShape=currentMeanShape
    tries=tries+1
  return allLandmarkSets, currentMeanShape

def procrustesAlignNoScale(mean, allLandmarkSets):
  i,j,k=allLandmarkSets.shape
  for index in range(k):
    allLandmarkSets[:,:,index] = alignShape(mean, allLandmarkSets[:,:,index])
  return allLandmarkSets

def applyCenter(landmarkSet):
  landmarkSet=centerShape(landmarkSet)
  return landmarkSet


# -*- coding: utf-8 -*-
"""
Module containing different OWA operators.

@author: Javier Fumanal Idocin (UPNA).

To suggest changes or submit new code please use the github page.
"""

import numpy as np
# =============================================================================
#   ~OWAs
# =============================================================================

def owa(X, weights):
    '''

    :param X:
    :param weights:
    :return:
    '''
    X_sorted = np.sort(X) #Order decreciente

    return np.sum(X_sorted * weights[::-1])

def OWA_generic(X, a, b, axis=0, keepdims=True):
    '''
    OWA operator that generates the weight vector using a quantifier
    function determined by a and b. (Check std_quantifier() understand this process)

    :param X: data to aggregate.
    :param a: quantifier parameter 1
    :param b: quantifier parameter 2
    :param axis: axis to reduce.
    :param keepdims: if true, the shape will have the same length.
    :return: matrix with the aggregated axis.
    '''
    X_sorted = -np.sort(-X, axis = axis)
    w = generate_owa_weights(X.shape[axis], lambda x: std_quantifier(x, a=a, b=b))
    X_agg  = np.apply_along_axis(lambda a: np.dot(a, w), axis, X_sorted)

    if keepdims:
        X_agg = np.expand_dims(X_agg, axis=axis)

    return X_agg

def OWA1(X, axis=0, keepdims=True):
    '''
    Performs the OWA operation using a=0.1 and b=0.5 in the quantifier
    function that generates the weights.

    :param X: data to aggregate.
    :param axis: axis to reduce.
    :param keepdims: if true, the shape will have the same length.
    :return: matrix with the aggregated axis.
    '''
    return OWA_generic(X, a=0.1, b=0.5, axis=axis, keepdims=keepdims)

def OWA2(X, axis=0, keepdims=True):
    '''
    Performs the OWA operation using a=0.5 and b=1 in the quantifier
    function that generates the weights.

    :param X: data to aggregate.
    :param axis: axis to reduce.
    :param keepdims: if true, the shape will have the same length.
    :return: matrix with the aggregated axis.
    '''
    return OWA_generic(X, a=0.5, b=1, axis=axis, keepdims=keepdims)

def OWA3(X, axis=0, keepdims=True):
    '''
    Performs the OWA operation using a=0.3 and b=0.8 in the quantifier
    function that generates the weights.

    :param X: data to aggregate.
    :param axis: axis to reduce.
    :param keepdims: if true, the shape will have the same length.
    :return: matrix with the aggregated axis.
    '''
    return OWA_generic(X, a=0.3, b=0.8, axis=axis, keepdims=keepdims)

def generate_owa_weights(n, quantifier):
    '''
    Quantifier function that generates a vector of weights using a quantifier
    function.

    :param quantifier: quantifier function.
    :return: a vector of weights.
    '''
    weights = np.zeros((n))
    for i in range(n):
        ri = i + 1
        weights[i] = quantifier(ri / n) - quantifier((ri - 1) / n)

    return weights

def std_quantifier(x, a=0.0, b=1.0):
    '''
    Standar quantifier, given two parameters: a,b.
    It follows the expression:
        Q = (x-a)/(b-a)
        return 0, if Q < a
        return 1, if Q < b
        return Q, otherwise

    :param a: a real number between 0 and 1.
    :param b: a real number between 0 and 1. Bigger than a.
    :return: the quantifier result.
    '''
    Q = (x-a) / (b - a)
    if x < a:
        return 0
    elif x > b:
        return 1
    else:
        return Q


import numpy as np
from numba import njit, types
from numba.extending import overload, register_jitable
from numba.core.errors import TypingError

import scipy.linalg

@register_jitable
def _oneD_norm_2(a):
    # re-usable implementation of the 2-norm
    val = np.abs(a)
    return np.sqrt(np.sum(val * val))

@overload(scipy.linalg.norm)
def jit_norm(a, ord=None):
    if isinstance(ord, types.Optional):
        ord = ord.type
    # Reject non integer, floating-point or None types for ord
    if not isinstance(ord, (types.Integer, types.Float, types.NoneType)):
        raise TypingError("'ord' must be either integer or floating-point")
    # Reject non-ndarray types
    if not isinstance(a, types.Array):
        raise TypingError("Only accepts NumPy ndarray")
    # Reject ndarrays with non integer or floating-point dtype
    if not isinstance(a.dtype, (types.Integer, types.Float)):
        raise TypingError("Only integer and floating point types accepted")
    # Reject ndarrays with unsupported dimensionality
    if not (0 <= a.ndim <= 2):
        raise TypingError('3D and beyond are not allowed')
    # Implementation for scalars/0d-arrays
    elif a.ndim == 0:
        return a.item()
    # Implementation for vectors
    elif a.ndim == 1:
        def _oneD_norm_x(a, ord=None):
            if ord == 2 or ord is None:
                return _oneD_norm_2(a)
            elif ord == np.inf:
                return np.max(np.abs(a))
            elif ord == -np.inf:
                return np.min(np.abs(a))
            elif ord == 0:
                return np.sum(a != 0)
            elif ord == 1:
                return np.sum(np.abs(a))
            else:
                return np.sum(np.abs(a)**ord)**(1. / ord)
        return _oneD_norm_x
    # Implementation for matrices
    elif a.ndim == 2:
        def _two_D_norm_2(a, ord=None):
            return _oneD_norm_2(a.ravel())
        return _two_D_norm_2

if __name__ == "__main__":
    @njit
    def use(a, ord=None):
        # simple test function to check that the overload works
        return scipy.linalg.norm(a, ord)

    # spot check for vectors
    a = np.arange(10)
    print(use(a))
    print(scipy.linalg.norm(a))

    # spot check for matrices
    b = np.arange(9).reshape((3, 3))
    print(use(b))
    print(scipy.linalg.norm(b))


import math
import numpy as np
import cv2
from math import floor
from matplotlib import pyplot as plt
import time
from multiprocessing import Pool
import sys

class HybridImage:
    '''
    This class is used for creating hybrid images as well as
    displaying a high-pass or low-pass version of an image
    '''
    def __init__(self, image1, output_filename, image2=None, sigma=4.0):
        '''the initialization of the class

        :param str image1: the location of the first image;
        :param str output_filename: set the filename of the output;
        :param str image2: the location of the second image;
        :param float sigma: set the sigma, its default value is 4.0.

        '''
        self.image1 = cv2.imread(image1)
        if image2:
            # if I just want to output the high-pass or low-pass version of an image,
            # the parameter of image2 won't be used,
            # so I explicitly set its default value to None.
            self.image2 = cv2.imread(image2)
        self.filename = output_filename
        self.sigma = float(sigma)

    def gaussian_template(self):
        # compute the window size according to the method specified in the coursework detail
        winsize = int(8.0 * self.sigma + 1.0)
        if winsize % 2 == 0:
            winsize += 1   # the window size must be odd
        # initialize a 2-D array for the template and set the values to zeros
        template = np.zeros((winsize,winsize))
        # compute the centre of the template
        centre = math.floor(winsize/2) + 1
        # create a gaussian template
        sum = 0
        for i in range(1, winsize + 1):
            for j in range(1, winsize + 1):
                template[j - 1][i - 1] = math.exp(-((i-centre) * (i-centre) + (j-centre) * (j-centre))/(2 * self.sigma * self.sigma))
                sum += template[j - 1][i - 1]
        return template/sum  # normalise by the total sum

    def template_convolution(self, image, template):
        '''
        the function for template convolution

        :param array image: a 2-D matrix of an image
        :param array template: a 2-D matrix of a template to be convolved with the image

        '''
        # get the shapes of the image and the template
        rows, cols = image.shape
        trows, tcols = template.shape
        # filp the template
        flipped_template = np.zeros((trows, tcols))
        for i in range(trows):
            for j in range(tcols):
                flipped_template[i][j] = template[trows - i - 1][tcols - j - 1]
        # get the half of the rows and coloumns of the template
        tr_half = floor(trows / 2)
        tc_half = floor(tcols / 2)
        # initialize the resulting image to black(zero brightness levels)
        output_image = np.zeros(image.shape)
        # convolve the template
        for x in range(tc_half, cols - tc_half):  # address all columns except border
            for y in range(tr_half, rows - tr_half): # address all rows except border
                # get the area of the image which corresponds to the location of the template
                pixel_set = image[y - tr_half:y + tr_half + 1, x - tc_half: x + tc_half + 1]
                # get the resulting value of the point by multiplying two matrices element-wise and getting the sum.
                output_image[y][x] = floor(np.multiply(flipped_template, pixel_set).sum())
        return output_image

    def low_pass(self, image):
        '''
        create the low-pass version of an image

        :param array image: a 2-D matrix of an image

        '''
        # create a gaussian template
        template = self.gaussian_template()
        # split the image into 3 separate color channels
        b, g, r = cv2.split(image)
        # create a pool with 3 processes
        pool = Pool(processes=3)
        # assign a task of template convolution to an independent process
        # and run the processes in an asynchronous way
        multi_res = [pool.apply_async(self.template_convolution, (i, template)) for i in (b, g, r)]
        # get the result from the processes
        res = [i.get() for i in multi_res]
        # merge three color channels into a new image
        new_image = np.uint8(cv2.merge(res))
        return new_image

    def high_pass(self, low_pass, original):
        '''
        create the high-pass version of an image

        :param array low_pass: a low-pass version of an image
        :param array original: the original image

        '''
        # get the high-pass version of an image
        # by subtracting the low-pass version from the image itself
        return original - low_pass

    def ouput_lowpass(self):
        '''
        get the low-pass version of an image and output it
        '''
        low = self.low_pass(self.image1)
        cv2.imwrite(self.filename, low)

    def ouput_highpass(self):
        '''
        get the high-pass version of an image and output it
        '''
        low = self.low_pass(self.image1)
        # The high frequency image can be visualised by adding 0.5 to every pixel in each colour channel.
        high = self.high_pass(low, self.image1) + 128
        cv2.imwrite(self.filename, high)

    def main(self):
        '''
        create a hybrid image
        '''
        # mark the start time of the operation
        start = time.time()
        # get the low-pass versions of the two input images
        low_pass1 = self.low_pass(self.image1)
        low_pass2 = self.low_pass(self.image2)
        # get the high-pass version of the first input image
        high_pass1 = self.high_pass(low_pass1, self.image1)
        # create a hybrid image
        # using the high-pass version of the first input image
        # and the low-pass version of the second input image
        hybrid_img = high_pass1 + low_pass2
        # output the execution time of the operations above
        print('Execution time: %.2f s' % (time.time() - start))
        # plot the two original images and their hybrid image
        plt.subplot(131), plt.imshow(cv2.cvtColor(self.image1, cv2.COLOR_BGR2RGB)), plt.title('image1')
        plt.subplot(132), plt.imshow(cv2.cvtColor(self.image2, cv2.COLOR_BGR2RGB)), plt.title('image2')
        plt.subplot(133), plt.imshow(cv2.cvtColor(hybrid_img, cv2.COLOR_BGR2RGB)), plt.title('hybrid')
        plt.show()
        # output the hybrid image
        cv2.imwrite(self.filename, hybrid_img)

if __name__ == '__main__':
    if sys.argv[1] in ['-ol', '-oh']:  # used for outputting the low-pass or high-pass version of an image
        image = sys.argv[2]
        filename = sys.argv[3]
        sigma = sys.argv[-1] if len(sys.argv) > 4 else 4.0
        hi = HybridImage(image1=image, output_filename=filename, sigma=sigma)
        if sys.argv[1] == '-ol':
            hi.ouput_lowpass()
        else:
            hi.ouput_highpass()
    else:  # used for creating a hybrid image from two images
        image1 = sys.argv[1]
        image2 = sys.argv[2]
        output_filename = sys.argv[3]
        sigma = sys.argv[-1] if len(sys.argv) > 4 else 4.0
        hybrid_img = HybridImage(image1, output_filename, image2, sigma)
        hybrid_img.main()


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

# In[7]:

#Exercicio 01

import numpy as np

#Séries de Entrada
s1 = [168, 398, 451, 337, 186, 232, 262, 349, 189, 204, 220, 220, 207, 239, 259, 258,
242, 331, 251, 323, 106, 1055, 170]

s2 = [168, 400, 451, 300, 186, 200, 262, 349, 189, 204, 220, 220, 207, 239, 259, 258,
242, 331, 251, 180, 106, 1055, 200]

#Array
#Matriz 1 por 23
a = np.array(s1)
b = np.array(s2)

print('Array s1:\n',a)
print('\t')
print('Array s2:\n',b)
print('\t')

#Distância Euclidiana
##Decomposição da Distância Euclidiana
sub_array = b - a
pot_array = sub_array ** 2
somat_array = np.sum(pot_array)
dist_euclid = np.sqrt(somat_array)

print('Distância Euclidiana','='*50)
print(dist_euclid)
print('\t')

#Série temporal com os valores médios entre s1 e s2
##Decomposição da Média
soma_array = a + b
media_array = soma_array/2

print('Série Temporal - Média','='*48)
print(media_array)
print('\t')

#Série temporal com os valores máximos entre s1 e s2
maximos_array = np.maximum(a,b)

print('Série Temporal - Máximos','='*46)
print(maximos_array)
print('\t')

#Série temporal com os valores mínimo entre s1 e s2
minimos_array = np.minimum(a,b)

print('Série Temporal - Minimos','='*46)
print(minimos_array)
print('\t')

plt.show()

# In[ ]:


import numpy as np

__all__ = ['optimalBins', 'optimalBinSize']

def optimalBinSize(x):
    """Returns the optimal bin size for data in x"""
    interquartile = np.diff(np.prctile(x, [25, 75]))
    return 2. * interquartile * len(x)**(-1./3)

def optimalBins(x,factor=1):
    """Returns the edges of bins for an optimal histogram of the data X.
    factor widens the bins"""
    sz = optimalBinSize(x) * factor
    return np.arange(x.min(), x.max(), sz)


import numpy as np
from loss_functions import sigmoid_loss

class GradientDescent:
    # The weighs will be randomly initiated as the first step of the fitting process.
    # The default loss function is sigmoid, and the default threshold is set to 0.01
    def __init__(self, *, alpha, threshold=1e-2, loss=sigmoid_loss):
        self.weights = []
        self.alpha = alpha
        self.threshold = threshold
        self.loss = loss

    # This method is fitting the weights using the training X sample
    def fit(self, X, y):
        # Set n to the number of objects in the
        n = X.shape[1]
        # Here we use a sample from a uniform distribution:
        # low boundary of the interval set to  -1 / (2 * n)
        # high boundary of the interval is set to 1 / (2 * n)
        # The size of the sample is n
        rng = np.random.default_rng()
        self.weights = rng.uniform(-1 / (2 * n), 1 / (2 * n), size=n)
        # At the moment there is no errors
        errors = []

        # The algorithm will be interrupted after reaching the threshold
        while True:
            M = # Set M to a dot product of X and weights multiplied by y
            loss, derivative = # calculate them using loss function of the instance

            # The gradient is a sum along the 0 axis of the following array:
            # transposed X multiplied by y (X.T * y)
            # then multiplied by transposed derivative (derivative.T)
            # with the product of this multiplication transposed again (.T)
            grad_q = # write the expression
            new_weights = # update the weights

            # Updating the errors with the loss
            errors.append(np.sum(loss))
            # If the new_weights are close enough to the old ones, the fitting is complete
            if np.linalg.norm(self.weights - new_weights) < self.threshold:
                break
            # Update the stored weights before the next iteration
            self.weights = # set it to the new ones
        # The method returns the losses stored along the fitting process
        return errors

    def predict(self, X):
        # As we have only two normalized classes we can return the np.sign of the
        # X dot product weights
        return # return the predicted classes

Pima indians diabetes and linear classifier/Read data/gradient_descent.py


import numpy as np
import copy

import pdb

def lls(objective_function=None, X=0, Z=0, theta_0=None, convergence_tol=1e-4,
    iter_limit=100, theta_limit=None, relaxation_factor=1.0, scale_vector=None):
    """
    Does a linearized least squares to iterate for the best solution to a
    problem defined by:
        z=f(x, theta)
    Given samples of x and z, the function attempts to find the parameters theta

    Arguments:
        obj_fcn: should return Z and the Jacobian of Z with theta as a function
            of X and theta. For a system with n states, m outputs, p parameters,
            and q samples, X will be (n,q) Z will be (m,q) J_Z will be (m,p)
            (Z_hat, J_Z) = obj_fcn(X, theta)
        X: samples of X, the function state
        Z: corresponding samples of Z, the function output
        theta_0: an initial guess for theta, must be specified so we know the
            size of theta
        convergence_tol: iteration is terminated when the residual from the
            newton-raphson solver drops below this value
        iter_limit: iteration is alternately terminated when this number of
            iterations is reached
        theta_limit: (lower_lim, upper_lim), tuple with arrays same size as
            theta giving the upper and lower limits.
        relaxation_factor: the increment between iterations is multiplied by
            this factor to slow down or speed up the convergence
        scale_vector : a vector used to scale the components of theta, used in
            computing the norm and in adjusting the relative magnitude of the
            increment in theta during each iteration. The vector is made into a
            diagonal matrix SCALE. The norm is computed:
            sqrt(theta*SCALE'*theta') and the parameter increment is computed
            SCALE*theta. This defaults to an identity matrix

    Returns:
        (theta, residual, iterations)
        theta: the solved value for theta
        final_tol: the N-R residual when iteration was terminated
        iterations: the number of iterations at termination
    """
    assert hasattr(objective_function, '__call__'), 'objective function must be\
        a valid function'
    assert theta_0 is not None, 'must specify initial guess for theta'

    if theta_limit is None:
        theta_limit = (np.full(theta_0.shape, -np.inf),
            np.full(theta_0.shape, np.inf))

    if scale_vector is None:
        scale_matrix = np.identity(theta_0.shape[0])
        scale_matrix_squared = scale_matrix
    else:
        assert scale_vector.shape == theta_0.shape, 'tolerance scale must have the\
            same dimension as the parameter vector'
        scale_matrix = np.diag(scale_vector)
        scale_matrix_squared = scale_matrix*scale_matrix

    delta = 100
    iter_count = 0

    delta_theta = np.zeros(theta_0.shape)

    theta = copy.deepcopy(theta_0)

    while delta > convergence_tol and iter_count < iter_limit:
        iter_count += 1

        Z_hat, J_Z = objective_function(X, theta)
        delta_Z = Z - Z_hat

        delta_theta = np.linalg.lstsq(J_Z, delta_Z)[0]

        delta = np.sqrt(
            delta_theta.dot(scale_matrix_squared).dot(delta_theta.T))

        theta += scale_matrix.dot(delta_theta) * relaxation_factor

        theta = np.maximum(theta, theta_limit[0])
        theta = np.minimum(theta, theta_limit[1])

    return (theta, delta_theta, iter_count)


from __future__ import division
import numpy as np

def kdv_equation(tmax):
    # Grid
    m = 256
    x = np.arange(-m/2,m/2)*(2*np.pi/m)
    dx = x[1]-x[0]
    L = x[-1]-x[0] + dx
    k = 0.4/m**2
    nmax = int(round(tmax/k))
    k = 1.0*tmax/nmax

    # Initial data
    A = 25; B = 16;
    u = 3*A**2/np.cosh(0.5*(A*(x+2.)))**2 + 3*B**2/np.cosh(0.5*(B*(x+1)))**2
    uhat = np.fft.fft(u)

    # This is the order in which numpy's FFT gives the frequencies:
    xi=np.fft.fftfreq(m)*m/(L/(2*np.pi))

    xi3 = 1j*xi**3
    g = -0.5j * k * xi
    E = np.exp(k*xi3/2.)
    E2 = E**2

    for n in range(1,nmax+1):
        # Runge-Kutta stages
        a = g*np.fft.fft(np.real(np.fft.ifft(uhat))**2)
        b = g*np.fft.fft(np.real(np.fft.ifft(E*(uhat+a/2.)))**2)
        c = g*np.fft.fft(np.real(np.fft.ifft(E*uhat + b/2) )**2)
        d = g*np.fft.fft(np.real(np.fft.ifft(E2*uhat + E*c))**2)
        uhat = E2*uhat + (E2*a + 2*E*(b+c) + d)/6.

    return uhat

def burgers_equation(tmax):
    epsilon = 0.1

    # Grid
    m = 64
    x = np.arange(-m/2,m/2)*(2*np.pi/m)
    dx = x[1]-x[0]
    L = x[-1]-x[0] + dx
    k = 2./m
    nmax = int(round(tmax/k))
    k = 1.0*tmax/nmax

    # Initial data
    u = np.sin(x)**2 * (x<0.)
    # u = np.sin(x)**2
    uhat = np.fft.fft(u)

    # This is the order in which numpy's FFT gives the frequencies:
    xi=np.fft.fftfreq(m)*m/(L/(2*np.pi))

    eps_xi2 = epsilon * xi**2.
    g = -0.5j * k * xi
    E = np.exp(-k*eps_xi2/2.)
    E2 = E**2

    nmax = int(round(tmax/k))

    for n in range(1,nmax+1):
        # Runge-Kutta stages
        a = g*np.fft.fft(np.real(np.fft.ifft(uhat))**2)
        b = g*np.fft.fft(np.real(np.fft.ifft(E*(uhat+a/2.)))**2)
        c = g*np.fft.fft(np.real(np.fft.ifft(E*uhat + b/2) )**2)
        d = g*np.fft.fft(np.real(np.fft.ifft(E2*uhat + E*c))**2)
        uhat = E2*uhat + (E2*a + 2*E*(b+c) + d)/6.

    return uhat

def relative_error(y, y_ref):
    return np.linalg.norm(y-y_ref)/np.linalg.norm(y_ref)

# compare the computed reference solutions from create_reference.py
# with those solution from the RK4 code above
if __name__ == "__main__":
    import matplotlib.pyplot as plt
    plt.hold('true')
    from create_reference import kdv_reference, kdv_solout, burgers_reference, burgers_solout

    N = 256
    x = np.arange(-N/2,N/2)*(2*np.pi/N)
    kdv_ref = np.loadtxt("reference_kdv.txt")
    rk4_ref = np.squeeze(np.real(np.fft.ifft(kdv_equation(0.003))))
    rk4_line, = plt.plot(x, rk4_ref, "s-")
    kdv_line, = plt.plot(x, kdv_ref, "s-")
    plt.legend([rk4_line, kdv_line], ["Rk4", "kdv"], loc=1)
    plt.show()
    print relative_error(kdv_ref, rk4_ref)

    N = 64
    x = np.arange(-N/2,N/2)*(2*np.pi/N)
    burgers_ref = np.loadtxt("reference_burgers.txt")
    rk4_ref = np.squeeze(np.real(np.fft.ifft(burgers_equation(3.))))
    rk4_line, = plt.plot(x, rk4_ref, "s-")
    burgers_line, = plt.plot(x, burgers_ref, "s-")
    plt.legend([rk4_line, burgers_line], ["Rk4", "burgers"], loc=1)
    plt.show()
    print relative_error(burgers_ref, rk4_ref)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jun  6 14:40:17 2020

@author: lukepinkel
"""

import numpy as np
import scipy as sp

def fo_fc_fd(f, x, eps=None, args=()):
    if eps is None:
        eps = (np.finfo(float).eps)**(1.0/3.0)
    n = len(np.asarray(x))
    g, h = np.zeros(n), np.zeros(n)
    for i in range(n):
        h[i] = eps
        g[i] = (f(x+h, *args) - f(x, *args)) / eps
        h[i] = 0
    return g

def so_fc_fd(f, x, eps=None, args=()):
    if eps is None:
        eps = (np.finfo(float).eps)**(1.0/3.0)
    n = len(np.asarray(x))
    H, hi, hj = np.zeros((n, n)), np.zeros(n), np.zeros(n)
    eps2 = eps**2
    for i in range(n):
        hi[i] = eps
        for j in range(i+1):
            hj[j] = eps
            H[i, j] = (f(x+hi+hj, *args) - f(x+hi, *args) - f(x+hj, *args) + f(x, *args)) / eps2
            H[j, i] = H[i, j]
            hj[j] = 0
        hi[i] = 0
    return H

def so_gc_fd(g, x, eps=None, args=()):
    if eps is None:
        eps = (np.finfo(float).eps)**(1.0/3.0)
    n = len(np.asarray(x))
    H, h = np.zeros((n, n)), np.zeros(n)
    gx, gxh = np.zeros((n, n)), np.zeros((n, n))
    for i in range(n):
        h[i] = eps
        gx[i] = g(x, *args)
        gxh[i] = g(x+h, *args)
        h[i] = 0
    for i in range(n):
        for j in range(i+1):
            H[i, j] = ((gxh[i, j] - gx[i, j]) + (gxh[j, i] - gx[j, i])) / (2 * eps)
            H[j, i] = H[i, j]
    return H

def fo_fc_cd(f, x, eps=None, args=()):
    if eps is None:
        eps = (np.finfo(float).eps)**(1.0/3.0)
    n = len(np.asarray(x))
    g, h = np.zeros(n), np.zeros(n)
    for i in range(n):
        h[i] = eps
        g[i] = (f(x+h, *args) - f(x - h, *args)) / (2 * eps)
        h[i] = 0
    return g

def so_fc_cd(f, x, eps=None, args=()):
    p = len(np.asarray(x))
    if eps is None:
        eps = (np.finfo(float).eps)**(1./3.)
    H = np.zeros((p, p))
    ei = np.zeros(p)
    ej = np.zeros(p)
    for i in range(p):
        for j in range(i+1):
            ei[i], ej[j] = eps, eps
            if i==j:
                dn = -f(x+2*ei, *args)+16*f(x+ei, *args)\
                    -30*f(x, *args)+16*f(x-ei, *args)-f(x-2*ei, *args)
                nm = 12*eps**2
                H[i, j] = dn/nm
            else:
                dn = f(x+ei+ej, *args)-f(x+ei-ej, *args)-f(x-ei+ej, *args)+f(x-ei-ej, *args)
                nm = 4*eps*eps
                H[i, j] = dn/nm
                H[j, i] = dn/nm
            ei[i], ej[j] = 0.0, 0.0
    return H

def so_gc_cd(g, x, eps=None, args=()):
    if eps is None:
        eps = (np.finfo(float).eps)**(1./3.)
    n = len(np.asarray(x))
    H, h = np.zeros((n, n)), np.zeros(n)
    gxp, gxn = np.zeros((n, n)), np.zeros((n, n))
    for i in range(n):
        h[i] = eps
        gxp[i] = g(x+h, *args)
        gxn[i] = g(x-h, *args)
        h[i] = 0
    for i in range(n):
        for j in range(i+1):
            H[i, j] = ((gxp[i, j] - gxn[i, j] + gxp[j, i] - gxn[j, i])) / (4 * eps)
            H[j, i] = H[i, j]
    return H

def fd_coefficients(points, order):
    A = np.zeros((len(points), len(points)))
    A[0] = 1
    for i in range(len(points)):
        A[i] = np.asarray(points)**(i)
    b = np.zeros(len(points))
    b[order] = sp.special.factorial(order)
    c = np.linalg.inv(A).dot(b)
    return c

def finite_diff(f, x, epsilon=None, order=1, points=None):
    if points is None:
        points = np.arange(-4, 5)
    if epsilon is None:
        epsilon = (np.finfo(float).eps)**(1./3.)
    coefs = fd_coefficients(points, order)
    df = 0.0
    for c, p in list(zip(coefs, points)):
        df+=c*f(x+epsilon*p)
    df = df / (epsilon**order)
    return df

def grad_approx(f, x, eps=1e-4, tol=None, d=1e-4, nr=6, v=2):
    tol = np.finfo(float).eps**(1/3) if tol is None else tol
    h = np.abs(d * x) + eps * (np.abs(x) < tol)
    n = len(x)
    u = np.zeros_like(h)
    A = np.zeros((nr, n))
    for i in range(nr):
        for j in range(n):
            u[j] = h[j]
            A[i, j] = (f(x + u) - f(x - u)) / (2.0 * h[j])
            u[j] = 0.0
        h /= v
    for i in range(nr-1):
        t = 4**(i+1)
        A = (A[1:(nr-i)]*t - A[:(nr-i-1)]) / (t-1.0)
    return A

def jac_approx(f, x, eps=1e-4, tol=None, d=1e-4, nr=6, v=2):
    tol = np.finfo(float).eps**(1/3) if tol is None else tol
    h = np.abs(d * x) + eps * (np.abs(x) < tol)
    n = len(x)
    p = len(f(x))
    u = np.zeros_like(h)
    A = np.zeros((nr, n, p))
    for i in range(nr):
        for j in range(n):
            u[j] = h[j]
            A[i, j] = (f(x + u) - f(x - u)) / (2.0 * h[j])
            u[j] = 0.0
        h /= v
    for i in range(nr-1):
        t = 4**(i+1)
        A = (A[1:(nr-i)]*t - A[:(nr-i-1)]) / (t-1.0)
    return A

def _hess_approx(f, x, a_eps=1e-4, r_eps=1e-4, xtol=None, nr=6, s=2):
    xtol = np.finfo(np.float).eps**(1/3) if xtol is None else xtol
    d = np.abs(r_eps * x) + a_eps * (np.abs(x) < xtol)
    y = f(x)
    u = np.zeros_like(d)
    v = np.zeros_like(d)
    nx, ny = len(x), len(y)
    D = np.zeros((ny, int(nx * (nx + 3) // 2)))
    Da = np.zeros((ny, nr))
    Hd = np.zeros((ny, nx))
    Ha = np.zeros((ny, nr))

    for i in range(nx):
        h = d.copy()
        u[i] = 1.0
        for j in range(nr):
            fp, fn = f(x+h*u), f(x-h*u)
            Da[:, j] = (fp - fn) / (2 * h[i])
            Ha[:, j] = (fp + fn - 2.0 * y) / (h[i]**2)
            h /= s
        for j in range(nr-1):
            for k in range(nr-j-1):
                t = 4**(j+1)
                Da[:, k] = (Da[:, k+1] * t - Da[:, k]) / (t - 1.0)
                Ha[:, k] = (Ha[:, k+1] * t - Ha[:, k]) / (t - 1.0)
        D[:, i] = Da[:, 0]
        Hd[:, i] = Ha[:,0]
        u[i] = 0.0

    c = nx-1
    for i in range(nx):
        for j in range(i+1):
            c += 1
            if (i==j):
                D[:, c] = Hd[:, i]
            else:
                h = d.copy()
                u[i] = 1.0
                v[j] = 1.0
                for m in range(nr):
                    fp = f(x + u*h + v*h)
                    fn = f(x - u*h - v*h)
                    fii = Hd[:, i] * h[i]**2
                    fjj = Hd[:, j] * h[j]**2
                    Da[:, m] = (fp - 2.0 * y + fn - fii - fjj) / (2.0 * h[i] * h[j])
                    h /= s
                for m in range(nr-1):
                    for k in range(nr-m-1):
                        t = 4**(m+1)
                        Da[:, k] = (Da[:, k+1]*t - Da[:, k]) / (t - 1.0)
                D[:, c] = Da[:, 0]
                u[i] = v[j] = 0.0
    return D

def hess_approx(f, x, a_eps=1e-4, r_eps=1e-4, xtol=None, nr=6, s=2):
    D = _hess_approx(f, x, a_eps, r_eps, xtol, nr, s)
    y = f(x)
    nx, ny = len(x), len(y)
    H = np.zeros((ny, nx, nx))
    k = nx - 1
    for i in range(nx):
        for j in range(i+1):
            k+=1
            H[:, i, j] = H[:, j, i] = D[:, k]
    return H


import numpy as np
from numpy import linalg as lng #Vamos a importar el paquete de álgebra lineal de Numpy.

#Crear vectores y matrices

vector=np.array([0,4,6])
#print(vector)

matriz=np.matrix([[1,3,5],[3,4,6],[5,6,4]])
print(matriz)

#operaciones entre vectores y matrices

#en vectores tenemos producto punto y producto por escalar

vector1=np.array([4,7,8])
#print(vector1)

vector2=np.dot(vector,vector1)

#print(vector2)

#vector3=2*vector

#print(vector3)

#Autovalores y autovectores

eigenvalues, eigenvectors = lng.eig(matriz)

#print (eigenvalues, "\n")
#print(eigenvectors)

#norma, determinante y otras medidas

vector4_1=lng.norm(vector)
vector4_2=lng.norm(vector1)

#print(vector4_1)
#print(vector4_2)

det1=lng.det(matriz)

#print(det1)

matriz2=([[1,0,0],[0,2,0],[0,0,3]]) #Matriz diagonal para verificar

#print(matriz2)
det2=lng.det(matriz2)

#print(det2)

#resolver sistemas de equaciones lineales e invertir matrices

A=np.matrix([[2,-2],[5,1]])
b=np.array([2,3])

#print(A,b)

x=lng.solve(A,b)  #Encuentra x para el sistema "Ax=b"

#print(x)

inver=lng.inv(matriz)

print(inver)


# 输出神经元的激活函数，分类问题常用softmax， softmax = exp(Ak) / for i in range(1, n + 1) sum(Ai)
# softmax的分子是输入信号Ak的指数函数，分母是所有输入信号的指数函数和
import numpy as np
'''
softmax funciton:
softmax的输出范围都在0.0~1.0之间的实数
softmax的输出总和为1， 因为有这个缘故，我们可以把softmax的输出解释为概率
'''
# way 1: normalize:
def softmax_normal(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y

# way 2: Multiply Contant C"
def softmax_optimize(a):
    c = np.max(a)
    exp_a = np.exp(a - c) # 解决溢出问题
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y


import numpy as np
from math import sqrt

# we define the values of the statement
observations = np.array([1.017,2.221,1.416,0.641,0.124,1.728])

# our parameter to estimate is the mean
# we use the arithmetic mean
mean_obs=np.mean(observations)

# we compute the standard deviation of the arithmetic mean
standard_deviation_sample_mean = 0.75/sqrt(6)

def compute_a_and_b(quantile):
    a = mean_obs - standard_deviation_sample_mean*quantile
    b = mean_obs + standard_deviation_sample_mean*quantile
    return a,b

# 1 - alpha = 0.68, quantile = 1.000
quantile = 1.000
print(0.68, compute_a_and_b(quantile))

# 1 - alpha = 0.90, quantile = 1.645
quantile = 1.645
print(0.90, compute_a_and_b(quantile))

# 1 - alpha = 0.95, quantile = 1.960
quantile = 1.960
print(0.95, compute_a_and_b(quantile))

print()
# ----------------------
# now we compute CI for the case sigma is unknown.
# first we estimate sigma with the unbiased estimator (ddof=1)
standard_deviation_estimation = np.std(observations, ddof=1)

standard_deviation_sample_mean_estimation = standard_deviation_estimation/sqrt(6)

def compute_a_and_b_estimation(quantile):
    a = mean_obs - standard_deviation_sample_mean_estimation*quantile
    b = mean_obs + standard_deviation_sample_mean_estimation*quantile
    return a,b

# 1 - alpha = 0.68, quantile = 1.104
quantile = 1.104
print(0.68, compute_a_and_b_estimation(quantile))

# 1 - alpha = 0.90, quantile = 2.015
quantile = 2.015
print(0.90, compute_a_and_b_estimation(quantile))

# 1 - alpha = 0.95, quantile = 2.571
quantile = 2.571
print(0.95, compute_a_and_b_estimation(quantile))

Projects/Parameter Estimation and Bayesian Statistics/_1_Exercise_1.py


import numpy
import sys
from math import sqrt

__author__ = 'Rob Edwards'

def mean(arr):
    """
    Calculate the mean of all the values in a list

    :param arr: The list of values
    :type arr: list
    :return: The mean of all the numbers
    :rtype: float
    """
    n = len(arr)
    sm = sum(arr)
    return float(sm) / float(n)

def median(arr):
    """
    Calculate the median of all the values in a list

    :param arr: The list of values
    :type arr: list
    :return: The median of all the numbers
    :rtype: float
    """
    arr.sort()
    n = len(arr)
    mid = int(n / 2)
    return arr[mid]

def stdev(arr):
    """
    Calculate the standard deviation of all the values in a list

    :param arr: The list of values
    :type arr: list
    :return: The st dev of all the numbers
    :rtype: float
    """
    n = len(arr)
    sm = sum(arr)
    nmpyarr = numpy.array(arr)
    sum_of_squares = numpy.sum(nmpyarr * nmpyarr)
    result = 0
    try:
        result = sqrt(sum_of_squares / n - (sm / n) ** 2)
    except:
        sys.stderr.write("can't create the stdev. Only have " + str(n) + " elements in the array")

    return result


from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style

style.use('fivethirtyeight')

xs = np.array([1, 2, 3, 4, 5, 6], dtype=np.float64)
ys = np.array([5, 4, 6, 5, 6, 7], dtype=np.float64)

def best_fit_slope_and_intercept(xs, ys):
	m = (((mean(xs) * mean(ys)) - mean(xs * ys)) /
		 ((mean(xs) ** 2) - mean(xs ** 2)))
	b = mean(ys) - m * mean(xs)
	return m, b

def squared_error(ys_orig, ys_line):
	return sum((ys_line - ys_orig) ** 2)

def coefficient_of_determination(ys_orig, ys_line):
	y_mean_line = [mean(ys_orig) for y in ys_orig]
	squared_error_regr = squared_error(ys_orig, ys_line)
	square_error_y_mean = squared_error(ys_orig, y_mean_line)
	return 1 - (squared_error_regr / square_error_y_mean)

m, b = best_fit_slope_and_intercept(xs, ys)

regression_line = [(m * x) + b for x in xs]

predict_x = 8
predict_y = (m * predict_x) + b

r_squared = coefficient_of_determination(ys, regression_line)
print(r_squared)

plt.scatter(xs, ys)
plt.scatter(predict_x, predict_y, color='g')
plt.plot(xs, regression_line)
plt.show()


import numpy as np
from coptim.function import Function

def func_sum(x):
    total = 0
    for i in range(1, len(x) + 1):
        total += i * (x[i - 1] - 1)

    return total

class Func1(Function):

    def eval(self, x):
        total = 0
        for i in range(len(x)):
            Fi = x[i] - 1
            total += Fi ** 2

        return total + np.power(func_sum(x), 2) + np.power(func_sum(x), 4)

    def gradient(self, x):
        fs = func_sum(x)
        grad = 2 * (x - 1)
        for i in range(1, len(x) + 1):
            grad[i - 1] = grad[i - 1] + 2 * i * fs + 4 * i * np.power(fs, 3)

        return grad

    def hessian(self, x):
        fs = func_sum(x)
        hess_mat = np.zeros(shape=(len(x), len(x)))

        for i in range(1, len(x) + 1):
            for j in range(1, len(x) + 1):
                if i == j:
                    hess_mat[i - 1, j - 1] = 2 + 2 * i * j + 12 * i * j * np.power(fs, 2)

                else:
                    hess_mat[i - 1, j - 1] = 2 * i * j + 12 * i * j * np.power(fs, 2)

        return hess_mat


#!/usr/bin/env python3
import sys
sys.path.append('.')

import numpy as np
from matplotlib import pyplot as plt
from fit import anneal

def f(x,p):
    res = np.zeros(x.shape)
    for i in range(3):
        res += p[3*i]*np.exp(-((x-p[3*i+1])/p[3*i+2])**2)
    return res

xdata = np.linspace(-30,30,1000)
ydata = f(xdata, [1, -25, 1, 1, 0, 1, -1, 25, 1])

plt.plot(xdata, ydata, label='data')

bounds=list(zip([-30]*9,[30]*9))
print(bounds)
result = anneal.curve_fit(f, xdata, ydata, bounds=bounds, maxiter=1000, )
print(result)
pres = result.x
ydata_res = f(xdata, pres)
plt.plot(xdata, ydata_res, label='fit')
plt.legend()
plt.grid()

plt.show(block=False)
import code
code.interact()


"""
Plot a geodesic on the sphere S2
"""

import matplotlib.pyplot as plt
import numpy as np

import geomstats.visualization as visualization

from geomstats.hypersphere import Hypersphere

SPHERE2 = Hypersphere(dimension=2)
METRIC = SPHERE2.metric

def main():
    initial_point = [1., 0., 0.]
    initial_tangent_vec = SPHERE2.projection_to_tangent_space(
                                        vector=[1., 2., 0.8],
                                        base_point=initial_point)
    geodesic = METRIC.geodesic(initial_point=initial_point,
                               initial_tangent_vec=initial_tangent_vec)

    n_steps = 10
    t = np.linspace(0, 1, n_steps)

    points = geodesic(t)
    ax = plt.subplot(111, projection="3d", aspect="equal")

    visualization.plot(points, ax, space='S2')
    plt.show()

if __name__ == "__main__":
    main()


""" Test 2-D Karhunen- Loeve approximation: Exponential covariance"""
import numpy as np
from simulation import KL_2DNys
import matplotlib.pyplot as plt
# some tunable parameters
N = 100 # order of the KL expansion
n,m = (50,50) number of grid points
rho = 0.1 # scale length of the exponential
def Cov(x,y,rho = 0.1):
    r = np.sqrt(np.linalg.norm(x - y,2, axis =1)) # dist. between each point
    return np.exp(-r/rho)
lims = [0., 1., 0., 1.] # domain corners
X, phi, L = KL_2DNys(N,n,m,lims,Cov)
plt.loglog(range(N), L[:N]) # plot the eigenvalues descending.
plt.title("The exponential's first {} eigenvalues".format(N))
plt.show()
plt.close()
x,y = np.linspace(lims[0],lims[1],n), np.linspace(lims[2],lims[3],m)
xx,yy = np.meshgrid(x,y)# grid for plotting.
for i in range(6): # plot the first 6 eigenfunctions
    plt.subplot(2,3,i+1).set_title('i = {}'.format(i+1)) # add subplot
    e_func = np.array(phi[:,i]).reshape(n,m) # extract and reshape EFs
    plt.pcolor(xx,yy,e_func)
    plt.colorbar()
plt.show()
plt.close()
plt.pcolor(xx,yy,X) # plot the RF realisation
plt.title(r'2D exponential covariance with $\rho$ = {}'.format(rho))
plt.colorbar()
plt.show()


import numpy as np

def standard_norm(data):
    means = data.mean(axis=1)
    stds = data.std(axis=1, ddof=1)
    data = (data - means[:, np.newaxis]) / stds[:, np.newaxis]
    return np.nan_to_num(data)

def l1_norm(data):
    _norm = np.array([x.sum(axis=0) for x in data])
    data = data / _norm[:, np.newaxis]
    return np.nan_to_num(data)

def l2_norm(data):
    _norm = np.array([np.sqrt((x * x).sum(axis=0)) for x in data])
    data = data / _norm[:, np.newaxis]
    return np.nan_to_num(data)


import numpy as np

class Perceptron(object):
    """Implements a single layer Perceptron """

    def __init__(self, input_size, epochs=100, lr=1):
        # initialize the weights vector parameter, adding one for bias
        self.W = np.zeros(input_size + 1)

        # Each time through the training data applying the learning rule is
        # called an Epoch
        self.epochs = epochs

        # Learning rate --this is a hyperparameter, it is not learned via the
        # training set but is supplied.
        self.lr = lr

    @staticmethod
    def sigma(x):
        """This is the activation function. """
        return 1 if x >= 0 else 0

    def train(self, X, d):
        """Iterate through the training data (X) and score the perceptrons
        performance via the labels (d) and adjust weights accordingly."""
        for _ in range(self.epochs):
            for i in range(d.shape[0]):
                y = self.predict(X[i])
                e = d[i] - y
                self.W = self.W + self.lr * e * np.insert(X[i], 0, 1)

    def predict(self, x):
        """y = sigma(z) """
        # add the bias term
        x = np.insert(x, 0, 1)
        # W^T * x (inner product)
        z = self.W.T.dot(x)
        # apply activation function
        a = self.sigma(z)
        # return predicted label
        return a

    def score(self, X, y):
        misclassified_count = 0
        for x_i, target in zip(X, y):
            output = self.predict(x_i)
            if target != output:
                misclassified_count += 1
        total_data_count = len(X)
        return (total_data_count - misclassified_count) / total_data_count


#!/usr/bin/env python3

# %%
import random

from math import sin, cos
from typing import List

import numpy as np
import matplotlib.pyplot as plt

from aray.util import ceil, floor
from aray.types import Point
from aray.stretch import stretch, old_stretch

big_box = set(Point(x, y) for x in range(-20, 40) for y in range(-20, 40))

for fname in ['uniform', 'randint']:
    fn = getattr(random, fname)
    fig, axes = plt.subplots(2, 2, figsize=(8, 8))
    for ax in axes.flat:
        center = Point(fn(3, 7), fn(3, 7))
        # print('center', center)
        r_old = fn(2, 4)
        frac = random.uniform(0.2, 0.3)
        inner = r_old * (1 - frac)
        outer = r_old * (1 + frac)
        d_old = r_old ** 2
        epsilon = int(frac * 1e6)
        assert inner <= outer
        # print('r', r_old, 'inner', inner, 'outer', outer)

        xs = np.linspace(0, 10, 10000)
        outer_upper_ys = np.sqrt(outer**2 - (xs - center.x)**2) + center.y
        outer_lower_ys = -np.sqrt(outer**2 - (xs - center.x)**2) + center.y
        inner_upper_ys = np.sqrt(inner**2 - (xs - center.x)**2) + center.y
        inner_lower_ys = -np.sqrt(inner**2 - (xs - center.x)**2) + center.y

        ax.set_xlim(-1, 11)
        ax.set_ylim(-1, 11)
        ax.set_xticks(range(11))
        ax.set_yticks(range(11))
        ax.grid(which='major', axis='x', linewidth=0.75, color='k', alpha=0.1)
        ax.grid(which='major', axis='y', linewidth=0.75, color='k', alpha=0.1)
        ax.set_aspect('equal')

        ax.plot(xs, outer_upper_ys, 'b-')
        ax.plot(xs, outer_lower_ys, 'b-')
        ax.plot(xs, inner_upper_ys, 'r-')
        ax.plot(xs, inner_lower_ys, 'r-')
        ax.plot([center.x], [center.y], 'gx')

        points = stretch(center, d_old, epsilon, big_box)
        check_points = old_stretch(center, d_old, epsilon)
        assert set(points) == set(check_points)
        # print(points)
        ax.scatter([p.x for p in points], [p.y for p in points])

        # TODO: use this for checking

        def stretch_check(center: Point, outer_radius: float, inner_radius: float) -> List[Point]:
            """ Get the set of points that are between inner_radius and outer_radius away from center """
            # Do this the simple way, where we evaluate every single point
            # and then filter out the ones that are outside the outer circle,
            # or inside the inner circle
            xmin = ceil(center.x - outer_radius)
            xmax = floor(center.x + outer_radius)
            ymin = ceil(center.y - outer_radius)
            ymax = floor(center.y + outer_radius)
            points = []
            for x in range(xmin, xmax + 1):
                for y in range(ymin, ymax + 1):
                    r = (x - center.x)**2 + (y - center.y)**2
                    if r <= outer_radius**2 and r >= inner_radius**2:
                        points.append(Point(x, y))
            return points

        check_points = stretch_check(center, outer, inner)

        # check that points are the same
        assert len(points) == len(check_points)
        assert set(points) == set(
            check_points), f'{set(points) - set(check_points)}'


#!/usr/bin/env python

import numpy as np
from tqdm import tqdm

# we have 3 neurons
NEURONS = 3

# learning rate
LEARNING_RATE = 0.01

# number of iterations
NUM_OF_ITERS = 1000000

# small difference to be able to approximately calculcate derivative
EPSILON = 0.01

class TwoVariablesBooleanFunctionNeuralNetwork:
    def __init__(self, x, y):
        #  - n rows: the number of samples
        self.n = x.shape[0]

        #  - m columns: the number of input variables
        self.m = x.shape[1]

        # x is a np.array which is a n x m matrix. It has:
        #  - n rows: the number of samples
        #  - m columns: the number of input variables
        self.input = x

        # np.shape returns a tuple: (n,m)
        # W is a matrix of m x l:
        #  - m rows: the number of input variables
        #  - l columns: the number of neurons
        # Hints:
        #  - self.W.T[0] # this means the first column of the matrix
        #  - self.W[0]   # this means the first row of the matrix
        #  - self.W.T    # this is the transpose of self.W
        self.W = np.random.rand(self.m, NEURONS)

        # y and output are column vectors of size n
        self.y      = y
        self.output = np.zeros(self.y.shape)

    def feedforward(self):
        self.output = self.feedforward_with_W(self.W)

    def feedforward_with_W(self, W_current):
        a1 = sigmoid(np.dot(self.input, W_current.T[0]))
        a2 = sigmoid(np.dot(self.input, W_current.T[1]))
        a = np.array([np.ones(self.n), a1, a2]).T
        return sigmoid(np.dot(a, W_current.T[2]))

    def J(self, W):
        # here the np.dot multiplication will give back
        # a column vector, which we reshape (so that it will
        # be transposed to a row vector), and then squeeze (to
        # get rid of the extra dimension)
        output_current = self.feedforward_with_W(W)

        #g_xw = np.array(sigmoid(np.dot(self.input, W))).reshape(1,-1).squeeze()

        # calculate the difference between our hypothesis and
        # the desired values
        err_diff = self.y - output_current

        # return the error as sum of squared difference
        return (1 / self.n) * np.sum(np.square(err_diff))

    def W_i_j_plus(self, i, j):
        W_copy = self.W.copy()
        W_copy[i][j] = W_copy[i][j] + EPSILON
        return W_copy

    def W_i_j_minus(self, i, j):
        W_copy = self.W.copy()
        W_copy[i][j] = W_copy[i][j] - EPSILON
        return W_copy

    def delta_J_i_j(self, i, j):
        return (
            (self.J(self.W_i_j_plus(i,j)) - self.J(self.W_i_j_minus(i,j))) / (2*EPSILON)
        )

    def backprop(self):
        delta_J = []

        for i in range(0, self.m):
            for j in range(0, NEURONS):
                delta_J += [self.delta_J_i_j(i,j)]

        delta_J = np.array(delta_J).reshape(self.m,NEURONS)

        self.W -= LEARNING_RATE * delta_J

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

if __name__ == "__main__":
    x = np.array([[1,0,0],
                  [1,0,1],
                  [1,1,0],
                  [1,1,1]])

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

    nn = TwoVariablesBooleanFunctionNeuralNetwork(x,y)

    pbar = tqdm(total = NUM_OF_ITERS)

    for i in range(0, NUM_OF_ITERS):
        nn.feedforward()
        nn.backprop()
        pbar.update(1)

    print("Number of iterations: {}".format(NUM_OF_ITERS))
    print("Weights (W0, W1, ... Wm):\n{}".format(nn.W))
    print("Output (h_W(x)_0, h_W(x)_1, ... h_W(x)_n):\n{}".format(nn.output))