#simple lp

import numpy as np
import scipy.sparse as sparse
import scipy.sparse.linalg as linalg
# min cx
# x >= 0
# Ax = b

def newtonDec(df, dx):
	return np.dot(df,dx)

# assumes that x + alpha*dx can be made positive
def linesearch(x, dx):
   alpha = 1.
   while not np.all( x + alpha*dx > 0):
   		alpha *= 0.1
   return alpha

# min cx

def solve_lp2(A, b, c, gamma, xstart=None):
	#x = np.ones(A.shape[1])
	#lam = np.zeros(b.shape)
	xsize = A.shape[1]
	if xstart is not None:
		x = xstart
	else:
		#xlam = np.ones(xsize + b.size)
		x = np.ones(xsize) # xlam[:xsize]
		#lam = xlam[xsize:]
	while True :
		print("Iterate")
		H = sparse.bmat( [[ sparse.diags(gamma / x**2)   ,   A.T ],
		                  [ A  ,                         0 ]]  )

		dfdx = c - gamma / x #+  A.T@lam
		dfdlam = A@x - b
		df = np.concatenate((dfdx, dfdlam))#np.zeros(b.size))) # dfdlam))
		#np.concatenate( , A@x - b)
		dxlam = linalg.spsolve(H,df)
		dx = - dxlam[:xsize]
		lam = dxlam[xsize:]

		alpha = linesearch(x,dx)
		x += alpha * dx
		#lam += dlam
		if newtonDec(dfdx,dx) >= -1e-10:
			print("stop")
			break

	return x, lam

def solve_lp(A,b,c, xstart=None):
	gamma = 1.0
	xsize = A.shape[1]
	x = np.ones(xsize)
	for i in range(8):
		x, lam = solve_lp2(A, b, c, gamma, xstart=x)
		gamma *= 0.1
	return x, lam

N = 12
A = np.ones(N).reshape(1,-1)
b = np.ones(1)*2
c = np.zeros(N)
c[0] = -1

#print(solve_lp(A,b,c, 0.000001))
print(solve_lp(A,b,c))

def BB(A, b, c, best, xhint = None):
	picked = np.zeros(xsize)
	picked[pickvar] = 1
	Anew = sparse.hstack((A, picked))
	bnew = np.concatenate((b,choice))
	x, lam =
	np.dot(c,x)
	if lp_solve(Anew, bnew, c) < best:
		best, x = BB(Anew, bnew , c, best, xhint)
	return best, x

'''
#min  cx + gamma * ln(x)
# s.t. Ax = b

# cx + gamma * ln(x) + lambda (Ax - b)

#gradient
delx = c + gamma * 1/x + lambda A
dellam = Ax - b
# hess
dlx = A
dxl = A.T
dxx = - gamma (1/x**2)

H @ (x l) = (delx dell)
'''


import sys
import scipy.io as sio
from pprint import pprint
import matplotlib.pyplot as plt
import numpy as np
from sympy import *
from scipy.optimize import newton

x = Symbol('x')
y = 2* (x**6) + 3 *(x**4) - 10 *(x**3) + 12 * (x**2) - 60 * x

def dx(y, x, x0):
	f = lambdify(x, y, 'numpy')
	return abs(f(np.array(x0)))

def f(x):
    return 2* (x**6) + 3 *(x**4) - 10 *(x**3) + 12 * (x**2) - 60 * x

def df(x):
    return 12 * (x**5) + 12 * (x**3) -30 * (x**3) + 24 * (x**2) - 60

e = 1e-8
x0 = 1
delta = dx(y, x, x0)
x_pred = []
y_pred = []
x_pred.append(x0)
y_pred.append(f(x0))

while delta > e:
	x0 = x0 - f(x0)/df(x0)
	delta = dx(y, x, x0)
	x_pred.append(x0)
	y_pred.append(f(x0))

print('Root: ', x0)
print('f(x): ', f(x0))

xn = newton(f, 1, df, tol=e)
print('Root with scipy newton: ', xn)
print('f(x) with scipy newton: ', f(xn))

plt.figure(figsize=(10, 8))
plt.title("Newton steps", fontsize=16)
plt.scatter(x_pred, y_pred, color = "b")
plt.xlabel('x', fontsize=14)
plt.ylabel('f(x)', fontsize=14)
plt.grid(True)
plt.show()

import numpy,import scipy,from scipy,from sympy


"""
Susceptible-Infected-Recovered (SIR) Model
without demographics (births/deaths) with mortality

References:

* Modeling Infectious Diseases in Humans and Animals,
  M. J. Keeling and P. Rohani,
  Princeton University Press,
  2008
"""

import numpy as np

from scipy import integrate

from matplotlib import pyplot as plt

# Total UK population (2019 estimate)
N = 67545757

def SIR(t, y, R0, gamma, m):
    """
    SIR ODEs

    Parameters
    ----------
    t: double
        Current time
    y: np.ndarray
        Current state
    R0: double
        Basic reproductive ratio
    m: double
        Mortality rate

    Notes
    -----
    Deaths are included in dR/dt (recovered rate) instead of dI/dt (infected rate);
    this is to simulate a delayed death after infection (incubation period).
    For this reason the infected rate is not influenced by the death rate.
    """

    assert 0 <= m <= 1

    # Changes in S, I and R do not depend on R
    S, I = y[0], y[1]

    # Gamma and beta are related by R0
    beta = gamma * R0

    dSdt = -beta * I * S
    dIdt = beta * I * S - gamma * I
    dRdt = gamma * I - m * gamma * I
    dDdt = m * gamma * I  # Deaths

    return np.array([dSdt, dIdt, dRdt, dDdt])

def solve_SIR(I0, tint, R0, gamma, m):
    """
    Solve system of SIR ODEs

    Parameters
    ----------
    I0: int
        Initial number of infected people
    tint: Tuple[double]
        Time interval
    R0: double
        Basic reproductive ratio
    m: double
        Mortality rate
    """

    # Initial susceptible and infected people as population fraction
    S0 = (N - I0) / N
    I0 = I0 / N

    y0 = np.array([S0, I0, 0, 0])

    sir = integrate.solve_ivp(
        lambda t, y: SIR(t, y, R0, gamma, m if m is not None else 0.0),
        tint,
        y0,
        rtol=1e-12,
        atol=1e-12,
    )

    # Check conservation of people
    assert np.allclose(sir.y.sum(axis=0), S0 + I0)

    return sir

def plot_sir(I0, tint, R0, gamma, m=None, tag=None):
    """
    Plot SIR model

    Parameters
    ----------
    I0: int
        Initial number of infected people
    tint: Tuple[double]
        Time interval
    R0: double
        Basic reproductive ratio
    m: double (optional)
        Mortality rate
    tag: str
        Plot tag
    """

    sir = solve_SIR(I0, tint, R0, gamma, m)

    fig = plt.figure()

    plt.plot(sir.t, sir.y[0], label="Susceptible")
    plt.plot(sir.t, sir.y[1], label="Infected")
    plt.plot(sir.t, sir.y[2], label="Recovered")

    if m is not None:
        plt.plot(sir.t, sir.y[3], label="Dead")
    plt.legend()

    plt.title(f"SIR Model ($R_0$  = {R0:.2f} {f', m = {m}' if m is not None else ''})")
    plt.xlabel("Time")
    plt.ylabel("Fraction of Population")

    if tag is None:
        tag = "simple" if m is None else "mortality"
    plt.savefig(f"plots/SIR_{tag}.pdf")
    plt.close(fig)

    return sir.t, sir.y

def plot_infected(I0, tint, R0, gamma, m=None):
    """
    Plot fraction of infected people (SIR model) for different values of R0

    Parameters
    ----------
    I0: int
        Initial number of infected people
    tint: Tuple[double]
        Time interval
    R0: List[double]
        Basic reproductive ratio
    m: double
        Mortality rate
    """

    fig = plt.figure()

    for r0 in R0:
        sir = solve_SIR(I0, tint, r0, gamma, m)

        plt.plot(sir.t, sir.y[1], label=f"$R_0$  = {r0:.2f}")

    plt.legend()
    plt.title(f"SIR Model (Infected)")
    plt.xlabel("Time")
    plt.ylabel("Fraction of Infected Population")

    tag = "simple" if m is None else "mortality"
    plt.savefig(f"plots/SIR_{tag}_R.pdf")
    plt.close(fig)

# Number of infected people in the UK (13/03/2020)
i0 = 1
tint = (0, 40)

# No mortality
plot_sir(I0=i0, tint=tint, R0=2, gamma=1)

# 2.5% mortality
plot_sir(I0=i0, tint=tint, R0=2, gamma=1, m=0.025)

# No mortality
plot_infected(I0=i0, tint=tint, R0=[1.5, 2.0, 2.5, 3.0, 3.5], gamma=1)


# coding: utf-8

# # Pysius - a Python solver for Blasius equation

# In[1]:

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

# Let $f(\eta)$ be the non-dimensional stream function, with $\eta$ being the similarity variable. Blasius equation reads:
#
# $$f''' + \frac{1}{2}ff'' = 0$$
#
# This is a third-order ODE, that can be thus cast to a system of 3 first-order ODEs:
#
# $$\begin{cases}
# f' = u\\
# u'  = \xi\\
# \xi' = -\frac{1}{2}f\xi
# \end{cases}$$
#
# the first two rows representing de-facto the definitions of the additional variables $f$ and $\xi$. Boundary conditions are:
#
# $$f(0)=0$$
# $$u(0)=0$$
# $$\lim_{\eta \to \infty}u(\eta)=\int_0^\infty \xi d\eta = 1$$
#
# A complete set of initial conditions is not available (the value of $\xi$ at $\eta=0$ is not provided). This makes it impossible to solve the equation with an $\eta$-marching scheme (meaning, starting from initial conditions towards higher values of eta), hence they need to be solved nonlinearly. Also, the third condition can be either written as a condition on $u$ or an integral condition on $\xi$; the first option will be used in this script (although the same result can be obtained with the latter).

# The theoretical domain would be $\eta\in[0,\infty)$; a discretisation is needed. Here, $\infty$ is approximated with a high enough value `eta_max`, while the parameter `N` indicates  the number of nodes on which the function is evaluated (including extremes, so including $0$ and `eta_max`. Finally `deta` is the spacing between nodes.
#
# _Hint: try starting from a low number of nodes and a low value for_ `eta_max` _and gradually increase them. Do this until no substantial change in the final solution is observed._

# In[2]:

eta_max = 20 # extreme of domain
N = 1000 # number of nodes

deta = eta_max / (N-1)

# Next up, it has to be decided how to store the unknowns in memory. There are three variables, $f$, $u$ and $\xi$, and $N$ nodes - which adds up to $3N$ unknowns (corresponding to the value of each variable on each node). All of them are stored in a one-dimensional array $y$ of size $3N$:

# In[3]:

y = np.zeros(3*N)

# It is conventionally decided that the first N elements of `y` represent $\xi$ at each of the nodes, namely:
# ```python
# xi = y[0:N] # where xi[n-1] represents xi at the n-th node
# ```
# notice that, when using Python's slicing `0:N`, the first extreme ($0$) is inclusive, the second ($N$) is not. The other variables just follow:
# ```python
# u = y[N:(2*N)]
# f = y[(2*N):(3*N)]
# ```

# Using a forward-Euler discretisation, the equation can be evaluated at each node $n$ as:
#
# $$
# \left(
# \begin{bmatrix}
# \xi_{n+1}\\
# u_{n+1}\\
# f_{n+1}
# \end{bmatrix} -
# \begin{bmatrix}
# \xi_{n}\\
# u_{n}\\
# f_{n}
# \end{bmatrix}
# \right) \frac{1}{\Delta \eta} +
# \begin{bmatrix}
# \frac{1}{2}\xi_{n}f_n\\
# -\xi_{n}\\
# -u_{n}
# \end{bmatrix} = 0
# $$
#
# Notice that such equation cannot be written for $n=N$ (the last cell), as it would require informations from the unexistent cell $N+1$. Therefore, $3(N-1)$ equations can be written on all the other nodes; the remaining 3 equations are provided by boundary conditions, so that the number of equations matches the one of unknowns.
#
# This can be written as a whole as:
#
# $$
# Ay + b(y) = 0
# $$
#
# where $A$ is a matrix, vector $b$ is a function of y. The whole left hand side is thus a vector, where the arrangement of rows is arbitrary; here it is performed so that:
# - the first $N-1$ rows represent the discretised Blasius equation for $\xi$
# - the next row represents the boundary contition for $u(0) = 0$
# - the next $N-1$ rows represent the discretised Blasius equation for $u$
# - the next row represents the boundary condition for $u(\eta\to\infty)$
# - the next row is the boundary condition for $f(0)$
# - the final $N-1$ rows represent the discretised Blasius equation for $f$

# In[4]:

# Keep in mind that when slicing or using "range", the first bound is inclusive
# while the second is not!
# In this way, for instance, range(1,11) produces an array of numbers from 1 to 10,
# which has size 11-1 = 10.

A = np.zeros((3*N, 3*N))

# Blasius equation for xi (derivative of xi)
for ii in range(N-1):
    A[ii,ii] = -1/deta
    A[ii,ii+1] = 1/deta

# boundary condition for u(0)
A[N-1, N] = 1

# Blasius equation for u (derivative of u)
for ii in range(N,2*N-1):
    A[ii,ii] = -1/deta
    A[ii,ii+1] = 1/deta

# boundary conditions for u at etamax ~ infinity
A[2*N-1,2*N-1] = 1

# boundary condition for f(0)
A[2*N,2*N] = 1

# Blasius equation for f (derivative of f)
for ii in range(2*N+1, 3*N):
    A[ii,ii-1] = -1/deta
    A[ii,ii] = 1/deta

# In[5]:

# now, a function returning vector b is defined

def b(y):

    result = np.zeros(3*N)

    # Blasius equation for xi (derivative of xi)
    for ii in range(N-1):
        result[ii] = y[ii]*y[2*N+ii]/2

    # Blasius equation for u (derivative of u)
    for ii in range(N,2*N-1):
        result[ii] = - y[ii-N]

    # boundary conditions for u at etamax ~ infinity
    result[2*N-1] = -1

    # Blasius equation for f (derivative of f)
    for ii in range(2*N+1, 3*N):
        result[ii] = -y[ii-1-N]

    return result

# In[6]:

# Jacobi's matrix for b; this will come in handy later

def db(y):

    result = np.zeros((3*N,3*N))

    # Blasius equation for xi (derivative of xi)
    for ii in range(N-1):
        result[ii,2*N+ii] = y[ii]/2
        result[ii,ii] = y[2*N+ii]/2

    # Blasius equation for u (derivative of u)
    for ii in range(N,2*N-1):
        result[ii,ii-N] = - 1

    # Blasius equation for f (derivative of f)
    for ii in range(2*N+1, 3*N):
        result[ii,ii-1-N] = -1

    return result

# We now have a non-linear equation in the form:
#
# $$ F(y) = Ay + b(y) = 0$$
#
# this can be solved with a Newton-Raphson scheme starting from an initial guess `y0`:
#
# $$ y^{\,k+1} = y^{\,k} - J^{-1}(y^{\,k})\, F(y^{\,k}) $$
#
# where $J$ is the Jacobi's matrix of $F$, while $k$ denotes the iteration of the algorithm. Iterations are stopped when a maximum of iterations `KMAX` is reached or when the infinity norm of the vector of the change between iterations $||y^{\,k+1} - y^{\,k}||_\infty$ is below a threshold value `TOL`.

# In[7]:

# define F and the jacobian J
F = lambda yy : A.dot(yy) + b(yy)
J = lambda yy : A + db(yy)

# In[8]:

# initial condition (linear profiles which respect B.C.)
y0 = np.zeros(3*N)
y0[0:N] = 0.5*np.linspace(1.0,0.0,num=N)*deta
y0[N:2*N] = np.linspace(0,1,num=N)
y0[2*N:3*N] = np.linspace(0,1,num=N)

# In[9]:

KMAX = 100 # maximum number of iterations
TOL = 1e-10 # tolerance

exit4it = True
for ii in range(KMAX):
    dy = - np.linalg.solve(J(y0),F(y0))
    y = y0 + dy
    y0 = y
    print('Iteration {}, maximum absolute value of residual: {}'.format(ii+1,max(abs(F(y)))))
    if max(abs(dy)) < TOL:
        exit4it = False
        print()
        print('Algorithm terminated due to satisfactory residual (number of iterations: {}).'.format(ii+1))
        break
if exit4it:
    print()
    print('Warning: algorithm was terminated for too many iterations;')
    print('converngence might have not been achieved.')

# Now, the solution is extracted and plotted:

# In[10]:

eta = np.linspace(0,eta_max,num=N)
xi = y[0:N]
u = y[N:(2*N)]
f = y[(2*N):(3*N)]

# In[11]:

fig, ax = plt.subplots()
ax.plot(eta,f)
ax.plot(eta,u)
ax.plot(eta,xi)
ax.plot(eta,eta*u-f)
ax.set_xlabel('$\eta$')
ax.set_xlim([0,10])
ax.set_ylim([-0.1,2])
ax.grid()
ax.legend(['$f$', '$u$', '$\\xi$', '$\eta u - f$'])

# # Postprocessing

# Now, the streamlines associated to the Blasius solution are plotted in a $x,z$ plane; these correspond to the isocontours of the dimensional stream function $\psi$. It is thus necessary to first find the values of $f$ in real space (as a function of $x$ and $y$). To do so, a mesh is created; then, the value of $\eta$ corresponding to each couple $(x,y)$ is calculated:
#
# $$ \eta = y\sqrt{\frac{u_\infty}{\nu x}} $$
#
# Knowing $\eta$, it is possible to find the value of $f$ for each $(x,y)$ by interpolation of the Blasius solution; hence, it is also possible to calculate the dimensional stream function $\psi$:
#
# $$ \psi = f\sqrt{\nu u_\infty x} $$

# In[12]:

# first off: define u_inf and nu
# unitary values are arbitrarily chosen
u_inf = 1
nu = 1

# In[13]:

# function that returns eta from x and y
get_eta = lambda x,y : y / math.sqrt(nu*x/u_inf)

# When creating a mesh, one needs to be careful: $x=0$ is __outside__ of the domain for Blasius boundary layer (also, the solution is discontinuous there). Moreover, a value $x=0$ would make $\eta$ undefined (see its definition).

# In[14]:

# mesh settings
x_min = 0.05; x_max = 1 # bounds for x
y_max = 1; # bounds for y (lower bound is assumed to be 0)
nox = 30; noy = 30 # number of points in each direction

# generate mesh
if get_eta(x_min, y_max) > eta_max:
    print('Warning: requested mesh exceeds the calculated values of eta.')
x, y = np.meshgrid(np.linspace(x_min,x_max,num=nox), np.linspace(0,y_max,num=noy))

# In[15]:

# calculate stream function associated to each x, y
psi = np.empty_like(x)
for ii in range(noy):
    for jj in range(nox):
        current_eta = get_eta(x[ii,jj], y[ii,jj])
        psi[ii,jj] = np.interp(current_eta, eta, f) * math.sqrt(nu*u_inf*x[ii,jj])

# In[16]:

fig, ax = plt.subplots()
ax.contour(x,y,psi,levels=[0.02, 0.04, 0.06, 0.08, 0.1, 0.12, 0.14, 0.16])
ax.set_title('Streamlines')
ax.set_xlabel('x')
ax.set_ylabel('y')

# And finally, the non-dimensional velocity field can be found as:
#
# $$ u_x = u_\infty \, f' $$
#
# $$ u_y = \frac{1}{2}\sqrt{\frac{\nu u_\infty}{x}} \, \left(\eta\, f' -f \right) $$
#
# _Notice that this leads to an infinite value of $u_y$ in the limit for $x \to 0$._

# In[17]:

# mesh is here redefined to have better looking graphs
nox = math.floor(nox/4) # number of points in x is divided by 4
noy = math.floor(noy/1.5) # number of points in y is divided by 1.5
x, y = np.meshgrid(np.linspace(x_min,x_max,num=nox), np.linspace(0,y_max,num=noy))

# calculate velocity field associated to each x, y
ux = np.empty_like(x)
uy = np.empty_like(x)
for ii in range(noy):
    for jj in range(nox):
        current_eta = get_eta(x[ii,jj], y[ii,jj])
        ux[ii,jj] = np.interp(current_eta, eta, u) * u_inf
        uy[ii,jj] = (current_eta*np.interp(current_eta, eta, u) - np.interp(current_eta, eta, f)) * math.sqrt(nu*u_inf/x[ii,jj])/2

# In[18]:

fig, ax = plt.subplots()
ax.quiver(x,y,ux,uy)
ax.set_title('Velocity field')
ax.set_xlabel('x')
ax.set_ylabel('y')

# Again, the high values of $u_y$ towards $x \to 0$ are a peculiarity of Blasius' solution; this is a direct consequence of the similarity. The $u_x$ velocity component is assumed to be uniform and equal to $u_\infty$ for $x\leq0$ (as the external solution is assumed to have such shape); in other words, for $x=0$ and $\forall y$, $u_x = u_\infty$. For $x>0$, on the other hand, $u_x$ is given by a self similar solution for the boundary layer; the only way to satisfy both these conditions is to have a zero-thickness of the boundary layer in $x=0$, which also makes sense on a physical standpoint. As $x=0$ is approached, the y-profile of the self similar solution for $u_x$ becomes increasingly flat with an almost constant value of $u_\infty$, as a consequence of the thinner and thinner boundary layer. This can be clearly observed below:

# In[19]:

x, y = np.meshgrid(np.linspace(0.01,x_max,num=nox), np.linspace(0,y_max,num=noy))
ux = np.empty_like(x)
uy = np.empty_like(x)
for ii in range(noy):
    for jj in range(nox):
        current_eta = get_eta(x[ii,jj], y[ii,jj])
        ux[ii,jj] = np.interp(current_eta, eta, u) * u_inf

fig, ax = plt.subplots()
ax.quiver(x,y,ux,0)
ax.set_title('x-component of velocity field')
ax.set_xlabel('x')
ax.set_ylabel('y')

plt.show()

# However, the fact that the boundary layer thickness (from now on indicated with $\delta(x)$) tends to 0 for $x \to 0$ also means that $\eta$ becomes singular there, as it can be written as:
#
# $$ \eta = \frac{y}{\delta(x)} $$
#
# thus $\lim_{x \to 0} \eta = \infty$. Similarly, also the vertical velocity component $u_y$ becomes singular as the term $1/\sqrt{x}$ diverges for $x \to 0$, while the term $\left(\eta\, f' -f \right)$ converges to a constant for $\eta \to \infty$:
#
# $$ u_y = \frac{1}{2}\sqrt{\frac{\nu u_\infty}{x}} \, \left(\eta\, f' -f \right) $$
#
#
# Another issue with the vertical velocity component is that, for any $x$ and for $y \to \infty$, non-zero values are observed due to the term $\left(\eta\, f' -f \right)$ (actually, $u_y$ becomes larger and larger moving away from the wall, until an asymptotic value is found); this does not match the imposed external, irrotational flow, which is assumed to be:
#
# $$\mathbf{U}_{ext} = u_\infty\hat{\mathbf{x}} + 0\,\hat{\mathbf{y}}$$
#
# and hence has a null vertical component. Be aware that, despite the mismatch, Blasius solution is mathematically correct; a possible explanation for the incongruence resides in the non-physical choice of external velocity $\mathbf{U}_{ext}$. This has been calculated by solving Euler's equations with non penetration boundary conditions applied at the plate; meaning, the presence of the boundary layer is not taken into account. In fact, the external solution must phisically be aware of the boundary layer; this can be done numerically by iteratively modifying the external solution with the associated boundary layer one, then calculating a new boundary layer solution. In other words:
# - calculate boundary layer solution associated to external solution;
# - update external solution (which is, calculate a new one) so that it accounts for the presence of the boundary layer previously calculated;
# - repeat until convergence (if convergence is ever obtained!).

# In[ ]:


"""
Functions to sample zero-centered nx x ny random vector with exponential spatial covariance
See https://math.stackexchange.com/questions/163470/generating-correlated-random-numbers-why-does-cholesky-decomposition-work
"""
import argparse

import numpy
from scipy.linalg import cholesky
from scipy.spatial.distance import cdist

def exponential_covariance(distance, length, sigma):
    return numpy.exp(-distance/numpy.float(length)) * sigma**2

def build_covariance_matrix(nx, ny, length, sigma, lx=1.0, ly=1.0):
    dx, dy = lx/nx, ly/ny
    x, y = numpy.meshgrid((numpy.arange(nx)+.5)*dx, (numpy.arange(ny)+.5)*dy)
    coords = list(zip(x.ravel(), y.ravel()))
    distance_matrix = cdist(coords, coords)
    covariance_matrix = exponential_covariance(distance_matrix, length, sigma)
    return covariance_matrix

def generate(nx, ny, length, sigma, lx=1.0, ly=1.0):
    """ generate single realization """
    chol = _chol(nx, ny, length, sigma, lx, ly)
    u = numpy.random.randn(ny, nx).ravel()
    v = chol.dot(u).reshape(ny, nx)
    return v

def batch_generate(nx, ny, length, sigma, lx=1.0, ly=1.0, sample_size=1):
    """ generate multiple realizations """
    chol = _chol(nx, ny, length, sigma, lx, ly)
    ubatch = numpy.random.randn(ny, nx, sample_size).reshape(ny*nx, sample_size)
    vbatch = chol.dot(ubatch)
    vbatch = vbatch.T.reshape(sample_size, ny, nx)
    return vbatch

def _chol(nx, ny, length, sigma, lx, ly):
    covariance_matrix = build_covariance_matrix(nx, ny, length, sigma, lx, ly)
    chol = cholesky(covariance_matrix, lower=True)
    return chol

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--seed', type=int, default=42)
    parser.add_argument('--outfile', default='tmp.npy', help='output filename')
    parser.add_argument('--grid_size', nargs=2, type=int, default=[64, 64], help='resolution of the grid')
    parser.add_argument('--domain_size', nargs=2, type=float, default=[1.0, 1.0], help='default is a unit square')
    parser.add_argument('--length', type=float, default=1.0, help='length scale of exponential covariance')
    parser.add_argument('--sigma', type=float, default=1.0, help='amplitude of exponential covariance')
    parser.add_argument('--sample_size', type=int, default=10)
    args = parser.parse_args()

    numpy.random.seed(args.seed)

    nx, ny = args.grid_size
    lx, ly = args.domain_size

    data = batch_generate(nx, ny, args.length, args.sigma, lx, ly, args.sample_size)
    numpy.save(args.outfile, data)

    print('Generated data saved at {}'.format(args.outfile))


'''
几何中夹角余弦可用来衡量两个向量方向的差异，机器学习中借用这一概念来衡量样本向量之间的差异。

1.二维空间向量的夹角余弦相似度
'''
import numpy as np

def cosn(a, b):
    """
    n维夹角余弦
    """
    sum1 = sum2 = sum3 = 0
    for i in range(len(a)):
        sum1 += a[i] * b[i]
        sum2 += a[i] ** 2
        sum3 += b[i] ** 2
    cos = sum1 / (np.sqrt(sum2) * np.sqrt(sum3))
    return cos

print('a,b 多维夹角余弦距离：', cosn((1, 1, 1, 1), (2, 2, 2, 2)))

def cosn2(a, b):
    """
    n维夹角余弦, 不使用循环
    """
    A, B = np.array(a), np.array(b)
    sum1 = sum(A * B)
    sum2 = np.sqrt(np.sum(A ** 2))
    sum3 = np.sqrt(np.sum(B ** 2))
    cos = sum1 / (sum2 * sum3)
    return cos

print('a,b 多维夹角余弦距离：', cosn2((1, 1, 1, 1), (2, 2, 2, 2)))

print('a,b 二维夹角余弦距离：', cosn((0, 2), (2, 0)))

print(np.pi / 3)
print(np.sin(np.pi / 3))
print(355 / 113)


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

non_decimal = re.compile(r'[^\d]+')

df = pd.read_csv('./data/moore.csv', delimiter='\t', header=None)
x = df[2].apply(
    lambda v: non_decimal.sub('', v.split('[')[0])
)
y = df[1].apply(
    lambda v: non_decimal.sub('', v.split('[')[0])
)

x = x.values.astype(np.int)
y = y.values.astype(np.int)

plt.scatter(x, y)
plt.show()

# Transform y to be more like linear
y = np.log(y)
plt.scatter(x, y)
plt.show()

denominator = x.dot(x) - x.mean() * x.sum()

a = (x.dot(y) - y.mean() * x.sum()) / denominator
b = (y.mean() * x.dot(x) - x.mean() * x.dot(y)) / denominator

y_hat = a * x + b

plt.scatter(x, y)
plt.plot(x, y_hat)
plt.show()

d1 = y - y_hat
d2 = y - y.mean()
r2 = 1 - d1.dot(d1) / d2.dot(d2)
print("a:", a, "b", b)
print('R2', r2)

# log(tc) = a * year + b
# tc = exp(b) * exp(a * year)
# Time to double
# 2*tc = 2 * exp(b) * exp(a * year)
#      = exp(ln(2)) * exp(b) * exp(a * year)
#      = exp(b) * exp(a * year * ln(2))
# exp(b) * exp(a * year2) = exp(b) * exp(a * year1 + ln2)
# a * year2 = a * year1 + ln2
# year2 = year1 + ln2/a
print('time to double:', np.log(2) / a, 'years')


from numpy import *

#points is array of row of data sets

def compute_error_for_given_points(b, m, points):
	totalError = 0
	# i is the row of the data sets
	for i in range(0, len(points)):
		x = points[i, 0]
		y = points[i, 1]
		# **2 means square
		totalError += (y - (m*x + b))**2

	return totalError / float(len(points))

def step_gradient(b_current, m_current, points, learning_rate):
	#gradient descent
	b_gradient = 0
	m_gradient = 0
	#N is number of points
	N = float(len(points))

	#we are gonna up date b and m over time
	for i in range(0,len(points)):
		x = points[i, 0]
		y = points[i, 1]
		b_gradient += -(2/N) * (y - ((m_current * x) + b_current))
		m_gradient += -(2/N) * x * (y - ((m_current * x) + b_current))
	new_b = b_current - (learning_rate * b_gradient)
	new_m = m_current - (learning_rate * m_gradient)
	return [new_b, new_m]

def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations):
	b = starting_b
	m = starting_m

	for i in range(num_iterations):
		b, m = step_gradient(b, m, array(points), learning_rate)

	return [b,m]

def run():
	#data sets will be separated by a comma like an x and y pair
	points = genfromtxt('data.csv', delimiter=',')

	#hyperparameters
	learning_rate = 0.0001

	#y = mx + b (slope formula)
	initial_b = 0
	initial_m = 0
	num_iterations = 1000

	print "Before gradient descent with b = {0}, m = {1}, error = {2}".format(initial_b, initial_m, compute_error_for_given_points(initial_b, initial_m, points))

	[b, m] = gradient_descent_runner(points,initial_b, initial_m, learning_rate, num_iterations)

	print "After gradient descent with b = {0}, m = {1}, error = {2}".format(b, m, compute_error_for_given_points(b, m, points))

if __name__ == '__main__':
	run()


from sympy import symbols, solve, diff, pprint, integrate, ln, lambdify
import matplotlib.pyplot as plt
import numpy as np
import math

# An oil tanker is leaking oil at a rate given in barrels per hour by the function shown below,
# where t is the time in hours after the tanker hits a hidden rock

t = symbols( 't' )
dL = 76 * ln( t + 1 ) / ( t + 1 )

# Time in hours, first day
a, b = 0, 24
day1 = integrate( dL, ( t, a, b ) ).evalf()

round( day1 ) # Round to three decimal places as needed.

solve( integrate( dL, t ), t )

# Time in hours, first day
a, b = 24, 48
day2 = integrate( dL, ( t, a, b ) ).evalf()

round( day2 ) # Round to three decimal places as needed.

# Time in hours, first day
a, b = 10000 * 24, 10001 * 24
day11 = integrate( dL, ( t, a, b ) ).evalf()

round( day11 ) # Round to three decimal places as needed.

# What is the leak doing?
g_xlim = [ 1, 100 ]
g_ylim = [ -5, 15 ]

lam_l = lambdify( t, integrate( dL, t ), np )

x_vals = np.linspace( g_xlim[0], g_xlim[1], 1000, endpoint=True )
y_vals = lam_l( x_vals )
plt.plot( x_vals, y_vals )
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Thu Dec  2 19:35:06 2021

@author: Oliver
"""

"""Linear Algebra Numerical Solving Methods"""

"""Gauss Elimination"""

import numpy as np

def linearsolver(A,b):
    n = len(A)
    #Initialise solution vector as an empty array
    x = np.zeros(n)
    #Join A and use concatenate to form an augmented coefficient matrix
    M = np.concatenate((A,b.T), axis=1)
    for k in range(n):
        for i in range(k,n):
                if abs(M[i][k]) > abs(M[k][k]):
                    M[[k,i]] = M[[i,k]]
                else:
                    pass
                    for j in range(k+1,n):
                        q = M[j][k] / M[k][k]
                        for m in range(n+1):
                            M[j][m] += -q * M[k][m]
    #Python starts indexing with 0, so the last element is n-1
    x[n-1] =M[n-1][n]/M[n-1][n-1]
    #We need to start at n-2, because of Python indexing
    for i in range (n-2,-1,-1):
        z = M[i][n]
        for j in range(i+1,n):
            z = z - M[i][j]*x[j]
        x[i] = z/M[i][i]
    return x

#Initialise the matrices to be solved.
A=np.array([[10., 15., 25],[4., 5., 6],[25, 3, 8]])
b=np.array([[34., 25., 15]])
print(f'Gauss Elimination: {linearsolver(A,b)}')

"""Gaussian Elimination with Partial Pivoting"""
def column(m, c):
    return [m[i][c] for i in range(len(m))]

def row(m, r):
    return m[r][:]

def height(m):
    return len(m)

def width(m):
    return len(m[0])

def print_matrix(m):
    for i in range(len(m)):
        print(m[i])

def gaussian_elimination_with_pivot(m):
# forward elimination
    n = height(m)
    for i in range(n):
        pivot(m, n, i)
        for j in range(i+1, n):
            m[j] = [m[j][k] - m[i][k]*m[j][i]/m[i][i] for k in range(n+1)]
    if (m[n-1][n-1]) == 0:
        raise ValueError('No unique solution')
    # backward substitution
    x = [0] * n
    for i in range(n-1, -1, -1):
        s = sum(m[i][j] * x[j] for j in
        range(i, n))
        x[i] = (m[i][n] - s) / m[i][i]
    return x

def pivot(m, n, i):
    max = -1e100
    for r in range(i, n):
        if max < abs(m[r][i]):
            max_row = r
            max = abs(m[r][i])
        m[i], m[max_row] = m[max_row], m[i]

if __name__ == '__main__':
    #m = [[0,-2,6,-10], [-1,3,-6,5], [4,-12,8,12]]
    #m = [[1,-1,3,2], [3,-3,1,-1], [1,1,0,3]]
    m = [[1,-1,3,2], [6,-6,2,-2], [1,1,0,3]]
    print(f'Guassian pivot:{gaussian_elimination_with_pivot(m)}')


"""
E14_ode_coupled.py

Solving coupled ODEs (Lorentz Attractor)
charles stanier
charles.stanier@gmail.com
Oct 2, 2019

See about the Lorentz Attractor at https://en.wikipedia.org/wiki/Lorenz_system

The behavior of the system depends on the parameters rho, beta, and sigma
It is a coupled nonlinear system of 3 ODEs

derivative function and driver in same file
defining problem parameters as a dictionary
in the driver and passing that to the derivative
scipy.integrate.solve_ivp does not permit passing of
additional arguments, so we use the awkward lambda
construction per https://github.com/scipy/scipy/issues/8352

this will be fixed with scipy version 1.4 but it has not
yet been released

This passing of model parameters through a dictionary
is a good practice to make  your code readable
and keep all of your model specifications in one place.

for engineering problems, you can add notes and/or units to the pinfo dictionary
as well as error codes.  This can keep you organized and minimize chance of mistakes

Log of changes: none so far

"""

import numpy as np
from scipy.integrate import solve_ivp
import pylab as pl

def lorentz_deriv(t, state_vec, pinfo):
  x, y, z = state_vec  # unpack the state vector
  # unpack the problem parameters
  rho = pinfo['rho']
  beta = pinfo['beta']
  sigma = pinfo['sigma']
  dxdt = sigma * (y - x)
  dydt = x * (rho - z) - y
  dzdt = x * y - beta * z
  return [ dxdt, dydt, dzdt ]   # derivatives

# OK here is the driver

# dictionary with problem parameters
pinfo = {  "rho" :   28,
           "sigma":  10,
           "beta"  : 8/3,
           "tspan" : [0, 100],
           "init_state_vec": [ 1, 1, 1 ],  # initial x, y, z
           "npoints": 1E4 }  # set to None to use default evaluation points

# initial condition
init_cond = pinfo['init_state_vec']
tspan = pinfo['tspan']

# if a number of points has been declared then use it
try:
    tvals = np.linspace( tspan[0], tspan[1], pinfo['npoints'] )
except:
    tvals = None

# calling the ODE solver
soltn = solve_ivp( fun=lambda t, svec : lorentz_deriv(t,svec,pinfo), t_eval = tvals, t_span = tspan, y0=init_cond)

# extracting the results
t_plot = soltn.t # time values to plot
x_plot = soltn.y[0,:] # x variable from lorentz
y_plot = soltn.y[1,:] # y variable from lorentz
z_plot = soltn.y[2,:] # z variable from lorentz

# plot the results
pl.plot(t_plot,x_plot,color='black')
pl.plot(t_plot,y_plot,color='red')
pl.plot(t_plot,z_plot,color='blue')
pl.xlabel('time')
pl.ylabel('x, y, z')
pl.legend( [ 'x','y','z' ])
pl.show()

# making a 3D plot
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(x_plot,y_plot,z_plot)
plt.show()


from sympy import symbols, sin, cos, pi, latex, pprint
from sympy.diffgeom import Manifold, Patch, CoordSystem
from sympy.simplify import simplify

from common1 import plot_latex

r, theta = symbols('r, theta')
x, y = symbols('x, y')
m = Manifold('M', 2)
# 定义一个patch
patch = Patch('P', m)
# 为这个patch建立坐标系，一个是笛卡尔，一个是极坐标
rect: CoordSystem = CoordSystem('rect', patch)
polar = CoordSystem('polar', patch)
# print(rect in patch.coord_systems)
# 建立两个坐标系之间的转换关系，逆变换是自动的。
# 这个地方，如果笛卡尔是3维，另一个是2维曲面（但是也用u,v，不用极坐标，那会怎么样呢？）
polar.connect_to(rect, [r, theta], [r*cos(theta), r*sin(theta)])
# t = polar.coord_tuple_transform_to(rect, [1, pi])
# 极坐标存在奇点，r = 0时，其他怎么取都是0

t = polar.coord_tuple_transform_to(rect, [0, pi])
print(t)
t = polar.coord_tuple_transform_to(rect, [2, pi/2])
print(t)
t = rect.coord_tuple_transform_to(polar, [0, 2])
print(t)

# 雅克比矩阵。极坐标到笛卡尔坐标系的变换矩阵函数，在每个点，是可以求出一个具体的矩阵的
# 但整体来说是一个函数。
m = polar.jacobian(rect, [r, theta])
print(m)
plot_latex(latex(m))
# 笛卡尔坐标系到笛卡尔坐标系的雅克比矩阵是单位矩阵
m = rect.jacobian(rect, [x, y])
# plot_latex(latex(m))

p = rect.point([3, 2])
# base_vector返回一个向量场。给定一个点，会返回一个向量？
# v_x = rect.base_vector(0)
# v_y = rect.base_vector(1)
# pprint(v_x(p), v_y(p))

# 返回标量场。标量场的意思是取一个点，返回一个标量值。x y轴可以视为两个天然的标量场，但应该开可以返回其他标量场
x_scalar_field = rect.coord_function(0)

# 返回横坐标
print(x_scalar_field(p))

# base_oneform返回的又是什么？
# pprint(rect.base_oneform(0))


import numpy as np

try:
    import QENSmodels
except ImportError:
    print('Module QENSmodels not found')

def lorentzian(x, scale=1.0, center=0.0, hwhm=1.0):
    r""" Lorentzian model

    Parameters
    ----------
    x: float or list or :class:`~numpy:numpy.ndarray`
        domain of the function

    scale: float
        scale factor. Default to 1.

    center: float
        center of peak. Default to 0.

    hwhm: float
        Half Width at Half Maximum. Default to 1.

    Return
    ------
    float or :class:`~numpy:numpy.ndarray`
        output number or array

    Examples
    --------
    >>> round(lorentzian(1, 1, 1, 1), 3)
    0.318

    >>> round(lorentzian(3., 2., 2., 5.), 3)
    0.122

    >>> result = lorentzian([1, 3.], 1., 1., 1.)
    >>> round(result[0], 3)
    0.318
    >>> round(result[1], 3)
    0.064

    Notes
    -----
    * A Lorentzian function is defined as

    .. math::

       \text{Lorentzian}(x, \text{scale}, \text{center}, \text{hwhm}) =
       \frac{\text{scale}}{\pi} \frac{\text{hwhm}}
       {(x-\text{center})^2+\text{hwhm}^2}

    * Note that the maximum of a Lorentzian is
      :math:`\text{Lorentzian}(\text{center}, \text{scale}, \text{center},
      \text{hwhm})=\frac{\text{scale}}{\pi \text{hwhm}}`.

    * **Equivalence between different implementations**
      ``Lorentzian`` corresponds to the following implementations in
      `Mantid
      <http://docs.mantidproject.org/nightly/fitfunctions/Lorentzian.html>`__
      and
      `DAVE <https://www.ncnr.nist.gov/dave/documentation/pandoc_DAVE.pdf>`__

      +------------------+-----------------+------------------+
      | QENSmodels       | Mantid          |  DAVE            |
      +==================+=================+==================+
      | ``Lorentzian``   | ``Lorentzian``  |  ``Lorentzian``  |
      +------------------+-----------------+------------------+
      | ``scale``        | Amplitude       |  A               |
      +------------------+-----------------+------------------+
      | ``center``       | PeakCentre      |  :math:`x_0`     |
      +------------------+-----------------+------------------+
      | ``hwhm``         | FWHM /2         | W/2              |
      +------------------+-----------------+------------------+

    """
    # Input validation
    x = np.asarray(x)

    if hwhm == 0:
        model = QENSmodels.delta(x, scale, center)
    else:
        model = scale * hwhm / ((x - center)**2 + hwhm**2) / np.pi

    return model

if __name__ == "__main__":
    import doctest
    doctest.testmod()


import numpy as np
import numpy.linalg as la
from io import StringIO

def print_mat(mat):

    stream = StringIO()
    np.savetxt(stream, mat, fmt="%.3f")
    print( stream.getvalue() )

# -----------------------------------

def get_convergent_vector(L, r_0, threshold):

    '''
    :param L: transition matrix
    :param r_0: initial vector
    :param threshold: parameter for convergence condition
    :return: convergent vector
    '''

    r_cur = r_0

    while True:

        r_next = np.matmul(L, r_cur)

        if la.norm(r_next - r_cur) < threshold:
            # check convergence condition is met or not
            break

        r_cur = r_next

    return r_cur

# -----------------------------------

# transition matrix L
L_without_damping = np.array([
    [0,   1/2, 1/3, 0, 0,   0,   0 ],
    [1/3, 0,   0,   0, 1/2, 0,   0 ],
    [1/3, 1/2, 0,   1, 0,   1/3, 0 ],
    [1/3, 0,   1/3, 0, 1/2, 1/3, 0 ],
    [0,   0,   0,   0, 0,   0,   0 ],
    [0,   0,   1/3, 0, 0,   0,   0 ],
    [0,   0,   0,   0, 0,   1/3, 1 ],
])

h, w = L_without_damping.shape

if h != w:
    raise Exception('transition matrix should be a square matrix.')

n = h

# damping factor
d = 0.5

L_with_damping = L_without_damping * d + (1-d)/n * np.ones(n)

# initial vector for r
r_0 = ( np.ones(n) / n ) * 100.0

# ------------------------------------

print_mat(L_without_damping)

result_without_damping = get_convergent_vector(L_without_damping, r_0, threshold=0.01)
print_mat(result_without_damping)

result_with_damping = get_convergent_vector(L_with_damping, r_0, threshold=0.01)
print_mat(result_with_damping)


from math import radians, cos, sin
import numpy as np
from numpy.linalg import inv

def rotation_matrix(theta):
    """Generates a 2-d rotation matrix.

    .. math::
        \\begin{pmatrix} \\cos\\theta & -\\sin\\theta \\\\ \\sin\\theta & \\
        \\cos\\theta \\end{pmatrix}

    Parameters
    ----------
    theta : float
        Angle of rotation in degrees.

    Returns
    -------
    ndarray
        A 2-d rotation matrix.

    """
    theta = radians(theta)
    matrix = np.array([
        [cos(theta), -sin(theta)],
        [sin(theta), cos(theta)]
    ])
    return matrix

def reflection_matrix(orientation=90.):
    """Generates a 2-d reflection matrix.

    .. math::
        \\begin{pmatrix} \\cos 2\\theta & \\sin 2\\theta \\\\ \\sin 2\\theta \\
        & -\\cos 2\\theta \\end{pmatrix}

    Parameters
    ----------
    orientation : float, optional
        Angle line of reflection makes to the x-axis in degrees.

    Returns
    -------
    ndarray
        A 2-d reflection matrix.

    """
    orientation = radians(orientation)
    return np.array([
        [cos(2 * orientation), sin(2 * orientation)],
        [sin(2 * orientation), -cos(2 * orientation)]
    ])

def check_orthogonal(matrix):
    """Checks if `matrix` is orthogonal by equating the inverse and transpose.

    Parameters
    ----------
    matrix : array_like
        An array representing a matrix.

    Returns
    -------
    bool
        True if the matrix is orthogonal, False otherwise.

    """
    matrix_t = np.transpose(matrix)
    matrix_i = inv(matrix)

    if not np.all(np.isclose(matrix_t, matrix_i)):
        return False
    return True

def get_point_group_transformation_matrix(matrix_type, rotation_or_matrix):
    """Generates a matrix from the input

    Parameters
    ----------
    matrix_type : function
        Any function able to return an orthogonal matrix.
    rotation_or_matrix : int, float, array_like
        An object to convert into a matrix. If an int or a float, this will be
        passed to the `matrix_type` creator. If a matrix, it will be checked
        for orthogonality.

    Returns
    -------
    ndarray
        A matrix representing the specified transformation.

    """
    matrix = None
    if matrix_type == "rotation":
        matrix_type = rotation_matrix
    elif matrix_type == "reflection":
        matrix_type = reflection_matrix

    if isinstance(rotation_or_matrix, float) or isinstance(rotation_or_matrix,
                                                           int):
        matrix = matrix_type(rotation_or_matrix)
    elif isinstance(rotation_or_matrix, np.ndarray):
        matrix = rotation_or_matrix
    if not check_orthogonal(matrix):  # Simple check to see if the matrix is a valid rotation matrix.
        raise ValueError(
            "Not a valid point group transformation matrix (non-orthogonal).")
    assert matrix is not None
    return matrix

def get_rotation_matrix(rotation):
    return get_point_group_transformation_matrix("rotation", rotation)

def get_reflection_matrix(orientation):
    return get_point_group_transformation_matrix("reflection", orientation)


#!/usr/bin/env python3
## source: https://machinelearningmastery.com/singular-value-decomposition-for-machine-learning/

from numpy import array
from numpy import diag
from numpy import dot
from numpy import zeros
from scipy.linalg import svd

# Singular-value decomposition
def convert_to_svd(matrix):
    U, s, VT = svd(matrix)
    return U,s,VT

# Reconstruct SVD
def reconstruct_source_from_svd(U,s,VT, row_size, col_size, truncate=False, n_elements=2):
    """
    ## truncate,n_elements flow is also available via TruncatedSVD
    from sklearn.decomposition import TruncatedSVD

    # svd
    svd = TruncatedSVD(n_components=2)
    svd.fit(A)
    result = svd.transform(A)
    print(result)
    """
    # create m x n Sigma matrix
    Sigma = zeros((row_size, col_size))
    if row_size == col_size:
        Sigma = diag(s)
    elif row_size < col_size:
        # populate Sigma with m x m diagonal matrix
        Sigma[:row_size, :row_size] = diag(s)
    elif row_size > col_size:
        # populate Sigma with n x n diagonal matrix
        Sigma[:col_size, :col_size] = diag(s)
    if truncate == True:
        Sigma = Sigma[:, :n_elements]
        VT = VT[:n_elements, :]
        # transform
        T = U.dot(Sigma)
        print(T)
        T = A.dot(VT.T)
        print(T)
    # reconstruct matrix
    return U.dot(Sigma.dot(VT))

if __name__ == "__main__":
    # define main data matrix
    A = array([
        [1, 200, 3, 4],
        [3, 400, 500, 6],
        [500, 6, 7, 800],
    ])
    print("Main Matrix:\n%s" % A)
    print("rows: %s, cols: %s" % (A.shape[0], A.shape[1]))
    U,s,VT = convert_to_svd(A)
    print("\nSVD:\n%s" % s)
    print("%s singular-values" % len(s))
    ReA = reconstruct_source_from_svd(U,s,VT, 3, 4)
    print("\nReconstructed Matrix:\n %s" % A)


# Logistic regression
#%%
# Importing libraries
import numpy as np
import random
from matplotlib import pyplot as plt

#%%
class LogisticRegression():
    def __init__(self):
        self.w = np.random.rand(1,1)
        self.b = np.random.rand(1,1)
        self.J = 0

    def __sigmoid(self, z):
        return 1 / (1 + (np.exp(-1 * z)))

    def __step(self, w, b, X, y, alpha, m, print_params=False):
        """ Implements one step of backpropagation """
        if print_params:
            print("w.shape:",w.shape)
            print("b.shape:", b.shape)
            print("X.shape:", X.shape)
            print("y.shape:", y.shape)
        Z = np.dot(w, X) + b
        # print("Z.shape:", Z.shape)
        A = self.__sigmoid(Z)
        # print("A.shape:", A.shape)
        dZ = A - y
        # print("dZ.shape:", dZ.shape)
        dw = np.dot(dZ, X.T) / m
        db = np.sum(dZ) / m
        w = w - alpha * dw
        b = b - alpha * db
        J = np.sum(-y * np.log(A) - (1-y) * np.log(1-A)) / m
        return w, b, J

    def train(self, X, y, print_params=False):
        """ Trains logsitic regression model - X is N x M matrix and y is 1 x M matrix """
        alpha = 0.01
        m = 1000
        for i in range(100000):
            self.w, self.b, self.J = self.__step(self.w, self.b, X, y, alpha, m)
            if print_params:
                print("J:", self.J)
                print("w:", self.w, "b:", self.b)
                print("========")

    def predict(self, x_tst):
        """ Returns the prediction of the input x_tst - x_tst should be 1 x N vector """
        return self.__sigmoid(np.dot(self.w, x_tst) + self.b)

#%%
# Generating training data
x = np.linspace(1, 10, 1000)[:, np.newaxis].T
y = np.hstack((np.zeros((1,500)), np.ones((1,500))))

# Adding some randomness
y[0,random.randint(0,500)] = 1
y[0,random.randint(500,1000)] = 0
print("X is", x.shape)
print("y is", y.shape)

#%%
# Creating and training the model
model = LogisticRegression()
model.train(x, y)

#%%
# Generating a test example
x1 = np.array([[0.05]])
# Predicting the output
y1 = model.predict(x1)
print("Prediction of", x1, " is", y1)

#%%
# Generating predictions for all input examples X
x_test = np.linspace(1, 10, 100)[:,np.newaxis].T
y_p = model.predict(x_test)

#%%
# Plotting training data
plt.figure(0)
plt.plot(x, y, "r+")
# plt.show()
# Plotting predictions
print("x is", x.shape, "and y_p is", y_p.shape)
print("max of y_p:", np.max(y_p))
plt.plot(x_test, y_p, "bo")
plt.show()


#	...................................................................................
'''
author: Palash Nandi.
'''
#	...................................................................................

import math
import random
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
from collections import Counter
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
import seaborn as sns
import matplotlib.pyplot as plt
from tqdm import tqdm
from numpy import linalg as eigen_decomp

import read_dataset as rd
#	.....................................
def check_ratios(x):
    sum_ = np.sum(x)
    _ = [ i/sum_ for i in x]
    _.sort(reverse=True)

    print(f'The normalized variances are: {_}')

# 	.....................................
def PCA_from_scratch_linalg(df):
	print(f'PCA from scratch using linalg of Numpy')

	mean_arr = df.mean(axis = 0).to_numpy()
	X = np.array(df) - mean_arr
	X_T = X
	X = X.T
	# print(f'X: {X.shape}')
	# print(f'X_T: {X_T.shape}')

	covarience_matrix = np.matmul(X, X_T) / X.shape[1]

	print(f'X: {X.shape}, X_T: {X_T.shape}, cov_mat: {covarience_matrix.shape}\n')
	# print(covarience_matrix)

	lambdas, e_vecs = eigen_decomp.eig(covarience_matrix)
	# print(lambs)
	# Numpy returns the eigen vectors column-wise
	print(f'\nEigen vectors are:\n{np.transpose(e_vecs)}\n')
	check_ratios(lambdas)

# 	.....................................
def PCA_from_scratch_SVD(df):
	print(f'\n\nPCA from scratch using SVD of Numpy')
	mean_arr = df.mean(axis = 0).to_numpy()
	X = np.array(df) - mean_arr
	X_T = X
	X = X.T
	# print(f'X: {X.shape}')
	# print(f'X_T: {X_T.shape}')
	covarience_matrix = np.matmul(X, X_T) / X.shape[1]
	print(f'X: {X.shape}, X_T: {X_T.shape}, cov_mat: {covarience_matrix.shape}\n')
	# print(covarience_matrix)

	u, s, vh = np.linalg.svd(X, full_matrices=True)
	# print(f'u: {u.shape}, s: {s.shape}, vh: {vh.shape}')
	e_vec = []
	for eigenvector in np.transpose(u):
	    e_vec.append(np.dot(eigenvector.T, np.dot(covarience_matrix, eigenvector)))

	# Numpy returns the eigen vectors row-wise
	print(f'\nEigen vectors are:\n{np.transpose(u)}\n')
	check_ratios(e_vec)

# 	.....................................
def PCA_from_scratch_sklearn(df):
	#  PCA needs df in (row_number, features) format
	print(f'\n\nPCA from sklearn')

	eigenvalue_list = []
	mean_arr = df.mean(axis = 0).to_numpy()
	X = np.array(df) - mean_arr
	X_T = X
	X = X.T
	pca = PCA(n_components=X.shape[0])
	pca.fit(X_T)

	# print(pca.explained_variance_ratio_)
	# print(pca.singular_values_)
	# print(pca.components_.shape)

	for eigenvalue, eigenvector in zip(pca.explained_variance_ratio_, pca.components_):
	    eigenvalue_list.append(eigenvalue)

	print(f'\nEigen vectors are:\n{pca.components_}\n')
	print(f'Eigen values: {eigenvalue_list}')

#	.....................................
# dataset_path = 'iris_dataset.csv'
# dataset_path = 'cancer_data.csv'
# dataset_path = 'mall_customer_dataset.csv'
dataset_path = 'wine_quality_dataset.csv'

print('\n\n\n')

df = rd.read_dataset(dataset_path)
PCA_from_scratch_linalg(df)
PCA_from_scratch_SVD(df)
PCA_from_scratch_sklearn(df)

print('\n\n\n')


import numpy

# initialize two array
x = numpy.array([[1, 2], [3, 4]], dtype=numpy.float64)
y = numpy.array([[3, 4], [5, 6]], dtype=numpy.float64)

print('Print the two matrices')
print('X = \n', x)
print('Y = \n', y)

# Elementwise sum; both produce the array
print('\nElementwise addition of two matrices: ( X + Y of Matlab )')
print('Add using add operator: \n', x + y)
print('Add using add function: \n', numpy.add(x, y))

# Elementwise difference; both produce the array
print('\nElementwise subtraction of two matrices: ( X - Y of Matlab )')
print('Subtract using operator: \n', x - y)
print('Subtract using function: \n', numpy.subtract(x, y))

# Elementwise product; both produce the array
print('\nElementwise Multiplication of two matrices: ( X .* Y of Matlab )')
print('Multiply using operator: \n', x * y)
print('Multiply using function: \n', numpy.multiply(x, y))

# Elementwise division; both produce the array
print('\nElementwise division of two matrices: ( X ./ Y of Matlab )')
print('Division using operator: \n', x / y)
print('Division using function: \n', numpy.divide(x, y))

# Elementwise square root; produces the array
print('\nSquare root each element of X matrix\n', numpy.sqrt(x))

# Matrix Multiplication
print('\nMatrix Multiplication of two matrices: ( X * Y of Matlab )')
print(x.dot(y))


#!/usr/bin/env python

'''

@Author: Balasubramanyam Evani
Manipal University Jaipur

Visualization helper script

'''

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib import cm
import scipy.interpolate as interp

##### Objective functions

def f_M(X , Y):
    return -1.0 * ((np.sin(X) * np.power( np.sin( (X**2) / np.pi ) , 20)) + (np.sin(Y) * np.power( np.sin( (2 * Y**2) / np.pi ) , 20)))

def f_R(X, Y):
    return (1 - X)**2 + 100*(Y - X**2)**2

def f_S(X, Y):
    return X**2 + Y**2

def f_A(X, Y):
    return -20 * np.exp(-0.20 * np.sqrt(0.5*(X**2 + Y**2))) - np.exp(0.5*(np.cos(2*np.pi*X) + np.cos(2*np.pi*Y))) + 20 + np.e

####### Surface Plots

def Visualization_Michalewicz():

    X = np.linspace(0 , np.pi , 100)
    Y = np.linspace(0 , np.pi , 100)
    X,Y = np.meshgrid(X , Y)
    Z = f_M(X , Y)
    plt.figure(1)
    ax = plt.axes(projection='3d')
    surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,linewidth=0, antialiased=False)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')

def Visualization_Rosenbrock():

    X, Y = np.meshgrid(np.linspace(-1.3, 1.3, 31), np.linspace(-0.9, 1.7, 31))
    Z = f_R(X, Y)
    plt.figure(1)
    ax = plt.axes(projection='3d')
    surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,linewidth=0, antialiased=False)

def Visualization_Sphere():

    X, Y = np.meshgrid(np.linspace(-1.3, 1.3, 31), np.linspace(-1.3, 1.3, 31))
    Z = f_S(X, Y)
    plt.figure(1)
    ax = plt.axes(projection='3d')
    surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,linewidth=0, antialiased=False)

def Visualization_Ackley():

    X,Y = np.meshgrid(np.linspace(-4, 4, 31), np.linspace(-4, 4, 31))
    Z = f_A(X, Y)
    plt.figure(1)
    ax = plt.axes(projection='3d')
    surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,linewidth=0, antialiased=False)

######## Contour Visualization

def contour_Michalewicz():

    plt.figure(2)
    X = np.linspace(0 , np.pi , 100)
    Y = np.linspace(0 , np.pi , 100)
    X,Y = np.meshgrid(X , Y)
    Z = f_M(X , Y)
    plt.contourf(X,Y,Z)

def contour_Rosenbrock():

    plt.figure(2)
    X, Y = np.meshgrid(np.linspace(-1.3, 1.3, 31), np.linspace(-0.9, 1.7, 31))
    Z = f_R(X, Y)
    plt.contourf(X,Y,Z)

def contour_Sphere():

    plt.figure(2)
    X, Y = np.meshgrid(np.linspace(-1.3, 1.3, 31), np.linspace(-1.3, 1.3, 31))
    Z = f_S(X, Y)
    plt.contourf(X,Y,Z)

def contour_Ackley():

    plt.figure(2)
    X, Y = np.meshgrid(np.linspace(-4, 4, 31), np.linspace(-4, 4, 31))
    Z = f_A(X, Y)
    plt.contour(X,Y,Z)


import numpy as np
from scipy import optimize

from matplotlib import pyplot as plt, cm, colors

def distance_from_center(c, x, y):
    '''
    Distance of each 2D points from the center (xc, yc)

    Parameters
    ----------
    c : array_like
        Coordinates of the center

    x,y : array_like
        Arrays with the x,y coordinates
    '''
    xc = c[0]
    yc = c[1]

    Ri = np.sqrt((x-xc)**2 + (y-yc)**2)

    return Ri - Ri.mean()

def least_square_circle(x, y):
    '''
    Least-square determination of the center of a circle

    Parameters
    ----------
    x,y : array_like
        Arrays with the x,y coordinates of the points on/inside the circle
    '''

    # coordinates of the barycenter
    x_m = np.mean(x)
    y_m = np.mean(y)
    center_estimate = x_m, y_m

    center, ier = optimize.leastsq(distance_from_center, center_estimate, args=(x, y))

    # results
    xc, yc = center
    Ri     = np.sqrt((x-xc)**2 + (y-yc)**2)
    R      = Ri.mean()
    residu = np.sum((Ri - R)**2)

    return xc, yc, R, residu

def plot_data_circle(x, y, xc, yc, R):
    f = plt.figure(0)
    plt.axis('equal')

    theta_fit = np.linspace(-np.pi, np.pi, 180)

    x_fit = xc + R*np.cos(theta_fit)
    y_fit = yc + R*np.sin(theta_fit)
    plt.plot(x_fit, y_fit, 'b-', label="fitted circle", lw=2)
    plt.plot([xc], [yc], 'bD', mec='y', mew=1)
    plt.xlabel('x')
    plt.ylabel('y')

    # plot data
    plt.plot(x, y, 'r-.', label='data', mew=1)

    plt.legend(loc='best', labelspacing=0.1)
    plt.grid()
    plt.title('Least Squares Circle')


import numpy as np
import math
def euclid_distance(X1, X2):
    #return np.linalg.norm(X1, X2)
    sqrsum = 0
    for i in range(len(X1)):
        sqrsum += (X1[i] - X2[i]) ** 2
    return math.sqrt(sqrsum)

class k_means():
    """K defines the number of clusters that the algorithm needs to find.
       X is the training X, and y is the training y,
       num_iters is the number of iterations that needs to perform,
       is_noisy is a boolean to control the stdout"""
    def __init__(self, k, X, y, num_iters, is_noisy, tolerance):
        self.is_noisy = is_noisy
        self.random_initialize(k, X)
        self.num_iters = num_iters
        self.clusters = [ []  for i in range(k)]
        self.X = X
        self.tolerance = tolerance
        if(is_noisy):
            print('Init completed.')

    """This function randomly choose k numbers of samples as the original center.
    """
    def random_initialize(self, k, X):
        #do the random_initialization to randomly select centroids
        rand_indexes = np.arange(k * 2)
        np.random.shuffle(rand_indexes)
        rand_indexes = rand_indexes[:k]
        self.centroids = [ X[i] for i in rand_indexes ]
        if(self.is_noisy):
            print('Randomly Selected Centroids: ')
            print(self.centroids)
    """
    The classify step, where it's updated by choosing the minimun distance to the
    center as it's cluster.
    """
    def classify(self):
        for i in range(self.num_iters):
            self.clusters = [[] for i in range(0, len(self.centroids))]
            for x in self.X:
                distances = [ euclid_distance(x, centroid) for centroid in self.centroids ]
                self.clusters[distances.index(min(distances))].append(x)
            old_centroids = self.centroids
            self.update_centroids()
            if(self.is_noisy):
                print("> epoch {0} completed, new centroids: {1}".format(i, self.centroids_toString()))
            if(self.tolerance!=0):
                if(self.is_noisy):
                    print("Checking tolerance ...")
                if(self.check_tolerance(old_centroids)):
                    break
    """
    Turn the numpy array into string for print.
    """
    def centroids_toString(self):
        string_centroids = []
        for centroid in self.centroids:
            string_centroids.append(np.array2string(centroid, precision=2, separator=',', suppress_small=True))
        return ','.join(string_centroids)

    def train(self):
        self.classify()
        resultX = []
        resultLabel = []
        for i in range(0,len(self.clusters)):
            for item in self.clusters[i]:
                resultX.append(item)
                resultLabel.append(i)
        return (resultX, resultLabel)

    def update_centroids(self):
        for i in range(len(self.clusters)):
            cur_cluster = self.clusters[i]
            self.centroids[i] = np.average(cur_cluster, axis = 0)
    """
    Determine when to stop if tolerance is set, it's determined by the updated distance
    between old centroid and the latest one.
    """
    def check_tolerance(self, old_centroids):
        isOptimal = False
        for i in range(len(self.centroids)):
            isOptimal = isOptimal and np.sum((old_centroids[i] - self.centroids[i]) / self.centroids[i] * 100) < tolerance
        return isOptimal


import numpy as np
import matplotlib.pyplot as plt

# Exercise 1

# Function definition
def myfunc( x ):
   out = np.cos(x) * np.exp(x)
   return out

# Exercise 2

# a) plot function

# create vector for x
x = np.linspace(-2*np.pi, 2*np.pi, 100, endpoint=True);
print x

# call the function
y = myfunc(x);

# plot the data
plt.plot(x, y)
plt.grid()
plt.ylabel('cos(x) * exp(x)')
plt.xlabel('x')

# b) store image
plt.savefig('myfunc.png')
plt.show()

# Exercise 3

# e) fix random seed
np.random.seed(123)

# a) normal distrubution
mu, sigma = 5.0, 2.0
normal_vector = np.random.normal(mu, sigma, 100000)

# b) uniform distribution
uniform_vector = np.random.uniform(0, 10, 100000)

# c) print mean and std. dev.
print np.mean(normal_vector), np.std(normal_vector)
print np.mean(uniform_vector), np.std(uniform_vector)

# d) plot
plt.figure()
count, bins, ignored = plt.hist(uniform_vector, 100, normed=True)
plt.plot(bins, np.ones_like(bins)*0.1, linewidth=2, color='r')

plt.figure()
count, bins, ignored = plt.hist(normal_vector, 100, normed=True)
plt.plot(bins, 1/(sigma * np.sqrt(2 * np.pi)) *
                np.exp( - (bins - mu)**2 / (2 * sigma**2) ),
         linewidth=2, color='r')

plt.show()


# python simulation of mvp transformation

import numpy as np

def get_model_matrix(angle, scale, x, y, z):
    R = np.array([[np.cos(angle), 0, np.sin(angle), 0], [0, 1, 0, 0], [-1*np.sin(angle), 0, np.cos(angle), 0], [0, 0, 0, 1]])
    S = np.array([[scale, 0, 0, 0], [0, scale, 0, 0], [0, 0, scale, 0], [0, 0, 0, 1]])
    T = np.array([[1, 0, 0, x], [0, 1, 0, y], [0, 0, 1, z], [0, 0, 0, 1]])
    TxR = np.dot(T, R)
    TxRxS = np.dot(TxR, S)
    return TxRxS

def get_view_matrix(x, y, z):
    view_matrix = np.array([[1, 0, 0, -1*x], [0, 1, 0, -1*y], [0, 0, 1, -1*z], [0, 0, 0, 1]])
    return view_matrix

def get_projection_matrix(eye_fov, aspect_ratio, z_near, z_far):
    b = 1 / np.tan(eye_fov / 2)
    a = b / aspect_ratio
    z_near = -1 * z_near
    z_far = -1 * z_far
    k = 1 / (z_near-z_far)
    c = (z_near+z_far)*k
    d = 2*z_near*z_far*k
    projection_matrix = np.array([[a, 0, 0, 0], [0, b, 0, 0], [0, 0, c, d], [0, 0, 1, 0]])
    return projection_matrix

if __name__ == "__main__":
    x1, y1, z1 = 2.203125, 4.0859375, 3.5859375
    view_matrix = get_view_matrix(x1, y1, z1)
    print("The view matrix is:\n")
    print(view_matrix)
    print("\n\n################\n\n")

    x2, y2, z2 = 3.315235, 6.59312, 1.23589
    angle = 0.78539815
    scale = 1.4609375
    model_matrix = get_model_matrix(angle, scale, x2, y2, z2)
    print("The model matrix is:\n")
    print(model_matrix)
    print("\n\n################\n\n")

    projection_matrix = get_projection_matrix(1.04719753, 1.6789, 2.203125, 4.0859375)
    print("The projection matrix is:\n")
    print(projection_matrix)
    print("\n\n################\n\n")

    print("The mvp matrix is:\n")
    print(np.dot(np.dot(projection_matrix, view_matrix), model_matrix))


from math import sqrt, pow
from distributions import norm
import scipy.stats as ss

# Cuando la desviacion poblacional es conocidad se recibe average (X barra) (s) que es la desviacion
# n que es tamano de la muestra y (z) que se define dependiendo del coeficiente de confianza deseado
# Seria 1.96 para un coeficiente de confianza del 95%
# 2.58 para un 99%
# 1.64 para 90%
def interval_poblational_average(average, s, n, z):
    standard = s / sqrt(n)
    minimun = average - z*standard
    maximun = average + z*standard
    return (minimun, maximun)

def interval_poblational_average_scipy(average, s, n, confidence_level):
  return ss.norm(average, s / (n**0.5)).interval(confidence_level)

def interval_poblational_average_t(average, deviation, sample_length, alpha):
  t = ss.t.interval(alpha, sample_length - 1)[1]
  standard = deviation / sqrt(sample_length)
  minimun = average - t*standard
  maximun = average + t*standard

  return (minimun, maximun)

def get_interval_p(n, n_special, z):
    p = n_special / n
    sp = sqrt((p * (1 - p)) / n)
    mini = p - z * sp
    maxi = p + z * sp
    return (mini, maxi)

def get_sample_length(z, s, error):
  return (pow(z, 2) * pow(s,2)) / pow(error, 2)

def get_sample_length_p_m(z, variance_p, average_m, average_p):
  return (pow(z, 2) * pow(variance_p, 2)) / (pow(average_m - average_p, 2))

def get_sample_length_p_p(z, proportion_p, proportion_m):
  pi = ( proportion_p ) * ( 1 - proportion_p )
  return (pow(z, 2) * pi) / pow( proportion_m - proportion_p, 2 )

def get_z(coefficient):
  return norm(1, 0, 0, coefficient / 2)


import numpy as np

def calculate(list):
  if len(list) != 9:
    raise ValueError("List must contain nine numbers.")

  else:
    list = np.array(list)

    #print(list)

    #initial declaration

    mean_col = [list[[0, 3, 6]].mean(), list[[1, 4, 7]].mean(), list[[2, 5, 8]].mean()]
    mean_rows = [list[[0, 1, 2]].mean(), list[[3, 4, 5]].mean(), list[[6, 7, 8]].mean()]

    var_col = [list[[0, 3, 6]].var(), list[[1, 4, 7]].var(), list[[2, 5, 8]].var()]
    var_rows = [list[[0, 1, 2]].var(), list[[3, 4, 5]].var(), list[[6, 7, 8]].var()]

    std_col = [list[[0, 3, 6]].std(), list[[1, 4, 7]].std(), list[[2, 5, 8]].std()]
    std_rows = [list[[0, 1, 2]].std(), list[[3, 4, 5]].std(), list[[6, 7, 8]].std()]

    min_col = [list[[0, 3, 6]].min(), list[[1, 4, 7]].min(), list[[2, 5, 8]].min()]
    min_rows = [list[[0, 1, 2]].min(), list[[3, 4, 5]].min(), list[[6, 7, 8]].min()]

    max_col = [list[[0, 3, 6]].max(), list[[1, 4, 7]].max(), list[[2, 5, 8]].max()]
    max_rows = [list[[0, 1, 2]].max(), list[[3, 4, 5]].max(), list[[6, 7, 8]].max()]

    sum_col = [list[[0, 3, 6]].sum(), list[[1, 4, 7]].sum(), list[[2, 5, 8]].sum()]
    sum_rows = [list[[0, 1, 2]].sum(), list[[3, 4, 5]].sum(), list[[6, 7, 8]].sum()]

    #return calculations
    return {
    'mean': [mean_col, mean_rows, list.mean()],
    'variance': [var_col, var_rows, list.var()],
    'standard deviation': [std_col, std_rows, list.std()],
    'max': [max_col, max_rows, list.max()],
    'min': [min_col, min_rows, list.min()],
    'sum': [sum_col, sum_rows, list.sum()]
  }


import numpy as np
from itertools import combinations

def matrix_minor(A, r_indxs, c_indxs):
    '''This function returns the minor of a matrix A with rows indexed by r_indxs and
    columns by c_indxs. r_indxs and c_indxs are lists (or 1D numpy arrays), and these indexes
    start from 0 (which is the first row/column index).'''
    return np.linalg.det(A[np.ix_(r_indxs, c_indxs)]) if len(r_indxs)==len(c_indxs) else None

def compute_MC_matrix( A, p ):
    '''This function computes the p'th order multiplicative
    compound matrix of the given matrix A. It returns the MC
    matrix and the lexicography order (with 0 as the first index)'''
    x = np.arange(np.minimum(*A.shape), dtype=int)  # 0, .., n-1, where n:=min(matrix dimensions)
    lp = np.array(list(combinations(x, p))) # lexicography order of the p inxedes in x
    lp_len = len(lp)
    Q = np.array([matrix_minor(A, lp[r], lp[c]) for r in range(lp_len) for c in range(lp_len)]).reshape(lp_len, lp_len)
    return Q, lp


import sympy as sp
import numpy as np

if __name__ == '__main__':
    x = sp.Symbol('x')
    theta, phi = sp.symbols("theta phi")

    # Let's say we are given a symbolic function f and we want to know his primitive
    f = sp.cos(x + theta) + sp.sin(x + phi)
    print(f"f = {f}")

    # To obtain his primitive we can use the integrate method of sympy. It will compute a symbolic integral
    # with mathematical identities.
    F = sp.integrate(f, x)
    print(f"F = {F}")

    # We also able to obtain his evalution in x for 0 to pi.
    F_0_to_pi = sp.integrate(f, (x, 0, np.pi))
    print(f"F_0_to_pi = {F_0_to_pi}")

    # And we can eval it for theta = 0 and phi = pi/4
    F_0_to_pi_evalf = F_0_to_pi.evalf(subs={theta: 0, phi: np.pi/4})
    print(f"F_0_to_pi_evalf = {F_0_to_pi_evalf}")

    # Another way to find the primitive of f is to create an Integral object
    F = sp.Integral(f, x)
    print(f"F = {F}")

    # After we can find the primitive
    F = F.doit()
    print(f"F = {F}")

    # Let's say we are given another function with two independant variables
    y = sp.Symbol('y')
    g = sp.sin(x + theta)*sp.cos(y + phi) + sp.tan(x + theta) + sp.acos(y + phi)
    G = sp.integrate(g, x, y)
    print(f"G = {G}")
    # We are able to do a multiple definite integral
    G_evalf = sp.integrate(g, (x, 0, np.pi), (y, 0, 2*np.pi)).evalf(subs={theta: np.pi/2, phi: np.pi/6})
    print(f"G_evalf = {G_evalf}")


# Demonstrate criterion of Chauvenet to exclude poor data
from scipy.special import erfc

import numpy as np
import pandas as pd

def chauvenet(y, mean=None, stdv=None) -> np.Array:
    """
    #-----------------------------------------------------------
    # Input:  NumPy arrays x, y that represent measured data
    #         A single value of a mean can be entered or a
    #         sequence of means with the same length as
    #         the arrays x and y. In the latter case, the
    #         mean could be a model with best-fit parameters.
    # Output: It returns a boolean array as filter.
    #         The False values correspond to the array elements
    #         that should be excluded
    #
    # First standardize the distances to the mean value
    # d = abs(y-mean)/stdv so that this distance is in terms
    # of the standard deviation.
    # Then the  CDF of the normal distr. is given by
    # phi = 1/2+1/2*erf(d/sqrt(2))
    # Note that we want the CDF from -inf to -d and from d to +inf.
    # Note also erf(-d) = -erf(d).
    # Then the threshold probability = 1-erf(d/sqrt(2))
    # Note, the complementary error function erfc(d) = 1-erf(d)
    # So the threshold probability pt = erfc(d/sqrt(2))
    # If d becomes bigger, this probability becomes smaller.
    # If this probability (to obtain a deviation from the mean)
    # becomes smaller than 1/(2N) than we reject the data point
    # as valid. In this function we return an array with booleans
    # to set the accepted values.
    #
    # use of filter:
    # xf = x[filter]; yf = y[filter]
    # xr = x[~filter]; yr = y[~filter]
    # xf, yf are cleaned versions of x and y and with the valid entries
    # xr, yr are the rejected values from array x and y
    #-----------------------------------------------------------
    source: https://www.astro.rug.nl/software/kapteyn/
    :param x:
    :param y:
    :param mean:
    :param stdv:
    :return:
    """
    if mean is None:
        mean = y.mean()           # Mean of incoming array y
    if stdv is None:
        stdv = y.std()            # Its standard deviation

    N = len(y)                   # Lenght of incoming arrays
    criterion = 1.0/(2*N)        # Chauvenet's criterion
    d = abs(y-mean)/stdv         # Distance of a value to mean in stdv's
    d /= 2.0**0.5                # The left and right tail threshold values

    prob = erfc(d)               # Area normal dist.
    mask = prob >= criterion   # The 'accept' filter array with booleans

    return mask                # Use boolean array outside this function

def reject_outliers(data: pd.Series) -> pd.Series:
    return data[chauvenet(data)].reset_index(drop=True)


# Assignment on normal distribution

import numpy as np
from scipy.stats import norm

'''
1.Find the area under the standard normal curve which lie
a)To the right of Z=2.70
b)To the left of Z=1.73
c)To the right of Z=-0.66
d)To the left of Z=-1.88
e)Between Z=-0.90 and Z=-1.85
f)Between Z=-1.45 and Z=1.45
g)Between Z=-0.90 and Z=1.58
'''
print("Assignment 1")

print("Area to the right of z=2.70:",1-norm.cdf(x=2.70))
print("Area to the left of z=1.73:",norm.cdf(x=1.73))
print("Area to the right of z=-0.66:",1-norm.cdf(x=-0.66))
print("Area to the left of z=-1.88:",norm.cdf(x=-1.88))
print("Area Between Z=-0.90 and Z=-1.85:",norm.cdf(x=-0.90)-norm.cdf(x=-1.85))
print("Area Between Z=-1.45 and Z=1.45:",norm.cdf(x=1.45)-norm.cdf(x=-1.45))
print("Area Between Z=-0.90 and Z=1.58:",norm.cdf(x=1.58)-norm.cdf(x=-0.90))

print("\n")

'''
2.The life of a certain kind of electronic device has a mean of 300 hours and a standard deviation of 25 hours. Assuming that the distribution of life times which are measured to the nearest hours can be approximated closely with a normal curve
a)Find the probability that any one of these devices will have a lifetime of more than 350 hours.
b)What percentage will have life time from 220 to 260 hours?
'''
print("Assignment 2")

print("Probability of lifetime More than 350 Hours:",
        1 - norm.cdf(x=350,loc=300,scale=25))
print("Percentage of lifetime from 220 to 260 Hours:",
        (norm.cdf(x=260,loc=300,scale=25)\
        -norm.cdf(x=220,loc=300,scale=25))*100)
print("\n")

'''
3.The customer accounts of a certain departmental store have an average balance of Rs.120 and standard deviation of Rs.40. Assuming that the account balances are normally distributed, find
a)What proportion of accounts is over Rs.150 ?
b)What proportion of accounts is between Rs.100 and Rs.150?
c)What proportion of accounts is between Rs.60 and Rs.90?
'''
print("Assignment 3")

print("Proportion of accounts is over Rs.150:",\
        1 - norm.cdf(x=150,loc=120,scale=40))
print("Proportion of accounts is between Rs.100 and Rs.150:",\
        norm.cdf(x=150,loc=120,scale=40) - norm.cdf(x=100,loc=120,scale=40))
print("Proportion of accounts is between Rs.60 and Rs.90:",\
        norm.cdf(x=90,loc=120,scale=40) - norm.cdf(x=60,loc=120,scale=40))

print("\n")


import numpy as np
import matplotlib.pyplot as plt
PI = np.pi

# =========================define sinc
# ---------------normalized

def sinc1(x):
    PI = np.pi
    x = np.array(x)
    y = np.where(np.abs(PI * x) < 1e-38, 1.0, np.sin(PI * x) / (PI * x))
    return y

def sinc_interpolation(x, t, T):
    ns = np.arange(x.size)
    print(ns, "============")

    y = []
    for tt in t:
        y.append(np.sum(x * sinc1((tt - ns * T) / T)))

    return np.array(y)

# =========================test sinc definition
f0 = 100

Ns = 2000
Tp = 20.0 / Ns
t = np.linspace(-10, 10, Ns)
t2 = np.linspace(-10, 10, Ns * 2)
y1 = sinc1(t / Tp)

x = np.sin(2 * PI * f0 * t)
print(x.shape)

y = sinc_interpolation(x, t2, Tp)

print(y.shape, "===")

yfft = np.fft.fftshift(np.fft.fft(y))

plt.figure()
plt.subplot(131)
plt.plot(t, x, '^b')
plt.plot(t2, y, '+r')
plt.legend(['original', 'sinc interpolated'])
plt.title('sinc(t/Tp), ' + "Tp=" + str(Tp))
plt.xlabel('Time/s')
plt.ylabel('Amplitude')
plt.grid()

plt.show()


import sys
import math
import numpy as np
import scipy as sp
from scipy import linalg

A = np.array([
    [-2,1,0,1,0,0,0,0,0],
    [1,-3,1,0,1,0,0,0,0],
    [0,1,-2,0,0,1,0,0,0],
    [1,0,0,-3,1,0,1,0,0],
    [0,1,0,1,-4,1,0,1,0],
    [0,0,1,0,1,-3,0,0,1],
    [0,0,0,1,0,0,-2,1,0],
    [0,0,0,0,1,0,1,-3,1],
    [0,0,0,0,0,1,0,1,-2]])
b = np.array([1, 1, 1, 1, 0, -1, -1, -1, -1])

# Provide some information: not necessary to the method
print("# matrix:")
for r in A:
    print("   ", r)
print("  eigenvalues:", ", ".join(["{:.6g}".format(e) for e in np.linalg.eigvals(A)]))
rankA = np.linalg.matrix_rank(A)
print("  rank:", rankA)

# Verify that problem is well-posed when nullspace has dimension 1
if rankA == len(b) - 1:
    kerA = [a[0] for a in sp.linalg.null_space(A)]
    print("  nullspace: <", kerA, '>')
    print("  kerA . b:", kerA @ b)

##### Actual solver starts here #####

# Initialize containers and values
print("# intialization:")
x = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0.])
print("  initial guess:", x)

residual = b - A @ x
rms2 = residual @ residual
print("  RMS error:", math.sqrt(rms2))

direction = residual
print("  new direction:", direction)

# Iterate
for k in range(min(A.shape)):
    kp1 = k + 1
    print("# iteration:", kp1)

    # Compute step
    alpha = rms2 / (direction @ A @ direction)
    print("  alpha[{}]: {}".format(k, alpha))

    # Update solution
    x += alpha * direction
    print("  updated solution:", x)

    # Update residual
    residual = b - A @ x
    new_rms2 = residual @ residual
    print("  RMS error:", math.sqrt(new_rms2))

    # Terminate early when possible
    if new_rms2 < 1e-6:
        print("# CG converged to solution: {} with residual norm: {}".format(
            x, math.sqrt(new_rms2)))
        sys.exit(0)

    # Compute new direction
    beta = new_rms2 / rms2
    print("  beta[{}]: {}".format(k, beta))
    direction = residual - beta * direction
    print("  new direction:", direction)

    # Update residual squared L2
    rms2 = new_rms2


from math import exp, cos

from numpy import polynomial, ndarray

def f(_x: float) -> float:
    return exp(-_x ** 2) * cos(_x)

def fGauss(a: float, b: float, n: int) -> float:
    result = 0
    for i in range(n):
        x = ((b + a) + (b - a) * t[i]) / 2
        result += + A[i] * f(x)
    return (b - a) / 2 * result

def fSimpson(a: float, b: float, z: int) -> float:
    # formula Cotes
    h = (b - a) / z
    _n = int(z / 2)
    result = 0
    for i in range(_n):
        x = h * (2 * i) + a
        result += 2 * f(x)
    for j in range(_n):
        x = h * (2 * j + 1) + a
        result += 4 * f(x)
    result += f(a) + f(b)
    return result * h / 3

if __name__ == '__main__':
    # iteration counter C
    C = 0
    n = 6
    # t - Legendre points, A - Legendre weights
    t: ndarray = polynomial.legendre.leggauss(n)[0]
    A: ndarray = polynomial.legendre.leggauss(n)[1]
    print(f"Функция: f(x)=exp(-x^2)*cos(x), a={-float('inf')},b={float('inf')}")
    print(f"Результат полученный методом Гаусса:"
          f" {round(fGauss(-1.97, 1.97, n), n)}")
    # Simpson variables
    eps = 0.001
    a, b = -10, 10
    n = 2
    R1, R2 = 0, fSimpson(a, b, n)
    while (abs(R1 - R2) / R2) > eps:
        n += 2
        R1, R2 = R2, fSimpson(a, b, n)
        C += 1
    print(f"Результат полученный методом Симпсона:"
          f" {round(R2, 6)}, кол-во интервалов: {C + 1}")


def lorenz_system_solver(h, N, s=10.0, r=28.0, b=8.0/3.0):
    # Need one more for the initial values
    empty_vector  = lambda size   : zeros((size + 1,), dtype=float)
    lorenz_system = lambda x, y, z: (s*(y - x), x*(r -z) - y , x*y - b*z)
    next          = lambda p_old, slope   : p_old + (slope * h) # euler's Method
    x, y, z       = empty_vector(N), empty_vector(N), empty_vector(N)

    x[0], y[0], z[0] = -14.0, -15.0, 20.0
    for i in range(1, N+1):
        dx  , dy  , dz   = lorenz_system(x[i-1], y[i-1], z[i-1])
        x[i], y[i], z[i] = next(x[i-1], dx), next(y[i-1], dy), next(z[i-1], dz)
    return x, y, z

def plot_lorenz_system_solution(x, y, z):
    ax = plt.figure().gca(projection='3d')
    ax.plot(x, y, z, 'co',lw=2.1)
    ax.plot(x, y, z, 'm--',lw=.5)
    ax.set_xlabel("X")
    ax.set_ylabel("Y")
    ax.set_zlabel("Z")
    ax.set_title("Lorenz System Solution")
    plt.show()

if __name__ == "__main__":
    from numpy import zeros
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    x, y, z = lorenz_system_solver(0.01, 10000)
    plot_lorenz_system_solution(x, y, z)


import numpy as np

class Kmeans():
    '''
        Clusters data using the K-means algorithm.
    '''
    def __init__(self, n_clusters=3, max_iterations=2000, init=None):
        self.n_clusters = n_clusters;
        self.max_iterations = max_iterations;
        self.init = None;

    def _init_centroids(self, X):
        '''
        We have two ways to initialize the initial centroids.
        Default initialization: Chooses the centroids by selecting K random points.
        kmeans++ initialization: Chooses the centroids so that they are spread out as much as possible.
        :param X: Data to cluster
        :return: Inicial centroids
        '''
        m, n = X.shape;
        if (self.init is None):
            rand_ind = np.random.choice(m, self.n_clusters, replace=False);
            return X[rand_ind];
        elif (self.init == 'Kmeans++'):
            centroids = np.full((self.n_clusters, n), np.inf);
            rand_ind = np.random.choice(m);
            centroids[0] = X[rand_ind];
            for i in range(1, self.n_clusters):
                dists = np.array([np.linalg.norm(x - centroids[self._closest_centroid(x, centroids)]) for x in X]);
                norm = dists / np.sum(dists);
                rand_ind = np.random.choice(m, p=norm);
                centroids[i] = X[rand_ind];
            return centroids[i];

    #Given the clusters calculate the new centroids.
    def _calculate_centroids(self, X, clusters):
        return np.array([X[cluster].mean(axis=0) for cluster in clusters]);

    #Given the centroids calculate the new clusters.
    def _create_clusters(self, X, centroids):
        clusters = [[] for i in range(self.n_clusters)];

        for x_ind, x in enumerate(X):
            closest_centroid = self._closest_centroid(x, centroids);
            clusters[closest_centroid].append(x_ind);

        return clusters;

    #Finds the centroid that is closest to x.
    def _closest_centroid(self, x, centroids):
        return np.argmin(np.linalg.norm(x - centroids, axis=1))

    #Calculates the distance between each point in X with all the centroids.
    def transform(self, X):
        return np.array([np.linalg.norm(x - self.centroids_, axis=1) for x in X]);

    def fit(self, X):
        m = len(X);
        centroids = self._init_centroids(X);
        for i in range(self.max_iterations):
            prev_centroids = centroids;
            clusters = self._create_clusters(X, prev_centroids);
            centroids = self._calculate_centroids(X, clusters);
            if not (centroids - prev_centroids).any():
                break;

        self.centroids_ = centroids;
        clusters = self._create_clusters(X, centroids)
        self.labels_ = np.zeros(m, int);
        for cluster_ind, cluster in enumerate(clusters):
            for x_ind in cluster:
                self.labels_[x_ind] = cluster_ind;

        dists = self.transform(X);

        #The inertia is a way to measure the performance of a clustering algorithm.
        #It is the squared sum of the distances of each point to its corresponding cluster.
        self.inertia_ = np.sum(dists[range(m), self.labels_] ** 2)


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

# code starts here
df = pd.read_csv(path)
total = len(df)
print('Total is ', total)
p_a = len(df[df['fico'] > 700]) / total
print('Probability p(A) for the event that fico is ', p_a)
p_b = len(df[df['purpose'] == 'debt_consolidation']) / total
print('Probabilityp(B) for the event that purpose is ', p_b)
df1 = df[df['purpose'] == 'debt_consolidation'].copy()
total_ab = len(df1)
print('Length of new dataframe is ', total_ab)
p_a_b = len(df1[df1['fico'] > 700]) / total_ab
print('Probablity p(B|A) for the event purpose is ', p_a_b)
result = p_a_b == p_a
print(result)
# code ends here

# --------------
# code starts here
#print(df)
prob_lp = len(df[df['paid.back.loan'] == 'Yes']) / len(df)
print('Probability p(A) for the event that paid.back.loan is ', prob_lp)
prob_cs = len(df[df['credit.policy'] == 'Yes']) /len(df)
print('Probability p(B) for the event that credit.policy is ', prob_cs)
new_df = df[df['paid.back.loan'] == 'Yes']
prob_pd_cs = len(new_df[new_df['credit.policy'] == 'Yes']) / len(new_df)
print('Probablity p(B|A) for the event paid.back.loan is ', prob_pd_cs)
bayes = prob_pd_cs * prob_lp / prob_cs
print('Conditional probability of the event P(A|B) is ',bayes)
# code ends here

# --------------
# code starts here
#print(df)
#value_pur = df['purpose'].value_counts()
value_pur = df.groupby(['purpose']).size()
value_pur.plot(kind='bar')
plt.show()

df1 = df[df['paid.back.loan'] == 'No'].copy()
new_value_pur = df1.groupby(['purpose']).size()
new_value_pur.plot(kind='bar')
plt.show()
# code ends here

# --------------
# code starts here
inst_median = df['installment'].median()
print('Median for installment is ', inst_median)
inst_mean = df['installment'].mean()
print('Mean for installment is ', inst_mean)
plt.hist(df['installment'], normed=True, bins=30)
plt.axvline(inst_median, color='k', linestyle='dashed', linewidth=1)
plt.axvline(inst_mean, color='g', linestyle='dashed', linewidth=1)
plt.show()

incm_median = df['log.annual.inc'].median()
print('Median for log.annual.inc is ', incm_median)
incm_mean = df['log.annual.inc'].mean()
print('Mean for log.annual.inc is ', incm_mean)
plt.hist(df['log.annual.inc'], normed=True, bins=30)
plt.axvline(incm_median, color='k', linestyle='dashed', linewidth=1)
plt.axvline(incm_mean, color='g', linestyle='dashed', linewidth=1)
plt.show()
# code ends here


import numpy as np
from numpy.linalg import inv

def plu(A):
    n = A.shape[0]

    P = np.eye(n, dtype=float)
    L = np.eye(n, dtype=float)
    U = A.copy()

    for i in range(n):
        # pivoting, row swapping, change P if needed
        max_el = abs(U[i, i])
        max_row = i
        for k in range(i + 1, n):
            pivot = abs(U[i, i])
            if pivot > max_el:
                max_el = pivot
                max_row = k

        if max_row != i:
            U[[max_row, i]] = U[[i, max_row]]
            P[[max_row, i]] = P[[i, max_row]]

        # forward loop, fill L and U
        factor = U[i + 1:, i] / U[i, i]
        L[i + 1:, i] = factor
        U[i + 1:] -= factor[:, np.newaxis] * U[i]

    return P, L, U

def forward_substitution(L, b):
    n = L.shape[0]

    y = np.zeros(n, dtype=np.float)
    for i in range(n):
        y[i] = b[i] - np.dot(L[i, :i], y[:i])

    return y

def back_substitution(U, y):
    n = U.shape[0]

    x = np.zeros(n)
    for i in range(n - 1, -1, -1):
        x[i] = (1. / U[i, i]) * (y[i] - np.dot(U[i, i:], x[i:]))

    return x

def inverse(A: np.ndarray) -> np.ndarray:
    """
    Computes the inverse of the A matrix, using the LU decomposition and
    solving linear equations to solve AX = B, where X is the inverse of A and
    B is an identity matrix.

    :param A: square matrix of shape (n, n)
    :return: inverse of X, it it is not singular (ValueError then)
    """
    A = A.astype(float)

    P, L, U = plu(A)
    n = A.shape[0]

    if (np.diagonal(U) == 0).any():
        raise ValueError("A is a singular value")

    A_inv = np.zeros(A.shape, dtype=float)

    for i in range(n):
        b = np.zeros(n)
        b[i] = 1

        y = forward_substitution(L, P @ b)
        x = back_substitution(U, y)

        A_inv[:, i] = x

    return A_inv

if __name__ == '__main__':
    A = np.array([[2, 2, 3],
                  [4, 5, 6],
                  [7, 8, 9]])

    print(inverse(A))


import sympy as sym

class TaylorSeries:
    """Class for symbolic Taylor series."""
    def __init__(self, f, num_terms=4):
        self.f = f
        self.N = num_terms
        # Introduce symbols for the derivatives
        self.df = [f]
        for i in range(1, self.N+1):
            self.df.append(sym.Symbol('D%d%s' % (i, f.name)))

    def __call__(self, h):
        """Return the truncated Taylor series at x+h."""
        terms = self.f
        for i in range(1, self.N+1):
            terms += sym.Rational(1, sym.factorial(i))*self.df[i]*h**i
        return terms

class DiffOp:
    """Class for discrete difference operators."""
    def __init__(self, f, independent_variable='x',
                 num_terms_Taylor_series=4):
        self.Taylor = TaylorSeries(f, num_terms_Taylor_series)
        self.f = self.Taylor.f
        self.h = sym.Symbol('d%s' % independent_variable)

        # Finite difference operators
        h, f, f_T = self.h, self.f, self.Taylor # short names
        theta = sym.Symbol('theta')
        self.diffops = {
            'Dtp':  (f_T(h) - f)/h,
            'Dtm':  (f - f_T(-h))/h,
            'Dt':   (f_T(h/2) - f_T(-h/2))/h,
            'D2t':  (f_T(h) - f_T(-h))/(2*h),
            'DtDt': (f_T(h) - 2*f + f_T(-h))/h**2,
            'barDt': (f_T((1-theta)*h) - f_T(-theta*h))/h,
            }
        self.diffops = {diffop: sym.simplify(self.diffops[diffop])
                        for diffop in self.diffops}

        self.diffops['weighted_arithmetic_mean'] = \
             self._weighted_arithmetic_mean()
        self.diffops['geometric_mean'] = self._geometric_mean()
        self.diffops['harmonic_mean'] = self._harmonic_mean()

    def _weighted_arithmetic_mean(self):
        # The expansion is around n*h + theta*h
        h, f, f_T = self.h, self.f, self.Taylor
        theta = sym.Symbol('theta')
        f_n = f_T(-h*theta)
        f_np1 = f_T((1-theta)*h)
        a_mean = theta*f_np1 + (1-theta)*f_n
        return sym.expand(a_mean)

    def _geometric_mean(self):
        h, f, f_T = self.h, self.f, self.Taylor
        f_nmhalf = f_T(-h/2)
        f_nphalf = f_T(h/2)
        g_mean = f_nmhalf*f_nphalf
        return sym.expand(g_mean)

    def _harmonic_mean(self):
        h, f, f_T = self.h, self.f, self.Taylor
        f_nmhalf = f_T(-h/2)
        f_nphalf = f_T(h/2)
        h_mean = 2/(1/f_nmhalf + 1/f_nphalf)
        return sym.expand(h_mean)

    def D(self, i):
        """Return the symbol for the i-th derivative."""
        return self.Taylor.df[i]

    def __getitem__(self, operator_name):
        return self.diffops.get(operator_name, None)

    def operator_names(self):
        """Return all names for the operators."""
        return list(self.diffops.keys())

def truncation_errors():
    # Make a table
    u, theta = sym.symbols('u theta')
    diffop = DiffOp(u, independent_variable='t',
                    num_terms_Taylor_series=5)
    D1u = diffop.D(1)   # symbol for du/dt
    D2u = diffop.D(2)   # symbol for d^2u/dt^2
    print('R Dt:', diffop['Dt'] - D1u)
    print('R Dtm:', diffop['Dtm'] - D1u)
    print('R Dtp:', diffop['Dtp'] - D1u)
    print('R barDt:', diffop['barDt'] - D1u)
    print('R DtDt:', diffop['DtDt'] - D2u)
    print('R weighted arithmetic mean:', diffop['weighted_arithmetic_mean'] - u)
    print('R arithmetic mean:', diffop['weighted_arithmetic_mean'].subs(theta, sym.Rational(1,2)) - u)
    print('R geometric mean:', diffop['geometric_mean'] - u)
    dt = diffop.h
    print('R harmonic mean:', (diffop['harmonic_mean'] - u).\
          series(dt, 0, 3).as_leading_term(dt))

if __name__ == '__main__':
    truncation_errors()


import functional as F
import math
import copy
from sympy import Symbol, pprint, solve

def shape(matrix):
    if not F.is_sequence(matrix): return None

    shapes = []
    acc_matrix = matrix

    while True:
        matrix_length = len(acc_matrix)
        shapes.append(matrix_length)

        if not F.is_sequence(acc_matrix[0]):
            return shapes
        acc_matrix = acc_matrix[0]

def mat_mul(X, Y):
    shape_X, shape_Y = shape(X), shape(Y)

    if shape_X[1] != shape_Y[0]:
        error_message = "Shape of X [1] and B [0] must be same. X: {}, Y: {}".format(shape_X, shape_Y)
        raise ValueError(error_message)

    return [[sum(a*b for a, b in zip(X_row,Y_col)) for Y_col in zip(*Y)] for X_row in X]

def mat_add(X, Y):
    shape_X, shape_Y = shape(X), shape(Y)

    if shape_X != shape_Y:
        error_message = "Shape of X and B must be same. X: {}, Y: {}".format(shape_X, shape_Y)
        raise ValueError(error_message)

    return [x + y for x, y in zip(X, Y)] if len(shape_X) == 1 \
        else [mat_add(x, y) for x, y in zip(X, Y)]

def sca_mul(a, X):
    shape_X = shape(X)

    return [a * x for x in X] if len(shape_X) == 1 \
        else [sca_mul(a, x) for x in X]

def linear_combination(V, basis):
    len_V = shape(V)[0]
    shape_basis = shape(basis)

    if F.any(shape_basis, lambda b: b != len_V):
        error_message = "Length of V and basis must be same.";
        raise ValueError(error_message)

    S = [Symbol('S{}'.format(i)) for i in range(len_V)]
    expr = [F.reduce([S[j] * basis[i][j] for j in range(len_V)], lambda x,y: x+y, -V[i]) for i in range(len_V)]
    solution = solve(expr, dict=True)

    if len(solution) == 0:
        error_message = "There is linear independent basis.";
        raise ValueError(error_message)

    s = solution[0]
    return [s[S[i]] for i in range(len_V)]

def is_linear_independent(basis):
    shape_basis = shape(basis)
    V = F.reduce(basis, lambda a,b: mat_add(a,b), [0 for _ in range(0,shape_basis[0])])

    try:
        return len(linear_combination(V, basis)) > 0
    except ValueError:
        return False

def determinant(A, x = -1):
    shape_A = shape(A)
    len_A = shape_A[0]

    if len_A != shape_A[1]:
        error_message = "Matrix A must be square matrix : {}".format(shape_A)
        raise ValueError(error_message)

    if len_A == 1:
        return A[0][0]

    if x == -1:
        result = 0
        sign = -1
        for i in range(len_A):
            sign *= -1
            result += determinant(A, i) * sign
        return result
    else:
        return A[0][x] * determinant(adj_sub_matrix(A, x))

def adj_sub_matrix(A, x, y = 0):
    shape_A = shape(A)
    len_A = shape_A[0]

    if len_A != shape_A[1]:
        error_message = "Matrix A must be square matrix : {}".format(shape_A)
        raise ValueError(error_message)

    return [[value for i, value in enumerate(row) if i != x] for j, row in enumerate(A) if j != y]

def mat_transpose(A):
    shape_A = shape(A)

    t_A = []
    for i in range(shape_A[1]):
        row = []
        for j in range(shape_A[0]):
            row.append(A[j][i])
        t_A.append(row)
    return t_A

def mat_inverse(A):
    shape_A = shape(A)
    len_A = shape_A[0]

    if len_A != shape_A[1]:
        error_message = "Matrix A must be square matrix : {}".format(shape_A)
        raise ValueError(error_message)

    det_A = determinant(A)
    adj_A = [[determinant(adj_sub_matrix(A, j, i)) for j in range(len_A)] for i in range(len_A)]
    t_adj_A = mat_transpose(adj_A)

    sign = -1 / det_A
    for i in range(len_A):
        for j in range(len_A):
            sign *= -1
            t_adj_A[i][j] *= sign

    return t_adj_A

def validate_same_length_two_vector(A, B):
    shapes = [shape(A), shape(B)]

    if F.any(shapes, lambda s: len(s) > 1) or shapes[0][0] != shapes[1][0]:
        error_message = "A and B must be vector and same length : {}".format(shapes)
        raise ValueError(error_message)

    return shapes[0][0]

def dot_product(A, B):
    validate_same_length_two_vector(A, B)

    return sum([a * b for a, b in zip(A, B)])

def cross_product(A, B):
    # 일반식이 복잡함. 2,3차만 따로 구현
    shapes = validate_same_length_two_vector(A, B)

    if shapes == 2:
        return determinant([[a, b] for a, b in zip(A, B)])
    elif shapes == 3:
        M = [[0, a, b] for a, b in zip(A, B)]
        return [determinant(adj_sub_matrix(M, 0, y)) for y in range(shapes)]
    else:
        raise NotImplementedError("cross_product is enable for 2D and 3D vectors.")

if __name__ == '__main__':
    pass


from numpy import array, sqrt
from benchmarks.benchmark import Benchmark

class Bukin6(Benchmark):
    """dim: 2"""
    def __init__(self, lower=(-15, -3), upper=(-5, 3), dimension=2):
        super(Bukin6, self).__init__(lower, upper, dimension)

    def get_optimum(self):
        return array([[-10, 1]]), 0.0

    @staticmethod
    def eval(sol):
        x1 = sol[0]
        x2 = sol[1]
        term1 = 100 * sqrt(abs(x2 - 0.01 * x1 ** 2))
        term2 = 0.01 * abs(x1 + 10)

        return term1 + term2

if __name__ == '__main__':
    bukin6 = Bukin6(lower=(-15, -3), upper=(-5, 3), dimension=2)
    print(bukin6.get_optimum())
    bukin6.plot(scale=0.32)


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

n=1000
x_list=np.random.rand(n)
y_list=np.random.rand(n)

count=0
for x,y in zip(x_list, y_list):
    if x**2.+y**2.<1:
        count=count+1

pi=4.*float(count)/float(n)

print('pi='+str(pi))

theta=np.arange(0,7,0.001)

plt.figure(figsize=(5,5),dpi=96)
plt.scatter(x_list,y_list,alpha=0.2)
plt.plot(np.cos(theta),np.sin(theta))
plt.xlim(0,1.2)
plt.ylim(0,1.2)
plt.show()


import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt

# Let's create a function to model and create data
def func(x, a0, b0, c0, a1, b1, c1):
    return a0 * np.exp(-(x - b0) ** 2 / (2 * c0 ** 2))\
    + a1 * np.exp(-(x - b1) ** 2 / (2 * c1 ** 2))

# Generating clean data
x = np.linspace(0, 20, 200)
y = func(x, 1, 3, 1, -2, 15, 0.5)
# y2 = func(x[np.where(x > 10)], 0, 1, 1, -2, 15,0.5)

# Adding noise to the data
yn = y + 0.2 * np.random.normal(size=len(x))

# Plot out the current state of the data and model
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, c='k', label='Function')
ax.scatter(x, yn)

# Executing curve_fit on noisy data
popt, pcov = curve_fit(func, x, yn, p0=[1, 3, 1, 1, 15, 1])

#popt returns the best fit values for parameters of the given model (func)
print popt

ym = func(x, popt[0], popt[1], popt[2], popt[3], popt[4], popt[5])
ax.plot(x, ym, c='r', label='Best fit')
ax.legend()
fig.savefig('scipy_311_ex3.pdf', bbox_inches='tight')


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

square_size = 1
points_inside_square = 0
points_inside_circle = 0
sample_size = 3000

arc = np.linspace(0, np.pi/2, 100)
def generate_points(size):
    x = random.random()*size
    y = random.random()*size
    return (x, y)

def is_in_circle(point, size):
    return math.sqrt(point[0]**2+point[1]**2) <= size

def compute_pi(points_inside_circle, points_inside_square):
    return 4*points_inside_circle / points_inside_square

plt.axes().set_aspect('equal')
plt.plot(1*np.cos(arc), 1*np.sin(arc), 'r')

for i in range(sample_size):
    point = generate_points(square_size)
    plt.plot(point[0], point[1], 'c.')
    points_inside_square += 1
    if is_in_circle(point, square_size):
        points_inside_circle += 1
plt.show()
print("Approximate value of pi is {}" .format(compute_pi(points_inside_circle,points_inside_square)))


import numpy as np
import statistics
import time

def hms2dec(h, m, s):
  return 15*(h + m/60 + s/3600)

def dms2dec(d, m, s):
  if (d >= 0):
    return d + m/60 + s/3600
  else:
    return d - m/60 - s/3600

def angular_dist(a1, d1, a2, d2):
  p1 = np.square(np.sin(np.absolute(d1-d2)/2))
  p2 = np.cos(d1)*np.cos(d2)*np.square(np.sin(np.absolute(a1-a2)/2))
  return 2*np.arcsin(np.sqrt(p1+p2))

def crossmatch(cat1, cat2, max_dist):
  matches, nomatches = [], []
  cat1id, cat2id = 0, 0

  start = time.perf_counter()

  # convert everything to radians
  cat1 = np.radians(cat1)
  cat2 = np.radians(cat2)
  max_dist = np.radians(max_dist)

  ra2s = cat2[:,0]
  dec2s = cat2[:,1]

  for cat1line in cat1:
    dists = angular_dist(cat1line[0], cat1line[1], ra2s, dec2s)
    min_dist = np.min(dists)
    if(min_dist <= max_dist):
      matches.append((cat1id,np.argmin(dists),min_dist))
    else:
      nomatches.append(cat1id)

    cat1id += 1

  return (matches, nomatches, time.perf_counter() - start)

# You can use this to test your function.
# Any code inside this `if` statement will be ignored by the automarker.
if __name__ == '__main__':
  # The example in the question
  ra1, dec1 = np.radians([180, 30])
  cat2 = [[180, 32], [55, 10], [302, -44]]
  cat2 = np.radians(cat2)
  ra2s, dec2s = cat2[:,0], cat2[:,1]
  dists = angular_dist(ra1, dec1, ra2s, dec2s)
  print(np.degrees(dists))

  cat1 = np.array([[180, 30], [45, 10], [300, -45]])
  cat2 = np.array([[180, 32], [55, 10], [302, -44]])
  matches, no_matches, time_taken = crossmatch(cat1, cat2, 5)
  print('matches:', matches)
  print('unmatched:', no_matches)
  print('time taken:', time_taken)

  # A function to create a random catalogue of size n
  def create_cat(n):
    ras = np.random.uniform(0, 360, size=(n, 1))
    decs = np.random.uniform(-90, 90, size=(n, 1))
    return np.hstack((ras, decs))

  # Test your function on random inputs
  np.random.seed(0)
  cat1 = create_cat(10)
  cat2 = create_cat(20)
  matches, no_matches, time_taken = crossmatch(cat1, cat2, 5)
  print('matches:', matches)
  print('unmatched:', no_matches)
  print('time taken:', time_taken)

wk2/Crossmatching with k-d trees/02_vectorization.py