import numpy as _np
import matplotlib.pyplot as _plt
from johnspythonlibrary2.Process.Misc import findNearest
import xarray as _xr

# from scipy.signal import gaussian as _gaussian

#%% Basic signal generation

def gaussian_pulse(sigma, t, amplitude=1, plot=False):
	"""
	Basic gaussian pulse with units in time
	std_time is the standard deviation of the pulse with units of t

	Example
	-------

	Example 1::

		dt=1e-9
		t=np.arange(0,0.001+dt/2,dt)
		t-=t.mean()
		std_time=1e-4
		gaussian_pulse(std_time, t=t, plot=True)

	References
	----------
	  * http://www.cse.psu.edu/~rtc12/CSE486/lecture11_6pp.pdf
	"""
	out= _xr.DataArray( _np.exp(-t**2 / (2 * sigma**2)), dims='t', coords=[t])
	if plot == True:
		out.plot()
	return out

def gaussian_1st_deriv(sigma, t, amplitude=1, plot=False):
	"""
	Basic gaussian pulse with units in time
	std_time is the standard deviation of the pulse with units of t

	Example
	-------

	Example 1::

		dt=1e-9
		t=np.arange(0,0.001+dt/2,dt)
		t-=t.mean()
		std_time=1e-4
		s=gaussian_1st_deriv(sigma = std_time, t=t, plot=True)

	References
	----------
	  * http://www.cse.psu.edu/~rtc12/CSE486/lecture11_6pp.pdf
	"""
	# dt=t[1]-t[0]
	out= _xr.DataArray( -t / (2 * sigma**2) * _np.exp(-t**2 / (2 * sigma**2)) * amplitude * sigma * 3.298, dims='t', coords=[t])
	if plot == True:
		fig,ax=_plt.subplots()
		out.plot(ax=ax)
	return out

def squareWave(	freq,
				time,
				duty_cycle=0.5,
				plot=False):
	"""

	Examples
	--------
	Example 1::

		freq=1e2
		dt=1e-5
		time=np.arange(10000)*dt

		fig,ax=plt.subplots()
		ax.plot(	squareWave(freq,time))
	"""
	from scipy import signal as sg

	y = _xr.DataArray(	sg.square(2 * _np.pi * freq * time, duty=duty_cycle),
						dims=['t'],
						coords={'t':time})

	# optional plot
	if plot==True:
		fig,ax=_plt.subplots()
		ax.plot(y)

	return y

def chirp(	t,
			tStartStop=[0, 1],
			fStartStop=[1e3, 1e4],
			phi=270,
			plot=False,
			method='logarithmic'):
	"""
	Creates an exponential (also called a "logarithmic") chirp waveform

	Wrapper for scipy function.

	Example
	-------
	::

		import numpy as np
		t=np.arange(0,.005,6e-6);
		f0=1e3
		f1=2e4
		t0=1e-3
		t1=4e-3
		y=chirp(t,[t0,t1],[f0,f1],plot=True)

	"""
	from scipy.signal import chirp

	# find range of times to modify
	iStart=findNearest(t, tStartStop[0])
	iStop=findNearest(t, tStartStop[1])

	# create chirp signal using scipy function.  times are (temporarily) shifted such that the phase offset, phi, still makes sense
	y=_np.zeros(len(t))
	y[iStart:iStop] = chirp(t[iStart:iStop] - tStartStop[0], fStartStop[0], tStartStop[1] - tStartStop[0], fStartStop[1], method, phi=phi)
	y= _xr.DataArray(	y,
						dims=['t'],
						coords={'t':t})

	# optional plot
	if plot==True:
		fig, ax = _plt.subplots()
		y.plot(ax=ax)

	return y

def gaussianNoise(t, mean=0, stdDev=1, plot=False):
	"""
	Produces Gaussian noise based on the standard deviation
	This is a wrapper for the numpy.random.normal function

	Parameters
	----------
	shape : tuple of ints
		Shape of desired noise; such as generated from np.shape()
	mean : float
		Offset value. The default is 0.
	stdDev : float
		"Amplitude" (standard deviation) of the noise. The default is 1.
	plot : bool, optional
		Optional plot of results

	Returns
	-------
	noise : numpy array
		Array containing the generated noise.

	Examples
	--------

	Example 1::

		t=np.arange(0,10e-3,2e-6)
		y1=np.sin(2*np.pi*t*1e3)
		y2=y1+gaussianNoise(t,mean=0,stdDev=0.5)
		fig,ax=plt.subplots()
		ax.plot(t,y2,label='signal with noise')
		ax.plot(t,y1,label='signal without noise')
		ax.legend()

	"""

	noise = _xr.DataArray(	_np.random.normal(mean, stdDev, t.shape),
							dims=['t'],
							coords={'t': t})

	if plot==True:
		fig, ax = _plt.subplots()
		ax.plot(noise)

	return noise

#%% More complicated functions

def tentMap(N=1000, plot=False, ICs={'x0': _np.sqrt(2) / 2.0}):
	"""
	generate fake data using a tentmap

	x=tentMap(1000,plot=True)

	References
	----------
	* https://en.wikipedia.org/wiki/Tent_map
	"""
	t = _np.arange(0, N+1)
	x = _np.zeros(t.shape, dtype=_np.float64)
	x[0] = ICs['x0']
	def tentMap(x, mu=_np.float64(2 - 1e-15)):
		for i in range(1, x.shape[0]):
			if x[i-1] < 0.5:
				x[i] = mu * x[i - 1]
			elif x[i - 1] >= 0.5:
				x[i] = mu * (1 - x[i - 1])
			else:
				raise Exception('error')
		return x

	x = tentMap(x)

	# the actual data of interest is the first difference of x
	xDelta=_xr.DataArray(	x[1:] - x[0: -1],
							dims=['t'],
							coords={'t': t[1:]},
							attrs={'units': "au",
								 'standard_name': 'Amplitude'})
	xDelta.t.attrs = {'units': 'au'}

	if plot is True:
		fig, ax = _plt.subplots(2, sharex=True)
		ax[0].plot(t, x, linestyle='-', marker='.', linewidth=0.5, markersize=3, label='x')
		xDelta.plot(ax=ax[1], linestyle='-', marker='.', linewidth=0.5, markersize=3, label='x derivative')
		ax[0].legend()
		ax[1].legend()

	return xDelta

def saved_lorentzAttractor(	N=2000,
							dt=0.05,
							ICs={	'x0': -9.38131377,
									'y0': -8.42655716 ,
									'z0': 29.30738524},
							plot=False,
							removeMean=False,
							normalize=False,
							removeFirstNPoints=500,
							args={	'sigma': 10.0,
									'b': 8.0 / 3.0,
									'r': 28.0}):

	filename='lorentz_attractor_N_%d_dt_%.6f_x0_%.3f_y0_%.3f_z0_%.3f.NetCDF' % (N, dt, ICs['x0'], ICs['y0'], ICs['z0'])

	try:
		ds = _xr.open_dataset(filename)
		print('loaded previously generated dataset')
	except:
		print('creating dataset')
		ds = lorentzAttractor(	N=N,
								dt=dt,
								ICs=ICs,
								plot=plot,
								removeMean=removeMean,
								normalize=normalize,
								removeFirstNPoints=removeFirstNPoints,
								args=args)

		ds.to_netcdf(filename)
		ds = _xr.open_dataset(filename)

	return ds

def lorentz96(N=5, F=8, t_final=30.0, dt=0.01, plot=False):
	"""

	References
	----------
	* https://en.wikipedia.org/wiki/Lorenz_96_model
	* 'a common benchmark for spatiotemporal filtering methods due to its chaotic dynamics and moderate dimensionality' : https://journals.aps.org/prx/abstract/10.1103/PhysRevX.6.011021

	Examples
	--------

	Example 1::

		x = lorentz96(plot=True)

	Example 2::

		x = lorentz96(40, t_final=50, plot=True)
	"""

	if N < 4:
		raise Exception("N must be >= 4")

	# parameters

	# library
	from scipy.integrate import odeint

	# subfunction
	def L96(x, t):
		"""Lorenz 96 model with constant forcing"""
		# Setting up vector
		d = _np.zeros(N)
		# Loops over indices (with operations and Python underflow indexing handling edge cases)
		for i in range(N):
			d[i] = (x[(i + 1) % N] - x[i - 2]) * x[i - 1] - x[i] + F
		return d

	# setup time
	t = _np.arange(0.0, float(t_final), dt)

	# setup ICs
	x0 = F * _np.ones(N)  # Initial state (equilibrium)
	x0[0] += 0.01  # Add small perturbation to the first variable

	# solve
	x = odeint(L96, x0, t)

	# finalize
	x = _xr.DataArray(x.transpose(), dims=['n', 't'], coords=[_np.arange(0, N) + 1, t])
	x.t.attrs = {'long_name': 'Time', 'units': 'au'}

	if plot is True:
		fig = _plt.figure()
		ax = fig.gca(projection="3d")
		ax.plot(x.sel(n=1), x.sel(n=2), x.sel(n=3))
		ax.set_xlabel("$x_1$")
		ax.set_ylabel("$x_2$")
		ax.set_zlabel("$x_3$")

		fig, ax = _plt.subplots()
		x.plot(ax=ax)

	return x

def lorentzAttractor(	N=2000,
						dt=0.05,
						ICs={	'x0': -9.38131377,
								'y0': -8.42655716 ,
								'z0': 29.30738524},
						plot=False,
						removeMean=False,
						normalize=False,
						removeFirstNPoints=500,
						args={	'sigma': 10.0,
								'b': 8.0 / 3.0,
								'r': 28.0}):
	"""
	Solves the lorentz attractor nonlinear ODEs.

	References
	----------
	 * https://en.wikipedia.org/wiki/Lorenz_system
	 * https://www.diasporist.org/jeroen/assets/lecture%20-%20A%20brief%20introduction%20to%20the%20Lorenz%2063%20system.pdf

	Examples
	--------
	Example 1::

		_plt.close('all')
		x,y,z=solveLorentz( 	N=5000,
						dt=0.02,
						IC=[-9.38131377, -8.42655716 , 29.30738524],
						plot='all',
						removeMean=True,
						normalize=True,
						removeFirstNPoints=500)

	"""

	from scipy.integrate import solve_ivp

	## initialize
	N += removeFirstNPoints
	T = N * dt
	ICs = _np.array(list(ICs.items()))[:, 1].astype(float)

	## Solve Lorentz system of equations
	def ODEs(t, y, *args):
		X, Y, Z = y
		sigma, b, r = args
		derivs =	[	sigma * (Y - X),
						-X * Z + r * X - Y,
						X * Y - b * Z]
		return derivs

	t_eval = _np.arange(0, T, dt)
	psoln = solve_ivp(	ODEs,
						[0, T],
						ICs,  # initial conditions
						args=args.values(),
						t_eval=t_eval
						)
	x, y, z = psoln.y

	## Cleanup data
	x = _xr.DataArray(	x,
						dims=['t'],
						coords={'t': t_eval},
						attrs={	'units': "au",
								'long_name': 'Amplitude'})
	x.t.attrs = {'units': 'au', 'long_name': 'Time'}
	y = _xr.DataArray(	y,
						dims=['t'],
						coords={'t': t_eval},
						attrs={	'units': "au",
								'long_name': 'Amplitude'})
	y.t.attrs = {'units': 'au', 'long_name': 'Time'}
	z=_xr.DataArray(	z,
						dims=['t'],
						coords={'t': t_eval},
						attrs={	'units': "au",
								'long_name': 'Amplitude'})
	z.t.attrs = {'units': 'au', 'long_name': 'Time'}

	if removeFirstNPoints > 0:
		x = x[removeFirstNPoints:]
		y = y[removeFirstNPoints:]
		z = z[removeFirstNPoints:]

	if removeMean is True:
		x -= x.mean()
		y -= y.mean()
		z -= z.mean()

	if normalize is True:
		x /= x.std()
		y /= y.std()
		z /= z.std()

	ds=_xr.Dataset({	'x':x,
						'y':y,
						'z':z})

	## optional plots
	if plot is not False:
		fig, ax = _plt.subplots(3, sharex=True)
		markersize = 2
		x.plot(ax=ax[0], marker='.', markersize=markersize)
		y.plot(ax=ax[1], marker='.', markersize=markersize)
		z.plot(ax=ax[2], marker='.', markersize=markersize)
		ax[0].set_title('Lorentz Attractor\n' + r'($\sigma$, b, r)=' + '(%.3f, %.3f, %.3f)' % (args['sigma'], args['b'], args['r']) + '\nICs = (%.3f, %.3f, %.3f)' % (ICs[0], ICs[1], ICs[2]))

	if plot == 'all':
		_plt.figure(); _plt.plot(x, y)
		_plt.figure(); _plt.plot(y, z)
		_plt.figure(); _plt.plot(z, x)

		import matplotlib as _mpl
		_mpl.rcParams.update({'figure.autolayout': False})
		fig = _plt.figure()
		ax = fig.add_subplot(121, projection='3d')
		ax.plot(x, y, zs=z)
		ax.set_xlabel('x')
		ax.set_ylabel('y')
		ax.set_zlabel('z')
		ax.set_title('Lorentz Attractor\n' + r'($\sigma$, b, r)=' + '(%.3f, %.3f, %.3f)' % (args['sigma'], args['b'], args['r']) + '\nICs = (%.3f, %.3f, %.3f)' % (ICs[0], ICs[1], ICs[2]) + '\nState space')

		ax = fig.add_subplot(122, projection='3d')
		ax.plot(x[6::3], x[3:-3:3], zs=x[:-6:3])
		ax.set_xlabel('x(t)')
		ax.set_ylabel(r'x(t-$\tau$)')
		ax.set_zlabel(r'x(t-2$\tau$)')
		ax.set_title('Lorentz Attractor\n' + r'($\sigma$, b, r)=' + '(%.3f, %.3f, %.3f)' % (args['sigma'], args['b'], args['r']) + '\nICs = (%.3f, %.3f, %.3f)' % (ICs[0], ICs[1], ICs[2]) + '\nTime-lagged state space')
		fig.tight_layout(pad=5, w_pad=5)

	return ds

def predatorPrey(	N=100000,
					T=100,
					ICs={	'x0': 0.9,
							'y0': 0.9},
					args={	'alpha': 2.0 / 3.0,
							'beta': 4.0 / 3.0,
							'delta': 1.0,
							'gamma': 1.0},
					plot=False,
					removeMean=False,
					normalize=False):
	"""
	Lotka–Volterra (classic predator-prey problem) equations

	References
	----------
	* https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations

	Examples
	--------
	Example 1::
		predatorPrey(plot=True)
	"""

	from scipy.integrate import solve_ivp

	## initialize
	dt=T/N

	## Solve Lorentz system of equations
	def ODEs(t, y, *args):
		X,Y = y
		alpha, beta, delta, gamma = args
		#print(X,Y,alpha,beta,delta,gamma)
		derivs=	[	alpha * X - beta * X * Y,
					delta * X * Y - gamma * Y]
		return derivs

	t_eval = _np.arange(0, T, dt)
	psoln = solve_ivp(	ODEs,
						[0, T],
						_np.array(list(ICs.items()))[:, 1].astype(float),  # initial conditions
						args=args.values(),
						t_eval=t_eval
						)
	x, y = psoln.y

	## Cleanup data
	x = _xr.DataArray(	x,
						dims=['time'],
						coords={'time': t_eval},
						attrs={'units': "au",
								 'standard_name': 'Amplitude'})
	x.time.attrs = {'units': 'au'}
	y = _xr.DataArray(	y,
						dims=['time'],
						coords={'time': t_eval},
						attrs={'units': "au",
								 'standard_name': 'Amplitude'})
	y.time.attrs = {'units': 'au'}

	if removeMean is True:
		x -= x.mean()
		y -= y.mean()

	if normalize is True:
		x /= x.std()
		y /= y.std()

	ds = _xr.Dataset({	'x': x,
						'y': y})

	## Optional plots
	if plot is True:
		fig, ax = _plt.subplots(2, sharex=True)
		ds.x.plot(ax=ax[0])
		ds.y.plot(ax=ax[1])

		fig, ax = _plt.subplots()
		ax.plot(ds.x.values, ds.y.values)

	return ds

def coupledHarmonicOscillator(	N=10000,
								T=1,
								ICs={	'y1_0': 0,
										'x1_0': 0.9,
										'y2_0': 0,
										'x2_0': -1},
								args={	'k': 1,
										'kappa': 1,
										'm': 1e-4},
								plot=False):
	"""
	Solve a coupled harmonic oscillator problem. See reference for details.

	Examples
	--------
	Example1 ::
		x1,x2=coupledHarmonicOscillator(plot=True,N=1e5,T=10)

	References
	----------
	http://users.physics.harvard.edu/~schwartz/15cFiles/Lecture3-Coupled-Oscillators.pdf

	"""

	import matplotlib.pyplot as _plt

	dt  = T / N

	from scipy.integrate import solve_ivp

	def ODEs(t, y, *args):
		y1, x1, y2, x2 = y
		k, kappa, m = args
		derivs = [	-(k + kappa) / m * x1 + kappa / m * x2,
					y1,
 					-(k + kappa) / m * x2 + kappa / m * x1,
					y2]
		return derivs

	time = _np.arange(0, T, dt)
	psoln = solve_ivp(	ODEs,
						t_span=[0, T],
						y0=_np.array(list(ICs.values())),  # initial conditions
						args=args.values(),
						t_eval=time
					)

	y1, x1, y2, x2 = psoln.y

	x1=_xr.DataArray(	x1,
						dims=['time'],
						coords={'time': time},
						attrs={'units': "au",
								 'standard_name': 'Amplitude'})
	x1.time.attrs = {'units': 'au'}
	x2=_xr.DataArray(	x2,
						dims=['time'],
						coords={'time': time},
						attrs={'units': "au",
								 'standard_name': 'Amplitude'})
	x2.time.attrs={'units': 's'}

	if plot==True:
		fig, ax = _plt.subplots(2, sharex=True)
		x1.plot(ax=ax[0], marker='.')
		x2.plot(ax=ax[1], marker='.')
		ax[0].set_title('Coupled harmonic oscillator\n' + r'(k, kappa, m)='+'(%.3f, %.3f, %.6f)' % (args['k'], args['kappa'], args['m']) + '\nICs = (%.3f, %.3f, %.3f, %.3f)' % (ICs['y1_0'], ICs['x1_0'], ICs['y2_0'], ICs['x2_0']))

	ds=_xr.Dataset(	{'x1': x1,
					 'x2': x2})

	return ds

def coupledHarmonicOscillator_nonlinear(	N=500,
											dt=0.1,
											ICs={	'y1_0': 0,
													'x1_0': 1,
													'y2_0': 0,
													'x2_0': 0},
											args={	'f1': 45,
													'f2': 150,
													'm': 1,
													'E': 1e5},
											plot=False,
											verbose=False):
	"""
	Solve a coupled harmonic oscillator problem. See reference for details.

	Examples
	--------
	Example1 ::
		x1,x2=coupledHarmonicOscillator_nonlinear(N=10000,dt=1e-3,plot=True,verbose=True).values()
		nperseg=2000
		from johnspythonlibrary2.Process.Spectral import fft_average
		fft_average(x1,plot=True,nperseg=nperseg,noverlap=nperseg//2)

	References
	----------
	https://arxiv.org/pdf/1811.02973.pdf

	"""

	f1, f2, m, E = args.values()
	k1 = (2 * _np.pi * f1)**2 * m
	k2 = ((2 * _np.pi * f2)**2 * m - k1) / 2

	import matplotlib.pyplot as _plt

	from scipy.integrate import solve_ivp

	def ODEs(t, y, *args):
		y1, x1, y2, x2 = y
		k1, k2, m1, m2, E = args
		if verbose:
			print(-k1 * x1 / m1, +k2 * (x2 - x1) / m1, E * x1**2 / m1)
		derivs=	[	-k1 * x1 / m1 + k2 * (x2 - x1) / m1 + E * x1**2 / m1,
					y1,
 					-k1 * x2 / m2 - k2 * (x2 - x1) / m2,
					y2]
		return derivs

	time = _np.arange(0, N) * dt
	T = time[-1]
	psoln = solve_ivp(	ODEs,
						t_span=[0, T],
						y0=_np.array(list(ICs.values())),  # initial conditions
						args=(k1, k2, m, m, E),
						t_eval=time
					)

	y1, x1, y2, x2 = psoln.y

	x1=_xr.DataArray(	x1,
						dims=['t'],
						coords={'t': time},
						attrs={'units': "au",
								 'standard_name': 'Amplitude'})
	x1.t.attrs = {'units': 'au'}
	x2 = _xr.DataArray(	x2,
						dims=['t'],
						coords={'t': time},
						attrs={'units': "au",
								 'standard_name': 'Amplitude'})
	x2.t.attrs = {'units': 's'}

	if plot is True:
		fig, ax = _plt.subplots(2, sharex=True)
		x1.plot(ax=ax[0], marker='.')
		x2.plot(ax=ax[1], marker='.')
		ax[0].set_title('Coupled harmonic oscillator\n' + r'(f1, f2, m, E)=' + '(%.3f, %.3f, %.3f,%.3f)' % (f1, f2, m, E) + '\nICs = (%.3f, %.3f, %.3f, %.3f)' % (ICs['y1_0'], ICs['x1_0'], ICs['y2_0'], ICs['x2_0']))

	ds=_xr.Dataset(	{'x1': x1,
					 'x2': x2})

	return ds


import sympy as sym
from approx1D import least_squares
import scipy
import scipy.integrate

def sin_series(N):
  psi = []
  for k in range(1,N):
    psi_k = sym.sin(sym.pi*k*x)
    psi.append(psi_k)
  return psi

def taylor_series(N):
  psi = []
  for k in range(1,N):
    psi_k = x**k
    psi.append(psi_k)
  return psi

def series(series_type, N):
  if series_type=="Taylor" : return taylor_series(N)
  elif series_type=="sin"  : return sin_series(N)
  else: print("series type unknown ") # sys.exit(0)

X = scipy.arange(0, 1, 0.0001)
import pylab
def plot_solution(series_type, N, func):

  print("series_type ", series_type, " N ", N, " func ", func)

  psi = series(series_type, N)
  print(psi)
  u, c = least_squares(func, psi, Omega, False)

  u = sym.lambdify([x], u)
  U = [u(xi) for xi in X]
  pylab.plot(X, U)

def plot_u(u):

  u = sym.lambdify([x], u)
  U = [u(xi) for xi in X]
  pylab.plot(X, U)

Omega = [0, 1]
x = sym.Symbol("x")
gauss_bell = sym.exp(-(x-0.5)**2) - sym.exp(-0.5**2)
#step = sym.Piecewise( (1, 0.25 < x and x < 0.75), (0, True)  )
step = sym.Piecewise( (1, 0.25 <= x), (0, True)  )- sym.Piecewise( (1, 0.75 < x), (0, True)  )
hat = sym.Piecewise( (x, x <= 0.5), (0, True)  ) + sym.Piecewise( (1-x, 0.5 < x), (0, True)  )

print("hat (0.3) ", hat.subs(x, 0.3))

func=hat

Ns =[5, 10, 15, 20, 25]
#Ns =[2, 3, 4, 5]
for N in Ns:
#  plot_solution("sin", N, func)
  plot_solution("Taylor", N, func)
legend = ["N=%d" % N for N in Ns]
plot_u(func)
legend.append("analytical solution")
pylab.legend(legend)
pylab.show()
pylab.savefig("step.png", title="sin approximation")


from numpy import tanh,sinh,cosh,pi,abs

class DoubleExp:
    def g(self,t):
        return tanh( pi/2. * sinh(t)  )
    def dgdt(self,t):
        return pi/2. *cosh(t)/cosh( pi/2. * sinh(t)  )**2.

    def F(self,t):
        #this will be used to determine the error
        return self.func( self.g(t) )*self.dgdt(t)

    def d2Fdt(self,t,_h=1e-8):
        '''
        This will give the second derivatives we need for the error estimation.
        For the moment take derivatives numerically.
        Later I will do the derivatives of g analytically, but for the moment should be fine.
        '''
        return (self.F(t+_h )- 2 * self.F(t ) +  self.F(t -_h ))/(_h**2.)

    def __init__(self,func,_exp=1,_exp_max=15,rtol=1e-5,atol=1e-5,p=10):
        '''
        func: function to be integrated in the interval [-1,1].
        exp: initial value of h=2^-exp
        exp_min: the minimum exp, with hmin= 2^{-exp_max}
        p: precision.

        Nmax=maximum number of evaluations

        Note that x_{-j}=-x_j and w_{-j}=-w_j .
        '''
        self.func=func

        self._exp=_exp
        self._exp_max=_exp_max

        self.h=2**-_exp
        self.hmin=2**-_exp_max

        self.rtol=rtol
        self.atol=atol
        self.eps=10**(-p)

        #initialize N. Start with N=0.
        self.N=0
        self.N_init=False

        #eval will tell us if we have already evaluated the integral for given N and h (no need to sum thingswe already have)
        self.eval=True
        self.h_stop=False

        #initialize the integral and the error.
        #As you update h and N, you need to add to the sum only new values produced
        #Also, since h changes, multipy by h at the end of the evaluation.
        self.integral=self.func( self.g(0) ) *self.dgdt(0)
        self.err=self.d2Fdt(0)

    def N_start(self):
        '''
        Find an appropriate N to start.
        As you update h, just update N->N*2 (later we may use something better)
        '''

        #start from this.
        tmp_N=self.N+1
        while True:
            #remember that x_j=-x_{-j}, w_j = w_{-j}
            _x=self.g(self.h*tmp_N)
            _w=self.dgdt(self.h*tmp_N)
            _f1=_w*self.func(_x)
            _f2= _w*self.func(-_x)

            #Note that we want N to start as N>0. This way we make sure that N gets updated correctly
            #(if N starts at 0, it's not going to be updated).
            if abs(_f1)<self.eps and abs(_f2 )<self.eps and self.N>1:
                self.N_init=True
                self.eval=False
                break
            else:

                self.integral+=_f1+_f2
                self.err+=self.d2Fdt( tmp_N*self.h)

                self.N=tmp_N
                tmp_N+=1

    def evaluate(self):
        '''
        Evaluate the integral for given h and N.
        Also evaluate the error.

        Note for later: since we update h->h/2, we just need to update the sum including only the new
        addition we make. That is, you only calculate for odd j!
        '''
        j=1
        while self.eval:
            _x=self.g(self.h*j)
            _w=self.dgdt(self.h*j)

            self.integral+=_w*(self.func(_x) + self.func(-_x))
            self.err+=self.d2Fdt( j*self.h)+self.d2Fdt( -j*self.h)
            j+=2
            if j>self.N-2:
                self.eval=False
                break

    def h_control(self):
        '''
        Determines if  the error is acceptable. If not, decrese h until it is (or hmin is found).
        '''
        abs_err=abs(self.err*self.h*(self.h/(2*pi))**2.)

        _sc=self.atol + self.rtol*abs(self.integral)

        if abs_err/_sc <1 :
            self.h_stop=True
        else:
            if self.h<self.hmin:
                self.h_stop=True
            else:
                self.h=self.h/2
                self.N=self.N*2
                self.eval=True

    def integrate(self):
        if self.N_init==False:
            self.N_start()

        while self.h_stop==False:
            self.h_control()
            self.evaluate()

        self.eval=False
        return (self.integral*self.h , abs(self.err*self.h*(self.h/(2*pi))**2.)  )

if __name__ == "__main__":
    def F(x):
        return 1/((1+x)**0.5 +(1-x)**0.5 +2  )

    DE=DoubleExp(func=F,_exp=10,_exp_max=50,p=20,rtol=1e-10,atol=1e-10)

    print(
        'result: {0[0]} \nerror: {0[1]} \nexact: {1}'.format(
        DE.integrate(),4*2**0.5-pi-2)
        )


import numpy as np
from matplotlib import pyplot as plt

def amsignal(am, ap, fm, fp, mostraSpettro):
    '''
    Calcola il segnale modulato e ne disegna il grafico
    :param am: Ampiezza modulante in V
    :param ap: Ampiezza portante in V
    :param fm: Frequenza modulante in Hz
    :param fp: Frequenza portante in Hz
    :param mostraSpettro: booleano per dire se mostrare lo spettro o no
    :return:
    '''
    t = np.arange(0, 500, 0.01)
    fpMHz = fp/1e6
    fmMHz = fm/1e6
    vpt = ap * np.cos(2 * np.pi * fpMHz * t)
    vmt = am * np.cos(2 * np.pi * fmMHz * t)
    vit = [x + ap for x in vmt]
    vam = (1 / ap) * vpt * vit
    if not mostraSpettro:
        plt.plot(t, vam, color="blue", label="Segnale modulato")
        plt.plot(t, vit, color="orange", label="Segnale inviluppo")
        plt.xlabel("Tempo [us]")
        plt.ylabel("Ampiezza [V]")
        plt.grid()
        plt.legend()
        plt.show()

    else:
        spettro(am, ap, fm, fp)

def spettro(am, ap, fm, fp):
    '''
    Disegna il grafico dello spettro del segnale
    :param am: Ampiezza modulante in V
    :param ap: Ampiezza portante in V
    :param fm: Frequenza modulante in Hz
    :param fp: Frequenza portante in Hz
    '''
    fm = fm/1e3
    fp = fp/1e3
    plt.xlim(fp - fm * 2, fp + fm * 2)
    plt.ylim(0, ap + am / 2)
    plt.xlabel("Frequenza [KHz]")
    plt.ylabel("Volt [V]")
    plt.plot([fp, fp], [0, ap], color="black")
    plt.text(fp - 1, ap + 0.7, "Ap=" + str(ap))
    plt.plot([fp - fm, fp - fm], [0, am / 2], color="black")
    plt.text((fp - fm) - 1, (am / 2) + 0.7, "Am/2=" + str(am / 2))
    plt.plot([fp + fm, fp + fm], [0, am / 2], color="black")
    plt.text((fp + fm) - 1, (am / 2) + 0.7, "Am/2=" + str(am / 2))
    plt.show()

def max(*args):
    max = 0
    for x in args:
        if x > max:
            max = x
    return max


import numpy as np
# import pandas as pd
# import matplotlib.pyplot as plt

from sal_timer import timer

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

@timer
def main():

    x = np.array([
        2,
        4,
        10,
        100,

        0,

        -0.1,
        -0.44,
        -10,
        -100
    ])

    z = sigmoid(x)
    print(z)

    x = 4
    z = sigmoid(x)
    print(z)

if __name__ == '__main__':
    print('========================================== START ==========================================')
    #...
    main()
    print('========================================== END ============================================')


#!/usr/bin/env python

""" main module """

import time
import numpy as np
from matplotlib import pyplot as plt
from eulers import *

def my_method(x_init, x_dot_func, step, time):
    """ euer method """
    # finding count
    count = int(time / step) + 1

    # initialization
    x = np.zeros(count)
    x_dot = np.zeros(count)
    time = np.zeros(count)
    x[0] = x_init
    x_dot[0] = x_dot_func(x[0], time[0])

    for i in range(count - 1):
        time[i + 1] = time[i] + step
        k = x_dot_func(x[i], time[i]) * step
        x_dot[i + 1] = x_dot_func(x[i] + k, time[i] + 0.5 * step)
        x[i + 1] = x[i] + (x_dot[i + 1] + x_dot[i]) * 0.5 * step
        x_dot[i + 1] = x_dot_func(x[i + 1], time[i + 1])

    return x, x_dot, time, count

def real(t):
    return 1 / 20.0 * (np.exp(-t) - np.exp(-21 * t))

def x_dot(x, t):
    """ x_dot function """
    return -21 * x + np.exp(-t)

def main():
    """ main body """
    x_init = 0
    end = 4
    steps = np.array([0.01, 0.02, 0.04, 0.06, 0.08, 0.1])
    runtime = np.zeros((2, len(steps)))

    ######## Part A #########
    x = [None] * len(steps)
    x_dots = [None] * len(steps)
    time0 = [None] * len(steps)
    count = [None] * len(steps)

    for i, step in enumerate(steps):
        start = time.time()
        x[i], x_dots[i], time0[i], count[i] = euler(x_init, x_dot, step, end)
        runtime[0, i] = time.time() - start

    for i in range(len(steps)):
        plt.plot(time0[i], x[i], '-', label=f"h = {steps[i]}")
        plt.xlabel("t")
        plt.ylabel("y")
        plt.grid()
        plt.legend()
        plt.title(f"h = {steps[i]}, euler method")
        plt.savefig(f"results/euler_{steps[i]}.jpg", dpi=200, bbox_inches='tight')
        plt.close()

    ######## Part B ###########
    x1 = [None] * len(steps)
    x_dots1 = [None] * len(steps)
    time1 = [None] * len(steps)
    count1 = [None] * len(steps)

    for i, step in enumerate(steps):
        start = time.time()
        x1[i], x_dots1[i], time1[i], count1[i] = backward_euler(x_init, x_dot, step, end)
        runtime[1, i] = time.time() - start

    for i in range(len(steps)):
        plt.plot(time1[i], x1[i], '-', label=f"h = {steps[i]}")
        plt.xlabel("t")
        plt.ylabel("y")
        plt.grid()
        plt.title(f"h = {steps[i]}, backward euler")
        plt.legend()
        plt.savefig(f"results/backward_euler_{steps[i]}.jpg", dpi=200, bbox_inches='tight')
        plt.close()

    ########### Part C #############
    print("===============Euler===========")
    for i in range(len(steps)):
        error = x[i][-1] - real(time0[i][-1])
        print(f"h = {steps[i]},\t error = {error},\t runtime = {runtime[0, i]}")

    print("============= Backward Euler==========")
    for i in range(len(steps)):
        error = x1[i][-1] - real(time1[i][-1])
        print(f"h = {steps[i]},\t error = {error},\t runtime = {runtime[1, i]}")

    ######### Part D ###########
    x = [None] * len(steps)
    x_dots = [None] * len(steps)
    time0 = [None] * len(steps)
    count = [None] * len(steps)

    for i, step in enumerate(steps):
        start = time.time()
        x[i], x_dots[i], time0[i], count[i] = my_method(x_init, x_dot, step, end)
        runtime[0, i] = time.time() - start

    for i in range(len(steps)):
        plt.plot(time0[i], x[i], '-', label=f"h = {steps[i]}")
    plt.legend()
    plt.show()

    print("===============Euler===========")
    for i in range(len(steps)):
        error = x[i][-1] - real(time0[i][-1])
        print(f"h = {steps[i]},\t error = {error},\t runtime = {runtime[0, i]}")

if __name__ == "__main__":
    main()


import cv2

import numpy as np
import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D

def surf(data):
    y = np.arange(0, data.shape[0])
    x = np.arange(0, data.shape[1])
    X, Y = np.meshgrid(x, y)

    fig = plt.figure()
    ax = fig.gca(projection='3d')

    ax.plot_surface(X, Y, data, rstride=1, cstride=1, linewidth=0,
                    cmap='jet', antialiased=False)

    plt.show(block=False)

def LoG(size, sigma):
    x = y = np.linspace(-size, size, 2*size+1)
    x, y = np.meshgrid(x, y)

    f = (x**2 + y**2)/(2*sigma**2)
    k = -1./(np.pi * sigma**4) * (1 - f) * np.exp(-f)

    return k

# Edge demo

# Read Lena image
lenaL = cv2.imread('images/lena.png')
cv2.imshow('Original image, color', lenaL)

# Convert to monochrome (grayscale) using BGR2GRAY.
lenaMono = cv2.cvtColor(lenaL, cv2.COLOR_BGR2GRAY)  # TODO: change this line with the call to cv2.cvtColor
cv2.imshow('Original image, monochrome', lenaMono)

# Make a blurred/smoothed version. Use cv2.getGaussianKernel to get the h kernel
h = cv2.getGaussianKernel(ksize=5, sigma=2.5)  # TODO: change this line with the call to cv2.getGaussianKernel
h = h @ h.T
print(h)

# Mimic Matlab's surf(h)
surf(h)

# Use cv2.filter2D with BORDER_CONSTANT to get results similar to the Matlab demo
lenaSmooth = cv2.filter2D(lenaMono, -1, h, borderType=cv2.BORDER_CONSTANT)  # TODO: use cv2.filter2D
cv2.imshow('Smoothed image', lenaSmooth)

# Method 1: Shift left and right, and show diff image
lenaL = np.copy(lenaSmooth)  # Let's use np.copy to avoid modifying the original array
# TODO: use numpy indexing to copy and paste the array to the right position
lenaL[:, :-1] = lenaL[:, 1:]
cv2.imshow('Lena L', lenaL)

lenaR = np.copy(lenaSmooth)  # Let's use np.copy to avoid modifying the original array
# TODO: use numpy indexing to copy and paste the array to the right position
lenaR[:, 1:] = lenaR[:, :-1]
cv2.imshow('Lena R', lenaR)

# TODO: Subtract lenaL from lenaR. Don't forget about using the correct data type
lenaDiff = lenaR.astype(float) - lenaL.astype(float)   # Change this line with the answer

# Here we shift the value range to fit [0, 255] and make sure the data type is uint8 in order to display the results.
lenaDiff = cv2.normalize(lenaDiff, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)
cv2.imshow('Difference between right and left shifted images', lenaDiff.astype(np.uint8))

# Method 2: Canny edge detector
# OpenCV doesn't have a function similar to edge but it does have a Canny Edge detector
# OpenCV needs you to specify low and high threshold values. While these are not the
# exactly the same as the ones used in the demo you should refer to the lines below
# as a reference on how cv2.Canny works
thresh1 = 110
thresh2 = 60

cannyEdges = cv2.Canny(lenaMono, thresh1, thresh2)  # TODO: use cv2.Canny with lenaMono and the thresholds defined above
cv2.imshow('Original edges', cannyEdges)

cannyEdges = cv2.Canny(lenaSmooth, thresh1, thresh2)  # TODO: use cv2.Canny with lenaSmooth and the thresholds defined above
cv2.imshow('Edges of smoothed image', cannyEdges)

# Method 3: Laplacian of Gaussian
h = LoG(4, 1.)
surf(h)

# Let's use cv2.filter2D with the new h
logEdges = cv2.filter2D(lenaMono, -1, h)  # TODO: use cv2.filter2D
logEdgesShow = cv2.normalize(logEdges, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)

cv2.imshow('Laplacian image before zero crossing', logEdgesShow.astype(np.uint8))

# OpenCV doesn't have a function edge like Matlab that implements a 'log' method. This would
# have to be implemented from scratch. This may take a little more time to implement this :).

cv2.waitKey(0)


#!/usr/bin/env python
# coding: utf-8
# vim: set et sw=4 ts=4 sts=4 fenc=utf-8
# Author: Yang Tong
# Created: 2016-07-24 10:15 SGT

#Application: This script applies cubic spline interpolation to fit lines.
#             The given x-y data should be put into a file in which there are just
#             two columns: the first one is x and the second y.
#             >>>Multiple lines can be fitted simultaneously if they have the same x grids.
#                And the x-y data for each line should be separated from each other by a empty line
#How to use: >>>python cubic_spline_interpolation.py
#History:
#       created at 2016-07-24 10:15 SGT
#       modified at 2016-9-29 15:08 SGT

from scipy import interpolate
import numpy as np
import matplotlib.pyplot as plt
import re

def read_x_y(filename):
    f = open(filename, 'r')

    x_points, y_points = [], []
    for line in f:
        x_y = re.findall("[-+0-9\.eE]+", line)
        if x_y:
            x, y = float(x_y[0]), float(x_y[1])
            if y >= 10000:
                continue
            if x not in x_points:
                x_points.append(x)
            ind = x_points.index(x)
	    if len(y_points) == ind:
	        y_points.append([])
	    if len(y_points) < ind:
	        raise Exception, "some mistake"
	    y_points[ind].append(y)
    f.close()

    assert len(x_points) == len(y_points), "The number of x data does not match that of y data. Please check!"
    length = len(y_points[0])
    for item in y_points:
        assert len(item) == length, "The provided data is not complete. Please Check!"
    y_ = []
    for ind in range(len(y_points[0])):
        y_.append([item[ind] for item in y_points])
    return (x_points, y_)

def multiple_lines_cubic_spline_interpolation(x, y_s, start, end, accuracy):
    y_ = []
    for y in y_s:
        x_new, y_new = cubic_spline_interpolation(x, y, start, end, accuracy)
        y_.append(y_new)
    return x_new, y_

def cubic_spline_interpolation(x, y, start, end, accuracy):
    tck = interpolate.splrep(x, y, s=0)

    x_new = np.arange(start, end, accuracy*0.1)
    y_new = interpolate.splev(x_new, tck, der=0)
    return x_new, y_new

def plot_and_save_fig(known_x_y, new_x_y, operation):
    '''Plot the polynomial or save the figure:
       code\toperation
       0\tquit
       1\tonly plot
       2\tonly save
       3\tplot & save'''

    if operation == '0':
        return 0

    if "y" in raw_input("Is the given data also included in the figure? (y or n): ").lower():
        for y in known_x_y[1]:
            plt.plot(known_x_y[0], y, 's') #, label="Given Data", markersize=6, linewidth=2)
    for y in new_x_y[1]:
        plt.plot(new_x_y[0], y, "r-") #, label="Cubic Spline", linewidth=2)
    plt.xlim([new_x_y[0][0]*1.1-0.1*new_x_y[0][-1], 1.10*new_x_y[0][-1]-0.1*new_x_y[0][0]])
    plt.legend(loc='best')
    plt.xlabel(raw_input("Enter the x title: "))
    plt.ylabel(raw_input("Enter the y title: "))
    plt.title(raw_input("Enter the title: "))
    if operation in ('2', '3'):
        plt.savefig("Cubic_Spline.png")
    if operation in ('1', '3'):
        plt.show()

def save_interpolated_data(x, y_s, filename):
    f = open(filename, "w")

    for y in y_s:
        for i, j in zip(x, y):
            f.write("%f\t%f\n" % (i, j))
        f.write("\n")
    f.close()

def cal_significant_figure(accuracy, figs=0):
    while accuracy < 1:
        figs += 1
        accuracy *= 10
    return figs

def input_data():
    print("\n>>>Cubic spline interpolation is employed to give an interpolating polynomial<<<\n")
    filename = raw_input("Please enter the filename where each line includes a known x-y pair: ")
    accuracy = float(raw_input("Please enter the accuracy (e.g.0.1, 0.01, 0.001, ...): accuracy="))
    sig_figs = cal_significant_figure(accuracy)
    return filename, accuracy, sig_figs

if __name__ == "__main__":

    filename, accuracy, sig_figs = input_data()
    x_points, y_points = read_x_y(filename)
    x, y = multiple_lines_cubic_spline_interpolation(x_points, y_points, x_points[0], x_points[-1], accuracy)
    if "y" in raw_input("\n>>>Print the min points for each interpolated line up to the accuary give above? (y or n): ").lower():
        output_format = ">>>For curve %d, E_min=%f at %." + str(sig_figs) + "f<<<"
        for ind, y_ in enumerate(y):
            print(output_format % (ind, y_.min(), x[list(y_).index(y_.min())]))

    if "y" in raw_input("\n>>>Save the cubic spline interpolated data? (y or n): ").lower():
        filename = raw_input("Enter the filename to store the interpolated data:")
        save_interpolated_data(x, y, filename)

    if "y" in raw_input("\n>>>Plot or save the figure? (y or n): ").lower():
        print(plot_and_save_fig.__doc__)
        code = raw_input("Enter the code: ").strip(" ")[0]
        plot_and_save_fig([x_points, y_points], [x, y], code)


# ------------- Machine Learning - Topic 7: K-means Clustering and Principal Component Analysis

#     featureNormalize
#     pca
#     drawLine
#     projectData
#     recoverData
#     displayData
#     kMeansInitCentroids
#     runkMeans
#     hsv
#     plotDataPoints

from scipy.io import loadmat
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import os, sys
sys.path.append(os.getcwd() + os.path.dirname('/ml/ex7/'))
from helpers import featureNormalize, pca, drawLine, displayData, projectData, \
    recoverData, kMeansInitCentroids, runkMeans, hsv, plotDataPoints

## ================== Part 1: Load Example Dataset  ===================
#  We start this exercise by using a small dataset that is easily to
#  visualize
#
print('Visualizing example dataset for PCA.\n');

#  The following command loads the dataset. You should now have the
#  variable X in your environment
mat = loadmat('ml/ex7/data/ex7data1.mat')
X = np.array(mat["X"])

# interactive graphs
plt.ion()

#  Visualize the example dataset
plt.close()

# kept the scatter() (vs. the plot()) version
#  because scatter() makes properly circular markers
# plt.plot(X[:, 0], X[:, 1], 'o', markersize=9, markeredgewidth=1, markeredgecolor='b', markerfacecolor='None')
plt.scatter(X[:,0], X[:,1], s=75, facecolors='none', edgecolors='b')
plt.axis([0.5, 6.5, 2, 8])
plt.gca().set_aspect('equal', adjustable='box')
plt.show(block=False)

input('Program paused. Press enter to continue.')

## =============== Part 2: Principal Component Analysis ===============
#  You should now implement PCA, a dimension reduction technique. You
#  should complete the code in pca.m
#
print('Running PCA on example dataset.\n');

#  Before running PCA, it is important to first normalize X
X_norm, mu, _ = featureNormalize(X)

#  Run PCA
U, S = pca(X_norm)

#  Compute mu, the mean of the each feature

#  Draw the eigenvectors centered at mean of data. These lines show the
#  directions of maximum variations in the dataset.
plt.hold(True)
drawLine(mu, mu + 1.5 * S[0,0] * U[:,0].T, c='k', linewidth=2)
drawLine(mu, mu + 1.5 * S[1,1] * U[:,1].T, c='k', linewidth=2)
plt.hold(False)

print('Top eigenvector: \n')
print(' U(:,1) = {:f} {:f} \n'.format(U[0,0], U[1,0]))
print('(you should expect to see -0.707107 -0.707107)')

input('Program paused. Press enter to continue.')

## =================== Part 3: Dimension Reduction ===================
#  You should now implement the projection step to map the data onto the
#  first k eigenvectors. The code will then plot the data in this reduced
#  dimensional space.  This will show you what the data looks like when
#  using only the corresponding eigenvectors to reconstruct it.
#
#  You should complete the code in projectData.m

print('Dimension reduction on example dataset.\n');

#  Plot the normalized dataset (returned from pca)
plt.close()
plt.scatter(X_norm[:,0], X_norm[:,1], s=75, facecolors='none', edgecolors='b')
plt.axis([-4, 3, -4, 3])
plt.gca().set_aspect('equal', adjustable='box')
plt.show(block=False)

#  Project the data onto K = 1 dimension
K = 1
Z = projectData(X_norm, U, K)
print('Projection of the first example: ', Z[0])
print('(this value should be about 1.481274)\n')

X_rec  = recoverData(Z, U, K)
print('Approximation of the first example: ', X_rec[0, 0], X_rec[0, 1])
print('(this value should be about  -1.047419 -1.047419)\n')

#  Draw lines connecting the projected points to the original points
plt.hold(True)
plt.scatter(X_rec[:, 0], X_rec[:, 1], s=75, facecolors='none', edgecolors='r')
for i in range(X_norm.shape[0]):
    drawLine(X_norm[i,:], X_rec[i,:], linestyle='--', color='k', linewidth=1)
plt.hold(False)

input('Program paused. Press enter to continue.')

## =============== Part 4: Loading and Visualizing Face Data =============
#  We start the exercise by first loading and visualizing the dataset.
#  The following code will load the dataset into your environment
#
print('Loading face dataset.\n');

#  Load Face dataset
mat = loadmat('ml/ex7/data/ex7faces.mat')
X = np.array(mat["X"])

#  Display the first 100 faces in the dataset
displayData(X[:100, :])

input('Program paused. Press enter to continue.')

## =========== Part 5: PCA on Face Data: Eigenfaces  ===================
#  Run PCA and visualize the eigenvectors which are in this case eigenfaces
#  We display the first 36 eigenfaces.
#
print('Running PCA on face dataset.\n(this mght take a minute or two ...)\n')

#  Before running PCA, it is important to first normalize X by subtracting
#  the mean value from each feature
X_norm, _, _ = featureNormalize(X)

#  Run PCA
U, S = pca(X_norm)

#  Visualize the top 36 eigenvectors found
displayData(U[:, :36].T)

input('Program paused. Press enter to continue.')

## ============= Part 6: Dimension Reduction for Faces =================
#  Project images to the eigen space using the top k eigenvectors
#  If you are applying a machine learning algorithm
print('Dimension reduction for face dataset.\n');

K = 100
Z = projectData(X_norm, U, K)

print('The projected data Z has a size of: ')
print('{:d} {:d}'.format(Z.shape[0], Z.shape[1]))

input('Program paused. Press enter to continue.')

## ==== Part 7: Visualization of Faces after PCA Dimension Reduction ====
#  Project images to the eigen space using the top K eigen vectors and
#  visualize only using those K dimensions
#  Compare to the original input, which is also displayed

print('Visualizing the projected (reduced dimension) faces.\n');

K = 100;
X_rec  = recoverData(Z, U, K)

# Display normalized data
plt.close()
plt.subplot(1, 2, 1)
displayData(X_norm[:100,:])
plt.title('Original faces')
plt.gca().set_aspect('equal', adjustable='box')

# Display reconstructed data from only k eigenfaces
plt.subplot(1, 2, 2)
displayData(X_rec[:100,:])
plt.title('Recovered faces')
plt.gca().set_aspect('equal', adjustable='box')

input('Program paused. Press enter to continue.')

## === Part 8(a): Optional (ungraded) Exercise: PCA for Visualization ===
#  One useful application of PCA is to use it to visualize high-dimensional
#  data. In the last K-Means exercise you ran K-Means on 3-dimensional
#  pixel colors of an image. We first visualize this output in 3D, and then
#  apply PCA to obtain a visualization in 2D.

plt.close()

# Re-load the image from the previous exercise and run K-Means on it
# For this to work, you need to complete the K-Means assignment first

# A = double(imread('bird_small.png'));
mat = loadmat('ml/ex7/data/bird_small.mat')
A = mat["A"]

# from ex7.py, part 4
A = A / 255.0
img_size = A.shape
X = A.reshape(img_size[0] * img_size[1], 3, order='F').copy()
K = 16
max_iters = 10
initial_centroids = kMeansInitCentroids(X, K)
centroids, idx = runkMeans(X, initial_centroids, max_iters)

#  Sample 1000 random indexes (since working with all the data is
#  too expensive. If you have a fast computer, you may increase this.
#  use flatten(). otherwise, Z[sel, :] yields array w shape [1000,1,2]
sel = np.floor(np.random.rand(1000, 1) * X.shape[0]).astype(int).flatten()

#  Setup Color Palette
palette = hsv(K)
colors = np.array([palette[int(i)] for i in idx[sel]])

#  Visualize the data and centroid memberships in 3D
fig1 = plt.figure(1)
ax = fig1.add_subplot(111, projection='3d')
ax.scatter(X[sel, 0], X[sel, 1], X[sel, 2], s=100, c=colors)
plt.title('Pixel dataset plotted in 3D. Color shows centroid memberships')
plt.show(block=False)

input('Program paused. Press enter to continue.\n\n')

## === Part 8(b): Optional (ungraded) Exercise: PCA for Visualization ===
# Use PCA to project this cloud to 2D for visualization

# Subtract the mean to use PCA
X_norm, _, _ = featureNormalize(X)

# PCA and project the data to 2D
U, S = pca(X_norm)
Z = projectData(X_norm, U, 2)

# Plot in 2D
fig2 = plt.figure(2)
plotDataPoints(Z[sel, :], idx[sel], K)
plt.title('Pixel dataset plotted in 2D, using PCA for dimensionality reduction');
plt.show(block=False)

input('Program paused. Press enter to continue.')


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Feb 11 15:16:13 2019

@author: alankar
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.special.orthogonal import p_roots #Legendre Polynomial roots

def gauss_quad(func,a,b,n,*args):#Legendre
    [x,w] = p_roots(n+1)
    I_G = 0.5*(b-a)*np.sum(w*func(0.5*(b-a)*x+0.5*(b+a),*args))
    return I_G

a = [2,3,4]
x = np.linspace(0,5,100)

plt.figure(figsize=(13,10))
for A in a:
    plt.plot(x,x**(A-1)*np.exp(-x), label='a=%d'%A)

plt.legend(loc='best',prop={'size': 18})
plt.grid()
plt.title(r'$x^{a-1} \exp (-x)$',size=25,y=1.02)
plt.xlabel(r'$x$',size=22)
plt.ylabel(r'$f(x)$',size=22)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('15.png')
plt.show()

def I (z,a):
    c = a-1
    x = c/(1/z-1)
    factor = c/(1-z)**2
    return np.exp((a-1)*np.log(x)-x)*factor

N = 100
gamma = gauss_quad(I,0.,1.,N,1.5)
print('Gamma(3/2) = %.3f'%gamma)

gamma = np.array([gauss_quad(I,0.,1.,N,a) for a in np.arange(2,11,1)])
for i,g in enumerate(gamma):
    print('Gamma(%d) = %d'%(i+2,g))

"""
Output:

Gamma(3/2) = 0.886
Gamma(2) = 1
Gamma(3) = 2
Gamma(4) = 6
Gamma(5) = 24
Gamma(6) = 120
Gamma(7) = 720
Gamma(8) = 5040
Gamma(9) = 40320
Gamma(10) = 362880

"""


import numpy as np

def sigmoid(z):
    g = np.zeros(z.size)

    # ===================== Your Code Here =====================
    # Instructions : Compute the sigmoid of each value of z (z can be a matrix,
    #                vector or scalar
    #
    # Hint : Do not import math
    g = 1/(1+np.exp(-z))

    return g


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def make_3d_grid():
    """Generate a 3d grid of evenly spaced points"""
    return np.mgrid[0:21, 0:21, 0:5]

def homo_coordinate(g):
    """Transform a numpy 3D meshgrid to 3D data points in homogeneous coordinates"""
    axes = np.vstack(list(map(np.ravel, g)))
    data_points = axes.T
    return np.insert(data_points, 3, 1, axis=1)

def plot_3d_grid(m3):
    """Plot a numpy array of 3D data points in homogeneous coordinates"""
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    x, y, z = m3.T[0], m3.T[1], m3.T[2]  # ignore the extra scale dimension
    ax.scatter(x, y, z, facecolor=(0, 0, 0, 0.5), marker='o', s=20, edgecolor='black')  # (R,G,B,A)
    ax.set_xticks([0, 5, 10, 15, 20, 25])
    ax.set_yticks([0, 5, 10, 15, 20, 25])
    ax.set_zlim(0, 15)
    ax.set_title("3D Grid of evenly spaced points")
    plt.show()

def rigid_transform(m3, theta, omega, phi, p, q, r):
    """
    Return the matrix (in homogeneous coordinates) of the rigid transform corresponding to
       1. rotation of angle theta around the x-axis
       2. rotation of angle omega around the y-axis
       3. rotation of angle phi around the z-axis
       4. translation of vector (p, q, r)
    """
    # 3D translation matrix
    t = np.array([[1, 0, 0, p],
                  [0, 1, 0, q],
                  [0, 0, 1, r],
                  [0, 0, 0, 1]])

    # 3D rotation matrix
    rxz = np.array([[np.cos(omega), 0, np.sin(omega), 0],
                    [0, 1, 0, 0],
                    [-np.sin(omega), 0, np.cos(omega), 0],
                    [0, 0, 0, 1]])

    ryz = np.array([[1, 0, 0, 0],
                    [0, np.cos(theta), -np.sin(theta), 0],
                    [0, np.sin(theta), np.cos(theta), 0],
                    [0, 0, 0, 1]])

    rxy = np.array([[np.cos(phi), -np.sin(phi), 0, 0],
                    [np.sin(phi), np.cos(phi), 0, 0],
                    [0, 0, 1, 0],
                    [0, 0, 0, 1]])

    T = t @ ryz @ rxz @ rxy

    m3_transformed = np.apply_along_axis(lambda row: T @ row.T, 1, m3)
    return m3_transformed

def affine_transform(m3, s, theta, omega, phi, p, q, r):
    """Add a scaling factor s on top of the rigid_transform()"""
    # 3D scaling matrix
    sx = sy = sz = s
    sm = np.array([[sx, 0, 0, 0],
                   [0, sy, 0, 0],
                   [0, 0, sz, 0],
                   [0, 0, 0, 1]])

    m3_transformed = rigid_transform(m3, theta, omega, phi, p, q, r)
    m3_scaled = np.apply_along_axis(lambda row: sm @ row.T, 1, m3_transformed)
    return m3_scaled

def run():
    """Test the transform functions on the 3D grid, plot to show the result"""
    data = homo_coordinate(make_3d_grid())
    plot_3d_grid(data)

    dat1 = rigid_transform(data, 0, 0, 0, 5, 5, 8)  # test translation
    dat2 = rigid_transform(data, np.pi/12, np.pi/12, -np.pi/9, 0, 0, 15)  # test rotation
    dat3 = affine_transform(data, 0.5, np.pi/2, -np.pi/4, 0, 30, 15, 30)  # test scaling

    # plot translation
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    ax.scatter(data.T[0], data.T[1], data.T[2], facecolor=(0, 0, 0, 0.5), marker='o', s=20, edgecolor='black')
    ax.scatter(dat1.T[0], dat1.T[1], dat1.T[2], facecolor='#9400D3', marker='o', s=20, edgecolor='#9400D3', alpha=0.35)
    ax.set_xticks([0, 5, 10, 15, 20, 25])
    ax.set_yticks([0, 5, 10, 15, 20, 25])
    ax.set_zticks([0, 5, 10, 15, 20, 25])
    ax.set_zlim(0, 20)
    ax.set_title("test translation")
    plt.show()

    # plot rotation
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    ax.scatter(data.T[0], data.T[1], data.T[2], facecolor=(0, 0, 0, 0.5), marker='o', s=20, edgecolor='black')
    ax.scatter(dat2.T[0], dat2.T[1], dat2.T[2], facecolor='tomato', marker='o', s=20, edgecolor='r', alpha=0.3)
    ax.set_xticks([0, 5, 10, 15, 20, 25])
    ax.set_yticks([0, 5, 10, 15, 20, 25])
    ax.set_zticks([0, 5, 10, 15, 20, 25])
    ax.set_zlim(0, 20)
    ax.set_title("test rotation")
    plt.show()

    # plot scaling
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    ax.scatter(data.T[0], data.T[1], data.T[2], facecolor=(0, 0, 0, 0.5), marker='o', s=20, edgecolor='black')
    ax.scatter(dat3.T[0], dat3.T[1], dat3.T[2], facecolor='r', marker='o', s=20, edgecolor='r', alpha=0.1)
    ax.set_xticks([0, 5, 10, 15, 20, 25])
    ax.set_yticks([0, 5, 10, 15, 20, 25])
    ax.set_zticks([0, 5, 10, 15, 20, 25])
    ax.set_zlim(0, 20)
    ax.set_title("test scaling")
    plt.show()


import numpy as np
from numpy.testing import assert_allclose, assert_
from astroML.density_estimation import \
    scotts_bin_width, freedman_bin_width, knuth_bin_width, histogram

def test_scotts_bin_width(N=10000, rseed=0):
    np.random.seed(rseed)
    X = np.random.normal(size=N)
    delta = scotts_bin_width(X)

    assert_allclose(delta,  3.5 * np.std(X) / N ** (1. / 3))

def test_freedman_bin_width(N=10000, rseed=0):
    np.random.seed(rseed)
    X = np.random.normal(size=N)
    delta = freedman_bin_width(X)

    indices = np.argsort(X)
    i25 = indices[N / 4 - 1]
    i75 = indices[(3 * N) / 4 - 1]

    assert_allclose(delta, 2 * (X[i75] - X[i25]) / N ** (1. / 3))

def test_knuth_bin_width(N=10000, rseed=0):
    np.random.seed(0)
    X = np.random.normal(size=N)
    dx, bins = knuth_bin_width(X, return_bins=True)
    assert_allclose(len(bins), 59)

def test_histogram(N=1000, rseed=0):
    np.random.seed(0)
    x = np.random.normal(0, 1, N)

    for bins in [30, np.linspace(-5, 5, 31),
                 'knuth', 'scotts', 'freedman']:
        counts, bins = histogram(x, bins)
        assert_(counts.sum() == len(x))
        assert_(len(counts) == len(bins) - 1)


""" Tests for the `pyllars.stats_utils` module.
"""
import pytest
import pyllars.stats_utils as stats_utils

import numpy as np

def test_symmetric_entropy():
    """ Test the calculation of symmetric KL divergence
    for categorical distributions.

    The example calculation is taked from wikipedia: https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence
    """
    p = (0.36, 0.48, 0.16)
    q = (0.333, 0.333, 0.333)

    expected_output = (0.0852996+0.097455)/2
    actual_output = stats_utils.symmetric_entropy(p,q)

    assert pytest.approx(expected_output, actual_output)

def test_calculate_univariate_gaussian_kl():
    """ Test the calculation of univariate Gaussian KL divergence
    """
    p = (0,2)
    q = (1,1)

    # expected derived by hand
    expected_output = 1 - np.log(np.sqrt(2))
    actual_output = stats_utils.calculate_univariate_gaussian_kl(p,q)

    assert pytest.approx(expected_output, actual_output)

def test_symmetric_gaussian_kl():
    """ Test the calculation of symmetric KL divergence for
    univariate Gaussian distributions.

    Implicitly, this also tests calculating the non-symmetric KL
    for univariate Gaussians.
    """
    p = (0,2)
    q = (1,1)

    kl_pq = np.log(np.sqrt(2))
    kl_qp = 1 - np.log(np.sqrt(2))

    expected_output = (kl_pq + kl_qp) / 2
    actual_output = stats_utils.symmetric_gaussian_kl(p,q)

    assert pytest.approx(expected_output, actual_output)


import numpy as np

from Regularization import cost_l2_regularization

########################################################################################################################
"""
GROUP: Cost Function
	-Computes the cost function of your neural networks output based on it's output and the corresponding label

EXTERNAL FUNCTIONS:
					1)compute_cost: selects the method in which to compute cost, whether to have regularization at cost
					  	-FEATURE: hoping to add a cost_type function in the future and experiment with different costs
INTERNAL FUNCTIONS:
					1)cross_entropy_cost- computes cross-entropy error
					2)reg_utils_cost - cost function from reg_utils of deeplearning.ai appears similiar to cross-entropy
"""
########################################################################################################################

### EXTERNAL FUNCTIONS ###

def compute_cost(AL, Y, parameters,lambd,cost_type="cross_entropy_cost"):
	"""
	Implement the cost function based on what is passed in by cost type

	Arguments:
	AL -- post-activation, output of forward propagation, of shape (output size, number of examples)
	Y -- "true" labels vector, of shape (output size, number of examples)
	lambd -- the value used to compute l2 regularization

	Optional Arguments:
	cost_type -- the selected cost function to use.

	Returns:
	cost -- computed cost(loss) of the output
	"""

	#select the cost type based on cost_type passed in
	if(cost_type=="reg_utils_cost"):
		cost = reg_utils_cost(AL, Y)
	elif(cost_type=="cross_entropy_cost"):
		cost = cross_entropy_cost(AL, Y)
	else:
		print('ERROR: compute_cost - no cost function was selected')
		print("activation_type=" + activation_type)
		sys.exit(1)

	#if lambd is greater than zero compute l2 regularization for the cost function
	if lambd > 0:
		cost = cost + cost_l2_regularization(Y, parameters, lambd)

	return cost

### INTERNAL FUNCTIONS ###

def cross_entropy_cost(AL, Y):
	"""
	Implement the cross_entropy cost function

	Arguments:
	AL -- post-activation, output of forward propagation, of shape (output size, number of examples)
	Y -- "true" labels vector, of shape (output size, number of examples)

	Returns:
	cost -- cross-entropy cost
	"""
	m = Y.shape[1]
	# Compute loss from AL and Y.
 	try:
 		cost = -(1.0/m)*np.nansum(np.multiply(Y,np.log(AL)) + np.multiply((1-Y),np.log(1-AL+1e-8)))
 	except RuntimeWarning as w:
 		print("***cross_entropy_cost***")
 		print(w)
 		print(AL)
 		#print(sys.float_info.min)
 		print(Y)
	#cost = np.squeeze(cost)      # To make sure your cost's shape is what we expect (e.g. this turns [[17]] into 17).
	return cost

def reg_utils_cost(a3, Y):
	"""
	Implement the cost function

	Arguments:
	a3 -- post-activation, output of forward propagation
	Y -- "true" labels vector, same shape as a3

	Returns:
	cost - value of the cost function
	"""

	m = Y.shape[1]

	logprobs = np.multiply(-np.log(a3),Y) + np.multiply(-np.log(1 - a3), 1 - Y)
	logprobs[logprobs==np.inf]=0
	cost = 1./m * np.nansum(logprobs)

	return cost


import numpy as np

def calc_positions(a1, a2, a3, n1, n2, n3, basis):
    """
    Finish writing this function.
    The function must calculate the lattice point (R) and the absolute position
    of each atom in the basis, for this particular lattice point.

    Inputs:
    - a1, a2, a3: 3-component vector. The primitive lattice vectors
    - n1, n2, n3: integers. The lattice point coefficients.
    - basis: (n, 3) array. The positions of the atoms in the basis. Examples:
             "basis[0, 2]" refers to the first atoms z-component,
             "basis[2, 1]" refers to the third atoms y-component,
             "basis[1, 0]" refers to the second atoms x-component.

    Required outputs:
    - lattice_point: 3-component vector. The lattice vector for this specific
                     lattice point
    - atomic_positions: (n, 3) array. The absolute positions of the atoms in
                        the basis, for this specific lattice point.

    Sample inputs:
    a1 = [1, 0, 0],
    a2 = [0, 1, 0],
    a3 = [0, 0, 1],
    n1 = 2,
    n2 = 1,
    n3 = 3,
    basis = [[  0,   0,   0],
             [0.5, 0.5, 0.5]]

    Sample outputs:
    lattice_point = [2, 1, 3]
    atomic_positions = [[  2,   1,   3],
                        [2.5, 1.5, 3.5]]
    """

    # Mock up:
    # Calculate the current lattice point
    lattice_point = np.array((0, 0, 0))

    # Get the number of atoms in the basis
    n = basis.shape[0]

    # Calculate the atomic positions
    atomic_positions = np.zeros((n, 3))

    # Sample solution

    lattice_point = n1*a1 + n2*a2 + n3*a3
    # Reshape the lattice_point, so we can easily add it to the basis
    lattice_add = lattice_point.reshape(1, 3)
    atomic_positions = basis + lattice_add

    return lattice_point, atomic_positions

def calc_reciprocal_lattice(a1, a2, a3, m1, m2, m3):
    """
    Finish this function.
    The function must calculate the primitive lattice vectors for the
    reciprocal lattice, along with the specific reciprocal lattice point given
    by the coefficients.

    Inputs:
    - a1, a2, a3: 3-component vectors. Primitive lattice vectors of the direct
                  lattice.
    - m1, m2, m3: integers. Coefficients for the reciprocal lattice point.

    Outputs:
    - b1, b2, b3: 3-component vectors. Primitive lattice vectors of the
                  reciprocal lattice.
    - G: 3-component vector. The reciprocal lattice point.

    Sample inputs:
    a1 = [1, 0, 0]
    a2 = [0, 1, 0]
    a3 = [0, 0, 1]
    m1 = 3
    m2 = 1
    m3 = 0

    Sample outputs:
    b1 = [2*pi, 0, 0]
    b2 = [0, 2*pi, 0]
    b3 = [0, 0, 2*pi]
    G = [6*pi, 2*pi, 0]
    """

    # Mock up:
    b1 = np.array((0, 0, 0))
    b2 = np.array((0, 0, 0))
    b3 = np.array((0, 0, 0))
    G = np.array((0, 0, 0))

    # Sample Solution

    # Calculate the scale factor
    scale = (2 * np.pi) / np.dot(a1, np.cross(a2, a3))
    # Calculate the primitive lattice vectors
    b1 = scale * np.cross(a2, a3)
    b2 = scale * np.cross(a3, a1)
    b3 = scale * np.cross(a1, a2)
    # Calculate the lattice point
    G = m1*b1 + m2*b2 + m3*b3

    return b1, b2, b3, G


# -*- coding: utf-8 -*-
"""Created on Sat Mar 25 21:24:00 2017 @author: andres

This module contains error/loss functions that can be used to train the network

"""
__all__ = ['mean_squared_error']
#import numpy as np
from netbuilder import np

def mean_squared_error(target,actual,derivative=False):
    """A simple loss function. It computes the difference between target and actual and
    raises the value to the power of 2, and everything is divided by 2. The computed value is the error.

    Parameters
    ----------
    target : numpy array
        Contains the values we want the network to approximate.
    actual : numpy array
        Same shape as target. It is the output of the network after feedforward propagation.

    Returns
    -------
    float/numpy array
        If function is called in normal mode, then a float (the error) is returned. When derivative mode
        is used, a numpy array is returned (same shape as input arrays).
    """

    try:
        assert(target.shape==actual.shape)
    except AssertionError:
        print("""Shape of target array '{0}' does not match shape of actual '{1}'""".format(target.shape,actual.shape))
        raise
    if not derivative:
        #compute the error and return it
        #print('='*80)
        #print('Error Function: MSE\nTarget:\tActual:')
        #for i in range(len(target)):
        #    print(target[i],actual[i])

        #print()
        #print('Summing over rows and squaring:')
        #for i in range(len(target)):
        #    print(np.sum((target[i]-actual[i])**2))
        error = np.sum(0.5 * np.sum((target-actual)**2, axis=1, keepdims=True))
        return error
    else:
        return (actual - target)


import numpy as np
import matplotlib.pyplot as plt

# 目标函数:y=x^2
def func(x):
    return np.square(x)

# 目标函数一阶导数也即是偏导数:dy/dx=2*x
def dfunc(x):
    return 2 * x

# Gradient Descent
def GD(x_start, df, epochs, lr):
    """
    梯度下降法。给定起始点与目标函数的一阶导函数，求在epochs次迭代中x的更新值
    :param x_start: x的起始点
    :param df: 目标函数的一阶导函数
    :param epochs: 迭代周期
    :param lr: 学习率
    :return: x在每次迭代后的位置（包括起始点），长度为epochs+1
    """
    xs = np.zeros(epochs+1)
    x = x_start
    xs[0] = x
    for i in range(epochs):
        dx = df(x)
        # v表示x要改变的幅度
        v = - dx * lr
        x += v
        xs[i+1] = x
    return xs

def demo_GD():
    # 演示如何使用梯度下降法GD()
    line_x = np.linspace(-5, 5, 100)
    line_y = func(line_x)

    x_start = -5
    epochs = 5

    lr = 0.3
    x = GD(x_start, dfunc, epochs, lr=lr)

    color = 'r'
    plt.plot(line_x, line_y, c='b')
    plt.plot(x, func(x), c=color, label='lr={}'.format(lr))
    plt.scatter(x, func(x), c=color, )
    plt.legend()
    plt.show()

if __name__ == "__main__":
    demo_GD()


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

import numpy as np
from sympy import *
from math import *

def regra_1_3_Simpson(fx,a,h,x):

    return ((h)*(fx.subs(x,a) + 4*fx.subs(x,a+h) + fx.subs(x,a+2*h)))/3

x = symbols('x')

fx = sympify(str(input("\n\n   Digite a função f(x): ")))
a = float(input("   Digite o começo do intervalo de integração: "))
b = float(input("   Digite o fim do intervalo de integração: "))
t = float(input("   Digite o modo de integração: (0 - sem repetição, 1 - com repetição): "))

if(t == 0):

    Ir = integrate(fx,(x,a,b))
    Ia = regra_1_3_Simpson(fx,a,abs(b-a)/2,x)
    print("   Integral aproximada: "+str(Ia)+"\n\n")

elif(t == 1):

   m = int(input("   Digite a quantidade m de intervalos: "))

   h = float(abs(b-a)/m)

   Et = -(h**5/90)*diff(diff(diff(diff(fx,x),x),x),x)

   Es = Et.subs(x,a)

   if(m%2 == 0 and m*h == (b-a)):
        Ia = 0
        for i in range(0,m-1,2):
            Es += Et.subs(x,a+h)
            Ia += regra_1_3_Simpson(fx,a,h,x)
            a += 2*h

        print("   Integral aproximada: "+str(Ia)+"\n\n")
   else:
        print("   Erro: m não é múltiplo de 2\n\n")


# numpy provides ufunc sinh(), cosh(), tanh()
# that takes values in radians and produce corresponding sinh, cosh, tanh values.
import numpy as np

y = np.array([np.pi/2, np.pi/3, np.pi/4, np.pi/5])
z = np.cosh(y)                    # cos hyperbolic function
print(z)

h = np.array([1.0, 0.2, 0.3, 0.4])
g = np.arcsinh(h)                            # finding angles from sinh, cosh, tanh values
print(g)

Python/Numpy/numpy 42 - ufunc hyperbollic function.py


import numpy as np

def mad(x, axis=None, no_scale=False):
    """
    median absolute deviation - scaled like a standard deviation

        mad = 1.4826*median(|x-median(x)|)

    Parameters
    ----------
    x: array-like
        array to take MAD of
    axis: int
        axis over which to take mode (default: None)
    no_scale: bool
        return MAD not scaled to match a Gaussian (default: False)

    Returns
    -------
    mad: float
        MAD of array x
    """
    mad = np.median(np.abs(x - np.median(x,axis=axis)),axis=axis)
    if no_scale:
        return mad
    else:
        return 1.4826*mad


#!/usr/bin/python3
import sys
import numpy as np
from scipy import optimize
from matplotlib import pyplot as plt

def catenary(x, a, p, q):
    """
    The catenary function in its most generic form.
    """
    return a * np.cosh((x-p)/a) + q

def f(a, h, v, L):
    """
    This function will be used by the Newton-Raphson algorithm to find
    a value for 'a'.
    """
    return 2 * a * np.sinh(0.5 * h/a) - np.sqrt(np.power(L, 2)-np.power(v, 2))

def fprime(a, h):
    """
    The derivative of f will also be used by the Newton-Raphson algorithm to find
    a value for 'a'.
    """
    return 2 * np.sinh(0.5 * h/a) - (h/a) * np.cosh(0.5 * h/a)

def nr_first_guess(ff, start_x, start_step, alpha):
    """
    This helper function finds a good enough first guess for the value of 'a', and
    allows the Newton-Raphson algorithm to converge.
    """
    xx = start_x
    step = start_step
    yy = ff(xx)
    yy_prev = yy

    while yy * yy_prev > 0:
        yy_prev = yy
        xx += step
        step *= alpha
        yy = ff(xx)

    # Backtrack a bit
    return xx - 0.5 * step / alpha

def get_params(p1, p2, L):
    """
    Return the curve parameters 'a', 'p', 'q' as well as the integration
    constant 'c', given the input parameters.
    """
    hv = p2 - p1
    m = p1 + p2
    def f_bind(a): return f(a, *hv, L)
    def fprime_bind(a): return fprime(a, hv[0])

    # Newton-Raphson algorithm to find a value for 'a'
    a0 = nr_first_guess(f_bind, 0.1, 0.01, 1.8)
    a = optimize.newton(f_bind, a0, fprime_bind)

    # Use our formulas to compute the rest
    p = 0.5 * (m[0] - a * np.log((L+hv[1])/(L-hv[1])))
    q = 0.5 * (m[1] - L / np.tanh(0.5 * hv[0]/a))
    c = -a * np.sinh((p1[0]-p)/a)

    return a, p, q, c

def arclen_remap(s_bar, L, a, p, c):
    """
    Map a normalized length fraction between 0 and 1 to an argument
    value to feed the catenary function
    """
    return a * np.arcsinh((s_bar * L - c) / a) + p

def main(argv):
    p1 = np.array([0.5, 0.6])
    p2 = np.array([4.1, 2.5])
    min_len = np.linalg.norm(p2-p1)

    fig, axs = plt.subplots(1, 2)

    for ii in range(1, 4):
        L = ii * min_len + 0.1
        a, p, q, c = get_params(p1, p2, L)
        # print(L, a, p, q, c)

        xp = np.linspace(p1[0], p2[0], 100)
        yp = catenary(xp, a, p, q)

        xps = np.linspace(p1[0], p2[0], 15)
        yps = catenary(xps, a, p, q)

        s_bar = np.linspace(0, 1, 15)
        xps_arclen = arclen_remap(s_bar, L, a, p, c)
        yps_arclen = catenary(xps_arclen, a, p, q)

        axs[0].plot(xp, yp, linewidth=2)
        axs[0].plot(xps, yps, linewidth=10, linestyle='solid', color='black', alpha=0.15)
        axs[0].scatter(xps, yps, linewidth=2, marker='+', s=100, color='red')

        axs[1].plot(xp, yp, linewidth=2)
        axs[1].plot(xps_arclen, yps_arclen, linewidth=10, linestyle='solid', color='black', alpha=0.15)
        axs[1].scatter(xps_arclen, yps_arclen, linewidth=2, marker='+', s=100, color='red')

    axs[0].set_xlabel('x')
    axs[0].set_ylabel('y')
    axs[0].set_title('Initial parametrization')
    axs[0].set_aspect('equal', adjustable='box')

    axs[1].set_xlabel('x')
    axs[1].set_ylabel('y')
    axs[1].set_title('Arc length parametrization')
    axs[1].set_aspect('equal', adjustable='box')

    plt.show()

if __name__ == '__main__':
    main(sys.argv[1:])


# -*- coding: utf-8 -*-
"""
Created on Sat Jun 23 21:20:02 2018

@author: SilverDoe
"""

#============== Trignometric functions ========================================
import numpy as np
a = np.array([0,30,45,60,90])

rad =a*np.pi/180 # converting to radian

deg = np.degrees(rad) # converting to degrees

# finding trigonmetric ratios
sine = np.sin(rad)
cosine = np.cos(rad)
tangent = np.tan(rad)

print(sine)
print(cosine)
print(tangent)

# finding inverse
sinv = np.arcsin(sine)
cinv = np.arccos(cosine)
tinv = np.arctan(tangent)

print(sinv)
print(cinv)
print(tinv)

#============= round, ceil, floor =============================================

import numpy as np
a = np.array([1.0,5.55, 123, 0.567, 25.532])

print(np.around(a))
print(np.around(a, decimals = 1))
print(np.around(a, decimals = -1))

print(np.floor(a))
print(np.ceil(a))

#============= Arithmetic Operations ==========================================

import numpy as np

a = np.arange(9, dtype = np.float_).reshape(3,3)
print('First array:')
print(a)
print('\n')

print('Second array:')
b = np.array([10,10,10])
print(b)
print('\n')

print('Add the two arrays:')
print(np.add(a,b))
print('\n')

print('Subtract the two arrays:')
print(np.subtract(a,b) )
print('\n')

print('Multiply the two arrays:')
print(np.multiply(a,b))
print('\n')

print('Divide the two arrays:')
print(np.divide(a,b))

#============== reciprocal, power, mod =====================================

# recoprocal
import numpy as np
a = np.array([1,2,3,4,5],dtype='float') # reciprocal of integers will be zero for any number greater than 1
print(np.reciprocal(a))

# power
import numpy as np
a = np.array([10,100,1000])
b = np.array([1,2,3])
print(np.power(a,b))

'''import numpy as np
a = np.array([1,2,3,4,5])
b = np.array([2,3,4,5,6])
print(np.power(a,2))
print(a,b)'''

# mod
import numpy as np
a = np.array([10,11,12,13,14,15])
b = np.array([2,3,4,5,6,7])
print(np.mod(a,2))
print(np.mod(a,b)) #np.remainder(a,b) also produces the same result

#============= operation on complex numbers ===================================

import numpy as np
a = np.array([-5.6j, 0.2j, 11. , 1+1j])

print(np.real(a))
print(np.imag(a))
print(np.conj(a))
print(np.angle(a))
print(np.angle(a, deg = True))

#============ Statistical functions ===========================================

import numpy as np
a = np.array([[3,7,5],[8,5,3],[2,87,9]])
print(a)
# return the minimum from the elements in the given array along the specified axis
print(np.amin(a))
print(np.amin(a, axis=0))
print(np.amin(a, axis=1))

# return the maximum from the elements in the given array along the specified axis
print(np.amax(a))
print(np.amax(a,axis=0))
print(np.amax(a,axis=1))

# ptp() returns the range (maximum-minimum) of values along an axis.
print(np.ptp(a)) # 87-2
print(np.ptp(a,axis=0)) # 8-2, 87-5, 9-3
print(np.ptp(a,axis=1)) # 7-3, 8-3, 87-2

import numpy as np
a = np.array([[90,91,94],[80,60,93],[70,90,60]])
print(a)

print(np.percentile(a,50))
print(np.percentile(a,50, axis=0))
print(np.percentile(a,50, axis=1))

# defined as the value separating the higher half of a data sample from the lower half
#The median is the 50th percentile: the point in the data where 50% of the data fall below that point, and 50% fall above it.
print(np.median(a))
print(np.median(a, axis=0))
print(np.median(a, axis=1))

print(np.mean(a))
print(np.mean(a, axis=0))
print(np.mean(a, axis=1))

#Weighted average is an average resulting from the multiplication of each component by a factor reflecting its importance.
wts = np.array([4,3,2])
print(np.average(a)) # same as mean when no weight is given
print(np.average(a,returned=True))# Returns the sum of weights, if the returned parameter is set to True.
print(np.average(a,weights = wts, axis=0))
print(np.average(a,weights = wts, axis=1))

# mean(abs(x - x.mean())**2)
print(np.var(a))
print(np.var(a, axis=0))
print(np.var(a, axis=1))

# sqrt(mean(abs(x - x.mean())**2))
print(np.std(a))
print(np.std(a, axis=0))
print(np.std(a, axis=1))


import numpy as np
import copy
try:
    from tqdm import tqdm
except ImportError:
    pass

eps = 1e-6

def jacobian(f, p, **kwargs):
    '''
    Finds the Jacobian matrix of the function f at the point p.

    Arguments
    ---------
    f : callable
    A function F^n -> F^m (F = R or C);
    takes in an n-length numpy array and returns an m-length numpy array.

    p : np.ndarray, shape (n,)
    The n-length numpy array around which we want to approximate the Jacobian.

    Returns
    -------
    jac : np.ndarray, shape (m, n)
    The Jacobian matrix (the matrix of first-order partial derivatives).
    '''
    n = len(p)
    center = f(p, **kwargs)
    rights = f(p + eps * np.eye(n), **kwargs)
    lefts = f(p - eps * np.eye(n), **kwargs)
    return .5 * ((rights.T - center) / eps + (center - lefts.T) / eps).T

class KFilter:
    '''
    Interface to a Kalman Filter or Extended Kalman Filter.

    Attributes
    ----------
    f : callable or np.ndarray
    The state-transition function or matrix.

    Q : np.ndarray, (s, s).
    The covariance of the state-transition model.

    h : callable or np.ndarray
    The measurement function or matrix.

    R : np.ndarray, (m, m)
    The covariance of the measurement model.

    s : int
    The dimension of the state.

    m : int
    The dimension of the measurement.

    state : np.ndarray, (s,)
    The state vector.

    P : np.ndarray, (s, s)
    The true state covariance.

    prev_P : np.ndarray, (s, s)
    The true state covariance one timestep ago, to check steady-state.

    steady_state : bool
    Whether the filter is in steady state (and can skip covariance updates).

    predict_chain : np.ndarray, (s, num_timesteps)
    The chain of state predictions made at each timestep.

    filter_chain : np.ndarray, (s, num_timesteps)
    The chain of MMSE state estimates at each timestep.

    Methods
    -------
    check_consistency()
    Checks whether dimensions match up.

    predict()
    Carries out a Kalman prediction.

    update(measurement:np.ndarray, (m,))
    Carries out a Kalman update.

    run_kf(signal:np.ndarray, (m, num_timesteps), progress:bool)
    Runs the filter in a predict-update cycle with the measurements in 'signal'.
    Does or doesn't use tqdm for progress based on 'progress'.

    reset()
    Resets the chains and the discovered covariance.
    '''
    def __init__(self, f, Q, h, R, state=None):
        self.f = f
        self.Q = Q
        self.h = h
        self.R = R
        self.s = Q.shape[0]
        self.m = R.shape[0]
        if state is None:
            self.state = np.zeros(self.s)
        else:
            self.state = state
        self.prev_P = np.zeros((self.s, self.s))
        self.P = np.zeros((self.s, self.s))
        self.steady_state = False
        self.predict_chain = np.empty((self.s, 0))
        self.filter_chain = np.empty((self.s, 0))
        self.check_consistency()

    def check_consistency(self):
        assert (isinstance(self.f, np.ndarray) and self.f.shape == (self.s, self.s)) or callable(self.f), "state transition is invalid"
        assert (isinstance(self.h, np.ndarray) and self.h.shape == (self.m, self.s)) or callable(self.h), "state measurement is invalid"
        assert isinstance(self.Q, np.ndarray) and self.Q.shape == (self.s, self.s), "model covariance is invalid"
        assert isinstance(self.R, np.ndarray) and self.R.shape == (self.m, self.m), "measurement covariance is invalid"

    def measure(self):
        if callable(self.h):
            return self.h(self.state)
        else:
            return self.h @ self.state

    def predict(self, **kwargs):
        self.prev_P = copy.deepcopy(self.P)
        if isinstance(self.f, np.ndarray):
            self.state = self.f @ self.state
            self.P = self.f @ self.P @ self.f + self.Q
        else:
            A = jacobian(self.f, copy.deepcopy(self.state), **kwargs)
            self.state = self.f(self.state)
            self.P = A @ self.P @ A + self.Q

    def update(self, measurement):
        if isinstance(self.h, np.ndarray):
            C = self.h
        else:
            C = jacobian(self.h, copy.deepcopy(self.state))
        innovation = measurement - self.measure()
        if not self.steady_state:
            self.K = self.P @ C.T @ np.linalg.inv(C @ self.P @ C.T + self.R)
            self.P -= self.K @ (C @ self.P)
            if np.allclose(self.P, self.prev_P):
                self.steady_state = True
        innovation = measurement - self.measure()
        self.state = self.state + self.K @ innovation

    def run_kf(self, signal, progress=True, **kwargs):
        predict_chain = np.zeros(len(signal), self.s)
        filter_chain = np.zeros(len(signal), self.s)
        if progress and 'tqdm' in globals():
            track_progress = tqdm
        else:
            track_progress = lambda x: x
        try:
            for i, m in enumerate(track_progress(signal)):
                self.predict(**kwargs)
                predict_chain[i] = self.state
                self.update(m, **kwargs)
                filter_chain[i] = self.state
        except KeyboardInterrupt:
            print("Filter terminated early, saving results.")
        for objchain, chain in zip([self.predict_chain, self.filter_chain], [predict_chain, filter_chain]):
            objchain = np.hstack((objchain, chain))

    def reset(self):
        self.predict_chain = np.empty((self.s, 0))
        self.filter_chain = np.empty((self.s, 0))
        self.steady_state = False
        self.prev_P = np.zeros_like(self.P)
        self.P = np.zeros_like(self.P)
        self.state = np.zeros_like(self.state)


import numpy as np

class Roots:

    """
    Instantiate a function.

    :param func: The lambda function to evaluate.
    :type func: Lambda function object

    :param dx: The derivative of the function passed to 'func'. Optional - Used only in Newton-Raphson method.
    :type dx: Lambda function object
    """
    def __init__(self, func, dx = None):
        self.func = func
        self.dx = dx

    def bisection(self, lbound: float, ubound: float, max_itn = 100, delta = 1e-6):
        """
        Finding the root of a function using bisection method.

        :param lbound: The lower bound of the range to evaluate.
        :type lbound: int

        :param ubound: The upper bound of the range to evaluate.
        :type ubound: int

        :param max_itn: The maximum number of iterations to evaluate.
        :type itn: int

        :param delta: The error threshhold.
        :type itn: float

        :return: Numpy array of iteration steps.
        :rtype: ndarray
        """

        a = lbound
        b = ubound
        itn = 0

        if self.func(a)*self.func(b) > 0:
            return 'Invalid upper and lower limits'

        else:
            while itn <= max_itn and abs(a-b) >= delta:
                x = (a + b)/2
                if itn == 0:
                    arr = [a, b, x, self.func(x)]
                else:
                    arr = np.vstack([arr, [a, b, x, self.func(x)]])
                if self.func(x) > 0:
                    b = x
                elif self.func(x) < 0:
                    a = x
                else:
                    return arr
                itn += 1

            return arr

    def incremental():
        return

    def false_position(self, x0: float, x1: float, max_itn = 100, delta = 1e-6):
        """
        Finding the root of a function using false position method.

        :param x0: The lower bound of the range to evaluate.
        :type lbound: int

        :param x1: The upper bound of the range to evaluate.
        :type ubound: int

        :param max_itn: The maximum number of iterations to evaluate.
        :type itn: int

        :param delta: The error threshhold.
        :type itn: float

        :return: Numpy array of iteration steps.
        :rtype: ndarray
        """

        a = x0
        b = x1
        lst = [a, b]
        i = 2
        itn = 0

        if self.func(a)*self.func(b) > 0:
            return 'Invalid upper and lower limits'

        else:
            while itn <= max_itn and abs(a - b) >= delta:
                lst.append(lst[0] - (lst[i - 1] - lst[0])*(self.func(lst[0]))/(self.func(lst[i-1]) - self.func(lst[0])))
                if itn == 0:
                    arr = [itn, i, lst[i-1], lst[i], self.func(lst[i])]
                else:
                    arr = np.vstack([arr, [itn, i, lst[i-1], lst[i], self.func(lst[i])]])
                a = lst[i - 2]
                b = lst[i - 1]
                itn += 1
                i += 1
            return arr

    def secant(self, x0: float, x1: float, max_itn = 100, delta = 1e-6):
        """
        Finding the root of a function using secant method.

        :param x0: The lower bound of the range to evaluate.
        :type lbound: int

        :param x1: The upper bound of the range to evaluate.
        :type ubound: int

        :param max_itn: The maximum number of iterations to evaluate.
        :type itn: int

        :param delta: The error threshhold.
        :type itn: float

        :return: Numpy array of iteration steps.
        :rtype: ndarray
        """

        a = x0
        b = x1
        lst = [a, b]
        i = 2
        itn = 0

        if self.func(a)*self.func(b) > 0:
            return 'Invalid upper and lower limits'

        else:
            while itn <= max_itn and abs(a - b) >= delta:
                lst.append(lst[i-1] - (self.func(lst[i - 1])*(lst[i-1] - lst[i-2]))/(self.func(lst[i-1]) - self.func(lst[i-2])))
                if itn == 0:
                    arr = [itn, i, lst[i-1], lst[i], self.func(lst[i])]
                else:
                    arr = np.vstack([arr, [itn, i, lst[i-1], lst[i], self.func(lst[i])]])
                a = lst[i - 2]
                b = lst[i - 1]
                itn += 1
                i += 1
            return arr
        return

    def newton_raphson(self, x0: float, max_itn = 100, delta = 0.0001):
        """
        Finding the root of a function using Newton-Raphson method.

        :param x0: The initial value to evaluate.
        :type x0: int

        :param max_itn: The maximum number of iterations to evaluate.
        :type itn: int

        :param delta: The error threshhold.
        :type itn: float

        :return: Numpy array of iteration steps.
        :rtype: ndarray
        """

        x = x0
        itn = 0

        while itn <= max_itn and abs(self.func(x)/self.dx(x)) >= delta:
            x = x - (self.func(x)/self.dx(x))

            if itn == 0:
                arr = [itn, x, self.func(x), self.dx(x), abs(self.func(x)/self.dx(x))]

            else:
                arr = np.vstack([arr, [itn, x, self.func(x), self.dx(x), abs(self.func(x)/self.dx(x))]])
            itn += 1

        return arr

class Solutions:
    """
        Instantiate a function.

        :param arr_a: The coefficient matrix.
        :type arr_a: Numpy ndarray

        :param arr_b: The constant matrix.
        :type arr_b: Numpy ndarray

    """

    def __init__(self, arr_a: np.ndarray, arr_b: np.ndarray):
        self.arr_a = np.array(arr_a).astype(float)
        self.arr_b = np.array(arr_b).astype(float)

    def gauss(self):
        """
            Finding the solution to a system of equations using Gauss Elimination.

            :return: The solution vector.
            :rtype: Numpy ndarray

        """
        a = self.arr_a
        b = self.arr_b
        n = len(b)
        x = [0]*n
        for k in range(0,n-1):
            for i in range(k+1,n):
                if a[i,k] != 0.0:
                    lam = a[i,k]/a[k,k]
                    a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
                    b[i] = b[i] - lam*b[k]

        for k in range(n-1,-1,-1):
            b[k] = (b[k] - np.dot(a[k,k+1:n],b[k+1:n]))/a[k,k]

        return b

    def gauss_seidel(self, funcs: list, guess: list, delta = 0.01):

        """
            Gauss Seidel approximation of the solution to a system of equations.

            :param funcs: List of lambda functions of list.
            :type funcs: list

            :param guess: Vector of guessed values.
            :type guess: list

            :param delta: Tolerance value.
            :type delta: float

            :return: Iterated approximations of solution.
            :rtype: Numpy ndarray

        """

        funcs = funcs
        arr1 = guess
        arr2 = guess
        flag = False
        itn = 1

        while flag == False:

            temp_arr2 = []
            for i in range(len(funcs)):
                temp_arr = []
                for j in range(len(funcs)):
                    if i != j:
                        temp_arr.append(arr1[j])

                temp_arr2.append(funcs[i](temp_arr))

            arr2 = np.vstack([arr2, temp_arr2])
            arr1 = temp_arr2

            ls = [abs((arr2[itn, i] - arr2[itn-1, i])/arr2[itn, i]) for i in range(len(arr1))]

            for val in ls:
                if val < delta:
                    flag = True

            itn += 1

        return arr2

    def LUdecomp(self):
        """
            LU Decomposition of symmetric matrix.

            :param arr_a: The coefficient matrix.
            :type arr_a: Numpy ndarray

            :return: Decomposed L and U matrices.
            :rtype: Numpy ndarray, Numpy ndarray

        """

        a = self.arr_a
        n = len(a)
        for k in range(0,n-1):
            for i in range(k+1,n):
                if a[i,k] != 0.0:
                    lam = a [i,k]/a[k,k]
                    a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
                    a[i,k] = lam

        L = np.array([[0.0]*3]*3)
        U = np.array([[0.0]*3]*3)

        for i in range(n):
            for j in range(n):
                if i > j:
                    L[i, j] = a[i, j]
                elif i == j:
                    L[i, j] = 1
                    U[i, j] = a[i, j]
                else:
                    U[i, j] = a[i, j]

        return L, U

    def LUsolve(self):
        """
            Finding the solution to a system of equations using LU Decomposition.

            :param arr_a: The coefficient matrix.
            :type arr_a: Numpy ndarray

            :param arr_b: The constant matrix.
            :type arr_b: Numpy ndarray

            :return: The solution vector.
            :rtype: Numpy ndarray

        """
        a = self.arr_a
        b = self.arr_b
        n = len(a)
        for k in range(1,n):
            b[k] = b[k] - np.dot(a[k,0:k],b[0:k])
        b[n-1] = b[n-1]/a[n-1,n-1]

        for k in range(n-2,-1,-1):
            b[k] = (b[k] - np.dot(a[k,k+1:n],b[k+1:n]))/a[k,k]

        return b

class Eigenvalue:
    def __init__(self, matrix, init_vector):
        self.matrix = matrix
        self.init_vector = init_vector

        return

    def jacobi(self):
        return

    def power(self, max_itn = 100, delta = 0.0001):
        eig_val = 0.0000
        eig_prev = 1.0000
        eig_vect = self.init_vector
        matrix = self.matrix
        itn = 1

        table = np.array(['itn', 'eig_val', 'eig_vect'], dtype = object)

        while abs(eig_val - eig_prev) >= delta and itn <= max_itn:
            eig_vect = np.dot(matrix, eig_vect)

            eig_prev = eig_val

            eig_val = abs(eig_vect).max()
            eig_vect = eig_vect/eig_vect.max()

            table = np.vstack([table, [itn, eig_val, eig_vect]])

            itn += 1

        return table

class Interpolation:

    """
    Instantiate a function.

    :param arr_a: Known 'x' values.
    :type arr_a: Numpy ndarray or list or series

    :param arr_b: Known 'y' values.
    :type arr_b: Numpy ndarray or list or series

    """
    def __init__(self, x_val, y_val):
        self.x_val = x_val
        self.y_val = y_val

    def lagrange(self, x: float) -> float:

        """
            Implementation of Lagrange's interpolation formula.

            :param x: The 'x' value in question.
            :type x: float

            :return: Interpolated 'y' value.
            :rtype: float

        """

        result = 0.0
        for i in range(len(self.x_val)):
            temp = self.y_val[i]

            for j in range(len(self.x_val)):
                if i != j:
                    temp = temp* (x - self.x_val[j])/(self.x_val[i] - self.x_val[j])

            result += temp

        return result

    def newton_dd(self, x_new: float, forward : bool = True):
        """
            Implementation of Newton's divided difference algorithm.

            :param x_new: The 'x' value in question.
            :type x_new: float

            :param forward: Selection of forward or backward algorithm
            :type forward: bool

            :return: Interpolated 'y' value.
            :rtype: float

        """

        x = self.x_val
        y = self.y_val

        n = len(y)
        coef = np.zeros([n, n])
        coef[:,0] = y
        for j in range(1,n):
            for i in range(n-j):
                coef[i][j] = (coef[i+1][j-1]-coef[i][j-1])/(x[i+j]-x[i])

        if forward:
            cf = coef[0, :]
        else:
            cf = np.flipud(coef).diagonal()

        k = 1
        p = 0
        for i in range(len(cf)):
            p += cf[i]*k
            k *= x_new - x[i]

        return coef, p

    def differentiate(self, x_new: float, h : float, second :bool = False, forward: bool = True):

        """
            Implementation of Newton's forward and backward difference differentiation algorithms.

            :param x_new: The 'x' value in question.
            :type x_new: float

            :param h: The h value - the step size
            :type h: float

            :param second: Selection of order of derivation
            :type second: bool

            :param forward: Selection of forward or backward algorithm
            :type forward: bool

            :return: Interpolated 'y' value.
            :rtype: float

        """

        x = self.x_val
        y = self.y_val

        n = len(y)

        arr = [0, 0.0, 0.9999999999217246, 0.9999999999746642, 0.9166666666919701, 0.8333333333563364, 0.7611111111427086, 0.7000000000510863, 0.6482142857779337, 0.6039682540329495, 0.5657936508246969, 0.5325396825532482, 0.5033128908190265, 0.47741702740370173, 0.45430482214953094, 0.4335416435253855, 0.4147786241242193, 0.39773282270807536, 0.3821725024909123, 0.36790611347003926, 0.35477396569519837]

        if forward:

            x_index = x.index(x_new)

            coef = np.zeros([n, n])
            coef[:,0] = y
            for j in range(1,n):
                for i in range(n-j):
                    coef[i][j] = (coef[i+1][j-1]-coef[i][j-1])

            delt = coef[x_index,:]
            val = 0

            if second:
                for i in range(2, n):
                    if i%2 == 0:
                        val += arr[i]*delt[i]
                    else:
                        val -= arr[i]*delt[i]
                dy = (1/h**2)*val

            else:

                i = 1
                while i<n:
                    if i%2 == 0:
                        val -= (1/i)*delt[i]
                    else:
                        val += (1/i)*delt[i]
                    i += 1
                dy = (1/h)*val

        else:

            x.reverse()
            y.reverse()

            x_index = x.index(x_new)

            coef = np.zeros([n, n])
            coef[:,0] = y
            for j in range(1,n):
                for i in range(n-j):
                    coef[i][j] = -(coef[i+1][j-1]-coef[i][j-1])

            delt = coef[x_index,:]
            val = 0

            if second:
                for i in range(2, n):
                    val += arr[i]*delt[i]
                dy = (1/h**2)*val

            else:

                i = 1
                while i<n:
                   val += (1/i)*delt[i]
                   i += 1
                dy = (1/h)*val

        return dy

    def trapezoidal(self, h : float):
        """
        Implementation of Trapezoidal method of Integration

        :param h: The 'h' value - step size.
        :type h: float

        """
        y = self.y_val

        return (h/2)*(2*sum(y[1: -1]) + y[0] + y[-1])

    def simpsons(self, h: float, three_eighths : bool= False ):
        """
        Implementation of Simpson's methods of Integration

        :param h: The 'h' value - step size.
        :type h: float

        :param three_eights: True - Simpson's 3/8 method, False(Default) - Simspson's 1/3 method
        :type three_eights: bool

        """
        y = self.y_val
        f = y.pop(0)
        l = y.pop(-1)

        if ~three_eighths:

            return (h/3)*(f + l + 4*sum([y[i] for i in range(len(y)) if i%2 == 0]) + 2*sum([y[i] for i in range(len(y)) if i%2 == 1]))

        else:
            return (3*h/8)*(f + l + 3*sum([y[i] for i in range(len(y)) if (i + 1)%3 != 0]) + 2*sum([y[i] for i in range(len(y)) if (i + 1)%3 == 0]))

class O_D_E:
    """
    Instantiate a function.

    :param func: Function of x and y
    :type func: Lambda function

    :param x_init: Initial x value
    :type x_init: float

    :param y_init: Initial y value
    :type y_init: float

    """
    def __init__(self, func, x_init: float, y_init: float):
        self.func = func
        self.x_init = x_init
        self.y_init = y_init

    def euler(self, x_new : float, h: float):

        """
    Implementation of Euler's method for solving Initial Value Problem.

    :param x_new: The x value as input
    :type x_new: float

    :param h: The h value - step size.
    :type h: float

    """

        fun = self.func
        x = []
        y = []

        x_val = self.x_init
        y_val = self.y_init

        while x_val < x_new:
            y_val += h*fun(x_val, y_val)
            x_val += h
            x.append(x_val)
            y.append(y_val)

        return list(zip(x, y))

    def mod_euler(self, x_new : float, h: float):

        """
    Implementation of Modified Euler's method for solving Initial Value Problem.

    :param x_new: The x value as input
    :type x_new: float

    :param h: The h value - step size.
    :type h: float

    """

        fun = self.func
        x = []
        y = []

        x_val = self.x_init
        y_val = self.y_init

        while x_val < x_new:
            y_val += h*fun(x_val + h/2, y_val + (h/2)*fun(x_val, y_val))
            x_val += h
            x.append(x_val)
            y.append(y_val)

        return list(zip(x, y))

    def runge_kutta2(self, x_new : float, h: float):

        """
    Implementation of Second Order Runge-Kutta method for solving Initial Value Problem.

    :param x_new: The x value as input
    :type x_new: float

    :param h: The h value - step size.
    :type h: float

    """

        fun = self.func
        x = []
        y = []

        x_val = self.x_init
        y_val = self.y_init

        while x_val < x_new:

            k1 = h*fun(x_val, y_val)
            k2 = h*fun(x_val + h, y_val + k1)

            y_val += (k1 + k2)/2
            x_val += h
            x.append(x_val)
            y.append(y_val)

        return list(zip(x, y))

    def runge_kutta3(self, x_new : float, h: float):

        """
    Implementation of Third Order Runge-Kutta method for solving Initial Value Problem.

    :param x_new: The x value as input
    :type x_new: float

    :param h: The h value - step size.
    :type h: float

    """

        fun = self.func
        x = []
        y = []

        x_val = self.x_init
        y_val = self.y_init

        while x_val < x_new:

            k1 = h*fun(x_val, y_val)
            k2 = h*fun(x_val + h/2, y_val + k1/2)
            k3 = h*fun(x_val + h, y_val + 2*k2 - k1)

            y_val += (1/6)*(k1 + 4*k2 + k3)
            x_val += h
            x.append(x_val)
            y.append(y_val)

        return list(zip(x, y))

    def runge_kutta4(self, x_new : float, h: float):

        """
    Implementation of Fourth Order Runge-Kutta method for solving Initial Value Problem.

    :param x_new: The x value as input
    :type x_new: float

    :param h: The h value - step size.
    :type h: float

    """

        fun = self.func
        x = []
        y = []

        x_val = self.x_init
        y_val = self.y_init

        while x_val < x_new:

            k1 = h*fun(x_val, y_val)
            k2 = h*fun(x_val + h/2, y_val + k1/2)
            k3 = h*fun(x_val + h/2, y_val + k2/2)
            k4 = h*fun(x_val + h, y_val + k3)

            y_val += (1/6)*(k1 + 2*k2 + 2*k3 + k4)
            x_val += h
            x.append(x_val)
            y.append(y_val)

        return list(zip(x, y))


from scipy import array
from scipy.special import legendre

def roots_legendre(n):
    """
    Computes the sample points and weights for Gauss-Legendre quadrature.
    The sample points are the roots of the n-th degree Legendre polynomial
    `P_n(x)`.  These sample points and weights correctly integrate
    polynomials of degree `2n - 1` or less over the interval
    `[-1, 1]` with weight function `f(x) = 1.0`.

    Parameters
    ----------
    n : int
        quadrature order

    Returns
    -------
    x : ndarray
        Sample points
    w : ndarray
        Weights
    """
    p = legendre(n + 1)
    x = legendre(n).roots
    w = 2 * (1 - x ** 2) / ((n + 1) ** 2 * p(x) ** 2)
    temp = list(zip(x, w))
    temp.sort()
    x, w = array([i for i, _ in temp]), array([i for _, i in temp])
    return x, w


def test_random_points(M):
  import numpy as np
  # p is a Mx2 matrix with two numbers in [-1, 1]
  p = 2.0 * np.random.rand(M, 2) - 1.0
  # count these points iff they are within one unit or less
  # within the origin
  return np.sum(np.linalg.norm(p, axis = 1) <= 1.0) / float(M)

def main():
  import sys
  N = int(sys.argv[1])
  M = int(sys.argv[2])
  # Note that test_random_point is now a PyCOMPSs task
  # This means that calling this task does not imply immediate results
  # So, a future object will be returned instead
  proportions = [test_random_points(M) for _ in range(N)]
  proportion = sum([x / float(N) for x in proportions])
  # Print our pi approximation with up to 16 decimal places
  print("%.16f" % (4.0 * proportion))

if __name__ == "__main__":
  main()