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

import sys
import numpy as np
import scipy.linalg as sc

#=============================================================================
# 1. Assembling the stiffness matrix, with option for 2nd order effect
#-----------------------------------------------------------------------------

def stiffness(L, EI, P=None):
    ''' Global stiffness matrix for a discretized column.
        All input parameters are numpy vectors, one element per
        column discretization element.

        L:   lengths
        EI:  stifnesses
        P:   axial loading (negative for compression)
    '''

# Preliminary settings

    N   =  len(L) + 1
    KG  =  np.zeros((2*N,2*N))

    ke11 = 12*EI/L/L/L
    ke12 =  6*EI/L/L
    ke22 =  4*EI/L
    ke24 =  2*EI/L

    if (P is None):
        kg11 = 36*P/L/30
        kg12 =  3*P  /30*np.ones(L.shape)
        kg22 =  4*P*L/30
        kg24 =    P*L/30

# Iterate for all discretization elements

    for k in range(N-1):

        i1 = 2* k           #0
        i2 = 2* k + 1       #1
        i3 = 2*(k + 1)      #2
        i4 = 2*(k + 1) + 1  #3

# Linear elastic stifness matrix

        k11k = ke11[k] # 12
        k12k = ke12[k] # 6L
        k22k = ke22[k] # 4L^2
        k24k = ke24[k] # 2L^2

        KG[i1,i1] += k11k
        KG[i1,i2] += k12k;    KG[i2,i1] += k12k
        KG[i1,i3] -= k11k;    KG[i3,i1] -= k11k
        KG[i1,i4] += k12k;    KG[i4,i1] += k12k

        KG[i2,i2] += k22k;
        KG[i2,i3] -= k12k;    KG[i3,i2] -= k12k
        KG[i2,i4] += k24k;    KG[i4,i2] += k24k

        KG[i3,i3] += k11k
        KG[i3,i4] -= k12k;    KG[i4,i3] -= k12k
        KG[i4,i4] += k22k

# Linear elastic stifness matrix

        if (P is None):
            k11k = kg11[k] # 36
            k12k = kg12[k] # 3L
            k22k = kg22[k] # 4L^2
            k24k = kg24[k] # L^2

            KG[i1,i1] += k11k
            KG[i1,i2] += k12k;    KG[i2,i1] += k12k
            KG[i1,i3] -= k11k;    KG[i3,i1] -= k11k
            KG[i1,i4] += k12k;    KG[i4,i1] += k12k

            KG[i2,i2] += k22k
            KG[i2,i3] -= k12k;    KG[i3,i2] -= k12k
            KG[i2,i4] -= k24k;    KG[i4,i2] -= k24k

            KG[i3,i3] += k11k
            KG[i3,i4] -= k12k;    KG[i4,i3] -= k12k
            KG[i4,i4] += k22k

    return KG

#=============================================================================
# 2. Assembling consistent mass matrix
#-----------------------------------------------------------------------------

def consistMass(L, mu):
    ''' Global consistent mass matrix for a discretized column.
        All input parameters are numpy vectors, one element per
        column discretization element.

        L:   lengths
        mu:  mass per unit lengths
    '''

# Preliminary settings

    N   =  len(L) + 1
    MG  =  np.zeros((2*N,2*N))

    m11 = 156*mu*L    /420
    m12 =  22*mu*L*L  /420
    m13 =  54*mu*L    /420
    m14 =  13*mu*L*L  /420
    m22 =   4*mu*L*L*L/420
    m24 =   3*mu*L*L*L/420

# Iterate for all discretization elements

    for k in range(N-1):

        i1 = 2* k           #0
        i2 = 2* k + 1       #1
        i3 = 2*(k + 1)      #2
        i4 = 2*(k + 1) + 1  #3

        m11k = m11[k]
        m12k = m12[k]
        m13k = m13[k]
        m14k = m14[k]
        m22k = m22[k]
        m24k = m24[k]

        MG[i1,i1] += m11k
        MG[i1,i2] += m12k;    MG[i2,i1] += m12k
        MG[i1,i3] += m13k;    MG[i3,i1] += m13k
        MG[i1,i4] -= m14k;    MG[i4,i1] -= m14k

        MG[i2,i2] += m22k;
        MG[i2,i3] += m14k;    MG[i3,i2] += m14k
        MG[i2,i4] -= m24k;    MG[i4,i2] -= m24k

        MG[i3,i3] += m11k;
        MG[i3,i4] -= m12k;    MG[i4,i3] -= m12k
        MG[i4,i4] += m22k

    return MG

#=============================================================================
# 3. Complete case analysis
#-----------------------------------------------------------------------------

def analyseCase(H, EI, mu, P1, n):

    L = (H/n)*np.ones(n)
    P =  P1 + np.cumsum(L*mu)

    K =  stiffness(L, EI, P)
    M =  consistMass(L, mu)

    K       =  K[:-2,:-2]
    M       =  M[:-2,:-2]
    M[0,0] +=  P1/9.81

    w2, Phi = sc.eig(K, M)
    iw      = w2.argsort()
    w2      = w2[iw]
    Phi     = Phi[:,iw]
    wk      = np.sqrt(np.real(w2))
    fk      = wk/2/np.pi

    return fk[0]

#=============================================================================


import matplotlib.pyplot as plt
import numpy as np
fig1 = plt.figure();
fig2 = plt.figure();
fig3 = plt.figure();
fig4 = plt.figure();
fig5 = plt.figure();

ax1 = fig1.add_subplot(111)
ax2 = fig2.add_subplot(111)
ax3 = fig3.add_subplot(111)
ax4 = fig4.add_subplot(111)
ax5 = fig5.add_subplot(111)

dom1 = np.arange(-20,20.1,0.1)
dom2 = np.arange(0.1,100,0.1)
dom3 = np.arange(0,10.5,1)
dom4 = np.arange(0,10.5,1)
dom5 = np.arange(-10,10.5,0.5)

img1 = [x**2 for x in dom1]
img2 = [np.log2(x) for x in dom2]
img3 = [np.math.factorial(x) for x in dom3]
img4 = [2**x for x in dom4]
img5 = [np.sin(x) for x in dom5]

ax1.plot(dom1,img1,color = 'gray',label=r"$f(x) = x^2$")
ax2.plot(dom2,img2,color='red',linestyle = '-.',label=r"$f(x) = \log_2(x)$")
ax3.plot(dom3,img3,color = 'green', marker='o',linestyle = ' ',label=r"$f(x) = x!$")
ax4.plot(dom4,img4,color = 'yellow',marker = 's',linestyle=' ',label=r"$f(x) = 2^x$")
ax5.plot(dom5,img5,color = 'blue',marker = '^',linestyle = '--',label=r"$f(x)=\sin(x)$")

ax1.grid(True)
ax2.grid(True)
ax3.grid(True)
ax4.grid(True)
ax5.grid(True)

ax1.set_xticks(np.arange(-20,20.1,1))
ax2.set_xticks(np.arange(0.1,100,5))
ax3.set_xticks(dom3)
ax4.set_xticks(dom4)
ax5.set_xticks(dom5)

ax1.legend(loc='best')
ax2.legend(loc='best')
ax3.legend(loc='best')
ax4.legend(loc='best')
ax5.legend(loc='best')

ax1.set_title("Comportamento da Função"+r"$f(x)=x^2$")
ax2.set_title("Comportamento da Função"+r"$f(x) = \log_2(x)$")
ax3.set_title("Comportamento da Função"+r"$f(x) = x!$")
ax4.set_title("Comportamento da Função"+r"$f(x) = 2^x$")
ax5.set_title("Comportamento da Função"+r"$f(x)=\sin(x)$")

plt.show()


import numpy as np

from scipy.linalg import svd

import matplotlib.pyplot as plt

from skimage import data

def plot_img(img, savename="tmp.png"):

    fig, ax = plt.subplots(1, 1, figsize=(4, 4))

    ax.imshow(img, cmap=plt.cm.gray)
    ax.axis(False)

    plt.savefig(savename, bbox_inches="tight", dpi=300, transparent=True)

    plt.show()

if __name__ == "__main__":

    #####
    #####
    #####
    #####
    #####

    # --> Get image from skimage.
    img = getattr(data, "camera")()

    plot_img(img, savename="../data/camera.png")

    # --> Compute its SVD.
    U, S, Vt = svd(img)

    # for i in range(18):
    #     x = np.outer(U[:, i], Vt[i])
    #     plot_img(x, "../imgs/camera_svd_{0}_comp.png".format(i+1))
    # plt.show()

    for i in range(0, 90, 5):
        if i == 0:
            x = np.outer(U[:, i], Vt[i])
        else:
            x = U[:, :i+1] @ np.diag(S[:i+1]) @ Vt[:i+1, :]

        plot_img(x, "../imgs/camera_svd_{0}_estimate.png".format(i+1))

    # --> Outpost singular spectrum.
    # out = np.c_[np.arange(1, len(S)+1), S / np.max(S)]
    # np.savetxt("../data/camera_svd.dat", out)

    # out = np.c_[np.arange(1, len(S)+1), np.cumsum(S/np.sum(S))]
    # np.savetxt("../data/camera_svd_bis.dat", out)


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

import numpy as np
import scipy.linalg as sl
import matplotlib.pyplot as plt
from numba import njit

def givens_rotation(a, b):
    """Compute matrix entries for Givens rotation."""
    r = np.hypot(a, b)
    c = a/r
    s = -b/r

    return (c, s)

@njit(cache=True, nogil=True, fastmath=True)
def householder_reflection(x):
    alpha = x[0]
    s = np.linalg.norm(x[1:])**2
    v = x.copy()

    if s == 0:
        tau = 0.
    else:
        t = np.sqrt(alpha**2 + s)
        v[0] = alpha - t if alpha <= 0 else -s / (alpha + t)

        tau = 2 * v[0]**2 / (s + v[0]**2)
        v /= v[0]

    return v, tau

@njit(cache=True, nogil=True, fastmath=True)
def householder_reflection_right(x):
    alpha = x[-1]
    s = np.linalg.norm(x[:-1])**2
    v = x.copy()

    if s == 0:
        tau = 0.
    else:
        t = np.sqrt(alpha**2 + s)
        v[-1] = alpha - t if alpha <= 0 else -s / (alpha + t)

        tau = 2 * v[-1]**2 / (s + v[-1]**2)
        v /= v[-1]

    return v, tau

def qr_gr(A):
    """Perform QR decomposition of matrix A using Givens rotation."""
    num_rows, num_cols = np.shape(A)

    # Initialize orthogonal matrix Q and upper triangular matrix R.
    Q = np.identity(num_rows)
    R = np.copy(A)

    # Iterate over lower triangular matrix.
    (rows, cols) = np.tril_indices(num_rows, -1, num_cols)
    for (row, col) in zip(rows, cols):

        # Compute Givens rotation matrix and
        # zero-out lower triangular matrix entries.
        if R[row, col] != 0:
            (c, s) = givens_rotation(R[col, col], R[row, col])

            G = np.identity(num_rows)
            G[[col, row], [col, row]] = c
            G[row, col] = s
            G[col, row] = -s

            R = np.dot(G, R)
            Q = np.dot(Q, G.T)

    return (Q, R)

def qr_hh(A):
    """Perform QR decomposition of matrix A using Householder reflections.
    """

    M = A.copy()
    m, n = M.shape
    Q = np.eye(m)

    for i in range(min(m, n)):

        a = M[i:, i]

        v, tau = householder_reflection(a)

        H = np.identity(m-i)
        H -= tau * np.outer(v, v)

        M[i:] = H @ M[i:]
        Q[:, i:] = Q[:, i:] @ H

    return Q, M

def rq_hh(A):
    """Perform RQ decomposition of matrix A using Householder reflections.
    """

    M = A.copy()
    m, n = M.shape
    Q = np.eye(n)

    for i in range(min(m, n)):

        a = M[-1-i, :n-i]

        v, tau = householder_reflection_right(a)

        H = np.identity(n-i)
        H -= tau * np.outer(v, v)

        M[:, :n-i] = M[:, :n-i] @ H
        Q[:n-i, :] = H @ Q[:n-i, :]

    return M, Q

def ql(M):
    """Perform QL decomposition of matrix A using scipy
    """
    r, q = sl.rq(M.T)
    return q.T, r.T

@njit(cache=True, nogil=True)
def shredder_basic(M, tol, verbose):

    m, n = M.shape
    Q = np.eye(m)

    j = 0

    for i in range(min(m, n)):

        while j < n:

            a = np.ascontiguousarray(Q.T[i:]) @ np.ascontiguousarray(M[:, j])
            do = False
            for ia in a:
                if abs(ia) > tol:
                    do = True
                    break

            if do:
                # apply Householder transformation
                v, tau = householder_reflection(a)

                H = np.identity(m-i)
                H -= tau * np.outer(v, v)

                Q[:, i:] = np.ascontiguousarray(Q[:, i:]) @ H

                if verbose:
                    print("...shredding row", j, "and col", i)
                break

            else:
                j += 1

    return Q

@njit(cache=True, nogil=True)
def shredder_pivoting(M, tol, verbose):
    """The DS-decomposition with pivoting"""

    m, n = M.shape
    M = M.copy().astype(np.float64)
    Q = np.eye(m)
    P = np.arange(n)

    j = 0

    for i in range(min(m, n)):

        # pivoting loop
        k = i
        while True:
            if k == n:
                break
            if np.any(np.abs(M[i:, k]) > tol):
                if k != i:
                    # move columns
                    M[:, np.array([i, k])] = M[:, np.array([k, i])]
                    P[np.array([i, k])] = P[np.array([k, i])]
                break
            k += 1

        while j < n:
            if np.any(np.abs(M[i:, j]) > tol):

                # apply Householder transformation
                a = M[i:, j]
                v, tau = householder_reflection(a)

                H = np.identity(m-i)
                H -= tau * np.outer(v, v)
                M[i:] = H @ M[i:]
                Q[:, i:] = np.ascontiguousarray(Q[:, i:]) @ H

                if verbose:
                    print("...shredding row", j, "and col", i)
                break

            else:
                j += 1

    return Q, M, P

@njit(cache=True, nogil=True)
def shredder_non_pivoting(M, tol, verbose):
    """The DS-decomposition with anti-pivoting"""

    m, n = M.shape
    M = M.copy().astype(np.float64)
    Q = np.eye(m)
    P = np.arange(n)

    j = 0

    for i in range(min(m, n)):

        # anti-pivoting
        # for each i move those columns forward that do not need transformation

        for k in range(j, n):
            if np.all(np.abs(M[i:, k]) < tol):
                # move columns
                if k != j:
                    M[:, np.array([j, k])] = M[:, np.array([k, j])]
                    P[np.array([j, k])] = P[np.array([k, j])]

                if verbose:
                    print('...switching rows', j, 'and', k)
                j += 1

        while j < n:
            if np.any(np.abs(M[i:, j]) > tol):

                # apply Householder transformation
                a = M[i:, j]
                v, tau = householder_reflection(a)

                H = np.identity(m-i)
                H -= tau * np.outer(v, v)
                M[i:] = H @ M[i:]
                Q[:, i:] = np.ascontiguousarray(Q[:, i:]) @ H

                if verbose:
                    print("...shredding row", j, "and col", i)
                break

            else:
                j += 1

    return Q, M, P

def shredder(M, pivoting=None, tol=1e-11, verbose=False):
    """The QS decomposition from "Efficient Solution of Models with Occasionally Binding Constraints" (Gregor Boehl)

    The QS decomposition uses Householder reflections to bring a system in the row-echolon form. This is a dispatcher for the sub-functions.
    """

    if pivoting is None:
        q = shredder_basic(M, tol, verbose)
        return q, q.T @ M
    elif pivoting:
        return shredder_pivoting(M, tol, verbose)
    elif not pivoting:
        return shredder_non_pivoting(M, tol, verbose)
    else:
        raise NotImplementedError

def nearestPSD(A):

    B = (A + A.T)/2
    H = sl.polar(B)[1]

    return (B + H)/2


######
# this script containing matrix operations is solely developed and written by Tianyi Zhu
######
# fix python 2.7 division issues by importing python 3.6 division module
from __future__ import division
# import all modules
import math
import copy
import decimal
from sympy import*
import numpy as np
# roundHalfUp from 15-112 hw1
######
def roundHalfUp(d):
    # Round to nearest with ties going away from zero.
    rounding = decimal.ROUND_HALF_UP
    return int(decimal.Decimal(d).to_integral_value(rounding=rounding))
######
# almostEqual from 15-112 hw1
######
def almostEqual(d1, d2):
    epsilon=10**-2
    return (abs(d2 - d1) < epsilon)
######
# this function takes in two possible matrices and returns False if the addition is not legal, else returns the result of addition as a new matrix
def addition(a, b):
    rowA = len(a)
    colA = len(a[0])
    rowB = len(b)
    colB = len(b[0])
    # the matrices have different dimentions
    if rowA != rowB or colA != colB:
        # return error message
        return "Matrices do not have the same dimensions!"
    else:
        result = [ ([0] * colA) for row in range(rowA) ]
        for row in range(rowA):
            for col in range(colA):
                result[row][col] = a[row][col] + b[row][col]
    return result
# this function takes in two possible matrices and returns False if the subtraction is not legal, else returns the result of subtraction as a new matrix
def subtraction(a, b):
    rowA = len(a)
    colA = len(a[0])
    rowB = len(b)
    colB = len(b[0])
    # the matrices have different dimensions
    if rowA != rowB or colA != colB:
        # return error message
        return "Matrices do not have the same dimensions!"
    else:
        result = [ ([0] * colA) for row in range(rowA) ]
        for row in range(rowA):
            for col in range(colA):
                result[row][col] = a[row][col] - b[row][col]
    return result
# this function takes in two possible matrices and returns False if the subtraction is not legal, else returns the result of subtraction as a new matrix
def multiplication(a, b):
    rowA = len(a)
    colA = len(a[0])
    rowB = len(b)
    colB = len(b[0])
    # the matrices have different inner dimensions
    if colA != rowB:
        # return error message
        return "Matrices do not have the same inner dimensions!"
    else:
        result = [ ([0] * colB) for row in range(rowA) ]
        for i in range(rowA):
            for j in range(colB):
                for k in range(rowB):
                    result[i][j] += a[i][k] * b[k][j]
    return result
# this function takes in a matrix and returns the transposed matrix
def transposed(a):
    row = len(a)
    col = len(a[0])
    result = [ ([0] * row) for i in range(col) ]
    for i in range(row):
        for j in range(col):
            result[j][i] = a[i][j]
    return result
# this recursive function takes in a matrix and returns False if the input matrix is not square or is a 1x1 matrix, else returns the determinant of the original square matrix
# this function computes the determinant by using the Laplace expansion formula
def determinant(a):
    row = len(a)
    col = len(a[0])
    entryList = []
    # if not square matrix
    if row != col:
        # return error message
        return "Matrix is not square!"
    # if matrix has only one row
    if row <= 1:
        # return error message
        return "Matrix has a single row!"
    # base case for recursion: the matrix is 2x2
    if col == 2:
        return a[0][0] * a[1][1] - a[0][1] * a[1][0]
    # recursively Laplace expand
    else:
        for j in range(col):
            new = copy.deepcopy(a)
            del new[0]
            deleteCol(new, j)
            multiplier = a[0][j] * math.pow(-1, (2+j))
            det = determinant(new)
            entryList.append(multiplier * det)
        return int(sum(entryList))
# this function takes in a matrix, a row index i, and a row index j. It destructively modify the input matrix by swapping the i th row and the j th row
def swapRow(a, i, j):
    a[i], a[j] = a[j], a[i]
    return a
# this function takes in the number of rows and cols, n, and return a square indentity matrix of the specified dimension
def makeIdentity(n):
    identity = [ ([0] * n) for row in range(n) ]
    for i in range(n):
        for j in range(n):
            if i == j:
                identity[i][j] = 1
    return identity
# this function takes in a matrix and a key and destructively removes the specified column from the matrix
def deleteCol(a, key):
    row = len(a)
    for i in range(row):
        del a[i][key]
# this function takes in a matrix, a row index, and a column index to see if only zeros exist at or below row i in col j
def checkForZeros(a, i, j):
    rows = len(a)
    cols = len(a[0])
    nonZeros = []
    firstNonZero = -1
    for m in range(i, rows):
        nonZero = a[m][j]!=0
        nonZeros.append(nonZero)
        if firstNonZero == -1 and nonZero:
            firstNonZero = m
    zeroSum = sum(nonZeros)
    return zeroSum, firstNonZero
# this function takes in a matrix and returns false if the matrix is not square or invertible, else returns the inverse matrix
# this function solves the inverse of the matrix by performing Gaussian-Jordan elimination
def inverse(a):
    b = copy.deepcopy(a)
    rows = len(b)
    cols = len(b[0])
    # if matrix not square
    if rows != cols:
        # return error message
        return "Matrix is not square!"
    # if matrix not invertible
    if determinant(b) == 0:
        # return error message
        return "Matrix is not invertible!"
    else:
        identity = makeIdentity(rows)
        # append identity to the right side
        for i in range(rows):
            b[i] += identity[i]
        # this is the standard row reduced procedure
        i = 0
        for j in range(cols):
            zeroSum, firstNonZero = checkForZeros(b, i, j)
            if zeroSum == 0:
                if j == cols:
                    return formatted(b)
            if firstNonZero != i:
                b = swapRow(b, i, firstNonZero)
            b[i] = [m/b[i][j] for m in b[i]]
            for q in range(rows):
                if q != i:
                    scaledRow = [b[q][j] * m for m in b[i]]
                    b[q] = [b[q][m] - scaledRow[m] for m in range(len(scaledRow))]
            if i == rows or j == cols:
                break
            i += 1
        # return the right side of the matrix
        for i in range(rows):
            b[i] = b[i][cols:len(b[i])]
    return formatted(b)
# this function takes in a matrix and outputs a formatted matrix
def formatted(a):
    b = copy.deepcopy(a)
    rows = len(b)
    cols = len(b[0])
    for i in range(rows):
        for j in range(cols):
            # if the entry is an integer, convert to int type
            if almostEqual(int(b[i][j]), b[i][j]):
                b[i][j] = int(b[i][j])
            # if the entry is a float, convert to two decimal float
            if not almostEqual(int(b[i][j]), b[i][j]):
                b[i][j] = roundHalfUp(b[i][j]*100)/100
    return b
# Sympy Implementation of rref
def rrefSym(a):
    b = copy.deepcopy(a)
    m = Matrix(b)
    l = m.rref()[0]
    new = np.array(l).astype(np.float64).tolist()
    return formatted(new)
# this customized rref function takes in a matrix and returns the reduced row echelon form of the matrix
def rrefCus(a):
    b = copy.deepcopy(a)
    lead = 0
    row = len(b)
    col = len(b[0])
    for r in range(row):
        if lead >= col:
            new = formatted(b)
            return new
        i = r
        while b[i][lead] == 0:
            i += 1
            if i == row:
                i = r
                lead += 1
                if col == lead:
                    new = formatted(b)
                    return new
        swapRow(b, i, r)
        leadVar = b[r][lead]
        b[r] = [ rowElem / float(leadVar) for rowElem in b[r]]
        for i in range(row):
            if i != r:
                leadVar = b[i][lead]
                b[i] = [ iVar - leadVar * rVar for rVar,iVar in zip(b[r],b[i])]
        lead += 1
    new = formatted(b)
    return new
# this function takes in two vectors as 1D lists. It returns False if two vectors have different dimensions, else adds two vectors
def vectAdd(a, b):
    if len(a) != len(b):
        return False
    else:
        new = []
        for i in range(len(a)):
            new.append(a[i] + b[i])
    return new
# this function takes in two vectors as 1D lists. It returns False if two vectors have different dimensions, else subtracts two vectors
def vectSub(a, b):
    if len(a) != len(b):
        return False
    else:
        new = []
        for i in range(len(a)):
            new.append(a[i] - b[i])
    return new
# this function takes a possibly-0 multiplier and a vector. It returns a new vector whose entries are scaled by the multiplier
def vectorScal(num, a):
    new = []
    for i in range(len(a)):
        new.append(num * a[i])
    return new
# this function takes in two vectors as 1D lists. It returns False if two vectors have different dimensions, else returns the dot product of two vectors
def dotProduct(a, b):
    if len(a) != len(b):
        return False
    else:
        dotProduct = 0
        for i in range(len(a)):
            dotProduct += a[i]*b[i]
    return dotProduct
# this function takes in a matrix whose rows are linearly independent vectors and returns a matrix whose rows are a set of orthogonalized vectors
def gramSchmidt(a):
    copyA = copy.deepcopy(a)
    # if condition not met
    if linearDep(a) == "linearly dependent":
        # return error message
        return "Not linearly independent vectors!"
    else:
        a = copyA
        rows = len(a)
        cols = len(a[0])
        Y = [0] * rows
        Q = [[0] * cols for row in range(rows)]
        # set X1 to V1
        productX1 = 1.0 / math.sqrt(dotProduct(a[0],a[0]))
        Q[0] = vectorScal(productX1, a[0])
        # find X2, X3,...
        for j in range(1, rows):
            Y = a[j]
            for i in range(0, j):
                Y = vectSub(Y, vectorScal(dotProduct(a[j], Q[i]), Q[i]))
            productYj = 1.0 / math.sqrt(dotProduct(Y, Y))
            Q[j] = vectorScal(productYj, Y)
    return formatted(Q)
# this function takes in a matrix and returns the rank of the matrix
def rank(a):
    RREF = rrefSym(a)
    rows = len(RREF)
    cols = len(RREF[0])
    # count rows of zeros
    count = 0
    for row in RREF:
        if row.count(0) == cols:
            count += 1
    # num of non-zero rows
    return rows - count
# this function takes in a matrix whose rows are a set of spanning vectors and the dimension n of R^n. It returns True if the vector set spans R^n, else returns False
def spanSpace(a, n):
    if rank(a) == n:
        return "the vector set spans R^n"
    else:
        return "the vector set does not span R^n"
# this function takes in a matrix whose rows are a set of vectors. It returns True if the set of vectors are linearly dependent, else returns False
def linearDep(a):
    RREF = rrefSym(a)
    rows = len(RREF)
    cols = len(RREF[0])
    # count rows of zeros
    count = 0
    for row in RREF:
        if row.count(0) == cols:
            count += 1
    if count != 0:
        return "linearly dependent"
    elif count == 0:
        return "linearly independent"
# this function takes in a matrix and returns a matrix whose rows are the bases for the row space of the original matrix
def rowSpaceBasis(a):
    bases = []
    original = copy.deepcopy(a)
    RREF = rrefSym(a)
    cols = len(RREF[0])
    count = 0
    for i in range(len(RREF)):
        if RREF[i].count(0) != cols:
            bases.append(original[i])
    return formatted(bases)
# this function takes in a matrix and returns a matrix whose rows are the bases for the row space of the original matrix
def colSpaceBasis(a):
    bases = []
    originalTrans = transposed(copy.deepcopy(a))
    RREF = rrefSym(a)
    RREFtrans = transposed(RREF)
    r = len(RREFtrans)
    count = 0
    for i in range(len(RREFtrans)):
        if RREFtrans[i].count(1) == 1 and RREFtrans[i].count(0) == (r - 1):
            bases.append(originalTrans[i])
    return formatted(bases)
# this function takes in a matrix and returns a matrix whose rows are the bases for the row space of the original matrix
def nullSpaceBasis(a):
    m = Matrix(a)
    # the sympy implementation of nullspace
    l = m.nullspace()
    new = np.array(l).astype(np.float64).tolist()
    if len(new) == 0:
        return "Nullspace is empty!"
    else:
        return formatted(new)
# this function takes in a matrix whose rows are a set of vectors that spann a vector space. It returns a matrix whose rows form a basis for that vector space
def basisOfSpaceSpannedBySet(a):
    # the same as finding the basis for the row space
    return rowSpaceBasis(a)


# -*- coding: utf-8 -*-
"""
Created on Thu Jan 12 15:27:52 2017

@author: aliTakin
"""

# this is task 5
import numpy as np

def normalize_data(X):

    [x1,x2]=np.shape(X)

    X_norm=[]
    for i in  x2 :
       X_norm[:,i]= X[:,i]-np.mean(X[:,i], axis = 0)
       X_norm[:,i]= X[:,i]/np.std(X[:,i], axis = 0)

    return X_norm


#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Tue May 29 12:27:32 2018

@author: kristianeschenburg
"""

from scipy.stats import ncx2,chi2
import numpy as np

def mahalanobis(samples,mu,sigma):

    """
    Compute the Mahalanobis distance for a set of samples from an MVN
    with parameters mean = mu and covariance = sigma.

    Parameters:
    - - - - -
        samples : test samples
        mu : mean of target distribution
        sigma : covariance of target distribution
    """

    try:
        P = np.linalg.inv(sigma)
    except np.linalg.LinAlgError:
        raise np.linalg.LinAlgError('Covariance matrix is singular.')

    if mu.ndim == 1:
        mu =  mu[np.newaxis,:]

    deviation = samples - mu
    quadratic = np.dot(deviation,np.dot(P,deviation.T))

    distances = np.sqrt(np.diag(quadratic))

    return distances

def noncentrality(mu,H):

    """
    Compute the non-centrality parameter of a non-central chi-squared
    distribution.

    Parameters:
    - - - - -
        mu : mean vector
        H : transformation matrix

    where the quadratic term has the form (x-mu)'H(x-mu) such that the
    non-centrality parameter \phi = (mu)'H(mu)/2.
    """

    mu = mu.squeeze()
    if mu.ndim == 1:
        mu = mu[np.newaxis,:]

    phi = (1./2) * np.dot(mu.T,np.dot(H,mu))

    return phi

def chi_x2(samples,df):

    """
    Compute the central chi-squared statistics for set of chi-squared
    distributed samples.

    Parameters:
    - - - - -
        samples : chi-square random variables
        df : degrees of freedom
    """

    return chi2.pdf(samples,df)

def nchi_x2(samples,df,nc):

    """
    Compute the non-central chi-squared statistics for a set non-central
    chi-squared distributed samples.

    Parameters:
    - - - - -
        samples : chi-square random variables
        df : degrees of freedom
        nc : non-centrality parameter
    """

    return ncx2.pdf(samples,df,nc)


import numpy as np
import cv2

def return_to_spatial_domain(spectrum):
	"""
	puts the DC component back at position f(0,0) and computes the inverse FFT to return to the spatial domain
	"""
	uncentered_filtered_spectrum = np.fft.ifftshift(spectrum)
	return np.fft.ifft2(uncentered_filtered_spectrum)

def go_to_frequency_domain(image):
	"""
	compute the FFT to find the frequency transform, then shift the zero frequency component to the center where it will be more easy to analyze
	"""
	fft = np.fft.fft2(image)
	return np.fft.fftshift(fft)

def compute_magnitude(frequency_image):
	"""
	computes the magnitude of the frequency spectrum in a logarithmic approach so we can analyze it properly
	"""
	magnitude = 20*np.log(np.abs(frequency_image))
	return magnitude

def square_high_pass_filter(frequency_image, frequency_radius):
  (height, width) = frequency_image.shape
  (center_x, center_y) = width // 2, height // 2

  frequency_image[center_y - frequency_radius:center_y + frequency_radius, center_x - frequency_radius:center_x + frequency_radius] = 0

  return frequency_image

def square_smooth_high_pass_filter(frequency_image, frequency_radius):
  (height, width) = frequency_image.shape[:2]
  (center_x, center_y) = width // 2, height // 2

  filter_mask = np.full((height, width), 255, dtype = np.uint8)
  start_point = (center_x - frequency_radius, center_y - frequency_radius)
  end_point = (center_x + frequency_radius, center_y + frequency_radius)
  filter_mask = cv2.rectangle(filter_mask,
    start_point, end_point, 0, -1)

  kernel_size = frequency_radius//2 + 1
  filter_mask = cv2.GaussianBlur(filter_mask,
  (kernel_size,kernel_size),0)/255.0
  masked_image = frequency_image * filter_mask

  return masked_image

def circle_high_pass_filter(frequency_image, frequency_radius):
  (height, width) = frequency_image.shape[:2]
  (center_x, center_y) = width // 2, height // 2

  Y, X = np.ogrid[:height, :width]

  dist_from_center = np.sqrt((X - center_x)**2 + (Y - center_y)**2)

  mask = dist_from_center <= frequency_radius
  frequency_image[mask] = 0

  return frequency_image

def circle_smooth_high_pass_filter(frequency_image, frequency_radius):
  (height, width) = frequency_image.shape[:2]
  (center_x, center_y) = width // 2, height // 2

  filter_mask = np.full((height, width), 255, dtype = np.uint8)
  filter_mask = cv2.circle(filter_mask,
    (center_x, center_y),
    frequency_radius, 0, -1)

  kernel_size = frequency_radius//2 + 1
  filter_mask = cv2.GaussianBlur(filter_mask,
  (kernel_size,kernel_size),0)/255.0
  masked_image = frequency_image * filter_mask

  return masked_image


import numpy as np

def is_not_none_if_condition(cls, x, y, condition):
    for i in y:
        attr = getattr(x, i, None)
        if condition:
            cls.assertTrue(attr is not None)
        else:
            cls.assertTrue(attr is None)

def angle_to_coord(angle, normalized=False):
    # multiply to unnormalize
    x = np.cos(np.radians(angle))
    y = np.sin(np.radians(angle))
    if not normalized:
        x *= 2
        y *= 0.5
    return x, y


import numpy as np
import matplotlib.pyplot as plt
from cv2 import cv2
import argparse

def bgr2gray(img):
    img_gray = np.zeros((img.shape[0], img.shape[1])).astype(np.float64)
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            img_gray[i, j] = (15 * img[i, j, 0] + 75 * img[i, j, 1] + 38 * img[i, j, 2]) >> 7
    return img_gray

def PSNR(img, img_idct):
    MSE = np.sum((img_idct - img)**2)/(img.shape[0]*img.shape[1])
    PSNR = 10 * np.log(255**2/MSE)
    return PSNR

def twoDCTBlock(img, sample=None, quan=None):
    # cv2.dct, cv2.idct
    # img_dct = cv2.dct(img)
    # img_idct = cv2.idct(img_dct)

    # my dct, idct
    co = np.zeros_like(img)
    co[0, :] = np.sqrt(1/img.shape[0])

    for i in range(1, img.shape[0]):
        for j in range(img.shape[1]):
            co[i, j] = np.cos((2 * j + 1) * np.pi * i / (2 * img.shape[0])) * np.sqrt(2 / img.shape[0])

    img_dct = np.dot(np.dot(co, img), co.T)

    if sample != None:
        remain = int(np.sqrt(sample))
        remain = img.shape[0] // remain
        for i in range(remain, img.shape[0]):
            for j in range(remain, img.shape[1]):
                img_dct[i, j] = 0

    if isinstance(quan, np.ndarray):
        img_dct_quan = quantization(img_dct, quan)
        img_dct = dequantization(img_dct_quan, quan)

    img_idct = np.dot(np.dot(co.T, img_dct), co)
    return img_dct, img_idct

def twoDCT(img, block=8, sample=None, quan=None):
    # img_dct = np.zeros_like(img)
    # img_idct = np.zeros_like(img)

    h, w = img.shape
    if h % block != 0:
        h = int(np.ceil(h / block) * block)
    if w % block != 0:
        w = int(np.ceil(w / block) * block)
    img_padding = np.zeros((h, w))
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            img_padding[i, j] = img[i, j]

    himg = np.vsplit(img_padding, h // block)
    himg_dct, himg_idct = [], []
    for i in range(h // block):
        wimg = np.hsplit(himg[i], w // block)
        wimg_dct, wimg_idct = [], []
        for j in range(w // block):
            bimg = wimg[j]
            bimg_dct, bimg_idct = twoDCTBlock(bimg, sample, quan)
            wimg_dct.append(bimg_dct)
            wimg_idct.append(bimg_idct)
        himg_dct.append(np.hstack(wimg_dct))
        himg_idct.append(np.hstack(wimg_idct))

    img_dct = np.vstack(himg_dct)[:img.shape[0], :img.shape[1]]
    img_idct = np.vstack(himg_idct)[:img.shape[0], :img.shape[1]]
    psnr = PSNR(img, img_idct)
    return img_dct, img_idct, psnr

def quantization(dct, quan):
    qdct = np.zeros_like(dct)
    for i in range(qdct.shape[0]):
        for j in range(qdct.shape[1]):
            qdct[i, j] = int(np.round(dct[i, j] / quan[i, j]))
    return qdct

def dequantization(qdct, quan):
    dct = np.zeros_like(qdct)
    for i in range(dct.shape[0]):
        for j in range(dct.shape[1]):
            dct[i, j] = int(qdct[i, j] * quan[i, j])
    return dct

def cmpWithOrNotQuan(img, quan):

    # 2 dct without quan
    img_dct, img_idct, psnr = twoDCT(img, block=8)

    plt.figure(figsize=(20, 12))
    # plot source image
    plt.subplot(2, 3, 1)
    plt.title("Source Image")
    plt.imshow(img, cmap="gray")
    plt.xticks([])
    plt.yticks([])

    # plot 2 dct
    plt.subplot(2, 3, 2)
    plt.title("2DCT BLOCK=8*8 Quan=False")
    plt.imshow(np.log(abs(img_dct)))
    plt.xticks([])
    plt.yticks([])

    # plot 2 idct
    plt.subplot(2, 3, 3)
    plt.title("IDCT PSNR={:.2f}".format(psnr))
    plt.imshow(img_idct, cmap="gray")
    plt.xticks([])
    plt.yticks([])

    # 2 dct with quan
    img_dct, img_idct, psnr = twoDCT(img, block=8, quan=quan)

    # plot source image
    plt.subplot(2, 3, 4)
    plt.title("Source Image")
    plt.imshow(img, cmap="gray")
    plt.xticks([])
    plt.yticks([])

    # plot 2 dct
    plt.subplot(2, 3, 5)
    plt.title("2DCT BLOCK=8*8 Quan=True")
    plt.imshow(np.log(abs(img_dct)))
    plt.xticks([])
    plt.yticks([])

    # plot 2 idct
    plt.subplot(2, 3, 6)
    plt.title("IDCT PSNR={:.2f}".format(psnr))
    plt.imshow(img_idct, cmap="gray")
    plt.xticks([])
    plt.yticks([])

    plt.show()

def cmpAandQ(img, quans):
    psnr_Q = []
    psnr_Canon = []
    psnr_Nikon = []
    for i in np.arange(0.1, 2.1, 0.1):
        # Q
        quan = i * quans[0]
        img_dct, img_idct, psnr = twoDCT(img, block=8, quan=quan)
        psnr_Q.append(psnr)
        # Canon
        quan = i * quans[1]
        img_dct, img_idct, psnr = twoDCT(img, block=8, quan=quan)
        psnr_Canon.append(psnr)
        # Nikon
        quan = i * quans[2]
        img_dct, img_idct, psnr = twoDCT(img, block=8, quan=quan)
        psnr_Nikon.append(psnr)

    plt.plot(np.arange(0.1, 2.1, 0.1), psnr_Q, color='royalblue', label='Q')
    plt.plot(np.arange(0.1, 2.1, 0.1), psnr_Canon, color='cyan', label='Canon')
    plt.plot(np.arange(0.1, 2.1, 0.1), psnr_Nikon, color='orange', label='Nikon')
    plt.xlabel("a")
    plt.ylabel("PSNR")
    plt.legend()
    plt.show()

def cmpAndQShow(img, quans):

    plt.figure(figsize=(20, 12))
    for num, i in enumerate([0.5, 1.0, 1.5]):
        # Q
        quan = i * quans[0]
        img_dct, img_idct, psnr = twoDCT(img, block=8, quan=quan)
        plt.subplot(3, 6, num * 2 + 1)
        plt.title("DCT BLOCK=8*8 {:.1f}*Q".format(i))
        plt.imshow(np.log(abs(img_dct)))
        plt.xticks([])
        plt.yticks([])

        plt.subplot(3, 6, num * 2 + 2)
        plt.title("IDCT PSNR={:.2f}".format(psnr))
        plt.imshow(img_idct, cmap="gray")
        plt.xticks([])
        plt.yticks([])

        # Canon
        quan = i * quans[1]
        img_dct, img_idct, psnr = twoDCT(img, block=8, quan=quan)
        plt.subplot(3, 6, num * 2 + 7)
        plt.title("DCT BLOCK=8*8 {:.1f}*Canon".format(i))
        plt.imshow(np.log(abs(img_dct)))
        plt.xticks([])
        plt.yticks([])

        plt.subplot(3, 6, num * 2 + 8)
        plt.title("IDCT PSNR={:.2f}".format(psnr))
        plt.imshow(img_idct, cmap="gray")
        plt.xticks([])
        plt.yticks([])

        # Nikon
        quan = i * quans[2]
        img_dct, img_idct, psnr = twoDCT(img, block=8, quan=quan)
        plt.subplot(3, 6, num * 2 + 13)
        plt.title("DCT BLOCK=8*8 {:.1f}*Nikon".format(i))
        plt.imshow(np.log(abs(img_dct)))
        plt.xticks([])
        plt.yticks([])

        plt.subplot(3, 6, num * 2 + 14)
        plt.title("IDCT PSNR={:.2f}".format(psnr))
        plt.imshow(img_idct, cmap="gray")
        plt.xticks([])
        plt.yticks([])

    plt.show()

def main():
    parse = argparse.ArgumentParser(description='Image Compression')
    parse.add_argument('-i', dest='input', type=str, default='lena.png', help='input picture')
    args = parse.parse_args()

    if not args.input:
        print("Invalid input picture")
        exit(-1)

    # to gray
    img = cv2.imread(args.input).astype(np.int64)
    img_gray = bgr2gray(img)

    Q = np.asarray([
         [16, 11, 10, 16, 24, 40, 51, 61],
         [12, 12, 14, 19, 26, 58, 60, 55],
         [14, 13, 16, 24, 40, 57, 69, 56],
         [14, 17, 22, 29, 51, 87, 80, 62],
         [18, 22, 37, 56, 68, 109, 103, 77],
         [24, 35, 55, 64, 81, 104, 113, 92],
         [49, 64, 78, 87, 103, 121, 120, 101],
         [72, 92, 95, 98, 112, 100, 103, 99]
    ])

    Canon = np.asarray([
         [1, 1, 1, 2, 3, 6, 8, 10],
         [1, 1, 2, 3, 4, 8, 9, 8],
         [2, 2, 2, 3, 6, 8, 10, 8],
         [2, 2, 3, 4, 7, 12, 11, 9],
         [3, 3, 8, 11, 10, 16, 15, 11],
         [3, 5, 8, 10, 12, 15, 16, 13],
         [7, 10, 11, 12, 15, 17, 17, 14],
         [14, 13, 13, 15, 15, 14, 14, 14]
    ])

    Nikon = np.asarray([
         [2, 1, 1, 2, 3, 5, 6, 7],
         [1, 1, 2, 2, 3, 7, 7, 7],
         [2, 2, 2, 3, 5, 7, 8, 7],
         [2, 2, 3, 3, 6, 10, 10, 7],
         [2, 3, 4, 7, 8, 13, 12, 9],
         [3, 4, 7, 8, 10, 12, 14, 11],
         [6, 8, 9, 10, 12, 15, 14, 12],
         [9, 11, 11, 12, 13, 12, 12, 12]
    ])

    # task0
    cmpWithOrNotQuan(img_gray, Q)

    # task1
    quans = [Q, Canon, Nikon]
    cmpAandQ(img_gray, quans=quans)
    cmpAndQShow(img_gray, quans=quans)

if __name__ == '__main__':
    main()


import numpy as np

class MarkovChain(object):

    def __init__(self, states, t_matrix=None):
        self.states = states
        self.index_dict = {self.states[index]: index for index in range(len(self.states))}

        #If we have a transition matrix, we can add it here
        if t_matrix:
            self.transistion_matrix = t_matrix

    def _row_mod(self, row):
        if (sum(row) > 0):
            return row / sum(row)

    def generate_tmatrix(self, sequence):
        """
        Given a sequence, generate a transition matrix.
        sequence: list -> list of values to generate matrix from.
        """
        n = len(set(sequence))

        #To allow differnt types of data,
        #We need to setup a dittionary
        #That maps to each value within the sequence
        seq_dict = {j: i for i, j in zip(list(range(n)), set(sequence))}

        matrix = [[0] * n for _ in range(n)]

        for (i, j) in zip(sequence, sequence[1:]):
            matrix[seq_dict[i]][seq_dict[j]] += 1

        for row in matrix:

            s = sum(row)
            if s > 0:
                row = [f / s for f in row]

        #Set transistion matrix value here
        self.transistion_matrix = matrix

    def generate_tmatrix_2(self, sequence):
        """
        Given a sequence, generate a transition matrix.
        sequence: list -> list of values to generate matrix from.
        """
        n = len(set(sequence))

        #To allow differnt types of data,
        #We need to setup a dittionary
        #That maps to each value within the sequence
        seq_dict = {j: i for i, j in zip(list(range(n)), set(sequence))}

        matrix = np.array([[0] * n for _ in range(n)])

        for (i, j) in zip(sequence, sequence[1:]):
            matrix[seq_dict[i]][seq_dict[j]] += 1

        jeff = np.apply_along_axis(self._row_mod, 1, matrix)

        #Set transistion matrix value here
        self.transistion_matrix = jeff

    def normalize_weights(self, weights):
        """Python has a floating point problem,
        where not every number is equal to the int.
        eg. 0.1 + 0.2 + 0.7 = 1.000000001.

        To solve this, we can normalize the weights before we use them.
        """

        weights /= np.sum(weights)
        return weights

    def next_state(self, current_state):
        """Calculates and returns the next state in the markov chain."""
        weights = self.transistion_matrix[self.index_dict[current_state]]
        normalized = self.normalize_weights(weights)

        return np.random.choice(
            self.states,
            p=normalized
            )

    def generate_states(self, current_state, no=10):
        """
        Generate sequence on length no.
        """
        future = []
        for i in range(no):
            next_state = self.next_state(current_state)
            future.append(next_state)
            current_state = next_state

        return future

def generate_from_sequence(sequence):
    """
    If we do not know the states, and are only given a sequence,
    this function will transform the sequence into a Markov chain.

    sequence -> list: list containing possible states
    """
    states = set(sequence)
    chain = MarkovChain(list(states))
    chain.generate_tmatrix_2(sequence)

    return chain

if __name__ == "__main__":

    matrix = [[0.8, 0.19, 0.1],
              [0.2, 0.7, 0.1],
              [0.1, 0.2, 0.7]]

    chain = MarkovChain(["Sunny", "Rainy", "Snowy"], t_matrix=matrix)
    print(chain.next_state("Snowy"))

    sequence = chain.generate_states("Snowy", no=100)

    #newChain = MarkovChain(["Sunny", "Rainy", "Snowy"])
    #newChain.generate_tmatrix(sequence)
    newChain = generate_from_sequence(sequence)

    print(newChain.generate_states("Sunny", no=20))


"""
#########   Homework_5.3(a)  ################
"""

cur_b = 4 # The algorithm starts at b = 4
learning_rate = 0.01 # Learning rate
precision = 0.000001 #This tells us when to stop the algorithm
previous_step_size = 0.1 #
max_iCount = 10000 # maximum number of iterations
iCount = 0 #iteration counter

df = lambda b: 6*b - 5 #Gradient of our function

while iCount < max_iCount and precision < previous_step_size:
    prev_b = cur_b #Store current g value in prev_g
    cur_b = cur_b - learning_rate * df(prev_b) #Gradient descent
    previous_step_size = abs(cur_b - prev_b) #Change in b
    iCount = iCount + 1 #iteration count
    print("At iteration: ",iCount," b value :", cur_b) #Print iterations

print("The minimizer is : ", cur_b)

min_value = 3*cur_b**2 - 5*cur_b + 4
print("The minimum value = ", min_value)

"""
#########   Homework_5.5(a)  ################

"""

import numpy as np

X = [[180, 150], [150, 175], [170, 165]]
y = [[110], [140], [180]]

X_Transpose =np.transpose(X)
X_Transpose__X = np.matrix(X_Transpose)* np.matrix(X)

X_Transpose__X__Inv = np.linalg.inv(X_Transpose__X)
X_Transpose__X__Inv__X_Transpose = np.matrix(X_Transpose__X__Inv) * np.matrix(X_Transpose)

Beta = np.matrix(X_Transpose__X__Inv__X_Transpose) * np.matrix(y)

print("X:", X)
print("X_Transpose:", X_Transpose)

print("X_Transpose . X :", X_Transpose__X)

print("X_Transpose__X Inverse: ", X_Transpose__X__Inv)

print("X_Transpose__X__Inv . X_Transpose: ", X_Transpose__X__Inv__X_Transpose)

print("Beta", Beta)

x_feature = [[175, 170]]
y_predicted = np.matrix(x_feature) * np.matrix(Beta)

print("x_feature: ", x_feature)
print ("y_predicted: ", y_predicted)

"""
#########   Homework_5.5(b)  ################

"""

import numpy as np
from numpy import linalg as LA

X = [[1, 2], [3, 4]]
Norm_X = LA.norm(X)
print("X:", X)
print("Norm_X:", Norm_X)

import numpy as np
from numpy import linalg as LA

X = [[180, 150], [150, 175], [170, 165]]
y = [[110], [140], [180]]

X_Transpose =np.transpose(X)
X_Transpose__y = np.matrix(X_Transpose)* np.matrix(y)
Norm_X = LA.norm(X)
Beta = X_Transpose__y / (Norm_X * Norm_X)

print("X:", X)
print("Norm_X:", Norm_X)

print("X_Transpose:", X_Transpose)

print("X_Transpose . y :", X_Transpose__y)

print("Beta", Beta)

x_feature = [[175, 170]]
y_predictedValue =  (np.matrix(x_feature) * np.matrix(Beta) )

print("x_feature: ", x_feature)
print ("y_predictedValue: ", y_predictedValue)

Simple Linear Regression/Gradient Descent1.py


import itertools

import matplotlib.pyplot as plt
from matplotlib import cm
import networkx as nx
import numpy as np
from scipy.spatial import Voronoi

########################################################################################################################
#                                                                                #
#                   Trees                                                                                #
#                                                                                #
########################################################################################################################

def make_random_tree(num_nodes,
                     x_lim,
                     y_lim,
                     max_degree=4,
                     edge_stretch_interval=None,
                     seed=None
                     ):
    """
    Create a tree from random points in a rectangle by using
    Kruskal on complete graph with Euclidean edge weights.
    Each node can have at most a degree of max_degree.
    """
    if seed is not None:
        np.random.seed(seed)

    points = make_random_points(num_nodes, x_lim, y_lim)
    dist = {(i, j): np.linalg.norm(points[i] - points[j]) for i, j in itertools.combinations(range(len(points)), r=2)}
    tree = max_degree_kruskal(dist, max_degree)
    pos = {i: list(p) for i, p in enumerate(points)}
    nx.set_node_attributes(tree, pos, name='pos')

    # stretch edge lengths
    ####################################
    if edge_stretch_interval is not None:
        random_stretch_edge_lengths(tree, stretch_interval=edge_stretch_interval, seed=seed)

    return tree

def max_degree_kruskal(dist, max_degree):
    """
    adjusted from networkx for max_degree
    """
    subtrees = nx.utils.UnionFind()
    edges = sorted((d, e[0], e[1]) for e, d in dist.items())
    tree = nx.Graph()
    max_degree_nodes = set()
    for wt, u, v in edges:
        if u in max_degree_nodes or v in max_degree_nodes:
            continue
        if subtrees[u] != subtrees[v]:
            tree.add_edge(u, v, length=round(wt, 2))
            for node in [u, v]:
                if tree.degree[node] == max_degree:
                    max_degree_nodes.add(node)
            subtrees.union(u, v)
    return tree

########################################################################################################################
#                                                                                #
#                   Voronoi graphs                                                                                #
#                                                                                #
########################################################################################################################

def make_voronoi_graph(num_voronoi_points,
                       x_lim,
                       y_lim,
                       random_leaves_frac=0.4,
                       num_nodes=None,
                       edge_stretch_interval=None,
                       seed=None,
                       ):
    """
    Create a Voronoi diagram and consider the graph that is spanned by the ridges between
    the Voronoi cells. The is our basic Voronoi graph with Euclidean edge lengths. Now,
    we can add additional random leaves (according to random_leaves_frac), split the edges
    to obtain a specified number of nodes, or stretch each edge by a random factor.

    :param num_voronoi_points:
        int: number of points to build Voronoi regions
    :param x_lim:
        float: upper x-range bound for box in which we generate Voronoi points (lower bound=0)
    :param y_lim:
        float: upper y-range bound for box in which we generate Voronoi points (lower bound=0)
    :param random_leaves_frac:
        float: add this percentage of additional leaves to the voronoi graph
    :param num_nodes:
        int (or None): split edges such that the returned graph has num_nodes many nodes
    :param edge_stretch_interval:
        tuple of length 2 (or None): choose random stretch factor in this interval for each edge
    :param seed:
        random seed for reproducible results
    :return: networkx.Graph
    """
    if seed is not None:
        np.random.seed(seed)

    # basic Voronoi graph
    ####################################
    points = make_random_points(num_voronoi_points, x_lim, y_lim)
    vor = Voronoi(points)

    graph = nx.Graph()
    in_box_vertices = {i: v for i, v in enumerate(vor.vertices) if all(v >= (0, 0)) and all(v <= (x_lim, y_lim))}
    in_box_edges = [(u, v) for u, v in vor.ridge_vertices if u in in_box_vertices and v in in_box_vertices]
    my_edges = [(u, v, round(np.linalg.norm(in_box_vertices[u] - in_box_vertices[v]), 2)) for u, v in in_box_edges]
    graph.add_weighted_edges_from(my_edges, weight='length')
    nx.set_node_attributes(graph, in_box_vertices, name='pos')
    graph = nx.convert_node_labels_to_integers(graph)

    # additional leaves
    ####################################
    if random_leaves_frac:
        _num_nodes = graph.number_of_nodes()
        for i in range(int(_num_nodes * random_leaves_frac)):
            u = _num_nodes + i + 1
            u_pos = make_random_points(1, x_lim, y_lim)[0]
            v, d = find_nearest_node_without_intersection(graph, u_pos)
            graph.add_node(u, pos=u_pos)
            graph.add_edge(u, v, length=round(d, 2))

    # split edges for given num_nodes
    ####################################
    if num_nodes is not None:
        num_splits = num_nodes - graph.number_of_nodes()
        split_edges(graph=graph, num_splits=num_splits)

    # stretch edge lengths
    ####################################
    if edge_stretch_interval is not None:
        random_stretch_edge_lengths(graph, stretch_interval=edge_stretch_interval, seed=seed)

    return graph

########################################################################################################################
#                                                                                #
#                   Two-level Voronoi networks                                                                         #
#                                                                                #
########################################################################################################################

def make_two_level_voronoi_graph(n_level_1_voronoi_points,
                                 n_level_2_voronoi_points,
                                 x_lim,
                                 y_lim,
                                 random_leaves_frac_l1=0,
                                 random_leaves_frac_l2=0.6,
                                 n_level_1_nodes=None,
                                 num_nodes=None,
                                 edge_stretch_interval=None,
                                 length_factor_level_1=0.5,
                                 seed=None,
                                 ):
    """
    The two level network consists of two nested Voronoi graphs. The level 1 layer is a
    priority network where travelling is generally faster (adjust with length_factor_level_1).
    Therefore, an additional edge attribute 'weight' is introduced.

    :param n_level_1_voronoi_points:
        int: number of points to build level 1 Voronoi regions
    :param n_level_2_voronoi_points:
        int: number of points to build level 2 Voronoi regions
    :param x_lim:
        loat: upper x-range bound for box in which we generate Voronoi points (lower bound=0)
    :param y_lim:
        float: upper y-range bound for box in which we generate Voronoi points (lower bound=0)
    :param random_leaves_frac_l1:
        float: add this percentage of additional leaves to the level 1 voronoi graph
    :param random_leaves_frac_l2:
        float: add this percentage of additional leaves to the level 2 voronoi graph
    :param n_level_1_nodes:
        int (or None): split edges such that the level 1 graph has num_nodes many nodes
    :param num_nodes:
        int (or None): split edges such that the returned graph has num_nodes many nodes
    :param edge_stretch_interval:
        tuple of length 2 (or None): choose random stretch factor in this interval for each edge
    :param length_factor_level_1:
        float: travelling on level 1 graph is faster by this factor (a new edge attribute 'weight'
        is introduced)
    :param seed:
        random seed for reproducible results
    :return: networkx.Graph
    """

    level_1_layer = make_voronoi_graph(num_voronoi_points=n_level_1_voronoi_points, x_lim=x_lim, y_lim=y_lim,
                                       random_leaves_frac=random_leaves_frac_l1, num_nodes=None,
                                       edge_stretch_interval=None, seed=seed)
    level_2_layer = make_voronoi_graph(num_voronoi_points=n_level_2_voronoi_points, x_lim=x_lim, y_lim=y_lim,
                                       random_leaves_frac=random_leaves_frac_l2, num_nodes=None,
                                       edge_stretch_interval=None, seed=seed)

    # map level_1_layer to level_2_layer
    ####################################
    v2_list, pos2_list = zip(*level_2_layer.nodes(data='pos'))
    l1_to_l2_nodes = {}
    for v1, pos1 in level_1_layer.nodes(data='pos'):
        i = find_closest_point(pos1, pos2_list)
        l1_to_l2_nodes[v1] = v2_list[i]

    level_1_edges = set()
    for u, v in level_1_layer.edges:
        sh_path = nx.dijkstra_path(level_2_layer, l1_to_l2_nodes[u], l1_to_l2_nodes[v], weight='length')
        for e in zip(sh_path[:-1], sh_path[1:]):
            level_1_edges.add(tuple(sorted(e)))
    level_2_edges = set(e for e in level_2_layer.edges if e not in level_1_edges)

    two_level_network = level_2_layer
    level = {e: 1 + int(e in level_2_edges) for e in two_level_network.edges}
    nx.set_edge_attributes(two_level_network, level, name='level')

    # split level 1 edges
    ####################################
    if n_level_1_nodes is not None:
        num_splits = n_level_1_nodes - len(set(l1_to_l2_nodes.values()))
        split_edges(graph=two_level_network, num_splits=num_splits, no_split_edges=level_2_edges)

    # travelling on level_1_graph is faster
    ####################################
    weight = {(u, v): l * length_factor_level_1 if (u, v) in level_1_edges else l
              for u, v, l in two_level_network.edges(data='length')}
    nx.set_edge_attributes(two_level_network, weight, name='weight')

    # split level 2 edges
    ####################################
    if num_nodes is not None:
        num_splits = num_nodes - two_level_network.number_of_nodes()
        no_split_edges = [e for e in two_level_network.edges if two_level_network.edges[e]['level'] == 1]
        split_edges(graph=two_level_network, num_splits=num_splits, no_split_edges=no_split_edges)

    # stretch edge lengths
    ####################################
    if edge_stretch_interval is not None:
        random_stretch_edge_lengths(two_level_network, stretch_interval=edge_stretch_interval, seed=seed)

    return two_level_network

########################################################################################################################
#                                                                                #
#                   general functions                                                                                #
#                                                                                #
########################################################################################################################

def make_random_graph(graph_params, seed=None):
    """
    Detect based on graph_params, which graph should be built and call the respective function.
    """
    make_graph_function = {
        'tree': make_random_tree,
        'voronoi': make_voronoi_graph,
        'two_level_voronoi': make_two_level_voronoi_graph
    }
    variant = graph_params['variant']
    assert variant in make_graph_function
    graph_params = {k: v for k, v in graph_params.items() if k != 'variant'}
    graph = make_graph_function[variant](**graph_params, seed=seed)
    pos = {v: [round(p[0], 4), round(p[1], 4)] for v, p in graph.nodes(data='pos')}
    nx.set_node_attributes(graph, pos, 'pos')

    return graph

def make_random_points(num_points, x_lim, y_lim):
    points = []
    for i in range(num_points):
        pos_x = round(np.random.random() * x_lim, 4)
        pos_y = round(np.random.random() * y_lim, 4)
        points.append((pos_x, pos_y))
    points = np.array(points)
    return points

def find_nearest_node_without_intersection(graph, u_pos):
    """
    Return the nearest node in graph to u_pos, such that the
    direct line between the two does not intersect any edge
    of the graph.
    """

    def ccw(A, B, C):
        """
        code from: https://stackoverflow.com/questions/3838329/how-can-i-check-if-two-segments-intersect
        (ccw stands for counterclockwise)
        """
        x, y = 0, 1
        return (C[y] - A[y]) * (B[x] - A[x]) > (B[y] - A[y]) * (C[x] - A[x])

    def intersect(A, B, C, D):
        """
        Returns true if line segments AB and CD intersect.
        code from: https://stackoverflow.com/questions/3838329/how-can-i-check-if-two-segments-intersect
        """
        return ccw(A, C, D) != ccw(B, C, D) and ccw(A, B, C) != ccw(A, B, D)

    pos = dict(graph.nodes(data='pos'))
    dists = sorted([(np.linalg.norm(pos[v] - u_pos), v) for v in graph.nodes])
    while True:
        dist, v = dists.pop(0)
        for a, b in graph.edges:
            if a == v or b == v:
                continue
            if intersect(pos[a], pos[b], pos[v], u_pos):
                break
        return v, dist

def find_closest_point(point, other_points):
    """
    finds the index of the point in other_points closest to given point
    """
    dist = [(np.linalg.norm(point - p), i) for i, p in enumerate(other_points)]
    closest_point = min(dist)[1]
    return closest_point

def split_edges(graph, num_splits, no_split_edges=None):
    """
    This function splits the edges (except for no_split_edges) in graph num_splits times.
    Therefore, we store in lens_edge_nsplit a triple (l, e, n_split) for every
    possible split_edge. Here, e is the edge which we cut into n_split many equal
    length pieces, and l indicates the length of any of the pieces.
    Once we decide to split an edge, the new triple is
        (l * n_split / (n_split + 1), e, n_split + 1).
    """
    if no_split_edges is None:
        no_split_edges = []

    lens_edge_nsplit = sorted([(graph.edges[e]['length'], e, 1) for e in graph.edges() if e not in no_split_edges])

    for _ in range(num_splits):
        l, e, num = lens_edge_nsplit.pop()
        new_triple = (l * num / (num + 1), e, num + 1)
        lens_edge_nsplit.append(new_triple)
        lens_edge_nsplit.sort()

    next_node_id = max(graph.nodes) + 1
    for length, e, num in lens_edge_nsplit:
        if num > 1:
            # edge e is replaced by num edges
            e_dict = graph.edges[e]
            graph.remove_edge(*e)
            e_dict['length'] = round(length, 2)
            last_v = e[0]
            pos_u = np.array(graph.nodes[e[0]]['pos'])
            pos_v = np.array(graph.nodes[e[1]]['pos'])
            delta = (pos_v - pos_u) / num
            for i in range(num - 1):
                i_pos = list(pos_u + delta * (i + 1))
                graph.add_node(next_node_id, pos=i_pos)
                graph.add_edge(last_v, next_node_id, **e_dict)
                last_v = next_node_id
                next_node_id += 1
            graph.add_edge(last_v, e[1], **e_dict)

def random_stretch_edge_lengths(graph, edge_list=None, stretch_interval=(1, 1.5), seed=None):

    if seed is not None:
        np.random.seed(seed)
    if edge_list is None:
        edge_list = graph.edges()

    for e in edge_list:
        stretch_factor = np.random.uniform(*stretch_interval)
        graph.edges[e]['length'] = round(graph.edges[e]['length'] * stretch_factor, 2)
    return

def draw_graph(graph, traffic=False):
    pos = nx.get_node_attributes(graph, 'pos')
    nx.draw_networkx_nodes(graph, pos=pos, node_size=6, node_color='k')
    nx.draw_networkx_edges(graph, pos=pos, width=2)
    if traffic:
        edge_colors = [graph.edges[e]['traffic'] for e in graph.edges]
        edges = nx.draw_networkx_edges(graph, pos=pos, edge_color=edge_colors,
                                       edge_cmap=cm.get_cmap('rainbow'), width=4)
        plt.colorbar(edges)

    plt.axis('off')
    plt.show()

import numpy,from scipy,import networkx,from networkx


import numpy as np

#progressive averaging
def prog_avg(x):
    s = np.cumsum(x, dtype=float)
    return np.array([s[i]/(i+1) for i in range(len(x))])

#sliding averaging
def win_avg(x, w, compensate=False):
    cum_sum = np.cumsum(x, dtype=float)
    valid_y = np.true_divide(np.subtract(cum_sum[w:], cum_sum[:-w]), float(w))
    if compensate:
        begin_y = np.true_divide(cum_sum[:w], range(1,w+1))
    else:
        begin_y = np.true_divide(cum_sum[:w], float(w))
    return np.append(begin_y, valid_y)


import math
import numpy as np

ChebPoly = {'T0': lambda x: 1,
            'T1': lambda x: x,
            'T2': lambda x: 2 * (x**2) - 1,
            'T3': lambda x: 4 * (x**3) - 3 * x,
            'T4': lambda x: 8 * (x**4) - 8 * (x**2) + 1}

def mexhat(x, alpha=0.01):
    return np.exp(-alpha * x * x) * np.cos(x)

# -- Decaying cosine
def decaying_cos(x, alpha=0.01):
    return np.exp(-alpha * x) * np.cos(x * x)
    # return np.cos(x)

# -- Runge's function
def runge(x):
    return 1 / (1 + 25.0 * x * x)

# -- Square-root cusp
def sqcusp(x):
    return math.sqrt(math.fabs(x)) ** (2.0 / 5.0)

# -- Quadratic with two small real roots
def quad(x):
    return (x - 1.0) ** 2 - 0.000001

# -- Useful for testing corner cases
def lin(x):
    return x

def expf(x):
    return np.exp(x)

def g(x):
    return np.sin(x) + 0.1 * np.sin(10*x)

def h(x):
    return 0.1 * x * np.exp(0.01 * x) * np.sin(x) + 0.1 * np.sin(10*x)

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


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

import math
import numpy as np

def reglineal(y,x,n):
	A=0.0
	B=0.0
	C=0.0
	D=0.0

	for i in range(0,n):
		A=A+x[i]
		B=B+y[i]
		C=C+x[i]*y[i]
		D=D+x[i]*x[i]

	if(A*A-n*D)==0:
		print("La pendiente es indeterminada. Los datos se ajustan a una recta de la familia x=a")
		sys.exit()
	else:
		m=(A*B-n*C)/(A*A-n*D)
		b=(B-m*A)/n
	sol=[m,b]
	return sol

def regcuad(y,x,n):
	A=0.0
	B=0.0
	C=0.0
	D=0.0
	E=0.0
	F=0.0
	G=0.0

	for i in range(0,n):
		A=A+x[i]
		B=B+y[i]
		C=C+x[i]*y[i]
		D=D+x[i]*x[i]
		E=E+x[i]*x[i]*y[i]
		F=F+x[i]*x[i]*x[i]
		G=G+x[i]*x[i]*x[i]*x[i]
	den=(n*D-A*A)*(n*G-D*D)-(n*F-D*A)**2.0

	if den==0:
		print("Los valores son indeterminados. Los datos no se ajustan a un polinomio grado 2")
		sys.exit()
	else:
		#Aquí esta el error en cc en (F-D*A*ni) en el tablero puse (F-D*B*ni). En teoría debería darnos los mismos valores 			si cambio ese valor.
		ac = ((n*D-A*A)*(n*E-D*B)-(n*F-D*A)*(n*C-A*B))/den
		bc =((n*C-A*B)*(n*G-D*D)-(n*E-D*B)*(n*F-D*A))/den
		cc =(B-bc*A-ac*D)/n

	sol=[ac,bc,cc]
	return sol


import numpy as np

class GMM:
    """
        This class is the implementation of the Gaussian Mixture Models
        inspired by sci-kit learn implementation.
    """

    def __init__(self, n_components, max_iter=100, comp_names=None):
        """
            This functions initializes the model by seting the following paramenters:
                :param n_components: int
                    The number of clusters in which the algorithm must split
                    the data set
                :param max_iter: int, default = 100
                    The number of iteration that the algorithm will go throw to find the clusters
                :param comp_names: list of strings, default=None
                    In case it is setted as a list of string it will use to
                    name the clusters
        """
        self.n_componets = n_components
        self.max_iter = max_iter
        if comp_names == None:
            self.comp_names = [f"comp{index}" for index in range(self.n_componets)]
        else:
            self.comp_names = comp_names
        # pi list contains the fraction of the dataset for every cluster
        self.pi = [1 / self.n_componets for comp in range(self.n_componets)]

    def multivariate_normal(self, X, mean_vector, covariance_matrix):
        """
            This function implements the multivariat normal derivation formula,
            the normal distribution for vectors it requires the following parameters
                :param X: 1-d numpy array
                    The row-vector for which we want to calculate the distribution
                :param mean_vector: 1-d numpy array
                    The row-vector that contains the means for each column
                :param covariance_matrix: 2-d numpy array (matrix)
                    The 2-d matrix that contain the covariances for the features
        """
        return (2 * np.pi) ** (-len(X) / 2) * np.linalg.det(covariance_matrix) ** (-1 / 2) * np.exp(
            -np.dot(np.dot((X - mean_vector).T, np.linalg.inv(covariance_matrix)), (X - mean_vector)) / 2)

    def fit(self, X):
        """
            The function for training the model
                :param X: 2-d numpy array
                    The data must be passed to the algorithm as 2-d array,
                    where columns are the features and the rows are the samples
        """
        # Spliting the data in n_componets sub-sets
        new_X = np.array_split(X, self.n_componets)
        # Initial computation of the mean-vector and covarience matrix
        self.mean_vector = [np.mean(x, axis=0) for x in new_X]
        self.covariance_matrixes = [np.cov(x.T) for x in new_X]
        # Deleting the new_X matrix because we will not need it anymore
        del new_X
        for iteration in range(self.max_iter):
            """ --------------------------   E - STEP   -------------------------- """
            # Initiating the r matrix, evrey row contains the probabilities
            # for every cluster for this row
            self.r = np.zeros((len(X), self.n_componets))
            # Calculating the r matrix
            for n in range(len(X)):
                for k in range(self.n_componets):
                    self.r[n][k] = self.pi[k] * self.multivariate_normal(X[n], self.mean_vector[k],
                                                                         self.covariance_matrixes[k])
                    self.r[n][k] /= sum(
                        [self.pi[j] * self.multivariate_normal(X[n], self.mean_vector[j], self.covariance_matrixes[j])
                         for j in range(self.n_componets)])
            # Calculating the N
            N = np.sum(self.r, axis=0)
            """ --------------------------   M - STEP   -------------------------- """
            # Initializing the mean vector as a zero vector
            self.mean_vector = np.zeros((self.n_componets, len(X[0])))
            # Updating the mean vector
            for k in range(self.n_componets):
                for n in range(len(X)):
                    self.mean_vector[k] += self.r[n][k] * X[n]
            self.mean_vector = [1 / N[k] * self.mean_vector[k] for k in range(self.n_componets)]
            # Initiating the list of the covariance matrixes
            self.covariance_matrixes = [np.zeros((len(X[0]), len(X[0]))) for k in range(self.n_componets)]
            # Updating the covariance matrices
            for k in range(self.n_componets):
                self.covariance_matrixes[k] = np.cov(X.T, aweights=(self.r[:, k]), ddof=0)
            self.covariance_matrixes = [1 / N[k] * self.covariance_matrixes[k] for k in range(self.n_componets)]
            # Updating the pi list
            self.pi = [N[k] / len(X) for k in range(self.n_componets)]

    def predict(self, X):
        """
            The predicting function
                :param X: 2-d array numpy array
                    The data on which we must predict the clusters
        """
        probas = []
        for n in range(len(X)):
            probas.append([self.multivariate_normal(X[n], self.mean_vector[k], self.covariance_matrixes[k])
                           for k in range(self.n_componets)])
        cluster = []
        for proba in probas:
            cluster.append(self.comp_names[proba.index(max(proba))])
        return cluster


import matplotlib.pyplot as plt #pip install matplotlib
import math
import numpy as np #pip install numpy
import statistics
theta=0.5 # mean of the function

u=[] #to fill with 100000 values from linear congruence generator
def gen_lincong_foru():
    #X(i+1)=X(i)A mod M and U(i+1)=X(i+1)/M -> Linear Congruence Generator
    x=23 # x0 value
    m=4096
    a=17
    for i in range(0,100000):
        u.append(x/m)
        t=(a*x+1)%m #b=1
        x=t

x=[] #to get x from the inverse of exponential distribution
def gen_xfromu():
    #X=-(theta)log(1-U) -> Inverse of exponential distribution
    for i in range(0,100000):
        t=(-theta)*(math.log(1-u[i]))
        x.append(t)

fx=[]
def gen_fx_fromx():
    #F(x)=1-e^(-x/theta)
    for i in range(0,100000):
        t=1-math.exp(-x[i]/theta)
        fx.append(t)

def plot_xvsu(l):
    #take 0.05 interval length and plot X vs [0,5]
    x_cor=[] #will have the midpoint of that interval
    y_cor=[] #will have the freq of x belonging to that interval
    ini=0
    freq=0
    while(ini<=5):
        x_cor.append(ini+0.05/2)
        for i in range(0,len(l)):
            if(l[i]>=ini and l[i]<=ini+0.05):
                freq=freq+1
        y_cor.append(freq)
        ini+=0.05
    s_mean='Sample mean= '+str(np.mean(l)) #sample mean
    s_var='Sample variance= '+str(statistics.variance(l)) #sample var
    plt.plot(x_cor,y_cor)
    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.title(s_mean+'\n'+s_var+'\nDistribution function for '+str(len(l))+' generated values of X')
    plt.show()

def plot_xvsu_fr(l):
    #take 0.05 interval length and plot X vs [0,5]
    x_cor=[] #will have the midpoint of that interval
    y_cor=[] #will have the freq of x belonging to that interval
    ini=0
    while(ini<=5):
        freq=0
        x_cor.append(ini+0.05/2)
        for i in range(0,len(l)):
            if(l[i]>=ini and l[i]<=ini+0.05):
                freq=freq+1
        y_cor.append(freq)
        ini+=0.05
    plt.plot(x_cor,y_cor)
    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.title('Frequency vs Interval for '+str(len(l))+' generated values of X')
    plt.show()

def plot_fxvsx(no_values):
    x_cor=[] #will have the value of x generated
    y_cor=[] #will have the corresponding value of fx
    for i in range(0,no_values):
        x_cor.append(x[i])
        y_cor.append(fx[i])
    plt.scatter(x_cor,y_cor)
    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.title('Distribution function f(x) '+str(no_values)+' generated values of X')
    plt.show()

print('Actual Mean= '+str(theta))
print('Actual Variance= '+str(theta*theta))
gen_lincong_foru()
gen_xfromu()
gen_fx_fromx()
plot_xvsu(x[0:100])
plot_xvsu_fr(x[0:100])
plot_fxvsx(100)
plot_xvsu(x[0:1000])
plot_xvsu_fr(x[0:1000])
plot_fxvsx(1000)
plot_xvsu(x[0:10000])
plot_xvsu_fr(x[0:10000])
plot_fxvsx(10000)
plot_xvsu(x[0:100000])
plot_xvsu_fr(x[0:100000])
plot_fxvsx(100000)

Semester 5/MA 323 (Monte Carlo)/Lab Submissions/Lab 2/180123062_ABSatyaprakash_q2.py


import numpy as np # import Numpy library to generate

weights = np.around(np.random.uniform(size=6), decimals=2) # initialize the weights
biases = np.around(np.random.uniform(size=3), decimals=2) # initialize the biases

print(weights)
print(biases)

x_1 = 0.5 # input 1
x_2 = 0.85 # input 2

print('x1 is {} and x2 is {}'.format(x_1, x_2))

z_11 = x_1 * weights[0] + x_2 * weights[1] + biases[0]

print('The weighted sum of the inputs at the first node in the hidden layer is {}'.format(z_11))

z_12 = x_1 * weights[2] + x_2 * weights[3] + biases[1]

print('The weighted sum of the inputs at the second node in the hidden layer is {}'.format(np.around(z_12, decimals=4)))

a_11 = 1.0 / (1.0 + np.exp(-z_11))

print('The activation of the first node in the hidden layer is {}'.format(np.around(a_11, decimals=4)))

a_12 = 1.0 / (1.0 + np.exp(-z_12))

print('The activation of the second node in the hidden layer is {}'.format(np.around(a_12, decimals=4)))

z_2 = a_11 * weights[4] + a_12 * weights[5] + biases[2]

print('The weighted sum of the inputs at the node in the output layer is {}'.format(np.around(z_2, decimals=4)))

a_2 = 1.0 / (1.0 + np.exp(-z_2))

print('The output of the network for x1 = 0.5 and x2 = 0.85 is {}'.format(np.around(a_2, decimals=4)))

n = 2 # number of inputs
num_hidden_layers = 2 # number of hidden layers
m = [2, 2] # number of nodes in each hidden layer
num_nodes_output = 1 # number of nodes in the output layer

import numpy as np  # import the Numpy library

num_nodes_previous = n  # number of nodes in the previous layer

network = {}  # initialize network an an empty dictionary

# loop through each layer and randomly initialize the weights and biases associated with each node
# notice how we are adding 1 to the number of hidden layers in order to include the output layer
for layer in range(num_hidden_layers + 1):

    # determine name of layer
    if layer == num_hidden_layers:
        layer_name = 'output'
        num_nodes = num_nodes_output
    else:
        layer_name = 'layer_{}'.format(layer + 1)
        num_nodes = m[layer]

    # initialize weights and biases associated with each node in the current layer
    network[layer_name] = {}
    for node in range(num_nodes):
        node_name = 'node_{}'.format(node + 1)
        network[layer_name][node_name] = {
            'weights': np.around(np.random.uniform(size=num_nodes_previous), decimals=2),
            'bias': np.around(np.random.uniform(size=1), decimals=2),
        }

    num_nodes_previous = num_nodes

print(network)  # print network

def initialize_network(num_inputs, num_hidden_layers, num_nodes_hidden, num_nodes_output):
    num_nodes_previous = num_inputs  # number of nodes in the previous layer

    network = {}

    # loop through each layer and randomly initialize the weights and biases associated with each layer
    for layer in range(num_hidden_layers + 1):

        if layer == num_hidden_layers:
            layer_name = 'output'  # name last layer in the network output
            num_nodes = num_nodes_output
        else:
            layer_name = 'layer_{}'.format(layer + 1)  # otherwise give the layer a number
            num_nodes = num_nodes_hidden[layer]

            # initialize weights and bias for each node
        network[layer_name] = {}
        for node in range(num_nodes):
            node_name = 'node_{}'.format(node + 1)
            network[layer_name][node_name] = {
                'weights': np.around(np.random.uniform(size=num_nodes_previous), decimals=2),
                'bias': np.around(np.random.uniform(size=1), decimals=2),
            }

        num_nodes_previous = num_nodes

    return network  # return the network

def compute_weighted_sum(inputs, weights, bias):
    return np.sum(inputs * weights) + bias

from random import seed
import numpy as np

np.random.seed(12)
inputs = np.around(np.random.uniform(size=5), decimals=2)

print('The inputs to the network are {}'.format(inputs))

def node_activation(weighted_sum):
    return 1.0 / (1.0 + np.exp(-1 * weighted_sum))

def forward_propagate(network, inputs):
    layer_inputs = list(inputs)  # start with the input layer as the input to the first hidden layer

    for layer in network:

        layer_data = network[layer]

        layer_outputs = []
        for layer_node in layer_data:
            node_data = layer_data[layer_node]

            # compute the weighted sum and the output of each node at the same time
            node_output = node_activation(compute_weighted_sum(layer_inputs, node_data['weights'], node_data['bias']))
            layer_outputs.append(np.around(node_output[0], decimals=4))

        if layer != 'output':
            print('The outputs of the nodes in hidden layer number {} is {}'.format(layer.split('_')[1], layer_outputs))

        layer_inputs = layer_outputs  # set the output of this layer to be the input to next layer

    network_predictions = layer_outputs
    return network_predictions

my_network = initialize_network(5, 3, [2, 3, 2], 3)

inputs = np.around(np.random.uniform(size=5), decimals=2)

predictions = forward_propagate(my_network, inputs)
print('The predicted values by the network for the given input are {}'.format(predictions))


""""
Function  : area_triangle_3d.py
Title     : Triangle area 3D
Written by: Alejandro Rios
Date      : November/2019
Last edit : August/2020
Language  : Python
Aeronautical Institute of Technology - Airbus Brazil

Description:
    - This module calculate the area triangle for applications in 3D surfaces area calculation

Future implementations:
    -

Inputs:
    - Triangle coordinates x, y, z
Outputs:
    - area
"""
########################################################################################
"""Importing Modules"""
########################################################################################
########################################################################################
"""Function definition"""
########################################################################################

import numpy as np
def area_triangle_3d(x, y, z):

    x1 = x[0]
    x2 = x[1]
    x3 = x[2]
    #
    y1 = y[0]
    y2 = y[1]
    y3 = y[2]
    #
    z1 = z[0]
    z2 = z[1]
    z3 = z[2]
    # A=sqrt(s*(s-a)*(s-b)*(s-c))
    # Method  2
    # https://math.stackexchange.com/questions/128991/how-to-calculate-area-of-3d-triangle
    T1 = (x2*y1 - x3*y1 - x1*y2 + x3*y2 + x1*y3 - x2*y3)**2
    T2 = (x2*z1 - x3*z1 - x1*z2 + x3*z2 + x1*z3 - x2*z3)**2
    T3 = (y2*z1 - y3*z1 - y1*z2 + y3*z2 + y1*z3 - y2*z3)**2
    area = (np.sqrt(T1+T2+T3)/2)
    return(area)


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

import numpy as np
import commonlibs.math.vectors as m

UNIT_X = [1, 0, 0]
SOME_VECTOR = [55, 89.33, 190.03]

def test_unit_vector():
    """
    Test unit vector function
    """

    # Assert vector have length 1
    for v in [UNIT_X, SOME_VECTOR]:
        v = m.unit_vector(v)
        assert np.linalg.norm(v) == 1

    # Assert list/numpy arrays produce same output
    v_from_list = m.unit_vector(list(SOME_VECTOR))
    v_from_np = m.unit_vector(np.array(SOME_VECTOR))
    assert np.testing.assert_array_equal(v_from_list, v_from_np) is None

def test_angle_between():
    """
    Test function to determine angle between vectors
    """

    assert m.angle_between((1, 0, 0), (0, 1, 0)) == 1.5707963267948966
    assert m.angle_between((1, 0, 0), (1, 0, 0)) == 0.0
    assert m.angle_between((1, 0, 0), (-1, 0, 0)) == 3.141592653589793


'''
本模块储存着一些常用的常数

This module contents some usual constants

数学 / math

chaos_1: 第一费根鲍姆常数 / the first Feigenbaum constant
chaos_2: 第二费根鲍姆常数 / the second Feigenbaum constant
e: 自然常数 / natural constant
gamma: 欧拉-马歇罗尼常数 / Euler-Mascheroni constant
golden: 黄金比例 / golden ratio
K: 兰道-拉马努金常数 / Landau–Ramanujan constant
K0: 辛钦常数 / Khintchine constant
pi: 圆周率 / Ratio of circumference to diameter
lp(): 勒让德多项式 / Legendre Polynomials
lpn()：勒让德多项式的模 / Module of Legendre Polynomials
alp()：连带勒让德多项式 / Associated Legendre Polynomials
alpn()：连带勒让德多项式的模 / Module of Associated Legendre Polynomials

物理 / physics

AU: 天文单位 / astronomical unit
atm: 标准大气压 / standard atmospheric pressure
c: 真空光速 / the speed of light in vacuum
c_e: 元电荷 / elementary charge
epsilon: 真空介电常数 / permittivity of vacuum
g: 重力加速度 / gravitational acceleration
G: 万有引力常数 / gravitational constant
h: 普朗克常数 / Planck constant
hr: 约化普朗克常数 / reduced Planck constant
k: 玻尔兹曼常数 / Boltzmann constant
lambdac: 康普顿波长 / Compton wavelength
ly: 光年 / light-year
m_e: 电子质量 / mass of electron
m_earth: 地球质量 / mass of the earth
m_n: 中子质量 / mass of neutron
m_p: 质子质量 / mass of proton
m_s: 太阳质量 / mass of the sun
miu: 真空磁导率 / permeability of vacuum
NA: 阿伏伽德罗常数 / Avogadro constant
pc: 秒差距 / Parsec
Platonic_year: 柏拉图年 / Platonic year
R: 理想气体常数 / gas constant
r_earth: 地球平均半径 / mean radius of the earth
r_sun: 太阳平均半径 / mean radius of the sun
r_e_m: 地月平均距离 / mean distance between the earth and the moon
SB: 斯特藩-玻尔兹曼常量 / Stefan-Boltzmann constant
v1: 第一宇宙速度 / first cosmic velocity
v2: 第二宇宙速度 / second cosmic velocity
v3: 第三宇宙速度 / third cosmic velocity
hp()：厄米多项式 / Hermite Polynomials
v_mean()：麦克斯韦速率分布律下的平均速率
v_p()：麦克斯韦速率分布律下的最概然速率
v_rms()：麦克斯韦速率分布律下的均方根速率
'''
import numpy as np
from scipy.special import factorial

# math
pi = 3.141592653589793
e = 2.718281828459045
golden = 1.618033988749895
gamma = 0.57721566490153286060651209
K = 0.76422365358922066299069873125
chaos_1 = 4.669201609102990
chaos_2 = 2.502907875095892
K0 = 2.6854520010

def lpn(l):
    '''
    l阶勒让德多项式的模

    参数
    ----
    l：整型，阶数

    返回
    ----
    数类型

    Module of Legendre Polynomials at degree l

    Paramater
    ---------
    l: int, degree

    Return
    ------
    num
    '''
    return (2 / (2 * l + 1))**0.5

def lp(x, l):
    '''
    l阶勒让德多项式的值

    参数
    ----
    x：数，输入值
    l：整型，阶数

    返回
    ----
    数

    Value of Legendre Polynomials at degree l

    Parameters
    ----------
    x: num, input value
    l: int, degree

    Return
    ------
    num
    '''
    result = 0
    for k in range(int(l/2) + 1):
        result += (-1)**k * factorial(2 * l - 2 * k)\
        / 2**l / factorial(k) / factorial(l - k) / factorial(l - 2 * k)\
        * x**(l - 2 * k)
    return result

def alpn(l, m):
    '''
    m阶l次连带勒让德多项式的模

    参数
    ----
    l：整型，次数
    m：整型，阶数

    Value of Associated Legendre Polynomials of order m and degree l

    Parameters
    ----------
    l: int, degree
    m: int, order
    '''
    up_part = 2 * factorial(l + m)
    down_part = (2 * l + 1) * factorial(l - m)
    return (up_part / down_part)**0.5

def _dm_lp(x, l, m, acc):
    '''
    勒让德多项式的m阶导数
    '''
    if m == 0:
        x = lp(x, l)
    elif m == 1:
        x = (lp(x + acc * 0.5, l) - lp(x - acc * 0.5, l)) / acc
    else:
        x = (_dm_lp(x + acc * 0.5, l, m-1, acc) - _dm_lp(x - acc * 0.5, l, m-1, acc)) / acc
    return x

def alp(x, l, m, acc=0.1):
    '''
    m阶l次连带勒让德多项式的值
    Plm(x) = (1-x^2)^(m/2) * d^m/dx^m * Pl(x)

    参数
    ----
    x：数，输入值
    l：整型，次数
    m：整型，阶数
    acc：浮点数类型，求导精度

    Value of Associated Legendre Polynomials of order m and degree l
    Plm(x) = (1-x^2)^(m/2) * d^m/dx^m * Pl(x)

    Parameters
    ----------
    x: num, input value
    l: int, degree
    m: int, order
    acc: float, callable, accuracy of derivation, default=0.1
    '''
    return (1 - x**2)**(m / 2) * _dm_lp(x, l, m, acc)

# physics
atm = 101325
c = 299792458.0
c_e = 1.602176634e-19
m_e = 9.1093837015e-31
m_earth = 5.965e24
m_n = 1.67492749804e-27
m_p = 1.67262192369e-27
m_sun = 1.9891e30
G = 6.6743e-11
g = 9.80665
h = 6.62607015e-34
hr = 1.0545718176461565e-34
k = 1.380649e-23
miu = 1.2566370614359173e-06
epsilon = 8.85e-12
NA = 6.02214179e23
R = 8.31446114959365
r_earth = 6371393
r_sun = 6.955e8
r_moon = 384403000
lambdac = 2.4263102175e-12
ly = 9460730472580800
AU = 149597870700
pc = 3.0856775814671915808e16
Platonic_year = 26000
SB = 5.670367e-8
v1 = 7900
v2 = 11200
v3 = 16700

def hp(x, n, li=False):
    '''
    n阶厄米多项式

    参数
    ----
    x：数，输入值
    n：整型，阶数
    li：布尔类型，可选，True表示将0到n阶勒让德多项式组成一维ndarray返回，默认为False

    返回
    ----
    数或一维ndarray

    Value of Hermite Polynomials at degree n

    Parameters
    ----------
    x: num, input value
    n: int, degree
    li: bool, callable, True means that 0 to n order Legendre polynomials are formed into 1-D ndarray and returned, default=False

    Reuturn
    -------
    num or 1-D ndarray
    '''
    if n <= 0:
        n = 0

    if not li:
        if n == 0:
            return 1

        elif n == 1:
            return 2 * x

        else:
            return 2 * x * hp(x, n-1) - 2 * (n-1) * hp(x, n-2)

    else:
        hp_list = []
        for i in range(n+1):
            if i == 0:
                hp_list.append(1)

            elif i == 1:
                hp_list.append(2 * x)

            else:
                hp_list.append(2 * x * hp_list[i-1] - 2 * (i-1) * hp_list[i-2])
        return np.array(hp_list)

def v_mean(m, T):
    '''
    麦克斯韦速率分布律下的平均速率

    参数
    ----
    m：气体分子质量, 单位：kg
    T：气体温度, 单位：K

    返回
    ----
    数或数组类型，速率，单位：m/s

    Average Velocity in Maxwell's Velocity Distribution Law

    Parameters
    ----------
    m: mass of gas molecule, unit: kg
    T: temperature of gas, unit: K

    Return
    ------
    num or array, velocity, unit: m/s
    '''
    return 8**0.5 * (k * T / (np.pi * m))**0.5

def v_p(m, T):
    '''
    麦克斯韦速率分布律下的最概然速率

    参数
    ----
    m：气体分子质量, 单位：kg
    T：气体温度, 单位：K

    返回
    ----
    数或数组类型，速率，单位：m/s

    Most Probable Velocity in Maxwell's Velocity Distribution Law

    Parameters
    ----------
    m: mass of gas molecule, unit: kg
    T: temperature of gas, unit: K

    Return
    ------
    num or array, velocity, unit: m/s
    '''
    return 2**0.5 * (k * T / (np.pi * m))**0.5

def v_rms(m, T):
    '''
    麦克斯韦速率分布律下的均方根速率

    参数
    ----
    m：气体分子质量, 单位：kg
    T：气体温度, 单位：K

    返回
    ----
    数或数组类型，速率，单位：m/s

    Root-Mean-Square Velocity Maxwell's Velocity Distribution Law

    Parameters
    ----------
    m: mass of gas molecule, unit: kg
    T: temperature of gas, unit: K

    Return
    ------
    num or array, velocity, unit: m/s
    '''
    return 3**0.5 * (k * T / (np.pi * m))**0.5


import logging

import numpy as np
from pylops import LinearOperator
from pylops.utils.backend import get_array_module

logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING)

class Regression(LinearOperator):
    r"""Polynomial regression.

    Creates an operator that applies polynomial regression to a set of points.
    Values along the t-axis must be provided while initializing the operator.
    The coefficients of the polynomial regression form the model vector to
    be provided in forward mode, while the values of the regression
    curve shall be provided in adjoint mode.

    Parameters
    ----------
    taxis : :obj:`numpy.ndarray`
        Elements along the t-axis.
    order : :obj:`int`
        Order of the regressed polynomial.
    dtype : :obj:`str`, optional
        Type of elements in input array.

    Attributes
    ----------
    shape : :obj:`tuple`
        Operator shape
    explicit : :obj:`bool`
        Operator contains a matrix that can be solved explicitly
        (``True``) or not (``False``)

    Raises
    ------
    TypeError
        If ``t`` is not :obj:`numpy.ndarray`.

    See Also
    --------
    LinearRegression: Linear regression

    Notes
    -----
    The Regression operator solves the following problem:

    .. math::
        y_i = \sum_{n=0}^{order} x_n t_i^n  \qquad \forall i=1,2,...,N

    where :math:`N` represents the order of the chosen polynomial. We can
    express this problem in a matrix form

    .. math::
        \mathbf{y}=  \mathbf{A} \mathbf{x}

    where

    .. math::
        \mathbf{y}= [y_1, y_2,...,y_N]^T,
        \qquad \mathbf{x}= [x_0, x_1,...,x_{order}]^T

    and

    .. math::
        \mathbf{A}
        = \begin{bmatrix}
            1  & t_{1} & t_{1}^2 & .. & t_{1}^{order}  \\
            1  & t_{2} & t_{2}^2 & .. & t_{1}^{order}  \\
            .. & ..    & ..      & .. & ..             \\
            1  & t_{N} & t_{N}^2 & .. & t_{N}^{order}  \\
        \end{bmatrix}

    """
    def __init__(self, taxis, order, dtype='float64'):
        ncp = get_array_module(taxis)
        if not isinstance(taxis, ncp.ndarray):
            logging.error('t must be numpy.ndarray...')
            raise TypeError('t must be numpy.ndarray...')
        else:
            self.taxis = taxis
        self.order = order
        self.shape = (len(self.taxis), self.order+1)
        self.dtype = np.dtype(dtype)
        self.explicit = False

    def _matvec(self, x):
        ncp = get_array_module(x)
        y = ncp.zeros_like(self.taxis)
        for i in range(self.order+1):
            y += x[i]*self.taxis**i
        return y

    def _rmatvec(self, x):
        ncp = get_array_module(x)

        return ncp.vstack([ncp.dot(self.taxis**i, x)
                           for i in range(self.order+1)])

    def apply(self, t, x):
        """Return values along y-axis given certain ``t`` location(s) along
        t-axis and regression coefficients ``x``

        Parameters
        ----------
        taxis : :obj:`numpy.ndarray`
            Elements along the t-axis.
        x : :obj:`numpy.ndarray`
            Regression coefficients
        dtype : :obj:`str`, optional
        Returns
        ----------
        y : :obj:`numpy.ndarray`
            Values along y-axis

        """
        torig = self.taxis.copy()
        self.taxis = t
        y = self._matvec(x)
        self.taxis = torig
        return y


#Contains methods for sign prediction in signed networks
#Based on Chiang et. al, 2014

import numpy as np
import scipy.sparse as sp
from scipy.linalg import svd
from scipy.linalg import norm
import logging

#Input: adjacency matrix with signs as entries
# Rank (max rank of solution before taking sign matrix)
# Tolerance (how close does solution need to be to stop iterating)
# Maximum number of iterations
# Step size: how fast to update solution
# Mode: normal or test mode to perform additional tests
#       (only called by unit tests)

#Output: signed version low rank matrix that approximately solves
# sign prediction optimization problem
#NOTE: signed version is probably not low rank
#TODO write more official tests
def sign_prediction_SVP(adj_matrix,
                        rank,
                        tol,
                        max_iter,
                        step_size,
                        mode = "normal"):

  #Initialization
  num_iters = 1 #start counting from 1
  dens = 0.01 #initialize factors with this density
  solution = 2*sp.rand(adj_matrix.shape[0], adj_matrix.shape[1], density=dens, format="csr")

  #Iterate until tolerance level or maximum # iters is reached
  while num_iters <= max_iter and not within_tol(solution, adj_matrix, tol):
    #update
    print "projection"
    rows, cols = adj_matrix.nonzero()
    solution = solution - step_size*(projection(
                          solution, rows, cols) - adj_matrix)
    #compute top <rank> SVs
    solution = solution.asfptype()
    print "svd"
    left_svecs, svals, right_svecs = svd(solution.A)

    #form low rank approximation
    solution = sp.csr_matrix(np.dot(np.dot(left_svecs[:,:rank], np.diag(svals[:rank])), right_svecs[:rank,:]))
    print "completed iteration ", num_iters
    num_iters += 1

  #confirm that solution (before signing, which will change things) is desired rank
  if mode == "test":
    assert np.linalg.matrix_rank(np.asarray(solution.todense())) == rank

  return sp.csr_matrix.sign(solution) #recall we want signs (edge sign predictions)

#Input: "solution" of SVP, adjacency matrix, tolerance
#Output: boolean reflecting whether or not
# projection of solution (onto nonzero elements of adj matrix)
# is "tolerably" close to adjacency matrix
def within_tol(solution, adj_matrix, tol):
  rows, cols = adj_matrix.nonzero()
  proj = projection(solution, rows, cols)
  diff = proj - adj_matrix
  try:
    return norm(diff.A, "fro") < tol #.A gets nonzero entries of sparse matrix
  except ValueError:
    logging.exception("Exception: ")
    print("%d NaNs in solution" % np.any(np.isnan(solution.A)))

#Input: matrix to project
#       rows, columns that can be nonzero in projection
#Output: projection (keep observed indices)
def projection(matrix, rows, cols):
  proj = sp.csr_matrix((matrix[rows,cols].A[0], (rows,cols)), shape=matrix.shape)
  return proj