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

def principleStresses(stressTensor,method='eigen'):
    principle_stresses = []
    if method=='eigen':
        principle_stresses = np.linalg.eigvals(stressTensor)
    elif method=='invariants':
        I = stressInvariants(stressTensor)
        principle_stresses = np.roots([1,-I[0],I[1],-I[2]])
    elif method=='Mohr':
        # Requires plane stress assumption on the XY-plane
        print('Making plane stress assumption for XY plane...')
        retDict = generateMohrsCircle(stressTensor)
        principle_stresses = [retDict['sigmaP1'],retDict['sigmaP2'],stressTensor[2][2]]
    quickSort(principle_stresses,0,len(principle_stresses)-1)
    return principle_stresses

def stressInvariants(stressTensor):
    I_1 = np.trace(stressTensor)
    I_2 = np.linalg.det([[stressTensor[0][0], stressTensor[0][1]],
                         [stressTensor[0][1], stressTensor[1][1]]]) + \
          np.linalg.det([[stressTensor[0][0], stressTensor[0][2]],
                         [stressTensor[0][2], stressTensor[2][2]]]) + \
          np.linalg.det([[stressTensor[1][1], stressTensor[1][2]],
                         [stressTensor[1][2], stressTensor[2][2]]])
    I_3 = np.linalg.det(stressTensor)
    I = [I_1, I_2, I_3]
    return I

def octohedralStresses(stressTensor):
    prin_stresses = principleStresses(stressTensor)

    sigma_oct = 1/3*sum(prin_stresses)
    tau_oct = 1/3*( (prin_stresses[0] - prin_stresses[1])**2 + (prin_stresses[0] - prin_stresses[2])**2 + (prin_stresses[1] - prin_stresses[2])**2 )**0.5
    return [sigma_oct, tau_oct]

def partition(arr, low, high):
    i = (low-1)
    pivot = arr[high]

    for j in range(low, high):
        if arr[j] >= pivot:
            i = i+1
            arr[i], arr[j] = arr[j], arr[i]

    arr[i+1], arr[high] = arr[high], arr[i+1]
    return (i+1)

def quickSort(arr, low, high):
    if len(arr) == 1:
        return arr
    if low < high:

        # pi is partitioning index, arr[p] is now
        # at right place
        pi = partition(arr, low, high)

        # Separately sort elements before
        # partition and after partition
        quickSort(arr, low, pi-1)
        quickSort(arr, pi+1, high)

def generateMohrsCircle(stressTensor,filename=None,thetaCutPlane=None,show=[0]):
    # Arguments: stressTensor - the stress tensor describing the stress state; MUST OBEY PLANE STRESS ASSUMPTIONS
    #            filename - name to save file as for HW formatting; do not include file extension; disregard
    #            thetaCutPlane - angle of the cut plane in degrees; OPTIONAL - default is no angle
    #            show - the figures to show; OPTIONAL - default is Mohr's Circle only

    # Calculate values necessary for constructing Mohr's Circle
    sigmaXX = stressTensor[0][0]
    sigmaYY = stressTensor[1][1]
    tauXY = stressTensor[0][1]
    center = (sigmaXX+sigmaYY)/2
    radius = math.hypot(sigmaYY-center,tauXY)
    sigmaP1,sigmaP2 = center-radius,center+radius
    thetaP1 = 1/2*math.asin(tauXY/radius)
    thetaP2 = thetaP1+math.pi

    # Calculate angle necessary for maximum in-plane shear stress
    phi = math.pi/4 + thetaP1

    # Calculate values necessary for a cut plane if angle for said plane is provided
    alpha,sigma1,sigma2,tau1=None,None,None,None
    if thetaCutPlane != None:
        alpha = -(2*math.radians(thetaCutPlane)+2*thetaP1)
        sigma1 = center + radius*math.cos(alpha)
        sigma2 = center - radius*math.cos(alpha)
        tau1 = radius*math.sin(alpha)

    # A function designed to draw the figures. Workings are irrelevant to actual assignment
    plotMohrsCircle(filename,sigmaYY,sigmaXX,tauXY,center,radius,sigmaP1,sigmaP2,thetaP1,phi,alpha,sigma1,sigma2,tau1,thetaCutPlane,show)

    # Constructing an object to return. You can ignore this as well
    keys = ['center','radius','sigmaP1','sigmaP2','thetaP1','phi']
    values = [center,radius,sigmaP1,sigmaP2,math.degrees(thetaP1),math.degrees(phi)]
    if thetaCutPlane != None:
        keys = keys + ['alpha','sigma1','sigma2','tau1']
        values = values + [math.degrees(alpha),sigma1,sigma2,tau1]
    meDict = dict(zip(keys,values))

    return meDict

def plotMohrsCircle(filename,sigmaXX,sigmaYY,tauXy,center,radius,sigmaP1,sigmaP2,thetaP1,phi=None,alpha=None,sigma1=None,sigma2=None,tau1=None,thetaCutPlane=None,show=[0],units='MPa'):
    fig,axes = plt.subplots(nrows=1,ncols=len(show))
    if len(show)==1:
        axes = [axes]
    for i in range(len(axes)):
        axes[i].set_aspect('equal')

    axesIndex = 0

    if 0 in show:
        circ = plt.Circle((center,0),radius,edgecolor='#0000FF',facecolor='#FFFFFF')
        axes[axesIndex].add_artist(circ)
        axes[axesIndex].set_xlim(center-(radius+0.5*radius),center+(radius+0.75*radius))
        axes[axesIndex].set_ylim(-(radius+0.3*radius),(radius+0.3*radius))
        axes[axesIndex].plot([center],[0],'ko',[sigmaXX],[tauXY],'ro',[sigmaYY],[-tauXY],'ro',[sigmaXX,sigmaYY],[tauXY,-tauXY],'k--')
        axes[axesIndex].plot([sigmaP1],[0],'bo',[sigmaP2],[0],'bo')
        axes[axesIndex].plot([center-(radius+0.5*radius),center+(radius+0.75*radius)],[0,0],'k')
        axes[axesIndex].text(sigmaXX + 0.1*radius,tauXY,"({},{})".format(sigmaXX,tauXY),fontsize=12)
        axes[axesIndex].text(sigmaYY + 0.1*radius, -tauXY-0.25*radius, "({},{})".format(sigmaYY,-tauXY),fontsize=12)
        axes[axesIndex].text(center+0.25*radius,0.1*radius,"{}°".format(round(thetaP1*180/math.pi,3)),fontsize=12)
        axes[axesIndex].text(center+0.1*radius,-0.25*radius, "{}".format(round(center,3)),fontsize=12)
        axes[axesIndex].text(sigmaP1+0.1*radius,-0.25*radius,"s_P1",fontsize=12)
        axes[axesIndex].text(sigmaP2+0.1*radius,-0.25*radius,"s_P2",fontsize=12)

        axesIndex+=1

    if 1 in show:
        corners = [[2,4,4,2],
                   [2,2,4,4]]
        lines = [[1.5,3,3,4.5,0,3,3,6,0,3,3,6],
                 [3,1.5,4.5,3,3,0,6,3,3,0,6,3]]
        ends = [[0,3,3,6,0.25,3.25,3.25,5.75,0.25,2.75,2.75,5.75],
                [3,0,6,3,3.25,0.25,5.75,3.25,2.75,0.25,5.75,2.75]]
        R = [[math.cos(thetaP1),-math.sin(thetaP1)],
             [math.sin(thetaP1),math.cos(thetaP1)]]
        corners = [list(line) for line in np.matmul(np.array(R),np.array(corners))]
        lines = [list(line) for line in np.matmul(np.array(R),np.array(lines))]
        ends = [list(line) for line in np.matmul(np.array(R),np.array(ends))]
        corners[0].append(corners[0][0])
        corners[1].append(corners[1][0])
        axes[axesIndex].plot(corners[0],corners[1])
        for i in range(len(lines[0])):
            axes[axesIndex].plot([lines[0][i],ends[0][i]],[lines[1][i],ends[1][i]],'k')
        axes[axesIndex].plot([corners[0][1],corners[0][1]],[min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5],'k--')
        axes[axesIndex].text(ends[0][2]+0.1,ends[1][2]+0.1,"{} {}".format(round(sigmaP1,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][3]+0.1,ends[1][3]+0.1,"{} {}".format(round(sigmaP2,3),units),fontsize=12)
        axes[axesIndex].set_xlim(min(lines[0]+ends[0])-0.5,max(lines[0]+ends[0])+2.25)
        axes[axesIndex].set_ylim(min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5)

        axesIndex+=1

    if 2 in show:
        corners = [[2,4,4,2],
                   [2,2,4,4]]
        lines = [[1.5,3.0,3.0,4.5,   0.00,3.00,3.00,6.00,   0.00,3.00,3.00,6.00,   1.75,2.00,4.25,2.00,   1.75,2.00,4.00,4.25,   4.25,1.75],
                 [3.0,1.5,4.5,3.0,   3.00,0.00,6.00,3.00,   3.00,0.00,6.00,3.00,   2.00,1.75,2.00,4.25,   4.00,4.25,1.75,2.00,   1.75,4.25]]
        ends =  [[0.0,3.0,3.0,6.0,   0.25,3.25,3.25,5.75,   0.25,2.75,2.75,5.75,   1.75,4.00,4.25,4.00,   1.50,2.25,3.75,4.50,   4.25,1.75],
                 [3.0,0.0,6.0,3.0,   3.25,0.25,5.75,3.25,   2.75,0.25,5.75,2.75,   4.00,1.75,4.00,4.25,   3.75,4.50,1.50,2.25,   1.75,4.25]]
        R = [[math.cos(phi),-math.sin(phi)],
             [math.sin(phi),math.cos(phi)]]
        corners = [list(line) for line in np.matmul(np.array(R),np.array(corners))]
        lines = [list(line) for line in np.matmul(np.array(R),np.array(lines))]
        ends = [list(line) for line in np.matmul(np.array(R),np.array(ends))]
        corners[0].append(corners[0][0])
        corners[1].append(corners[1][0])
        axes[axesIndex].plot(corners[0],corners[1])
        for i in range(len(lines[0])):
            axes[axesIndex].plot([lines[0][i],ends[0][i]],[lines[1][i],ends[1][i]],'k')
        axes[axesIndex].plot([corners[0][1],corners[0][1]],[min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5],'k--')
        axes[axesIndex].text(ends[0][2]+0.1,ends[1][2]+0.1,"{} {}".format(round(center,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][3]+0.1,ends[1][3]+0.1,"{} {}".format(round(center,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][-1]-2.5,lines[1][-1]-0.1,"{} {}".format(round(radius,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][-2]+0.1,lines[1][-2]-0.1,"{} {}".format(round(radius,3),units),fontsize=12)
        axes[axesIndex].set_xlim(min(lines[0]+ends[0])-1.5,max(lines[0]+ends[0])+2.25)
        axes[axesIndex].set_ylim(min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5)

        axesIndex+=1

    if 3 in show:
        corners = [[2,4,4,2],
                   [2,2,4,4]]
        lines = [[1.5,3.0,3.0,4.5,   0.00,3.00,3.00,6.00,   0.00,3.00,3.00,6.00,   1.75,2.00,4.25,2.00,   1.75,2.00,4.00,4.25,   4.25,1.75],
                 [3.0,1.5,4.5,3.0,   3.00,0.00,6.00,3.00,   3.00,0.00,6.00,3.00,   2.00,1.75,2.00,4.25,   4.00,4.25,1.75,2.00,   1.75,4.25]]
        ends =  [[0.0,3.0,3.0,6.0,   0.25,3.25,3.25,5.75,   0.25,2.75,2.75,5.75,   1.75,4.00,4.25,4.00,   1.50,2.25,3.75,4.50,   4.25,1.75],
                 [3.0,0.0,6.0,3.0,   3.25,0.25,5.75,3.25,   2.75,0.25,5.75,2.75,   4.00,1.75,4.00,4.25,   3.75,4.50,1.50,2.25,   1.75,4.25]]
        R = [[math.cos(alpha),-math.sin(alpha)],
             [math.sin(alpha),math.cos(alpha)]]
        corners = [list(line) for line in np.matmul(np.array(R),np.array(corners))]
        lines = [list(line) for line in np.matmul(np.array(R),np.array(lines))]
        ends = [list(line) for line in np.matmul(np.array(R),np.array(ends))]
        corners[0].append(corners[0][0])
        corners[1].append(corners[1][0])
        axes[axesIndex].plot(corners[0],corners[1])
        for i in range(len(lines[0])):
            axes[axesIndex].plot([lines[0][i],ends[0][i]],[lines[1][i],ends[1][i]],'k')
        axes[axesIndex].plot([corners[0][1],corners[0][1]],[min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5],'k--')
        axes[axesIndex].text(ends[0][2]+0.1,ends[1][2]+0.1,"{} {}".format(round(sigma1,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][3]+0.1,ends[1][3]+0.1,"{} {}".format(round(sigma2,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][-2]+0.1,lines[1][-2]-0.1,"{} {}".format(round(tau1,3),units),fontsize=12)
        axes[axesIndex].set_xlim(min(lines[0]+ends[0])-1.5,max(lines[0]+ends[0])+2.25)
        axes[axesIndex].set_ylim(min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5)

    fig.set_figheight(4)
    fig.set_figwidth(4*len(axes))
    fig.tight_layout()

    if filename!=None:
        fig.savefig(filename+'.png')


import numpy as np
import scipy.stats
import matplotlib.pyplot as plt

# Problem 1
## Question b)
s = 1
sigma = 0.5
X = np.array([-1.41, -0.57, 1.12, 3.05])
Y = np.array([1.69, 0.51, -1.08, -2.44])
## Posterior parameters estimation.
Sigma = ((1 / sigma ** 2) * np.dot(X, X) + (1 / s ** 2)) ** (-1)
M_pos = (1 / sigma ** 2) * Sigma * np.dot(X, Y)

xnew = 2.18
### MonteCarlo to estimate mu_new
n = 1000
beta_simul = np.random.normal(loc=M_pos, scale=Sigma ** (0.5), size=n)
MC_estimate = np.mean(x_new * beta_simul)
std_simul = np.std(beta_simul * xnew)
### CI
alpha = 0.05
q = scipy.stats.norm.ppf(1 - alpha / 2)
sup_bound = (q * std_simul) / np.sqrt(n) + MC_estimate
inf_bound = - (q * std_simul) / np.sqrt(n) + MC_estimate
print("MC estimate : {}".format(MC_estimate))
print("{}% CI : [{},{}]".format((1 - alpha) * 100, inf_bound, sup_bound))

## Question c)
n = 100000
error_simul = np.random.normal(loc=0, scale=0.5, size=n)
Y_new_simul = error_simul + xnew * np.random.normal(loc=M_pos, scale=Sigma ** (0.5), size=n)
count = 0
for i in range(len(Y_new_simul)):
    if Y_new_simul[i] < -2.1:
        count += 1
print("Estimated P(Y_new < -2.1) = {}".format(count / n))

# Problem 3
P = np.array([[0, 1 / 3, 1 / 3, 1 / 3, 0],
              [1 / 3, 0, 1 / 3, 0, 1 / 3],
              [1 / 2, 1 / 2, 0, 0, 0],
              [1 / 2, 0, 0, 0, 1 / 2],
              [0, 1 / 2, 0, 1 / 2, 0]])
n = np.shape(P)[0]
## Question a)
print("Stationary Distribution : {}".format(solveStationary(P)))

## Question b)

## Question d)
b = [0, -1, 0, 0, 0]
P_mod = P.copy()
P_mod[:, 0] = [0] * n
print("mu_c = {}".format(np.linalg.solve(P.T - np.eye(n), b)[2]))

## Question e)
P_mod2 = np.array([[0, 0, 1 / 3],
                   [0, 0, 0.5],
                   [1 / 2, 1 / 2, 0]])
b = [-1 / 3, -1 / 2, 0]

# np.linalg.solve(P_mod2 - np.eye(n-2),b)
print("p_B = {}".format(np.linalg.solve(P_mod2 - np.eye(n - 2), b)[0]))

## Question f)
b = [-1] * n
np.linalg.solve(P_mod - np.eye(n), b)
print("Expected number of steps starting from C before returning to A : {}".format(
    np.linalg.solve(P_mod - np.eye(n), b)[2]))

def solveStationary(A):
    """ x = xA where x is the answer
    x - xA = 0
    x( I - A ) = 0 and sum(x) = 1
    """
    n = A.shape[0]
    a = np.eye(n) - A
    a = np.vstack((a.T, np.ones(n)))
    b = np.matrix([0] * n + [1]).T
    return np.linalg.lstsq(a, b)[0]

## Problem 5)
def kernel_1(x, y, sigma, r=0.5):
    return (sigma ** 2) * np.exp(-r * (x - y) ** 2)

### Estimation of sigma.
def estimate_sigma(Y):
    sigma_estimates = [i ** 2 for i in Y]
    return np.mean(sigma_estimates) ** 0.5

## Question b)
X = np.array([-1, 0, 2])
Y = np.array([2.4, 3.9, -2.8])
plt.scatter(X, Y)
plt.show()

sigma = 1 / 2
print(estimate_sigma(Y))

def mu_new(x_new, r=0.5):
    K33 = np.zeros((3, 3))
    for i in range(3):
        for j in range(3):
            X_i = X[i]
            X_j = X[j]
            K33[i, j] = kernel_1(X_i, X_j, sigma, r)
    K_3new = np.array([kernel_1(x_new, x, sigma, r) for x in X])
    return np.matmul(K_3new, np.matmul(np.linalg.inv(K33), Y))

r_val = [0.5, 1, 2]
for r in r_val:
    X_new = np.linspace(-3, 4, 50)
    Y_new_expected_values = np.array([mu_new(x_new, r) for x_new in X_new])
    print("mu_new(1) = {}".format(mu_new(1, r)))
    plt.scatter(X_new, Y_new_expected_values, label="r = {}".format(r), alpha=0.5)

plt.scatter(X, Y)
plt.legend()
plt.grid()
plt.show()

## Question d)
def covariance2(r, x, y):
    return -2 * r * (x - y) * np.exp(-r * (x - y) ** 2)

def covariance3(r, x, y):
    return 2 * r * np.exp(-r * (x - y) ** 2) * (1 - 2 * r * (x - y) ** 2)

Y = np.array([2.4, 3.9, -2.8, 6.4, -2.0, -0.8])

def mu_new_2(xnew, r=0.5):
    K_6new = [kernel_1(i, xnew, sigma, r) for i in X] \
             + [covariance2(r, i, xnew) for i in X]

    K66 = np.zeros((6, 6))
    for i in range(0, 6):
        if i < 3:
            tab = [kernel_1(j, X[i], sigma, r) for j in X] \
                  + [covariance2(r, j, X[i]) for j in X]
        if i >= 3:
        tab = [covariance2(r, X[i - 3], j) for j in X] \
              + [covariance3(r, X[i - 3], j) for j in X]
        K66[i] = tab
    # print(K66)
    return np.matmul(K_6new, np.matmul(np.linalg.inv(K66), Y))

# Plots
r_val = [0.5, 1, 2]
for r in r_val:
    X_new = np.linspace(-3, 3, 50)
    Y_new_expected_values = np.array([mu_new_2(x_new, r) for x_new in X_new])
    print("mu_new_2(1) = {}".format(mu_new_2(1, r)))
    plt.scatter(X_new, Y_new_expected_values, label="r = {}".format(r), alpha=0.5)

plt.scatter(X, Y[0:3], alpha= 1)
plt.legend()
plt.grid()
plt.show()


import numpy as np
def rotate_to_north_pole(v, angle=None):
    """
    Rotate a unit vector v to the north pole of a unit sphere

    Return the rotation matrix
    See Rodrigues' rotation formula: https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula#Matrix_notation
    Input: 1D array (i.e., a point on a unit sphere)
    """
    d = len(v) # dimension of the feature space

    ## normalize vector v
    v = v / np.linalg.norm(v)
    ## north pole coordinates d-dimension [0, ..., 0, 1]
    north_pole = [0] * (d - 1)
    north_pole.append(1)
    north_pole = np.asarray(north_pole)

    inner_prod = np.inner(north_pole, v)
    if not angle:
        angle = np.arccos(inner_prod)
    if np.abs(inner_prod - 1) < 1e-15:
        return np.eye(d)
    elif np.abs(inner_prod + 1) < 1e-15:
        return -np.eye(d)
    c = v - north_pole * inner_prod
    c = c / np.linalg.norm(c)
    A = np.outer(north_pole, c) - np.outer(c, north_pole)

    rot = np.eye(d) + np.sin(angle)*A + (np.cos(angle) - 1)*(np.outer(north_pole, north_pole)\
                                                             + np.outer(c, c))
    return rot
def log_north_pole(x):
    """
    LOGNP Riemannian log map at North pole of S^k
        LogNP(x) returns k x n matrix where each column is a point on tangent
        space at north pole and the input x is (k+1) x n matrix where each column
        is a point on a sphere.
    Input: d x n matrix w.r.t. the extrinsic coords system
    Output: (d-1) x n matrix w.r.t. the coords system (tangent space) origined at the NP
    """
    d, n = x.shape
    scale = np.arccos(x[-1, :]) / np.sqrt(1-x[-1, :]**2)
    scale[np.isnan(scale)] = 1
    log_px = scale * x[:-1, :]
    return log_px
def exp_north_pole(x):
    """
    EXPNP Riemannian exponential map at North pole of S^k
    returns (k+1) x n matrix where each column is a point on a
    sphere and the input v is k x n matrix where each column
    is a point on tangent  space at north pole.

    Input: d x n matrix
    """
    d, n = x.shape
    nv = np.sqrt(np.sum(x ** 2, axis=0))
    tmp = np.sin(nv) / (nv + 1e-15)
    exp_px = np.vstack((tmp * x, np.cos(nv)))
    exp_px[:, nv < 1e-16] = np.repeat(np.vstack((np.zeros((d, 1)), 1)), np.sum(nv<1e-16), axis=1)
    return exp_px


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jul 10 17:23:06 2021

@author: alessandro
"""
import numpy as np
import scipy.linalg as spl
import matplotlib.pyplot as plt

def usolve(U, b):
    m, n = U.shape
    x = np.zeros((n, 1))
    for i in range(n - 1, -1, -1):
        s = np.dot(U[i, i+1:], x[i+1:])
        x[i] = (b[i] - s) / U[i, i]
    return x

def qr(x, y, n):
    H = np.vander(x, n + 1)
    Q, R = spl.qr(H)
    X = np.dot(Q.T, y)
    pol = usolve(R[:n+1, :], X[:n+1])
    return pol

dati = np.array([1900, 76, 1910, 92, 1920, 106, 1930, 123, 1940, 132, 1950, 151, 1960, 179, 1970, 203, 1980, 226, 1990, 249, 2000, 281, 2010, 305], dtype=float)

x = dati[0::2]
y = dati[1::2]

p1 = qr(x, y, 1)
p2 = qr(x, y, 2)
p3 = qr(x, y, 3)
p4 = qr(x, y, 4)

xx = np.linspace(1900, 2010, 100)
y1 = np.polyval(p1, xx)
y2 = np.polyval(p2, xx)
y3 = np.polyval(p3, xx)

plt.plot(x, y, 'o')
plt.plot(xx, y1, label="p1(x)")
plt.plot(xx, y2, label="p2(x)")
plt.plot(xx, y3, label="p3(x)")
plt.legend()
plt.show()

y1 = np.polyval(p1, x)
y2 = np.polyval(p2, x)
y3 = np.polyval(p3, x)
e1, e2, e3 = 0, 0, 0

for i in range(12):
    e1 += (y1[i] - y[i]) ** 2
    e2 += (y2[i] - y[i]) ** 2
    e3 += (y3[i] - y[i]) ** 2

print(f"e1 = {e1}")
print(f"e2 = {e2}")
print(f"e3 = {e3}")


import numpy as np

def compute_cost(AL, Y):
        """
        Implement the cost function defined by equation (7).

        Parameters
        ----------
        AL : numpy array
            Probability vector corresponding to your label predictions
            with shape (1, number of examples)
        Y : numpy array
            True "label" vector (for example: containing 0 if non-cat, 1 if cat)
            with shape (1, number of examples)

        Returns
        -------
        total_cost : float
            cross-entropy cost
        """
        # Compute loss from aL and y.
        log_cost = np.multiply(-np.log(AL), Y) + np.multiply(-np.log(1 - AL), 1 - Y)
        total_cost = np.nansum(log_cost)
        assert(total_cost.shape == ())

        return total_cost

def regularized_cost(AL, Y, parameters, lambd):
    """
    Implement the cost function with L2 regularization.

    Parameters
    ----------
    AL : numpy array
        post-activation, output of forward propagation,
        of shape (output size, number of examples)
    Y : numpy array
        "true" labels vector, of shape (output size, number of examples)
    parameters : dict
        python dictionary containing parameters of the model

    Returns
    -------
    total_regularized_cost :
        value of the regularized loss function (formula (2))
    """
    cost_cross_entropy = compute_cost(AL, Y) # This gives you the cross-entropy part of the cost
    cost_L2 = (lambd / 2) * np.nansum(list(map(lambda x: np.nansum(np.square(x)), parameters.values())))
    total_regularized_cost = cost_cross_entropy + cost_L2

    return total_regularized_cost


import numpy as np

def generate_data():
    '''
    Generates fake data to use for calculating lane curvature.
    In your own project, you'll ignore this function and instead
    feed in the output of your lane detection algorithm to
    the lane curvature calculation.
    '''
    # Set random seed number so results are consistent for grader
    # Comment this out if you'd like to see results on different random data!
    np.random.seed(0)
    # Generate some fake data to represent lane-line pixels
    ploty = np.linspace(0, 719, num=720)  # to cover same y-range as image
    quadratic_coeff = 3e-4  # arbitrary quadratic coefficient
    # For each y position generate random x position within +/-50 pix
    # of the line base position in each case (x=200 for left, and x=900 for right)
    leftx = np.array([200 + (y ** 2) * quadratic_coeff + np.random.randint(-50, high=51)
                      for y in ploty])
    rightx = np.array([900 + (y ** 2) * quadratic_coeff + np.random.randint(-50, high=51)
                       for y in ploty])

    leftx = leftx[::-1]  # Reverse to match top-to-bottom in y
    rightx = rightx[::-1]  # Reverse to match top-to-bottom in y

    # Fit a second order polynomial to pixel positions in each fake lane line
    left_fit = np.polyfit(ploty, leftx, 2)
    right_fit = np.polyfit(ploty, rightx, 2)

    return ploty, left_fit, right_fit

def measure_curvature_pixels():
    '''
    Calculates the curvature of polynomial functions in pixels.
    '''
    # Start by generating our fake example data
    # Make sure to feed in your real data instead in your project!
    ploty, left_fit, right_fit = generate_data()

    # Define y-value where we want radius of curvature
    # We'll choose the maximum y-value, corresponding to the bottom of the image
    y_eval = np.max(ploty)

    ##### TO-DO: Implement the calculation of R_curve (radius of curvature) #####
    ## Implement the calculation of the left line here
    left_curverad = ((1 + (2 * left_fit[0] * y_eval + left_fit[1]) ** 2) ** (3 / 2)) / np.abs(2 * left_fit[0])
    ## Implement the calculation of the right line here
    right_curverad = ((1 + (2 * right_fit[0] * y_eval + right_fit[1]) ** 2) ** (3 / 2)) / np.abs(2 * right_fit[0])

    return left_curverad, right_curverad

# Calculate the radius of curvature in pixels for both lane lines
left_curverad, right_curverad = measure_curvature_pixels()

print(left_curverad, right_curverad)
# Should see values of 1625.06 and 1976.30 here, if using
# the default `generate_data` function with given seed number


# -*- coding: utf-8 -*-
"""
Created on Thu Sep 30 08:41:19 2021

@author: Ezra
"""

import numpy as np
import time
import pandas as pd

def f(x):
    return -x*np.sin(x)-x*x/4*np.cos(x)

def fPrime(x):
    return x**2/4*np.sin(x)-3*x/2*np.cos(x)-np.sin(x)

def fwdDiff(x0,h):
    dfdx=(f(x0+h)-f(x0))/h
    return dfdx

def bacDiff(x0,h):
    dfdx=(f(x0)-f(x0-h))/h
    return dfdx

def cenDiff(x0,h):
    dfdx=(f(x0+h)-f(x0-h))/(2*h)
    return dfdx

def fourthOrdDiff(x0,h):
    dfdx = (4*cenDiff(x0, h/2)-cenDiff(x0,h))/3
    return dfdx

def sixthOrdDiff(x0,h):
    dfdx=(16*(fourthOrdDiff(x0, h/2))-fourthOrdDiff(x0, h))/15
    return dfdx

x0=np.array([-3,-2.5,-2,-1.5,-1,-.5,0,.5,1,1.5,2,2.5,3])

t1 = time.perf_counter()
fde=fwdDiff(x0,.1)
tfwd  = time.perf_counter()-t1
print("time to run fwdDiff", tfwd)

t2 = time.perf_counter()
bde=bacDiff(x0,.1)
tbc  = time.perf_counter()-t2
print("time to run bacDiff", tbc)

t3 = time.perf_counter()
cde=cenDiff(x0,.1)
tcen  = time.perf_counter()-t3
print("time to run cendiff", tcen)

t4 = time.perf_counter()
foe=fourthOrdDiff(x0, 0.1)
tfourth  = time.perf_counter()-t4
print("time to run fourthOrdDiff", tfourth)

t5 = time.perf_counter()
soe=sixthOrdDiff(x0,.1)
tsix  = time.perf_counter()-t5
print("time to run sixthOrdDiff", tsix)

act=fPrime(x0)

print("Forward Difference Estimate=", fde)
print("Backwards Difference Estimate=", bde)
print("Center Difference Estimate=", cde)
print("Fourth Order Difference Estimate=", foe)
print("Sixth Order Difference Estimate=", soe)
print("Analytic Value of Derivative=", act)

def err(x,y):

    return abs((y-x)/y)

h=np.array([.5,.5**2,.5**3,.5**4,.5**5])

x0=1
analytical= fPrime(x0)
errFwd=[]
errCen=[]
errBac=[]
errFourthOrder=[]
errSixthOrder=[]
dfdxFwd=[]
dfdxBac=[]
dfdxCen=[]
dfdxFourth=[]
dfdxSixth=[]

for i in range(len(h)):
    dfdxFwd.append(fwdDiff(x0,h[i]))
    dfdxBac.append(bacDiff(x0,h[i]))
    dfdxCen.append(cenDiff(x0,h[i]))
    dfdxFourth.append(fourthOrdDiff(x0,h[i]))
    dfdxSixth.append(sixthOrdDiff(x0, h[i]))
    errFwd.append(err(dfdxFwd[i],analytical))
    errBac.append(err(dfdxBac[i],analytical))
    errCen.append(err(dfdxCen[i],analytical))
    errFourthOrder.append(err(dfdxFourth[i],analytical))
    errSixthOrder.append(err(dfdxSixth[i],analytical))

import matplotlib.pyplot as plt

plt. figure(0)
plt.semilogy(h,errFwd,label="FWD")
plt.semilogy(h,errBac,label="BAC")
plt.semilogy(h,errCen,label="CEN")
plt.semilogy(h,errFourthOrder,label="FTH")
plt.semilogy(h,errSixthOrder,label="SIX")
plt.title("Error of methods of various orders")
plt.xlabel("h")
plt.ylabel("Relative error")
plt.grid(True)
plt.legend()
plt.show()

x=np.linspace(-5,5,100)
z=np.linspace(-5,5,20)

plt.figure(1)
plt.plot(x,fPrime(x),label="Analytic")
plt.scatter(z,fwdDiff(z, .1),color='red',label= 'fwdDiff')
plt.scatter(z,bacDiff(z, .1),color='green',label='bacDiff')
#plt.scatter(z,cenDiff(z, .1),color='orange',label="cenDiff")
plt.scatter(z,fourthOrdDiff(z, .1),color='pink',label="4thOrdDiff")
plt.title("graph of function using cenDiff and analytic solution")
plt.xlabel("x")
plt.ylabel("y")
plt.grid(True)
plt.legend()

dfdxFwd=tuple(dfdxFwd)
dfdxBac=tuple(dfdxBac)
dfdxCen=tuple(dfdxCen)
dfdxFourth=tuple(dfdxFourth)
dfdxSixth=tuple(dfdxSixth)
errFwd=tuple(errFwd)
errBac=tuple(errBac)
errCen=tuple(errCen)
errFourthOrder=tuple(errFourthOrder)
errSixthOrder=tuple(errSixthOrder)

Estimates=pd.DataFrame({'FwdDiff': dfdxFwd,'BacDiff':dfdxBac,'CenDiff':dfdxCen,
                        'FourthOrdDiff':dfdxFourth,'SixthOrdDiff':dfdxSixth,
                        'errFwd':errFwd,'errBac':errBac,'errCen':errCen,
                        'errFourthOrder':errFourthOrder,'errSixthOrder':errSixthOrder})
file_name='dfdx.xlsx'
Estimates.to_excel(file_name)


import numpy as np
import matplotlib.pyplot as plt

def serieFourierCuadrada(s, r, n):
    #s = float(input("Ingresa el valor de s: "))
    #r = float(input("Ingresa el valor de r: "))
    #n = int(input("Ingresa el valor de n: "))

    impares = [x for x in range(0,n+1) if x%2 == 1]
    #print(impares)

    t = np.arange(-8*s,8*s+0.01, 0.01)

    #print(t)

    #Calcula la serie.
    for i in range(0, len(impares)):

        if i == 0:
            ft = np.sin((impares[i]*np.pi*t)/s)
            ft*=(r/impares[i])
        else:
            ftAux = np.sin((impares[i]*np.pi*t)/s)
            ftAux*=(r/impares[i])
            ft+=ftAux

    ft*=(-4/np.pi)
    #print("Total: ",ft)

    line, = plt.plot(t, ft, lw=2)

    #Muestra los ejes xy.
    ax = plt.subplot()
    ax.spines['left'].set_position('zero')

    ax.spines['right'].set_color('none')
    ax.yaxis.tick_left()

    ax.spines['bottom'].set_position('zero')

    ax.spines['top'].set_color('none')
    ax.xaxis.tick_bottom()

    #Añade estilo a la representación de la gráfica.
    plt.ylim(-r-1, r+1)
    plt.grid(True)
    plt.axis('equal')
    #plt.title(r'f(t) = $\frac{-4}{\pi} \sum_{i=1}^\infty \frac{%.2f}{n}sin(\frac{nt}{%.2f})$' % (r, s))
    plt.title(r'f(t) = $\frac{-4}{\pi} ( \frac{%.2f}{1}sin(\frac{πt}{%.2f}) + \frac{%.2f}{3}sin(\frac{3πt}{%.2f}) + \frac{%.2f}{5}sin(\frac{5πt}{%.2f}) + \frac{%.2f}{7}sin(\frac{7πt}{%.2f}) + \frac{%.2f}{9}sin(\frac{9πt}{%.2f}) + \frac{%.2f}{11}sin(\frac{11πt}{%.2f}) + ... )$ ' % (r, s, r, s, r, s, r, s, r, s, r, s))
    plt.show()

def serieFourierTriangular(s, r, n):
    #s = float(input("Ingresa el valor de s: "))
    #r = float(input("Ingresa el valor de r: "))
    #n = int(input("Ingresa el valor de n: "))

    impares = [x for x in range(0,n+1) if x%2 == 1]
    #print(impares)

    t = np.arange(-8*s,8*s+0.01, 0.01)

    #print(t)

    #Calcula la serie.
    for i in range(0, len(impares)):

        if i == 0:
            ft = np.cos((impares[i]*np.pi*t)/s)
            ft*=(1/impares[i]**2)
        else:
            ftAux = np.cos((impares[i]*np.pi*t)/s)
            ftAux*=(1/impares[i]**2)
            ft+=ftAux

    ft*=(-4*r/np.pi**2)
    ft+=r/2
    #print("Total: ",ft)

    line, = plt.plot(t, ft, lw=2)

    #Muestra los ejes xy.
    ax = plt.subplot()
    ax.spines['left'].set_position('zero')

    ax.spines['right'].set_color('none')
    ax.yaxis.tick_left()

    ax.spines['bottom'].set_position('zero')

    ax.spines['top'].set_color('none')
    ax.xaxis.tick_bottom()

    #Añade estilo a la representación de la gráfica.
    plt.ylim(-r-1, r+1)
    plt.grid(True)
    plt.axis('equal')
    plt.title(r'f(t) = $ \frac{%.2f}{2} + \frac{-4(%.2f)}{π^{2}} ( \frac{1}{1^{2}}cos(\frac{πt}{%.2f}) + \frac{1}{3^{2}}cos(\frac{3πt}{%.2f}) + \frac{1}{5^{2}}cos(\frac{5πt}{%.2f}) + \frac{1}{7^{2}}cos(\frac{7πt}{%.2f}) + \frac{1}{9^{2}}cos(\frac{9πt}{%.2f}) + \frac{1}{11^{2}}sin(\frac{11πt}{%.2f}) + ... )$ ' % (r, r, s, s, s, s, s, s))
    plt.show()

#print("1.-Serie Fourier funcion Cuadrada")
#print("2.-Serie Fourier funcion Triangular")
#opcion = int(input("Opcion: "))

#if(opcion == 1):
#    SerieFourierCuadrada()
#else:
#    SerieFourierTriangular()


"""
  Name     : c9_32_matrix_multiplication.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""

import numpy as np
ret=np.matrix(np.array([[0.1,0.2],[0.10,0.1071],[-0.02,0.25],[0.012,0.028],[0.06,0.262],[0.14,0.115]]))
covar=np.dot(ret.T,ret)
print("covar")
print(covar)


"""

The point of this code is to prove that the descending interest method
is not always the optimal method.

The code computes the grand total cost of using the "gradient descent"
method and the "descending interest" method to pay 2 loans, assuming there
are $100 to apply above the loans' minimum payment.

The starting principal of loan X varies. It varies from being equal to the
other loan's starting principal to + $10000 greater. All other parameters
are held constant: the interests, the length (term), the extra money (a).
NOTE though, that the changing the starting principal changes the minimum
payment as per the "present value" equation used in creating amortized loans.

Each variation of loan X's principal yeilds a different total cost.
The total cost as a function of difference in starting principal is plotted
to compare methods.

"""

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

# Interests
IA = 0.35/12.0
IB = 0.10/12.0

# Term of loans (# of timesteps)
# B loan's min payment will be computed within loop
N = 100.0

x = 10000.0
nx = 800
max_iters = 9000
a = 100.0
L0A = 500.0
L0B = L0A + np.linspace(0.0, x, nx)
dL0 = L0B - L0A

mp1 = compute_monthly_loan_payment(L0A,IA,N)

Tgd = []
Tdi = []
for LB in L0B:

    L = np.array( [L0A, LB] )
    I = np.array( [IA, IB] )
    mpB = compute_monthly_loan_payment(LB, IB, N)
    mp = np.array( [mp1, mpB] )

    w, n, grand_total_paid = gradient_descent_algo1(L, I, mp, a, max_iters, 0.01)
    grand_total, tsteps = descending_interest_method(L, I, mp, a, 2)
    Tgd.append(grand_total_paid)
    Tdi.append(grand_total)

Tgd = np.array(Tgd)
Tdi = np.array(Tdi)

fig, ax = plt.subplots(2,1,figsize=(9,9),sharex=True)
ax[0].plot(dL0,Tdi,color='green',label='descending interest')
ax[0].plot(dL0,Tgd,color='orange',label='gradient descent')
ax[0].grid()
ax[0].legend()
#ax[0].set_xlabel('difference between loan principals ($)')
ax[0].set_ylabel('total amount paid ($)')
ax[1].plot(dL0,(Tdi-Tgd),color='blue')
ax[1].set_xlabel('difference between loan starting principals ($)')
ax[1].set_ylabel('difference in total amount paid ($)\n descending interest minus gradient descent')
ax[1].grid()
fig.suptitle('Comparison of methods using 2-loan scenario, varying only the starting\n principal of one of the loans.')
plt.show()


#!/usr/bin/env python
# coding: utf-8
import numpy as np
#This is a implementation and library for binary classification, a.k.a logistic regression

'''
In order to implement logistic regression, we need to initialize parameters w and b, note that when using logistic regression,
we have only one computing unit in the neural network. Therefore parameters can be initialized to all zeros.
However, if we are using multiple computing units(e.g. sigmoid or relu), it is required to initialize the parameter w randomly, while
b can be set to all zeros.
'''
def logistic_parameter_initialize(x_dim):
    # x_dim indicates the dimensions of input feature,a bias unit b is defaultly set.
    w = np.zeros((1,x_dim))
    b = 0

    parameter = {'w':w,'b':b}
    return parameter

#implementation of sigmoid function
def sigmoid(z):
    value = 1/(1+np.exp(-z))
    return value

#note the dimensions of vectors:
# w - (1,x_dims)
# b - (1,x_dims) *broadcasted*
# X - (x_dims,m) *m=number of samples

# Forward propagation step: compute the predicted y's label
def forward_prop(w,b,X):
    z = np.dot(w,X)+b
    a = sigmoid(z)
    return z,a

# Compute cost function: used to check convergence
def compute_cost(a,y):
    m = a.shape[1]
    cost = -np.sum(y*np.log(a)+(1-y)*np.log(1-a))/m
    return cost

# Back propagation step: compute partial derivatives of each parameter respectively
def back_prop(X,a,y):
    m = a.shape[1]
    dz = a - y
    dw = np.dot(X,dz.T).T/m
    db= np.sum(dz)/m
    # Note: dw should have the same dimension as w have.Therefore back_prop return dw.T
    return dw,db

# The overall implementation of trainning a logistic regression
def train_logistic_regression(X,y,number_of_iteration = 1000,learning_rate = 0.03,print_cost = True,plot_cost = True):
    # Dimension convert: make sure all vectors are in proper shapes.
    y = y.reshape(1,-1)   # y is a row vector
    m = y.shape[1]  #  m = total number of trainning examples
    if(X.shape[1] != m):
        X=X.T       #=====> Note that array.reshape and array.T are different!
    x_dim = X.shape[0]
    # Initialize parameters
    params = logistic_parameter_initialize(x_dim)
    w = params['w']
    b = params['b']

    if(plot_cost == True):
        i_curve = []
        cost_curve = []
        plt.figure(figsize=(5,5))
        plt.title('Cross entrophy of regression')

    for i in range(1,number_of_iteration+1):
        z,a = forward_prop(w,b,X)
        dw,db = back_prop(X,a,y)
        w = w - learning_rate*dw
        b = b - learning_rate*db
        cost = compute_cost(a,y)
        # Visualize the process of regression
        if(i%100 == 0 and print_cost == True):
            print('number of iterations:{}, cost = {}'.format(i,cost))
        if(i%100 == 0 and plot_cost == True):
            i_curve.append(i)
            cost_curve.append(cost)
    if(plot_cost==True):
        i_curve = np.reshape(i_curve,(1,-1))
        cost_curve = np.reshape(cost_curve,(1,-1))
        plt.scatter(i_curve,cost_curve)

    return w,b

#After training the unit, we can now use it to make predictions.
def logistic_predict(w,b,X,y=0,evaluate = True):
    if(not w.shape[1] == X.shape[0]):
        X = X.T
    yhat = np.dot(w,X)+b
    yhat = yhat>0.5
    #Codes below is used to evaluate the performance of logistic regression on given dataset X with label y
    #You can just ignore this part
    if(evaluate == True):
        y=y.reshape(1,-1)
        train_accuracy = np.sum(yhat==y)/y.shape[1]
        print('accuracy = %.2f\n'%train_accuracy)
    return yhat


import numpy as np
import sys

def matrix_chain_order(p):
	n = len(p) - 1
	m = np.zeros((n, n)).astype(int)
	s = np.zeros((n, n)).astype(int)

	for l in range(2, n+1):
		for i in range(0, n-l+1):
			j = i + l - 1
			m[i, j] = sys.maxint
			for k in range(i, j):
				q = m[i, k] + m[k+1, j] + p[i] * p[k+1] * p[j+1]
				if q < m[i, j]:
					m[i, j] = q
					s[i, j] = k

	return (m, s)

def print_optimal_parens(s, i, j):
	if i == j:
		sys.stdout.write("A" + str(i+1))
	else:
		sys.stdout.write("(")
		print_optimal_parens(s,i,s[i, j])
		print_optimal_parens(s, s[i, j] + 1, j)
		sys.stdout.write(")")

def main():
	#p = [30, 35, 15, 5, 10, 20, 25]
	p = [5, 10, 3, 12, 5, 50, 6]
	(m, s) = matrix_chain_order(p)

	print "m =\n", m
	print "s =\n", s

	print_optimal_parens(s, 0, 5)
	print

if __name__ == "__main__":
	main()


import numpy as np
from numpy import linalg as la
from matplotlib import pyplot as plt
import bisection as bis
class Levenberg():

    def descent(self, X = np.array([[0.0],[0.0]]), precision = 10**-6):
        self.Xs = []       # Candidates
        self.Xs.append(X)  # First Candidate (x0)
        self.precision = precision

        while la.norm(self.gradF(self.Xs[-1])) > self.precision:
            X = self.Xs[-1]
            grT = self.buildGradientR().T
            H = self.hessiana(X)
            d = np.dot(np.dot(la.inv(H), grT), self.r(X))
            X = X - 1*d  # TODO: calculate optimal alpha
            self.Xs.append(X)

        return self.Xs[-1]

    def r(self, X):
        x1 = X[0][0]
        x2 = X[1][0]

        return np.array([[4*(x1 - 2)], [4*(x2 - 3)]])

    ''' Builds the matrix of gradient of r(x)'''
    def buildGradientR(self):   # TODO: Transform function in CONSTANT
        return np.array([[4, 0],
                         [0, 4]])

    def gradF(self, X):
        grT = self.buildGradientR().T
        r = self.r(X)

        return np.dot(grT, r)

    ''' Returns Gradient of r(x)T * Gradient of r(x)'''
    def hessiana(self, X):
        grT = self.buildGradientR().T
        gr = self.buildGradientR()

        return np.dot(grT, gr)

    def plot(self, title, xlabel, ylabel):
        x1s = []
        x2s = []

        for X in self.Xs:
            x1s.append(X[0][0])
            x2s.append(X[1][0])

        plt.plot(x1s, x2s, 'ro')
        plt.title(title)
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        plt.show()


from mpi4py import MPI
import numpy

def paralelo_pi(n):
    count = 0
    for x, y in n:
        if x**2 + y**2 <= 1:
            count += 1
    pi = 4*float(count)/len(n)
    return pi

comm = MPI.COMM_WORLD
processadores = comm.Get_size()
oRank = comm.Get_rank()

if oRank == 0:
    N = 100000
    n = numpy.random.random((processadores, N, 2))
else:
    n = None
n = comm.scatter(n, root=0)

piBuffer = paralelo_pi(n)

somaPi = comm.reduce(piBuffer, root=0)

if oRank == 0:
    pi = somaPi / processadores
    error = abs(pi - numpy.pi)
    print("pi: %.16f, erro: %.16f" % (pi, error))


# -*- coding: utf-8 -*-
'''This module provides functions related to Digital Signal Processing and Fourier Analysis.'''
import numpy as np
import matplotlib.pyplot as plt

def make_sinusoidal_signal(
    mean: float=0,
    amp: float=1.0,
    freq: int=1,
    points: int=100,
    noise_mean: float=0.0,
    noise_var: float=0.1):
    """
    Generate a synthetic signal.

    mean: Signal baseline or average.
    amp: Signal amplitude
    freq: number of times you repeat one cycle
    points: number of points (samples) within each cycle
    noise_mean: mean of the white noise signal
    noise_var: variance of the white noise signal. Note that this impacts your signal/noise ratio
    """
    one_cycle = amp * np.sin(np.linspace(0, 2*np.pi, num=points))
    full_signal = np.tile(one_cycle, freq) + mean
    total_length = points * freq
    noise = np.random.normal(noise_mean, noise_var, size=total_length)
    return full_signal + noise

def dft_report(signal):
    # compute dft
    fft = np.fft.fft(signal)

    # get real components (index is freq as k/N of 2pi, value is the energy)
    real = np.abs(np.real(fft))

    # keep only the first half
    midpoint = int(signal.size / 2)
    real = real[:midpoint] / signal.size

    top_freq_index = np.argmax(real[1:]) + 1
    top_freq_amp = real[top_freq_index] * 2
    print(f'Average = {real[0]:.2f}', )
    print(f'Index of most prominent freq = {top_freq_index}')
    print(f'Top Freq Amplitude = {top_freq_amp:.4f}')
    print(f'''Period of the most prominent freq = {int(signal.size/top_freq_index)}''')
    plt.stem(2*real[1:]);


#===================================================================================
#
#   Code (đã cái tiến) cho PP chia đôi.
#       * Input: f(x) trong pt f(x) = 0; khoảng cách li ban đầu (a, b); sai số epsilon
#       * Output: Nghiệm PT f(x) = 0;
#       * Hạn chế: Chưa có gói tìm khoảng cách ly nghiệm
#       * Cải tiến: Giảm khối lượng tính toán - tính f(a) 1 lần + thêm SymPy
#
#===================================================================================
from sympy import *
from math import *
import sys

#===================================================================================
# Phần thuật toán chính
class bisection_oop:
#{
    def __init__(self, a_0, b_0, eps, expr):
    #{
        self.symf = sympify(expr);
        self.f = lambdify(symbols("x"), self.symf, "math");
        self.a_0 = a_0;
        self.b_0 = b_0;
        self.eps = eps;

    def __checkInputValidity(self):
    #{
        L = self.a_0
        R = self.b_0

        # Corner case: f(L) = 0 or f(R) = 0
        if(self.f(L) == 0 or self.f(R) == 0): return 1

        # Check if a < b
        if(L > R or (L == R and self.f(L) != 0)): return 0

        # Check if f(a) * f(b) < 0
        if(self.f(L) * self.f(R) >= 0): return 0

        return 1
    #}
    def __bisectionMethod(self):
    #{
        # Internal function
        # Return root of f(x) = 0 which f(x), eps and range [a_0, b_0] are given.
        # Assign [a, b] and eps
        nIterations = 0
        left    = self.a_0
        right   = self.b_0
        epsilon = self.eps

        # Special case: f(a) = 0 or f(b) = 0
        if(self.f(left) == 0): return left
        if(self.f(right) == 0): return right

        # Evaluation phase
        mid   = (left + right) / 2
        lft_sign = 1 if self.f(left) >= 0 else -1
        while abs(right - left) >= epsilon:
        #{
            mid = (left + right) / 2
            val = self.f(mid)

            # print(left, mid, right, sep=',', file=sys.stderr)

            if(val == 0): return mid
            if(val * lft_sign < 0):
                right = mid
            else:
                left = mid

            nIterations = nIterations + 1
        #}

        # print(left, mid, right, sep=',', file=sys.stderr)
        print(f"Phương pháp chia đôi kết thúc sau {nIterations} lần lặp")
        return mid
    #}

    def Solve(self):
    #{
        if(self.__checkInputValidity() == 0):
        #{
            print("Invalid input. The program will now exit", file=sys.stderr)
            return float("NaN")
        #}
        return self.__bisectionMethod()
    #}
#}

#===================================================================================
# Chương trình ví dụ
# uu = bisection_oop(L, R, eps, input_expr)
# print(uu.Solve())
#uu = bisection_oop(0.008, 0.08, 1e-10, "1/sqrt(x)+2*log(0.0000015/(3.7*0.005)+ 2.51/(13.743*sqrt(x)))/log(10)")
print(f" Nghiệm của PT f(x) = 0 theo PP chia đôi là: x = {uu.Solve()}")

Topic 1 - Find solution for single-variable functions/02.Chia_doi/bisection.py


import numpy as np

from pySDC.core.Collocation import CollBase
from pySDC.core.Errors import CollocationError

class CollGaussLegendre(CollBase):
    """
    Implements Gauss-Legendre Quadrature

    Attributes:
        order (int): order of the quadrature
        num_nodes (int): number of collocation nodes
        tleft (float): left interval point
        tright (float): right interval point
        nodes (numpy.ndarray): array of quadrature nodes
        weights (numpy.ndarray): array of quadrature weights for the full interval
        Qmat (numpy.ndarray): matrix containing the weights for tleft to node
        Smat (numpy.ndarray): matrix containing the weights for node to node
        delta_m (numpy.ndarray): array of distances between nodes
        right_is_node (bool): flag to indicate whether right point is collocation node
        left_is_node (bool): flag to indicate whether left point is collocation node
    """

    def __init__(self, num_nodes, tleft, tright):
        """
        Initialization

        Args:
            num_nodes (int): number of nodes
            tleft (float): left interval boundary (usually 0)
            tright (float): right interval boundary (usually 1)
        """
        super(CollGaussLegendre, self).__init__(num_nodes, tleft, tright)
        if num_nodes < 1:
            raise CollocationError("Number of nodes should be at least 1 for Gauss-Legendre, but is %d" % num_nodes)
        self.order = 2 * self.num_nodes
        self.nodes = self._getNodes
        self.weights = self._getWeights(tleft, tright)
        self.Qmat = self._gen_Qmatrix
        self.Smat = self._gen_Smatrix
        self.delta_m = self._gen_deltas
        self.left_is_node = False
        self.right_is_node = False

    @property
    def _getNodes(self):
        """
        Computes nodes for the Gauss-Legendre quadrature

        Python version by Dieter Moser, 2014

        Returns:
            np.ndarray: array of Gauss-Legendre nodes
        """
        M = self.num_nodes
        a = self.tleft
        b = self.tright

        # Building the companion matrix comp_mat with det(nodes*I-comp_mat)=P_n(nodes), where P_n is the
        # Legendre polynomial under consideration. comp_mat will be constructed in such a way that it is symmetric.
        linspace = np.linspace(1, M - 1, M - 1)
        v = [linspace[i] / np.sqrt(4.0 * linspace[i] ** 2 - 1.0) for i in range(len(linspace))]
        comp_mat = np.diag(v, 1) + np.diag(v, -1)

        # Determining the abscissas (nodes) - since det(nodesI-comp_mat)=P_n(nodes), the abscissas are the roots
        # of the characteristic polynomial, i.e. the eigenvalues of comp_mat
        [eig_vals, _] = np.linalg.eig(comp_mat)
        indizes = np.argsort(eig_vals)
        nodes = eig_vals[indizes]

        # take real part and shift from [-1,1] to [a,b]
        nodes = nodes.real
        nodes = (a * (1 - nodes) + b * (1 + nodes)) / 2

        return nodes


import numpy as np

__all__ = [
    "prop",
    "gini_impurity",
    "entropy",
    "info_gain",
]

def prop(x, s):
    """Returns the proportion of `x` in `s`.
    """
    return list(s).count(x)/len(s)

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

def gini_impurity(s):
    """Calculate the Gini Impurity for a list of samples.

    See:
    https://en.wikipedia.org/wiki/Decision_tree_learning#Gini_impurity
    """
    return 1 - sum(prop(s[i], s)**2 for i in range(len(s)))

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

def entropy(s):
    """Calculate the Entropy Impurity for a list of samples.
    """
    return -sum(
        p*np.log(p) for i in range(len(s)) for p in [prop(s[i], s)]
    )

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

def info_gain(current_impurity, true_branch, false_branch, criterion):
    """Information Gain.

    The uncertainty of the starting node, minus the weighted impurity of
    two child nodes.
    """

    measure_impurity = gini_impurity if criterion == "gini" else entropy
    p = float(len(true_branch)) / (len(true_branch) + len(false_branch))

    return current_impurity - p * measure_impurity(true_branch) - (1 - p) * measure_impurity(false_branch)

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


import numpy as np

# Write a function that takes as input two lists Y, P,
# and returns the float corresponding to their cross-entropy.

def cross_entropy(Y, P):
    return -1 * sum(
        [
            (Y[i] * np.log(P[i]) + (1 - Y[i]) * np.log(1 - P[i]))
            for i in range(len(Y))
        ]
    )

if __name__ == "__main__":
    Y = [1, 0, 1, 1]
    P = [0.4, 0.6, 0.1, 0.5]
    print(cross_entropy(Y, P))
    print('should be: 4.8283137373')


from numpy import array, pi, sin
from NIM.benchmarks.benchmark import Benchmark

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

    def get_optimum(self):
        return array([[1, 1]]), 1.3497838043956716e-31

    @staticmethod
    def eval(sol):
        temp1 = (sin(3 * pi * sol[0]) ** 2)
        temp2 = (sol[0] - 1) ** 2 * (1 + (sin(3 * pi * sol[1])) ** 2)
        temp3 = (sol[1] - 1) ** 2 * (1 + (sin(2 * pi * sol[1])) ** 2)

        return temp1 + temp2 + temp3


import numpy as np

def hyperbolic_gammas(gamma_max, k, num_heads):
    # Formula (27) from https://arxiv.org/pdf/1902.06865.pdf
    b = np.exp(np.log(1 - gamma_max**(1 / k)) / num_heads)
    gammas = (1 - b**(np.arange(num_heads) + 1))**k
    return gammas


def midpoint_double1(f, a, b, c, d, nx, ny):
    hx = (b - a)/nx
    hy = (d - c)/ny
    I = 0
    for i in range(nx):
        for j in range(ny):
            xi = a + hx/2 + i*hx
            yj = c + hy/2 + j*hy
            I = I + hx*hy*f(xi, yj)
    return I

def midpoint(f, a, b, n):
    h = (b-a)/n
    f_sum = 0
    for i in range(0, n, 1):
        x = (a + h/2.0) + i*h
        f_sum = f_sum + f(x)
    return h*f_sum

def midpoint_double2(f, a, b, c, d, nx, ny):
    def g(x):
        return midpoint(lambda y: f(x, y), c, d, ny)

    return midpoint(g, a, b, nx)

def test_midpoint_double():
    """Test that a linear function is integrated exactly."""
    def f(x, y):
        return 2*x + y

    a = 0;  b = 2;  c = 2;  d = 3
    import sympy
    x, y = sympy.symbols('x  y')
    I_expected = sympy.integrate(f(x, y), (x, a, b), (y, c, d))
    # Test three cases: nx < ny, nx = ny, nx > ny
    for nx, ny in (3, 5), (4, 4), (5, 3):
        I_computed1 = midpoint_double1(f, a, b, c, d, nx, ny)
        I_computed2 = midpoint_double2(f, a, b, c, d, nx, ny)
        tol = 1E-14
        #print I_expected, I_computed1, I_computed2
        assert abs(I_computed1 - I_expected) < tol
        assert abs(I_computed2 - I_expected) < tol

if __name__ == '__main__':
    test_midpoint_double()


# test_bernstein_polynomial.py
"""
This module is a unit test of the bernstein_polynomial implementation.

To run
$ conda load siblenv
$ cd ~/sibl
$ black --check geo/tests/test_bernstein_polynomial.py
$ pytest geo/tests/test_bernstein_polynomial.py -v
$ pytest geo/tests/test_bernstein_polynomial.py -v --cov=geo/src/ptg --cov-report term-missing
"""
# from unittest import TestCase, main
from unittest import TestCase

import numpy as np

# import ptg.code.bernstein_polynomial as bp
import ptg.bernstein_polynomial as bp

class TestBernstein(TestCase):
    @classmethod
    def setUpClass(cls):
        cls._TOL = 1e-6  # tolerance
        cls._nti = 4  # number of time intervals
        # t, e.g., four intervals, five evaluation points
        cls._t = np.linspace(0, 1, cls._nti + 1)
        cls._verbosity = False

    @classmethod
    def same(cls, a, b):
        same_to_tolerance = False
        l2norm_diff = np.linalg.norm(a - b)

        if cls._verbosity:
            print(f"array a = {a}")
            print(f"array b = {b}")
            print(f"l2norm_diff = {l2norm_diff}")

        if np.abs(l2norm_diff) < cls._TOL:
            same_to_tolerance = True

        return same_to_tolerance

    def test_b00_input_out_of_range_and_verbose(self):
        i, p = 0, 0
        verbose = True
        calc = bp.bernstein_polynomial(i, p, self._nti, verbose=verbose)
        self.assertIsNone(calc)

    def test_b01_and_verbose(self):
        known = 1 - self._t
        i, p = 0, 1
        verbose = True
        calc = bp.bernstein_polynomial(i, p, self._nti, verbose=verbose)
        self.assertTrue(self.same(known, calc))

    def test_b11(self):
        known = self._t
        i, p = 1, 1
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b02(self):
        known = (1 - self._t) ** 2
        i, p = 0, 2
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b12(self):
        known = 2 * self._t * (1 - self._t)
        i, p = 1, 2
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b22(self):
        known = (self._t) ** 2
        i, p = 2, 2
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b03(self):
        known = (1 - self._t) ** 3
        i, p = 0, 3
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b13(self):
        known = 3 * self._t * (1 - self._t) ** 2
        i, p = 1, 3
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b23(self):
        known = 3 * (self._t) ** 2 * (1 - self._t)
        i, p = 2, 3
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b33(self):
        known = (self._t) ** 3
        i, p = 3, 3
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b04(self):
        known = (1 - self._t) ** 4
        i, p = 0, 4
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b14(self):
        known = 4 * self._t * (1 - self._t) ** 3
        i, p = 1, 4
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b24(self):
        known = 6 * (self._t) ** 2 * (1 - self._t) ** 2
        i, p = 2, 4
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b34(self):
        known = 4 * (self._t) ** 3 * (1 - self._t)
        i, p = 3, 4
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

    def test_b44(self):
        known = (self._t) ** 4
        i, p = 4, 4
        calc = bp.bernstein_polynomial(i, p, self._nti)
        self.assertTrue(self.same(known, calc))

# if __name__ == "__main__":
#     main()  # calls unittest.main()


import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn import decomposition
from scipy import linalg as la

iris = load_iris()

def iris_base():
    fig = plt.figure(figsize=(10, 10))
    ax = fig.add_subplot(111, aspect='equal')

    plt.plot(iris.data[50:150,0], iris.data[50:150,2], 'k.')
    plt.xlim([2, 8])
    plt.ylim([2, 8])
    plt.xlabel(r"Sepal Length (cm)")
    plt.ylabel(r"Petal Length (cm)")

    return fig

def ibase():
    iris_base()
    plt.savefig('iris0.pdf')
    plt.clf()

def iris1():
    fig = iris_base()

    pca = decomposition.PCA(n_components=2)
    pca.fit(iris.data[50:150, np.array([0, 2])])
    mean = np.mean(iris.data[50:150, np.array([0, 2])], 0)
    stds = np.std(iris.data[50:150, np.array([0, 2])], 0)
    components = pca.components_

    plt.quiver(mean[0], mean[1], 1.5 * stds[0], 0, scale_units='xy', angles='xy', scale=1)
    plt.quiver(mean[0], mean[1], 0, 1.5 * stds[1], scale_units='xy', angles='xy', scale=1)
    plt.savefig('iris1.pdf')
    plt.clf()

def iris2():
    fig = iris_base()

    pca = decomposition.PCA(n_components=2)
    pca.fit(iris.data[50:150, np.array([0, 2])])
    mean = np.mean(iris.data[50:150, np.array([0, 2])], 0)
    stds = np.std(iris.data[50:150, np.array([0, 2])], 0)
    components = pca.components_
    variance_ratio = pca.explained_variance_ratio_

    plt.quiver(mean[0], mean[1],
               -2 * variance_ratio[0] * components[0,0],
               -2 * variance_ratio[0]*components[0,1],
               scale_units='xy', angles='xy', scale=1)
    plt.quiver(mean[0], mean[1],
               5 * variance_ratio[1] * components[1,0],
               5 * variance_ratio[1] * components[1,1],
               scale_units='xy', angles='xy', scale=1)
    plt.savefig('iris2.pdf')
    plt.clf()

def iris_pca():
    X = iris.data
    # pre-process
    Y = X - X.mean(axis=0)
    # get SVD
    U,S,VT = la.svd(Y,full_matrices=False)
    # project onto the first two principal components
    Yhat = U[:,:2].dot(np.diag(S[:2]))
    # plot results
    setosa = iris.target==0
    versicolor = iris.target==1
    virginica = iris.target==2
    p1, p2 = Yhat[:,0], Yhat[:,1]
    plt.scatter(p1[setosa],p2[setosa], marker='.', color='blue', label='Setosa')
    plt.scatter(p1[versicolor],p2[versicolor], marker='.', color='red', label='Versicolor')
    plt.scatter(p1[virginica],p2[virginica], marker='.', color='green', label='Virginica')
    plt.legend(loc=2)
    plt.ylim([-4,5])
    plt.xlim([-4,4])
    plt.xlabel("First Principal Component")
    plt.ylabel("Second Principal Component")
    plt.savefig('iris_pca.pdf')
    plt.clf()

def iris_scree():
    X = iris.data
    # pre-process
    Y = X - X.mean(axis=0)
    # get SVD
    U,S,VT = la.svd(Y,full_matrices=False)
    L = S**2
    plt.plot(L/L.sum(dtype=float), 'o-')
    plt.xlabel("Principal Components")
    plt.ylabel("Percentage of Variance")
    plt.savefig('iris_scree.pdf')
    plt.clf()

if __name__ == "__main__":
    iris_scree()
    iris_pca()


import numpy as np
import scipy.linalg as la

#######################################################################
# This function is an implementation of Newton's method for nonlinear #
# systems of equations.                                               #
#                                                                     #
#   PARAMETERS:                                                       #
#       F:  np column matrix of equations f1, f2, ... fn.             #
#       DF: Jacobian of F.                                            #
#       X:  Initial guess for solution set.                           #
#       n:  Number of iterations.                                     #
#                                                                     #
# Currently prints solution iteratively, but can be easily changed to #
# return the final value of X.                                        #
#                                                                     #
#       Colin McKinley | September 2018                               #
#######################################################################

def multNewton(F, Df, X, n):
    #Evaluation of lambdas must be done on 1d array
    #Arrays are flattened and reshaped
    DfShape = np.shape(Df)
    FShape = np.shape(F)
    XShape = np.shape(X)

    #Create arrays to hold values
    DfNums = np.zeros((np.size(Df)))
    FNums = np.zeros((np.size(F)))
    for i in range(n):
        print i
        print "="*25
        #Fill Number arrays with values from lambdas
        for j, f in enumerate(F.flatten()):
            FNums[j] = f(X[0])

        for k, df in enumerate(Df.flatten()):
            DfNums[k] = df(X[0])

        #Reshape for matrix operations
        FNums = FNums.reshape(FShape)
        DfNums = DfNums.reshape(DfShape)

        #Solve system
        Dx = la.solve(-DfNums, FNums)

        #Transpose X to column
        X = X.T
        X += Dx

        #Flatten back to 1d arrays
        FNums = FNums.flatten()
        DfNums = DfNums.flatten()
        X = X.reshape(XShape)
        print X
    return "EXIT_SUCCESS"

#Simple test case with f1 = x0^3 + x1 - 1 & f2 = x1^3 -
if __name__ == "__main__":
    #Functions for f
    F = np.array([[lambda x: x[0]**3 + x[1] - 1],
                  [lambda x: x[1]**3 - x[0] + 1]])

    #Partial derivatives
    Df11 = lambda x: 3*(x[0]**2) + x[1] - 1
    Df12 = lambda x: 1
    Df21 = lambda x: -1
    Df22 = lambda x: 3*(x[0]**2) + x[1] - 1
    Df = np.array([[Df11, Df12], [Df21, Df22]])
    X = np.array([.5, .5])[np.newaxis]
    n = 100
    print(multNewton(F, Df, X, n))


import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plot
import numpy, scipy, cvxpy, pprint, itertools
from scipy.spatial import ConvexHull
from patch import *

def random_2d_convex_hull():
  ps = numpy.random.rand(30, 2)
  hull = ConvexHull(ps)

  print("ps= {}".format(ps) )

  plot.plot(ps[:,0], ps[:,1], 'o')
  for simplex in hull.simplices:
    plot.plot(ps[simplex, 0], ps[simplex, 1], 'k-')
  plot.plot(ps[hull.vertices,0], ps[hull.vertices,1], 'r--', lw=2)
  plot.plot(ps[hull.vertices[0],0], ps[hull.vertices[0],1], 'ro')
  # plot.show()
  plot.savefig("random_2d_convex_hull.png")
  plot.gcf().clear()

def stability_region_mds_4_2():
  ps = numpy.array([ [0, 0],
    [2.5, 0], [0, 2.5], [2, 0], [0, 2], [2, 1], [1, 2] ] )
  hull = ConvexHull(ps)

  for simplex in hull.simplices:
    plot.plot(ps[simplex, 0], ps[simplex, 1], 'k-')
  plot.plot(ps[hull.vertices,0], ps[hull.vertices,1], 'r--', lw=2)
  plot.plot(ps[hull.vertices[0],0], ps[hull.vertices[0],1], 'ro')
  # plot.show()
  plot.savefig("stability_region_mds_4_2.png")
  plot.gcf().clear()

def opt():
  # Problem data.
  m = 30
  n = 20
  numpy.random.seed(1)
  A = numpy.random.randn(m, n)
  b = numpy.random.randn(m)

  # Construct the problem.
  x = Variable(n)
  obj = Minimize(sum_squares(A*x - b))
  constraints = [0 <= x, x <= 1]
  prob = Problem(obj, constraints)

  # The optimal obj is returned by prob.solve().
  result = prob.solve()
  # The optimal value for x is stored in x.value.
  print("x= {}".format(x.value) )
  # The optimal Lagrange multiplier for a constraint is stored in constraint.dual_value.
  print("constraints[0].dual_value= {}".format(constraints[0].dual_value) )

def plot_hull_of_ps(p_l, fname, title):
  ps = numpy.empty((len(p_l), 2))
  for i,p in enumerate(p_l):
    # ps[i,:] = [[p[0], p[1]]]
    ps[i,0] = p[0]
    ps[i,1] = p[1]
  print("ps= {}".format(ps) )
  plot.plot(ps[:,0], ps[:,1], 'o')
  """
  hull = ConvexHull(ps)
  for simplex in hull.simplices:
    plot.plot(ps[simplex, 0], ps[simplex, 1], 'k-')
  plot.plot(ps[hull.vertices,0], ps[hull.vertices,1], 'r--', lw=2)
  plot.plot(ps[hull.vertices[0],0], ps[hull.vertices[0],1], 'ro')
  """

  # plot.show()
  # axes = plot.gca()
  # axes.set_xlim([0, 2] )
  plot.title(title)
  plot.savefig(fname)
  plot.gcf().clear()

"""
# Matrix factorization
alpha = 0.002 # 0.0002
D = numpy.zeros((n,2))
step = 0
while step < 100000:
  step += 1
  for i in range(n):
    for j in range(2):
      if M[i,j] == 1:
        e_ij = M[i,j] - numpy.dot(D[i,:], C[:,j])
        for k in range(2):
          D[i][k] = D[i][k] + alpha*(2*e_ij * C[k][j] )
  # print("step= {}, D=\n{}".format(step, D) )
  e = 0
  for i in range(n):
    for j in range(2):
      e = e + pow(M[i][j] - numpy.dot(D[i,:], C[:,j]), 2)
  if e < 0.1:
    break
print("step= {}, D=\n{}".format(step, D) )

M_ = numpy.dot(D, C)
print("M=\n{}".format(M_) )
"""

def generator_matrix_to_M_C(G):
  n = G.shape[1]
  s_rg_l = [[], []]

  for s in range(0, 2):
    for c in range(n):
      m = numpy.column_stack((G[:,s], G[:,c]))
      if numpy.linalg.det(m) == 0: # c is a systematic node for s
        s_rg_l[s].append((c,))

    for subset in itertools.combinations(range(n), 2):
      if s in subset:
        continue
      m = numpy.column_stack((G[:,subset[0]], G[:,subset[1]]))
      # print("m= {}".format(m) )
      if numpy.linalg.det(m):
        # print("columns {}, {} are LI".format(os, c) )
        s_rg_l[s].append((subset[0], subset[1]))
  print("s_rg_l= {}".format(pprint.pformat(s_rg_l) ) )

  r_0 = len(s_rg_l[0] )
  r_1 = len(s_rg_l[1] )
  r = r_0 + r_1
  # if r != len(s_rg_l[1] ):
  #   log(ERROR, "Code was supposed to be symmetric, but it is not.")
  #   return 1
  x = s_rg_l[0] + s_rg_l[1]
  M = numpy.zeros((n,r))
  for i in range(n):
    for j in range(r):
      if i in x[j]:
        M[i,j] = 1
  print("M= {}".format(M) )

  C = numpy.zeros((2 ,r))
  C[0,0:r_0] = 1
  C[1,r_0:r] = 1
  print("C= {}".format(C) )

  return r, M, C

def generator_matrix(code, n, k=2):
  if k != 2:
    log(ERROR, "Only for k=2")
    return 1
  if code == 'MDS':
    G = numpy.zeros((2, n))
    for j in range(n):
      if j == 0:
        G[0,j] = 1
        G[1,j] = 0
      elif j == 1:
        G[0,j] = 0
        G[1,j] = 1
      else:
        G[0,j] = j-1
        G[1,j] = 1
    return G
  else:
    log(ERROR, "unexpected code= {}".format(code) )
    return 1

def plot_xy_stability_region(n):
  # Rep(2)
  # G = numpy.matrix([[1,0], [0,1]])
  # G = numpy.matrix([[1,0], [0,1], [1,0], [0,1] ])
  # MDS(3,2)
  # G = numpy.matrix([[1,0], [0,1], [1,1]])
  # MDS(4,2)
  # G = numpy.matrix([[1,0], [0,1], [1,1], [2,1]])
  # MDS(5,2)
  # G = numpy.matrix([[1,0], [0,1], [1,1], [2,1], [3,1]])
  # MDS(6,2)
  # G = numpy.matrix([[1,0], [0,1], [1,1], [2,1], [3,1], [4,1]])
  # MDS(7,2)
  # G = numpy.matrix([[1,0], [0,1], [1,1], [2,1], [3,1], [4,1], [5,1]])
  # Mixed
  # G = numpy.matrix([[1,0], [0,1], [1,1], [2,1], [3,1], [1,0] ])
  # G = numpy.matrix([[1,0], [0,1], [1,1], [2,1], [3,1], [1,1] ])
  # G = numpy.matrix([[1,0], [0,1], [1,1], [1,0], [0,1], [1,1] ])
  # G = G.transpose()

  code = 'MDS' # 'Rep' # 'MDS' # 'Mixed'
  G = generator_matrix(code, n)

  print("G= {}".format(G) )
  n = G.shape[1]
  r, M, C = generator_matrix_to_M_C(G)
  p_l = []
  #
  x = cvxpy.Variable(r, 1, name='x')
  for b in numpy.linspace(0, 1, 20):
    # print("b= {}".format(b) )

    length = math.sqrt((1-b)**2 + b**2)
    w = numpy.matrix([[(1-b)/length, b/length]] )
    # print("w.shape= {}, w= {}".format(w.shape, w) )
    w_ = w*C
    # print("w_= {}".format(w_) )
    # obj = cvxpy.Maximize(w*(C*x) )
    obj = cvxpy.Maximize(w_*x)
    # print("obj= {}".format(obj) )
    constraints = [M*x == 1, x >= 0] # [M*x <= 1, x >= 0]
    prob = cvxpy.Problem(obj, constraints)
    # print("prob= {}".format(prob) )
    prob.solve()
    print("status= {}".format(prob.status) )
    # print("optimal value= {}".format(prob.value) )
    y = C*(x.value)
    # print("optimal y= {}".format(y) )
    p_l.append((y[0], y[1]) )
  plot_hull_of_ps(p_l, "plot_xy_stability_region_{}_n_{}.png".format(code, n),
                  title='{}, n= {}, k= 2'.format(code, n) )
  log(WARNING, "done, code= {}, n= {}".format(code, n) )

if __name__ == "__main__":
  # random_2d_convex_hull()
  # stability_region_mds_4_2()
  # opt()

  plot_xy_stability_region(n=4)
  # for n in range(3, 10):
  #   plot_xy_stability_region(n)
  # plot_xy_stability_region(n=100)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Created by techno at 27/05/19

#Feature:  #Enter feature name here
# Enter feature description here

#Scenario:  # Enter scenario name here
# Enter steps here
import numpy as np

x = np.array([[1,2,3],
 [3,4,5]])
y = np.array([6,7,8])

# Add `x` and `y`
print (np.add(x,y) )

# Subtract `x` and `y`
np.subtract(x,y)

# Multiply `x` and `y`
np.multiply(x,y)

# Divide `x` and `y`
np.divide(x,y)

# Calculate the remainder of `x` and `y`
np.remainder(x,y)

"""
Remember how broadcasting works? Check out the dimensions and the shapes of
both x and y in your IPython shell. Are the rules of broadcasting respected?

But there is more.

Check out this small list of aggregate functions:
a.sum() 	Array-wise sum
a.min() 	Array-wise minimum value
b.max(axis=0) 	Maximum value of an array row
b.cumsum(axis=1) 	Cumulative sum of the elements
a.mean() 	Mean
b.median() 	Median
a.corrcoef() 	Correlation coefficient
np.std(b) 	Standard deviation

Besides all of these functions, you might also find it useful to know that there are
mechanisms that allow you to compare array elements. For example, if you want to
check whether the elements of two arrays are the same, you might use the == operator.
To check whether the array elements are smaller or bigger, you use the < or > operators.
"""


# This code is for fully educational purposes and it implements the simulated annealing AI algorithm
# for more information please visit: https://en.wikipedia.org/wiki/Simulated_annealing

import numpy
import time
import random

NEIGHBORHOOD_MAX = 40
NEIGHBORHOOD_MIN = 0
T_MAX = 100
T_MIN = 0.01

def f(x):
	assert NEIGHBORHOOD_MIN <= x <= NEIGHBORHOOD_MAX
	return numpy.sin(0.15 * x) + numpy.cos(x)

def acceptance(s, new_s, temperature):
	return numpy.exp(-1 * float(new_s - s) / float(temperature))

def next_neighbor(sol, alpha, T, T_max):
	move = alpha * NEIGHBORHOOD_MAX

	if sol - move <= NEIGHBORHOOD_MIN:
		return sol + move
	elif sol + move >= NEIGHBORHOOD_MAX:
		return sol - move
	else:
		return sol + move * random.uniform(-1, 1)

def SA(sol):
	s = sol
	T = T_MAX

	alpha = 0.4
	beta = 0.9

	while T > T_MIN:
		print 'S: %s | T: %s' % (str(s), str(T))
		for _ in range(200):
			new_s = next_neighbor(s, alpha, T, T_MAX)

			if f(new_s) > f(s) or acceptance(s, new_s, T) > random.uniform(0, 1):
				s = new_s

		alpha *= beta
		T *= beta

	return s, f(s), f(12.6025895306)

if __name__ == '__main__':
	print 'SOL: %s | RESULT: %s | TARGET: %s' % SA(15.0)


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

import numpy

def summary_square(start, end):
    return sum(i ** 2 for i in range(start, end))

def square_sum(start, end):
    return numpy.sum(numpy.arange(start, end)) ** 2

def generate_diff(start, end):
    """
    total result
    >>> generate_diff(1, 101)
    25164150
    >>> generate_diff(1, 100)
    24174150
    """
    a = summary_square(start, end)
    b = square_sum(start, end)

    return numpy.abs(a - b)

if __name__ == "__main__":
    import doctest

    doctest.testmod()


import numpy as np
from numpy import linalg as la
from scipy.linalg import solve_discrete_lyapunov,solve_discrete_are
from functools import reduce

###############################################################################
# General matrix math functions
###############################################################################

# Check for positive definiteness
def is_pos_def(A):
    try:
        la.cholesky(A)
        return True
    except np.linalg.LinAlgError:
        return False

# Vectorize a matrix by stacking its columns
def vec(A):
    return A.reshape(-1, order="F")

# Return the symmetric part of a matrix
def sympart(A):
    return 0.5*(A+A.T)

# Return the positive semidefinite part of a symmetric matrix
def psdpart(X):
    X = sympart(X)
    Y = np.zeros_like(X)
    eigvals, eigvecs = la.eig(X)
    for i in range(X.shape[0]):
        if eigvals[i] > 0:
            Y += eigvals[i]*np.outer(eigvecs[:,i],eigvecs[:,i])
    Y = sympart(Y)
    return Y

# Overload and extend the numpy kron function to take a single argument
def kron(*args):
    if len(args)==1:
        return np.kron(args[0],args[0])
    else:
        return np.kron(*args)

## Multi-dot
def mdot(*args):
    return reduce(np.dot, args)

# Spectral radius of a matrix
def specrad(A):
    try:
        return np.max(np.abs(la.eig(A)[0]))
    except np.linalg.LinAlgError:
        return np.nan

# Minimum singular value
def minsv(A):
    return la.svd(A)[1].min()

# Similar to MATLAB / operator for square invertible matrices
# Solves a = bx
def solveb(a,b):
    return la.solve(b.T,a.T).T

# Overload the numpy randn function so it always uses the same RandomState
# similarly to how MATLAB works (random number generator is 'global')
seed = 3187
rng = np.random.RandomState(seed)

def rand(*args):
    return rng.rand(*args)

def randn(*args):
    return rng.randn(*args)

def randint(*args):
    return rng.randint(*args)

def rngg():
    return rng

# Symmetric log transform
def symlog(X,scale=1):
    return np.multiply(np.sign(X),np.log(1+np.abs(X)/(10**scale)))

def printeigs(A):
    print(la.eig(A)[0])
    return

def succ(A,B):
    return is_pos_def(A-B)

def mip(A,B):
    # Matrix inner product
    return np.trace(mdot(A.T,B))

# Ammend the dlyap and dare functions to correct issue where
# input A, Q matrices are modified (unwanted behavior);
# simply pass a copy of the matrices to protect them from modification
def dlyap(A,Q):
    try:
        return solve_discrete_lyapunov(np.copy(A),np.copy(Q))
    except ValueError:
        return np.full_like(Q,np.inf)

def dare(A,B,Q,R):
    return solve_discrete_are(np.copy(A),np.copy(B),np.copy(Q),np.copy(R))


import numpy as np

def relu(Z):
    """Compute the Rectified Linear Unit (ReLU) activation function.

    Parameters
    ----------
    Z : ndarray
        Pre-activation parameters for the current layer.

    Returns
    -------
    A : ndarray
        Post-activation parameters for the current layer. Same shape as `Z`.
    ndarray
        Store `Z` for computing the backward pass efficiently.
    """

    A = np.maximum(0, Z)

    return A, Z

def relu_backward(dA, cache):
    """Compute backward propagation through the RELU activation function.

    Parameters
    ----------
    dA : ndarray
        Post-activation gradients for the current layer.
    cache : ndarray
        Stored `Z` from `relu()`.

    Returns
    -------
    dZ : ndarray
        Gradients of the cost with respect to `Z`.
    """

    Z = cache

    dZ = np.array(dA, copy=True)
    dZ[Z <= 0] = 0

    return dZ

def sigmoid(Z):
    """Compute the sigmoid activation function.

    Parameters
    ----------
    Z : ndarray
        Pre-activation parameters for the current layer.

    Returns
    -------
    A : ndarray
        Post-activation parameters for the current layer. Same shape as `Z`.
    ndarray
        Store `Z` for computing the backward pass efficiently.
    """

    A = 1 / (1 + np.exp(-Z))

    return A, Z

def sigmoid_backward(dA, cache):
    """Compute backward propagation through the sigmoid activation function.

    Parameters
    ----------
    dA : ndarray
        Post-activation gradients for the current layer.
    cache : ndarray
        Stored `Z` fron `sigmoid()`.

    Returns
    -------
    dZ : ndarray
        Gradients of the cost with respect to `Z`.
    """

    Z = cache

    A, _ = sigmoid(Z)
    dZ = dA * A * (1 - A)

    return dZ


#%%
"""
Created on July 11 2021
Monte Carlo Paths for the CIR Process

This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt

def GeneratePathsCIREuler(NoOfPaths,NoOfSteps,T,lambd,r0,theta,gamma):
    Z = np.random.normal(0.0,1.0,[NoOfPaths,NoOfSteps])
    W = np.zeros([NoOfPaths, NoOfSteps+1])
    R = np.zeros([NoOfPaths, NoOfSteps+1])
    R[:,0]=r0
    time = np.zeros([NoOfSteps+1])

    dt = T / float(NoOfSteps)
    for i in range(0,NoOfSteps):
        # making sure that samples from normal have mean 0 and variance 1
        if NoOfPaths > 1:
            Z[:,i] = (Z[:,i] - np.mean(Z[:,i])) / np.std(Z[:,i])
        W[:,i+1] = W[:,i] + np.power(dt, 0.5)*Z[:,i]
        R[:,i+1] = R[:,i] + lambd*(theta - R[:,i]) * dt + gamma* np.sqrt(R[:,i]) * (W[:,i+1]-W[:,i])

        # Truncated boundary condition
        R[:,i+1] = np.maximum(R[:,i+1],0.0)
        time[i+1] = time[i] +dt

    # Outputs
    paths = {"time":time,"R":R}
    return paths
def mainCalculation():
    NoOfPaths = 1
    NoOfSteps = 500
    T         = 50.0
    lambd     = 0.1
    gamma     = 0.05
    r0        = 0.05
    theta     = 0.05

    # Effect of mean reversion lambda
    plt.figure(1)
    legend = []
    lambdVec = [0.01, 0.2, 5.0]
    for lambdTemp in lambdVec:
        np.random.seed(2)
        Paths = GeneratePathsCIREuler(NoOfPaths,NoOfSteps,T,lambdTemp,r0,theta,gamma)
        legend.append('lambda={0}'.format(lambdTemp))
        timeGrid = Paths["time"]
        R = Paths["R"]
        plt.plot(timeGrid, np.transpose(R))
    plt.grid()
    plt.xlabel("time")
    plt.ylabel("R(t)")
    plt.legend(legend)

    # Effect of the volatility
    plt.figure(2)
    legend = []
    gammaVec = [0.1, 0.2, 0.3]
    for gammaTemp in gammaVec:
        np.random.seed(2)
        Paths = GeneratePathsCIREuler(NoOfPaths,NoOfSteps,T,lambd,r0,theta,gammaTemp)
        legend.append('gamma={0}'.format(gammaTemp))
        timeGrid = Paths["time"]
        R = Paths["R"]
        plt.plot(timeGrid, np.transpose(R))
    plt.grid()
    plt.xlabel("time")
    plt.ylabel("R(t)")
    plt.legend(legend)

mainCalculation()

Lecture 03-The HJM Framework/Materials/CIR_IR_paths.py


from sklearn.cluster import KMeans
from sklearn import metrics
from scipy.spatial.distance import cdist
import numpy as np
import matplotlib.pyplot as plt
import random
import math

# Nicely clustered set of values
# https://pythonprogramminglanguage.com/kmeans-elbow-method/
clustered_x_values = np.array([3, 1, 1, 2, 1, 6, 6, 6, 5, 6, 7, 8, 9, 8, 9, 9, 8])
clustered_y_values = np.array([5, 4, 5, 6, 5, 8, 6, 7, 6, 7, 1, 2, 1, 2, 3, 2, 3])

unclustered_x_values = np.array([random.random() * 10 for _ in range(1000)])
unclustered_y_values = np.array([random.random() * 10 for _ in range(1000)])

# List of K-Clusters
k_range = range(1, 10)

def create_points(x_values, y_values):
    return np.array(list(zip(x_values, y_values))).reshape(len(x_values), 2)

def run_elbow_method_using_distortion(x_values, y_values):
    list_of_distortions = []
    points = create_points(x_values, y_values)
    for k in k_range:
        k_means = KMeans(n_clusters=k)
        k_means.fit(points)

        # Calculate distortion per k value
        centroids = k_means.cluster_centers_
        distances_from_centroids = cdist(points, centroids, 'euclidean')
        smallest_distances_from_centroids = np.min(distances_from_centroids, axis=1)
        calculated_distortion = sum(smallest_distances_from_centroids) / points.shape[0]

        list_of_distortions.append(calculated_distortion)

    plot_elbow_method(k_range, list_of_distortions, 'Distortion', 'Plot of elbow method using Distortion')

def run_elbow_method_using_inertia(x_values, y_values):
    list_of_inertias = []
    points = create_points(x_values, y_values)
    for k in k_range:
        k_means = KMeans(n_clusters=k)
        k_means.fit(points)

        # Calculate inertia per k value
        calculated_inertia = k_means.inertia_  # Sum of the squared distances to the nearest cluster

        list_of_inertias.append(calculated_inertia)

    plot_elbow_method(k_range, list_of_inertias, 'Inertia', 'Plot of elbow method using Inertia')

def plot_points(x_values, y_values):
    plt.plot()
    plt.scatter(x_values, y_values)
    plt.title('Plot of the dataset')
    plt.show()

def plot_elbow_method(k_values, y_values, y_label, title):
    plt.plot(k_values, y_values, 'bx-')
    plt.xlabel('k')
    plt.ylabel(y_label)
    plt.title(title)
    plt.show()

if __name__ == '__main__':
    print('Plotting the elbow method using predefined clusters')
    plot_points(clustered_x_values, clustered_y_values)
    run_elbow_method_using_distortion(clustered_x_values, clustered_y_values)
    run_elbow_method_using_inertia(clustered_x_values, clustered_y_values)

    print('Now for random clusters')
    plot_points(unclustered_x_values, unclustered_y_values)
    run_elbow_method_using_distortion(unclustered_x_values, unclustered_y_values)
    run_elbow_method_using_inertia(unclustered_x_values, unclustered_y_values)


import numpy as np
import matplotlib.pyplot as plt

def main():
    """
    This is basic feed forward implementation demo
    It will implement a single forward calculation of nn with 1 hidden layer
    using random weights
    """

    nPerK = 500

    # create random data for 3 different classes (normally distributed numbers around some avg)
    XK1 = np.random.randn(nPerK, 2) + [0, -2]
    XK2 = np.random.randn(nPerK, 2) + [2, 2]
    XK3 = np.random.randn(nPerK, 2) + [-2, 2]

    # set the "actual" classes (0, 1, 2)
    Y = np.array([0] * nPerK + [1] * nPerK + [2] * nPerK)

    # join all classes to one dataset
    X = np.vstack([XK1, XK2, XK3])

    D = X.shape[1]  # number of "features"
    K = 3  # number of classes
    M = 3  # number of activation units in the first hidden layer

    # weights and bias for the first hidden layer
    W1 = np.random.randn(D, M)
    b1 = np.random.randn(M)

    # weights and bias for the output layer
    W2 = np.random.randn(M, K)
    b2 = np.random.randn(K)

    Y_hat = forward(X, W1, b1, W2, b2)
    class_rate = classification_rate(Y, Y_hat)
    print("Classification rate: %s" % class_rate)

    # by c=Y we assign each point in the scatter a colormap value of its class (Y)
    # plt.scatter(X[:, 0], X[:, 1], c=Y, alpha=0.5)
    # plt.show()

def forward(X, W1, b1, W2, b2):
    """
    Feed forward X in a network with a single hidden layer
    :param X: Input dataset
    :param W1: Weights of first hidden layer
    :param b1: bias for first hidden layer
    :param W2: weights for the output layer (aka V)
    :param b2: bias for the output layer
    :return: Matrix with probabilities of each sample belong to each k (group)
    """

    # First hidden layer, each row has M values per each activation unit (will use sigmoid)
    Z = X.dot(W1) + b1
    Z = 1 / (1 + np.exp(-1 * Z))  # sigmoid

    # Second layer (output layer), each row has K values per each class prediction probability (we'll use softmax)
    A = Z.dot(W2) + b2
    expA = np.exp(A)
    Y = expA / expA.sum(axis=1, keepdims=True)

    return Y

def classification_rate(Y_act, Y_hat):
    """
    Calculate the classification error
    :param Y_act: The actual class of each sample (nX1)
    :param Y_hat: The probability of each sample belongs to each k (nXk)
    :return:
    """

    # find the index of the maximum value per sample
    Y_hat = np.argmax(Y_hat, axis=1)
    class_rate = np.sum(Y_hat == Y_act)
    return class_rate / Y_act.size

if __name__ == '__main__':
    main()


import numpy as np

def mse(actual, predicted):
    """
    https://ml-cheatsheet.readthedocs.io/en/latest/linear_regression.html#cost-function
    MSE = the mean of (actual_outcome - predicted_outcome) squared
    """
    return np.mean(np.power(actual - predicted, 2))

def prime_mse(actual, predicted):
    """
    Derivate of mse for use with the Gradient Descent optimizer.
    Output error is calculated either as the result of the derivative
    of the cost function or as the result of the backpropagation
    of the previous layer.
    """
    return 2 * (predicted - actual) / actual.size


import numpy as np

# def find_bmu_act(vec, act):
#     """  """
#     for node in act:
#         for data in node[2]:
#             if vec in data:
#                 return node

# def dist(a, b):
#     """ Return euclidean distance """
#     return np.sqrt(np.sum(np.square(np.subtract(a, b))))

""" Error metric per node """

def qE_node(act):
    """ Return list of mean quantization error by node """
    return [np.mean([qE(data, node[1]) for data in node[2]]) for node in act]

def MAE_node(act):
    """ Return list of mean MAE by node """
    return [np.mean([MAE(data, node[1]) for data in node[2]]) for node in act]

def MSE_node(act):
    """ Return list of mean MSE by node """
    return [np.mean([MSE(data, node[1]) for data in node[2]]) for node in act]

def RMSE_node(act):
    """ Return list of mean RMSE by node """
    return [np.mean([RMSE(data, node[1]) for data in node[2]]) for node in act]

def MAPE_node(act):
    """ Return list of mean MAPE by node """
    return [np.mean([MAPE(data, node[1]) for data in node[2]]) for node in act]

""" Error metrics """

def qE(a, b):
    """ Return quantization error """
    return np.sqrt(np.sum(np.square(np.subtract(a, b))))

def MAE(a, b):
    """ Return Mean absolute error """
    return abs(np.subtract(a, b)).mean()

def MSE(a, b):
    """ Return Mean squared error """
    return np.square(np.subtract(a, b)).mean()

def RMSE(a, b):
    """ Return Root mean squared error """
    return np.sqrt(np.square(np.subtract(a, b)).mean())

def MAPE(a, b):
    """ Return Mean absolute percentage error """
    return abs(np.divide(np.subtract(a, b),a))*100


import numpy as np

def euclidean_distance(x: np.array, y: np.array) -> float:
    """Calculates euclidean distance.

    This function calculates euclidean distance between
    two points x and y in Euclidean n-space.

    Args:
        x, y: points in Euclidean n-space.

    Returns:
        length of the line segment connecting given points.

    """

    return np.sqrt(np.sum((x - y) ** 2))

def euclidean_similarity(x: np.array, y: np.array) -> float:
    """Calculates euclidean similarity.

    This function calculates euclidean similarity
    between points x and y in Euclidean n-space
    that based on euclidean distance between this points.

    Args:
        x, y: two points in Euclidean n-space.

    Returns:
        similarity between points x and y.

    """

    return 1 / (1 + euclidean_distance(x, y))

def pearson_similarity(x: np.array, y: np.array) -> float:
    """Calculates Pearson correlation coefficient.

    This function calculates Pearson correlation coefficient
    for given 1-D data arrays x and y.

    Args:
        x, y: two 1-D data arrays with float values.

    Returns:
        Pearson correlation between x and y.

    """

    x_b, y_b = x - x.mean(), y - y.mean()

    denominator = np.sqrt(np.sum(x_b ** 2) * np.sum(y_b ** 2))

    if denominator == 0:
        return np.nan

    return np.sum(x_b * y_b) / denominator

def recsys_euclidean_similarity(x: np.array, y: np.array) -> float:
    """Calculates euclidean similarity of two users/items.

    This function calculates users or items similarity based on
    euclidean similarity. The similarity between two users/items,
    that represented here as two 1-D arrays where each value
    is score for some item (from some user), is calculated only for
    those indices i for which there is score both
    for first and second user/item. It's assumed that zero values
    in arrays are equal to lack of score for some items (from some users).

    Args:
        x, y: two 1-D data arrays that represent user's/item's scores.

    Returns:
        euclidean similarity between two users/items x and y.

    """

    x_new, y_new = x[(x != 0) & (y != 0)], y[(x != 0) & (y != 0)]

    if x_new.shape[0] == 0:
        return -np.inf

    return euclidean_similarity(x_new, y_new)

def recsys_pearson_similarity(x: np.array, y: np.array) -> float:
    """Calculates pearson similarity of two users/items.

    This function calculates users or items similarity based on
    pearson similarity. The similarity between two users/items,
    that represented here as two 1-D arrays where each value
    is score for some item (from some user), is calculated only for
    those indices i for which there is score both
    for first and second user/item. It's assumed that zero values
    in arrays are equal to lack of score for some items (from some users).

    Args:
        x, y: two 1-D data arrays that represent user's/item's scores.

    Returns:
        pearson similarity between two users\items x and y.

    """

    x_new, y_new = x[(x != 0) & (y != 0)], y[(x != 0) & (y != 0)]

    if x_new.shape[0] == 0:
        return -np.inf

    return pearson_similarity(x_new, y_new)

def apk(actual: np.array, predicted: np.array, k: int=10) -> float:
    """Calculates the average precision at k.

    This function calculates the average precision at k
    for given predicted elements by actual elements.

    Args:
        actual: array of relevant elements in any order.
        predicted: array of predicted elements that are sorted
            by relevance in descending order.
        k: number of predicted elements.

    Returns:
        The average precision at k over the input lists.

    """

    if len(predicted) < k:
        k = len(predicted)

    precision_at_k = np.array([np.in1d(predicted[:i], actual).sum() / i for i in range(1, k+1)])
    rel = np.array([1 if p in actual else 0 for p in predicted[:k]])

    return (precision_at_k * rel).mean()

def mapk(actual: np.array, predicted: np.array, k: int=10) -> float:
    """Calculates the mean average precision at k.

    This function calculates the mean average precision at k
    for given predicted elements for multiple users by
    actual elements.

    Args:
        actual: 2-D array of relevant elements.
        predicted: 2-D array of predicted elements.
        k: number of predicted elements.

    Returns:
        The mean average precision at k over the input arrays.

    """

    sum = 0
    U = len(predicted)

    for i in range(U):
        sum += apk(actual[i], predicted[i], k)

    return sum / U


import sympy as sp
import numpy as np
import matplotlib.pyplot as plt

# parameter
k, m = sp.symbols("k m")

# time
t = sp.Symbol("t")

# generalized coordinate
q_fn = sp.Function("q")
q = q_fn(t)
q_dot = q.diff(t)

# cartesian coordinates in terms of q
x = q
y = 0
z = 0
print("generalized coordinates:")
print("x =", x, "\ny =", y, "\nz =", z)

# compute cartesian derivates in terms of q
x_dot = sp.diff(x, t)
y_dot = sp.diff(y, t)
z_dot = sp.diff(z, t)
print("x_dot =", x_dot, "\ny_dot =", y_dot, "\nz_dot =", z_dot)

# construct lagrangian
T = m*(x_dot**2 + y_dot**2 + z_dot**2)/2
V = k*x**2/2
L = T - V
print("lagrangian:", L)

# compute lagrange equation
lagrange_eq = sp.Eq(L.diff(q_dot).diff(t) - L.diff(q), 0)
print("equation of motion:", lagrange_eq)

# general solution
q_general_sol = sp.dsolve(lagrange_eq, q).rhs
q_dot_general_sol = q_general_sol.diff(t)
print("solution:")
print("q(t) =", q_general_sol)
print("q_dot(t) =", q_dot_general_sol)
C1, C2 = sp.symbols("C1 C2")

# solve initial conditions
q0 = 0
q_dot_0 = 1
eq1 = sp.Eq(q_general_sol.subs(t, 0), q0)
eq2 = sp.Eq(q_dot_general_sol.subs(t, 0), q_dot_0)
sol = sp.solve([eq1, eq2], [C1, C2])
print("parameters for the initial conditions:")
print("C1 =", sol[C1])
print("C2 =", sol[C2])
q_sol = q_general_sol.subs(C1, sol[C1]).subs(C2, sol[C2])
q_dot_sol = q_sol.diff(t)
print("solutions for initial conditions:")
print("q(t) =", q_sol)
print("q_dot(t) =", q_dot_sol)

# choose parameters
time = np.linspace(0, 10, 500)
k_val = 1.0
m_val = 1.0
q_vals = sp.lambdify(t, q_sol.subs(k, k_val).subs(m, m_val))(time).real
plt.plot(time, q_vals)
plt.xlabel("t [s]")
plt.ylabel("q [m]")
plt.title("q(0) = %.2f, q_dot(0) = %.2f, k = %.2f, m = %.2f" % (q0, q_dot_0, k_val, m_val))
plt.grid()
plt.show()


"""
A program that will allow the user to input
any two single-variable functions of x and
print the enclosed area between the two
"""

from sympy import Symbol, sympify, Integral, SympifyError

def area(expr1, expr2, var, lower, upper):
    _area = Integral(expr1 - expr2, (var, lower, upper)).doit()
    return _area

if __name__ == "__main__":
    try:
        expr1 = sympify(input("Enter the upper function in one variable: "))
        expr2 = sympify(input("Enter the lower function in one variable: "))
        var = Symbol(input("Enter variable: "))
        lower = float(input("Enter lower bound of the enclosed region: "))
        upper = float(input("Enter upper bound of the enclosed region: "))
    except SympifyError:
        print("Invalid input")
    else:
        result = area(expr1, expr2, var, lower, upper)
        if result:
            print("Area enclosed by {} and {} is equals to: {:.2f}".format(expr1, expr2, result))


import numpy as np
import matplotlib.pyplot as plt
import cv2
from scipy import fftpack,signal
import scipy.signal
import skimage.measure

class DeblurFactory:

    def ApplyDeblurFilter(self,name:str, **kwargs):
        choice = name.lower()
        if choice == 'wiener':
            return self.__wiener_filter__(**kwargs)
        else:
            raise Exception(f"invalid name {name}")

    def evaluate(self,restored,target):
        return skimage.metrics.peak_signal_noise_ratio(restored, target)

    def __wiener_filter__(image, psf, k=0.01):
        image_dft = fftpack.fft2(image)
        psf_dft = fftpack.fft2(fftpack.ifftshift(psf))
        filter_dft = np.conj(psf_dft) / (np.abs(psf_dft) ** 2 + k)
        recovered_dft = image_dft * filter_dft
        return np.real(fftpack.ifft2(recovered_dft))


import numpy as np

from ml_tooling.utils import DataType, MetricError

def lift_score(y_target: DataType, y_predicted: DataType) -> float:
    """
    Calculates lift score for a given estimator. The lift score quantifies how much better
    the estimator is compared to a random baseline.

    The formula is defined as follows:
        lift = (TP/(TP+FN)(TP+FP)/(TP+TN+FP+FN)
        Source: https://rasbt.github.io/mlxtend/user_guide/evaluate/lift_score

    :param y_target:
        Target labels

    :param y_predicted:
        Predicted labels

    :return:
        Lift score
    """
    y_target = np.array(y_target)
    y_predicted = np.array(y_predicted)

    if y_target.ndim > 1 or y_predicted.ndim > 1:
        raise MetricError("Input must be 1-dimensional")

    n = len(y_target)
    percent_positives_target = np.sum(y_target == 1) / n
    percent_positives_predicted = np.sum(y_predicted == 1) / n

    all_prod = np.column_stack([y_target, y_predicted])
    percent_correct_positives = (all_prod == 1).all(axis=1).sum() / n

    return percent_correct_positives / (
        percent_positives_target * percent_positives_predicted
    )


import numpy as np

class ActivationFunction:
    """Abstract class to represent activation functions"""

    def apply(self, x):
        pass

    def derivative(self, x):
        pass

class Step(ActivationFunction):
    """The step funtion"""

    def apply(self, x):
        if x < 0:
            return 0
        return 1

    def derivative(self, x):
        if x == 0:
            raise ValueError("Derivative in zero is infinite")
        return 0

class Sigmoid(ActivationFunction):
    """The sigmoid function"""

    def apply(self, x):
        return 1 / (1 + np.exp(-x))

    def derivative(self, x):
        return self.apply(x) * (1 - self.apply(x))

class Tanh(ActivationFunction):
    """The tanh function"""

    def apply(self, x):
        return np.tanh(x)

    def derivative(self, x):
        return 1 - np.tanh(x)**2


# Simulates a homogeneous Poisson point process on a circle.
# This code positions the points uniformly around a circle, which can be
# achieved by either using polar coordinates or normal random variables.
# Author: H. Paul Keeler, 2020.
# Website: hpaulkeeler.com
# Repository: github.com/hpaulkeeler/posts
# For more details, see the post:
# hpaulkeeler.com/simulating-a-poisson-point-process-on-a-circle/

import numpy as np;  # NumPy package for arrays, random number generation, etc
import matplotlib.pyplot as plt  # for plotting
from numpy import linalg as la #linear algebra pack

plt.close('all');  # close all figures

r = 1;  # radius of circle

# centre of circle
xx0 = 0;
yy0 = 0;

# Point process parameters
lambda0 = 10;  # intensity (ie mean density) of the Poisson process

lengthTotal=2*np.pi*r; #circumference of circle

# Simulate Poisson point process
numbPoints = np.random.poisson(lambda0 * lengthTotal);  # Poisson number of points

#METHOD 1 for positioning points: Use polar coodinates
theta = 2 * np.pi * np.random.uniform(0, 1, numbPoints);  # angular coordinates
rho = r ;  # radial coordinates

# Convert from polar to Cartesian coordinates
xx = rho * np.cos(theta);
yy = rho * np.sin(theta);

#METHOD 2 for positioning points: Use normal random variables
xxRand=np.random.normal(0,1,size=(numbPoints,2)); #generate two sets of normal variables
normRand=la.norm(xxRand,2,1); #Euclidean norms
xxRandBall=xxRand/normRand[:,None]; #rescale by Euclidean norms
xxRandBall=r*xxRandBall; #rescale for non-unit sphere
#retrieve x and y coordinates
xx= xxRandBall[:,0];
yy= xxRandBall[:,1];

# Shift centre of circle to (xx0,yy0)
xx = xx + xx0;
yy = yy + yy0;

# Plotting
plt.scatter(xx, yy, edgecolor='b', facecolor='none', alpha=0.5);
plt.xlabel('x');
plt.ylabel('y');
plt.axis('equal');