import numpy as np
from scipy.stats import entropy

__all__ = [
  'mutual_information',
  'binarize',
  'split'
]

def mutual_information(hist):
  """
  Mutual Information is used as a measure of dependency between variables.

  However, it is inherently difficult to reliably compute MI, thus, don't trust
  this function too much. Also, it is generally not a good idea to use it for
  anything beyond 2 dimensions.

  :param hist: 2D histogram for X and Y random variables.
  :return: rough estimation of mutual information coefficient between X and Y.
  """
  pmf = hist / np.sum(hist, dtype='float64')
  marginal_x, marginal_y = np.sum(pmf, axis=1), np.sum(pmf, axis=0)

  H_xy = entropy(pmf.reshape(-1))

  ### to avoid warnings...
  pmf = np.where(hist > 0, pmf, 1)

  H_x = -np.sum(
    np.where(hist > 0, pmf * (np.log(pmf) - np.log(marginal_x)[:, None]), 0)
  )

  H_y = -np.sum(
    np.where(hist > 0, pmf * (np.log(pmf) - np.log(marginal_y)[None, :]), 0)
  )

  MI = H_xy - H_x - H_y

  return MI

def binarize(xs, n_bins=10):
  if xs.dtype.kind == 'i':
    n = np.max(xs) - np.min(xs) + 1
    indx = xs - np.min(xs)
    return indx, np.arange(n + 1) - 0.5
  else:
    qs = np.linspace(0, 1, num=n_bins + 1)[1:-1]
    bins = np.quantile(xs, qs)
    indx = np.searchsorted(bins, xs)

    return indx, np.concatenate(([np.min(xs)], bins, [np.max(xs)]), axis=0)

def split(*data, split_ratios=0.8, seed=None):
  if len(data) == 0:
    return tuple()

  try:
    iter(split_ratios)
  except TypeError:
    split_ratios = (split_ratios, 1 - split_ratios)

  assert all([r >= 0 for r in split_ratios])

  size = len(data[0])

  split_ratios = np.array(split_ratios)
  split_sizes = np.ceil((split_ratios * size) / np.sum(split_ratios)).astype('int64')
  split_bins = np.cumsum([0] + list(split_sizes))
  split_bins[-1] = size

  if seed is not None:
    state = np.random.get_state()
    np.random.seed(seed)
  else:
    state = None

  r = np.random.permutation(size)

  if state is not None:
    np.random.set_state(state)

  result = list()

  for i, _ in enumerate(split_ratios):
    from_indx = split_bins[i]
    to_indx = split_bins[i + 1]
    indx = r[from_indx:to_indx]

    for d in data:
      if isinstance(d, np.ndarray):
        result.append(d[indx])
      else:
        result.append([ d[i] for i in indx ])

  return tuple(result)


import numpy as np
import codecs

#计算欧氏距离
def distance(x1,x2):
    return np.sqrt(sum(np.power(x1-x2,2)))

#对一个样本找到与该样本距离最近的聚类中心
def nearest(point, cluster_centers):
    min_dist = np.inf
    m = np.shape(cluster_centers)[0]  # 当前已经初始化的聚类中心的个数
    for i in range(m):
        # 计算point与每个聚类中心之间的距离
        d = distance(point, cluster_centers[i, ])
        # 选择最短距离
        if min_dist > d:
            min_dist = d
    return min_dist
#选择尽可能相距较远的类中心
def get_centroids(dataset, k):
    m, n = np.shape(dataset)
    cluster_centers = np.zeros((k , n))
    index = np.random.randint(0, m)
    cluster_centers[0,] = dataset[index, ]
    # 2、初始化一个距离的序列
    d = [0.0 for _ in range(m)]
    for i in range(1, k):
        sum_all = 0
        for j in range(m):
            # 3、对每一个样本找到最近的聚类中心点
            d[j] = nearest(dataset[j, ], cluster_centers[0:i, ])
            # 4、将所有的最短距离相加
            sum_all += d[j]
        # 5、取得sum_all之间的随机值
        sum_all *= np.random.rand()
        # 6、获得距离最远的样本点作为聚类中心点
        for j, di in enumerate(d):
            sum_all=sum_all - di
            if sum_all > 0:
                continue
            cluster_centers[i,] = dataset[j, ]
            break
    return cluster_centers

#主程序
def Kmeans(dataset,k):
    row_m=np.shape(dataset)[0]
    cluster_assign=np.zeros((row_m,2))
    center=get_centroids(dataset,k)
    change=True
    while change:
        change=False
        for i in range(row_m):
            mindist=np.inf
            min_index=-1
            for j in range(k):
                distance1=distance(center[j,:],dataset[i,:])
                if distance1<mindist:
                    mindist=distance1
                    min_index=j
            if cluster_assign[i,0] != min_index:
                change=True
            cluster_assign[i,:]=min_index,mindist**2
        for cen in range(k):
            cluster_data=dataset[np.nonzero(cluster_assign[:,0]==cen)]
            center[cen,:]=np.mean(cluster_data,0)
    return center ,cluster_assign
#cluster_center,cluster_assign=Kmeans(datas,3)

def kmeanspp_select(gradient, k):
    center, cluster_assign = Kmeans(gradient, k)
    keep_idx = []
    tot = cluster_assign.shape[0]
    mapp = {}
    for i in range(tot):
        cid = cluster_assign[i][0]
        dist = cluster_assign[i][1]
        if not cid in mapp:
            mapp[cid] = (dist, i)
        else:
            if dist < mapp[cid][0]:
                mapp[cid] = (dist, i)
    for cid in mapp:
        keep_idx.append(mapp[cid][1])
    return keep_idx


# Chapter 5 Compressing Data via Dimensionality Reduction
# Principal Components Analysis
# Total and explained variance
'''
First, we will start by loading the Wine dataset that we have been working with
in Chapter 4'''
import pandas as pd
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data', header=None)

'''
Next, we will process the Wine data into separate training and test sets using 70
percent and 30 percent of the data, respectively - and standardize it to unit variance'''
from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import StandardScaler
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X_train, X_test, y_train, y_test = \
train_test_split(X, y, test_size=0.3, random_state=0)
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)

'''
We will use the linalg.eig function from Numpy to obtain the eigenpairs of the Wine
covariance matrix'''
import numpy as np
cov_mat = np.cov(X_train_std.T)
eigen_vals, eigen_vecs = np.linalg.eig(cov_mat)
print '\nEigenvalues \n%s' % eigen_vals
'''
Using the numpy.cov function, we computed the covariance matrix of the
standardized training dataset. Using the linalg.eig function, we performed the
eigendecomposition that yielded a vector (eigen_vals) consisting of 13 eigenvalues
 and the corresponding eigenvectors stored as columns in a 13x13 -dimensional
 matrix (eigen_vecs).
'''
'''
Since we want to reduce the dimensionality of our dataset by compressing it onto
a new feature subspace, we only select the subset of the eigenvectors(principle
components) that contains most of the information (variance). Since the eigenvalues
define the magitude of the eigenvectors, we have to sort the eigenvalues by
decreasing magnitude; we are interested in the top k eigenvectors based on the
values of their corresponding eigenvalues.
But before we collect those k most informative eigenvectors, let's plot the variance
explained ratios of the eigenvalues

'''
'''
Using the NumPy cumsum function, we can then calculate the cumulative  sum of
explained variances, which we will plot via matplotlib's step function: '''
tot = sum(eigen_vals)
var_exp = [(i / tot) for i in sorted(eigen_vals, reverse=True)]
cum_var_exp = np.cumsum(var_exp)

import matplotlib.pyplot as plt
plt.bar(range(1, 14), var_exp, alpha=0.5, align='center', label='individual explained variance')
plt.step(range(1,14), cum_var_exp, where='mid', label='cumulative explained variance')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal components')
plt.legend(loc='best')
plt.show()

'''
The resulting plot indicates that the first principal component alone accounts for
40 percent of the variance. Also, we can see that the first two principal components
combined explain almost 60 percent of the variance in the data:
'''
# Feature transformation
# We start by sorting the eigenpairs by decreasing order of the eigenvalues:
eigen_pairs = [(np.abs(eigen_vals[i]), eigen_vecs[:, i]) for i in range(len(eigen_vals))]
eigen_pairs.sort(reverse=True)

w = np.hstack((eigen_pairs[0][1][:, np.newaxis],   # eigen_pairs[0][1].shape =(13,)
                       eigen_pairs[1][1][:, np.newaxis]))  # eigen_pairs[0][1][:, np.newaxis].shape= (13, 1)
print 'Matrix W: \n', w

'''
By executing the preceding code, we have created a 13x2-dimensional projection
matrix w from the top two eigenvectors. Using the projection matrix, we can now
transform a sample x (represented as 1x13-dimensional row vector) onto the PCA
subspace obraining x', a now two-dimensional sample vector consisting of two new
features:       x' = xW
'''
X_train_std[0].dot(w) # 1x13 13x2 = 1x2
# similarly, we can transform the entire 124 x 13-dimensional training dataset onto the two principal components
# by calculating the matrix dot product:
X_train_pca = X_train_std.dot(w) # 124x13 13x2 = 124x2

'''
Lastly, let's visualize the transformed Wine training set, now stored as an
124 x 2-dimensional matrix, in a two-dimensional scatterplot:
'''
colors = ['r', 'b', 'g']
markers = ['s', 'x', 'o']
for l, c, m in zip(np.unique(y_train), colors, markers):
    plt.scatter(X_train_pca[y_train==l, 0], X_train_pca[y_train==l, 1],c=c, label=1, marker = m)

plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.show()

# Principal component analysis in scikit-learn
from Plot_Decision_Regions import plot_decision_regions
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA
pca = PCA(n_components=2) # Number of components to keep. if n_components is not set all components are kept
lr = LogisticRegression()
X_train_pca = pca.fit_transform(X_train_std)
X_test_pca = pca.transform(X_test_std)
lr.fit(X_train_pca, y_train)
plot_decision_regions(X_train_pca, y_train, classifier=lr)
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.legend(loc='lower left')
plt.show()

'''
Let's plot the decision regions of the logistic regression on the transformed test
dataset to see if it can separate the classes well.
'''
plot_decision_regions(X_test_pca, y_test, classifier=lr)
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.legend(loc='lower left')
plt.show()

'''
If we are interested in the explained variance ratios of the different principal
components, we can simply intialize the PCA class with the n_components parameter
set to None, so all principal components are kept and the explained variance ratio can
then be accessed via the explained _variance_ratio_ attribute
'''
pca = PCA(n_components=None)
X_train_pca = pca.fit_transform(X_train_std)
pca.explained_variance_ratio_

'''
From Wikipedia:
The eigendecomposition of a symmetric positive semidefinite (PSD) matrix yields an
orthogonal basis of eigenvectors, each of which has a nonnegative eigenvalue. The
orthogonal decomposition of a PSD matrix is used in multivariate analysis, where the
sample covariance matrices are PSD. This orthogonal decomposition is called principal
components analysis (PCA) in statistics. PCA studies linear relations among variables.
PCA is performed on the covariance matrix or the correlation matrix (in which each
variable is scaled to have its sample variance equal to one). For the covariance or
correlation matrix, the eigenvectors correspond to principal components and the
eigenvalues to the variance explained by the principal components. Principal
component analysis of the correlation matrix provides an orthonormal eigen-basis for
the space of the observed data: In this basis, the largest eigenvalues correspond to
the principal components that are associated with most of the covariability among a
number of observed data.

'''

self_practice/Chapter 5 Principal Component Analysis.py


#--------------------------------
#SIMPLE UNIVARIABLE REGRESSION EXAMPLE
#--------------------------------

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

#------------------------
#GENERATE TOY DATA
#------------------------

data_type="E"; #L=linear  E=exp   Q=quadratic
model_type="E" #L=linear  E=exp   Q=quadratic

#GROUND TRUTH DATA GENERATING FUNCTION
def G(x):
	if(data_type=="L"): out=50*x+3
	if(data_type=="Q"): out=100*x**2+50*x+3
	if(data_type=="E"): out=50*np.exp(-0.1*(x-2))+3
	return out

#NOISY DATA
N=200; xmin=-10; xmax=10
x = np.linspace(xmin,xmax,N)
y = G(x)  #PRISTINE DATA
# noise=np.random.normal(loc=0.0, scale=0.05*(max(y)-min(y)),size=len(x))
noise=0.15*(max(y)-min(y))*np.random.uniform(-1,1,size=len(x))
yn = y+ noise

#GROUND TRUTH DATA
xe = np.linspace(xmin,2*xmax,int(2*N))
ye = G(xe)

#NORMALIZE
xe=(xe-np.mean(x))/np.std(x)
ye=(ye-np.mean(y))/np.std(y)
yn=(yn-np.mean(y))/np.std(y)

x=(x-np.mean(x))/np.std(x)
y=(y-np.mean(y))/np.std(y)

#------------------------
#TRAINING
#------------------------

##FITTING MODEL
def model(x,p0,p1,p2,p3):
	if(model_type=="L"): out=p1*x+p0
	if(model_type=="Q"): out=p2*x**2+p1*x+p0
	if(model_type=="E"): out=p3*np.exp(p2*(x-p1))+p0
	return out

#INITIAL GUESS FOR PARAMETERS p0,p1,p2,p3
po=[0.1,0.1,0.1,0.1]

#FIT MODEL
popt, pcov = curve_fit(model, x, yn,po) #,[0.1,0.1,0.1])

#------------------------
#PLOT
#------------------------

fig, ax = plt.subplots()
ax.plot(x, yn, 'o', label='Data')
ax.plot(xe, ye, '-', label='Ground-Truth')
ax.plot(xe, model(xe, *popt), 'r-', label="Model")

ax.legend()
FS=18   #FONT SIZE
plt.xlabel('X', fontsize=FS)
plt.ylabel('Y', fontsize=FS)

plt.show()


# Copyright 2020, Battelle Energy Alliance, LLC
# ALL RIGHTS RESERVED
import math
from scipy.integrate import quad
import time

def timeDepLambda(t,a,b):
  exponent = a+t*b
  return 10**exponent

def pdfFailure(t,a,b):
  first  = timeDepLambda(t,a,b)
  second = math.exp(-quad(timeDepLambda, 0, t, args=(a,b))[0])
  return first*second

t = 3700.
a_V2 = -4.
b_V2 = 1.E-4

start_time = time.clock()
probability1 = quad(pdfFailure, 0, t, args=(a_V2,b_V2))
print(probability1, time.clock() - start_time)

start_time = time.clock()
probability2 = 1. - math.exp(-quad(timeDepLambda, 0, t, args=(a_V2,b_V2))[0])
print(probability2, time.clock() - start_time)


"""
Plots spectral density for AR(1) X' = phi X + epsilon
"""
import numpy as np
import matplotlib.pyplot as plt

def ar1_sd(phi, omega):
    return 1 / (1 - 2 * phi * np.cos(omega) + phi**2)

omegas = np.linspace(0, np.pi, 180)
num_rows, num_cols = 2, 1
fig, axes = plt.subplots(num_rows, num_cols, figsize=(10, 8))
plt.subplots_adjust(hspace=0.4)

# Autocovariance when phi = 0.8
temp = r'spectral density, $\phi = {0:.2}$'
for i, phi in enumerate((0.8, -0.8)):
    ax = axes[i]
    sd = ar1_sd(phi, omegas)
    ax.plot(omegas, sd, 'b-', alpha=0.6, lw=2, label=temp.format(phi))
    ax.legend(loc='upper center')
    ax.set_xlabel('frequency')
    ax.set_xlim((0, np.pi))
plt.show()


import numpy as np

def gram_schmidt(A):
    U = np.zeros(A.shape)
    U[:, 0] = A[:, 0] / np.linalg.norm(A[:, 0])
    for i in range(1, A.shape[1]):
        U[:, i] = A[:, i]
        for j in range(i-1):
            U[:, i] = U[:, i] - (U[:, j].T @ U[:, i]) / (np.linalg.norm(U[:, j]))**2 * U[:, j]
        U[:, i] = U[:, i] / np.linalg.norm(U[:, i])

    return U

A = np.array([[1, 1, 1],
              [1/1000, 1/1000, 0],
              [1/1000, 0, 1/1000]])

U = gram_schmidt(A)

print(U)

# Orthogonality test
u1_u2_dot = U[:, 0].T @ U[:, 1]
print(f'u1.T @ u2 = {u1_u2_dot}')

u1_u3_dot = U[:, 0].T @ U[:, 2]
print(f'u1.T @ u3 = {u1_u3_dot}')


from numpy import loadtxt, zeros, e, array, log, ones, where

def feature_normalize(input):
    mu = input.mean(axis=0)
    sd = input.std(axis=0)
    input_norm = (input - mu) / sd

    return input_norm

def sigmoid(X):
    return 1.0 / (1.0 + e ** (-1.0 * X))

def cost_function(theta, x, y):
    m = y.size
    h = sigmoid(x.dot(theta))
    J = (1.0 / m) * ((-y.T.dot(log(h))) - ((1.0 - y.T).dot(log(1.0 - h))))
    return - 1 * J.sum()

def gradient_descent(x, y, theta, alpha = 0.01, num_iters = 1):
    m = x.shape[0]
    theta_size = theta.size
    for i in range(num_iters):

        predictions = x.dot(theta)

        for it in range(theta_size):
            temp = x[:, it]
            temp.shape = (m, 1)

            errors_x1 = (predictions - y) * temp

            theta[it][0] = theta[it][0] - alpha * (1.0 / m) * errors_x1.sum()

        cost_function(theta, x, y)

    return theta

def predict(theta, x):
    m, n = x.shape
    p = zeros(shape=(m, 1))

    h = sigmoid(x.dot(theta))

    for it in range(h.shape[0]):
        if h[it] >= 0.5:
            p[it, 0] = 1
        else:
            p[it, 0] = 0

    return p

data = loadtxt('ex2data1.txt', delimiter=',')
x = data[:, :-1]
y = data[:, -1]
m, n = x.shape
y.shape = (m, 1)

x_norm = feature_normalize(x)
#theta0, theta1, thetaM
x = ones(shape=(m, 3))
x[:, 1:3] = x_norm

theta = zeros(shape=(n+1, 1))

theta = gradient_descent(x, y, theta)
print theta

p = predict(array(theta), x)
print 'Accuracy: %f' % ((y[where(p == y)].size / float(y.size)) * 100.0)


import numpy as np

def accuracy(confusion_matrix: np.ndarray):
    """
    Computes accuracy of the classifier based on confusion matrix.

    Parameters
    ----------
    confusion_matrix: ArrayLike
        The Numpy nxn array or nxn list representing confusion matrix

    Returns
    -------
    float : accuracy value
    """
    return np.trace(confusion_matrix) / np.sum(confusion_matrix)

def mean_precision(confusion_matrix: np.ndarray):
    """
    Computes mean precision for all classes in the confusion matrix.

    Parameters
    ----------
    confusion_matrix: ArrayLike
        The Numpy nxn array or nxn list representing confusion matrix

    Returns
    -------
    float : mean precision value
    """
    return np.mean(
        confusion_matrix.diagonal() /
        np.sum(confusion_matrix, axis=1)
    )

def mean_sensitivity(confusion_matrix: np.ndarray):
    """
    Computes mean sensitivity for all classes in the confusion matrix.

    Parameters
    ----------
    confusion_matrix: ArrayLike
        The Numpy nxn array or nxn list representing confusion matrix

    Returns
    -------
    float : Mean sensitivity
    """
    return np.mean(
        confusion_matrix.diagonal() /
        np.sum(confusion_matrix, axis=0)
    )

def g_mean(confusion_matrix: np.ndarray):
    """
    Computes g-mean metric for the confusion matrix.

    Parameters
    ----------
    confusion_matrix: ArrayLike
        The Numpy nxn array or nxn list representing confusion matrix

    Returns
    -------
    float : G-Mean value
    """
    return np.float_power(np.prod(
        np.array(confusion_matrix).diagonal() /
        np.sum(confusion_matrix, axis=0)
    ), 1.0 / np.array(confusion_matrix).shape[0])


#!/bin/python3
import numpy as np

def find_closest_fibonacci_number(x):
    a = 1
    b = 1
    while (a + b) / 2 < x:
        a, b = b, a + b
    return a

def find_distance_closest_fibonacci_number(x):
    closest_fib = find_closest_fibonacci_number(x)
    return abs(closest_fib - x)

input_array = [1, 3, 4, 6, 7, 11, 17, 63, 101, 377, 467, 500, 1399]
closest_fibonacci_numbers = [1, 3, 3, 5, 8, 13, 13, 55, 89, 377, 377, 610, 1597]

test_result = [find_closest_fibonacci_number(x) for x in input_array]
print(np.array_equal(test_result, closest_fibonacci_numbers))


# Generate trigonometric patterns.

import numpy as np

def trigonometric_1(n, m, k=1, w=0, b=0):
    """
    Generate a trigonometric pattern.

    Args:
      * n: number of points to generate the pattern with
      * m: number of points multiplier

    Kwargs:
      * k: frequency adjustment for pattern
      * w: phase adjustment for pattern
      * b: offset adjustment for pattern

    """
    t = np.linspace(-m*np.pi, m*np.pi, n)
    r = t % n
    x = r * np.sin(k*t + w) + b
    y = r * np.cos(k*t + w) + b
    return x, y

def trigonometric_2(n, m, k=1, w=0, b=0):
    """
    Generate a trigonometric pattern.

    Args:
      * n: number of points to generate the pattern with
      * m: number of points multiplier

    Kwargs:
      * k: frequency adjustment for pattern
      * w: phase adjustment for pattern
      * b: offset adjustment for pattern

    """
    t = np.linspace(-m*n, m*n, n)
    r = t // n
    x = r * np.sin(k*t + w) + b
    y = r * np.cos(k*t + w) + b
    return x, y

trigonometric = trigonometric_1

def spirograph(n, m,
               rnx=1, rny=1,
               rmx=1, rmy=1,
               kn=1, km=1,
               wn=0, wm=0):
    """
    Generate a spirograph pattern by extruding circles around a central ring.

    Args:
      * n: number of points to generate the central ring with
      * m: number of points to generate each circle with

    Kwargs:
      * rnx: amplitude of central ring x-component
      * rny: amplitude of central ring y-component
      * rmx: amplitude of circles' x-component
      * rmy: amplitude of circles' y-component
      * kn: frequency adjustment of central ring (x and y)
      * km: frequency adjustment of circles (x and y)
      * wn: phase adjustment of central ring (x and y)
      * wm: phase adjustment of circles (x and y)

    Args must be supplied as a single integer value. Kwargs can be supplied either
    as a single numeric value or an array that can be broadcast to the shape (n, m).

    """
    t_ring = np.linspace(0, 2*np.pi, n*m)
    t_circle = np.linspace(0, 2*np.pi, m)

    x_ring = rnx * np.sin(t_ring*kn).reshape(n, m)
    y_ring = rny * np.cos(t_ring*kn).reshape(n, m)
    xp = rmx * np.sin(t_circle*km) + x_ring
    yp = rmy * np.cos(t_circle*km) + y_ring

    return xp.reshape(-1), yp.reshape(-1)


from sympy import *

def bernstein_series(N):
  # FIXME: check if a normalization constant is common in the definition
  # advantage is that the basis is always positive
  psi = []
  for k in range(0,N+1):
    psi_k = x**k*(1-x)**(N-k)
    psi.append(psi_k)
  return psi

def sin_series(N):
  # FIXME: do not satisfy bc
  psi = []
  for k in range(1,N):
    psi_k = sin(pi*k*x)
    psi.append(psi_k)
  return psi

def taylor_series(N):
  # FIXME: do not satisfy bc
  psi = []
  for k in range(1,N):
    psi_k = x**k
    psi.append(psi_k)
  return psi

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

N = 10
M = 8
series_type = "bernstein"
Omega = [0, 1]
x = Symbol("x")

psi = series(series_type, N)
eta = series(series_type, M)

A = zeros((N+1+M+1), (N+1+M+1))
b = zeros((N+1+M+1))
f = 0
g = 0

for i in range(0, N+1):
  for j in range(0, N+1):
    integrand = psi[i]*psi[j]
    A[i,j] = integrate(integrand, (x,Omega[0], Omega[1]))

for i in range(0, N+1):
  for j in range(0, M+1):
    integrand = diff(psi[i],x)*eta[j]
    A[i,N+1+j] = integrate(integrand, (x,Omega[0], Omega[1]))
    A[N+1+j,i] = A[i,N+1+j]

for i in range(0, N+1):
  integrand = f*psi[i]
  print(integrand)
  b[i,0] = integrate(integrand, (x, Omega[0], Omega[1]))

for i in range(0, M+1):
  integrand = g*eta[i]
  print(integrand)
  b[N+1+i,0] = integrate(integrand, (x, Omega[0], Omega[1]))

# bc
A[0,:] = zeros(1,N+1+M+1)
A[0,0] = 1
b[0] = 1

A[N,:] = zeros(1,N+1+M+1)
A[N,N] = 1
b[N] = 0

print(A)

#c = A.LUsolve(b)
# FIXME: pinvsolve does not work and there is no info about this function
c = A.pinvsolve(b)
print(c)
u = sum(c[r,0]*psi[r] for r in range(N+1))

print(u)

import numpy, pylab
U = lambdify([x], u)
x = numpy.arange(Omega[0], Omega[1], 1/((N+1)*10.0))
UU = U(x)
pylab.plot(UU)
pylab.show()


import numpy as np

def Entropy(class1, class2=[], sample_weight=[]):
    ## entropy is measure of randomness, attribute with highest entropy is splitted at each node. attribute-
    ## with 0 entropy will be a leaf node, entropy>0 needs further splitting.
    ## class1 is usually the target class
    if(len(class2)==0):
        unique_counts1 = np.unique(class1, return_counts=True)
        if(len(sample_weight)==0):
            size = len(class1)
            prob = unique_counts1[1]/size
            return -np.sum(prob*np.log2(prob))
        else:
            entropy = 0
            total_weights = np.sum(sample_weight)
            for j in range(len(unique_counts1[0])):
                ind1 = np.where(class1==unique_counts1[0][j])[0]
                p = np.sum(sample_weight[ind1])/total_weights
                entropy+=(-p*np.log2(p))
            return entropy
    else:   ##groupby class2 and find entropy of each group
        unique_counts = np.unique(class2, return_counts=True)
        size = len(class2)
        prob = unique_counts[1]/size
        entropy=0
        classes = np.asarray(unique_counts[0])
        if(len(sample_weight)!=0):
            total_weights = np.sum(sample_weight)
        for i in range(len(classes)):  ##for each class
            ind = np.where(class2==classes[i])[0]
            if len(sample_weight)!=0:
                p = Entropy(class1[ind], sample_weight = sample_weight[ind])
                #print(p, (np.sum(sample_weight[ind])/(total_weights)))
                entropy+=(np.sum(sample_weight[ind])/(total_weights))*p
                #print(entropy)
            else:
                p = Entropy(class1[ind])
                entropy+= prob[i]*p
        return entropy

def Information_Gain(class1, class2, sample_weight=[]):       ## change in entropy before and after split
    return (Entropy(class1, sample_weight=sample_weight) - Entropy(class1, class2, sample_weight=sample_weight))

def Gini_index(class1, class2, sample_weight=[]):
    ## higher the value of gini index higher the homogeneity.
    unique_counts = np.unique(class2, return_counts=True)
    size = len(class2)
    prob = unique_counts[1]/size
    if len(sample_weight)!=0:
        total_weight = np.sum(sample_weight)
    gini=0
    classes = np.asarray(unique_counts[0])
    for i in range(len(classes)):  ##for each class
        ind = np.where(class2==classes[i])[0]
        p1 = np.unique(class1[ind], return_counts=True)
        if len(sample_weight)==0:
            tot = np.sum(p1[1])
            p1 = p1[1]/tot
            p1 = 1-np.sum(np.power(p1,2))
            gini+=p1*(tot)/size
        else:
            p2 = 0
            total_weight1 = np.sum(sample_weight[ind])
            for j in range(len(p1[0])):
                ind1 = ind[class1[ind]==p1[0][j]]
                p3 = np.sum(sample_weight[ind1])/total_weight1
                p3 = np.power(p3,2)
                p2+=p3
            p2 = (total_weight1/total_weight)*(1-p2)
            gini+=p2
    return gini

def Chi_square(class1, class2):
    class_values = np.unique(class2, return_counts=True)
    classes = np.unique(class1, return_counts=True)
    expected = classes[1]/np.sum(classes[1])
    chaid = 0
    for i in range(len(class_values[0])):  ##for each class
        ind = np.where(class2==class_values[0][i])[0]
        p1 = np.unique(class1[ind], return_counts=True)
        p2 = np.power(np.divide(np.power((p1[1]-expected*np.sum(p1[1])),2),expected*np.sum(p1[1])), 0.5)
        #print(p1, p2)
        chaid +=np.sum(p2)
    return chaid

#def Variance_reduction():


import numpy as np

import thalesians.tsa.numpyutils as npu
import thalesians.tsa.utils as utils

def make_cov_2d(sd1, sd2, cor):
    offdiag = cor*sd1*sd2
    return np.array([[sd1*sd1, offdiag], [offdiag, sd2*sd2]])

def make_vol_2d(sd1, sd2, cor):
    return np.array([[sd1, 0.], [cor*sd2, np.sqrt(1. - cor*cor)*sd2]])

def cov_to_vol(cov):
    cov = npu.to_ndim_2(cov, ndim_1_to_col=True, copy=False)
    return np.linalg.cholesky(cov)

def cor_to_cov(cors, vars=None, sds=None, copy=True):  # @ReservedAssignment
    assert (vars is None and sds is not None) or (vars is not None and sds is None)
    sds = np.sqrt(vars) if vars is not None else sds
    if isinstance(cors, (utils.DiagonalArray, utils.SubdiagonalArray)):
        cors = cors.tonumpyarray()
    cors = npu.to_ndim_2(cors, copy=copy)
    dim = len(vars)
    assert dim == np.shape(cors)[0] and dim == np.shape(cors)[1]
    np.fill_diagonal(cors, 1.)
    for i in range(dim):
        cors[i,:] = sds[i] * cors[i,:]
        cors[:,i] = sds[i] * cors[:,i]
    npu.lower_to_symmetric(cors, copy=False)
    return cors

def vol_to_cov(vol):
    vol = npu.to_ndim_2(vol, ndim_1_to_col=True, copy=False)
    return np.dot(vol, vol.T)

def cholesky_sqrt_2d(sd1, sd2, cor):
    return np.array(((sd1, 0.), (sd2 * cor, sd2 * np.sqrt(1. - cor * cor))))

class OnlineStatsCalculator(object):
    def __init__(self, zero=0.0):
        self.reset(zero)

    def reset(self, zero):
        self.__n = 0
        self.__sum = zero
        self.__mean = zero
        self.__mean_sq = zero
        self.__M2 = zero

    @property
    def count(self):
        return self.__n

    @property
    def sum(self):
        return self.__sum

    @property
    def mean(self):
        return self.__mean

    @property
    def mean_sq(self):
        return self.__mean_sq

    @property
    def rms(self):
        return np.sqrt(self.mean_sq)

    @property
    def var_n(self):
        return self.__M2 / self.__n

    @property
    def var(self):
        return self.__M2 / (self.__n - 1)

    @property
    def sd(self):
        return np.sqrt(self.var)

    @property
    def sd_n(self):
        return np.sqrt(self.var_n)

    def add(self, x):
        self.__n += 1
        self.__sum += x
        delta = x - self.__mean
        self.__mean += delta / self.__n
        deltasq = x * x - self.__mean_sq
        self.__mean_sq += deltasq / self.__n
        if self.__n > 1:
            self.__M2 += delta * (x - self.__mean)

    def add_all(self, xs):
        for x in xs: self.add(x)


import numpy as np

from landlab.utils import get_watershed_mask

def hypsometric_integral(grid, outlet_id):
    """Calculate the hypsometric integral for the model grid.

    The hypsometric integral :math:`I` is defined as

    .. math::

        I = \\frac{\\frac{1}{N} \\sum_{i=0}^{N}
        \\left( z - \\min \\left( z\\right) \\right)}
        {\\max \\left(z\\right) - \\min \\left( z \\right)}

    Where :math:`z` is the set of elevation values, and :math:`N` is the number
    of elevation values.

    Parameters
    ----------
    grid : Landlab model grid
    outlet_id : int
        Outlet id of the watershed.

    Returns
    -------
    I : float
        The hypsometric integral.

    Examples
    --------
    First an example that only uses the ``hypsometric_integral`` function.

    >>> from landlab import RasterModelGrid
    >>> from landlab.components import FlowAccumulator
    >>> from umami.calculations import hypsometric_integral
    >>> grid = RasterModelGrid((10, 10))
    >>> z = grid.add_zeros("node", "topographic__elevation")
    >>> z += grid.x_of_node + grid.y_of_node
    >>> fa = FlowAccumulator(grid)
    >>> fa.run_one_step()
    >>> hypsometric_integral(grid, 1)
    0.5

    Next, the same calculations are shown as part of an umami ``Metric``.

    >>> from io import StringIO
    >>> from umami import Metric
    >>> file_like=StringIO('''
    ... hi:
    ...     _func: hypsometric_integral
    ...     outlet_id: 1
    ... ''')
    >>> metric = Metric(grid)
    >>> metric.add_from_file(file_like)
    >>> metric.names
    ['hi']
    >>> metric.calculate()
    >>> metric.values
    [0.5]
    """
    # Get just those elevation values that are within the watershed
    mask = get_watershed_mask(grid, outlet_id)
    vals = grid.at_node["topographic__elevation"][mask]

    # Get min and max
    min_val = np.amin(vals)
    max_val = np.amax(vals)

    # Calc and return the hypsometric_integral
    return np.mean(vals - min_val) / (max_val - min_val)


import numpy as np

#note that the datatype of the matrix should be float
#or the element during GE may be wrong

#original
A=np.float64([[1,-1,1]
             ,[-1,1,-1]
             ,[4,2,0]
             ,[0,2,5]])

B=np.float64([[0]
             ,[0]
             ,[8]
             ,[9]])

#if there is any row filling with 0
#delete it
def del_zero(A,B):
    zero_row=[]
    for i in range(0,len(A)):
        flag=0
        for j in range(0,np.shape(A)[1]):
            if A[i,j]==0:
                flag+=1
        if flag==np.shape(A)[1]:
            zero_row.append(i)
    A=np.delete(A,zero_row,axis=0)
    B=np.delete(B,zero_row,axis=0)
    return A,B

#to deal with the condition that A[0,0]==0
def change_row(A,B):
    if A[0,0]==0:
        temp=A[0,:]
        temp1=B[0]
        A=np.delete(A,0,axis=0)
        A=np.insert(A,[1],temp,axis=0)
        B=np.delete(B,0,axis=0)
        B=np.insert(B,[1],temp1,axis=0)
    return A,B

#Gaussian Elimination
def GE(A,B):
    for i in range(0,len(A)):
        for j in range(i+1,len(A)):
            if A[j,i]!=0:
                lam=A[j,i]/A[i,i]
                A[j,:]=A[j,:]-lam*A[i,:]
                B[j]=B[j]-lam*B[i]
                A,B=del_zero(A,B)
                break
        #break the outer for loop
        else:
            continue
        break

    print(np.c_[A,B],"\n")
    return A,B

#back substitution
def BS(A,B):
    sol=np.zeros((len(A)))
    sol[len(A)-1]=B[len(A)-1]/A[len(A)-1,len(A)-1]

    for i in range(len(A)-2,-1,-1):
        sol[i]=(B[i]-np.dot(A[i,i+1:len(A)],sol[i+1:len(A)]))/A[i,i]
    # sol[1]=(B[1]-A[1,2]*sol[2])/A[1,1]
    # sol[0]=(B[0]-A[0,2]*sol[2]-A[0,1]*sol[1])/A[0,0]
    for j in range(0,len(A)):
        print("i_",j+1,"=",sol[j],end="   ")
    print("\n")

#avoid the original A matrix has invalid value
A,B=del_zero(A,B)

#keep doing GE until the matrix are all the same
def GE_BS(A,B):
    #check if A[0,0]==0 if yew -> change the row
    A,B=change_row(A,B)
    for _ in range(1000):
        A,B=GE(A,B)
        C=np.c_[A,B]
        A,B=GE(A,B)
        D=np.c_[A,B]
        if np.all((C==D)):
            break
    BS(A,B)

GE_BS(A,B)
#result:
# [[ 1. -1.  1.  0.]
#  [ 4.  2.  0.  8.]
#  [ 0.  2.  5.  9.]]

# [[ 1. -1.  1.  0.]
#  [ 0.  6. -4.  8.]
#  [ 0.  2.  5.  9.]]

# [[ 1.         -1.          1.          0.        ]
#  [ 0.          6.         -4.          8.        ]
#  [ 0.          0.          6.33333333  6.33333333]]

# [[ 1.         -1.          1.          0.        ]
#  [ 0.          6.         -4.          8.        ]
#  [ 0.          0.          6.33333333  6.33333333]]

# i_ 1 = 0.9999999999999998   i_ 2 = 2.0   i_ 3 = 1.0000000000000002

#note that there might be a little bit wrong to the right answer
#because lambda can't be divided

#now we change the sequence of the original array test whether the solution are the same

#original
# A=np.float64([[1,-1,1]
#              ,[-1,1,-1]
#              ,[4,2,0]
#              ,[0,2,5]])

# B=np.float64([[0]
#              ,[0]
#              ,[8]
#              ,[9]])

#testing matrix change the 3 and 4 row can it get the same value?
C=np.float64([[1,-1,1]
             ,[-1,1,-1]
             ,[0,2,5]
             ,[4,2,0]])

D=np.float64([[0]
             ,[0]
             ,[9]
             ,[8]])

GE_BS(C,D)
#result:
# [[ 1. -1.  1.  0.]
#  [ 0.  2.  5.  9.]
#  [ 4.  2.  0.  8.]]

# [[ 1. -1.  1.  0.]
#  [ 0.  2.  5.  9.]
#  [ 0.  6. -4.  8.]]

# [[  1.  -1.   1.   0.]
#  [  0.   2.   5.   9.]
#  [  0.   0. -19. -19.]]

# [[  1.  -1.   1.   0.]
#  [  0.   2.   5.   9.]
#  [  0.   0. -19. -19.]]

# i_ 1 = 1.0   i_ 2 = 2.0   i_ 3 = 1.0

#testing matrix change the 2 and 3 row can it get the same value?
#original
# A=np.float64([[1,-1,1]
#              ,[-1,1,-1]
#              ,[4,2,0]
#              ,[0,2,5]])

# B=np.float64([[0]
#              ,[0]
#              ,[8]
#              ,[9]])

E=np.float64([[1,-1,1]
             ,[4,2,0]
             ,[-1,1,-1]
             ,[0,2,5]])

F=np.float64([[0]
             ,[8]
             ,[0]
             ,[9]])

GE_BS(E,F)
#results
# [[ 1. -1.  1.  0.]
#  [ 0.  6. -4.  8.]
#  [-1.  1. -1.  0.]
#  [ 0.  2.  5.  9.]]

# [[ 1. -1.  1.  0.]
#  [ 0.  6. -4.  8.]
#  [ 0.  2.  5.  9.]]

# [[ 1.         -1.          1.          0.        ]
#  [ 0.          6.         -4.          8.        ]
#  [ 0.          0.          6.33333333  6.33333333]]

# [[ 1.         -1.          1.          0.        ]
#  [ 0.          6.         -4.          8.        ]
#  [ 0.          0.          6.33333333  6.33333333]]

# i_ 1 = 0.9999999999999998   i_ 2 = 2.0   i_ 3 = 1.0000000000000002

#test if A[0,0]==0, will the change_row function run?
#original
# A=np.float64([[1,-1,1]
#              ,[-1,1,-1]
#              ,[4,2,0]
#              ,[0,2,5]])

# B=np.float64([[0]
#              ,[0]
#              ,[8]
#              ,[9]])

#change the 1 and 4 row
G=np.float64([[0,2,5]
             ,[-1,1,-1]
             ,[4,2,0]
             ,[1,-1,1]])

H=np.float64([[9]
             ,[0]
             ,[8]
             ,[0]])

GE_BS(G,H)
#results:

#we can observe that here change the row 1 and 2
# [[-1.  1. -1.  0.]
#  [ 0.  2.  5.  9.]
#  [ 0.  6. -4.  8.]
#  [ 1. -1.  1.  0.]]

# [[-1.  1. -1.  0.]
#  [ 0.  2.  5.  9.]
#  [ 0.  6. -4.  8.]]

# [[ -1.   1.  -1.   0.]
#  [  0.   2.   5.   9.]
#  [  0.   0. -19. -19.]]

# [[ -1.   1.  -1.   0.]
#  [  0.   2.   5.   9.]
#  [  0.   0. -19. -19.]]

# i_ 1 = 1.0   i_ 2 = 2.0   i_ 3 = 1.0


"""Small script that calculates the inertia matrix for common shapes. See
`this wiki <https://en.wikipedia.org/wiki/List_of_moments_of_inertia>`_ for more
information.
"""
import math

import numpy as np

# Script settings
CUBE_WIDTH = 0.02
CUBE_DEPTH = 0.032
CUBE_HEIGHT = 0.064

CYLINDER_RADIUS = 0.025
CYLINDER_HEIGHT = 0.02

MATERIAL_DENSITY = 2000

def get_cylinder_mass(radius, height, rho):
    """Calculate the mass of a cylinder.

    Args:
        radius (float): The radius of the cylinder [m].
        height (float): The height of the cylinder [m].
        rho (float): The material density [kg/m^3].

    Returns:
        float: The mass [kg].
    """
    return rho * math.pi * radius ** 2 * height

def get_cube_inertia(height, width, depth, mass):
    """Calculates the moment of inertia matrix for a cube.

    Args:
        height (float): The height of the cube [m].
        width (float): The width of the cube [m].
        depth (float): The length of the cube [m].
        mass (float): The mass of the cube [kg].

    Returns:
        numpy.ndarray: The inertia matrix [kg*m^2].
    """

    return np.array(
        [
            [(1 / 12) * mass * (width ** 2 + depth ** 2), 0, 0],
            [0, (1 / 12) * mass * (depth ** 2 + height ** 2), 0],
            [0, 0, (1 / 12) * mass * (width ** 2 + height ** 2)],
        ]
    )

def get_cube_mass(height, width, depth, rho):
    """Calculate the mass of a cube.

    Args:
        radius (float): The radius of the cylinder [m].
        height (float): The height of the cylinder [m].
        depth (float): The depth of the cylinder [m].
        rho (float): The material density [kg/m^3].

    Returns:
        float: The mass [kg].
    """
    return rho * height * width * depth

def get_cylinder_inertia(radius, height, mass):
    """Calculates the moment of inertia matrix for a cube.

    Args:
        radius (float): The radius of the cylinder [m].
        height (float): The height of the cylinder [m].
        mass (float): The mass of the cylinder [kg].

    Returns:
        numpy.ndarray: The inertia matrix [kg*m^2].
    """

    return np.array(
        [
            [(1 / 12) * mass * (3 * radius ** 2 + height ** 2), 0, 0],
            [0, (1 / 12) * mass * (3 * radius ** 2 + height ** 2), 0],
            [0, 0, (1 / 2) * mass * radius ** 2],
        ]
    )

if __name__ == "__main__":

    # Cue calculations
    print("==Cube==")
    cube_mass = get_cube_mass(
        height=CUBE_HEIGHT, width=CUBE_WIDTH, depth=CUBE_DEPTH, rho=MATERIAL_DENSITY
    )
    I_cube = get_cube_inertia(
        height=CUBE_HEIGHT, width=CUBE_WIDTH, depth=CUBE_DEPTH, mass=cube_mass
    )
    print(f"mass = {cube_mass}")
    print("inertia =")
    print(I_cube)
    print("\n")

    # Cylinder calculations
    print("==Cylinder==")
    cylinder_mass = get_cylinder_mass(
        radius=CYLINDER_RADIUS, height=CYLINDER_HEIGHT, rho=MATERIAL_DENSITY
    )
    I_cylinder = get_cylinder_inertia(
        radius=CYLINDER_RADIUS, height=CYLINDER_HEIGHT, mass=cylinder_mass
    )
    print(f"mass = {cylinder_mass}")
    print("inertia =")
    print(I_cylinder)


import numpy as np
import matplotlib.pyplot as plt

class Diff:
    def diff1(self,f,x):
        h = 1e-4
        return (f(x+h)-f(x-h)) / (2*h)

    def gradient(self,f,x):
        h = 1e-4
        grad = np.zeros_like(x)

        for idx in range(x.size):
            tmpVal = x[idx]

            x[idx] = tmpVal + h
            fxh1 = f(x)

            x[idx] = tmpVal - h
            fxh2 = f(x)

            grad[idx] = (fxh1 - fxh2) / (2*h)
            x[idx] = tmpVal
        return grad

    def diff_Line(self,a,x0,y0,x):
        return a*(x - x0)+y0

if __name__ == '__main__':
    df = Diff()
    def f(x):
        return 0.1*x**2 + 3*x + 9.0

    def f2(x):
        return x[0]**2 + x[1]**2

    ## 1D Diffrential
    x = np.arange(-10,10,0.1)
    y = f(x)

    x0 = x[50]
    y0 = y[50]

    diffline = df.diff_Line( df.diff1(f,x0) , x0,y0, x )

    plt.xlabel("x")
    plt.ylabel("y")
    plt.plot(x,y,'r')
    plt.plot(x0,y0,'g+')
    plt.plot(x,diffline,'b')
    #plt.show()

    ## Partial
    def ftmp1(x0):
        return x0**2 + 4.0**2.0

    res = df.diff1(ftmp1 , 3 )
    print(res)

    grad1 = df.gradient(f2 , np.array([3.0,4.0]))
    grad2 = df.gradient(f2 , np.array([0.0,4.0]))
    print(grad1)
    print(grad2)


import math
import numpy as np

def get_clip_mean(x, err=None, mask=None, high=3, low=3, maxiter=5):
    """Get the mean value of an input array using the sigma-clipping method

    Args:
        x (:class:`numpy.ndarray`): The input array.
        err (:class:`numpy.ndarray`): Errors of the input array.
        mask (:class:`numpy.ndarray`): Initial mask of the input array.
        high (float): Upper rejection threshold.
        low (float): Loweer rejection threshold.
        maxiter (int): Maximum number of iterations.

    Returns:
        tuple: A tuple containing:

            * **mean** (*float*) – Mean value after the sigma-clipping.
            * **std** (*float*) – Standard deviation after the sigma-clipping.
            * **mask** (:class:`numpy.ndarray`) – Mask of accepted values in the
              input array.
    """
    x = np.array(x)
    if mask is None:
        mask = np.zeros_like(x)<1

    niter = 0
    while(True):

        niter += 1
        if err is None:
            mean = x[mask].mean()
            std  = x[mask].std()
        else:
            mean = (x/err*mask).sum()/((1./err*mask).sum())
            std = math.sqrt(((x - mean)**2/err*mask).sum()/((1./err*mask).sum()))

        if maxiter==0 or niter>maxiter:
            # return without new mask
            break

        # calculate new mask
        new_mask = mask * (x < mean + high*std) * (x > mean - low*std)

        if mask.sum() == new_mask.sum():
            break
        else:
            mask = new_mask

    return mean, std, new_mask

def linear_fit(x,y,yerr=None,maxiter=10,high=3.0,low=3.0,full=False):
    """Fit the input arrays using a linear function.

    Args:
        x (:class:`numpy.ndarray`): The input X values.
        y (:class:`numpy.ndarray`): The input Y values.
        yerr (:class:`numpy.ndarray`): Errors of the Y array.
        maxiter (int): Maximum number of iterations.
        high (float): Upper rejection threshold.
        low (float): Lower rejection threshold.
        full (bool): `True` if return all information.

    Returns:
        tuple: A tuple containing:

            * **p** (:class:`numpy.ndarray`) – Parameters of the fitting.
            * **std** (*float*) – Standard deviation of the fitting.
            * **mask** (:class:`numpy.ndarray`) – Mask of the accepted values in
              the input array.
            * **func** (*func*) – Function.
            * **r2** (*float*) – *R*:sup:`2` value.
            * **p_std** (*tuple*) – Standar deviations of the parameters.
    """
    x = np.array(x)
    y = np.array(y)
    if yerr is not None:
        yerr = np.array(yerr)
    mask = np.ones_like(x)>0
    niter = 0
    func = lambda p, x: p[0] + p[1]*np.array(x)
    while(True):
        niter += 1
        if yerr == None:
            xm = x[mask].mean()
            ym = y[mask].mean()
            a = ((x[mask]-xm)*(y[mask]-ym)).sum()/((x[mask]-xm)**2).sum()
            b = ym - a*xm
            p = np.array([b,a])
            vy = func(p,x)
            std = (y[mask]-vy[mask]).std()
        else:
            w = 1./yerr[mask]**2
            w /= w.sum()
            b = (x[mask]*y[mask]*w).sum()
            c = (x[mask]*w).sum()
            d = (y[mask]*w).sum()
            e = (x[mask]**2*w).sum()
            f = e-c**2
            p = np.array([(e*d-c*b)/f, (b-c*d)/f])
            vy = func(p,x)
            std = math.sqrt(((y[mask]-vy[mask])**2*w).sum())

        if maxiter==0 or niter>maxiter:
            # return without new mask
            break

        # calculate new mask
        m1 = y > vy - low*std
        m2 = y < vy + high*std
        new_mask = np.logical_and(m1, m2)

        if mask.sum()==new_mask.sum() or new_mask.sum()<=2:
            break
        else:
            mask = new_mask
    if full:
        xm = x[mask].mean()
        ym = y[mask].mean()
        n  = mask.sum()
        lxx = (x[mask]**2).sum() - n*xm**2
        lyy = (y[mask]**2).sum() - n*ym**2
        lxy = (x[mask]*y[mask]).sum() - n*xm*ym
        r2 = lxy**2/lxx/lyy
        s = math.sqrt((lyy-lxy**2/lxx)/(n-2))
        sa = s/math.sqrt(lxx)
        sb = s*math.sqrt(1./n + xm**2/lxx)
        p_std = np.array([sb,sa])
        return p, std, mask, func, r2, p_std
    else:
        return p, std, mask, func

def iterative_polyfit(x, y, yerr=None, deg=3, mask=None, maxiter=10,
    upper_clip=None, lower_clip=None):
    """Fit data with polynomial iteratively. This is a wrap of numpy.polyfit
    function.

    Args:
        x (:class:`numpy.ndarray`): Input X values.
        y (:class:`numpy.ndarray`): Input Y values.
        yerr (:class:`numpy.ndarray`): Errors of **y**.
        deg (int): Degree of polynomial.
        mask (:class:`numpy.ndarray`): Input mask.
        maxiter (int): Maximum number of iterations.
        lower_clip (float): Lower sigma-clipping value.
        upper_clip (float): Upper sigma-clipping value.

    Returns:
        tuple: A tuple containing:

            * **coeff** (:class:`numpy.ndarray`) – Coefficients of polynomial.
            * **yfit** (:class:`numpy.ndarray`) – Fitted y values.
            * **yres** (:class:`numpy.ndarray`) – Residuals of y values.
            * **mask** (:class:`numpy.ndarray`) – Mask of y values.
            * **std** (float) – Standard deviation.
    """

    x = np.array(x)
    y = np.array(y)
    if yerr is None:
        pass
    else:
        yerr = np.array(yerr)
    if mask is None:
        mask = np.ones_like(x, dtype=np.bool)

    for ite in range(maxiter):
        coeff = np.polyfit(x[mask], y[mask], deg=deg)
        yfit = np.polyval(coeff, x)
        yres = y - yfit
        std = yres[mask].std(ddof=1)

        # replace np.nan by np.inf to avoid runtime warning
        yres[np.isnan(yres)] = np.inf

        # generate new mask
        # make a copy of existing mask
        new_mask = mask * np.ones_like(mask, dtype=np.bool)
        # give new mask with lower and upper clipping value
        if lower_clip is not None:
            new_mask *= (yres > -lower_clip * std)
        if upper_clip is not None:
            new_mask *= (yres < upper_clip * std)

        if new_mask.sum() == mask.sum():
            break
        mask = new_mask

    return coeff, yfit, yres, mask, std


from math import sqrt, exp, log, sin, pi
from sympy import Symbol, Limit, Derivative, solve, sympify, S, simplify, lambdify, pprint, init_printing, symbols
from fractions import Fraction

init_printing( order = 'rev-lex', use_unicode = True )

# 4.3.2

# Find the derivitave: f(x) =(2x)^3 * (3x)^2

Fx = ( 2*x )**3 * ( 3*x )**2
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.3.3

# Find the derivitave: f(x) = 7 * [ x ^ 7/3 + 11/7*x^7/5 + 13x^7/7 ] ^ 4/3

Fx = 7 * ( x ** 7/3 + 11/7*x**7/5 + 13*x**7/7 ) ** 4/3
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.3.9

# A particle's position is given by the function: x(t) = ( 4 - t ) ^ 3.
# What is the value of dx/dt when t = 3 ?

Ft = ( 4 - t ) ** 3
t = Symbol( 't' )
Dt = Derivative( Ft, t ).doit()
pprint( Dt )
Dt.subs( { t: 3 } )


import numpy as np

def loss_function(name, y, y_hat, derivative=False):
  """
  Computes the loss and its derivative

  Parameters
  ----------
  name: str
      Type of loss function.
      Options:
          mse ( Mean squared error )
          ce ( Cross entropy )

  y: list
      numpy array ( target )

  y_hat: list
      numpy array ( output )

  derivative: bool
      If True, returns the derivative of loss.
      Default: False

  Returns
  -------
  numpy array
  """

  # y - target, y_hat - output
  # Mean Squared Error
  if name == "mse":
      if derivative:
          return (y_hat - y)
      else:
          return np.mean((y - y_hat)**2)
  # Log-likelihood
  elif name == "ll":
      if derivative:
          return - (1 / y_hat)
      else:
          return -1 * np.log(y_hat)
  # y - target prob distro, y_hat - output prob distro
  # Cross Entropy
  elif name == "ce":
      if derivative:
          # if activation fn is sigmoid/softmax
          return (y_hat - y)
      else:
          return np.sum(np.nan_to_num(-y*np.log(y_hat)-(1-y)*np.log(1-y_hat)))

Activation functions and Loss functions/loss_functions.py


'''
  File: compress.py

  Author: Thomas Kost

  Date: 02 August 2021

  @breif performing simple image compression using a fourier basis
 '''
import numpy as np
from utility import *
import matplotlib.pyplot as plt
import cv2 as cv
import matplotlib as mpl
def compress(image : np.array, percentage: float, debug: bool  = False)->np.array:

    max_pixel_value = 256

    # Normalize by max value of an image pixel
    image = image/max_pixel_value

    # Take FFT of each pixel type
    rFimg = np.fft.fft2(image[:,:,0])
    gFimg = np.fft.fft2(image[:,:,1])
    bFimg = np.fft.fft2(image[:,:,2])

    # Plot Spectrums when debug requested
    if debug:
        plt.figure()
        plt.subplot(1,3,1)
        plt.imshow(np.log10(abs(np.fft.fftshift(rFimg))))
        plt.axis("off")
        plt.title("Red FFT")
        plt.subplot(1,3,2)
        plt.imshow(np.log10(abs(np.fft.fftshift(gFimg))))
        plt.axis("off")
        plt.title("Green FFT")
        plt.subplot(1,3,3)
        plt.imshow(np.log10(abs(np.fft.fftshift(bFimg))))
        plt.axis("off")
        plt.title("Blue FFT")

    # Sort FFT by magnitude
    rFimg_flat = np.sort(abs(rFimg.flatten()))
    gFimg_flat = np.sort(abs(gFimg.flatten()))
    bFimg_flat = np.sort(abs(bFimg.flatten()))

    # Find magnitude threshold for retaining Data
    max_val_red = rFimg_flat[int(len(rFimg_flat)*(1.0-percentage))]
    max_val_green = rFimg_flat[int(len(gFimg_flat)*(1.0-percentage))]
    max_val_blue = rFimg_flat[int(len(rFimg_flat)*(1.0-percentage))]

    # Create and apply data mask to keep top X percent of values
    r_mask = abs(rFimg) > max_val_red
    g_mask = abs(gFimg) > max_val_green
    b_mask = abs(bFimg) > max_val_blue
    rFimg = rFimg*r_mask
    gFimg = gFimg*g_mask
    bFimg = bFimg*b_mask

    # Perform IFFT
    r_img = np.fft.ifft2(rFimg)
    g_img = np.fft.ifft2(gFimg)
    b_img = np.fft.ifft2(bFimg)

    # Recombine colors de-normalize and cast as integer
    comp_img = (np.dstack((abs(r_img),abs(g_img), abs(b_img)))*max_pixel_value).astype(np.uint8)
    return comp_img

if __name__ == "__main__":
    # Read in RGB Image
    file = "../fixtures/jelly.jpg"
    img = read(file)

    #compress image
    compressed = compress(img, 0.05, True)

    # Plot Original image
    plt.figure()
    plt.subplot(1,2,1)
    plt.title("Original Image")
    plt.imshow(img)
    plt.axis("off")

    # Plot Compressed Image
    plt.subplot(1,2,2)
    plt.imshow(compressed)
    plt.title("Compressed Image")
    plt.axis("off")

    # Display plot
    plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 27 20:51:56 2017

@author: sitibanc
"""
import math
from scipy.stats import norm
import numpy as np
import matplotlib.pyplot as plt

# Black-Sholes定價模型
def blscall(s, l, t, r, sigma):
    d1 = math.log(s/l) + ( r + 0.5 * sigma ** 2) * t / sigma * math.sqrt(t)
    d2 = d1 - sigma * math.sqrt(t)
    return s * norm.cdf(d1) - l * math.exp(-r * t) * norm.cdf(d2)

# Monte-Carlo Method
def genMC():
    n = 100
    dt = t / n
    p = np.zeros([10000, n+1])

    for i in range(10000):
        p[i,0] = s
        for j in range(n):
            p[i,j+1] = p[i,j] * math.exp((r - 0.5 * sigma ** 2) * dt + np.random.normal(0, 1, 1) * sigma * math.sqrt(dt))
    return p

# 以Mote-Carlo算出的結果回推目前call option的合理價格
def getMCPrice(n, p):
    c = 0
    for i in range(n):
        if p[i,100] > l:
            c += (p[i,100] - l) / i
    return c * math.exp(-r * t)

s = 50.0
l = 40.0
t = 2.0
r = 0.08
sigma = 0.2

bls = blscall(s, l, t, r, sigma)
p = genMC()
# HW2-1-1
print("HW2-1-1: ")
plt.plot(p[:200].T)

# HW2-1-2
# Get MCPrice list
dif = [0] * 100
j = 0
# Another approach
# 直接在getMCPrice()中處理
# 處理i，當i % 100 == 99時（第n百筆），將累加的c拿出來平均i + 1，折現後存到(i + 1) / 100 - 1
for i in range(100, 10001, 100):
    dif[j] = getMCPrice(i, p) - bls
    j += 1
plt.plot(dif)


# TEST: Numeric Integration using adaptive Trapezoidal and Simpson methods

import matplotlib.pyplot as plt
import numpy as np
from quad import quad_trapezoid, quad_simpson
from quad import quad_trapezoid_adapt, quad_simpson_adapt

f = lambda x: -2*x**(-3) * np.cos(x**(-2))
a = 0.5;
b = 100;
Iexp = np.sin(10**(-4)) - np.sin(4) # Expected integral value

tol = 10**(-4)  # Error tolerance

print('---------------------------------------')
# Trapezoid adaprive method with relative error
I, x = quad_trapezoid_adapt(a, b, f, tol)
E = abs(Iexp - I)/Iexp      # Relative error
n = len(x)
print('> Trapezoid adaptive method\n I = {}\n E = {}\n n = {}'.format(I, E, n))
# Trapezoid method with relative error
I = quad_trapezoid(a, b, f, n-1)
E = abs(Iexp - I)/Iexp      # Relative error
print('> Trapezoid method\n I = {}\n E = {}\n n = {}'.format(I, E, n))
# Simpson's method with relative error
I = quad_simpson(a, b, f, n-1)
E = abs(Iexp - I)/Iexp      # Relative error
print('> Simpson method\n I = {}\n E = {}\n n = {}'.format(I, E, n))

print('---------------------------------------')
# Trapezoid adaprive method with relative error
I, x = quad_simpson_adapt(a, b, f, tol)
E = abs(Iexp - I)/Iexp      # Relative error
n = len(x)
print('> Simpson adaptive method\n I = {}\n E = {}\n n = {}'.format(I, E, n))
# Trapezoid method with relative error
I = quad_trapezoid(a, b, f, n-1)
E = abs(Iexp - I)/Iexp      # Relative error
print('> Trapezoid method\n I = {}\n E = {}\n n = {}'.format(I, E, n))
# Simpson's method with relative error
I = quad_simpson(a, b, f, n-1)
E = abs(Iexp - I)/Iexp      # Relative error
print('> Simpson method\n I = {}\n E = {}\n n = {}'.format(I, E, n))

# Plot function graph using the last Simpson nodes
N = 10000    # Number of points for a smooth plot
X = np.linspace(a, b, N)
#plt.plot(X, f(X))
fig, ax = plt.subplots()
ax.semilogx(X, f(X))

y = [ f(xi) for xi in x ]
for xi in x:
    ax.semilogx([xi,xi], [0, f(xi)],
             color='black', linestyle='dashed', linewidth=1)
plt.scatter(x, y, linewidths=0.2, marker='+')
plt.grid(True)
plt.show();


import numpy as np
import matplotlib.pyplot as plt

N = 50
x_start, x_end = -2.0, 2.0
y_start, y_end = -1.0, 1.0
x = np.linspace(x_start, x_end, N)
y = np.linspace(y_start, y_end, N)
X, Y = np.meshgrid(x, y)

kappa = 1.0
x_doublet, y_doublet = 0.0, 0.0

def get_velocity_doublet(strength, xd, yd, X, Y):
    u = (- strength / (2 * np.pi) *
         ((X - xd)**2 - (Y - yd) ** 2) / ((X - xd)**2 + (Y - yd)**2)**2)
    v = (- strength / (2 * np.pi) * 2 * (X - xd) * (Y - yd) /
         ((X - xd)**2 + (Y - yd)**2)**2)

    return u, v

def get_stream_function_doublet(strength, xd, yd, X, Y):
    psi = - strength / (2 * np.pi) * (Y - yd) / ((X - xd)**2 + (Y - yd)**2)

    return psi

u_doublet, v_doublet = get_velocity_doublet(kappa, x_doublet, y_doublet, X, Y)

psi_doublet = get_stream_function_doublet(kappa, x_doublet, y_doublet, X, Y)

size = 10
plt.figure(figsize=(size, (y_end - y_start) / (x_end - x_start) * size))
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.xlim(x_start, x_end)
plt.ylim(y_start, y_end)
plt.streamplot(X, Y, u_doublet, v_doublet,
               density=2, linewidth=1, arrowsize=1, arrowstyle='->')
plt.scatter(x_doublet, y_doublet, color='#CD2305', s=80, marker='o')

plt.show()

# Uniform flow past a doublet

u_inf = 1.0

u_freestream = u_inf * np.ones((N, N), dtype=float)
v_freestream = np.zeros((N, N), dtype=float)

psi_freestream = u_inf * Y

# Superposition of the doublet on the freestream flow
u = u_freestream + u_doublet
v = v_freestream + v_doublet
psi = psi_freestream + psi_doublet

# Plots the streamlines
size = 10
plt.figure(figsize=(size, (y_end - y_start) / (x_end - x_start) * size))
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.xlim(x_start, x_end)
plt.ylim(y_start, y_end)
plt.streamplot(X, Y, u, v, density=2, linewidth=1,
               arrowsize=1, arrowstyle='->')
plt.contour(X, Y, psi, levels=[
    0.], colors='#CD2305', linewidths=2, linestyles='solid')
plt.scatter(x_doublet, y_doublet, color='#CD2305', s=80, marker='o')

# Calculates the stagnation points
x_stagn1, y_stagn1 = +np.sqrt(kappa / (2 * np.pi * u_inf)), 0
x_stagn2, y_stagn2 = -np.sqrt(kappa / (2 * np.pi * u_inf)), 0

# Adds the stagnation points to the figure
plt.scatter([x_stagn1, x_stagn2], [y_stagn1, y_stagn2],
            color='g', s=80, marker='o')

plt.show()

# Computes the pressure coefficient field
cp = 1.0 - (u**2 + v**2) / u_inf**2

# Plots the pressure coefficient field
size = 10
plt.figure(
    figsize=(1.1 * size, (y_end - y_start) / (x_end - x_start) * size))
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.xlim(x_start, x_end)
plt.ylim(y_start, y_end)
contf = plt.contourf(
    X, Y, cp, levels=np.linspace(-2.0, 1.0, 100), extend='both')
cbar = plt.colorbar(contf)
cbar.set_label('$C_p$', fontsize=16)
cbar.set_ticks([-2.0, -1.0, 0.0, 1.0])
cbar.ax.invert_yaxis()
plt.scatter(x_doublet, y_doublet, color='#CD2305', s=80, marker='o')
plt.contour(X, Y, psi, levels=[
    0.], colors='#CD2305', linewidths=2, linestyles='solid')
plt.scatter([x_stagn1, x_stagn2], [y_stagn1, y_stagn2],
            color='g', s=80, marker='o')

plt.show()

# Cp-angle
theta = np.linspace(0, 2 * np.pi, 100)
cp = 1 - 4 * (np.sin(theta))**2

plt.figure()
plt.plot(theta, cp, color='k', lw=3)

plt.show()


import math
import random
import matplotlib.pyplot as plt
import numpy as np
def sphere(x,y):
    return x**2+y**2
def rosenbrock(x,y):
    return 100*(x**2 - y**2)+(1-x)**2
def griewank(x,y):
    return (x**2+y**2)/4000  - math.cos(x)*math.cos(y/math.sqrt(2))+1
def RandomNeighbour(x,y,StepSize,Function,Limits):
    x_values = [-StepSize+x,x,x+StepSize]
    y_values = [-StepSize+y,y,y+StepSize]
    Neighbours = []
    for i in x_values:
        for j in y_values:
            if Limits[0]<= i <=Limits[1] and Limits[2] <= j <=Limits[3]:

                Neighbours.append((Function(i,j),i,j))
    return Neighbours[random.randint(0,len(Neighbours)-1)]

Solution = list()
values = set()
def SimulatedAnnealing(Function,Limits,Step,IsMax):
    Temp= 1000
    EndTemp = 0.0000000000001
    Alpha = 0.8
    x = random.uniform(Limits[0],Limits[1])
    y = random.uniform(Limits[2],Limits[3])

    while Temp > EndTemp:
        for i in range(10):
            if IsMax:

                trial = RandomNeighbour(x,y,Step,Function,Limits)
                current = Function(x,y)
                delta = trial[0] - current
                values.add((x,y))
                if delta >0 :
                    x,y = trial[1],trial[2]

                else:
                    m = math.exp(delta/Temp)
                    p = random.uniform(0,1)
                    if p < m:
                        x,y = trial[1],trial[2]

            else:
                current = Function(x,y)
                trial = RandomNeighbour(x,y,Step,Function,Limits)
                delta = trial[0] - current
                values.add((x,y))
                if delta <0 :
                    x,y = trial[1],trial[2]

                else:
                    m = math.exp(-delta/Temp)
                    p = random.uniform(0,1)
                    if p < m:
                        x,y = trial[1],trial[2]
            Solution.append(current)

        Temp = Temp * Alpha
    return (current,x,y)
Limits = [-5,5,-5,5]
val=SimulatedAnnealing(griewank,Limits,0.1,False)
print(val)

x_values = list()
y_values = list()
for x,y in values:
    x_values.append(x)
    y_values.append(y)

# ax[0].plot(Solution,c='Red')
# ax[1].plot(x_values,c='Blue')
# ax[2].plot(y_values,c='Green')

plt.subplot(311)
plt.plot(Solution,c="Red")
plt.ylabel("Objective Function")

plt.subplot(312)
plt.plot(x_values,c="Blue")
plt.ylabel("X-coordinates")

plt.subplot(313)
plt.plot(y_values,c="Green")
plt.ylabel("Y-coordinates")
plt.show()


# Copyright (c) 2010-2022, InterDigital
# All rights reserved.

# See LICENSE under the root folder.

# Convert a LiDAR point cloud to a range image based on spherical coordinate conversion

import numpy as np

def cart2spherical(input_xyz):
    """Conversion from Cartisian coordinates to spherical coordinates."""

    r = np.sqrt(input_xyz[:, 0] ** 2 + input_xyz[:, 1] ** 2 + input_xyz[:, 2] ** 2)
    alpha = np.arctan2(input_xyz[:, 1], input_xyz[:, 0]) # corresponding to width
    epsilon = np.arcsin(input_xyz[:, 2] / r) # corrsponding to height
    return np.stack((r, alpha, epsilon), axis = 1)

def spherical2cart(input_spherical):
    """Conversion from spherical coordinates to Cartesian coordinates."""

    x = input_spherical[:, 0] * np.cos(input_spherical[:, 1]) * np.cos(input_spherical[:, 2])
    y = input_spherical[:, 0] * np.sin(input_spherical[:, 1]) * np.cos(input_spherical[:, 2])
    z = input_spherical[:, 0] * np.sin(input_spherical[:, 2])
    return np.stack((x, y, z), axis=1)

def pc2img(h_fov, v_fov, width, height, inf, data):
    """Convert a point cloud to an 2D image."""

    data_spherical = cart2spherical(data)

    # Project the point cloud onto an image!
    x = (data_spherical[:, 1] - h_fov[0]) / (h_fov[1] - h_fov[0])
    y = (data_spherical[:, 2] - v_fov[0]) / (v_fov[1] - v_fov[0])
    x = np.round(x * (width - 1)).astype(np.int32)
    y = np.round(y * (height - 1)).astype(np.int32)

    # exclude the pixels that are out of the selected FOV
    mask = ~((x < 0) | (x >= width) | (y < 0) | (y >= height))
    x, y = x[mask], y[mask]
    range = data_spherical[:, 0][mask]
    data_img = np.ones((height, width), dtype = np.float32) * inf
    data_img[y, x] = range

    return data_img

def img2pc(h_fov, v_fov, width, height, inf, data):
    """Convert an 2D image back to the point cloud."""

    alpha = (np.arange(width) / (width - 1)) * (h_fov[1] - h_fov[0]) + h_fov[0]
    epsilon = (np.arange(height) / (height - 1)) * (v_fov[1] - v_fov[0]) + v_fov[0]
    alpha, epsilon = np.meshgrid(alpha, epsilon)
    data_pc = np.stack((data, alpha, epsilon), axis=2)
    data_pc = data_pc.reshape(-1, 3)
    data_pc = data_pc[data_pc[:, 0] < inf - 1, :]
    data_pc = spherical2cart(data_pc)

    return data_pc


import numpy as np

def gaussian_KL(mu0, Sig0, mu1, Sig1inv):
    t1 = np.dot(Sig1inv, Sig0).trace()
    t2 = np.dot((mu1 - mu0), np.dot(Sig1inv, mu1 - mu0))
    t3 = -np.linalg.slogdet(Sig1inv)[1] - np.linalg.slogdet(Sig0)[1]
    return 0.5 * (t1 + t2 + t3 - mu0.shape[0])

def weighted_post_KL(th0, Sig0inv, sigsq, X, Y, w, reverse=True):
    muw, Sigw = weighted_post(th0, Sig0inv, sigsq, X, Y, w)
    mup, Sigp = weighted_post(th0, Sig0inv, sigsq, X, Y, np.ones(X.shape[0]))
    if reverse:
        return gaussian_KL(muw, Sigw, mup, np.linalg.inv(Sigp))
    else:
        return gaussian_KL(mup, Sigp, muw, np.linalg.inv(Sigw))

def weighted_post(th0, Sig0inv, sigsq, X, Y, w):
    Sigp = np.linalg.inv(Sig0inv + (w[:, np.newaxis] * X).T.dot(X) / sigsq)
    mup = np.dot(Sigp, np.dot(Sig0inv, th0) + (w[:, np.newaxis] * Y[:, np.newaxis] * X).sum(axis=0) / sigsq)
    return mup, Sigp

def potentials(sigsq, X, Y, samples):
    XST = X.dot(samples.T)
    return -1. / 2. * np.log(2. * np.pi * sigsq) - 1. / (2. * sigsq) * (
                Y[:, np.newaxis] ** 2 - 2 * XST * Y[:, np.newaxis] + XST ** 2)


import numpy as np
import matplotlib.pyplot as plt
import matplotlib2tikz.save as tikz_save
import math

def derivative(y, h, n: int=1):
	if n == 1:
		return lambda x: (y(x + h) - y(x - h)) / (2 * h)
	else:
		return derivative(derivative(y, h, n - 1), h, 1)

def integral(y, h, a, b):
	ret = 0

	sgn = 1

	if a > b:
		sgn = -1
		a, b = b, a

	if abs(b - a) < h:
		h *= abs(b - a)

	for i in np.arange(a, b, h):
		ret += y(i) * h

	return ret * sgn

def fourier(y, h, n, a, b):
	L = (b - a) / 2
	a_0 = integral(y, h, a, b) / (2 * L)
	a_n = [0] * n
	b_n = [0] * n
	for i in range(1, n + 1):
		a_n[i - 1] = (1 / L) * integral(lambda x: y(x) * np.cos(i * np.pi * x / L), h, a, b)
		b_n[i - 1] = (1 / L) * integral(lambda x: y(x) * np.sin(i * np.pi * x / L), h, a, b)

	return lambda x: fouriereval(x, a_0, a_n, b_n, L)

def fouriereval(x, a_0, a_n, b_n, l):
	ret = a_0

	for i in range(1, len(a_n) + 1):
		ret += a_n[i - 1] * np.cos(i * np.pi * x / l)
		ret += b_n[i - 1] * np.sin(i * np.pi * x / l)

	return ret

# def f(x):
# 	if x > 2:
# 		return f(x - 4)
# 	if x < -2:
# 		return f(x + 4)
# 	return ((x**3) - 4 * x) / 4

# def f(x):
# 	if x < -1:
# 		return f(x + 2)
# 	if x > 1:
# 		return f(x - 2)
# 	return -1 if x < 0 else 1

def fx(x, n):
	if n == 1:
		return np.sin(x)
	return fx(np.sin(x) * np.pi / 2, n - 1)

# def f(x):
# 	return np.cos(np.tan(np.sin(x)))

def sirc(x):
	return np.sqrt(1 - x**2)

def f(x):
	if x < -2:
		return f(x + 4)
	if x > 2:
		return f(x - 4)
	if x < 0:
		return -sirc(x + 1)
	else:
		return sirc(x - 1)

h = 0.001

x = np.arange(-4, 4, 0.01)

# kr = lambda x: derivative(f, h, 2)(x) / ((1 + derivative(f, h)(x)**2)**(3 / 2))

# dkr = derivative(kr, h)

# dy = derivative(f, h)

fr = fourier(f, h, 101, -2, 2)

plt.plot(x, np.vectorize(f)(x))
# plt.plot(x, np.vectorize(kr)(x))
# plt.plot(x, np.vectorize(dkr)(x))
# plt.plot(x, np.vectorize(dy)(x))
plt.plot(x, np.vectorize(fr)(x))

plt.axis([-4, 4, -5, 5])

plt.title("$f(x)$")

plt.grid(True)

tikz_save("PyPlotTesting/Figurer/" + "f" + str(1) + ".tikz", figureheight='\\figureheight', figurewidth='\\figurewidth')


import numpy as np
import math

# 激活函数

# sigmoid函数
def sigmoid(x):
    x_ravel = x.ravel()  # 将numpy数组展平
    length = len(x_ravel)
    y = []
    for index in range(length):
        curr = x_ravel[index]
        if curr >= 0:
            a = math.exp(-curr)
            y.append(1.0 / (1 + a))
        else:
            a = math.exp(curr)
            y.append(a / (a + 1))

    return np.array(y).reshape(x.shape)

# sigmoid函数
def d_sigmoid(x):
    return pow(sigmoid(x), 2)

# relu函数
def ReLU(x):
    return 1 * (x > 0) * x

def d_ReLU(x):
    y = (x > 0) * 1
    return y

if __name__ == '__main__':
    arr = np.array([
        [19, 44]
        , [20, -21]
        , [10, -3]
    ])

    print(arr.shape)
    print(ReLU(arr))
    print(d_ReLU(arr))


import numpy.matlib as M

def haar_matrix(dim):
    '''
    Generate haar transformation matrix.
    '''
    Q = M.matrix('1 1;-1 1')
    return 1 / M.sqrt(2) * M.kron(M.eye(dim / 2), Q)

def permutation_matrix(dim):
    '''
    Generate haar permuation matrix.
    '''
    I = M.eye(dim, dim)
    return M.concatenate((I[::2][:],
                          I[1::2][:]))

def haar_forward(image, dim=None, recursive=True):
    '''
    Apply haar transformation to image.

    :param image: square matrix
    :param dim: dimension
    :param recursive: apply recursively
    :return: None
    '''
    if dim is None:
        dim = M.shape(image)[0]

    if dim == 1:  # base case
        return

    P = permutation_matrix(dim)
    T = haar_matrix(dim)

    image[0:dim, 0:dim] = P * T * M.asmatrix(image[0:dim, 0:dim]) * T.T * P.T

    if recursive:
        haar_forward(image, dim / 2)

def haar_inverse(image, dim=2):
    '''
    Apply haar inverse transformation.

    :param image: square matrix
    :param dim: dimension
    :return: None
    '''
    P = permutation_matrix(dim)
    T = haar_matrix(dim)

    image[0:dim, 0:dim] = T.T * P.T * M.asmatrix(image[0:dim, 0:dim]) * P * T

    if dim == M.shape(image)[0]:
        return

    haar_inverse(image, dim * 2)


import numpy as np

def rot_x(phi):
    """Calculates a rotation matrix around the x-axis for the angle phi.

    Parameters
    ----------
    phi : float
        The value of the angle in radians.

    Return
    ------
    3x3 matrix
        A 3x3 matrix, such that its multiplication with a vector will rotate the vector by phi radians around the x-axis.
    """
    sin_phi = np.sin(phi)
    cos_phi = np.cos(phi)
    rot_mat = np.matrix([
        [1, 0, 0],
        [0, cos_phi, sin_phi ],
        [0, -sin_phi, cos_phi]])
    return rot_mat

def rot_y(phi):
    """Calculates a rotation matrix around the y-axis for the angle phi.

    Parameters
    ----------
    phi : float
        The value of the angle in radians.

    Return
    ------
    3x3 matrix
        A 3x3 matrix, such that its multiplication with a vector will rotate the vector by phi radians around the y-axis.
    """
    sin_phi = np.sin(phi)
    cos_phi = np.cos(phi)
    rot_mat = np.matrix([
        [cos_phi, 0, sin_phi ],
        [0, 1, 0],
        [-sin_phi, 0, cos_phi]])
    return rot_mat

def rot_x_optimzed(phi):
    n = len(phi)

    sin_phi = np.sin(phi).reshape((n, 1))
    cos_phi = np.cos(phi).reshape((n, 1))

    rotx = np.zeros((n, 3, 3))
    rotx[:, 1::3, 1] = cos_phi
    rotx[:, 1::3, 2] = sin_phi
    rotx[:, 2::3, 1] = -sin_phi
    rotx[:, 2::3, 2] = cos_phi
    rotx[:, 0::3, 0] = np.ones((n, 1))

    return rotx

def rot_y_optimzed(psi):
    n = len(psi)

    sin_psi = np.sin(psi).reshape((n, 1))
    cos_psi = np.cos(psi).reshape((n, 1))

    roty = np.zeros((n, 3, 3))
    roty[:, 0::3, 0] = cos_psi
    roty[:, 0::3, 2] = sin_psi
    roty[:, 2::3, 0] = -sin_psi
    roty[:, 2::3, 2] = cos_psi
    roty[:, 1::3, 1] = np.ones((n, 1))

    return roty

if __name__ == "__main__":
    a = np.array([1,2,3,4])
    b = np.array([1,2,3,4])
    c = rot_x_optimzed(a)
    d = rot_y_optimzed(b)
    print(c)
    print(d)


import sys
import numpy as np

def ang2mat(angle_data):
    alpha = angle_data[0]
    beta  = angle_data[1]
    gama  = angle_data[2]

    alpha_mat = np.matrix(np.eye(3))
    beta_mat = np.matrix(np.eye(3))
    gama_mat = np.matrix(np.eye(3))
    alpha_mat[1,1] =  np.cos(alpha)
    alpha_mat[1,2] = -np.sin(alpha)
    alpha_mat[2,1] =  np.sin(alpha)
    alpha_mat[2,2] =  np.cos(alpha)

    beta_mat[0,0] =  np.cos(beta)
    beta_mat[0,2] = -np.sin(beta)
    beta_mat[2,0] =  np.sin(beta)
    beta_mat[2,2] =  np.cos(beta)

    gama_mat[0,0] =  np.cos(gama)
    gama_mat[0,1] = -np.sin(gama)
    gama_mat[1,0] =  np.sin(gama)
    gama_mat[1,1] =  np.cos(gama)

    result_mat = gama_mat*beta_mat*alpha_mat
    return result_mat
def mat2ang(mat_data):
    alpha = np.arctan2(mat_data[2,1],mat_data[2,2])
    beta  = np.arctan2(mat_data[2,0],np.sqrt(mat_data[2,2]*mat_data[2,2]+mat_data[2,1]*mat_data[2,1]))
    gama  = np.arctan2(mat_data[1,0],mat_data[0,0])
    return [alpha,beta,gama]
def angpos2mat(angpos_data):
    result_mat = np.matrix(np.eye(4))
    result_mat[0:3,0:3] = ang2mat(angpos_data[3:6])
    result_mat[0:3,3]   = np.matrix(angpos_data[0:3]).T
    return result_mat
def mat2angpos(mat_data):
    result = np.zeros((6))
    result[0:3] = np.array(mat_data[0:3,3].T)
    result[3:6] = mat2ang(mat_data[0:3,0:3])
    return result

if True:
    file_name = sys.argv[1]

    data = np.loadtxt(file_name)

    data_size = data.shape[0]
    all_angpos = np.zeros((data_size-1,6))
    pose_last =np.matrix(np.eye(4,4))
    pose =np.matrix(np.eye(4,4))
    pose_last[0:3,:] = np.matrix(data[0,:].reshape(3,4))
    for i in range(1,data_size):
        pose[0:3,:] = np.matrix(data[i,:].reshape(3,4))
        motion = pose_last.I*pose
        angpos = mat2angpos(motion)
        all_angpos[i-1,:] = angpos
        pose_last = pose.copy()
        #print pose_line
    np.savetxt(sys.argv[2],all_angpos)
    print 'done'


import numpy as np
import jax.numpy as jnp

def radial_profile(data):
  """
  Compute the radial profile of 2d image
  :param data: 2d image
  :return: radial profile
  """
  center = data.shape[0]/2
  y, x = jnp.indices((data.shape))
  r = jnp.sqrt((x - center)**2 + (y - center)**2)
  r = r.astype('int32')

  tbin = jnp.bincount(r.ravel(), data.ravel())
  nr = jnp.bincount(r.ravel())
  radialprofile = tbin / nr
  return radialprofile

def measure_power_spectrum(map_data, pixel_size):
  """
  measures power 2d data
  :param power: map (nxn)
  :param pixel_size: pixel_size (rad/pixel)
  :return: ell
  :return: power spectrum

  """
  data_ft = jnp.fft.fftshift(jnp.fft.fft2(map_data)) / map_data.shape[0]
  nyquist = np.int(map_data.shape[0]/2)
  power_spectrum_1d =  radial_profile(jnp.real(data_ft*jnp.conj(data_ft)))[:nyquist] * (pixel_size)**2
  k = np.arange(power_spectrum_1d.shape[0])
  ell = 2. * np.pi * k / pixel_size / 360
  return ell, power_spectrum_1d

def make_power_map(power_spectrum, size, kps=None, zero_freq_val=1e7):
  #Ok we need to make a map of the power spectrum in Fourier space
  k1 = np.fft.fftfreq(size)
  k2 = np.fft.fftfreq(size)
  kcoords = np.meshgrid(k1,k2)
  # Now we can compute the k vector
  k = np.sqrt(kcoords[0]**2 + kcoords[1]**2)
  if kps is None:
    kps = np.linspace(0,0.5,len(power_spectrum))
  # And we can interpolate the PS at these positions
  ps_map = np.interp(k.flatten(), kps, power_spectrum).reshape([size,size])
  ps_map = ps_map
  ps_map[0,0] = zero_freq_val
  return ps_map # Carefull, this is not fftshifted


import numpy as np
from numpy.linalg import matrix_rank
from numpy.linalg import svd
from numpy.linalg import cond
from numpy.linalg import solve
import matplotlib.pyplot as plt

def readFile(fileName):
    returnContent = []
    with open(fileName,'r') as f:
        contents = f.readlines()
        for i in contents:
            returnContent.append(i.split(","))
        for i in returnContent:
            i[len(i)-1] = i[len(i)-1].replace("\n","")

        return returnContent

def helper():
    print("\n\n\n\n")

def getTranspose(matrix):
    tMatrix = [[0 for j in range(len(matrix[0]))] for i in range(len(matrix))]
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            tMatrix[j][i] = matrix[i][j]
    return tMatrix

def isSymmetrical(matrix):
    if len(matrix) != len(matrix[0]):
        return False
    tMatrix = getTranspose(matrix)
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            if matrix[i][j] != tMatrix[i][j]:
                # print(i,j)
                # print(matrix[i][j])
                # print(tMatrix[i][j])
                # print(matrix[j][i])
                return False
    return True

def isDiagonal(matrix):
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            if i != j and matrix[i][j] != 0:
                return False
    return True

def isOrthogonal(matrix):
    if len(matrix) != len(matrix[0]):
        return False
    tMatrix = getTranspose(matrix)                                                      #Transposed Matrix
    rMatrix = np.matmul(matrix,tMatrix)                                                 #Result Matrix
    for i in range(len(matrix)):
        if rMatrix[i][i] != 1:
            return False
    return isDiagonal(rMatrix)

def getSVD(matrix):
    u,s,h = svd(matrix)
    return min(s),max(s)

def generateRight(matrix):
    n = len(matrix)
    m = 1
    b = []
    for i in range(5):
        b = np.random.rand(n,m)
        try:
            x = solve(matrix,b)
            # print("{}. b = {}".format(i+1,b))
            # print("X found: {}".format(x))
        except:
            print("\nError - Couldn't find a valid x")
            return
    print("No problem finding a solution 5 randomly generated right-hand-sides (i.e. b)")

def nonZeroGraph(matrix):
    plt.spy(matrix,markersize='3')
    plt.grid('on')
    plt.title("Non-Zero Elements")
    plt.xlabel("col")
    plt.ylabel("row")
    plt.show()

def magHeatMap(matrix):
    plt.imshow(abs(matrix),cmap="hot",interpolation='nearest')
    plt.title("Magnitude of Elements")
    plt.xlabel("col")
    plt.ylabel("row")
    plt.colorbar()
    plt.show()

    pass

def mainHwFunction(fileName):
    contents = readFile(fileName)
    print("Data for: " + fileName)
    print("Matrix Dimentions : {} by {}".format(len(contents),len(contents[0])))        #Q1: Matrix Dimensions
    matrix = np.array(contents,dtype=float)
    print("Number of Non Zero Elements: {}".format(len(matrix[matrix != 0])))           #Q2: Zero Elements
    print("Is Symmetrical: {}".format(isSymmetrical(matrix)))                           #Q3: Symmetry
    print("Is Diagonal: {}".format(isDiagonal(matrix)))                                 #Q4: Diagonal
    print("Is Orthogonal: {}".format(isOrthogonal(matrix)))                             #Q5: Orthogonal
    print("Rank of Matrix: {}".format(matrix_rank(matrix)))                             #Q6: Rank
    min,max = getSVD(matrix)
    print("Smallest Singular Value: {} \nLargest Singular Value: {}".format(min,max))   #Q7&8: SVD
    print("Condition Number: {}".format(cond(matrix)))                                  #Q9: Condition Number
    generateRight(matrix)                                                               #Q10: Solving for a random b  (Ax = b)
    nonZeroGraph(matrix)
    magHeatMap(matrix)

mainHwFunction("mat5.txt")


"""
Solves the binary knapsack problem using a heuristic based on the
utility/weight ratio of the available elements.
"""

import numpy as np

def knapsack(weights, utilities, knapsack_weight):
    """
    Uses a greedy algorithm to solve the binary knapsack problem:
    1. Order the utility/weight (descendent)
    2. Put item in bag if it fits, else go to next item
    """
    weights, utilities = np.array(weights), np.array(utilities)
    number_of_items = weights.size

    benefits = utilities / weights

    ordered_indexes = np.argsort(benefits)[::-1]

    solution = np.zeros(number_of_items)

    # puts the item in the bag and if it excedes the weight, remove it from
    # bag and goes to the next iteration
    for i in ordered_indexes:
        solution[i] = 1
        if (solution * weights).sum() > knapsack_weight:
            solution[i] = 0

    return solution


import numpy as np
import scipy.linalg
from functools import partial

def fourier_diff_matrix(N, p=1):
    '''
    Returns spectral differentiation matrix of order p built on a grid x_j = jh where j = 1...N.
    The periodic interval is then (0; 2 pi].
    '''
    h = 2.*np.pi / N
    if p == 1:
        return scipy.linalg.circulant([1./2 * (-1)**j / np.tan(j*h/2.) if j != 0 else 0 for j in range(N)])
    elif p == 2:
        return scipy.linalg.circulant([-1./2 * (-1)**j / np.sin(j*h/2.)**2 if j != 0 else -np.pi**2 / (3.*h**2) - 1./6 for j in range(N)])
    Exception('p = {} is not implemented!'.format(p))

def cheb_points(N):
    '''
    Returns N Chebyshev points: x_j = cos(j pi / N) where j = 0,...,N.
    '''
    jj = np.arange(0, N + 1)
    return np.cos(jj*np.pi / N)

def cheb_diff_matrix(N, p=1):
    '''
    Returns spectral differentiation matrix of order p built on a grid x_j = cos(j pi / N) where j = 0,...,N.
    The interval is then [-1; 1].
    '''
    c = lambda idx: 2 if idx == 0 or idx == N else 1
    xx = cheb_points(N)
    D = np.zeros((N + 1, N + 1))
    for i in range(D.shape[0]):
        for j in range(D.shape[0]):
            if i != j:
                D[i, j] = c(i) / c(j) * (-1)**(i+j) / (xx[i] - xx[j])
        D[i, i] = -np.sum(D[i, :])
    return np.linalg.matrix_power(D, p)

def spectral_diff_via_matrix_multiplication(v, D_matrix=None, D_matrix_builder=fourier_diff_matrix):
    if D_matrix is None:
        D_matrix = D_matrix_builder()
    return np.dot(D_matrix, v)

def fourier_diff_via_matrix_multiplication(v, D_matrix=None):
    return spectral_diff_via_matrix_multiplication(v, D_matrix=D_matrix, D_matrix_builder=partial(fourier_diff_matrix, len(v)))

def cheb_diff_via_matrix_multiplication(v, D_matrix=None):
    return spectral_diff_via_matrix_multiplication(v, D_matrix=D_matrix, D_matrix_builder=partial(cheb_diff_matrix, len(v) - 1))

def fourier_diff_via_fft(v, order):
    N = len(v)
    if N % 2 != 0:
        Exception('Only even number of nodes can be considered')
    hat_v = np.fft.fft(v)
    diff_coeffs = np.array([(1j * k)**order for k in list(range(int(N//2))) + list(range(int(-N//2), 0))]) # it replicates the standard order of the Fourier coefficients in hat_v:
    # A[0] contains the zero-frequency term, A[1:n/2] contains the positive-frequency terms,
    # and A[n/2+1:] contains the negative-frequency terms, in order of decreasingly negative frequency.
    hat_w = diff_coeffs * hat_v
    return np.fft.ifft(hat_w)

def cheb_diff_via_fft(v, order):
    N = len(v) - 1
    x = cheb_points(N)
    V_ = np.r_[v, v[-2:0:-1]] # V = [v_0, ..., v_N, v_{N-1}, ..., v_1]
    hat_V = np.fft.fft(V_)
    diff_coeffs = np.array([(1j * k)**order for k in list(range(N)) + list(range(-N, 0))]) # it replicates the standard order of the Fourier coefficients in hat_v:
    # A[0] contains the zero-frequency term, A[1:n/2] contains the positive-frequency terms,
    # and A[n/2+1:] contains the negative-frequency terms, in order of decreasingly negative frequency.
    hat_W = diff_coeffs * hat_V
    W_ = np.real(np.fft.ifft(hat_W))
    w = np.zeros_like(v)
    w[1:-1] = np.array([-W_[i] / np.sqrt(1 - x[i]**2) for i in range(1, N)])
    w[0] = 1./N * (np.sum([i**2 * np.real(hat_V[i]) for i in np.arange(1, N)]) + 1./2 * N**2 * np.real(hat_V[N]))
    w[N] = 1./N * (np.sum([(-1)**(i+1) * i**2 * np.real(hat_V[i]) for i in np.arange(1, N)]) + 1./2 * (-1)**(N+1) * N**2 * np.real(hat_V[N]))
#    if N % 2 != 0:
#        Exception('Only even number of nodes can be considered')
    return w


"""
Filename: quadsums.py

Authors: Thomas Sargent,  John Stachurski

This module provides functions to compute quadratic sums of the form described
in the docstrings.

"""

import numpy as np
from numpy import sqrt, dot
import scipy.linalg

def var_quadratic_sum(A, C, H, beta, x0):
    r"""
    Computes the expected discounted quadratic sum

    .. math::

        q(x_0) = E \sum_{t=0}^{\infty} \beta^t x_t' H x_t

    Here {x_t} is the VAR process x_{t+1} = A x_t + C w_t with {w_t}
    standard normal and x_0 the initial condition.

    Parameters
    ----------
    A : array_like(float, ndim=2)
        The matrix described above in description.  Should be n x n
    C : array_like(float, ndim=2)
        The matrix described above in description.  Should be n x n
    H : array_like(float, ndim=2)
        The matrix described above in description.  Should be n x n
    beta: scalar(float)
        Should take a value in (0, 1)
    x_0: array_like(float, ndim=1)
        The initial condtion. A conformable array (of length n, or with
        n rows)

    Returns
    -------
    q0: scalar(float)
        Represents the value q(x_0)

    Remarks: The formula for computing q(x_0) is q(x_0) = x_0' Q x_0 + v
    where

        Q is the solution to Q = H + beta A' Q A and
        v = \trace(C' Q C) \beta / (1 - \beta)

    """
    # == Make sure that A, C, H and x0 are array_like == #
    A, C, H = list(map(np.atleast_2d, (A, C, H)))
    x0 = np.atleast_1d(x0)
    # == Start computations == #
    Q = scipy.linalg.solve_discrete_lyapunov(sqrt(beta) * A.T, H)
    cq = dot(dot(C.T, Q), C)
    v = np.trace(cq) * beta / (1 - beta)
    q0 = dot(dot(x0.T, Q), x0) + v

    return q0

def m_quadratic_sum(A, B, max_it=50):
    r"""
    Computes the quadratic sum

    .. math::

        V = \sum_{j=0}^{\infty} A^j B A^{j'}

    V is computed by using a doubling algorithm. In particular, we
    iterate to convergence on V_j with the following recursions for j =
    1, 2,... starting from V_0 = B, a_0 = A:

    .. math::

        a_j = a_{j-1} a_{j-1}

        V_j = V_{j-1} + a_{j-1} V_{j-1} a_{j-1}'

    Parameters
    ----------
    A : array_like(float, ndim=1)
        An n x n matrix as described above.  We assume in order for
        convergence that the eigenvalues of A have moduli bounded by
        unity
    B : array_like(float, ndim=1)
        An n x n matrix as described above.  We assume in order for
        convergence that the eigenvalues of A have moduli bounded by
        unity
    max_it : scalar(int), optional(default=50)
        The maximum number of iterations

    Returns
    ========
    gamma1: array_like(float, ndim=2)
        Represents the value V

    """
    alpha0 = A
    gamma0 = B

    diff = 5
    n_its = 1

    while diff > 1e-15:

        alpha1 = alpha0.dot(alpha0)
        gamma1 = gamma0 + np.dot(alpha0.dot(gamma0), alpha0.T)

        diff = np.max(np.abs(gamma1 - gamma0))
        alpha0 = alpha1
        gamma0 = gamma1

        n_its += 1

        if n_its > max_it:
            raise ValueError('Exceeded maximum iterations of %i.' % (max_it) +
                             ' Check your input matrices')

    return gamma1


import numpy as np

def det(x):
    """
    Calculate the determinant of ndarray `x`. This is similar to np.linalg.det,
    but the square dimensions are at the beginning instead of at the end.

    Parameters
    ----------
    * `x` : array_like, shape (ndim, ndim, ...)
        An ndarray which determinant to be calculated

    Returns
    -------
    * array_like or float
        The determinant of the `x`, arranged with shape: x.shape[2:]
    """
    # shape checks
    x = np.asarray(x)
    if (len(x.shape) < 2):
        raise ValueError("x must have at least 2 dimensions")
    if x.shape[0] != x.shape[1]:
        raise ValueError("The first two dimensions must equal")

    nd = x.shape[0]
    ndim = len(x.shape)
    if nd == 1:
        return _det_1d(x, ndim)
    elif nd == 2:
        return _det_2d(x, ndim)
    elif nd == 3:
        return _det_3d(x, ndim)
    else:
        return _det_nd(x, nd, ndim)

def _det_1d(x, ndim):
    idx = tuple([0,0] + ([slice(None, None, None)] * (ndim-2)))
    return x[idx]

def _det_2d(x, ndim):
    rest_idx = [slice(None, None, None)] * (ndim-2)
    i00 = tuple([0,0] + rest_idx)
    i01 = tuple([0,1] + rest_idx)
    i10 = tuple([1,0] + rest_idx)
    i11 = tuple([1,1] + rest_idx)
    return x[i00] * x[i11] - x[i01] * x[i10]

def _det_3d(x, ndim):
    rest_idx = [slice(None, None, None)] * (ndim-2)
    i00 = tuple([0,0] + rest_idx)
    i01 = tuple([0,1] + rest_idx)
    i02 = tuple([0,2] + rest_idx)
    i10 = tuple([1,0] + rest_idx)
    i11 = tuple([1,1] + rest_idx)
    i12 = tuple([1,2] + rest_idx)
    i20 = tuple([2,0] + rest_idx)
    i21 = tuple([2,1] + rest_idx)
    i22 = tuple([2,2] + rest_idx)
    return \
        x[i00]*x[i11]*x[i22] + x[i01]*x[i12]*x[i20] + x[i02]*x[i10]*x[i21] -\
        (x[i02]*x[i11]*x[i20] + x[i00]*x[i12]*x[i21] + x[i22]*x[i01]*x[i10])

def _det_nd(x, nd, ndim):
    # rearrange matrix to have shape: outshape + [ndim, ndim]
    perm_idx = list(range(2,ndim)) + list(range(2))
    y = np.transpose(x, perm_idx)
    # calculate and return the determinant
    return np.linalg.det(y)


# Kaggle metrcis
# Krzysztof Joachimiak 2017

import numpy as np
from kaggle_metrics.utils import check_shapes, align_shape

# REGRESSION METRICS

def mean_absolute_error(y_true, y_pred):

    '''

    Mean absolute error.

    Parameters
    ----------
    y_true: ndarray
        Ground truth
    y_pred: ndarray
        Array of predictions

    Returns
    -------
    rmsle: float
        Mean absolute error

    References
    ----------
    .. [1] https://www.kaggle.com/wiki/MeanAbsoluteError

    '''

    # Check shapes
    y_true, y_pred = align_shape(y_true, y_pred)
    check_shapes(y_true, y_pred)

    return np.abs(y_true - y_pred).mean()

def weighted_mean_absolute_error(y_true, y_pred, weights):

    '''

    Weighted mean absolute error.

    Parameters
    ----------
    y_true: ndarray
        Ground truth
    y_pred: ndarray
        Array of predictions

    Returns
    -------
    rmsle: float
        Weighted mean absolute error

    References
    ----------
    .. [1] https://www.kaggle.com/wiki/WeightedMeanAbsoluteError

    '''

    # Check shapes
    y_true, y_pred = align_shape(y_true, y_pred)
    check_shapes(y_true, y_pred)

    return (weights * np.abs(y_true - y_pred)).mean()

def root_mean_squared_error(y_true, y_pred):
    '''

    Root mean squared error.

    Parameters
    ----------
    y_true: ndarray
        Ground truth
    y_pred: ndarray
        Array of predictions

    Returns
    -------
    rmsle: float
        Root mean squared error

    References
    ----------
    .. [1] https://www.kaggle.com/wiki/RootMeanSquaredError

    '''

    # Check shapes
    y_true, y_pred = align_shape(y_true, y_pred)
    check_shapes(y_true, y_pred)

    return np.sqrt(((y_true - y_pred)**2).mean())

def root_mean_squared_logarithmic_error(y_true, y_pred):
    '''

    Root mean squared logarithmic error.

    Parameters
    ----------
    y_true: ndarray
        Ground truth
    y_pred: ndarray
        Array of predictions

    Returns
    -------
    rmsle: float
        Root mean squared logarithmic error

    References
    ----------
    .. [1] https://www.kaggle.com/wiki/RootMeanSquaredLogarithmicError
    .. [2] https://www.slideshare.net/KhorSoonHin/rmsle-cost-function

    '''

    # Check shapes
    y_true, y_pred = align_shape(y_true, y_pred)
    check_shapes(y_true, y_pred)

    return np.sqrt(((np.log(y_pred + 1) - np.log(y_true + 1))**2).mean())

def mean_absolute_percentage_error(y_true, y_pred):
    '''

    Mean absolute percentage error

    Parameters
    ----------
    y_true: ndarray
    Ground truth
    y_pred: ndarray
    Array of predictions

    Returns
    -------
    mean_absolute_percentage_error: float
        Mean absolute percentage error

    References
    ----------
    .. [1] https://en.wikipedia.org/wiki/Mean_absolute_percentage_error

    '''
    return (np.abs(y_true - y_pred) / y_true).mean()

def mean_percentage_error(y_true, y_pred):
    '''

    Mean percentage error

    Parameters
    ----------
    y_true: ndarray
        Ground truth
    y_pred: ndarray
        Array of predictions

    Returns
    -------
    mean_percentage_error: float
        Mean percentage error

    References
    ----------
    .. [1] https://en.wikipedia.org/wiki/Mean_percentage_error

    '''
    return ((y_true - y_pred) / y_true).mean()

# aliases
mae = mean_absolute_error
wmae = weighted_mean_absolute_error
rmse = root_mean_squared_error
rmsle = root_mean_squared_logarithmic_error
mape = mean_absolute_error
mpe = mean_percentage_error
mean_absolute_percentage_deviation = mean_absolute_percentage_error
mapd = mean_absolute_percentage_deviation


from __future__ import division
import warnings

from scipy.misc import derivative
from scipy.optimize import fminbound as fmin

import matplotlib.pyplot as plt
from prettytable import PrettyTable as PT
from time import localtime, strftime

import numpy as np
import decimal as dm

warnings.filterwarnings("ignore")

'''-----------------------------------------------------------------------------
Functions: f(x),f'(x) and f^4(x) and error function 10^-x
-----------------------------------------------------------------------------'''
f = lambda x : (x+np.log( np.sin((x**2)+np.arctan(np.sqrt(1+(x**3))))+1))*x
f_1 = lambda x: derivative(f,x,n=1)
f_4 = lambda x: derivative(f,x,n=4,order=5)

error = lambda x: 10**(-x)

'''-----------------------------------------------------------------------------
M calculation, max|f'(x)| or max|f^4(x)| for xE]a,b[
-----------------------------------------------------------------------------'''
def M(lower,upper,simpson=True):
    if simpson:
        return abs(f(fmin(lambda x: -f_4(x),lower,upper)))
    else:
        return abs(f(fmin(lambda x: -f_1(x),lower,upper)))

'''-----------------------------------------------------------------------------
Simpson Rule for Integration Approximation
-----------------------------------------------------------------------------'''
#Organize data and display
def simpson(a,b,errors):
    table = PT()
    table.title = "Numerical Approximation for Integration using Simpson Rule"
    table.field_names = ["Error","N","Approximation M="+str(M(a,b))]

    for e in errors:
        s,n = simpsonRule(a,b,e+1)
        table.add_row([str(error(e+1)),str(n),str(dm.Decimal(s))])
    print(table)
    print("\n"*3)

#Determine n for error e
def determineNSimpson(a,b,e):
    m = M(a,b)
    v = (np.power((b-a),5)*m)/(e*180)
    v = np.power(v,(1./4.))
    m = int(v)+2 if int(v)%2==0 else int(v)+1
    return m

#Actual method
def simpsonRule(a,b,e):
    n = determineNSimpson(a,b,error(e))
    val = lambda x: 2 if (i%2==0) else 4
    h = (b-a)/n
    s = f(a)+f(b)
    for i in range(n):
        s += val(i)*f(a+i*h)
    return (h/3)*s,n

'''-----------------------------------------------------------------------------
Rectangle Rule for Integration Approximation
-----------------------------------------------------------------------------'''
#Organize data and display
def rectangle(a,b,k,d=True):
    #if d is true,it will show the graph of approximation for every k
    m = M(a,b,simpson=False) #m calculation~

    table = PT()
    table.title = "Numerical Approximation for Integration using Rectangles"
    table.field_names = ["N","Approximation M="+str(m),"Error"]

    for i in range(1,k+1):
        s,e = rectangleRule(a,b,2**i,m,d);
        er = '%.2E' %dm.Decimal(str(e))
        table.add_row([str(2**i),str(dm.Decimal(s)),er])
    print(table)
    print("\n"*3)

#Rectangle rule method
def rectangleRule(a,b,n,m,d=True):
    step = (b-a)/n
    s = 0.0
    for i in range(n):
        s += f((a+step/2)+i*step)
    s *= step
    #error calculation
    e = (np.power((b-a),2)/(n*2))*m

    if d and n<=256:#draw option
        draw(a,b,n);
    return s,e
#Draw f and approximation rectangles
def draw(a,b,n):
    v = np.linspace(a,b,n)
    plt.plot(v,f(v),color='red')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title('Numerical approximation: Rectangular, N='+str(n))
    for i in range(1,len(v)):
        c = v[i-1]
        d = v[i]
        plt.plot([c,c],[0,f((c+d)/2)],color='blue')
        plt.plot([d,d],[0,f(d)],color='blue')
        plt.plot([c,d],[f((c+d)/2),f((c+d)/2)],color='blue')
    plt.savefig("./Images/RectangleDraw_N"+str(n)+"_"+strftime("%Y%m%d_%H%M%S", localtime())+".png",dpi=300, bbox_inches="tight")
    plt.cla()
    plt.clf()
    plt.close()

#-------------------------------------------------------------------------------
disp = input("Display Rectangles Graph? (yes/no)")
disp = disp == 'yes'
print
simpson(0,1,[7,12])#apply simpson to error 7 and 12 in [0,1]
rectangle(0,1,20,d=disp) #apply rectangle in [0,1] in n points n=1(1)20


import sympy as sp, math as m

x = sp.symbols('x')
sp.init_printing(use_unicode=True)

#f=sp.Lambda(x, x*sp.cos(x)-x**2*sp.sin(x))

def f(x):
    #return x*m.cos(x)-x**2*m.sin(x)
    #return m.cos(x)
    return x**3*m.cos(x**2+3)

def lagrange(nodes):

    l = []
    for i in range(len(nodes)):
        l.append(1)
        for j in range(len(nodes)):
            if j != i:
                l[i]*=sp.poly((x-nodes[j])/(nodes[i]-nodes[j]))

    return l

def n_c(f, a, b, n):

    nodes = []
    h = (b-a)/(n+2)

    for i in range(n+1):
        k = a+(i+1)*h
        nodes.append(k)

    if n == 0:
        return (b-a)*f(a+h)

    l = lagrange(nodes)

    result = 0

    for i in range(n+1):
        result += (l[i].integrate()(b)-l[i].integrate()(a))*f(nodes[i])

    return result

n = 0
a, b = 1, 1.5

result = n_c(f, a, b, n)

print(result)

# Ej 14, punto medio:
"""
def fa(x):
    return x**2*m.log(x)

def fb(x):
    return x**3*m.exp(-x)

def fc(x):
    return 3*x/(x**2-4)

def fd(x):
    return m.cos(x)*m.exp(3*x)

print("a) " + str(n_c(fa, 1, 1.5, 0)))
print("b) " + str(n_c(fb, 0, 1, 0)))
print("c) " + str(n_c(fc, 1, 1.8, 0)))
print("d) " + str(n_c(fd, 0, m.pi/4, 0)))
"""


#T# when two numbers have no common divisor, their common divisor is then 1

#T# to calculate the greatest common divisor, the numpy package is used
import numpy as np

#T# the gcd function from the numpy package calculates the greatest common divisor of two numbers
num1 = 8
num2 = 12
num3 = np.gcd(num1, num2) # 4

#T# the reduce function from the gcd module of the numpy package calculates the greatest common divisor of a set of numbers inside a one dimensional array
arr1 = np.array([12, 18, 36, 60])
num1 = np.gcd.reduce(arr1) # 6