import numpy as np
from numpy.fft import fft, fftshift

import matplotlib.pyplot as plt
from matplotlib import cm

""" Examples """

t = np.linspace(-np.pi, np.pi, 64, endpoint=False)
dt = t[1] - t[0]
f_max = 1 / dt
y = np.sin(np.sqrt(2) * t)
y[0] = 0  # the sample corresponding to -tmax should be set zero
y = fftshift(y)  # make y start with y(t=0)
Y = fftshift(fft(y)) / 64
w = np.linspace(-np.pi * f_max, np.pi * f_max, 64, endpoint=False)
plt.figure()
plt.subplot(2, 1, 1)
plt.plot(w, np.abs(Y), lw=2)
plt.xlim([-10, 10])
plt.ylabel(r"$|Y|$", size=16)
plt.title(r"Spectrum of $\sin\left(\sqrt{2}t\right)$")
plt.grid(True)
plt.subplot(2, 1, 2)
plt.plot(w, np.angle(Y), "ro", lw=2)
plt.xlim([-10, 10])
plt.ylabel(r"Phase of $Y$", size=16)
plt.xlabel(r"$\omega$", size=16)
plt.grid(True)
plt.savefig("Assignment_09/LaTeX/eg1.png")

t1 = np.linspace(-np.pi, np.pi, 64, endpoint=False)
t2 = np.linspace(-3 * np.pi, -np.pi, 64, endpoint=False)
t3 = np.linspace(np.pi, 3 * np.pi, 64, endpoint=False)
# y = np.sin(np.sqrt(2) * t)
plt.figure()
plt.plot(t1, np.sin(np.sqrt(2) * t1), "b", lw=2)
plt.plot(t2, np.sin(np.sqrt(2) * t2), "r", lw=2)
plt.plot(t3, np.sin(np.sqrt(2) * t3), "r", lw=2)
plt.ylabel(r"$y$", size=16)
plt.xlabel(r"$t$", size=16)
plt.title(r"$\sin\left(\sqrt{2}t\right)$")
plt.grid(True)
plt.savefig("Assignment_09/LaTeX/eg2.png")

t1 = np.linspace(-np.pi, np.pi, 64, endpoint=False)
t2 = np.linspace(-3 * np.pi, -np.pi, 64, endpoint=False)
t3 = np.linspace(np.pi, 3 * np.pi, 64, endpoint=False)
y = np.sin(np.sqrt(2) * t1)
plt.figure()
plt.plot(t1, y, "bo", lw=2)
plt.plot(t2, y, "ro", lw=2)
plt.plot(t3, y, "ro", lw=2)
plt.ylabel(r"$y$", size=16)
plt.xlabel(r"$t$", size=16)
plt.title(r"$\sin\left(\sqrt{2}t\right)$ with $t$ wrapping every $2\pi$")
plt.grid(True)
plt.savefig("Assignment_09/LaTeX/eg3.png")

t = np.linspace(-np.pi, np.pi, 64, endpoint=False)
dt = t[1] - t[0]
f_max = 1 / dt
y = t
y[0] = 0  # the sample corresponding to -tmax should be set zero
y = fftshift(y)  # make y start with y(t=0)
Y = fftshift(fft(y)) / 64
w = np.linspace(-np.pi * f_max, np.pi * f_max, 64, endpoint=False)
plt.figure()
plt.semilogx(np.abs(w), 20 * np.log10(np.abs(Y)), lw=2)
plt.xlim([1, 10])
plt.ylim([-20, 0])
plt.xticks([1, 2, 5, 10], ["1", "2", "5", "10"], size=16)
plt.ylabel(r"$|Y|$ (dB)", size=16)
plt.title(r"Spectrum of a digital ramp")
plt.xlabel(r"$\omega$", size=16)
plt.grid(True)
plt.savefig("Assignment_09/LaTeX/eg4.png")

t1 = np.linspace(-np.pi, np.pi, 64, endpoint=False)
t2 = np.linspace(-3 * np.pi, -np.pi, 64, endpoint=False)
t3 = np.linspace(np.pi, 3 * np.pi, 64, endpoint=False)
n = np.arange(64)
wnd = fftshift(0.54 + 0.46 * np.cos(2 * np.pi * n / 63))
y = np.sin(np.sqrt(2) * t1) * wnd
plt.figure()
plt.plot(t1, y, "bo", lw=2)
plt.plot(t2, y, "ro", lw=2)
plt.plot(t3, y, "ro", lw=2)
plt.ylabel(r"$y$", size=16)
plt.xlabel(r"$t$", size=16)
plt.title(r"$\sin\left(\sqrt{2}t\right)\times w(t)$ with $t$ wrapping every $2\pi$")
plt.grid(True)
plt.savefig("Assignment_09/LaTeX/eg5.png")

t = np.linspace(-np.pi, np.pi, 64, endpoint=False)
dt = t[1] - t[0]
f_max = 1 / dt
n = np.arange(64)
wnd = fftshift(0.54 + 0.46 * np.cos(2 * np.pi * n / 63))
y = np.sin(np.sqrt(2) * t) * wnd
y[0] = 0  # the sample corresponding to -tmax should be set zero
y = fftshift(y)  # make y start with y(t=0)
Y = fftshift(fft(y)) / 64
w = np.linspace(-np.pi * f_max, np.pi * f_max, 64, endpoint=False)
plt.figure()
plt.subplot(2, 1, 1)
plt.plot(w, np.abs(Y), lw=2)
plt.xlim([-8, 8])
plt.ylabel(r"$|Y|$", size=16)
plt.title(r"Spectrum of $\sin\left(\sqrt{2}t\right)\times w(t)$")
plt.grid(True)
plt.subplot(2, 1, 2)
plt.plot(w, np.angle(Y), "ro", lw=2)
plt.xlim([-8, 8])
plt.ylabel(r"Phase of $Y$", size=16)
plt.xlabel(r"$\omega$", size=16)
plt.grid(True)
plt.savefig("Assignment_09/LaTeX/eg6.png")

t = np.linspace(-4 * np.pi, 4 * np.pi, 256, endpoint=False)
dt = t[1] - t[0]
f_max = 1 / dt
n = np.arange(256)
wnd = fftshift(0.54 + 0.46 * np.cos(2 * np.pi * n / 256))
y = np.sin(np.sqrt(2) * t)
y = y * wnd
y[0] = 0  # the sample corresponding to -tmax should be set zero
y = fftshift(y)  # make y start with y(t=0)
Y = fftshift(fft(y)) / 256
w = np.linspace(-np.pi * f_max, np.pi * f_max, 256, endpoint=False)
plt.figure()
plt.subplot(2, 1, 1)
plt.plot(w, np.abs(Y), lw=2)
plt.xlim([-8, 8])
plt.ylabel(r"$|Y|$", size=16)
plt.title(r"Spectrum of $\sin\left(\sqrt{2}t\right)\times w(t)$")
plt.grid(True)
plt.subplot(2, 1, 2)
plt.plot(w, np.angle(Y), "ro", lw=2)
plt.xlim([-8, 8])
plt.ylabel(r"Phase of $Y$", size=16)
plt.xlabel(r"$\omega$", size=16)
plt.grid(True)
plt.savefig("Assignment_09/LaTeX/eg7.png")

""" End of Examples """

def spectrum(f, n, lim, windowing=True, t=None):
    """Evaluates the DFT spectrum of a function f(t)."""
    if t is None:
        t, dt = np.linspace(-lim, lim, n, endpoint=False, retstep=True)
    else:
        dt = t[1] - t[0]
    f_max = 1 / dt
    w = np.linspace(-np.pi * f_max, np.pi * f_max, n, endpoint=False)
    y = f(t)
    if windowing:
        y *= fftshift(0.54 + 0.46 * np.cos(2 * np.pi * np.arange(n) / n))
    y[0] = 0  # the sample corresponding to -tmax should be set zero
    y = fftshift(y)  # make y start with y(t=0)
    Y = fftshift(fft(y)) / n

    return w, Y

def plotter(w, Y, title, lim, out, xlabel="$\omega$", ylabels=("$|Y|$", r"$\angle Y$")):
    """Plots the passed DFT spectrum."""
    plt.figure()
    plt.subplot(2, 1, 1)
    plt.title(title)
    plt.ylabel(ylabels[0])
    plt.plot(w, np.abs(Y), lw=2)
    plt.xlim(-lim, lim)
    plt.grid(True)

    plt.subplot(2, 1, 2)
    plt.xlabel(xlabel)
    plt.ylabel(ylabels[1])
    phase = np.angle(Y)
    phase[np.where(np.abs(Y) < 3e-3)] = 0
    plt.plot(w, phase, "ro", lw=2)
    plt.xlim(-lim, lim)
    plt.grid(True)

    plt.savefig("Assignment_09/LaTeX/" + out)

def estimate_params(w, Y):
    """Estimates the parameters omega and delta of cos(omega*t + delta)."""
    ii = np.where(w > 0)
    omega = np.sum(np.abs(Y[ii]) ** 2 * w[ii]) / np.sum(np.abs(Y[ii]) ** 2)
    i = np.argmin(np.abs(w - omega))
    delta = np.angle(Y[i])

    return omega, delta

w, Y = spectrum(lambda t: np.cos(0.86 * t) ** 3, 64 * 4, 4 * np.pi, False)
plotter(w, Y, "Spectrum of $\cos^3(\omega_0 t)$ without a Hamming window", 3, "q2a")

w, Y = spectrum(lambda t: np.cos(0.86 * t) ** 3, 64 * 4, 4 * np.pi)
plotter(w, Y, "Spectrum of $\cos^3(\omega_0 t)$ with a Hamming window", 3, "q2b")

w, Y = spectrum(lambda t: np.cos(1.5 * t + 0.5), 128, np.pi)
plotter(w, Y, "Spectrum of $\cos(\omega_0 t + \delta)$", 3, "q3")
print("Estimated w_0 = {:f}, delta = {:f} without noise".format(*estimate_params(w, Y)))

w, Y = spectrum(lambda t: np.cos(1.5 * t + 0.5) + np.random.randn(128) / 10, 128, np.pi)
plotter(w, Y, "Spectrum of $\cos(\omega_0 t + \delta) with noise$", 3, "q4")
print("Estimated w_0 = {:f}, delta = {:f} with noise".format(*estimate_params(w, Y)))

w, Y = spectrum(lambda t: np.cos(16 * t * (1.5 + t / (2 * np.pi))), 1024, np.pi, False)
plotter(w, Y, "Spectrum of chirped signal without a Hamming window", 60, "q5a")

w, Y = spectrum(lambda t: np.cos(16 * t * (1.5 + t / (2 * np.pi))), 1024, np.pi)
plotter(w, Y, "Spectrum of chirped signal with a Hamming window", 60, "q5b")

Y_mag = np.zeros((16, 64))
for i, t in enumerate(np.split(np.linspace(-np.pi, np.pi, 1024, endpoint=False), 16)):
    w, Y = spectrum(lambda t: np.cos(16 * t * (1.5 + t / (2 * np.pi))), 64, np.pi, False, t)
    Y_mag[i] = np.abs(Y)

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")

t, dt = np.linspace(-np.pi, np.pi, 1024, endpoint=False, retstep=True)
f_max = 1 / dt
t = t[::64]
w = np.linspace(-f_max * np.pi, f_max * np.pi, 64, endpoint=False)
t, w = np.meshgrid(t, w)

surf = ax.plot_surface(w, t, Y_mag.T, cmap=cm.coolwarm, linewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.ylabel("Frequency ($\omega$)")
plt.xlabel("Time ($t$)")
plt.savefig("Assignment_09/LaTeX/q6")

plt.show()


#python3 steven 03/04/2020
#All found activation fuction in ML
#Reference: https://en.wikipedia.org/wiki/Activation_function
import numpy as np

def Identity(x):
    return x

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

def Binary_step(x):
    return np.where(x<0,0,1)

def Tanh(x):
    return np.tanh(x)

def SQNL(x): #https://ieeexplore.ieee.org/document/8489043
    y = np.zeros((len(x),))

    l = np.where(x > 2.0)
    if len(l) != 0:
        y[l[0]]=1
    l = np.where(x<=2.0)
    if len(l) != 0:
        y[l[0]]=x[l[0]]-x[l[0]]**2/4
    l = np.where(x<0)
    if len(l) != 0:
        y[l[0]]=x[l[0]]+x[l[0]]**2/4
    l = np.where(x<-2.0)
    if len(l) != 0:
        y[l[0]]=-1

    return y

def ArcTan(x):
    return np.arctan(x)

def ArcSinH(x):
    return np.arcsinh(x)

def Softsign(x): #http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf
    return x/(1+abs(x))

def ISRu(x,alpha=1.0): #https://arxiv.org/pdf/1710.09967.pdf
    return x/np.sqrt(1+alpha*x**2)

def ISRLu(x,alpha=1.0):#https://arxiv.org/pdf/1710.09967.pdf
    y = np.zeros((len(x),))
    l = np.where(x < 0)
    if len(l) != 0:
        y[l[0]]=ISRu(x[l[0]],alpha)

    l = np.where(x >= 0)
    if len(l) != 0:
        y[l[0]]=x[l[0]]

    return y

def PLu(x,alpha=0.1,c=1): #https://arxiv.org/pdf/1809.09534.pdf
    c = np.zeros((x.shape))+c
    return np.maximum(alpha*(x+c)-c, np.minimum(alpha*(x-c)+c, x))

def Relu(x): #https://www.cs.toronto.edu/~fritz/absps/reluICML.pdf
    return np.maximum(np.zeros((x.shape)),x)

def BReLu(x):#https://arxiv.org/pdf/1709.04054.pdf
    if len(x) % 2 ==0:
        return Relu(x)
    return -1*Relu(-1*x)

def LeakyRelu(x):#https://www.semanticscholar.org/paper/Rectifier-Nonlinearities-Improve-Neural-Network-Maas/367f2c63a6f6a10b3b64b8729d601e69337ee3cc
    return np.where(x<0,0.01*x,x)

def PRelu(x,alpha=.1): #https://arxiv.org/pdf/1502.01852.pdf
    return np.where(x<0,alpha*x,x)

def RRelu(x,alpha=.1): #https://arxiv.org/pdf/1505.00853.pdf
    return np.where(x<0,alpha*x,x)

def GELu(x):#https://arxiv.org/pdf/1606.08415.pdf   #https://github.com/hendrycks/GELUs
    return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * np.power(x, 3))))

def ELu(x,alpha=1.0): #https://arxiv.org/pdf/1511.07289.pdf
    return np.where(x<0, alpha*(np.exp(x)-1), x)

def SELu(x,alpha=1.67326,la=1.0507): #https://arxiv.org/pdf/1706.02515.pdf
    return la*ELu(x,alpha)

def softPlus(x): #http://proceedings.mlr.press/v15/glorot11a/glorot11a.pdf
    return np.log(1+np.exp(x))

def BentIdentity(x):
    return 0.5*(np.sqrt(x**2+1)-1)+x

def SoftExponential(x,alpha=1.0): #https://arxiv.org/pdf/1602.01321.pdf
    if alpha<0:
        return -1*(np.log(1-alpha*(x+alpha))/alpha)
    elif alpha == 0:
        return x
    else:
        return (np.exp(alpha*x)-1)/alpha + alpha

def SoftClipping(x,alpha=1.0): #https://arxiv.org/pdf/1810.11509.pdf
    return (1/alpha)*np.log((1+np.exp(alpha*x))/(1+np.exp(alpha*(x-1))))

def Squashing(x,alpha=0.5,lam=1.0,beta=1): #https://arxiv.org/pdf/1910.02486.pdf
    z1 = np.exp(beta*(x-(alpha-lam*0.5)))
    z2 = np.exp(beta*(x-(alpha+lam*0.5)))
    return (1/(lam*beta))*np.log((1+z1)/(1+z2))

def Sinusoid(x):
    return np.sin(x)

def Sinc(x):
    return np.where(x==0,1,np.sin(x)/x)

def Gaussian(x):
    return np.exp(-1*x**2)

def SQ_RBF(x):
    y = np.zeros((len(x),))
    l = np.where(abs(x) <= 1)
    if len(l) != 0:
        y[l[0]]=1-0.5*x[l[0]]**2

    l = np.where(abs(x) > 1)
    if len(l) != 0:
        y[l[0]]=0.5*(2-abs(x[l[0]]))**2

    l = np.where(abs(x) > 2)
    if len(l) != 0:
        y[l[0]]=0

    return y

def swish(x):
    return x*sigmoid(x)

def ACON_A(x, beta=1): #ActivateOrNot (ACON) ,RelU:max(x, 0)
    #https://arxiv.org/pdf/2009.04759.pdf
    #return x*sigmoid(x*beta)
    return ACON_B(x, 0, beta=beta)

def ACON_B(x, p, beta=1): #PRelU:max(x, p*x)
    #return (1-p)*x*sigmoid(beta*(1-p)*x) + p*x
    return ACON_C(x, 1, p, beta=beta)

def ACON_C(x, p1,p2, beta=1): #max(p1*x, p2*xx)
    return (p1-p2)*x*sigmoid(beta*(p1-p2)*x) + p2*x


# Operator functions
# MMultiply multiplies matrices. Written to ensure that any inbuilt optimal multiplication routines in python
# are not used and the fundamental matrix multiplication is done
# SparseDot uses the CSR format to perform sparse matrix multiplication efficiently

import numpy as np
from sparse_methods import MatrixtoCSR

def MMultiply(a, b, outer=False):
    if isinstance(a, (int, float)) and isinstance(b, (int, float)):
        return a*b

    if len(a.shape) == 1:
        if outer:
            a = a[:, np.newaxis]
        else:
            a = a[np.newaxis, :]
    if len(b.shape) == 1:
        if outer:
            b = b[np.newaxis, :]
        else:
            b = b[:, np.newaxis]

    (m, n) = np.shape(a)
    (p, q) = np.shape(b)

    if n != p:
        raise ValueError("Dimensions of a and b incompatible")

    ab = np.zeros((m, q), dtype=a.dtype)
    for i in range(m):
        for j in range(n):
            for k in range(q):
                ab[i][k] += a[i][j]*b[j][k]

    if ab.shape[1] == 1:
        ab = ab.transpose()[0]

    return ab

def SparseDot(Acsr, x, row=None):
    if row != None:
        dot = 0
        for i in range(Acsr['IA'][row], Acsr['IA'][row+1]):
            dot += Acsr['A'][i]*x[Acsr['JA'][i]]

        return dot

    m = Acsr['m']
    dot = np.zeros(m, dtype=Acsr['A'].dtype)
    for i in range(m):
        dot[i] = SparseDot(Acsr, x, i)

    return dot

# Test cases
if __name__ == "__main__":
    """
    A = np.array([[10., -1., 2., 0.],
              [-1., 11., -1., 3.],
              [2., -1., 10., -1.],
              [0.0, 3., -1., 8.]])
    """
    A = np.array([[10, 20, 0, 0, 0, 0],
                  [0, 30, 0, 40, 0, 0],
                  [0, 0, 50, 60, 70, 0],
                  [0, 0, 0, 0, 0, 80]], dtype=np.float64)

    b = np.array([6., 25., -11., 15., 0.3, -1])
    x = np.zeros_like(b)
    csr = MatrixtoCSR(A)
    print (b)
    print (x)
    print (MMultiply(A, b))
    print ("")
    print (np.dot(A, b))
    print ("")
    sd = SparseDot(csr, b)
    print (sd)


import numpy as np

def logistic(x):
    return 1.0 / (1.0 + np.exp(-x))

def logistic_derivative(x):
    return logistic(x) * (1.0 - logistic(x))

def tanh(x):
    return np.tanh(x)

def tanh_derivative(x):
    return (1.0 - (tanh(x)**2))

def relu(x):
    return np.maximum(x, 0, x)

def relu_derivative(x):
    x[x <= 0] = 0
    x[x > 0] = 1
    return x

def softplus(x):
    return np.log(1.0 + np.exp(x))

def softplus_derivative(x):
    return 1.0 / (1.0 + np.exp(-x))


__all__ = ["cramers_corrected_stat"]

import numpy as np
import pandas as pd
import scipy.stats as ss

def cramers_corrected_stat(contingency_table: pd.DataFrame) -> float:
    """Calculate bias corrected cramer's V

    Parameters
    ----------
    contingency_table : pandas.DataFrame
        Contingency table of two nominal variables

    Returns
    -------
    float
        Cramer's V value for the input nominal variables

    Examples
    --------
    >>> import pandas as pd
    >>> from mymldev.eda import cramers_corrected_stat
    >>> df = pd.read_csv("titanic_train.csv")
    >>> cramers_corrected_stat(
    ...     pd.crosstab(df["Pclass"], df["Survived"])
    ... )
    0.33668387622245516

    Notes
    -----
    Cramer's V is the measure of association between two
    nominal variables, giving a value between 0 and +1
    '0' corresponds to no association
    '1' corresponds to complete association, value can reach 1
    only when two variables are equal to each other.

    References
    ----------
    .. [1] https://en.wikipedia.org/wiki/Cram%C3%A9r%27s_V
    """
    chi2 = ss.chi2_contingency(contingency_table)[0]
    n = contingency_table.sum().sum()
    phi2 = chi2 / n
    r, k = contingency_table.shape
    phi2corr = max(0, phi2 - ((k - 1) * (r - 1) / (n - 1)))
    rcorr = r - ((r - 1) ** 2) / (n - 1)
    kcorr = k - ((k - 1) ** 2) / (n - 1)
    cramers_v = np.sqrt(phi2corr / min((kcorr - 1), (rcorr - 1)))
    return cramers_v


from scipy.sparse.linalg import LinearOperator
from scipy.sparse import spmatrix
import numpy as np
import logging

def spectral_norm(A, tol=1e-8, max_iter=1000):
    """Computes the spectral norm of a linear operator A using power iteration.

    Parameters
    ===================
    - `A` (`numpy.ndarray`, `scipy.sparse.spmatrix`, or `scipy.sparse.linalg.LinearOperator`):
      the matrix for which we want to compute the spectral norm.

    Keyword parameters
    ====================
    - `tol` (float, default = `1e-8`): tolerance used to determine whether or not we
      should stop iterating. Once the estimates for the spectral norm are within distance
      `tol` of one another, we stop the power iterations and return.
    - `max_iter` (int, default = `1000`): maximum number of power iterations to do. If
      we reach this number of iterations then this function will return, but will display
      a warning that we reached the maximum number of iterations.
      - Power iteration can be extremely slow to converge, so you may need a large value
        of `max_iter` in order to find the true spectral norm.

    Return
    ====================
    - `sp_norm` (float): the estimated spectral norm of `A`.

    Code by Will Shand at the request of Stephen Becker, March 2019
    """
    if not any(issubclass(type(A),T) for T in [np.ndarray, spmatrix, LinearOperator]):
        raise ValueError("spectral_norm can only take arguments of type "
                "numpy.ndarray, scipy.sparse.spmatrix, or "
                "scipy.sparse.linalg.LinearOperator.")

    # Create an anonymous function matvec_op whose effect is equivalent to multiplying
    # the input by A'A.
    if issubclass(type(A), LinearOperator):
        matvec_op = lambda x: A.adjoint().matvec(A.matvec(x))
    else:
        matvec_op = lambda x: A.T.dot(A.dot(x))

    sp_norm = 0.
    sp_iter = np.random.normal(size = A.shape[-1])
    for ii in range(max_iter):
        Ax = matvec_op(sp_iter)
        new_sp_norm = np.linalg.norm(sp_iter)

        # Stopping condition when eigenvalue estimates get sufficiently close
        if abs(new_sp_norm - sp_norm) < tol:
            break
        else:
            sp_norm = new_sp_norm
            sp_iter = Ax / new_sp_norm

    if ii == max_iter-1:
        logging.warn(" spectral_norm ran for max_iter = %d iterations "
            "without converging. Returning..." % max_iter)

    return np.sqrt(sp_norm)

"""
TESTING
"""
if __name__ == "__main__":
    from scipy.sparse import random as sprandom

    # 1. Test on some random numpy arrays
    for ii in range(50):
        X = np.random.normal(size=(50,30))
        assert(abs(np.linalg.norm(X,2) - spectral_norm(X, max_iter=5000, tol=1e-8)) <= 1e-7)

    # 2. Test on some LinearOperator instances
    for ii in range(50):
        X1 = sprandom(50,50,density=0.2)
        X2 = np.random.normal(size=(10,50))

        # Dense representation of difference X1 - X2'X2
        A = X1 - X2.T.dot(X2)

        # LinearOperator representation of X1 - X2'X2
        mv  = lambda x: X1.dot(x) - X2.T.dot(X2.dot(x))
        rmv = lambda x: X1.T.dot(x) - X2.T.dot(X2.dot(x))
        L   = LinearOperator(X1.shape, matvec=mv, rmatvec=rmv)

        assert(abs(np.linalg.norm(A,2) - spectral_norm(L, max_iter=5000, tol=1e-8)) <= 1e-7)


# Interval for classification accuracy

#common confidence intervals
#    1.64 (90%)
#    1.96 (95%)
#    2.33 (98%)
#    2.58 (99%)

# for 50 samples
# binomial confidence interval
from math import sqrt
interval = 1.96 * sqrt( (0.2 * (1 - 0.2)) / 50)
print('%.3f' % interval)

# for 100 samples
# binomial confidence interval
interval = 1.96 * sqrt( (0.2 * (1 - 0.2)) / 100)
print('%.3f' % interval)

# for a dataset of 100 samples; 95% confidence interval with a significance of 0.05
from statsmodels.stats.proportion import proportion_confint
lower, upper = proportion_confint(88, 100, 0.05)
print('lower=%.3f, upper=%.3f' % (lower, upper))


# %% Imports
import numpy as np
import matplotlib.pyplot as plt

# %% Data
x = [100, 50, 40, 15, 5]
y = [1, 2, 3, 4, 5]

# %% Using polyfit
p = np.polyfit(x, y, 2, rcond=None, full=False, w=None, cov=False)
print(p)

# %% Showing results
def f(p, t):
    return p[0] * t ** 2 + p[1] * t + p[2]

min_x = np.min(x)
max_x = np.max(x)

t = np.linspace(min_x, max_x, 100, endpoint=True)
plt.scatter(x, y, c='r')
plt.plot(t, f(p, t))

# %% Using poly1d
f = np.poly1d(p)
t = np.linspace(min_x, max_x, 100, endpoint=True)
plt.scatter(x, y, c='r')
plt.plot(t, f(t))

# %% Visualization
x = [130, 100, 70, 60, 30, 10]
y = [0.5 , 1, 2, 3, 4, 5]
min_x = np.min(x)
max_x = np.max(x)
p = np.polyfit(x, y, 1, rcond=None, full=False, w=None, cov=False)
print(p)
f = np.poly1d(p)
t = np.linspace(min_x, max_x, 100, endpoint=True)
plt.scatter(x, y, c='r')
plt.plot(t, f(t))


from numpy import array,mean,log,exp

def nonlinear_fit(x,yo):
    n = len(x)
    y = log(yo)
    xsum = sum(x)
    ysum = sum(y)
    xmean = mean(x)
    ymean = mean(y)
    xysum = sum(x * y)
    x2sum = sum(x**2)
    A = (n * xysum - xsum * ysum)/(n * x2sum - xsum**2)
    B = ymean - A * xmean
    print('A = %8.4f B = %8.4f' % (A, B))
    b = exp(B)
    a = A
    return a,b

def f(a,b,x):
    return b * exp(a * x)

x = array([0, 1, 2, 4])
yo = array([2, 4, 8, 15])
a,b = nonlinear_fit(x,yo)

print('a = %8.4f b = %8.4f' % (a,b))
print("y = %8.4f exp(%8.4f x)" % (b,a))


import numpy as np
from mlthon.backend import _dim_check

class GaussianNB:
	def __init__(self) -> None:
		pass

	def fit(self, X, y):
		_dim_check(X, 2, 'X')
		_dim_check(y, 1, 'y')

		self.classes = np.unique(y)
		self.mean_ = {}
		self.var_ = {}
		self.priors_ = {}

		for class_value in self.classes:
			sep_arr = X[np.where(y == class_value)[0]]
			self.priors_[class_value] = len(sep_arr)/X.shape[0]
			self.mean_[class_value] = np.mean(sep_arr, axis=0)
			self.var_[class_value] = np.var(sep_arr, axis=0)

		return self

	def predict(self, X):
		_dim_check(X, 2, 'X')

		predictions = []
		for row in X:
			posteriors = []
			for class_value in self.classes:
				prior = self.priors_[class_value]
				conditional = np.sum(np.log(self.gaussian_density(class_value, row)))
				posterior = prior + conditional
				posteriors.append(posterior)

			predictions.append(np.argmax(posteriors))

		predictions = np.array(predictions)
		return predictions

	def gaussian_density(self, class_value, X):
		mean = self.mean_[class_value]
		var = self.var_[class_value]
		return np.exp((-1/2)*((X - mean)**2) / (2 * var)) / np.sqrt(2 * np.pi * var)


"""
https://projecteuler.net/problem=205

Peter has nine four-sided (pyramidal) dice, each with faces numbered 1, 2, 3, 4.
Colin has six six-sided (cubic) dice, each with faces numbered 1, 2, 3, 4, 5, 6.

Peter and Colin roll their dice and compare totals: the highest total wins. The result is a draw if the totals are equal.

What is the probability that Pyramidal Pete beats Cubic Colin? Give your answer rounded to seven decimal places in the form 0.abcdefg
"""
import numpy as np

def calculate_pdf_dice(nr_side_of_dice, nr_dice):
    """"
    Help function that calculates the PDF of one dice player
    """
    max_score = nr_dice*nr_side_of_dice
    nr_possible_combinations = nr_side_of_dice**nr_dice

    outcome_frequency = np.zeros(max_score)
    dice_outcome_vector = np.ones(nr_dice)

    # Loop over all possible combination of dice to calculate the outcome frequency
    for i in range(0, nr_possible_combinations):
        outcome_result = dice_outcome_vector.sum()
        outcome_frequency[int(outcome_result - 1)] = outcome_frequency[int(outcome_result - 1)] + 1
        # Used to have a flexible way to have nested for-loops [decided by number of dice and side of dice]
        for dice_nr in range(0, nr_dice):
            updated_other_dice = False

            # Criteria to see if wone of the dice to the right shall have an updated value
            criteria_1 = dice_outcome_vector[0:dice_nr+1].sum() == nr_side_of_dice*(dice_nr+1)
            if dice_nr+1 < nr_dice:
                criteria_2 = dice_outcome_vector[dice_nr+1] != nr_side_of_dice
            else:
                criteria_2 = True

            # update dice to the right if all the dice to the left have maximum value (see criteria above)
            if criteria_1 and criteria_2 and i < nr_possible_combinations-1:
                dice_outcome_vector[0:dice_nr + 1] = 1
                dice_outcome_vector[dice_nr + 1] = dice_outcome_vector[dice_nr + 1] + 1
                updated_other_dice = True
                break
        # if not update dice to the right - update dice to the left
        if not updated_other_dice:
            dice_outcome_vector[0] = dice_outcome_vector[0] + 1

    # calculate the PDF funciton by taking the frequency/number of possible outcomes
    pdf_vector = outcome_frequency/outcome_frequency.sum()

    return pdf_vector

if __name__ == '__main__':
    peter_pdf = calculate_pdf_dice(nr_side_of_dice=4, nr_dice=9)
    colin_pdf = calculate_pdf_dice(nr_side_of_dice=6, nr_dice=6)

    # calculate probability that peter wins using conditional probabilities
    culumtative_win_peter = 0
    for i in range(0, len(peter_pdf)):
        culumtative_win_peter += colin_pdf[0:i].sum() * peter_pdf[i]

    print("probability win peter: {}".format(culumtative_win_peter))


import numpy as np
import matplotlib.pylab as plt

print("######")

def mean_squared_error(y, t):
    return 0.5 * np.sum((y - t) ** 2)

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)

    batch_size = y.shape[0]
    delta = 1e-7
    # return -np.sum(t * np.log(y + delta)) / batch_size
    return -np.sum(np.log(y[np.arange(batch_size), t] + delta)) / batch_size

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
y1 = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
y2 = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
print(mean_squared_error(np.array(y1), np.array(t)))
print(mean_squared_error(np.array(y2), np.array(t)))

print("######")
print(cross_entropy_error(np.array(y1), np.array(t)))
print(cross_entropy_error(np.array(y2), np.array(t)))

print("######")

# numerical differentiation

def numerical_diff(f, x):
    # h = 10e-50 # => 0.000 error
    # return (f(x+h) - f(x)) / h
    h = 10e-4  # => 0.0001
    return (f(x + h) - f(x - h)) / (h * 2)

def fn_example1(x):
    return 0.01 * x ** 2 + 0.1 * x

x = np.arange(0.0, 20.0, 0.1)
y = fn_example1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x, y)
plt.show()

print(numerical_diff(fn_example1, 5))
print(numerical_diff(fn_example1, 10))
print("######")

def tangent_line(f, x):
    d = numerical_diff(f, x)
    print(d)
    y = f(x) - d * x
    return lambda t: d * t + y

tf2 = tangent_line(fn_example1, 5)
tf3 = tangent_line(fn_example1, 10)
y2 = tf2(x)
y3 = tf3(x)

plt.plot(x, y)
plt.plot(x, y2)
plt.plot(x, y3)
plt.show()

print("######")
# Partial differentiation

def fn_example2(x):
    return x[0] ** 2 + x[1] ** 2
    # return np.sum(x**2)

def fn_tmp1(x0):
    return x0 * x0 + 4 ** 2

def fn_tmp2(x1):
    return 3 ** 2 + x1 * x1

print(numerical_diff(fn_tmp1, 3))
print(numerical_diff(fn_tmp2, 4))


import numpy as np

def householder_compute (x):
  s = np.dot(x,x)
  alpha = -np.sign(x[0])*np.sqrt(s)
  x[0] -= alpha
  x /= np.sqrt(np.dot(x,x))
  return alpha

def householder_apply (u,x):
  n = x.shape
  s = np.dot(u,x)
  for i in range(0,n[0]):
    x[i] -= 2*s*u[i]

def householder_qr (A):
  (m,n) = A.shape
  D = np.zeros(n)
  for k in range(0,n-1):
    D[k] = householder_compute(A[k:m,k])
    for i in range (k+1,n):
      householder_apply(A[k:m,k],A[k:m,i])
  return D

def householder_solve (QR,D,b)
  (m,n) = QR.shape
  for k in range (0,n-1):
    householder_apply(QR[k:m,k],b[k:m])
  x = np.zeros(n)
  for k in range(n-1,-1,-1):
    x[k] = b[k]
    for j in range(k+1,n):
      x[k] -= A[k,j]*x[j]
    x[k] /= D[k]
  return x

x = np.array([3.,4.,5.])
print (x.shape)
u = np.copy(x)
print(x)
a = householder_compute(u)
print(x)
print(u)
print(a)

householder_apply (u,x)
print(x)

print("QR")
A = np.array([[2.,-1.,0.],[-1.,2.,-1.],[0.,-1.,2.]])
diag = householder_qr(A)
print(A)
print(diag)


import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from sklearn import linear_model
import matplotlib.animation as animation
import os
import subprocess

# apply gradient descent algorithm to optimiza cost function of several variables

def cost(x):
	"""Cost function of linear regression (LR) model with coefficient x"""
	m = A.shape[0]
	return .5/m *np.linalg.norm(A.dot(x)-b,2)**2

def grad(x):
	"""Gradient function of cost function"""
	m = A.shape[0]
	return 1/m *A.T.dot(A.dot(x)-b)

def numerical_grad(x, cost):
	"""Calculating numerical gradient with coefficient x to check whether our grad function is correct or not"""
	eps = 1e-4
	g = np.zeros_like(x)
	for i in range(len(x)):
		x_p = x.copy()
		x_n = x.copy()
		x_p[i] += eps
		x_n[i] -= eps
		g[i] = (cost(x_p) - cost(x_n))/(2*eps)
	return g

def check_grad(x, cost, grad):
	"""Check grad function by comparing actual gradient value and numerical gradient (estimation)"""
	x = np.random.rand(x.shape[0], x.shape[1])
	grad1 = grad(x)
	grad2 = numerical_grad(x, cost)
	return True if np.linalg.norm(grad1 - grad2) < 1e-6 else False

def gradient_descent(x_init, grad, alpha):
	"""
	Running gradient descent with initial value x_init, grad function, learning rate alpha
	This function return x: list of solution after each iteration and iteration: last iteration it has gone through
	"""
	m = len(x_init)
	x = [x_init]
	for iteration in range(itr):
		x_new = x[-1] - alpha*grad(x[-1])
		if np.linalg.norm(grad(x_new))/m < 1e-3: #Stop gradient descent when grad value is too small.
			break
		x.append(x_new)
	return (x, iteration)

############### Main ####################

# create random data
b = np.array([[2,5,7,9,11,16,19,23,22,29,29,35,37,40,46,42,39,31,30,28,20,15,10,6]]).T
A = np.array([[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]]).T

ax = plt.axes(xlim=(-10, 50), ylim=(-10, 50)) #restrict the figure showing only values in specified range

#Visualize data
plt.plot(A,b,'ro', label='_nolegend_') # plot data points
fig1 = plt.figure(1)
# append column ones to matrix A
ones = np.ones((A.shape[0],1), dtype=np.int8)
A = np.concatenate((ones,A), axis=1)

## append x^2 to A
x_square = np.array([A[:,1]**2]).T
A = np.concatenate((A,x_square), axis=1)

line, = ax.plot([], [], color = 'blue') # plot function used in animation

# plot solution found by scikit learn
A_dagger = np.linalg.inv(A.transpose().dot(A)).dot(A.transpose())
x_formula = A_dagger.dot(b)

x0_gd = np.linspace(1,46,10000)
y0_sklearn = x_formula[0][0] + x_formula[1][0]*x0_gd + x_formula[2][0]*x0_gd*x0_gd
plt.plot(x0_gd, y0_sklearn, color='green')

# apply gradient descent
itr = 101
learning_rate = .000001 #.0001
x_init = np.array([[1], [1], [-1]])
x_init = np.array([[ 1.05455731],
       [ 3],
       [-0.01216413]])

print('Checking gradient...', check_grad(np.random.rand(3, 1), cost, grad))

# running gradient descent
myGD = gradient_descent(x_init, grad, learning_rate)

# plot x_init (black line)
x0_gd = np.linspace(1,46,10000)
y0_init = x_init[0][0] + x_init[1][0]*x0_gd + x_init[2][0]*x0_gd*x0_gd

plt.plot(x0_gd, y0_init, color = 'black')

# plot lines in each iteration
def update(i):
	x_gd = myGD[0][i]
	y0_gd = x_gd[0][0] + x_gd[1][0]*x0_gd + x_gd[2][0]*x0_gd*x0_gd
	line.set_data(x0_gd,y0_gd)
	# print(i)
	# print(round(np.linalg.norm(grad(myGD[0][i]))/3, 3))
	plt.xlabel('Iteration: {}/{}, |grad|={}'.format(i, myGD[1],round(np.linalg.norm(grad(myGD[0][i]))/3, 3)))
	return line,

# legend for graph
plt.legend(('Value in each GD iteration', 'Solution by formular', 'Inital value for GD'), loc=(0.52, 0.01))
ltext = plt.gca().get_legend().get_texts()
plt.setp(ltext[0], fontsize=10)
plt.setp(ltext[1], fontsize=10)
plt.setp(ltext[2], fontsize=10)

# save animation to mp4 file using ffmpeg
Writer = animation.writers['ffmpeg']
writer = Writer(fps=15, metadata=dict(artist='Me'), bitrate=1800)

line_ani = animation.FuncAnimation(fig1, update, myGD[1]+1, interval=50, blit=True)
line_ani.save('100iteration.mp4', writer='ffmpeg')

# https://stackoverflow.com/questions/11269575/how-to-hide-output-of-subprocess-in-python-2-7
# FNULL = open(os.devnull, 'w')
# subprocess.call(['ffmpeg', '-i', '50iter.mp4', '50iter.gif'], stdout=FNULL, stderr=subprocess.STDOUT) # Execute command line to convert .mp4 to .gif using ffmpeg and hide output of command line to terminal

plt.show()


from lega.legendre_basis import legendre_function, mass_matrix,\
    stiffness_matrix, legendre_basis
from scipy.linalg import eigh
from sympy.plotting import plot
from sympy import Symbol
from math import sqrt
import numpy as np

# Visualize the eigenfunctions of -u'' = lmnda u in (-1, 1) with u'(-1)=u'(1)=0

n = 10
basis = legendre_basis(10)

# Solve the eigenvalue problem to get coeffs of the new basis
A = stiffness_matrix(n)
M = mass_matrix(n)

lmbda1, V1 = eigh(A.toarray()[1:, 1:], M.toarray()[1:, 1:])
lmbdas = np.concatenate([np.array([0]), lmbda1])
V = np.eye(n)/sqrt(2.)
V[1:, 1:] = V1

# Make the new basis
Abasis = [legendre_function(v) for v in V.T]

print 'eigenvalues', lmbdas

# Plot the basis for comparison
x = Symbol('x')
f_fA = iter(zip(basis, Abasis))

f, fA = next(f_fA)
p = plot(f, (x, -1, 1), show=False)
p[0].line_color = 'red'
p_ = plot(fA, (x, -1, 1), show=False)
p_[0].line_color = 'blue'
p.append(p_[0])

for f, fA in f_fA:
    p_ = plot(f, (x, -1, 1), show=False)
    p_[0].line_color = 'red'
    p.append(p_[0])

    p_ = plot(fA, (x, -1, 1), show=False)
    p_[0].line_color = 'blue'
    p.append(p_[0])

p.show()

# Some questions, what are the approximation properties of Abasis? And how does
# the transformation between function and its series look? And, is there a
# clever way to get the eigenvalues and eigenvectors


import numpy as np

def Mahalanobis_distance(x, mu, M):
    """
    Calculating the Mahalanobis distance between x and mu, MD(x,mu), in a space with metric M.

    ------------PARAMETERS------------
    @param simul : Number of simulations

    @param x : First vector
    @param mu : Second vector, usually containing the expected values
    @param M : The inverse of the covariance matrix
    @returns : The Mahalanobis distance between x and mu MD(x,mu)
    ----------------------------------
    """

    delta = np.array(x)-np.array(mu)
    #MD(x,mu) = (delta*M*delta)^{1/2}
    return np.sqrt(np.dot(np.dot(delta,M),delta))

def multivariate_hypergeometric_Mahalanobis_distance(x, mu, m):
    """
    Calculating the Mahalanobis distance between x and mu, MD(x,mu), in a metric space defined by a multivariate hypergeometric distribution defined using the vector m.

    ------------PARAMETERS------------
    @param simul : Number of simulations

    @param x : First vector
    @param mu : Second vector, usually containing the expected values
    @param m : Vector containing the number of items in each category
    @returns : The MD(x,mu) and a vector containg the contributions to the square of the MD(x,mu)
    ----------------------------------
    """
    MD=[]
    N = sum(m)
    n = sum(x)
    #if n!=sum(mu):
    #    print("ERROR: The numer of element in each ranking vector must be the same")
    #    print("Please correct")
    #    return None
    gamma = n*(N-n)/(N-1)/N/N

    for i in range(len(x)):
        MD.append(pow((x-mu)[i],2)/N/m[i])

    return np.sqrt(sum(MD)/gamma), np.array(MD)/gamma


import matplotlib.pyplot as plt
import numpy as np

def feval(funcName, *args):
    return eval(funcName)(*args)

def RK4thOrder(func, yinit, x_range, h):
    m = len(yinit)
    n = int((x_range[-1] - x_range[0]) / h)

    x = x_range[0]
    y = yinit

    # Containers for solutions
    xsol = np.empty(0)
    xsol = np.append(xsol, x)

    ysol = np.empty(0)
    ysol = np.append(ysol, y)

    for i in range(n):
        k1 = feval(func, x, y)

        yp2 = y + k1 * (h / 2)

        k2 = feval(func, x + h / 2, yp2)

        yp3 = y + k2 * (h / 2)

        k3 = feval(func, x + h / 2, yp3)

        yp4 = y + k3 * h

        k4 = feval(func, x + h, yp4)

        for j in range(m):
            y[j] = y[j] + (h / 6) * (k1[j] + 2 * k2[j] + 2 * k3[j] + k4[j])

        x = x + h
        xsol = np.append(xsol, x)

        for r in range(len(y)):
            ysol = np.append(ysol, y[r])

    return [xsol, ysol]

#########################       INPUT GOES HERE

def myFunc(x, y):
    dy = np.zeros((len(y)))

    n = y[0]
    p = y[1]
    K = 100
    r = 0.6
    a = 0.04
    muy = 1.2

    dy[0] = r * n * (1 - n / K) - a * n * p
    dy[1] = -muy * p + a * n * p
    return dy

h = 0.01
t0 = 0.0
tn = 100

y01 = 70.0
y02 = 20.0

##########################       \INPUT GOES HERE

x = np.array([t0, tn])
yinit = np.array([y01, y02])

[ts, ys] = RK4thOrder('myFunc', yinit, x, h)

node = len(yinit)
ys1 = ys[0::node]
ys2 = ys[1::node]

##########################       PRINT RESULT

for i in range(len(ts)):
    print("t = ", ts[i])
    print("y1 = ", ys1[i], ";   y2 = ", ys2[i])

##########################       \PRINT RESULT

##########################       Plot y1, y2 with respect to t

plt.plot(ts, ys1, 'r')
plt.plot(ts, ys2, 'b')
plt.xlim(x[0], x[1])
plt.legend(["y(1)", "y(2)"], loc=2)
plt.xlabel('x', fontsize=17)
plt.ylabel('y', fontsize=17)
plt.tight_layout()
plt.show()

##########################       \Plot y1, y2 with respect to t

##########################       plot y1 - y2 as x - y

plt.plot(ys1, ys2, 'r')
plt.xlabel('con mồi', fontsize=17)
plt.ylabel('thú săn mồi', fontsize=17)
plt.show()

##########################       \plot y1 - y2

# Uncomment the following to print the figure:
#plt.savefig('Fig_ex7_RK4th.png', dpi=600)

Topic 5 - Solving Differential Equations/28.Runge_Kutta/R-K system of equation/RK4.py


import numpy as np
import random
import scipy

def generate_markov_seq(n_states, transition_matrix, len_seq, init_state=None):
    states = [k for k in range(n_states)]
    seq = []
    if init_state:
        x0 = init_state
    else:
        x0 = np.random.choice(states) #add initial probabilities
    x_prev = x0
    seq.append(x_prev)
    for i in range(len_seq):
        x_succ = np.where(np.random.multinomial(1, transition_matrix[x_prev, :], size=1) == 1)[1][0]
        seq.append(x_succ)
        x_prev = x_succ
    return seq

def generate_transtion_matrix(n_states):
    mat = []
    for k in range(n_states):
        row = np.random.random(n_states)
        row = row / np.sum(row)
        mat.append(list(row))
    return np.array(mat)

def generate_series(hidden_seq, params):
    T = len(hidden_seq)
    y = []
    for t in range(T):
        mu_step = params[hidden_seq[t]][0]
        sigma_step = params[hidden_seq[t]][1]
        y.append(np.random.normal(mu_step, sigma_step))
    return y

def generate_samples(n_sample, lengths_range, P, params, noise=0., init_state=None):
    Y = []
    for sample in range(n_sample):
        n_states = P.shape[0]
        T = np.random.randint(lengths_range[0], lengths_range[1])
        hidden_seq = generate_markov_seq(n_states, P, T, init_state) #hidden states sequence
        y = generate_series(hidden_seq, params) #time series following HMM model with hidden states and params
        y = np.array(y) + np.random.random(len(y)) * noise #adding noise to series
        Y.append(y)
    return Y


# Add the following two NumPy arrays and Modify a result array by calculating the square root of each element

# My Solution
import numpy as np

arrayOne = np.array([[5, 6, 9], [21, 18, 27]])
arrayTwo = np.array([[15, 33, 24], [4, 7, 1]])

print("addition of two array is\n")
print(arrayOne + arrayTwo)

print("\nResult array after calculating the square root of all elements\n")
print(np.square(arrayOne + arrayTwo))

# Given Solution
import numpy

arrayOne = numpy.array([[5, 6, 9], [21, 18, 27]])
arrayTwo = numpy.array([[15, 33, 24], [4, 7, 1]])

resultArray = arrayOne + arrayTwo
print("addition of two arrays is \n")
print(resultArray)

for num in numpy.nditer(resultArray, op_flags=['readwrite']):
    num[...] = num * num
print("\nResult array after calculating the square root of all elements\n")
print(resultArray)


import numpy as np

def unit_range(arr, data_min=None, data_max=None, samples_in='row'):
    """
    Normalize the data to have unit range. Return the normalized data, and
    the min,  and max used.
    Data is assumed to be in row samples
    If data is arranged in column samples, use samples_in='col'

    Computes norm_arr = (arr - data_min) / (data_max - data_min)

    Returns:
    [normalized_data, data_min, data_max]
    """
    samplesIn = 1 if samples_in == 'col' else 0
    dimsIn = int(not samplesIn)
    nSamples = arr.shape[samplesIn]
    nDims = arr.shape[dimsIn]
    theshape = [1, 1]
    theshape[dimsIn] = nDims
    if data_max is None:
        data_max = arr.max(axis=samplesIn)
    if data_min is None:
        data_min = arr.min(axis=samplesIn)
    norma = (arr - data_min.reshape(theshape) ) / (data_max - data_min).reshape(theshape)
    return norma, data_min, data_max

def mean_zero(arr, mean_vector=None, samples_in='row'):
    """
    Normalize input data to have zero mean.  Return the normalized data and the
    mean which was used to normalize the data

    [normalized, meanvec] = mean_zero(data)

            or

    [normalize, meanvec] = mean_zero(data, provided_mean_vector)
    """
    samplesIn = 1 if samples_in == 'col' else 0
    dimsIn = int(not samplesIn)
    nSamples = arr.shape[samplesIn]
    nDims = arr.shape[dimsIn]
    theshape = [1, 1]
    theshape[dimsIn] = nDims
    if not mean_vector:
        mean_vector = arr.mean(axis=samplesIn).reshape(theshape)
    amean = arr - mean_vector
    return amean, mean_vector

def mean_zero_unit_variance(arr, mean_vector=None, std_vector=None, samples_in='row'):
    """
    Normalize input data to have zero mean and unit variance.
    Return the normalized data, the mean, and the calculated standard
    deviation which was used to normalize the data

    [normalized, meanvec, stddev] = mean_zero_unit_variance(data)
            or
    [normalized, meanvec, stddev] = mean_zero(data, mean_vector=provided_mean_vector)
       etc.
    """
    samplesIn = 1 if samples_in == 'col' else 0
    dimsIn = int(not samplesIn)
    nSamples = arr.shape[samplesIn]
    nDims = arr.shape[dimsIn]
    theshape = [1, 1]
    theshape[dimsIn] = nDims

    if not mean_vector:
        mean_vector = arr.mean(axis=samplesIn).reshape(theshape)
    if not std_vector:
        std_vector  = arr.std(axis=samplesIn).reshape(theshape)
    # If you have a row with absolutely no information, you will divide by zero. Hence...
    std_vector[std_vector < 1e-6] = 1
    norma = (arr - mean_vector) / std_vector
    return norma, mean_vector, std_vector

def labels_to_one_hot(labels):
    '''
    Given a 1-dimensional array-like of labels, convert to a one-hot representation
    '''
    #TODO - Data checks
    n_samples = len(labels)
    dlabels = {}
    ix = 0
    for label in labels:
        if label not in dlabels:
            dlabels[label] = ix
            ix += 1
    n_dims = len(dlabels)
    arr = np.zeros((n_samples, n_dims))
    for i in xrange(n_samples):
        arr[i, dlabels[labels[i]]] = 1.0
    return arr, dlabels


#!/usr/bin/env python

from sympy import Function, lambdify, diff, hessian, pprint
from sympy.abc import x, y

''' Hessian matrix
    Compute Hessian matrix
    for a function f wrt parameters in varlist
    which may be given as a sequence or a row/column vector.
    A list of constraints may optionally be given.

    References:  https://en.wikipedia.org/wiki/Hessian_matrix
        http://people.duke.edu/~ccc14/sta-663-2016/12_MultivariateOptimizationAlgorithms.html
        https://www.youtube.com/watch?v=tWh1S8oCglA
        https://www.youtube.com/watch?v=H4rwPpfkPHw
'''

def hessian_inverse(function):
    '''  Hessian: init and set  '''
    h = (hessian(function, (x, y))).inv()
    return (lambdify([x,y], h))

if __name__ == "__main__" :

    # f = 2*x - y + 2*x*y - x**2 - y**2
    f = x**2 * y**3

    h = hessian_inverse(f)
    print(h(5, 2))

    # pprint((hessian(f, (x, y))).inv())


#! /usr/bin/env python
"""
@author : Luc ardaillon
created : 17/01/2019
last revision : 10/04/2019

conversion from the ground truth f0 value to the target vector of pitch classes to be predicted by the models (for training)
"""

import numpy as np

def freq2cents(f0, f_ref = 10.):
    '''
    Convert a given frequency into its corresponding cents value, according to given reference frequency f_ref
    :param f0: f0 value (in Hz)
    :param f_ref: reference frequency for conversion to cents (in Hz)
    :return: value in cents
    '''
    c = 1200 * np.log2(f0/f_ref)
    return c

def cents2freq(cents, f_ref = 10.):
    '''
    conversion from cents value to f0 in Hz

    :param cents: pitch value in cents
    :param fref: reference frequency used for conversion
    :return: f0 value
    '''
    f0 = f_ref * 2 ** (cents / 1200)
    return f0

def f0_to_target_vector(f0, vecSize = 486, fmin = 30., fmax = 1000., returnFreqs = False):
    '''
    convert from target f0 value to target vector of vecSize pitch classes (corresponding to the values in cents_mapping) that is used as output by the CREPE model
    Unlike the original CREPE model, the first class corresponds to a frequency of 0 (for unvoiced segments).
    If the frequency is 0, all values are 0, except for the 1st value that is = 1.
    For all other cases, the values are gaussian blurred around the target_pitch class, with a maximum value of 1
    :param f0: target f0 value
    :return: target vector of vecSize pitch classes (regularly spaced in cents, from fmin to fmax)
    '''

    fmin_cents = freq2cents(fmin)
    fmax_cents = freq2cents(fmax)
    mapping_cents = np.linspace(fmin_cents, fmax_cents, vecSize)

    target_vec = np.zeros(vecSize)

    #get the idx corresponding to the closest pitch
    f0_cents = freq2cents(f0)

    #gaussian-blur the vector auround the taget pitch idx as stated in the paper :
    sigma = 25
    for i in np.arange(vecSize):
        target_vec[i] = np.exp(-((mapping_cents[i] - f0_cents)**2)/(2*(sigma**2)))

    if(returnFreqs):
        return target_vec, mapping_cents
    else:
        return target_vec

def f0_to_target_vector_crepe(f0):
    '''
    convert from target f0 value to target vector of 360 pitch classes (corresponding to the values in cents_mapping) that is used as output by the CREPE model
    Unlike the original CREPE model, the first class corresponds to a frequency of 0 (for unvoiced segments).
    If the frequency is 0, all values are 0, except for the 1st value that is = 1.
    For all other cases, the values are gaussian blurred around the target_pitch class, with a maximum value of 1
    :param f0: target f0 value
    :return: target vector of 360 pitch classes (1st class is for f0=0. All other correspond to frequencies spaced by 20 cents
    '''

    vecSize = 360
    mapping = (np.linspace(0, 7180, vecSize) + 1997.3794084376191)

    target_vec = np.zeros(vecSize)

    #get the idx corresponding to the closest pitch
    f0_cents = freq2cents(f0)
    sigma = 25

    #gaussian-blur the vector auround the taget pitch idx as stated in the paper :
    for i in np.arange(vecSize):
        target_vec[i] = np.exp(-((mapping[i] - f0_cents)**2)/(2*(sigma**2)))

    return target_vec

if __name__ == '__main__':

    f0 = 100.

    CREPE = False

    if(CREPE):
        tv = f0_to_target_vector_crepe(f0)
    else:
        tv = f0_to_target_vector(f0)

    from matplotlib import pyplot as plt
    plt.figure()
    plt.plot(tv)
    plt.show()


"""ЛР 1.4, Ларькин Владимир, М8О-303Б-18"""

import numpy as np
import fire  # CLI
import matplotlib.pyplot as plt

from utilities import parse_matrix  # парсинг матрицы из файла

def max_el_indices(matrix):
    i_max = 0
    j_max = 1
    a_max = matrix[i_max, j_max]
    for i in range(matrix.shape[0]):
        for j in range(i + 1, matrix.shape[0]):
            if abs(matrix[i, j]) > a_max:
                a_max = abs(matrix[i, j])
                i_max, j_max = i, j
    return i_max, j_max

def calculate_phi(a_ii, a_ij, a_jj):
    return np.pi / 4 if a_ii == a_jj else 0.5 * np.arctan(2 * a_ij / (a_ii - a_jj))

def t(matrix: np.ndarray) -> float:
    """Корень из суммы квадратов недиагональных элементов"""
    return np.sqrt((np.triu(matrix, k=1) ** 2).sum() + (np.tril(matrix, k=-1) ** 2).sum())

def rotation_eig(matrix: np.ndarray, eps: float = .01, benchmark=False) -> (np.ndarray, np.ndarray):
    """Метод вращений Якоби для нахождения собственных значений и собственных векторов симметрической матрицы

    :param matrix: матрица
    :param eps: точность
    :param benchmark: возвращать ли количество итераций и погрешность
    :return: собственные значения и вектора, а если benchmark=True, то ещё количество итераций и погрешность
    """
    a_i = np.copy(matrix)
    n = a_i.shape[0]
    eigenvectors = np.identity(n)

    c = 0
    while True:
        u = np.identity(n)
        i, j = max_el_indices(a_i)
        phi = calculate_phi(a_i[i, i], a_i[i, j], a_i[j, j])
        u[i, j] = -np.sin(phi)
        u[j, i] = np.sin(phi)
        u[i, i] = u[j, j] = np.cos(phi)

        a_i = u.T @ a_i @ u

        eigenvectors = eigenvectors @ u

        if t(a_i) < eps:
            break

        c += 1

    eigenvalues = np.diag(a_i)

    return (eigenvalues, eigenvectors) if not benchmark else (eigenvalues, eigenvectors, c, t(a_i))

def main(src, test=False, eps=0.01):
    """Нахождение собственных значений и собственных векторов симметрической матрицы методом вращений Якоби

    :param src: путь к текстовому файлу с матрицей
    :param test: флаг, запускающий тестирование
    :param eps: точность вычисления
    """

    np.random.seed(42)

    # чтение файла
    with open(src, "r") as file:
        s = file.readlines()

    matrix = parse_matrix(s)

    print(f"Матрица:\n{matrix}")
    print(f"\neps={eps}")

    print("\nnumpy.linalg.eig:", *np.linalg.eig(matrix), sep="\n")
    print("\nrotation_eig:", *rotation_eig(matrix, eps), sep="\n")

    if test:
        powers = np.arange(0, 15, .1)  # порядки эпсилонов (eps = 10 ** -p)
        counts = []
        errors = []
        for p in powers:
            temp = rotation_eig(matrix, 10 ** -p, benchmark=True)
            counts.append(temp[2])
            errors.append(temp[3])

        plt.plot(counts, errors, "-*")
        plt.title("Зависимость погрешности вычислений от числа итераций")
        plt.xlabel("Число итераций")
        plt.ylabel("Погрешность (лог. шкала)")
        plt.grid(True)
        plt.yticks(np.arange(0, max(errors) + .05, .05))
        plt.yscale("log")
        plt.savefig("benchmark.jpg", dpi=300)
        plt.show()

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


import numpy as np
import matplotlib.pyplot as plt

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

def generate_graph(x, save_graph=False):
    plt.plot(x,tanh(x))
    if save_graph:
        plt.savefig("tanh.jpg")
    else:
        plt.show()

def main():
    print("Generating graph")
    save_graph = False # save the graph (true) or show (false)
    generate_graph(np.linspace(-10,10,100),save_graph=save_graph)

if __name__ == '__main__':
    main()


"""
Utility functions for curvature computations

Author: Santiago Cadena
Date:   11.19
email:  santiago.cadena@uni-tuebingen.de
"""
import numpy as np
from numpy.linalg import norm

def consecutive_difference(x):
    '''
    Computes the difference of consecutive elements along the first axis
    '''
    return np.diff(x, n=1, axis=0)

def normalize_arrays(x):
    '''
    Normalize each array along the first axis
    '''
    return np.array([a / norm(a.flatten()) for a in x])

def compute_curvature(x, deg=True):
    """
    Compute curvature by computing arccosine of consecutive normalized
    difference vectors [Hennaff et. al 2019]

    Args:
        x    : array with at least two dims.
        deg  : bool  True (default) for curvature output in degrees. Otherwise in rads.
    Returns:
        curvature : array with (1xM) with the curvature trajectory
    """

    x_diff   = consecutive_difference(x)
    x_diff_n = normalize_arrays(x_diff)

    # Flatten arrays along the first axis
    T      = x_diff_n.shape[0]
    x_flat = x_diff_n.reshape(T, -1)

    # shifted arrays
    x0 = x_flat[:-1,]
    x1 = x_flat[1:,]

    cosine    = np.diag(np.dot(x0, x1.T)) # dot product consecutive vectors
    cosine    = [c if c <= 1 else 1 for c in cosine] # sanity check to clip values at 1

    # curvature computation
    curvature = np.arccos(cosine)

    if deg:
        curvature = np.degrees(curvature)

    return curvature


import numpy
import itertools

def get_sieve(size=1000):
    arr = numpy.full(size, True, dtype=bool)
    arr[0] = arr[1] = False
    # Eliminate even numbers (except 2)
    for i in xrange(4, size, 2):
        arr[i] = False

    for i in xrange(2, size):
        if arr[i]:  # is prime
            for j in xrange(i * i, size, i):
                arr[j] = False

    return arr

if __name__ == '__main__':
    # https://brilliant.org/weekly-problems/2017-12-25/intermediate/?p=3
    # Are there any three distinct digits such that all six permutations are prime numbers?
    size = 1000
    sieve = get_sieve(size=size)
    primes = [x[0] for x in zip(range(size), sieve) if x[1]]
    print "primes:\n%s" % (primes,)
    digits = (1, 3, 7, 9)  # no even digits and 5
    three_digit_permutation_primes_exist = False
    for abc in itertools.combinations(digits, 3):
        is_prime = True
        for a1, b1, c1 in itertools.permutations(abc, 3):
            x = a1 * 100 + b1 * 10 + c1
            if not sieve[x]:
                print "not prime %s, counterexample: %s" % (abc, x)
                is_prime = False
                break
        if is_prime:
            print "prime: %s" % (abc,)
            three_digit_permutation_primes_exist = True
    if three_digit_permutation_primes_exist:
        print "Yes"
    else:
        print "No"
    # For combination with repetition the answer would be yes with:
    # (1, 1, 3), (1, 9, 9) and (3, 3, 7)


import numpy as np

def getminor(mat,temp,n,p,q):
  i,j = 0,0
  for row in range(n):
    for col in range(n):
      if (row!=p and col!=q):
        temp[i][j] = mat[row][col]
        j += 1
        if (j==n-1):
          j = 0
          i = i+1
  return temp

def determinant(mat,n):
  D = 0
  sign = 1
  temp = [[0,0,0,0],
          [0,0,0,0],
          [0,0,0,0],
          [0,0,0,0]]
  if (n==1):
    return mat[0][0]
  for f in range(n):
    temp = getminor(mat,temp,n,0,f)
    D += sign*mat[0][f]*determinant(temp,n-1)
    sign = -sign
  return D

N = 4
matrix = [[1, 0, 2, -1],
          [3, 0, 0, 5 ],
          [2, 1, 4, -3],
          [1, 0, 5, 0 ]]
matrix = np.array(matrix)
print("Determinant of the matrix is : {}".format(determinant(matrix, N)))


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize']= (20.0,10.0)

#reading data
data=pd.read_csv("headbrain.csv")
print(data.shape)
data.head()

#Callecting X & Y
X =data["Head Size(cm^3)"].values
Y =data["Brain Weight(grams)"].values

# Mean X & Y
MeanX = np.mean(X)
MeanY = np.mean(Y)

#total number of values
n= len(X)

# using the formula to calculate B1 and B0
numer = 0
denom  = 0
for i in range(n):
    numer +=(X[i]-MeanX)*(Y[i]-MeanY)
    denom +=(X[i]-MeanX)**2
B1 = numer /denom
B0 = MeanY - (B1 * MeanX)

#print Cofficients
print(B1, B0)

#plotting Values and Regression Line
MaxX= np.max(X)+100
MinX=np.min(X)-100

#Calculating Line values X and Y
x = np.linspace(MinX,MaxX,1000)
y =B0+B1*x

#ploting line
plt.plot(x,y,color='#58b970',label="Regression")
#ploting scatter points
plt.scatter(X,Y,color="#ef5423", label="Scatter Plot")

plt.xlabel("Head Size in Cm3")
plt.ylabel("Brain Weght in grams")
plt.legend()
plt.show()

ss_t = 0
ss_r = 0
for i in range(n):
    y_pred = B0 +B1 * X[i]
    ss_t += (Y[i] - MeanY) **2
    ss_r += (Y[i] - y_pred) **2
r2 = 1- (ss_r/ss_t)
print(r2)

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

#Cannot use Rank 1 matrix in scikit learn
X= X.reshape((n,1))
#creating Model
reg=LinearRegression()
#fitting training data
reg = reg.fit(X,Y)
#Y prediction
y_pred = reg.predict(X)
#calculating R2 Score
R2_score=reg.score(X,Y)
print(R2_score)


import numpy as np
# from sklearn.metrics.pairwise import euclidean_distances

D = 2
S = 1000
K = 3
max_iterations = 10

# 1000x2
data = np.random.rand(S, D)

# 3x2
centroids = np.random.rand(K, D)

def euclidean_distances(data, centroids):
    return np.array([np.sum((d - centroids)**2, 1)
        for d in data])

def calc_error(data, centroids, labels):
    total = 0
    for c in range(centroids.shape[0]):
        assigned = data[np.where(labels == c)]
        if assigned.shape[0] > 0:
            total += np.sum(euclidean_distances(assigned, centroids[c].reshape((1, D))))
    return total

# # 1000x3
for iterations in range(max_iterations):
    # result = np.dot(data, centroids.T)
    distances = euclidean_distances(data, centroids)

    # distances is: [SxK], per S_i find min K
    labels = np.argmin(distances, 1)

    print calc_error(data, centroids, labels)
    print centroids

    for c in range(centroids.shape[0]):
        assigned = data[np.where(labels == c)]
        if assigned.shape[0] > 0:
            centroids[c] = np.mean(assigned)


"""
Uses the Python interface to PETSc (petsc4py) to solve the transient 1D heat
diffusion with Dirichlet Boundary Conditions. This is a super simple example
showcase of the linear iterative solvers PETSc has to offer. It runs
sequentially without MPI.

Consider the 1D heat equation defining how a temperature is distributed e.g.
over a one-dimensional rod.

    ∂u/∂t = ∂²u/∂x²

with Dirichlet Boundary Conditions at both ends of a unit domain

    u(t, x=0) = 0 = u(t, x=1)

and the following initial condition

         ┌────────────────────────────────────────┐
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠒⠒⠒⠒⠒⠒⢲⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ │ y1
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡸⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀│
         │⢼⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠧⠤⠤⠤⠤⠤⠤⠼⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤│
         └────────────────────────────────────────┘
          0.0                                   1.0

-----

Solution strategy (BTCS):

Denote with û the solution at the next point in time. Then use
Finite Difference discretization on a uniform mesh. Discretize
the time derivative by implicit Euler and the second spatial
derivative by central differences.

    (û[i] − u[i])/Δt = (û[i−1] − 2 û[i] + û[i+1])/Δx²

with the conditions on the Dirichlet Boundary points

    û[0] = 0.0

    û[-1] = 0.0

Move all points at next time step to the lhs and all terms at
previous time step to rhs.

    û[i] + Δt/Δx² (− û[i−1] + 2 û[i] − û[i+1]) = u[i]

This can be expressed as a linear system of equations

    A x = b

with A being

    +-----                                                ------+
    |   1.0                                                     |
    | −Δt/Δx²  1 + 2Δt/Δx²  −Δt/Δx²                             |
    |           −Δt/Δx²  1 + 2Δt/Δx²  −Δt/Δx²
A = |                       −Δt/Δx²  1 + 2Δt/Δx²  −Δt/Δx²
    |                           ⋱         ⋱         ⋱
    |                             ⋱         ⋱         ⋱
    |                            −Δt/Δx²  1 + 2Δt/Δx²  −Δt/Δx²  |
    |                                                    1.0    |
    +-----                                                ------+

A therefore has the following sparsity structure

    +---------------+
    |*              |
    |* * *          |
    |  * * *        |
A = |    * * *      |
    |      * * *    |
    |        * * *  |
    |          * * *|
    |              *|
    +---------------+

which is not fully tri-diagonal, because we did not eliminate the
Dirichlet DoF. Hence, it is also not symmetric.

-----

The PETSc linear solver can be set at runtime.

Use this command to monitor the solution process:

    python petsc_python_simple_heat_diffusion.py -ksp_monitor

Use this flag to swap the linear solver

    python petsc_python_simple_heat_diffusion.py -ksp_type [SOLVER_NAME]

"""
import petsc4py
import sys
import numpy as np
import matplotlib.pyplot as plt

petsc4py.init(sys.argv)

from petsc4py import PETSc

N_POINTS = 1001
TIME_STEP_LENGTH = 0.005
N_TIME_STEPS = 10

def main():
    element_length = 1.0 / (N_POINTS - 1)
    mesh = np.linspace(0.0, 1.0, N_POINTS)

    # Create a new sparse PETSc matrix, fill it and then assemble it
    A = PETSc.Mat().createAIJ([N_POINTS, N_POINTS])
    A.setUp()

    diagonal_entry = 1.0 + 2.0 * TIME_STEP_LENGTH / element_length**2
    off_diagonal_entry = - 1.0 * TIME_STEP_LENGTH / element_length**2

    A.setValue(0, 0, 1.0)
    A.setValue(N_POINTS-1, N_POINTS-1, 1.0)

    for i in range(1, N_POINTS - 1):
        A.setValue(i, i, diagonal_entry)
        A.setValue(i, i-1, off_diagonal_entry)
        A.setValue(i, i+1, off_diagonal_entry)

    A.assemble()

    # Define the initial condition
    initial_condition = np.where(
        (mesh > 0.3) & (mesh < 0.5),
        1.0,
        0.0,
    )

    # Assemble the initial rhs to the linear system
    b = PETSc.Vec().createSeq(N_POINTS)
    b.setArray(initial_condition)
    b.setValue(0, 0.0)
    b.setValue(N_POINTS-1, 0.0)

    # Allocate a PETSc vector storing the solution to the linear system
    x = PETSc.Vec().createSeq(N_POINTS)

    # Instantiate a linear solver: Krylow subspace linear iterative solver
    ksp = PETSc.KSP().create()
    ksp.setOperators(A)
    ksp.setFromOptions()

    chosen_solver = ksp.getType()
    print(f"Solving with {chosen_solver:}")

    plt.plot(mesh, initial_condition)
    for iter in range(N_TIME_STEPS):
        ksp.solve(b, x)

        # Re-assemble the rhs to move forward in time
        current_solution = x.getArray()
        b.setArray(current_solution)
        b.setValue(0, 0.0)
        b.setValue(N_POINTS - 1, 0.0)

        # Visualize
        plt.plot(mesh, current_solution)
        plt.draw()
        plt.pause(0.5)

    plt.show()

if __name__ == "__main__":
    main()


import numpy as np
from scipy.special import gamma
import pandas as pd
from sklearn.metrics import mutual_info_score
from typing import Union

# def n_ball_volume(dim, norm):
#     if norm == 1:
#         return 2**dim/np.math.factorial(dim)
#     elif norm == 2:
#         return np.pi**(dim/2.) / (gamma(dim/2. + 1))
#     elif norm == float('inf') or norm == "inf":
#         return 2 ** dim

def n_ball_volume(dim, norm_p):
    if norm_p == 1:
        from scipy.special import factorial
        return 2.**dim / factorial(dim)
    elif norm_p == 2:
        return (np.sqrt(np.pi))**dim / gamma(dim/2 + 1)
    elif norm_p == float('inf') or norm_p == "inf":
        return 2 ** dim
    elif 0 < norm_p < float('inf'):
        return (2 * gamma(1 / norm_p + 1))**dim / gamma(dim/norm_p + 1)

def n_sphere_area(dim, norm):
    if norm == 1:
        return 2 ** dim / np.math.factorial(dim - 1)
    elif norm == 2:
        return 2 * np.pi ** ((dim + 1) / 2.) / (gamma((dim + 1) / 2.))
    elif norm == float('inf') or norm == "inf":
        return dim * 2 ** dim

def nd_rand_init(*tuples_lo_hi):
    return np.random.uniform(low=[t[0] for t in tuples_lo_hi], high=[t[1] for t in tuples_lo_hi])

def make_uniform_kernel(dim, norm_p):
    return lambda u: 1./n_ball_volume(dim, norm_p) if u <= 1 else 0.

def mutual_information(x, y, size_orig, n_bins="smooth") -> float:
    assert(x.size > 0 and y.size > 0)
    assert(n_bins in [None, 'smooth'] or isinstance(n_bins, int))
    if n_bins is None:
        n_bins = int(np.floor(np.sqrt(size_orig / 5)))

    elif n_bins is 'smooth':
        n_bins = int(np.floor(np.sqrt(size_orig / 3)))

    c_xy = np.histogram2d(x, y, n_bins)[0]
    return mutual_info_score(None, None, contingency=c_xy)

def lagged_mi(x, lag=1, *args, **kwargs) -> float:
    assert(lag >= 0)
    if lag > 0:
        return mutual_information(x[:-lag], x[lag:], x.shape[0], *args, **kwargs)
    else:
        return mutual_information(x, x, x.shape[0], *args, **kwargs)

def _lstsqr_design_matrix(x, degree=1):
    if degree > 1:
        raise NotImplementedError()
    return np.concatenate([x[:,None], np.ones((x.shape[0], 1))], axis=1)

def lstsqr(X, y):
    Xt = np.transpose(X)
    return np.linalg.pinv(Xt.dot(X)).dot(Xt).dot(y)

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


import numpy as np
import pandas as pd
from scipy.spatial import distance

def find_cluster(data_frame, data_frame_cluster):
    cluster = {}
    for i, center in enumerate(data_frame_cluster.values):
        cluster[i] = []

    for j, point in enumerate(data_frame.values):
        euclDist = float('inf')
        euclCenter = 0
        for i, center in enumerate(data_frame_cluster.values):
            dist = distance.euclidean(point, center)
            if dist < euclDist:
                euclDist = dist
                euclCenter = i

        # cluster[euclCenter] = []
        if cluster[euclCenter]:
            cluster[euclCenter].append(point)
        else:
            cluster[euclCenter] = [point]
    # print(cluster)
    return cluster

def mykmeans(X, k):
    # DONE create k random centroid from dataset.
    # TODO count euclidean distance from each centroid.
    # TODO we find the new centroid by taking the average of all the points assigned to that cluster.
    # TODO we repeat step 2 and 3 until none of the cluster assignments change. That means until our clusters remain stable, we repeat the algorithm
    try:
        data_frame = pd.DataFrame(data=X)
        # data_frame = data_frame
        data_frame_cluster = data_frame.sample(n=k)
        # print(data_frame_cluster)

        prev_centers = []
        while True:
            # Group data in clusters
            cluster = find_cluster(data_frame, data_frame_cluster)

            # Calculate new centroid
            centers = []
            for clusterKey, clusterValue in cluster.items():
                df = pd.DataFrame(clusterValue)
                center = []
                for column in df:
                    center.append(df[column].mean())
                centers.append(center)

            # Breaking condition, if prev centers and current centers are same
            if prev_centers == centers:
                break
            data_frame_cluster = pd.DataFrame(centers)
            prev_centers = centers
        print("For k = " + str(k) + " centers are: ")
        print(centers)
    except Exception as e:
        print(e)


"""
## =========================================================================== ##
MIT License
Copyright (c) 2020 Roman Parak
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## =========================================================================== ##
Author   : Roman Parak
Email    : Roman.Parak@outlook.com
Github   : https://github.com/rparak
File Name: bezier_curve_example.py
## =========================================================================== ##
"""

# System (Default Lib.)
import sys
# Numpy (Array computing Lib.) [pip3 install numpy]
import numpy as np
# Mtaplotlib (Visualization Lib.) [pip3 install matplotlib]
import matplotlib.pyplot as plt

class bezier_ctrl(object):
    """
    Description:
        A Bézier curve is a parametric curve used in computer graphics and related fields.

        The class shows several types of Bézier curves (Linear, Quadratic, Cubic).
    """
    def __init__(self, p_0, p_1, p_2, p_3, step):
        # << PUBLIC >> #
        # Time t ∈ [0, 1]
        self.t = np.linspace(0.0, 1.0, step)
        # Points
        self.p = [p_0, p_1, p_2, p_3]
        # Remove empty (None) parts of the array.
        self.p = [x for x in self.p if x is not None]
        # << PRIVATE >> #
        # Display (Plot) variable.
        self.__plt = plt

    @staticmethod
    def __linear_curve(p_0, p_1, t):
        """
        Description:
            Given two control points p_{0} and p_{1} we define the linear Bezier curve to be the curve parametrized by:

            p(t) = (1 - t)*p_{0} + t*p_{1}, t ∈ [0, 1]

        Args:
            (1 - 2) p_0, p_0 [Float Array]: Multiple points to create a curve.
            (3) t [Float Array]: Time variable.
        Returns:
            (1 - 2) x, y [Float Array]: Results of curve values.

        Examples:
            self.__linear_curve([1.0, 1.0], [2.0, 2.0])
        """

        x = (1 - t) * p_0[0] + t * p_1[0]
        y = (1 - t) * p_0[1] + t * p_1[1]

        return x, y

    @staticmethod
    def __quadratic_curve(p_0, p_1, p_2, t):
        """
        Description:
            Given three control points p_{0}, p_{1} and p_{2} we define the quadratic Bezier curve (degree 2 Bezier curve)
            to be the curve parametrized by:

            p(t) = ((1 - t)^2)*p_{0} + 2*t*(1 - t)*p_{1} + (t^2)*p_{2}, t ∈ [0, 1]

        Args:
            (1 - 2) p_0, p_0, p_2 [Float Array]: Multiple points to create a curve.
            (3) t [Float Array]: Time variable.
        Returns:
            (1 - 2) x, y [Float Array]: Results of curve values.

        Examples:
            self.__quadratic_curve([1.0, 1.0], [2.0, 2.0], [3.0, 2.0])
        """

        x = (1 - t)**2 * p_0[0] + 2 * t * (1 - t) * p_1[0] + t**2 * p_2[0]
        y = (1 - t)**2 * p_0[1] + 2 * t * (1 - t) * p_1[1] + t**2 * p_2[1]

        return x, y

    @staticmethod
    def __cubic_curve(p_0, p_1, p_2, p_3, t):
        """
        Description:
            Given four control points p_{0}, p_{1}, p_{2} and p_{3} we define the cubic Bezier curve (degree 3 Bezier curve) to
            be the curve parametrized by:

            p(t) = ((1 - t)^3)*p_{0} + 3*t*((1 - t)^2)*p_{1} + (3*t^2)*(1 - t)*p_{2} + (t^3) * p_{3}, t ∈ [0, 1]

        Args:
            (1 - 2) p_0, p_0, p_2 [Float Array]: Multiple points to create a curve.
            (3) t [Float Array]: Time variable.
        Returns:
            (1 - 2) parameter{1}, parameter{2} [Float Array]: Results of curve values.

        Examples:
            self.__cubic_curve([1.0, 1.0], [2.0, 2.0], [3.0, 2.0], [4.0, 1.0])
        """

        x = ((1 - t)**3) * (p_0[0]) + (3 * t * (1 - t)**2) * (p_1[0]) + 3 * (t**2) * (1 - t) * p_2[0] + (t**3) * p_3[0]
        y = ((1 - t)**3) * (p_0[1]) + (3 * t * (1 - t)**2) * (p_1[1]) + 3 * (t**2) * (1 - t) * p_2[1] + (t**3) * p_3[1]

        return x, y

    def __switch_dCtrl(self, s_index):
        """
        Description:
            Function to obtain a string with the number of points used in the calculation of the curve. (Figure Legend -> Label Name)

        Args:
            (1) s_index [INT]: Number of points for calculation.
        Returns:
            (1) param 1 [String]: The resulting string for the label.
        """

        # Switch Variable
        switch_var={
                2: r'Points: $p_{0}, p_{1}$',
                3: r'Points: $p_{0}, p_{1}, p_{2}$',
                4: r'Points: $p_{0}, p_{1}, p_{2}, p_{3}$',
        }

        # Return Result (Get the string with number of points for the Legend Label)
        return switch_var.get(s_index, "Wrong Input!")

    def __two_points(self):
        """
        Description:
            Function to create a multiple linear Bézier curve from two points.
        """

        x, y = self.__linear_curve(self.p[0], self.p[1], self.t)

        # Display the Linear Bézier Curve p(t) -> x, y
        self.__plt.plot(x, y, 'r--', label=r'Linear Bezier Curve: [$p_{0}$, $p_{1}$]' , linewidth=2.5)

    def __three_points(self):
        """
        Description:
            Function to create multiple linear Bézier curves and a quadratic Bézier curve from three points.
        """

        for i in range(len(self.p) - 1):
            x, y = self.__linear_curve(self.p[i], self.p[i + 1], self.t)

            if i == (len(self.p) - 1) - 1:
                self.__plt.plot(x, y, 'r--', label=r'Linear Bezier Curve: [$p_{0}$, $p_{1}$]; [$p_{1}$, $p_{2}$]' , linewidth=2.5)
            else:
                self.__plt.plot(x, y, 'r--', linewidth=2.5)

        x, y = self.__quadratic_curve(self.p[0], self.p[1], self.p[2], self.t)

        self.__plt.plot(x, y, 'g--', label=r'Quadratic Bezier Curve: [$p_{0}$, $p_{1}$, $p_{2}$]', linewidth=2.5)

    def __four_points(self):
        """
        Description:
            Function to create multiple linear / quadratic Bézier curves and a cubic four-point Bézier curve.
        """

        for i in range(len(self.p) - 1):
            x, y = self.__linear_curve(self.p[i], self.p[i + 1], self.t)

            if i == (len(self.p) - 1) - 1:
                self.__plt.plot(x, y, 'r--', label=r'Linear Bezier Curve: [$p_{0}$, $p_{1}$]; [$p_{1}$, $p_{2}$]; [$p_{2}$, $p_{3}$]' , linewidth=2.5)
            else:
                self.__plt.plot(x, y, 'r--', linewidth=2.5)

        for i in range(len(self.p) - 2):
            x, y = self.__quadratic_curve(self.p[i], self.p[i + 1], self.p[i + 2], self.t)

            if i == (len(self.p) - 2) - 1:
                self.__plt.plot(x, y, 'g--', label=r'Quadratic Bezier Curve: [$p_{0}$, $p_{1}$, $p_{2}$]; [$p_{1}$, $p_{2}$, $p_{3}$]', linewidth=2.5)
            else:
                self.__plt.plot(x, y, 'g--', linewidth=2.5)

        x, y = self.__cubic_curve(self.p[0], self.p[1], self.p[2], self.p[3], self.t)

        self.__plt.plot(x, y, 'b--', label=r'Cubic Bezier Curve: [$p_{0}$, $p_{1}$, $p_{2}$, $p_{3}$]', linewidth=2.5)

    def __display_aux_result(self):
        """
        Description:
            Function for displaying points and text labeling.
        """

        for i in range(len(self.p)):
            self.__plt.text(self.p[i][0] + 0.01, self.p[i][1] + 0.01, '$p_{' + str(i) + '}$ = [' + str(self.p[i][0]) + ', ' + str(self.p[i][1]) + ']', fontsize=20)

            if i != len(self.p) - 1:
                self.__plt.plot(self.p[i][0], self.p[i][1], marker = 'o', ms = 15, mfc = [1,1,1], markeredgecolor = [0,0,0], mew = 5)
            else:
                self.__plt.plot(self.p[i][0], self.p[i][1], label=self.__switch_dCtrl(len(self.p)), marker = 'o', ms = 15, mfc = [1,1,1], markeredgecolor = [0,0,0], mew = 5)

    def display_result(self):
        """
        Description:
            Function for calculating and displaying the results of Bézier curves.
        """

        try:
            assert len(self.p) > 1

            # Select a calculation method based on the number of points in the array (p).
            if len(self.p) == 2:
                self.__two_points()
            elif len(self.p) == 3:
                self.__three_points()
            elif len(self.p) == 4:
                self.__four_points()

            self.__display_aux_result()

            # Set additional features for successful display of the Bézier curves.
            self.__plt.grid()
            self.__plt.xlabel('x axis [Unit]', fontsize = 20, fontweight ='normal')
            self.__plt.ylabel('y axis [Unit]', fontsize = 20, fontweight ='normal')
            self.__plt.title('Bezier Curve', fontsize = 50, fontweight ='normal')
            self.__plt.legend(loc=0,fontsize=20)

            # Display a figure. Wait for the user to close the window.
            self.__plt.show()

        except AssertionError as error:
            print('[INFO] Insufficient number of entry points.')
            print('[INFO] The minimum number of entry points is 2.')

def main():
    # Initialization of the Class (Control Manipulator)
    # Input:
    #   (1 - 4) Points [Float Array]
    #   (5) Time Step  [INT]
    # Example:
    #   x = bezier_ctrl([1.0, 1.0], [1.25, 2.0], None, None, 100)

    # Try the calculation of the Bézier curve:
    # Select one of these options: 2 - Linear Curve, 3 - Quadratic Curve, 4 - Cubic Curve
    test = 4

    if test == 2:
        # Linear Curve
        bezier = bezier_ctrl([1.0, 1.0], [1.25, 2.0], None, None, 100)
    elif test == 3:
        # Quadratic Curve
        bezier = bezier_ctrl([1.0, 1.0], [1.25, 2.0], [1.75, 2.0], None, 100)
    elif test == 4:
        # Cubic Curve
        bezier = bezier_ctrl([1.0, 1.0], [1.25, 2.0], [1.75, 2.0], [2.0, 1.0], 100)

    # Display the result of the calculation -> figure with the resulting Bézier curves
    bezier.display_result()

if __name__ == '__main__':
    sys.exit(main())


import numpy as np

def lsq(X, y):
    """
    Least squares linear regression
    :param X: Input data matrix
    :param y: Target vector
    :return: Estimated coefficient vector for the linear regression
    """

    # add column of ones for the intercept
    ones = np.ones((len(X), 1))
    X = np.concatenate((ones, X), axis=1)

    # calculate the coefficients
    beta = np.dot(np.linalg.inv(np.dot(X.T, X)), np.dot(X.T, y))

    return beta

def fit_to_model(beta, X):
    """
    Applies the result of the least squares linear regression to the data.

    Parameters
    ----------
    beta : TYPE np.array
        Contains the linear model coefficients.
    X : TYPE np.array
        Contains dataset features (column with ones not included).

    Returns
    -------
    None.

    """

    # add column of ones for the intercept
    ones = np.ones((len(X), 1))
    X = np.concatenate((ones, X), axis=1)

    # apply the coefficients to the data
    y = np.dot(X, beta)
    return y

def mean_squared_error(y_true, y_pred):
    """
    Calculates the mean squared error from the true target values and the
    predicted target values.

    Parameters
    ----------
    y_true : TYPE np.array
        Contains the true targets.
    y_pred : TYPE np.array
        Contains the predicted targets.

    Returns
    -------
    MSE : TYPE float
        Returns the mean squared error value.

    """

    # check if shapes of the arrays match
    if y_true.shape != y_pred.shape:
        print('y_true and y_pred should have the same shape!')
        return None

    # calculate MSE
    MSE = np.sum((y_true-y_pred)**2);
    MSE /= len(y_true)
    return MSE

def wlsq(X, y, d):
    """
    Least squares linear regression
    :param X: Input data matrix
    :param y: Target vector
    :param d: Weight vector
    :return: Estimated coefficient vector for the linear regression
    """

    # add column of ones for the intercept
    ones = np.ones((len(X), 1))
    X = np.concatenate((ones, X), axis=1)

    # normalize weights
    #d = (1/np.max(d))*np.array(d)

    # calculate the coefficients
    xw = X.T * d
    beta = np.dot(np.linalg.inv(np.dot(xw, X)), np.dot(xw, y))

    return beta

def normalize(X):
    """
    Normalizes the feature array. The normalization step is done per feature.

    Parameters
    ----------
    X : TYPE np.array
        Contains the features of the dataset.

    Returns
    -------
    X : TYPE np.array
        Contains the normalized features of the dataset.
    """

    # loop over every feature independently
    for i in range(X.shape[1]):

        # for every value, subtract by the average value of that feature and
        # divide by the standard deviation.
        X[:,i] = (X[:,i] - np.mean(X[:,i])) / np.std(X[:,i]);

    return X

def kNN(X_train, y_train, X_test, k=5, mode='classification'):
    """
    Applies the k-Nearest Neighbor method to a dataset

    Parameters
    ----------
    X_train : TYPE np.array
        Contains the features of the training dataset.
    y_train : TYPE np.array
        Contains the targets of the training dataset.
    X_test : TYPE np.array
        Contains the features of the test dataset.
    k : TYPE int, optional
        The number of considered 'nearest neighbors'. The default is 5.
    mode : TYPE string, optional
        Choose between 'classification' and 'regression' mode. Determines the
        result of this function. The default is 'classification'.

    Returns
    -------
    y_test : TYPE
        Predicted targets of the test dataset.

    """

    # initialize y_test array
    if mode == 'classification':
        y_test = np.zeros((X_test.shape[0], 1))
    elif mode == 'regression':
        y_test = np.zeros((X_test.shape[0], y_train.shape[1]))
    else:
        print("False parameter for 'mode'; should be either 'classification' or 'regression'.")
        return None

    for i in range(X_test.shape[0]):
        # calculate distance between test object and all train objects
        distances = np.linalg.norm(X_train - X_test[i], axis=1)

        # retrieve the indices of the closest neighbors
        ids_of_NN = distances.argsort()[:k]

        # retrieve the targets of the closest 5 neighbors
        NN_targets = y_train[ids_of_NN]

        if mode == 'classification':
            # find the most occuring target and add this to y_test
            new_target = np.bincount(NN_targets.T[0]).argmax()
            y_test[i] = new_target

        elif mode == 'regression':
            # find the average of the training targets and add to y_test
            y_test[i,:] = np.mean(NN_targets, axis=0)

    return y_test


import pytest

from jax import random
import jax.numpy as jnp

import cr.sparse.dict as crdict
import cr.nimble as cnb
import cr.sparse as crs

def test_random_onb():
    key = random.PRNGKey(0)
    N = 16
    A = crdict.random_onb(key, N)
    assert cnb.is_matrix(A)
    assert cnb.is_square(A)
    assert A.shape == (N, N)
    assert cnb.has_orthogonal_columns(A)
    assert cnb.has_orthogonal_rows(A)

def test_hadamard_basis():
    N = 16
    A = crdict.hadamard_basis(N)
    assert cnb.is_matrix(A)
    assert cnb.is_square(A)
    assert A.shape == (N, N)
    assert cnb.has_orthogonal_columns(A)
    assert cnb.has_orthogonal_rows(A)
    G = crdict.gram(A)
    assert cnb.is_symmetric(G)
    if cnb.is_cpu():
        assert cnb.is_positive_definite(G)
    F = crdict.frame(A)
    assert cnb.is_symmetric(F)
    if cnb.is_cpu():
        assert cnb.is_positive_definite(F)

def test_cosine_basis():
    N = 16
    A = crdict.cosine_basis(N)
    assert cnb.is_matrix(A)
    assert cnb.is_square(A)
    assert A.shape == (N, N)
    assert cnb.has_orthogonal_columns(A)
    assert cnb.has_orthogonal_rows(A)
    G = cnb.mat_transpose(A) @ A
    assert cnb.is_symmetric(G)
    if cnb.is_cpu():
        assert cnb.is_positive_definite(G)
    mu = crdict.coherence(A)
    assert mu < 1e-3
    bounds = crdict.frame_bounds(A)
    assert len(bounds) == 2
    assert jnp.isclose(bounds[0], 1, atol=1e-4)
    assert jnp.isclose(bounds[1], 1, atol=1e-4)
    assert jnp.isclose(crdict.upper_frame_bound(A), 1, atol=1e-4)
    assert jnp.isclose(crdict.lower_frame_bound(A), 1, atol=1e-4)
    mu = crdict.mutual_coherence(A, A)
    assert jnp.isclose(mu, 1, atol=1e-4)
    b = crdict.babel(A)
    assert len(b) == N-1

def test_fourier_basis():
    N = 16
    A = crdict.fourier_basis(N)
    assert cnb.is_matrix(A)
    assert cnb.is_square(A)
    assert A.shape == (N, N)
    assert cnb.has_unitary_columns(A)
    assert cnb.has_unitary_rows(A)
    G = cnb.hermitian(A) @ A
    assert cnb.is_hermitian(G)
    G = crdict.gram(A)
    assert cnb.is_hermitian(G)
    F = crdict.frame(A)
    assert cnb.is_hermitian(F)


"""
Matrix Inverse Method
# Linear Regression: Inverse Matrix Method
#----------------------------------
#
# This function shows how to use Tensorflow to
# solve linear regression via the matrix inverse.
#
# Given Ax=b, solving for A:  y=a1*x1+a2*x2, a1=1
  for the sample  number of m
  XA=Y     shapel X:[m 2]  A:[2 1] Y:[m 1]
  X.T*X*A=X.T*Y
  A=(X.T*X)^(-1)*X.T*Y

#  A = (t(x) * x)^(-1) * t(x) * b
#  where t(x) is the transpose of x
"""
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
tf.reset_default_graph()

x_vals=np.linspace(0,10,100)
y_vals=x_vals+np.random.normal(0,1,100)
x_vals_column=np.transpose(np.matrix(x_vals))
ones_column=np.matrix(np.ones((100,1)))#ones_column = np.transpose(np.matrix(np.repeat(1, 100)))
x=np.column_stack((ones_column,x_vals_column))
y=np.matrix(y_vals).T#y=np.transpose(np.matrix(y_vals))

#create tensors
x_tensor=tf.constant(x)
y_tensor=tf.constant(y)
#matrix inverse
x_xt=tf.matmul(tf.transpose(x_tensor),x_tensor)
x_xt_inv=tf.matrix_inverse(x_xt)
product=tf.matmul(x_xt_inv,tf.transpose(x_tensor))
A=tf.matmul(product,y_tensor)

sess=tf.Session()
A_eval=sess.run(A)

print("A:\n",A_eval)
#get the best fit line
best_fit_y=[]
for i in x_vals:
    best_fit_y.append(A_eval[0]+i*A_eval[1])
#best_fit_y=(np.dot(x,np.matrix(A_eval))).tolist()

#plot the fit line
plt.plot(x_vals,y_vals,"o",label="Data")
plt.plot(x_vals,best_fit_y,'r-',label="Best fit line",linewidth=3)
plt.legend(loc='upper letf')
plt.show()

cookbook/3 Linear Regression/Matrix Inverse Method.py