from sympy import *
x = symbols("x")  # 符号x，自变量
y = -pow(10,-11)*pow(x,6) + pow(10,-8)*pow(x,5) - 4*pow(10,-6)*pow(x,4) + 0.0006*pow(x,3) - 0.0428*pow(x,2) + 1.7561*x + 16.528 #公式

dify = diff(y,x) #求导
print(dify)  #打印导数

#给定x值，求对应导数的值

for i in range(0,305,5):
    print(dify.subs('x',i))

x2 = symbols("x2")  # 符号x2，自变量
y2 = pow(x2,2)

dify2 = diff(y2,x2) #求导
print(dify2)  #打印导数


import numpy as np

def volumeSphere(radius=1000):
    """
    Volume of a sphere

    Args:
        radius (float): Radius (Default: 1000)

    Returns:
        volume (float): Volume in [input units³]
    """
    assert radius > 0

    volume = 4 / 3 * np.pi * radius**3

    return volume

def massSphere(radius=1000, density=1e-21,
                verbose=False, inferDensity=True):
    """
    Calculates mass of a sphere

    Args:
        radius (float): Radius in [nm]. (Default: 1000 nm)
        density (float): Density, ϱ, in [g/nm³]. (Default: 1e-21 g/nm³)
        verbose (bool): Flag whether to show extra information. (Default: False)
        inferDensity (bool): If true inputs above 1e-5 are interpreted as g/cm³. (Default: True)

    Returns:
        mass (float):  Mass in [g]
    """
    assert radius > 0
    assert density > 0

    volume = volumeSphere(radius=radius)

    # check if density is provided in g/cm³.
    if inferDensity and density > 1e-5:
        print("Assuming that ϱ was provided in [g/cm³]. Converting to g/nm³")
        density /= 1e21
        print("ϱ is now: {} g/nm³".format(density))

    mass = volume * density

    if verbose:
        print("Input:")
        print("Radius of Sphere [nm]: {}".format(radius))
        print("Density of material [g/nm³]: {} \n".format(density))

        print("Output:")
        print("Mass of sphere [g]: {}\n".format(mass))

    return mass


"""
Pozo de potencial (Problema 3)

Luis Eduardo Sánchez González

II Escuela de Verano de Física Computacional

lun 19 abr 2021 21:29:18 CDT

Repositorio: https://github.com/Luis2501/Ejercicios-IIEVFC
"""
import numpy as np
from scipy.constants import e, epsilon_0

class Potencial:

	def __init__(self, Z):

		self.Z1, self.Z2 = Z - 2, 2
		self.R = (2e-15)*((self.Z1)**(1/3))

	def __call__(self, r):

		Z1, Z2, R = self.Z1, self.Z2, self.R

		return np.array((self.Z1*self.Z2*(e**2))/(4*np.pi*epsilon_0*r))

	def Force(self, r):

		Z1, Z2 = self.Z1, self.Z2

		return np.array((self.Z1*self.Z2*(e**2))/(4*np.pi*epsilon_0*(r**2)))

if __name__ == "__main__":

	import sys
	sys.path.append("../")
	import matplotlib.pyplot as plt
	from PhysicsPy.Derivation import *

	Particula = Potencial(50)

	RR = np.linspace(Particula.R, 3*(Particula.R), 1001)

	Methods = [Forward, Central, Backward]
	Names = ["Forward", "Central", "Backward"]
	Solutions = []

	#Obtener soluciones
	for class_name in Methods:

		Solucion = class_name(Particula)
		Solucion.InitialConditions(RR, 1e-20)
		Fuerza = - Solucion.Solve()

		Solutions.append(Fuerza)

		del Fuerza, Solucion

	Solutions = np.array(Solutions)

	Force_Analytic = Particula.Force(RR)

	#Obtener los errores
	e_abs, e_r = [], []

	for Solution in Solutions:

		e_abs.append(abs(Force_Analytic - Solution))
		e_r.append(abs(Force_Analytic - Solution)/Force_Analytic)

	e_abs, e_r = np.array(e_abs), np.array(e_r)

	#Graficar la fuerza
	for Force, Name in zip(Solutions, Names):

		plt.plot(RR, Force, label = Name)

	plt.plot(RR, Force_Analytic, label = "Analytic Solution")

	plt.title("Fuerza que experimenta la partícula")
	plt.xlabel("Radio (r)") ; plt.ylabel("Fuerza (F)")
	plt.legend(fancybox=True) ; plt.grid()
	plt.show()

	#Grafica error absoluto
	for ea, Name in zip(e_abs, Names):

		plt.plot(RR, ea, label = Name)

	plt.title("Error absoluto")
	plt.xlabel("R") ; plt.ylabel(r"$e_{abs}$")
	plt.legend(fancybox=True) ; plt.grid()
	plt.show()

	#Grafica error relativo
	for er, Name in zip(e_r, Names):

		plt.plot(RR, er, label = Name)

	plt.title("Error relativo")
	plt.xlabel("R") ; plt.ylabel(r"$e_{r}$")
	plt.legend(fancybox=True) ; plt.grid()
	plt.show()


"""
Here we want to explore the changes in our outputs by incorporating a sinusoidal driver in our oscillator.
The only changes we want to make here compared to the last two parts are;
the addition of a sinusoidal driving term in our initial function for the van der Pol oscillator
and a new constant A (amplitude). Additionally we want to incorporate another omega term
for the sine function in the driver. We will hold mu constant at 1.0, and vary the driving omega term
for three sets of conditions.
"""
import numpy as np
import matplotlib.pyplot as plt
from math import sin
#Question 1c
w = 1.0
w2 = 4.0*w #defining new omega term with first desired condition
mu = 1.0
A = 8.53 #New amplitude constant
#We adjust the dvelocity function for our new driving term
def f(r,t):
    x = r[0]
    v = r[1]
    fx = v
    fv = A*sin(w2*t)+mu*(1-x**2)*v-(w**2)*x
    return np.array([fx,fv],float)

# everything from here on is taken from part a
a = 0.0
b = 60.0
N = 1000
h = (b-a)/N

time = np.arange(a,b,h)
xvals = []
vvals = []

r = np.array([1.0,0.0],float)
for t in time:
    xvals.append(r[0])
    vvals.append(r[1])
    k1 = h*f(r,t)
    k2 = h*f(r+0.5*k1,t+ 0.5*h)
    k3 = h*f(r+0.5*k2, t +0.5*h)
    k4 = h*f(r + k3, t + h)
    r += (k1+2*k2+2*k3+k4)/6

 #plotting
fig, graph = plt.subplots()
graph.plot(time, xvals)
graph.set(xlabel = 'time (s)', ylabel = 'position (m)', title = 'w2 = 4.0w, position vs time, van der Pol with sinusoidal driver')
plt.show()
fig, graph = plt.subplots()
plt.plot(xvals, vvals)
graph.set(ylabel = 'velocity (m/s)', xlabel = 'position (m)', title = 'w2 = 4.0w, phase space plot, van der Pol with sinusoidal driver')
plt.show()

"""
We repeat the exact code as that above two more times, with two different values of omega2
"""
w = 1.0
w2 = 0.5*w
mu = 1.0
A = 8.53
def f(r,t):
    x = r[0]
    v = r[1]
    fx = v
    fv = A*sin(w2*t)+mu*(1-x**2)*v-(w**2)*x
    return np.array([fx,fv],float)

a = 0.0
b = 60.0
N = 1000
h = (b-a)/N

time = np.arange(a,b,h)
xvals = []
vvals = []

r = np.array([1.0,0.0],float)
for t in time:
    xvals.append(r[0])
    vvals.append(r[1])
    k1 = h*f(r,t)
    k2 = h*f(r+0.5*k1,t+ 0.5*h)
    k3 = h*f(r+0.5*k2, t +0.5*h)
    k4 = h*f(r + k3, t + h)
    r += (k1+2*k2+2*k3+k4)/6

fig, graph = plt.subplots()
graph.plot(time, xvals)
graph.set(xlabel = 'time (s)', ylabel = 'position (m)', title = 'w2 = 0.5w, position vs time, van der Pol with sinusoidal driver')
plt.show()
fig, graph = plt.subplots()
plt.plot(xvals, vvals)
graph.set(ylabel = 'velocity (m/s)', xlabel = 'position (m)', title = 'w2 = 0.5w, phase space plot, van der Pol with sinusoidal driver')
plt.show()

w = 1.0
w2 = 1.0*w
mu = 1.0
A = 8.53
def f(r,t):
    x = r[0]
    v = r[1]
    fx = v
    fv = A*sin(w2*t)+mu*(1-x**2)*v-(w**2)*x
    return np.array([fx,fv],float)

a = 0.0
b = 60.0
N = 1000
h = (b-a)/N

time = np.arange(a,b,h)
xvals = []
vvals = []

r = np.array([1.0,0.0],float)
for t in time:
    xvals.append(r[0])
    vvals.append(r[1])
    k1 = h*f(r,t)
    k2 = h*f(r+0.5*k1,t+ 0.5*h)
    k3 = h*f(r+0.5*k2, t +0.5*h)
    k4 = h*f(r + k3, t + h)
    r += (k1+2*k2+2*k3+k4)/6

fig, graph = plt.subplots()
graph.plot(time, xvals)
graph.set(xlabel = 'time (s)', ylabel = 'position (m)', title = 'w2 = w, position vs time, van der Pol with sinusoidal driver')
plt.show()
fig, graph = plt.subplots()
plt.plot(xvals, vvals)
graph.set(ylabel = 'velocity (m/s)', xlabel = 'position (m)', title = 'w2 = w, phase space plot, van der Pol with sinusoidal driver')
plt.show()


from numpy import *
from matplotlib import *
from pylab import *
import matplotlib.lines as mlines

if __name__ == "__main__":
    rc('text', usetex=True)
    rc('font', family='serif')
    fs = 20
    ### Example 1
    x = arange(-5,5,0.1)

    # r > 0
    fig= figure(1)
    ax = fig.add_subplot(311)
    frame1 = plt.gca()
    hold
    r = 2
    xdot = r*x - x**2
    ax.plot(x,xdot,'b-')
    ylim([min(xdot),max(xdot)+3])
    xlim([min(x),max(x)])
    ax.plot(x,zeros_like(x),'k-')
    ax.plot(0,0,'bo',mfc='none',mec='b')
    ax.plot(r,0,'bo',fillstyle='full',mec='b')
    ax.set_title(r'$r > 0$')
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    ax.annotate('', xy=(-2, 0), xytext=(-1, 0),
             arrowprops=dict(facecolor='black', shrink=0.04, width=1),
             )
    ax.annotate('', xy=(r-1, 0), xytext=(0.5, 0),
             arrowprops=dict(facecolor='black', shrink=0.04, width=1),
             )
    ax.annotate('', xy=(r+1, 0), xytext=(r+2, 0),
             arrowprops=dict(facecolor='black', shrink=0.04, width=1),
             )

    # lft = mlines.Line2D.fillStyles
    # r = 0
    ax = fig.add_subplot(312)
    frame1 = plt.gca()
    r = 0
    xdot = r*x - x**2
    ax.plot(x,xdot,'b-')
    ax.plot(0,0,'bo',fillstyle='right',mec='b')
    ylim([min(xdot),max(xdot)+3])
    xlim([min(x),max(x)])
    ax.plot(x,zeros_like(x),'k-')
    ax.set_title(r'$r = 0$')
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    ax.annotate('', xy=(r-2, 0), xytext=(r-1, 0),
                arrowprops=dict(facecolor='black', shrink=0.04, width=1),
                )
    ax.annotate('', xy=(2, 0), xytext=(3, 0),
                arrowprops=dict(facecolor='black', shrink=0.04, width=1),
                )

    # r < 0
    ax = fig.add_subplot(313)
    frame1 = plt.gca()
    r = -2
    xdot = r*x - x**2
    ax.plot(x,xdot,'b-')
    ax.plot(0,0,'bo',fillstyle='full',mec='b')
    ax.plot(r,0,'bo', mfc='none',mec='b')
    ylim([min(xdot)-1,max(xdot)+3])
    xlim([min(x),max(x)])
    ax.plot(x,zeros_like(x),'k-')
    ax.set_title(r'$r < 0$')
    xlabel(r'$x$',fontsize=fs)
    ylabel(r'$\dot{x}$',fontsize=fs)
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    ax.annotate('', xy=(r - 2, 0), xytext=(r-1, 0),
                arrowprops=dict(facecolor='black', shrink=0.04, width=1),
                )
    ax.annotate('', xy=(-0.5, 0), xytext=(r+0.5, 0),
                arrowprops=dict(facecolor='black', shrink=0.04, width=1),
                )
    ax.annotate('', xy=(-r-1, 0), xytext=(-r, 0),
                arrowprops=dict(facecolor='black', shrink=0.04, width=1),
                )

    # bifurcation diagram
    x = arange(-5,5,0.1)
    fig= figure(2)
    ax = fig.add_subplot(111)
    frame1 = plt.gca()
    hold
    rp = arange(0,5,0.1)
    rn = arange(-5,0,0.1)
    rzp = zeros_like(arange(0,5,0.1))
    rzn = zeros_like(arange(-5,0,0.1))
    ax.plot(arange(-5,0,0.1),rzn,'b-')
    ax.plot(arange(0,5,0.1),rzp,'b--')
    ax.plot(arange(-5,0,0.1),rn,'b--')
    ax.plot(arange(0,5,0.1),rp,'b-')
    axvline(x=0,color='k')
    ax.set_title(r'$x^* = 0$')
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    ## log example

    x = arange(-0.999, 5, 0.02)
    fig = figure(3)
    i = 1
    my_xticks = ['' for j in x]
    zidx = nonzero(x == min(abs(x)))[0][0]
    my_xticks[zidx] = '0'
    for r in [-2,1,2]:
        ax = fig.add_subplot(310+i)
        plot(r*x - log(1+x))
        plot(zeros_like(x),'k-')
        a = ax.get_xticks().tolist()
        a[0:1] = ['',0]
        a[2:] = ['' for k in a[2:]]
        ax.set_xticklabels(a)
        frame1 = plt.gca()
        # frame1.axes.get_xaxis().set_visible(False)
        frame1.axes.get_yaxis().set_visible(False)
        i += 1

    fig = figure(4)
    rn = arange(-0.999,0.01,0.1)
    rp = arange(0.01,5,0.1)
    r = log(1+rp)/rp
    rr = log(1+rn)/rn
    ax = fig.add_subplot(111)
    plot(zeros(8),'k-')
    plot(r,rp,'b--')
    plot(rr,rn,'b-')

    a[0:1] = ['',1]
    a[2:] = ['' for k in a[2:]]
    ax.set_xticklabels(a)
    frame1 = plt.gca()
    # frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)
    xlabel(r'$r$',fontsize=fs)
    ylabel(r'$x^*$',fontsize=fs)
    ax.set_title(r'$x^* = 0$')

    # linear approximation
    fig = figure(5)
    ax = fig.add_subplot(111)
    rn = arange(0,1,0.01)
    rp = arange(1,2,0.1)
    plot(rn,2-2*rn,'b--')
    plot(rp,2-2*rp,'b')
    plot(zeros(4),'k-')
    plot(rn,zeros_like(rn),'b-')
    a[0:1] = ['',1]
    a[2:] = ['' for k in a[2:]]
    ax.set_xticklabels(a)
    frame1 = plt.gca()
    # frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)
    xlabel(r'$r$',fontsize=fs)
    ax.set_title(r'$x^* = 0$')

    # both
    fig = figure(6)
    ax = fig.add_subplot(111)
    rn = arange(0,1,0.1)
    rp = arange(1,2,0.1)
    plot(zeros(8),'k-')
    plot(rn,2-2*rn,'b--')
    plot(rp,2-2*rp,'b')
    rn = arange(-0.999,0.01,0.1)
    rp = arange(0.01,2,0.1)
    r = log(1+rp)/rp
    rr = log(1+rn)/rn
    plot(r,rp,'g--')
    plot(rr,rn,'g-')
    plot(rn,zeros_like(rn),'-')
    a[0:1] = ['',1]
    a[2:] = ['' for k in a[2:]]
    ax.set_xticklabels(a)
    frame1 = plt.gca()
    # frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)
    xlabel(r'$r$',fontsize=fs)
    ax.set_title(r'$x^* = 0$')

    show()


"""
fit data to y = mx + b using
least sum of |y - mx - b|, or L1 norm instead of sum of sq's
"""
import math
import numpy as np
import matplotlib.pyplot as plt
from SciInf_utilities import *
import sys

""" main
"""
#
print("\n fit data to y = mx + b using")
print(" least sum of |y - mx - b|, or L1 norm instead of sum of sq's \n")
# get data
#
if(len(sys.argv)>1):
  input_file = sys.argv[1]
else:
  input_file = input("file with one x, y data pair per line> ")
#input_file = 'xy.dat'
print('input file: ',input_file)
x = []
y = []
ndata = read_xy(x,y,input_file)
#
# basic averages
#
av_x = average_x(x)
av_y = average_x(y)
av_xy = average_xy(x,y)
#
av_xx = average_xy(x,x)
av_yy = average_xy(y,y)
#
var_x = av_xx - av_x**2
var_y = av_yy - av_y**2
var_xy = av_xy - av_x*av_y
stdev_x = math.sqrt(var_x)
stdev_y = math.sqrt(var_y)
#Rpearson = (av_xy - av_x*av_y)/(stdev_x*stdev_y)
Rpearson = var_xy/(stdev_x*stdev_y)
print('\n===========================================================')
print('data summary')
print('===========================================================')
print('av x    %12.5f y   %12.5f xy %12.5f ' % (av_x,av_y,av_xy))
print('av x^2  %12.5f y^2 %12.5f  ' % (av_xx,av_yy))
print('var x   %12.5f y   %12.5f xy %12.5f ' % (var_x,var_y,var_xy))
print('stdev x %12.5f y   %12.5f  ' % (stdev_x,stdev_y))
print('Pearson R %12.5f R^2 %12.5f ' %(Rpearson, Rpearson**2))
print('===========================================================\n')
#
#
# best fit slope, intercept etc, using property of least. abs. dev
# that line goes through at least 2 of points- brute force search over all pairs
#
sum_ad_min = 1.e6
print('minimizing absolute Y-deviation...')
for i in range(ndata):
  for j in range(i+1,ndata):
    m = (y[j] - y[i])/(x[j] - x[i])
    b = y[j] - m * x[j]
    sum_ad = 0.
    for k in range(ndata):
      sum_ad += abs(y[k] - m*x[k] - b)
    if(sum_ad < sum_ad_min):
      sum_ad_min = sum_ad
      minpts = [(i,j)]
      #print(sum_ad,i,j)
    elif(sum_ad == sum_ad_min):
      minpts.append((i,j))
if(len(minpts) > 1):
  print('multiple fits for point pairs: ',minpts)
  print('choosing the first pair')
sum_ad_min /= ndata
i_min = minpts[0][0]
j_min = minpts[0][1]
print('mean abs deviation %12.5f by passing through points %d and %d' % (sum_ad_min,i_min,j_min))
m_min = (y[j_min] - y[i_min])/(x[j_min] - x[i_min])
b_min= y[j_min] - m_min * x[j_min]
print('mean absolute deviation %10.5f slope %10.5f intercept %10.5f  ' % (sum_ad_min,m_min,b_min))
#
file_out = open('ladline_plot.dat','w')
file_out.write('#  n       x         y        yfit       yresid    \n')
ycalc = []
for i in range(ndata):
    ytemp = x[i]*m_min + b_min
    ycalc.append(float(ytemp))
    resid = abs(y[i] - m_min*x[i] - b_min)
    #print('%4d %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f ' % \
    #      (i,x[i],y[i],ycalc[i],resid[i],ycalc_plus_2s[i],ycalc_less_2s[i]))
    str_buf = '%4d %10.3f %10.3f %10.3f %10.3f \n' % \
          (i,x[i],y[i],ycalc[i],resid)
    file_out.write(str_buf)
file_out.close()
#
# plotting
#
if(MAKEPLOT):
  plt.figure(figsize=(8,7.75))
  plt.scatter(x,y,color='red',marker='o')
  plt.plot(x,ycalc,'b-')
  plt.xlabel('x')
  plt.ylabel('y')
  plt.title('Least abs. dev. fit (blue) ')
  plt.grid(True)
  plt.show()


import numpy as np
from scipy.linalg import svd

__all__ = ['orthomax']

def orthomax(Phi, gamma=1., maxiter=20, tol=1e-6):
    """
    Given n*k factor matrix Phi (k number of factors, n number of dimensions),
    find a rotated version of the factor loadings that fulfills optimization criteria,
    depending on gamma \in [0..1]

    If gamma=0 this is the "quartimax", for gamma=1 it is often called "varimax".

    It is not easy to find a good reference on this,
    one is
        Herve Abdi, "Factor Rotations in Factor Analyses."
        http://www.utd.edu/~herve/Abdi-rotations-pretty.pdf
    another which also has an algorithm listing and a nice application, but only for gamma = 0
        Justin Solomon et al., "Discovery of Intrinsic Primitives on Triangle Meshes", Eurographics 2011
        http://graphics.stanford.edu/projects/lgl/papers/sbbg-diptm-11/sbbg-diptm-11.pdf
    """
    if gamma < 0 or gamma > 1:
        raise ValueError("gamma must be between 0 and 1")
    p, k = Phi.shape
    R = np.eye(k)
    Lambda = Phi
    d_old = None
    for i in range(maxiter):
        if gamma > 0:
            Z = Lambda**3 - (gamma / p) * np.dot(Lambda, np.diag(np.diag(np.dot(Lambda.T, Lambda))))
        else:
            Z = Lambda**3
        U, s, Vt = svd(np.dot(Phi.T, Z), full_matrices=False)
        R = np.dot(U, Vt)
        Lambda = np.dot(Phi, R)
        d = np.sum(s)
        if d_old != None and d < d_old * (1 + tol):
            print("orthomax converged")
            break
        d_old = d
    return np.dot(Phi, R)

if __name__ == '__main__':
    import pylab as pl

    # generate randomly activated 2d gaussian blobs
    np.random.seed(2)
    n = 16
    n_components = 3
    n_obs = 200
    y, x = np.mgrid[:n, :n]
    xy = np.dstack((x, y)).astype(np.float)
    components = []
    for i in range(n_components):
        mean = np.random.uniform(0, n, 2)
        sigma = np.random.uniform(0, n/3, 2)
        p = np.exp( - ((xy - mean)**2 / (2*sigma)).sum(axis=-1))
        components.append(p.ravel())
    components = np.array(components)
    code = np.random.random((n_obs, n_components)) \
            + np.random.random(n_obs)[:,np.newaxis]
    obs = np.dot(code, components)
    obs += np.random.normal(scale=0.2, size=obs.shape)

    # pca
    U, s, Vt = svd(obs)
    pca_components = Vt[:n_components, :]
    # orthomaximalize the pca factors
    vmax = orthomax(pca_components.T, gamma=1., maxiter=100)

    # visualize
    pl.clf()
    for i, c in enumerate(pca_components):
        pl.subplot(3, n_components, i+1)
        pl.title("pca#%d" % i)
        pl.imshow(c.reshape(n,n))
    for i, c in enumerate(vmax.T):
        pl.subplot(3, n_components, i+4)
        pl.title("orthomax#%d" % i)
        pl.imshow(c.reshape(n,n))
    for i, c in enumerate(components[[2,0,1]]):
        pl.subplot(3, n_components, i+7)
        pl.title("original#%d" % i)
        pl.imshow(c.reshape(n,n))
    pl.show()


"""
  Name     : c9_22_LPSD.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 scipy as sp
import numpy as np

mean=0.15;
Rf=0.01
std=0.20
n=200
sp.random.seed(3412)
x=sp.random.normal(loc=mean,scale=std,size=n)

def LPSD_f(returns, Rf):
    y=returns[returns-Rf<0]
    m=len(y)
    total=0.0
    for i in sp.arange(m):
        total+=(y[i]-Rf)**2
    return total/(m-1)

answer=LPSD_f(x,Rf)
print("LPSD=",answer)


import math

import numpy

from . import util

class Exponential:
    def __init__(self, magnitude, rate):
        self.magnitude = magnitude
        self.rate = rate

    def __call__(self, x):
        return(self.magnitude*numpy.exp(-self.rate*numpy.array(x)))

    def __str__(self):
        return("{0:.2e}*exp(-{1:.2e}*t)".format(self.magnitude, self.rate))

    @property
    def area(self):
        return(self.magnitude/self.rate)

class MultiExponential:
    def __init__(self, params):
        self.exponentials = [Exponential(magnitude, rate) for magnitude, rate \
                             in zip(params[::2], params[1::2])]

    def __iter__(self):
        return(iter(self.exponentials))

    def __call__(self, x):
        return(sum(map(lambda f: f(x), self)))

    def __str__(self):
        return(" + ".join(map(str, self)))

    def __len__(self):
        return(len(self.exponentials))

    def __getitem__(self, index):
        return(self.exponentials[index])

    def __setitem__(self, index, value):
        self.exponentials[index] = value

    def sorted_by_area(self):
        return(list(sorted(self.exponentials, key=lambda x: x.area)))

    def sort_by_area(self):
        self.exponentials = self.sorted_by_area()

    def sorted_by_rate(self):
        return(list(sorted(self.exponentials, key=lambda x: x.rate)))

    def sort_by_rate(self):
        self.exponentials = self.sorted_by_rate()

    def areas(self):
        return(map(lambda x: x.area, self.exponentials))

    def rates(self):
        return(map(lambda x: x.rate, self.exponentials))

    def relative_areas(self, origin=None):
        """
        Report the relative areas of the two exponentials, optionally corrected
        by setting a starting time for integration.
        """
        if origin is None:
            return(util.normalize(self.areas))
        else:
            areas = list(map(lambda x: x.magnitude*math.exp(-x.rate*origin)\
                             /x.rate,
                             self))

            return(util.normalize(areas, key=sum))


#!/usr/bin/python3

'''
Interpolation tools
'''

try:
    import numpy as np
except ModuleNotFoundError:
    print("\"numpy\" not found. This module requires numpy")
try:
    import matplotlib.pyplot as plt
except ModuleNotFoundError:
    print("\"matplotlib\" not found. This module requires matplotlib")

from .Core import *

#######################################################################################################################################
#######################################################################################################################################
class MethodError(Exception):
    pass
########################################################################################################################################
########################################################################################################################################

def full_lagrange(xdata,ydata,points=100):
    '''
    Returns two arrays, x and y=f(x), where f is the Lagrange Polynomial Interpolation for the collection of points (xdata, ydata).
    Naturally, the x and y arrays have resolution of points given by the points argument.
    It is not recommended to interpolate a lot of points on xdata and ydata;
    the optimal value of points is maybe 15 of 20 (x,y) pairs, maybe more.
    '''
    if len(xdata)!=len(ydata):
        raise ValueError("xdata and ydata must be the same size, but have shapes %d and %d"%(len(xdata),len(ydata)))
    else:
        k=len(xdata)
    k=len(xdata)
    npx=np.array(xdata);npy=np.array(ydata)
    xglobal=np.linspace(npx[0],npx[-1],points)[:-1]
    yglobal=[]
    def L(q):
        rs=0
        for j in range(0,k):
            rp=1
            for i in exclude(j,k,0,1):
                    rp*=(q-npx[i])/(npx[j]-npx[i])
            rs+=npy[j]*rp
        return rs
    for w in xglobal:
        yglobal.append(L(w))
    return (xglobal,yglobal)

def reduced(xdata,ydata,points=100,reduction=1,method="random"):
    '''
    Reduce data before interpolation.
    '''
    if len(xdata)!=len(ydata):
        raise ValueError("xdata and ydata must be the same size, but have shapes %d and %d"%(len(xdata),len(ydata)))
    zipped=list(zip(xdata,ydata))
    zippedred=reduce_list(zipped,reduction,method)
    k_=len(zippedred)
    xunzip=np.zeros(k_)
    yunzip=np.zeros(k_)
    for i in range(k_):
        xunzip[i]+=zippedred[i][0]
        yunzip[i]+=zippedred[i][1]
    return full_lagrange(xunzip,yunzip,points)

def Interpolate_Data(x,y,n=3,p=50):
    '''
    Interpolate the data.
    It is not useful to do it with functions, but is sort of eqivalent to Newton-Cotes formulas, but slower.
    It is not a good idea use a large n. Usually 2 < n < 7
    '''
    shapes_comparation(x,y)
    res=len(x)%n
    xglobal=[]
    yglobal=[]
    for i in range(len(x)-res):
        if i%(n-1)==0:
            a=full_lagrange(x[i:i+n],y[i:i+n],p)
            xglobal+=list(a[0])
            yglobal+=list(a[1])
    if res>1:
        a=full_lagrange(x[len(x)-res+1:len(x)],y[len(x)-res+1:len(x)],100)
        xglobal+=list(a[0])
        yglobal+=list(a[1])
    return (xglobal,yglobal)


"""
Calculating the mean and the standard error of a randomly-generated sample.

CODE REVIEW CREDITS:
* IEatBagels: https://codereview.stackexchange.com/a/224039/20684
* dfhwze: https://codereview.stackexchange.com/a/224045/20684

TEST RESULTS CREDITS:
* https://www.miniwebtool.com/standard-error-calculator/

AUTHOR: Erel Segal-Halevi
SINCE:  2019-07-13
"""

import numpy as np

def mean_and_stderr(num_of_iterations:int, instance_generator) -> (float,float):
    """
    Calculate the mean and standard error of the given generator.

    :param instance_generator: a function that accepts no parameters,
             and returns either a float or a numpy array.
    :param num_of_iterations: number of times to run the instance_generator.

    :return (mean, standard_error)

    Test on a degenerate (constant) generator of numbers:
    >>> def five(): return 5
    >>> mean_and_stderr(100, five)
    (5.0, 0.0)

    Test on consecutive numbers
    >>> def iota(): iota.i+=1; return iota.i
    >>> iota.i = 0
    >>> mean_and_stderr(3, iota)   # 1,2,3
    (2.0, 0.57735026918962584)
    >>> mean_and_stderr(5, iota)   # 4,5,6,7,8
    (6.0, 0.70710678118654757)

    Test on a degenerate (constant) generator of vectors:
    >>> generator = lambda: np.array([1,2,3])
    >>> mean_and_stderr(100, generator)
    (array([ 1.,  2.,  3.]), array([ 0.,  0.,  0.]))
    """
    sum = sumSquares = None
    for i in range(num_of_iterations):
        x_i = instance_generator()
        if sum is None:
            sum = x_i
            sumSquares = (x_i*x_i)
        else:
            sum += x_i
            sumSquares += (x_i * x_i)
    mean = sum / num_of_iterations
    population_variance = sumSquares / num_of_iterations - (mean*mean)
    sample_variance = population_variance*num_of_iterations/(num_of_iterations-1)
    stderr = np.sqrt(sample_variance / num_of_iterations)
    return (mean,stderr)

if __name__=="__main__":
    generate_uniformly_random_number = np.random.random
    print(mean_and_stderr(10, generate_uniformly_random_number))
    # Typical output: (0.5863703739913031, 0.026898107452102943)
    print(mean_and_stderr(1000, generate_uniformly_random_number))
    # Typical output: (0.514204422858358, 0.0002934476865378269)

    generate_uniformly_random_vector = lambda: np.random.random(3)
    print(mean_and_stderr(10, generate_uniformly_random_vector))
    # Typical output: (array([ 0.53731682,  0.6284966 ,  0.48811251]), array([ 0.02897111,  0.0262977 ,  0.03192519]))
    print(mean_and_stderr(1000, generate_uniformly_random_vector))
    # Typical output: (array([ 0.50520085,  0.49944188,  0.50034895]), array([ 0.00028528,  0.00028707,  0.00029089]))


import os
os.environ['THEANO_FLAGS'] = 'device=cpu'

import numpy as np
import theano as th
import theano.tensor as tt
from theano import function
from theano import pp
from theano import shared
from theano.ifelse import ifelse
import time

th.config.floatX = 'float64'
th.config.mode = 'FAST_COMPILE'

# Baby Steps - Algebra
if False:
    print('--------------------')
    x = tt.dscalar('x') # declare a double scalar
    y = tt.dscalar('y')
    z = x + y
    f = function([x, y], z)
    print(f(2, 3))

if False:
    print('--------------------')
    x = tt.dmatrix('x')
    y = tt.dmatrix('y')
    z = x + y
    f = function([x, y], z)
    print(f(np.array([[1, 2], [3, 4]]), np.array([[10, 20], [30, 40]])))

# More Examples
if False:
    print('--------------------')
    a, b = tt.dmatrices('a', 'b')
    diff = a - b
    diff_abs = abs(diff)
    diff_squared = diff**2
    f = function([a, b], [diff, diff_abs, diff_squared])
    print(f([[1, 1], [1, 1]], [[0, 1], [2, 3]]))

if False:
    print('--------------------')
    state0 = shared(0)
    state1 = shared(0)
    inc = tt.iscalar('inc')
    #accumulator = function([inc], state0, updates={state0: state0+inc, state1: state1-inc})
    accumulator = function([inc], [state0, state1], updates=[(state0, state0+inc), (state1, state1-inc)])
    print(accumulator(1))
    state1.set_value(0)
    print(accumulator(300))
    print(state0.get_value(), state1.get_value())

if False:
    print('--------------------')
    state = shared(0)
    inc = tt.iscalar('inc')
    fn_of_state = state*2 + inc
    foo = tt.scalar(dtype=state.dtype)
    skip_shared = function([inc, foo], fn_of_state, givens=[(state, foo)], updates=[(state, state+1)])
    print('state: ', state.get_value())
    print(skip_shared(1, 3))
    print('state: ', state.get_value())
    print(skip_shared(1, 3))
    print('state: ', state.get_value())

# Derivatives in Theano
if False:
    print('--------------------')
    x = tt.dmatrix('x')
    y = tt.sum(1 / (1 + tt.exp(-x)))
    gy = tt.grad(y, x)
    f = th.function([x], gy)
    print(pp(gy))
    print(pp(f.maker.fgraph.outputs[0]))

if False:
    print('--------------------')
    x = tt.dvector('x')
    y = x**2
    j = th.gradient.jacobian(y, x)
    f = function([x], j)
    #print(pp(j))
    print(f([4, 4]))
    h = th.gradient.hessian(y.sum(), x)
    g = function([x], h)
    #print(pp(h))
    print(g([4, 4]))

if False:
    print('--------------------')
    x = tt.dvector('x')
    y = x**2
    J, updates = th.scan(
        lambda i,y,x: tt.grad(y[i], x),
        sequences=tt.arange(y.shape[0]),
        non_sequences=[y, x])
    f = function([x], J, updates=updates)
    print(f([4, 4]))

if False:
    print('--------------------')
    x = tt.dvector('x')
    y = (x**2).sum()
    gy = tt.grad(y, x)
    H, updates = th.scan(
        lambda i,gy,x: th.grad(gy[i], x),
        sequences=tt.arange(gy.shape[0]),
        non_sequences=[gy, x])
    f = function([x], H, updates=updates)
    print(f([4, 4]))

# Conditions
if False:
    print('--------------------')
    a, b = tt.scalars('a', 'b')
    x, y = tt.matrices('x', 'y')

    f_switch = function(
        [a, b, x, y],
        tt.switch(tt.lt(a, b), tt.mean(x), tt.mean(y)),
        mode=th.Mode(linker='vm'))
    f_ifelse = function(
        [a, b, x, y],
        ifelse(tt.lt(a, b), tt.mean(x), tt.mean(y)),
        mode=th.Mode(linker='vm'))

    val1 = 0.
    val2 = 1.
    big_mat1 = np.ones((10000, 1000))
    big_mat2 = np.ones((10000, 1000))

    n_times = 10

    tic = time.clock()
    for i in range(n_times):
        f_switch(val1, val2, big_mat1, big_mat2)
    print('time spent evaluating both values %f sec' % (time.clock() - tic))

    tic = time.clock()
    for i in range(n_times):
        f_ifelse(val1, val2, big_mat1, big_mat2)
    print('time spent evaluating both values %f sec' % (time.clock() - tic))

# Loop
if False:
    print('--------------------')
    X = tt.matrix('X')
    W = tt.matrix('W')
    B = tt.vector('B')

    results, updates = th.scan(lambda v: tt.tanh(tt.dot(v,W) + B), sequences=X)
    f = function(inputs=[X,W,B], outputs=results)

    x = np.eye(2, dtype=th.config.floatX)
    w = np.ones((2, 2), dtype=th.config.floatX)
    b = np.ones((2), dtype=th.config.floatX)
    b[1] = 2
    print('tanh(x(t)*W + b)\n', f(x, w, b))

if True:
    print('--------------------')
    coeffs = tt.vector('coeffs')
    X = tt.scalar('X')

    poly, _ = th.scan(
        lambda i, f, x: coeffs[i] + f*x,
        sequences=tt.arange(coeffs.size),
        outputs_info=tt.zeros_like(X),
        non_sequences=[X],
        go_backwards=True)
    f = function([coeffs, X], poly)
    test_coeffs = np.asarray([1, 0, 2], dtype=th.config.floatX)
    print(f(test_coeffs, 3))


import numpy as np
from scipy.optimize import minimize

def rosen(x):
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)

x0 = np.array([1.3,0.7,0.8,1.9,1.2])

print('Testing the Rosenbrock function using scipy solvers')

print('\n1 - Nelder-Mead Simplex')
res_nms = minimize(rosen, x0, method='nelder-mead', options={'xtol': 1e-8, 'disp':True})
print(res_nms.x)

print('\n2 - BFGS')
def rosen_der(x):
    xm = x[1:-1]
    xm_ml = x[:-2]
    xm_pl = x[2:]
    der = np.zeros_like(x)
    der[1:-1] = 200*(xm-xm_ml**2) - 400*(xm_pl -xm**2)*xm - 2*(1-xm)
    der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
    der[-1] = 200*(x[-1]-x[-2]**2)
    return der

res_bfgs = minimize(rosen, x0, method='BFGS', jac=rosen_der, options={'disp':True})
print(res_bfgs.x)

print('\n3- Newton-CG')
def rosen_hess(x):
    x = np.asarray(x)
    H = np.diag(-400*x[:-1],1) - np.diag(400*x[:-1],-1)
    diagonal = np.zeros_like(x)
    diagonal[0] = 1200 * x[0]**2 - 400*x[1]+2
    diagonal[-1] = 200
    diagonal[1:-1] = 202 + 1200*x[1:-1]**2 - 400*x[2:]
    H = H + np.diag(diagonal)
    return H
res_ncg = minimize(rosen,x0,method='Newton-CG',jac=rosen_der, hess=rosen_hess,options={'xtol':1e-8, 'disp':True})
print(res_ncg.x)


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

ny, nx = 5, 6

y = np.arange(0, ny)
x = np.arange(0, nx)

# x-component of covariance
Cx = np.zeros((nx, nx))
for i in range(nx):
    for j in range(nx):
        Cx[i, j] = np.exp(-0.5*((x[i] - x[j])/1.0)**2)
    Cx[i, i] += 1E-6

# y-component of covariance
Cy = np.zeros((ny, ny))
for i in range(ny):
    for j in range(ny):
        Cy[i, j] = np.exp(-0.5*((y[i] - y[j])/1.0)**2)
    Cy[i, i] += 1E-6

# Eigen decompositions
Ex, Vx = np.linalg.eig(Cx)
Ey, Vy = np.linalg.eig(Cy)

# Explicit full covariance matrix
n = nx*ny
C = np.zeros((n, n))
for i in range(n):
    for j in range(n):
        dx = x[i % nx] - x[j % nx]
        dy = y[i // nx] - y[j // nx]
        rsq = dx**2 + dy**2
        C[i, j] = np.exp(-0.5*rsq/1.0**2)
    C[i, i] += 1E-6
# C would equal np.kron(Cy, Cx) if it weren't for the extra diagonal bit.

# Some data
L = np.linalg.cholesky(C)
ns = rng.randn(n)
ys = C @ ns
img = ys.reshape((ny, nx))
plt.imshow(img)
plt.show()

# Explicit solve
logl0 = -n*0.5*np.log(2*np.pi) - 0.5*np.log(np.linalg.det(C)) \
                - 0.5*np.dot(ys, np.linalg.solve(C, ys))

# Cholesky solve
logl1 = -n*0.5*np.log(2*np.pi) - np.sum(np.log(np.diag(L))) \
                - 0.5*np.dot(ys, ns)

# Kronecker solve
E = np.kron(Ey, Ex) # All eigenvalues of C

# Dot product of the data against eigenvectors of C
# = the data represented in that basis
coeffs = np.zeros(n)
k = 0
for i in range(ny):
    for j in range(nx):
        V = np.kron(Vy[:,i], Vx[:,j])
        coeffs[k] = np.dot(ys, V)
        k += 1

logl2 = -n*0.5*np.log(2*np.pi) - 0.5*np.sum(np.log(E)) \
                - 0.5*np.dot(coeffs, coeffs/E)

# Eigendecomposition: C = V @ diag(E) @ V.T since C is symmetric V.T = Vinv

# First two should be equal, third should be close.
print(logl0, logl1, logl2)
plt.plot(coeffs)
plt.show()


# coding=utf-8
import numpy as np

# 随机数
print("random:\n{}\n".format(np.random.random(10)))  # 生成包含10个元素在[0.0, 1.0)之间的一维随机数组
print("random:\n{}\n".format(np.random.random((5, 4))))  # 生成5行4列，元素在[0.0, 1.0)之间的二维随机数组
print("rand:\n{}\n".format(np.random.rand(3, 4)))  # 根据指定的shape生成随机数
print("randint:\n{}\n".format(np.random.randint(0, 100, 20)))  # 生成指定范围内（[0, 100)）的指定数量（20）的随机整数
print("permutation:\n{}\n".format(np.random.permutation(np.arange(20))))  # 随机打乱已有的数据顺序

# 数学运算
base_data = np.random.randint(0, 100, 25).reshape(5, -1)  # 随机生成一个元素值在（[0, 100)）内的5行5列整数矩阵
print("base_data = \n{}\n".format(base_data))
print("np.amin：{} np.amax：{} np.average：{} np.sum：{} \n".format(
    np.amin(base_data),
    np.amax(base_data),
    np.average(base_data),
    np.sum(base_data)))

base_data2 = np.array([(-0.746, -0.2, -1.5, 2.0), (9.4, 7.447, 10, 11.555)])
print("base_data2 = \n{}\n".format(base_data2))
print("np.floor(base_data2)):\n", np.floor(base_data2))  # 返回不大于输入参数的最大整数，向下取整
print("np.ceil(base_data2)):\n", np.ceil(base_data2))  # 返回输入值的上限，向上取整
print("np.around(base_data2)):\n", np.around(base_data2, decimals=2))  # 返回四舍五入后的值，可指定精度
print("np.where(base_data2)):\n", np.where(base_data2 >= 3))  # 返回所有值大于等于3的元素的索引
print("np.diff(base_data2)):\n", np.diff(base_data2))  # 返回相邻元素的差（后一个元素值减去前一个元素值）
print("np.msort(base_data2)):\n", np.msort(base_data2))  # 数组排序

base_data3 = np.array([(3, 21, 10), (2, 9, 23), (11, 5, 17)])
print("\nbase_data3 = \n{}\n".format(base_data3))
print("tolist：{}\n".format(base_data3.tolist()))  # 返回python的list
print("clip：{}\n".format(base_data3.clip(2, 5)))  # 小于2的元素替换为2，大于5的元素替换为5
print("max：{} min：{} ptp：{} \n".format(base_data3.max(),
                                       base_data3.min(),
                                       base_data3.ptp()))  # 返回所有元素的最大值、最小值、最大值和最小值之差
print("var：{} std：{}\n".format(base_data3.var(),
                               base_data3.std()))  # 返回方差（元素与均值之差的平方的均值）、标准差（方差的算术平方根）
print("sum：{} prod：{} mean：{} \n".format(base_data3.sum(),
                                         base_data3.prod(),
                                         base_data3.mean()))  # 返回所有元素的和、乘积、算术平均值
print("argmin：{} argmax：{}\n".format(base_data3.argmin(),
                                     base_data3.argmax()))  # 返回最小值、最大值在扁平数组中的索引

base_data4 = np.array([3, 2, 4, 1, 5])
print("\nbase_data4 = \n{}\n".format(base_data4))
print("np.log(base_data4)): ", np.log(base_data4))  # 返回对数数组
print("np.exp(base_data4)): ", np.exp(base_data4))  # 返回指数数组
print("np.sqrt(base_data4)): ", np.sqrt(base_data4))  # 返回开方数组
print("np.power(base_data4, 3)): ", np.power(base_data4, 3))  # 返回3次方数组

# ### 随机数
# Docs：https://docs.scipy.org/doc/numpy/reference/routines.random.html
# 利用numpy.random包中的随机数算法可以生成多种随机数据；
#
# ### 数学运算
# Docs：https://docs.scipy.org/doc/numpy/reference/routines.math.html


# -*- coding: utf-8 -*-
"""
  Name     : c10_43_EW_port.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_A=np.array([0.102,-0.02, 0.213,0.12,0.13])
ret_B=np.array([0.1062,0.23, 0.045,0.234,0.113])

port_EW=(ret_A+ret_B)/2

round(np.mean(ret_A),3),round(np.mean(ret_B),3),round(np.mean(port_EW),3)

round(np.std(ret_A),3),round(np.std(ret_B),3),round(np.std(port_EW),3)


import numpy as np
from BasicML import visdata

class Regression:

    def __init__(self, x, y, w=[], d=1):
        self.y = y
        self.d = d
        self.w = w
        self.x = x
        self.m = x.shape[0]

    def poly_feat(self, x):
        px = []
        for i in range(2, self.d+1):
            px.append(x[:,1:]**i)
        return np.squeeze(np.array(px)).T

    def add_poly_feat(self, x):
        if self.d < 2:
            pass
        elif self.d == 2:
            zz = np.reshape(self.poly_feat(x), (self.poly_feat(x).shape[0], 1))
            x = np.append(x, zz, axis=1)
        else:
            x = np.append(x, self.poly_feat(x), axis=1)
        return x

    def initiate_weights(self,  process="zero"):
        if process == "zero":
            self.w = np.zeros((self.x.shape[1], 1))
        elif process == "random":
            self.w = np.random.randn(self.x.shape[1], 1)

    def cost_fn(self, reg):
        J = (np.sum((self.y - self.x@self.w)**2) + reg*np.sum(self.w[1:]@self.w[1:].T))/(2*self.m)
        return J

    def gradients(self, x, y, w, reg):
        dw = np.zeros(w.shape)
        m = x.shape[0]
        dw[0] = x[:,0].T@(x@w - y)/m
        dw[1:] = (x[:,1:].T@(x@w - y) + reg*w[1:])/m
        return dw

    def train(self, alpha=0.001, batch_size=1, n=1000, reg=0):
        cost = np.zeros((n,2))
        self.x = self.add_poly_feat(self.x)
        self.initiate_weights()
        ind = np.random.permutation(self.m)
        X = self.x[ind]
        Y = self.y[ind]
        for i in range(n):
            for j in range(self.m//batch_size):
                x = X[j*batch_size:(j+1)*batch_size, :]
                y = Y[j*batch_size:(j+1)*batch_size, :]
                self.w = self.w-alpha*self.gradients(x, y, self.w, reg)
            if self.m % batch_size != 0:
                x = X[self.m//batch_size*batch_size:self.m]
                y = Y[self.m//batch_size*batch_size:self.m]
            self.w = self.w - alpha*self.gradients(x, y, self.w, reg)
            print("Cost after", i+1, "Iterations : ", self.cost_fn(reg))
            cost[i,0] = i+1
            cost[i,1] = self.cost_fn(reg)
        print("\nFinal weights after training : ", self.w)
        return cost

    def predict(self, x):
        x = self.add_poly_feat(x)
        y_ = x@self.w
        return y_


# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.4.2
#   kernelspec:
#     display_name: Python [conda env:mdd] *
#     language: python
#     name: conda-env-mdd-py
# ---

# %% [markdown]
# # Using FFT the right way to find the correct spectrum
#

# %% jupyter={"outputs_hidden": false}
from numpy import *
from numpy.fft import fft
import matplotlib.pyplot as plt
# %matplotlib inline

# redefine default figure size and fonts
import matplotlib as mpl
mpl.rc('font', size=16)
mpl.rc('figure',figsize=(12,10))
mpl.rc('lines', linewidth=1, color='lightblue',linestyle=':',marker='o')

# %%
import sys
sys.path.append('../../scripts')
from signal_processing import create_signal, spectrum

# %%
fs = 30 # sampling frequency, Hz
N = 256 # number of sampling points

t,y = create_signal(fs,N)

# %%
def plot_signal_with_fft(t,y,fs,N):
    """ plots a signal in time domain and its spectrum using
    sampling frequency fs and number of points N
    """

    T = N/fs # sampling period
    y -= y.mean() # remove DC
    frq,Y = spectrum(y,fs) # FFT(sampled signal)

    # Plot
    fig, ax = plt.subplots(2,1)
    ax[0].plot(t,y,'b.')
    ax[0].plot(t,y,':',color='lightgray')
    ax[0].set_xlabel('$t$ [s]')
    ax[0].set_ylabel('Y [V]')
    ax[0].plot(t[0],y[0],'ro')
    ax[0].plot(t[-1],y[-1],'ro')
    ax[0].grid()

    ax[1].plot(frq,abs(Y),'r') # plotting the spectrum
    ax[1].set_xlabel('$f$ (Hz)')
    ax[1].set_ylabel('$|Y(f)|$')
    lgnd = str(r'N = %d, f = %d Hz' % (N,fs))
    ax[1].legend([lgnd],loc='best')
    ax[1].grid()

# %%
### Note we remove DC right before the spectrum

# %%
plot_signal_with_fft(t,y,fs,N)

# %%
fs = 66 # sampling frequency, Hz
N = 256 # number of sampling points

t,y = create_signal(fs,N)
plot_signal_with_fft(t,y,fs,N)

# %%
fs = 100 # sampling frequency, Hz
N = 256 # number of sampling points

t,y = create_signal(fs,N)
plot_signal_with_fft(t,y,fs,N)

# %%
fs = 500 # sampling frequency, Hz
N = 256 # number of sampling points

t,y = create_signal(fs,N)
plot_signal_with_fft(t,y,fs,N)

# %% [markdown]
# * Very poor frequency resolution, $\Delta f = 1/T = fs/N = 500 / 256 = 1.95 Hz$ versus $\Delta f = 100 / 256 = 0.39 Hz$
# * peaks are at 9.76 Hz and 35.2 Hz
# * wasting a lot of frequency axis above 2 * 35.7

# %% [markdown]
# ## So the problem is still there:
# the peak is now shifted to 35.5 Hz, let's see: when we sampled at 30 we got aliased 35.5 - 30 = 5.5 Hz. Seems correct,
# when we sampled at 66 it was above 2/3*f but below 2*f, so 66 - 35.5 = 30.5 Hz, also seem to fit. So now we have explained (!)
# the previous results and we believe that hte signal has 10 Hz and 35.5 Hz (approximately) and 100 Hz is good enough
# but we could improve it by going slightly above Nyquist 2*35.5 = 71 Hz, and also try to fix the number of points such that
# we get integer number of periods. It's imposible since we have two frequencies, but at least we can try to get it close
# enough: 1/10 = 0.1 sec so we could do 256 * 0.1 = 25.6 Hz or X times that = 25.6 * 3 = 102.4 - could be too close
# let's try:

# %% jupyter={"outputs_hidden": false}
fs = 102.4 # sampling frequency, Hz
N = 256 # number of sampling points
t,y = create_signal(fs,N)
plot_signal_with_fft(t,y,fs,N)

# %% [markdown]
# Then the same principle to get better 35.5 Hz
# 1/35.5 * 256 = 7.21 * 15 = 108.16 Hz

# %% jupyter={"outputs_hidden": false}
fs = 108.16 # sampling frequency, Hz
N = 256 # number of sampling points
t,y = create_signal(fs,N)
plot_signal_with_fft(t,y,fs,N)

# %% jupyter={"outputs_hidden": false}
fs = 144.22 # sampling frequency, Hz
N = 512 # number of sampling points
t,y = create_signal(fs,N)
plot_signal_with_fft(t,y,fs,N)

# %% [markdown]
# ## Use windowing or low-pass filtering
#

# %% jupyter={"outputs_hidden": false}
from scipy.signal import hann  # Hanning window

# %% jupyter={"outputs_hidden": false}
fs = 150; N = 256;
t,y = create_signal(fs,N)

# %% jupyter={"outputs_hidden": false}
# we made a mistake with the DC

DC = y.mean()
y -= DC

yh = y*h

plt.plot(t,y,'b-',t,yh,'r')

# %% jupyter={"outputs_hidden": false}
frq,Y = spectrum(yh,fs)

Y = abs(Y)*sqrt(8./3.)# /(N/2)

plt.plot(frq,Y)

# %% [markdown]
# ### let's try to reconstruct

# %% jupyter={"outputs_hidden": false}
a1,f1 = Y.max(),frq[Y.argmax()]
print (a1,f1)
b = Y.copy()
b[:Y.argmax()+10] = 0 # remove the first peak
a2,f2 = b.max(),frq[b.argmax()]
print (a2,f2)

# %% jupyter={"outputs_hidden": false}
yhat = DC + a1*sin(2*pi*f1*t) + a2*sin(2*pi*f2*t)
plt.plot(t,y+DC,'b-',t,yhat,'r--');
plt.legend(('$y$','$\hat{y}$'),fontsize=16);


import numpy as np
import matplotlib.pyplot as plt

def sine_func(x_i):
	y = np.sin(2*np.pi*x_i)
	return y

def gen_toy_sine_curve_dataset(N, std, periods=1):
	"""
	:param N: Number of data points/samples
	:param std: Standard deviation for (white/Gaussian) noise input
	:return: 2-tuple (x_vector, y_train)
	"""
	y_vector = []
	x_vector = []
	for period_i in range(periods):
		for x_i in np.linspace(0, 1, N):
			x_i = x_i + period_i
			noise = np.random.normal(0, std)
			y_i = sine_func(x_i) + noise
			x_vector.append(x_i)
			y_vector.append(y_i)
	return x_vector, y_vector

N = 50
periods = 3
std_noise = 0

x_train, y_train = gen_toy_sine_curve_dataset(N, std_noise, periods)
# x_vector_test, y_hat_vector = gen_toy_sine_curve_dataset(10, 0.25)

fig_dims = (12, 12)
fig, ax = plt.subplots(figsize=fig_dims)
# fig, axes = plt.subplots(1, 3, figsize = fig_dims)
ax.plot(x_train, y_train, "g", label="Sine curve plot (x_train vs y_train) - num data points = {0} std = {1}".format(N, std_noise))
ax.grid(True)
ax.legend()
# plt.plot(x_train, y_train, "g")
# plt.plot(x_vector_test, y_hat_vector, "r")
plt.show()


#!/usr/bin/python
"""
author  : Bhekimpilo Ndhlela
author  : 18998712
module  : Applied Mathematics(Numerical Methods) TW324
task    : Computer Assignment 03 question 3 (a. to e.)
since   : Friday-09-03-2018
"""

def question_a(N=4, debug=True):
    x = zeros(N)
    x = [cos(((2 * k - 1) * pi)/(2 * N)) for k in xrange(1, N + 1)]
    # defining the chebyshev Vandermonde matrix where V = 4x4 Matrix
    V = C.chebvander(x, N - 1)
    if debug is True:
        print "DEBUG MODE: ON [Question 3 (a.)]:"
        print "vector x   = ", x
        print "\nchebyshev Vandermonde matrix where V:      \n", V
    return x, V

def question_b(x, V, debug=True):
    # finding the e^xi for the x elements
    exp_x = [exp(i) for i in x]
    V_inv = inv(V)
    c = matmul(V_inv, exp_x)
    if debug is True:
        print "\nDEBUG MODE: ON [Question 3 (b.)]:"
        print "vector e^x = ", exp_x
        print "\n(chebyshev Vandermonde matrix)^-I where V :\n", V_inv
        print "\nVc=exp(x)=> c=inv(V)exp(c) <=> inv(V)V=I  :\n", c

def question_c(b=3, a=0, N=0.0, debug=True):
    approx = lambda N : exp(N) * ((b-a)**N / (fact(N) * 2**(2*N-1)))
    while 10**-10 <= approx(N):
        N = N + 1.0
        print N
    if debug is True:
        print "\nDEBUG MODE: ON [Question 3 (c.)]:"
        print "Number of Chebyshev Points N = ", int(N)
        print "Approximated Value,      @ N = ", approx(N)
    return N

def question_d(N, debug=True):
    chebyshev_points = zeros(int(N))
    chebyshev_points = [3./2. + 3./2.*cos(((2 * k - 1) * pi)/(2 * N)) \
                        for k in xrange(1, int(N) + 1)]
    if debug is True:
        print "\nDEBUG MODE : ON [Question 3 d.)]"
        print "The n Chebyshev Point where n = ", int(N)
        print "i", "\t", "Chebyshev_Points"
        for i, value in enumerate(chebyshev_points):
            print i, "\t", value
    return chebyshev_points

def question_e(chebyshev_points, debug=True):
    exp_xk = [exp(vk) for vk in chebyshev_points]
    #use polyfit
    fit = polyfit(chebyshev_points, exp_xk, len(exp_xk) - 1)
    err_vals = [e - f for e, f in zip(exp_xk, fit)]
    warnings.simplefilter('ignore', RankWarning) #ignore warnings
    # plot the error function
    #plt.plot(linspace(0,3,num=19), exp_xk, label="exp(xk)")
    plt.plot(linspace(0,3,num=19), err_vals, label="err_vals")
    #plt.plot(linspace(0,3,num=19), fit, label="fit")
    plt.title('Error Functioin')
    plt.ylabel('Error')
    plt.xlabel('x')
    plt.legend(bbox_to_anchor=(1.0, 1), loc=0, borderaxespad=0.)
    plt.show()
    if debug is True:
        print "\nDEBUG MODE : ON  [Question 3 e.)]"
        print "k", "\t", "chebyshev_points(k)", "\t", "exp(k)\t\t\t",\
              "polyfit_points(k)"
        for k, (xk, e) in enumerate(zip(chebyshev_points, exp_xk)):
            print k, "\t", "{:.16f}".format(xk), "\t","{:.16f}".format(e), \
                  "\t", "{:.16f}".format(fit[k])
    else:
        print "\nDEBUG MODE : OFF [Question 3 e.)]"

if __name__ == "__main__":
    from math import (cos, pi, exp)
    from numpy import (array, matmul, zeros, RankWarning)
    from numpy import (linspace, shape, transpose, polyfit)
    from numpy.polynomial import chebyshev as C
    from numpy.linalg import inv
    from scipy.misc import factorial as fact
    import warnings
    import matplotlib.pyplot as plt

    x, V = question_a(debug=True)
    question_b(x, V, debug=True)
    N = question_c()
    chebyshev_points = question_d(N)
    question_e(chebyshev_points)
else:
    import sys
    sys.exit("Run the library as the client.")


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

# # Calculating the Bilingual Evaluation Understudy (BLEU) score: Ungraded Lab

# In this ungraded lab, we will implement a popular metric for evaluating the quality of machine-translated text: the BLEU score proposed by Kishore Papineni, et al. in their 2002 paper ["BLEU: a Method for Automatic Evaluation of Machine Translation"](https://www.aclweb.org/anthology/P02-1040.pdf), the BLEU score works by comparing "candidate" text to one or more "reference" translations. The result is better the closer the score is to 1. Let's see how to get this value in the following sections.

# # Part 1:  BLEU Score

# ## 1.1  Importing the Libraries
#
# We will first start by importing the Python libraries we will use in the first part of this lab. For learning, we will implement our own version of the BLEU Score using Numpy. To verify that our implementation is correct, we will compare our results with those generated by the [SacreBLEU library](https://github.com/mjpost/sacrebleu). This package provides hassle-free computation of shareable, comparable, and reproducible BLEU scores. It also knows all the standard test sets and handles downloading, processing, and tokenization.

# In[1]:

import numpy as np                  # import numpy to make numerical computations.
import nltk                         # import NLTK to handle simple NL tasks like tokenization.
nltk.download("punkt")
import math
from nltk.util import ngrams
from collections import Counter     # import a counter.

import sacrebleu                    # import sacrebleu in order compute the BLEU score.
import matplotlib.pyplot as plt     # import pyplot in order to make some illustrations.

# ## 1.2  Defining the BLEU Score
#
# You have seen the a way for calculating the BLEU score in this week's lectures. More formally, we can express the BLEU score as:
#
# $$BLEU = BP\times\Bigl(\prod_{i=1}^{4}precision_i\Bigr)^{(1/4)}$$
#
#
# We used the productory and the denominator of the exponent as 4 because we are assuming that we are going to work up to 4-grams. The Brevity Penalty defined as an exponential decay:
#
# $$BP = min\Bigl(1, e^{(1-({ref}/{cand}))}\Bigr)$$
#
# In the previous equation, ${ref}$ and ${cand}$ refers to the length or count of words in the reference and candidate translations. The brevity penaly $BP$ helps to handle very short translations.
#
# The precision is defined as :
#
# $$precision_i = \frac {\sum_{snt \in{cand}}\sum_{i\in{snt}}min\Bigl(m^{i}_{cand}, m^{i}_{ref}\Bigr)}{w^{i}_{t}}$$
#
# where:
#
# * $m^{i}_{cand}$, is the count of i-gram in candidate matching the reference translation.
# * $m^{i}_{ref}$, is the count of i-gram in the reference translation.
# * $w^{i}_{t}$, is the total number of i-grams in candidate translation.
#

# ## 1.3 Explaining the BLEU score

# ### Brevity Penalty (example):

# In[2]:

ref_length = np.ones(100)
can_length = np.linspace(1.5, 0.5, 100)
x = ref_length / can_length
y = 1 - x
y = np.exp(y)
y = np.minimum(np.ones(y.shape), y)

# Code for in order to make the plot
fig, ax = plt.subplots(1)
lines = ax.plot(x, y)
ax.set(
    xlabel="Ratio of the length of the reference to the candidate text",
    ylabel="Brevity Penalty",
)
plt.show()

# The brevity penalty penalizes generated translations that are too short compared to the closest reference length with an exponential decay. The brevity penalty compensates for the fact that the BLEU score has no recall term.

# ### N-Gram Precision (example):

# In[3]:

# Mocked dataset showing the precision for different n-grams
data = {"1-gram": 0.8, "2-gram": 0.7, "3-gram": 0.6, "4-gram": 0.5}
names = list(data.keys())
values = list(data.values())

fig, ax = plt.subplots(1)
bars = ax.bar(names, values)
ax.set(ylabel="N-gram precision")

plt.show()

# The n-gram precision counts how many unigrams, bigrams, trigrams, and four-grams (i=1,...,4) match their n-gram counterpart in the reference translations. This term acts as a precision metric. Unigrams account for adequacy while longer n-grams account for fluency of the translation. To avoid overcounting, the n-gram counts are clipped to the maximal n-gram count occurring in the reference ($m_{n}^{ref}$). Typically precision shows exponential decay with the degree of the n-gram.

# ### N-gram BLEU score (example):

# In[4]:

# Mocked dataset showing the precision multiplied by the BP for different n-grams
data = {"1-gram": 0.8, "2-gram": 0.77, "3-gram": 0.74, "4-gram": 0.71}
names = list(data.keys())
values = list(data.values())

fig, ax = plt.subplots(1)
bars = ax.bar(names, values)
ax.set(ylabel="Modified N-gram precision")

plt.show()

# When the n-gram precision is multiplied by the BP, then the exponential decay of n-grams is almost fully compensated. The BLEU score corresponds to a geometric average of this modified n-gram precision.

# ## 1.4 Example Calculations of the BLEU score

# In this example we will have a reference translation and 2 candidates translations. We will tokenize all sentences using the NLTK package introduced in Course 2 of this NLP specialization.

# In[5]:

reference = "The NASA Opportunity rover is battling a massive dust storm on planet Mars."
candidate_1 = "The Opportunity rover is combating a big sandstorm on planet Mars."
candidate_2 = "A NASA rover is fighting a massive storm on planet Mars."

tokenized_ref = nltk.word_tokenize(reference.lower())
tokenized_cand_1 = nltk.word_tokenize(candidate_1.lower())
tokenized_cand_2 = nltk.word_tokenize(candidate_2.lower())

print(f"{reference} -> {tokenized_ref}")
print("\n")
print(f"{candidate_1} -> {tokenized_cand_1}")
print("\n")
print(f"{candidate_2} -> {tokenized_cand_2}")

# ### STEP 1: Computing the Brevity Penalty

# In[6]:

def brevity_penalty(candidate, reference):
    ref_length = len(reference)
    can_length = len(candidate)

    # Brevity Penalty
    if ref_length < can_length: # if reference length is less than candidate length
        BP = 1 # set BP = 1
    else:
        penalty = 1 - (ref_length / can_length) # else set BP=exp(1-(ref_length/can_length))
        BP = np.exp(penalty)

    return BP

# ### STEP 2: Computing the Precision

# In[7]:

def clipped_precision(candidate, reference):
    """
    Clipped precision function given a original and a machine translated sentences
    """

    clipped_precision_score = []

    for i in range(1, 5):
        ref_n_gram = Counter(ngrams(reference,i))
        cand_n_gram = Counter(ngrams(candidate,i))

        c = sum(cand_n_gram.values())

        for j in cand_n_gram: # for every n-gram up to 4 in candidate text
            if j in ref_n_gram: # check if it is in the reference n-gram
                if cand_n_gram[j] > ref_n_gram[j]: # if the count of the candidate n-gram is bigger
                                                   # than the corresponding count in the reference n-gram,
                    cand_n_gram[j] = ref_n_gram[j] # then set the count of the candidate n-gram to be equal
                                                   # to the reference n-gram
            else:
                cand_n_gram[j] = 0 # else set the candidate n-gram equal to zero

        clipped_precision_score.append(sum(cand_n_gram.values())/c)

    weights =[0.25]*4

    s = (w_i * math.log(p_i) for w_i, p_i in zip(weights, clipped_precision_score))

    s = math.exp(math.fsum(s))
    return s

# ### STEP 3: Computing the BLEU score

# In[8]:

def bleu_score(candidate, reference):
    BP = brevity_penalty(candidate, reference)
    precision = clipped_precision(candidate, reference)
    return BP * precision

# ### STEP 4: Testing with our Example Reference and Candidates Sentences

# In[9]:

print(
    "Results reference versus candidate 1 our own code BLEU: ",
    round(bleu_score(tokenized_cand_1, tokenized_ref) * 100, 1),
)

print(
    "Results reference versus candidate 2 our own code BLEU: ",
    round(bleu_score(tokenized_cand_2, tokenized_ref) * 100, 1),
)

# ### STEP 5: Comparing the Results from our Code with the Sacrebleu Library

# In[10]:

print(
    "Results reference versus candidate 1 sacrebleu library sentence BLEU: ",
    round(sacrebleu.corpus_bleu(candidate_1, reference).score, 1),
)
print(
    "Results reference versus candidate 2 sacrebleu library sentence BLEU: ",
    round(sacrebleu.corpus_bleu(candidate_2, reference).score, 1),
)

# # Part 2:  BLEU computation on a corpus

# ## Loading Data Sets for Evaluation Using the BLEU Score

# In this section, we will show a simple pipeline for evaluating machine translated text. Due to storage and speed constraints, we will not be using our own model in this lab (you'll get to do that in the assignment!). Instead, we will be using [Google Translate](https://translate.google.com) to generate English to German translations and we will evaluate it against a known evaluation set. There are three files we will need:
#
# 1. A source text in English. In this lab, we will use the first 1671 words of the [wmt19](http://statmt.org/wmt19/translation-task.html) evaluation dataset downloaded via SacreBLEU. We just grabbed a subset because of limitations in the number of words that can be translated using Google Translate.
# 2. A reference translation to German of the corresponding first 1671 words from the original English text. This is also provided by SacreBLEU.
# 3. A candidate machine translation to German from the same 1671 words. This is generated by feeding the source text to a machine translation model. As mentioned above, we will use Google Translate to generate the translations in this file.
#
# With that, we can now compare the reference and candidate translation to get the BLEU Score.

# In[11]:

# Loading the raw data
wmt19_src = open("data/wmt19_src.txt", "r")
wmt19_src_1 = wmt19_src.read()
wmt19_src.close()

wmt19_ref = open("data/wmt19_ref.txt", "r")
wmt19_ref_1 = wmt19_ref.read()
wmt19_ref.close()

wmt19_can = open("data/wmt19_can.txt", "r")
wmt19_can_1 = wmt19_can.read()
wmt19_can.close()

tokenized_corpus_src = nltk.word_tokenize(wmt19_src_1.lower())
tokenized_corpus_ref = nltk.word_tokenize(wmt19_ref_1.lower())
tokenized_corpus_cand = nltk.word_tokenize(wmt19_can_1.lower())

# Inspecting the first sentence of the data.

# In[12]:

print("English source text:")
print("\n")
print(f"{wmt19_src_1[0:170]} -> {tokenized_corpus_src[0:30]}")
print("\n")
print("German reference translation:")
print("\n")
print(f"{wmt19_ref_1[0:219]} -> {tokenized_corpus_ref[0:35]}")
print("\n")
print("German machine translation:")
print("\n")
print(f"{wmt19_can_1[0:199]} -> {tokenized_corpus_cand[0:29]}")

# In[13]:

print(
    "Results reference versus candidate 1 our own BLEU implementation: ",
    round(bleu_score(tokenized_corpus_cand, tokenized_corpus_ref) * 100, 1),
)

# In[14]:

print(
    "Results reference versus candidate 1 sacrebleu library BLEU: ",
    round(sacrebleu.corpus_bleu(wmt19_can_1, wmt19_ref_1).score, 1),
)

# **BLEU Score Interpretation on a Corpus**
#
# |Score      | Interpretation                                                |
# |:---------:|:-------------------------------------------------------------:|
# | < 10      | Almost useless                                                |
# | 10 - 19   | Hard to get the gist                                          |
# | 20 - 29   | The gist is clear, but has significant grammatical errors     |
# | 30 - 40   | Understandable to good translations                           |
# | 40 - 50   | High quality translations                                     |
# | 50 - 60   | Very high quality, adequate, and fluent translations          |
# | > 60      | Quality often better than human                               |

# From the table above (taken [here](https://cloud.google.com/translate/automl/docs/evaluate)), we can see the translation is high quality (*if you see "Hard to get the gist", please open your workspace, delete `wmt19_can.txt` and get the latest version via the Lab Help button*). Moreover, the results of our coded BLEU score are almost identical to those of the SacreBLEU package.


from typing import Tuple, Union

from sympy import Symbol, sympify
from sympy.core.function import Function
from sympy.parsing.sympy_parser import (convert_xor, function_exponentiation,
                                        implicit_multiplication_application,
                                        parse_expr, standard_transformations)

from ..error_handler import AGreatB, SameSigns

x = Symbol('x')
transformations = standard_transformations + \
    (convert_xor, implicit_multiplication_application, function_exponentiation)

def bisection(f: Function, a: float, b: float, error: float,
              rational: bool = False, swap: bool = False,
              iterated_data: bool = False) -> Union[float, Tuple[float, dict]]:
    """
    Using bisection method, returns root (or tupled with iterated data).
    - Repeatedly bisects an interval and then selects a
    sub-interval in which a root must lie for further
    processing.

    ..Note:
        - Return iterated_data defaults to False
        - Always use 'x' as symbol

    Algorithm
    =========
    1. Find two numbers (a, b) of which f(x) has different signs
    2. Define c = (a+b)/2
    3. if (b-c) <= error tolerance, accept c as the root,
        stop iteration
    4. if f(a)f(c) <= 0, b = c else a = c

    Examples
    ========
    ** Non-pythonic expressions will be parsed by SymPy module
    >>> from newton_raphson import newton_raphson
    >>> bisection('x^2-8x+11', 1, 2, 0.001)
    1.7646484375

    ** Using pythonic expressions is also accepted
    >>> bisection('x**2-8*x+11', 1, 2, 0.001)
    1.7646484375

    ** Turning rational True
    >>> bisection('x^3-3x+1', 0, 1, 0.001, rational=True)
    355/1024

    ** Turning iterated_data True
    >>> bisection('x^3-3x+1', 0, 1, 0.001, iterated_data=True)
    (0.3466796875, {1: {'a': 0, 'b': 1, 'c': 1/2, 'fa': 1, 'fc': -3/8, 'bc': 1/2, 'swap': 'b = c'}, ...)

    Iterated data
    =============
    count / iteration :
        - The first key you see in the dictionary

    a :
        - Value of the first interval

    b :
        - Value of the second interval

    c :
        - Value of (a + b)/2 [value of the root]

    fa :
        - Value of the f(a)

    fc :
        - Value of the f(c)

    bc :
        - Value of (b - c)

    swap :
        - What values swapped

    Exceptions
    ==========
    SameSign :
        - Raised when values of f(a) and f(b) has the same sign

    AGreatB :
        - Raised when the value of a is greater than b
        - [Optional flag] - Swap the values

    Parameters
    ==========
    f :
        - Should be STRING
        - Mathematical expression
        - A mathematical function
        - Example:
            'x^2-8x+11'
            'x**3-3*x+1'
            'x^3+10x^2-5'

    a :
        - The first interval

    b :
        - The second interval

    error :
        - Error tolerance

    rational :
        - Returns fraction/rational value
        - Defaults to False

    swap :
        - Flag to prevent AGreatB Exception
        - Defaults to False

    iterated_data:
        - Returns the iterated data in dictionary
        - Defaults to False
    """

    f = parse_expr(f, transformations=transformations)
    a = sympify(a)
    b = sympify(b)
    error = sympify(error)

    if a > b and swap:
        z = a
        a = b
        b = z
        del z
    elif a > b:
        raise AGreatB

    if f.subs(x, a) * f.subs(x, b) > 0.0:
        raise SameSigns

    data = {}
    count = 1

    while True:
        c = (a + b) / 2
        fa = f.subs(x, a)
        fc = f.subs(x, c)
        bc = b-c

        data[count] = {'a': a, 'b': b, 'c': c, 'fa': fa, 'fc': fc, 'bc': bc,
                       'swap': 'b = c' if fa * fc <= 0 else 'a = c'}

        if bc <= error or round(bc, 4) <= error:
            break

        if fa*fc <= 0:
            b = c
        else:
            a = c

        count += 1

    if not rational:
        c = float(c)

    if iterated_data:
        return c, data
    else:
        return c

def false_position(f: Function, a: float, b: float, rational: bool = False,
                   swap: bool = False, iterated_data: bool = False) -> Union[float, Tuple[float, dict]]:
    """
    Using false-position method, returns root (or tupled with iterated data)
    - The same as bisection method, because the size of an interval
    containing a root is reduced through iteration.

    ..Note:
        - Return iterated_data defaults to False
        - Always use 'x' as symbol

    Algorithm
    =========
    1. Find two numbers (a, b) of which f(x) has different signs
    2. Define c = (a*fb-b*fa)/(fb-fa)
    3. if b and c are converging, return c as root
    4. if f(a)f(c) <= 0, b = c else a = c

    Examples
    ========
    ** Non-pythonic expressions will be parsed by SymPy module
    >>> false_position('x^2-8x+11', 1, 2)
    1.7639484978540771

    ** Using pythonic expressions is also accepted
    >>> false_position('x**2-8*x+11', 1, 2)
    1.7639484978540771

    ** Turning rational True
    >>> false_position('x^3-3x+1', 0, 1, rational=True)
    119624146949151814554649/344443180703677909347131

    ** Turning iterated_data True
    >>> false_position('x^3-3x+1', 0, 1, iterated_data=True)
    (0.3472971847047936, {1: {'a': 0, 'b': 1, 'fa': 1, 'fb': -1, 'c': 1/2, 'fc': -3/8, 'CoS': '-'}, ...)

    Iterated data
    =============
    count / iteration :
        - The first key you see in the dictionary

    a :
        - Value of the first interval

    b :
        - Value of the second interval

    fa :
        - Value of the f(a)

    fb :
        - Value of the f(b)

    c :
        - Value of (a*fb-b*fa)/(fb-fa) [value of the root]

    fc :
        - Value of the f(c)

    Exceptions
    ==========
    SameSign :
        - Raised when values of f(a) and f(b) has the same sign

    AGreatB :
        - Raised when the value of a is greater than b
        - [Optional flag] - Swap the values

    Parameters
    ==========
    f :
        - Should be STRING
        - Mathematical expression
        - A mathematical function
        - Example:
            'x^2-8x+11'
            'x**3-3*x+1'
            'x^3+10x^2-5'

    a :
        - The first interval

    b :
        - The second interval

    rational :
        - Returns fraction/rational value
        - Defaults to False

    swap :
        - Flag to prevent AGreatB Exception
        - Defaults to False

    iterated_data:
        - Returns the iterated data in dictionary
        - Defaults to False
    """
    from math import isclose

    f = parse_expr(f, transformations=transformations)
    a = sympify(a)
    b = sympify(b)

    if a > b and swap:
        z = a
        a = b
        b = z
        del z
    elif a > b:
        raise AGreatB

    if f.subs(x, a) * f.subs(x, b) > 0.0:
        raise SameSigns

    data = {}
    count = 1

    while True:
        fa = f.subs(x, a)
        fb = f.subs(x, b)
        c = (a*fb-b*fa)/(fb-fa)
        fc = f.subs(x, c)

        data[count] = {'a': a, 'b': b, 'fa': fa, 'fb': fb, 'c': c,
                       'fc': fc}

        if isclose(b, c, rel_tol=1e-4):
            break
        if fa*fc <= 0:
            b = c
        else:
            a = c

        count += 1

    if not rational:
        c = float(c)

    if iterated_data:
        return c, data
    else:
        return c


import matplotlib
matplotlib.use("TKagg")
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

# use seaborn plotting defaults
import seaborn as sns; sns.set()

#Motivating Support Vector Machines
#SVMs are powerful and flexible supervised algorithmes
#for classification and regression
#This is an example of  discriminative classification

from sklearn.datasets.samples_generator import make_blobs
X, y = make_blobs(n_samples=50, centers=2,
                  random_state=0, cluster_std=0.60)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn');
plt.show()
plt.clf()

#Theres a couple dividing lines we could draw
xfit = np.linspace(-1, 3.5)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plt.plot([0.6], [2.1], 'x', color='red', markeredgewidth=2, markersize=10)

for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]:
    plt.plot(xfit, m * xfit + b, '-k')

plt.xlim(-1, 3.5);
plt.show()
plt.clf()

#Maximizing the margin
xfit = np.linspace(-1, 3.5)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')

for m, b, d in [(1, 0.65, 0.33), (0.5, 1.6, 0.55), (-0.2, 2.9, 0.2)]:
    yfit = m * xfit + b
    plt.plot(xfit, yfit, '-k')
    plt.fill_between(xfit, yfit - d, yfit + d, edgecolor='none',
                     color='#AAAAAA', alpha=0.4)

plt.xlim(-1, 3.5);
plt.show()
plt.clf()

from sklearn.svm import SVC #'Support Vector Classifier'
model = SVC(kernel='linear', C=1E10)
model.fit(X, y)

def plot_svc_decision_function(model, ax=None, plot_support=True):
    """Plot the decision function for a 2D SVC"""
    if ax is None:
        ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    # create grid to evaluate model
    x = np.linspace(xlim[0], xlim[1], 30)
    y = np.linspace(ylim[0], ylim[1], 30)
    Y, X = np.meshgrid(y, x)
    xy = np.vstack([X.ravel(), Y.ravel()]).T
    P = model.decision_function(xy).reshape(X.shape)

    # plot decision boundary and margins
    ax.contour(X, Y, P, colors='k',
               levels=[-1, 0, 1], alpha=0.5,
               linestyles=['--', '-', '--'])

    # plot support vectors
    if plot_support:
        ax.scatter(model.support_vectors_[:, 0],
                   model.support_vectors_[:, 1],
                   s=300, linewidth=1, facecolors='none');
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)
    plt.show()
    plt.clf()

plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plot_svc_decision_function(model);
print(model.support_vectors_) # points circled in the graph

#compare the model learned from first 60 and first 120 points
def plot_svm(N=10, ax=None):
    X, y = make_blobs(n_samples=200, centers=2,
                      random_state=0, cluster_std=0.60)
    X = X[:N]
    y = y[:N]
    model = SVC(kernel='linear', C=1E10)
    model.fit(X, y)

    ax = ax or plt.gca()
    ax.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    ax.set_xlim(-1, 4)
    ax.set_ylim(-1, 6)
    plot_svc_decision_function(model, ax)

fig, ax = plt.subplots(1, 2, figsize=(16, 6))
fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
for axi, N in zip(ax, [60, 120]):
    plot_svm(N, axi)
    axi.set_title('N = {0}'.format(N))

#the extra data points dont change the model
#since none cross the support vecotrs margins

###Beyond linear boundaries: kernel SVM
#some data thats not linearly seperatable

from sklearn.datasets.samples_generator import make_circles
X, y = make_circles(100, factor=.1, noise=.1)

clf = SVC(kernel='linear').fit(X, y)

plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plot_svc_decision_function(clf, plot_support=False);

#radial basis function on the center clump
r = np.exp(-(X ** 2).sum(1))

from mpl_toolkits import mplot3d

def plot_3D(elev=30, azim=30, X=X, y=y):
    ax = plt.subplot(projection='3d')
    ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
    ax.view_init(elev=elev, azim=azim)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('r')
    plt.show()
    plt.clf()

plot_3D()

# the radial bias fuctions centering made this possible
#we need a way to automate this so its correct
#we can use the kernel trick

clf = SVC(kernel='rbf', C=1E6) #change linear to rbf for this
clf.fit(X, y)

plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plot_svc_decision_function(clf)
plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
            s=300, lw=1, facecolors='none');
plt.show()
plt.clf()

#Tuning the SVM:Softening margins
X, y = make_blobs(n_samples=100, centers=2,
                  random_state=0, cluster_std=1.2)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn');

#Example: Face Recognition
from sklearn.datasets import fetch_lfw_people
faces = fetch_lfw_people(min_faces_per_person=60)
print(faces.target_names)
print(faces.images.shape)

fig, ax = plt.subplots(3, 5)
for i, axi in enumerate(ax.flat):
    axi.imshow(faces.images[i], cmap='bone')
    axi.set(xticks=[], yticks=[],
            xlabel=faces.target_names[faces.target[i]])

plt.show()
plt.clf()

from sklearn.svm import SVC
from sklearn.decomposition import PCA as RandomizedPCA
from sklearn.pipeline import make_pipeline
#going to extract 150 features instead of using raw pixel data
pca = RandomizedPCA(n_components=150, whiten=True, random_state=42)
svc = SVC(kernel='rbf', class_weight='balanced')
model = make_pipeline(pca, svc)

from sklearn.model_selection import train_test_split
Xtrain, Xtest, ytrain, ytest = train_test_split(faces.data, faces.target,
                                                random_state=42)

from sklearn.model_selection import GridSearchCV
param_grid = {'svc__C': [1, 5, 10, 50],
              'svc__gamma': [0.0001, 0.0005, 0.001, 0.005]}
grid = GridSearchCV(model, param_grid)
grid.fit(Xtrain, ytrain)
print(grid.best_params_)

model = grid.best_estimator_
yfit = model.predict(Xtest)
fig, ax = plt.subplots(4, 6)
for i, axi in enumerate(ax.flat):
    axi.imshow(Xtest[i].reshape(62, 47), cmap='bone')
    axi.set(xticks=[], yticks=[])
    axi.set_ylabel(faces.target_names[yfit[i]].split()[-1],
                   color='black' if yfit[i] == ytest[i] else 'red')
fig.suptitle('Predicted Names; Incorrect Labels in Red', size=14);
plt.show()
plt.clf()

#we can better assert our estimators performance using
#a classification report

from sklearn.metrics import classification_report
print(classification_report(ytest, yfit,
                            target_names=faces.target_names))

#we can also check th econfusion matrix

from sklearn.metrics import confusion_matrix
mat = confusion_matrix(ytest, yfit)
sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False,
            xticklabels=faces.target_names,
            yticklabels=faces.target_names)
plt.xlabel('true label')
plt.ylabel('predicted label');
plt.show()
plt.clf()


import numpy as np

def delayed_adstock(x, L, P, D):
    '''
    params:
    x: original media variable, array
    L: length
    P: peak, delay in effect
    D: decay, retain rate
    returns:
    array, adstocked media variable
    '''
    # https://github.com/sibylhe/mmm_stan#12-adstock
    # prepend x with zeros equal to the length -1
    x = np.append(np.zeros(L-1), x)

    # create an array of zeros equal to the length
    weights = np.zeros(L)

    # loop through each day in length
    for l in range(L):
        # weight is decay to the power of index - peak squared
        weight = D**((l-P)**2)
        # add weight to weights in the right place (from back to front)
        weights[L-1-l] = weight

    # create an empty list
    adstocked_x = []
    # loop through length - 1 up to len(x)
    for i in range(L-1, len(x)):
        # get array of x from index - length + 1 to index + 1
        x_array = x[i-L+1:i+1]
        # sum the x_array * weights / sum(weights) to get adstock value
        xi = sum(x_array * weights)/sum(weights)
        # append adstocked value to adstocked_x
        adstocked_x.append(xi)

    # convert adstocked_x into an np.array
    adstocked_x = np.array(adstocked_x)

    # return adstocked_x
    return adstocked_x