import streamlit as st
from datetime import datetime
import matplotlib.pyplot as plt
from tree_generator import BinomialTree
import numpy as np
st.set_option('deprecation.showPyplotGlobalUse', False)
# headings
month = datetime.now().month
title = "Binomial Tree Option Pricing"
st.title(title + "🌲🎄")
st.sidebar.title("Parameters")
# user inputs on sidebar
S = st.sidebar.number_input('Stock Price (S)', value=100.,)
K = st.sidebar.number_input('Exercise Price (K)', value=100.,)
T = st.sidebar.number_input('Time Periods (T)', value=2., max_value=15.)
dt =  st.sidebar.number_input('Time step (dt)', value=1., max_value=15.,step=0.01)
r = st.sidebar.number_input('Inter-period Interest Rate (r)', value=0.05,)
q = st.sidebar.number_input('Dividend Yield (q)', value=0.0,)
sigma = st.sidebar.number_input('stock annualized volatility (sigma)', value=0.1,min_value=0.)

tree = BinomialTree()
tree.fit(r,q,sigma)
price_tree = tree.create_price_tree(S,dt,T)
st.sidebar.write("Stock Upper Factor (u) ", round(tree.u, 3))
st.sidebar.write("Stock Down Factor (d) ", round(tree.d, 3))
st.sidebar.write("Risk Neutral Probability (p) ", round(tree.p, 3))
# back to main body
st.header("*Cox-Ross-Rubinstein (CRR) binomial tree*")
st.markdown("This visualisation aims to explore the dynamics of CRR binomial tree in option pricing. "
            "https://en.wikipedia.org/wiki/Binomial_options_pricing_model"
            )
st.subheader('Key:')
c1,c2,c3,c4,c5 = st.columns(5)
with c1:
    price = st.checkbox('price tree')
with c2:
    European_call = st.checkbox('European Call tree')
with c3:
    European_put = st.checkbox('European Put tree')
with c4:
    American_call = st.checkbox('American Call tree')
with c5:
    American_put = st.checkbox('American Put tree')
# plot stock tree
if price:
    st.pyplot(tree.plot_tree(price_tree))
if European_call:
    payoff = tree.create_payoff_tree(price_tree,lambda x:np.fmax(x-K,0))
    st.pyplot(tree.plot_tree(payoff))
    st.write("European Call price ", round(tree.compute_payoff(payoff), 3))
if European_put:
    payoff = tree.create_payoff_tree(price_tree,lambda x:np.fmax(K-x,0))
    st.pyplot(tree.plot_tree(payoff))
    st.write("European put price ", round(tree.compute_payoff(payoff), 3))
if American_call:
    payoff = tree.create_payoff_tree(price_tree,lambda x:np.fmax(x-K,0),style='American')
    st.pyplot(tree.plot_tree(payoff))
    st.write("American Call price ", round(tree.compute_payoff(payoff,style='American'), 3))
if American_put:
    payoff = tree.create_payoff_tree(price_tree,lambda x:np.fmax(K-x,0),style='American')
    st.pyplot(tree.plot_tree(payoff))
    st.write("American Put price ", round(tree.compute_payoff(payoff,style='American'), 3))

st.subheader("Disclaimer")
st.write("All information aims to provide for educational purposes only and does not constitute financial advice")


# -*- coding: utf-8 -*-
import numpy as np
import numpy.random as npr
import matplotlib.pylab as plt

RANDOM_SEED = 1  # random seed
SIZE = 10000  # 試行回数
EXPECTED_V = 5  # 期待値

def x_uniform():
    # 一様分布
    # aからbまで一様の確率のときその間のxを返す。
    a = 0
    b = EXPECTED_V * 2 - a
    return npr.uniform(a, b, SIZE)

def x_poisson():
    # ポアソン分布
    # 単位時間あたりλ回起こる事象がちょうどk回発生するときのkを返す
    lambd = EXPECTED_V  # 期待値=λとなる
    return npr.poisson(lambd, size=SIZE)

def x_exponential():
    # 指数分布
    # 単位時間当たり平均λ回起こる事象＝平均1/λ時間で1回発生する事象の発生間隔がt時間であるときのtを返す
    # 平均余命など。
    lambd = 1.0 / EXPECTED_V  # 1分あたりlambd回発生する。= 平均1/lambd時間の間隔で1回発生
    return npr.exponential(1. / lambd, size=SIZE)  # 発生間隔をランダムに返す

def x_erlang():
    # アーラン分布
    # アーラン分布はガンマ分布の形状母数kを正整数に限定したもの
    k = 3
    lambd = k / EXPECTED_V  # 期待値 = k/λとなる。
    return npr.gamma(k, 1. / lambd, size=SIZE)

def histogram(x_distribution, ax, title="histogram"):
    mean = np.mean(x_distribution)
    print("%s: mean:%f" % (title, mean))
    bins = np.arange(np.floor(np.amin(x_distribution)),
                     np.ceil(np.amax(x_distribution)) + 1.0, 1.)
    n, bins, patches = ax.hist(
        x_distribution, bins=bins, align='left', normed=1, color="gray", edgecolor="k")

    ax.set_title(title, fontsize=14)
    ax.set_xlabel('value')
    ax.set_ylabel('frequency / N')

if __name__ == '__main__':
    npr.seed(RANDOM_SEED)
    fig = plt.figure()
    fig.suptitle('Histogram: N=%d, E=%-4.2f' % (SIZE, EXPECTED_V), fontsize=16)
    fig.subplots_adjust(left=None, bottom=None, right=None,
                        top=None, wspace=0.3, hspace=0.4)

    ax1 = fig.add_subplot(221)
    histogram(x_uniform(), ax1, title="uniform")

    ax2 = fig.add_subplot(222)
    histogram(x_poisson(), ax2, title="poisson")

    ax3 = fig.add_subplot(223)
    histogram(x_exponential(), ax3, title="exponential")

    ax4 = fig.add_subplot(224)
    histogram(x_erlang(), ax4, title="erlang(k=3)")

    plt.show()


import numpy
import scipy.stats

def gen_paths(S0, r, sigma, T, M, I):
    """ Generates Monte Carlo paths for geometric Brownian motion.

    Parameters
    ==========
    S0: float
        initial stock/index value. Irrelevant if you only care about relative returns
    r: float
        constant short rate ("mean return")
    sigma: float
        constant volatility ("standard deviation")
    T: float
        final time horizon (always specify 1.0?)
    M: int
        number of time steps/intervals
    I: int
        number of paths to be simulated

    Returns
    =======
    paths : ndarray, shape (M+1, I)
    """

    dt = float(T) / M
    paths = numpy.zeros((M+1, I), numpy.float64)
    paths[0] = S0
    for t in range(1, M+1):
        rand = numpy.random.standard_normal(I)
        rand = (rand - rand.mean()) / rand.std()
        paths[t] = paths[t - 1] * numpy.exp(( r - 0.5 * sigma ** 2) * dt + sigma * numpy.sqrt(dt) * rand)
    return paths

if __name__ == '__main__':
    S0 = 100.
    r = 0.05
    sigma = 0.2
    T = 1.0
    M = 50
    I = 250000

    paths = gen_paths(S0, r, sigma, T, M, I)

    log_returns = numpy.log(paths[1:] / paths[0:-1])

    # How to apply metrics.ssr or metrics.pwa to this vectorised?
    print(len(log_returns[0]))


from sympy import Symbol, Derivative, sympify, pprint

def derivative_calc(expr, var):
    var = Symbol(var)
    derivative = Derivative(expr, var).doit()
    pprint(derivative)

if __name__ == "__main__":
    try:
        expr = sympify(input("Enter an expression to calculate: "))
        var = input("Enter the variable to differentiate with respect to: ")
    except Exception as err:
        print("Invalid input", err)
    else:
        derivative_calc(expr, var)

# todo : command line version


import math
import numpy as np
from matplotlib import pyplot as plt
from .Generaldistribution import Distribution

class Weibull(Distribution):
  """ Weibull distribution class for calculating and
  visualizing a Weibull distribution.

  Attributes:

      mean (float): the mean value of the distribution
      stdev (float): the standard deviation of the distribution

      data (list of floats): extracted from the data file

      lmbda (float): scale parameter of the distribution
      (missing an 'a' to prevent name clash with Python keyword)
      k (float): shape parameter of the distribution

  """
  def __init__(self, lmbda=1, k=1.5):
    self.lmbda = lmbda
    self.k = k
    self.calculate_mean()
    self.calculate_stdev()

  def calculate_mean(self, round_to=2):
    """ Method to calculate the mean

    Args:
        round_to (int): Round the mean value. [Default value: 2 floating point]

    Returns:
        float: mean of the distribution
    """
    gamma_factor = math.gamma(1 + (1 / self.k))
    self.mean = self.lmbda * gamma_factor
    return round(self.mean, round_to)

  def calculate_stdev(self, round_to=2):
    """ Method to calculate the standard deviation

    Args:
        round_to (int): Round the mean value. [Default value: 2 floating point]

    Returns:
        float: standard deviation of the distribution
    """
    first = math.gamma(1 + (2 / self.k))
    second = math.gamma(1 + (1 / self.k)) ** 2
    self.stdev = math.sqrt(self.lmbda ** 2 * (first - second))
    return round(self.stdev, round_to)

  def calculate_pdf(self, x, round_to=2):
    """ Probability density function calculator for the Weibull distribution.

    Args:
        x (float): point for caluclating the probability density function
        round_to (int): Round the mean value. [Default value: 2 floating point]

    Returns:
        float: probability density function
    """
    if x < 0:
      out = 0
    else:
      exp_factor = math.exp(-(x / self.lmbda) ** self.k)
      out = (self.k / self.lmbda) * exp_factor * \
            (x / self.lmbda) ** (self.k - 1)
    return round(out, round_to)

  def calculate_cdf(self, x, round_to=2):
    """
    Cumulative density function calculator for the Weibull distribution.
    Args:
        x (float): point for calculating the cumulative density function
        round_to (int): Round the mean value. [Default value: 2 floating point]

    Returns:
        float: cumulative density function output
    """
    if x < 0:
      out = 0
    else:
      out = 1 - math.exp(-(x / self.lmbda) ** self.k)
    return round(out, round_to)

  def plot_pdf(self, samples=250):
    """ Method to plot the pdf of the Weibull distribution.

    Args:
        samples (int): number of discrete data points

    Returns:
        list: y values for the pdf plot

    """
    x = np.linspace(0, 2.5, num=samples)
    y = np.zeros_like(x)

    for index, value in enumerate(x):
      y[index] = self.calculate_pdf(value)

    plt.plot(x, y, label=f"lambda={self.lmbda}; k={self.k}")
    plt.legend()
    plt.title(f"Probability Distribution Function for Weibull \
              Distribution lambda={self.lmbda} k={self.k}")
    plt.show()
    return y

  def __repr__(self):
    """ Method to output the characteristics of the Weibull instace.
    Args:
        None
    Returns:
        string: characteristics of the Weibull
    """
    return f"lambda: {self.lmbda}, k: {self.k}, mean: {self.mean}, \
            standard deviation: {self.stdev}"


import LinearAlgebraPurePython as la
import MachineLearningPurePy as ml

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
import numpy as np

import matplotlib.pyplot as plt

import random
import sys

# Section 1: Fake data preparation
#   and visualization
X = [1.5,2.0,2.5,3.0,3.5,4.0]
def y_of_x(x):
    return 0.2*x + 0.7*x**2 + random.uniform(-1,1)
Y = []
for x in X:
    Y.append(y_of_x(x))

plt.scatter(X, Y)

# Section 2: Get fake data in correct format
X = la.transpose([X])
Y = la.transpose([Y])

# Section 3: Pure Python Tools Fit
poly_pp = ml.Poly_Features_Pure_Py(order = 2)
Xp = poly_pp.fit_transform(X)
print('PP Feature Names:', poly_pp.get_feature_names())
ls_pp = ml.Least_Squares(tol=2, add_ones_column=False)
ls_pp.fit(Xp, Y)
print()

# Section 4: SciKit Learn Fit
poly_sk = PolynomialFeatures(degree = 2)
X_poly = poly_sk.fit_transform(X)
print('SK Feature Names:', poly_sk.get_feature_names())
ls_sk = LinearRegression()
ls_sk.fit(X_poly, Y)
print()

# Section 5: Coefficients Comparison
tmp_ls_pp_coefs = sorted(ls_pp.coefs)
rounded_ls_pp_coefs = [round(x,8)+0 for x in la.transpose(tmp_ls_pp_coefs)[0]]
print('PurePy  LS coefficients:', rounded_ls_pp_coefs)

tmp_ls_sk_coefs = ls_sk.intercept_.tolist() + ls_sk.coef_[0][1:].tolist()
tmp_ls_sk_coefs = sorted(tmp_ls_sk_coefs)
rounded_ls_sk_coefs = [round(x,8)+0 for x in tmp_ls_sk_coefs]
print('SKLearn LS coefficients:', rounded_ls_sk_coefs, '\n')

print('Coef Deltas:', [rounded_ls_pp_coefs[i] - rounded_ls_sk_coefs[i]
        for i in range(len(rounded_ls_pp_coefs))])

# Section 6: Create Fake Test Data
XLS = [0,1,2,3,4,5]

# Section 6.1: Predict with Fake Test Data
#   Using Pure Python Tools
XLSpp = poly_pp.transform(la.transpose([XLS]))
YLSpp = ls_pp.predict(XLSpp)
YLSpp = la.transpose(YLSpp)[0]

# Section 6.2: Predict with Fake Test Data
#   Using SciKit Learn Tools
XLSsk = poly_sk.transform(la.transpose([XLS]))
YLSsk = ls_sk.predict(XLSsk)
YLSsk = YLSsk.tolist()
YLSsk = la.transpose(YLSsk)[0]

# Section 7: Calculate Prediction Differences
YLSpp.sort()
YLSsk.sort()
deltas = [ YLSpp[i] - YLSsk[i] for i in range(len(YLSpp)) ]
print( '\nPrediction Deltas:', deltas , '\n')

# Section 8: Plot Both Methods
plt.plot(XLS, YLSpp, XLS, YLSsk)
plt.xlabel('X Values')
plt.ylabel('Y Values')
plt.title('Pure Python Least Squares Line Fit')
plt.show()


'''
SymPy demonstration
http://docs.sympy.org/latest/tutorial/intro.html
'''
import sympy as sp
sp.init_printing(use_unicode=True)

print(sp.sqrt(3))
print(sp.sqrt(8))

x, y = sp.symbols('x y')
expr = x + 2*y + 1
print(expr)

print(sp.expand(x * expr))
print(sp.factor(sp.expand(x * expr)))

x, t, z, nu = sp.symbols('x t z nu')
print(sp.diff(sp.sin(x)*sp.exp(x), x))
print(sp.integrate(sp.exp(x)*sp.sin(x) + sp.exp(x)*sp.cos(x), x))

print(sp.integrate(sp.sin(x**2), (x, -sp.oo, sp.oo)))

print(sp.limit(sp.sin(x)/x, x, 0))

print(sp.solve(x**2 - 2, x))
y = sp.Function('y')
print(sp.solve(sp.Eq(y(t).diff(t, t) - y(t), sp.exp(t)), y(t)))

A = sp.Matrix([[1,2],[2,2]])
print(A.eigenvals())
print(sp.latex(A.eigenvals()))


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

from io_utilities import load_data
from visualizations import show_clusters_centroids

def distance(a,b):
    """
    Compute Euclidean Distance Between Two Points
    Input:
        a (list): an n-dimensional list or array
        b (list): an n-dimensional list or array
    Output:
        The Euclidean Distance between vectors a and b
    """
    return np.sqrt(np.sum((np.array(b)-np.array(a))**2))

def get_clusters(points,centroids):
    """
    Returns a list of clusters given all the points in the dataset and
    the current centroids.
    Input:
        points (list of lists): A list with every point in the dataset
        centroids (list of lists): A list with the current centroids
    Output:
        clusters (list of lists of lists): A List of Clusters. Each cluster
        is also a list of points in the cluster.
    """
    clusters = [[] for f in centroids]

    for i, point in enumerate(points):
        point_to_centroids = []
        for j, centroid in enumerate(centroids):
            point_to_centroids.append(distance(point,centroid))
        closest_idx = np.argmin(point_to_centroids)
        clusters[closest_idx].append(point)

    return clusters

def update_centroids(clusters):
    """
    Given a list of clusters (as prepared by get_clusters) get the new centroids
    Input:
        clusters (list of lists of lists): A List of Clusters. Each cluster
        is also a list of points in the cluster.
    Output:
        A (list of lists): The new centroids.
    """
    new_centroids = []

    for cluster in clusters:
        new_centroids.append(np.mean(cluster,axis = 0))
    return new_centroids

def k_means(points, k, iterations=10):
    """
    K Means Unsupervised ML Algorithm Implementation with Forgy Initialization
    Input:
        points (numpy array): a 2D Array with the data to cluster.
        k (int): The number of clusters to find
    """
    idx = np.random.randint(len(points),size=k)

    centroids = points[idx,:]
    clusters = get_clusters(points,centroids)

    for i in range(iterations):

        if i % 1 == 0:
            if i == 0:
                title = "Initialization"
            else:
                title = "Iteration {}".format(i+1)

            show_clusters_centroids(
                clusters,
                centroids,
                title,
            )

        clusters = get_clusters(points,centroids)
        centroids = update_centroids(clusters)

    return clusters,centroids

if __name__ == "__main__":
    data = load_data('./data/iris.data')
    k = 3

    X = np.array([f[:-1] for f in data])
    y = np.array([f[-1] for f in data])

    clusters,centroids = k_means(X,3)

    show_clusters_centroids(clusters,centroids,"Result", keep=True)
    plt.show()


#!/usr/bin/env python

import numpy as np
import iterate

class Grid:
    """
        Simple class to generate a computational grid and apply boundary conditions.
    """

    def __init__(self, nx=10, ny=10, xmin=0.0, xmax=1.0, ymin=0.0, ymax=1.0):

        self.xmin, self.xmax, self.ymin, self.ymax = xmin, xmax, ymin, ymax
        self.nx, self.ny = nx, ny

        self.dx = (xmax - xmin) / (nx - 1)

        self.dy = (ymax - ymin) / (ny - 1)

        self.u = np.zeros((ny, nx), dtype=np.double)

    def set_boundary_condtion(self, side = 'top', boundary_condition_function = lambda x,y: 0.0):

        xmin, ymin = self.xmin, self.ymin

        xmax, ymax = self.xmax, self.ymax

        x = np.linspace(xmin, xmax, self.nx)

        y = np.linspace(ymin, ymax, self.ny)

        if side == 'bottom':
            self.u[0 ,:] = boundary_condition_function(ymax,x)
        elif side == 'top':
            self.u[-1 ,:] = boundary_condition_function(ymin,x)
        elif side == 'left':
            self.u[:, 0] = boundary_condition_function(xmin,y)
        elif side == 'right':
            self.u[:, -1] = boundary_condition_function(xmax,y)

        self.initial_u = self.u.copy()

class LaplaceSolver(Grid):
    """
        Class that solves the Laplace equation in 2D
    """

    def iterate(self):
        """
            A Python (slow) implementation of a finite difference iteration
        """

        u = self.u

        ny, nx = u.shape

        dx2, dy2 = self.dx ** 2, self.dy ** 2

        err = 0.0

        for i in range(1, nx - 1):

            for j in range(1, ny - 1):

                tmp = u[i,j]

                u[i,j] = ((u[i-1, j] + u[i+1, j]) * dy2 +
                          (u[i, j-1] + u[i, j+1]) * dx2) / (dx2 + dy2) / 2

                diff = u[i,j] - tmp

                err += diff * diff

        return np.sqrt(err)

    def solve(self, max_iterations=10000, tolerance=1.0e-16, quiet=False):
        """
            Calls iterate() sequentially until the error is reduced below a tolerance.
        """

        for i in range(max_iterations):

            error = self.iterate()

            if error < tolerance:
                if not quiet:
                    print("Solution converged in " + str(i) + " iterations.")
                break

    def swig_solve(self, max_iterations=10000, tolerance=1.0e-16, quiet=False):
        """
            Calls iterate.iterate() sequentially until the error is reduced below a tolerance.
        """

        for i in range(max_iterations):

            error = iterate.iterate(self.u, self.dx, self.dy)

            if error < tolerance:
                if not quiet:
                    print("Solution converged in " + str(i) + " iterations.")
                return

        print("Solution FAILED to converged in " + str(max_iterations) + " iterations.")

        return

    def swig_solve_red_black(self, max_iterations=10000, tolerance=1.0e-16, quiet=False):
        """
            Calls iterate.iterate() sequentially until the error is reduced below a tolerance.
        """

        for i in range(max_iterations):

            error = iterate.iterate_red_black(self.u, self.dx, self.dy)

            if error < tolerance:
                if not quiet:
                    print("Solution converged in " + str(i) + " iterations.")
                return

        print("Solution FAILED to converged in " + str(max_iterations) + " iterations.")

        return

    def get_solution(self):
        return self.u

    def reset(self):
        self.u = self.initial_u.copy()
        return


##this will demonstrate boolean masks to access ada in numpy arrays

#counting rainydays exp.
import numpy as np
import pandas as pd

#pandas will read in our data
rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values
inches = rainfall / 254.0 #1/10mm -> inches
print(inches.shape) #(365,)

import matplotlib
matplotlib.use('TKagg')

import matplotlib.pyplot as plt
import seaborn; seaborn.set() #set plot style

plt.hist(inches, 40)
plt.show()

##digging into the data
#Comparison Operators as ufuncs

x = np.array([1,2,3,4,5])
print(x < 3) #less than, also have <, <=, !=, ==, ect

print((2*x) == (x ** 2))

rng = np.random.RandomState(0)
x = rng.randint(10, size=(3,4))
print(x)
print(x<6)

#working with boolean arrays
print("x:\n", x)
#count # of true entries
print(np.count_nonzero(x < 6)) # how many values less that 6
print(np.sum(x < 6)) # same value another way but you can specify axis
print(np.sum(x < 6, axis=1))
print(np.any(x > 8)) # any values greater than 8?
print(np.all(x >2)) # are all values greater than 2?
#also works along axis
print(np.any(x<4, axis = 1))

##Bolean operators
# &, | , ^, ~
print(np.sum((inches > 0.5) & (inches < 1)))
# & equive to np.bitwise_and, ect

print("Number of days without rain:     ", np.sum(inches == 0))
print("number of days with rain:        ", np.sum(inches != 0))
print("Days with more than 0.5 inches:  ", np.sum(inches > 0.5))
print("Rainy days with < 0.2 inches:    ", np.sum((inches > 0) & (inches < 0.2)))

#Boolean arrays as masks
print("x:\n", x)

print(x[x<5]) #masking operation

#construct a mask of rainy days
rainy = (inches > 0)

#construct a mask of all summer days ( 6/21 is the 172nd day)
days = np.arange(365)
summer = (days > 172) & (days < 262)

print("The median precip on rainy days in 2014 (inches):  ",
      np.median(inches[rainy]))
print("Median precip on summer days in 2014 (inches):     ",
      np.median(inches[summer]))
print("Maximum precip on summer days in 2014 (inches):    ",
      np.max(inches[summer]))
print("Median precip on non-summer rainy days(inches):    ",
      np.median(inches[rainy & ~summer]))


import numpy as np

def lin_reg_vec(theta,x,y):
    """
    Function to calculate cost and gradient for given arguments for linear regression model
    """
    val = np.dot(theta,x) - y
    grad = np.transpose(np.dot(x,np.transpose(val)))
    cost = np.sum(val**2)/2
    return cost,grad

def grad_check(theta,x,y):
    eps = pow(10,-4)
    grad = np.zeros(theta.shape)
    numf = theta.shape[0] # number of features
    for i in range(numf):
        evec = np.zeros(theta.shape)
        evec[i] = 1
        cplus,_ = lin_reg_vec(theta + eps*evec,x,y)
        csub,_ = lin_reg_vec(theta - eps*evec,x,y)
        grad[i] = (cplus - csub)/(2*eps)
    return grad

data = np.loadtxt("housing.data")  #specify path to data file
data = np.insert(data,0,1,axis=1)
np.random.shuffle(data)

train_data = data[:400,:-1]
train_labels = data[:400,-1]

test_data = data[400:,:-1]
test_labels = data[400:,-1]

train_data = np.transpose(train_data)
test_data = np.transpose(test_data)

theta = np.random.rand(train_data.shape[0])

_,grad_lin_reg = lin_reg_vec(theta,train_data,train_labels)
grad_ch = grad_check(theta,train_data,train_labels)

diff = np.abs(grad_lin_reg - grad_ch)
print (diff)

assert all(np.less(diff,1e-4)) # the difference between the two gradients computed is very less


from scipy.integrate import odeint
import pandas as pd
import numpy as np

# The SIR model differential equations.
def SIR(y, t, N, beta, gamma):

    S, I, R = y
    dSdt = -beta * S * I / N
    dIdt = beta * S * I / N - gamma * I
    dRdt = gamma * I

    return dSdt, dIdt, dRdt

def entrypoint(current_state, model_parameters):

    args = {
        'y0': (current_state['population'] - current_state['current_infected'] - 0, # S
               current_state['current_infected'],                                   # I
               current_state['recovered']                                                                   # R
              ),
        't': np.linspace(0, model_parameters['days_from_t0'], model_parameters['days_from_t0']+1),
        'args': (current_state['population'],                            # N
                 model_parameters['R0'] / model_parameters['sick_days'], # beta
                 1. / model_parameters['sick_days'],                     # gamma
                )
    }

    result = odeint(SIR, **args)

    result = pd.DataFrame(result, columns=['S', 'I', 'R'])

    result['days'] = args['t']
    result['I2'] = result['I'] * model_parameters['i2_percentage'] / 100 # severe cases
    result['I3'] = result['I'] * model_parameters['i3_percentage'] / 100 # critical cases
    result['I1'] = result['I'] - result['I2'] - result['I3']             # mild cases

    return result


import tclab  # pip install tclab
import numpy as np
import time
import matplotlib.pyplot as plt
from scipy.integrate import odeint

# define energy balance model
def heat(x,t,Q1,Q2):
    # Parameters
    Ta = 23 + 273.15   # K
    U = 10.0           # W/m^2-K
    m = 4.0/1000.0     # kg
    Cp = 0.5 * 1000.0  # J/kg-K
    A = 10.0 / 100.0**2 # Area in m^2
    As = 2.0 / 100.0**2 # Area in m^2
    alpha1 = 0.0100     # W / % heater 1
    alpha2 = 0.0075     # W / % heater 2
    eps = 0.9          # Emissivity
    sigma = 5.67e-8    # Stefan-Boltzman

    # Temperature States
    T1 = x[0]
    T2 = x[1]

    # Heat Transfer Exchange Between 1 and 2
    conv12 = U*As*(T2-T1)
    rad12  = eps*sigma*As * (T2**4 - T1**4)

    # Nonlinear Energy Balances
    dT1dt = (1.0/(m*Cp))*(U*A*(Ta-T1) \
            + eps * sigma * A * (Ta**4 - T1**4) \
            + conv12 + rad12 \
            + alpha1*Q1)
    dT2dt = (1.0/(m*Cp))*(U*A*(Ta-T2) \
            + eps * sigma * A * (Ta**4 - T2**4) \
            - conv12 - rad12 \
            + alpha2*Q2)

    return [dT1dt,dT2dt]

# save txt file
def save_txt(t,u1,u2,y1,y2,sp1,sp2):
    data = np.vstack((t,u1,u2,y1,y2,sp1,sp2))  # vertical stack
    data = data.T                 # transpose data
    top = 'Time (sec), Heater 1 (%), Heater 2 (%), ' \
        + 'Temperature 1 (degC), Temperature 2 (degC), ' \
        + 'Set Point 1 (degC), Set Point 2 (degC)'
    np.savetxt('data.txt',data,delimiter=',',header=top,comments='')

# Connect to Arduino
a = tclab.TCLab()

# Turn LED on
print('LED On')
a.LED(100)

# Run time in minutes
run_time = 10.0

# Number of cycles
loops = int(60.0*run_time)
tm = np.zeros(loops)

# Temperature (K)
Tsp1 = np.ones(loops) * 23.0 # set point (degC)
T1 = np.ones(loops) * a.T1 # measured T (degC)

Tsp2 = np.ones(loops) * 23.0 # set point (degC)
T2 = np.ones(loops) * a.T2 # measured T (degC)

# Predictions
Tp1 = np.ones(loops) * a.T1
Tp2 = np.ones(loops) * a.T2
error_eb = np.zeros(loops)

# impulse tests (0 - 100%)
Q1 = np.ones(loops) * 0.0
Q1[10:] = 100.0 # step up after 10 sec
Q2 = np.ones(loops) * 0.0
Q2[300:] = 100.0 # step up after 5 min (300 sec)

print('Running Main Loop. Ctrl-C to end.')
print('  Time   Q1     Q2    T1     T2')
print('{:6.1f} {:6.2f} {:6.2f} {:6.2f} {:6.2f}'.format(tm[0], \
                                                       Q1[0], \
                                                       Q2[0], \
                                                       T1[0], \
                                                       T2[0]))

# Create plot
plt.figure(figsize=(10,7))
plt.ion()
plt.show()

# Main Loop
start_time = time.time()
prev_time = start_time
try:
    for i in range(1,loops):
        # Sleep time
        sleep_max = 1.0
        sleep = sleep_max - (time.time() - prev_time)
        if sleep>=0.01:
            time.sleep(sleep-0.01)
        else:
            time.sleep(0.01)

        # Record time and change in time
        t = time.time()
        dt = t - prev_time
        prev_time = t
        tm[i] = t - start_time

        # Read temperatures in Kelvin
        T1[i] = a.T1
        T2[i] = a.T2

        # Simulate one time step with Energy Balance
        Tinit = [Tp1[i-1]+273.15,Tp2[i-1]+273.15]
        Tnext = odeint(heat,Tinit, \
                       [0,dt],args=(Q1[i-1],Q2[i-1]))
        Tp1[i] = Tnext[1,0]-273.15
        Tp2[i] = Tnext[1,1]-273.15
        error_eb[i] = error_eb[i-1] \
                      + (abs(Tp1[i]-T1[i]) \
                      +  abs(Tp2[i]-T2[i]))*dt

        # Write output (0-100)
        a.Q1(Q1[i])
        a.Q2(Q2[i])

        # Print line of data
        print('{:6.1f} {:6.2f} {:6.2f} {:6.2f} {:6.2f}'.format(tm[i], \
                                                               Q1[i], \
                                                               Q2[i], \
                                                               T1[i], \
                                                               T2[i]))

        # Plot
        plt.clf()
        ax=plt.subplot(3,1,1)
        ax.grid()
        plt.plot(tm[0:i],T1[0:i],'ro',label=r'$T_1$ measured')
        plt.plot(tm[0:i],Tp1[0:i],'k-',label=r'$T_1$ energy balance')
        plt.plot(tm[0:i],T2[0:i],'bx',label=r'$T_2$ measured')
        plt.plot(tm[0:i],Tp2[0:i],'k--',label=r'$T_2$ energy balance')
        plt.ylabel('Temperature (degC)')
        plt.legend(loc=2)
        ax=plt.subplot(3,1,2)
        ax.grid()
        plt.plot(tm[0:i],error_eb[0:i],'k-',label='Energy Balance Error')
        plt.ylabel('Cumulative Error')
        plt.legend(loc='best')
        ax=plt.subplot(3,1,3)
        ax.grid()
        plt.plot(tm[0:i],Q1[0:i],'r-',label=r'$Q_1$')
        plt.plot(tm[0:i],Q2[0:i],'b:',label=r'$Q_2$')
        plt.ylabel('Heaters')
        plt.xlabel('Time (sec)')
        plt.legend(loc='best')
        plt.draw()
        plt.pause(0.05)

    # Turn off heaters
    a.Q1(0)
    a.Q2(0)
    # Save text file and plot at end
    save_txt(tm[0:i],Q1[0:i],Q2[0:i],T1[0:i],T2[0:i],Tsp1[0:i],Tsp2[0:i])
    # Save figure
    plt.savefig('test_Models.png')

# Allow user to end loop with Ctrl-C
except KeyboardInterrupt:
    # Disconnect from Arduino
    a.Q1(0)
    a.Q2(0)
    print('Shutting down')
    a.close()
    save_txt(tm[0:i],Q1[0:i],Q2[0:i],T1[0:i],T2[0:i],Tsp1[0:i],Tsp2[0:i])
    plt.savefig('test_Models.png')

# Make sure serial connection still closes when there's an error
except:
    # Disconnect from Arduino
    a.Q1(0)
    a.Q2(0)
    print('Error: Shutting down')
    a.close()
    save_txt(tm[0:i],Q1[0:i],Q2[0:i],T1[0:i],T2[0:i],Tsp1[0:i],Tsp2[0:i])
    plt.savefig('test_Models.png')
    raise


import numpy as np

def reg_window(N):
    return np.ones(N)

def hanning_window(N):
    nn = [i for i in range(N)]
    return 0.5 * (1 - np.cos(np.multiply(nn, 2 * np.pi) / (N - 1)))

def hamming_window(N):
    nn = [i for i in range(N)]
    return 0.54 - 0.46 * np.cos(np.multiply(nn, 2 * np.pi) / (N - 1))


# coding:utf-8
import numpy as np
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

'''
author: heucoder
email: 812860165@qq.com
date: 2019.6.13
'''

def pca(data, n_dim):
    '''

    pca is O(D^3)
    :param data: (n_samples, n_features(D))
    :param n_dim: target dimensions
    :return: (n_samples, n_dim)
    '''
    data = data - np.mean(data, axis = 0, keepdims = True)

    cov = np.dot(data.T, data)

    eig_values, eig_vector = np.linalg.eig(cov)
    # print(eig_values)
    indexs_ = np.argsort(-eig_values)[:n_dim]
    picked_eig_values = eig_values[indexs_]
    picked_eig_vector = eig_vector[:, indexs_]
    data_ndim = np.dot(data, picked_eig_vector)
    return data_ndim

# data 降维的矩阵(n_samples, n_features)
# n_dim 目标维度
# fit n_features >> n_samples, reduce cal
def highdim_pca(data, n_dim):
    '''

    when n_features(D) >> n_samples(N), highdim_pca is O(N^3)

    :param data: (n_samples, n_features)
    :param n_dim: target dimensions
    :return: (n_samples, n_dim)
    '''
    N = data.shape[0]
    data = data - np.mean(data, axis = 0, keepdims = True)

    Ncov = np.dot(data, data.T)

    Neig_values, Neig_vector = np.linalg.eig(Ncov)
    indexs_ = np.argsort(-Neig_values)[:n_dim]
    Npicked_eig_values = Neig_values[indexs_]
    # print(Npicked_eig_values)
    Npicked_eig_vector = Neig_vector[:, indexs_]
    # print(Npicked_eig_vector.shape)

    picked_eig_vector = np.dot(data.T, Npicked_eig_vector)
    picked_eig_vector = picked_eig_vector/(N*Npicked_eig_values.reshape(-1, n_dim))**0.5
    # print(picked_eig_vector.shape)

    data_ndim = np.dot(data, picked_eig_vector)
    return data_ndim

if __name__ == "__main__":
    data = load_iris()
    X = data.data
    Y = data.target
    data_2d1 = pca(X, 2)
    plt.figure(figsize=(8,4))
    plt.subplot(121)
    plt.title("my_PCA")
    plt.scatter(data_2d1[:, 0], data_2d1[:, 1], c = Y)

    sklearn_pca = PCA(n_components=2)
    data_2d2 = sklearn_pca.fit_transform(X)
    plt.subplot(122)
    plt.title("sklearn_PCA")
    plt.scatter(data_2d2[:, 0], data_2d2[:, 1], c = Y)
    plt.show()


from typing import Tuple

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
from scipy.special import factorial

def likelihood(runs: int, hits: int, p: float) -> float:
    """Calculate the likelihood.

    Args:
      runs: Number of sample runs.
      hits: Number of positive sample runs.
      p: Model parameter.

    Returns:
      The likelihood.
    """
    return (
        factorial(runs)
        / (factorial(hits) * factorial(runs - hits))
        * p ** hits
        * (1 - p) ** (runs - hits)
    )

def calcaulte_marginal_likelihood(
    runs: int, hits: int, prior: np.ndarray, p: np.ndarray
) -> np.ndarray:
    """Calculate the marginal likelihood.

    Args:
      runs: Number of sample runs.
      hits: Number of positive sample runs.
      prior: Probability density function of prior.
      p: Model parameter.

    Returns:
      The marginal likelihood.
    """
    integration_size = 1 / len(prior)
    integral = 0
    for (p_value, prior_value) in zip(p, prior):
        integral += likelihood(runs, hits, p_value) * prior_value
    return integral * integration_size

def calculate_posterior(
    runs: int, hits: int, prior: np.ndarray, p: np.ndarray
) -> np.ndarray:
    """Calculate the posterior.

    Args:
      runs: Number of sample runs.
      hits: Number of positive sample runs.
      prior: Probability density function of prior.
      p: Model parameter.

    Returns:
      The posterior probability density function.
    """
    posterior = np.zeros(len(prior))
    marginal_likelihood = calcaulte_marginal_likelihood(runs, hits, prior, p)
    for i, (p_value, prior_value) in enumerate(zip(p, prior)):
        posterior[i] = (
            likelihood(runs, hits, p_value) * prior_value / marginal_likelihood
        )
    return posterior

def calculate_confidence_interval(
    posterior: np.ndarray, confidence_level: float
) -> Tuple[float, float, int, int]:
    """Calculate confidence interval.

    Args:
      confidence_level: Confidence level as a decimal number.
      posterior: Probability density function of posterior.

    Returns:
      Confidence interval.
    """
    low = 1
    idx_low = 0
    high = 0
    idx_high = 0
    integral = 0
    integration_size = 1 / len(posterior)
    for idx in np.argsort(posterior)[::-1]:
        integral += posterior[idx] * integration_size
        if low > idx * integration_size:
            low = idx * integration_size
            idx_low = idx
        if high < idx * integration_size:
            high = idx * integration_size
            idx_high = idx
        if integral > confidence_level:
            break
    return low, high, idx_low, idx_high

plt.rc("font", size=12)
plt.rc("axes", titlesize=12)
plt.rc("axes", labelsize=12)
plt.rc("xtick", labelsize=12)
plt.rc("ytick", labelsize=12)
plt.rc("legend", fontsize=12)
plt.rc("figure", titlesize=12)

sampled_data = np.array(
    [
        1,
        1,
        1,
        0,
        0,
        1,
        0,
        0,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        1,
        0,
        0,
        0,
        1,
        0,
        0,
        1,
        0,
        0,
    ]
)
prior = np.zeros(1000) + 1
p = np.linspace(0, 1, 1000)
posterior = calculate_posterior(len(sampled_data), np.sum(sampled_data), prior, p)
conf_low, conf_high, conf_idx1, conf_idx2 = calculate_confidence_interval(
    posterior, 0.95
)

fig, ax = plt.subplots(1, figsize=(6, 4))
ax.plot(p, posterior, color="blue", linewidth=3)
ax.fill_between(
    p[conf_idx1:conf_idx2], posterior[conf_idx1:conf_idx2], alpha=0.3, color="green"
)
ax.set_ylim(0, 5)
ax.set_xlim(0, 1)
ax.set_xlabel("p")
ax.set_ylabel("Probability Density")
plt.tight_layout()
plt.grid()
plt.savefig("bayesian_mysterious_stranger.svg")

fig, ax = plt.subplots(1, figsize=(6, 4))
fig.set_tight_layout(True)
ax.set_ylim(0, 5)
ax.set_xlim(0, 1)
ax.set_xlabel("p")
ax.set_ylabel("Probability Density")
plt.grid()

# Make first frame
sampled_data = np.array(
    [
        1,
        1,
        1,
        0,
        0,
        1,
        0,
        0,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        1,
        1,
        0,
        1,
        1,
        0,
        0,
        0,
        1,
        0,
        0,
        1,
        0,
        0,
    ]
)
p = np.linspace(0, 1, 1000)
prior = np.linspace(0, 1, 1000)
posterior = calculate_posterior(
    len(sampled_data[:1]), np.sum(sampled_data[:1]), prior, p
)
conf_low, conf_high, conf_idx1, conf_idx2 = calculate_confidence_interval(
    posterior, 0.95
)
(probability_density_function,) = ax.plot(p, posterior, color="blue", linewidth=3)
ax.fill_between(
    p[conf_idx1:conf_idx2], posterior[conf_idx1:conf_idx2], alpha=0.3, color="green"
)

def update(i: int) -> None:
    """Update animiate.

    Args:
      i: frame number.
    """
    p = np.linspace(0, 1, 1000)
    prior = np.zeros(1000) + 1
    posterior = calculate_posterior(
        len(sampled_data[:i]), np.sum(sampled_data[:i]), prior, p
    )
    conf_low, conf_high, conf_idx1, conf_idx2 = calculate_confidence_interval(
        posterior, 0.95
    )
    probability_density_function.set_ydata(posterior)
    ax.collections.clear()
    ax.fill_between(
        p[conf_idx1:conf_idx2], posterior[conf_idx1:conf_idx2], alpha=0.3, color="green"
    )
    ax.set_title(
        f"Runs: {len(sampled_data[:i])}; Mysterious Strangers: {np.sum(sampled_data[:i])}"
    )

# Make animation
anim = FuncAnimation(
    fig, update, frames=np.arange(1, len(sampled_data) + 1), interval=300
)
anim.save("ms_bayesian.gif", dpi=400)


#Problema 2

#Primero que nada, se importan los modulos a utilizar

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mp
import math as m
from astropy import constants as astro   #modulo con las constantes que se usan en la funcion de Planck

#Funcion de Planck: B = ((2*h*v**3)/c**2)/(np.exp((h*v)/(k_B*T) - 1)

#c: Rapidez de la luz en el vacio
c = astro.c.value
#h: Constante de Planck
h = astro.h.value
#v: Frecuencia
#k_B: Constante de Boltzmann
k_B = astro.k_B.value
#T: Temperatura

#Parte 1

#Se debe graficar los datos que se encuentran en el archivo dado, cuidando las unidades de medida
#Para esto se lee el archivo y se transforma en un array con numpy.loadtxt('nombre del archivo')

archivo = np.loadtxt('firas_monopole_spec_v1.txt')

#Del archivo, se tiene que cada columna significa:

#Columna 1: Frecuencia [cm^-1]
#Columna 2: Espectro del monopolo medido por el instrumento FIRAS del satelite COBE [MJy/sr], donde 1 [MJy] = 10^−20 [Wm^−2Hz^−1]
#Columna 3: Espectro residual del monopolo [kJy/sr]
#Columna 4: Incertidumbre del espectro medido [kJy/sr]
#Columna 5: Modelo del espectro de la galaxia en los polos galacticos [kJy/sr]

#De estas columnas nos interesan las columnas 1 (frecuencia), 2 (espectro medido) y 4 (incertidumbre/error) para la
#realizacion del grafico.

frecuencia = archivo[:,0]*100*c          #primera columna, es de tipo np.ndarray
espectro = archivo[:,1]                              #segunda columna
error = archivo[:,3]/1000                            #cuarta columna

#Se tiene que el espectro medido esta en [MJy/sr] y el error en [kJy/sr], por lo que se divide el error por mil. La frecuencia
#esta en [cm^-1], por lo que se multiplica por 100 y por c para quedar en [1/s = Hz]

#Como el error es muy pequeño por la precision del FIRAS, se multiplica por 400 (factor sugerido como ejemplo en el enunciado)

error_400 = 400*error

#Grafico:
"""
plt.plot(frecuencia, espectro)
plt.title('Espectro de monopolo medido por FIRAS')
plt.xlabel('Frecuencia [Hz]')
plt.ylabel('Espectro del monopolo [MJy/sr]')
plt.errorbar(frecuencia, espectro, xerr=0, yerr=error_400)

plt.savefig('espectro_monopolo_1.png')
plt.show()
"""

#Parte 2

#P = (2*h/c)*((k_B*T/h)**4)*integral_{0}^{infinito}(x^3/(exp(x)-1))dx = K(T)*I

integral_analitica = (np.pi**4)/15

#Para resolver la integral, se utiliza el cambio de variable sugerido y = arctan(x) y asi poder cambiar lo limites de integracion

#y = arctan(x); x = tan(y)
#dy = (1/(1+x^2))dx; dx = (1+x^2)dy

#x = 0 entonces y = arctan(0) = 0
#x = infinito entonces lim_{x = infinito}arctan(x) = pi/2

#P = K(T)*integral_{0}^{pi/2}(tan^3(y))(1+tan^2(y))/(exp(tan(y)) - 1)dy = K(T)*integral_{0}^{pi/2}f(y)dy

#Se tiene que f(y) tiene numerador y denominador infinitos en y = 0 y solo denominador infinito en en y = pi/2, por lo que
#se usara el metodo del valor medio en ambos extremos. Se resolvera con el metodo de Simpson 1/3 para lo demas

#Definimos la función dentro de la integral

def f_integral(x):
    out = (np.tan(x)**3 + np.tan(x)**5)/(np.exp(np.tan(x)) - 1)
    return out

#Para calcular con dicho metodo, se debe de escoger un h tal que la integral se acerque a su valor analitico, por lo que
#se refinarra el valor de la integral al ir aumentando al doble el numero de terminos que se dividira (b - a) = (pi/2 - 0) = pi/2

#Sabemos que la integral tiene un valor analitico igual a (np.pi**4)/15, por lo que se generara un particion cada vez
# la mitad de tamaño que la anterior hasta estar cerca de ese valor. Se debe definir entonces una tolerancia

x_0 = 0
x_n = np.pi/2

def valor_integral(a,b, N=10 , toler=1e-7):
    integral = 0
    while np.abs(integral - integral_analitica) > toler:
        integral = 0
        h = (b-a)/N
        for i in range(N+1):
            x = a + h*i    #valor a evaluar la funcion f_integral
            if x == a:
                integral += h*f_integral(x + h/2)   #valor medio en el inicio
            elif x == b:
                integral += h*f_integral(x - h/2)   #valor medio en el final
            elif i % 2 == 0:   #si el numero de termino es par
                integral += 2*f_integral(x)
            else: #si el numero de termino es impar y no se esta evaluando en los extremos
                integral += 4*f_integral(x)
        integral += f_integral(a+h) + f_integral(b-h)  #se le suman los terminos inicial y final del metodo de Simpson 1/3
        integral *= h/3
        N *= 2  #se duplica el numero de divisiones, disminuye a la mitad h
    return integral

integral_2 = valor_integral(x_0,x_n)

#Parte 3

#De la parte 2: P = (2*h/c**2)*((k_B*T/h)**4)*valor_integral, donde integral ya fue calculado

#Se quiere calcular T, para lo cual se calcula la integral de lo graficado en la parte 1

#Para calcular la integral del espectro (del archivo) respecto a la frecuencia se usara el metodo del trapecio. Hay que
#tener en cuenta que para poder igualarlo a P, se utilizaran dimensiones del sistema internacional, asi que hay que
#hacer las conversiones pertinentes. Ya se habia convertido la frecuencia a Hz, pero falta pasar el espectro de MJy/sr a Wm^−2Hz^−1/sr

#Recordar: [MJy] = 10^−20 [Wm^−2Hz^−1] , donde el espectro es la distribucion de energia por unidad de frecuencia

espectro_2 = espectro*10**(-20)

#Teniendo ya bien las dimensiones, se integra con el metodo del trapecio. Para usarlo no es necesario tener un h constante,
#la distancia entre cada termino a evaluar puede ser variable y adaptarse a los puntos si es que ya se dispone de ellos.
#h vendra dado por la distancia entre 2 frecuenciaa consecutivas, siendo el largo de las bases de los trapecios el valor del
#espectro en dichas frecuencias

integral_espectro = 0

for i in range(len(frecuencia)-1):
    integral_espectro += (espectro_2[i]+espectro_2[i+1])*(frecuencia[i+1]-frecuencia[i])/2

#Igualando este resultado con P: integral_espectro = (2*h/c**2)*((k_B*T/h)**4)*valor_integral

#Despejando T: T = (h/k_B)*((integral_espectro*c**2)/(valor_integral*2*h))**(1/4)

T = (h/k_B)*((integral_espectro*c**2)/(valor_integral(x_0,x_n)*2*h))**(1/4)

print('T =', T, '[K]')

#Da T = 2.684 K, siendo que el valor real es 2.725 K, esto ocurre por arrastre de errores de aproximacion

#Parte 4

#Para graficar lo echo en la parte 1 junto a los resultados obtenidos, es conveniente definir la funcion de Planck dependiente
#de la frecuencia y de la temperatura, escrita al principio.

def planck(v,T):
    output = ((2*h*v**(3))/c**(2))/(np.exp((h*v)/(k_B*T)) - 1)
    return output

#Grafico:
"""
plt.plot(frecuencia, espectro_2, label='Medicion FIRAS (S.I.)')              #grafico parte 1 pero en S.I.
plt.plot(frecuencia, planck(frecuencia,T), label='T calculada')       #T es la temperatura calculada en la parte 3
plt.plot(frecuencia, planck(frecuencia,2.725), label='2.725 K')       #2.725 K es la temperatura a la que se queria llegar con T

plt.title('Espectro segun FIRAS y segun Planck')
plt.xlabel('Frecuencia [Hz]')
plt.ylabel('Espectro del monopolo [$W m^{−2} Hz^{−1}/sr$]')

plt.legend()
plt.savefig('espectro_monopolo_2.png')
plt.show()
"""

#Como de grafico de las mediciones de FIRAS son muy cercanos a los de la funcion de Planck en T=2.725 K, es conveniente realizar
#un grafico de la diferencia de estos.

diferencia_espectro = [m.fabs(espectro_2[i] - planck(frecuencia[i],2.725)) for i in range(len(frecuencia))]

"""
plt.title("Diferencia del espectro de FIRAS y el de Planck")

plt.plot(frecuencia, diferencia_espectro)

plt.yscale('log')

plt.xlabel('Frecuencia [Hz]', fontsize=15)
plt.ylabel('Medición FIRAS - Planck en T = 2.725 [K]', fontsize=10)

plt.savefig('diferencia_espectro.png')
plt.show()
"""

#Parte 5

import scipy.integrate as sc

#sc.trapz(y,x) realiza el algortimo del metodo del trapecio sobre la funcion y en los puntos dados por x,
#por lo que se usara en la integral de la parte 3, es decir, en los datos del archivo de FIRAS.

integral_parte3 = sc.trapz(espectro_2,frecuencia)
print('integral_parte3 =',integral_parte3)

#sc.quad(func,a,b) calcula la integral de la funcion func desde a hasta b. Este se usara para la integral de P (parte 2).
#Para esto, se define la funcion de la integral sin el cambio de variable.

def func(x):
    y = (x**3)/(np.exp(x) - 1)
    return y

integral_parte2 = sc.quad(func, x_0, np.inf)
print('integral_parte2 =', integral_parte2)


import superimport

import numpy as np
from scipy.spatial.distance import cdist
import math
import matplotlib.pyplot as plt
from cycler import cycler
import pyprobml_utils as pml

CB_color = ['#377eb8', '#ff7f00', '#4daf4a']

cb_cycler = (cycler(linestyle=['-', '--', '-.']) * cycler(color=CB_color))
plt.rc('axes', prop_cycle=cb_cycler)

np.random.seed(0)
N = 100
x = 10 * (np.linspace(-1, 1, 100).reshape(-1, 1))
ytrue = np.array([math.sin(abs(el)) / (abs(el)) for el in x]).reshape(-1, 1)
noise = 0.1
y = ytrue + noise * np.random.randn(N, 1)
x = (x - x.mean()) / x.std()  # normalizing.

plt.plot(x, ytrue)
plt.plot(x, y, 'kx')

def rbf_features(X, centers, sigma):
    dist_mat = cdist(X, centers, 'minkowski', p=2.)
    return np.exp((-0.5 / (sigma ** 2)) * (dist_mat ** 2))

# Nadaraya-Watson Kernel Regressor
# using rbf kernel with autosSelected bandwidth given a range.
class NdwkernelReg:

    def __init__(self, gammas=None):
        self.gammas = gammas
        self.gamma = None

    def fit(self, X, y):
        self.X = X
        self.y = y

        self.gamma = self.select_gamma(self.gammas)

    def predict(self, X):
        K = rbf_features(self.X, X, self.gamma)
        return (K * self.y).sum(axis=0) / K.sum(axis=0)

    # leave-one-out crossValidation
    def select_gamma(self, gammas):
        mse = []

        for gamma in gammas:
            K = rbf_features(self.X, self.X, gamma)
            K = K - np.diag(np.diag(K))  # vanishing the diagonal elements
            y_pred = (K * self.y).sum(axis=0) / K.sum(axis=0)
            mse.append(((y_pred[:, np.newaxis] - self.y) ** 2).mean())

        return gammas[np.argmin(mse)]

nws = NdwkernelReg(gammas=np.linspace(0.1, 1, 10))
nws.fit(x, y)
y_estimate = nws.predict(x)
plt.plot(x, y_estimate)
plt.legend(['true', 'data', 'estimate'], fontsize=12)
pml.savefig("kernelRegressionDemo.pdf")
plt.show()


import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import ui

print("构建一个逻辑回归模型来预测，某个学生是否被大学录取。设想你是大学相关部分")
print("的管理者，想通过申请学生两次测试的评分，来决定他们是否被录取。现在你拥有")
print("之前申请学生的可以用于训练逻辑回归的训练样本集。对于每一个训练样本，你有")
print("他们两次测试的评分和最后是被录取的结果。为了完成这个预测任务，我们准备构")
print("建一个可以基于两次测试评分来评估录取可能性的分类模型。")
ui.split_line1()
print("载入数据并查看分布")
path = 'data/ex2data1.txt'
data = pd.read_csv(path, header=None, names=['Exam 1', 'Exam 2', 'Admitted'])
print("查看数据前五行")
print(data.head())
print("创建两个分数的散点图，并使用颜色编码来可视化")
positive = data[data['Admitted'].isin([1])]
negative = data[data['Admitted'].isin([0])]

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
plt.show()
ui.split_line2()
print("sigmoid 函数")

def sigmoid(z):
    return 1 / (1 + np.exp(-z))
print("输出函数图像")
nums = np.arange(-10, 10, step=1)

fig, ax = plt.subplots(figsize=(12,8))
ax.plot(nums, sigmoid(nums), 'r')
plt.show()
ui.split_line2()
print("定义代价函数")

def cost(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    return np.sum(first - second) / (len(X))

ui.split_line2()
print("设置 X, theta, y")
# add a ones column - this makes the matrix multiplication work out easier
data.insert(0, 'Ones', 1)

# set X (training data) and y (target variable)
cols = data.shape[1]
X = data.iloc[:,0:cols-1]
y = data.iloc[:,cols-1:cols]

# convert to numpy arrays and initalize the parameter array theta
X = np.array(X.values)
y = np.array(y.values)
theta = np.zeros(3)
print("检查 X, theta, y 的维度")
print(X.shape, theta.shape, y.shape)
print("计算优化前的代价函数")
print(cost(theta, X, y))
ui.split_line1()
print("定义梯度下降函数，注意，这里指计算出梯度步长，实际由 SciPy 的 optimize 函数计算 Cost 和梯度")

def gradient(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        term = np.multiply(error, X[:,i])
        grad[i] = np.sum(term) / len(X)

    return grad

print("第一次执行梯度下降的结果")
print(gradient(theta, X, y))
print("用SciPy's truncated newton（TNC）实现寻找最优参数")
import scipy.optimize as opt
result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(X, y))
print("结果为", result)
print("重新计算 Cost")
print(cost(theta, X, y))
ui.split_line2()
print("编写预测函数")

def predict(theta, X):
    probability = sigmoid(X * theta.T)
    return [1 if x >= 0.5 else 0 for x in probability]

print("计算当前 theta 在训练集上的准确率")
theta_min = np.matrix(result[0])
predictions = predict(theta_min, X)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))

ui.split_line1()
print("设想你是工厂的生产主管，你有一些芯片在两次测试中的测试结果。对于这两次测试，你想")
print("决定是否芯片要被接受或抛弃。为了帮助你做出艰难的决定，你拥有过去芯片的测试数据集，")
print("从其中你可以构建一个逻辑回归模型")
ui.split_line2()
print("载入数据并可视化")
path = 'data/ex2data2.txt'
data2 = pd.read_csv(path, header=None, names=['Test 1', 'Test 2', 'Accepted'])
print("查看数据前 5 行")
print(data2.head())
print("绘制数据分布")
positive = data2[data2['Accepted'].isin([1])]
negative = data2[data2['Accepted'].isin([0])]

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['Test 1'], positive['Test 2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative['Test 1'], negative['Test 2'], s=50, c='r', marker='x', label='Rejected')
ax.legend()
ax.set_xlabel('Test 1 Score')
ax.set_ylabel('Test 2 Score')
plt.show()
print("构造一组多项式特征")
degree = 5
x1 = data2['Test 1']
x2 = data2['Test 2']

data2.insert(3, 'Ones', 1)

for i in range(1, degree):
    for j in range(0, i):
        data2['F' + str(i) + str(j)] = np.power(x1, i-j) * np.power(x2, j)

data2.drop('Test 1', axis=1, inplace=True)
data2.drop('Test 2', axis=1, inplace=True)
print("查看新数据的前 5 行")
print(data2.head())
ui.split_line2()
print("定义带正则化项的 Cost 函数")

def costReg(theta, X, y, learningRate):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    reg = (learningRate / (2 * len(X))) * np.sum(np.power(theta[:,1:theta.shape[1]], 2))
    return np.sum(first - second) / len(X) + reg

print("定义梯度下降函数")

def gradientReg(theta, X, y, learningRate):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        term = np.multiply(error, X[:,i])

        if (i == 0):
            grad[i] = np.sum(term) / len(X)
        else:
            grad[i] = (np.sum(term) / len(X)) + ((learningRate / len(X)) * theta[:,i])

    return grad

print("准备好训练需要用的数据")
# set X and y (remember from above that we moved the label to column 0)
cols = data2.shape[1]
X2 = data2.iloc[:,1:cols]
y2 = data2.iloc[:,0:1]

# convert to numpy arrays and initalize the parameter array theta
X2 = np.array(X2.values)
y2 = np.array(y2.values)
theta2 = np.zeros(11)
print("设置初始学习率")
learningRate = 1
print("优化前的 Cost 为")
print(costReg(theta2, X2, y2, learningRate))
print("第一步的梯度步长为")
print(gradientReg(theta2, X2, y2, learningRate))
print("计算优化后的结果")
result2 = opt.fmin_tnc(func=costReg, x0=theta2, fprime=gradientReg, args=(X2, y2, learningRate))
print(result2)
print("预测准确率")
theta_min = np.matrix(result2[0])
predictions = predict(theta_min, X2)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y2)]
accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))

ui.split_line1()
print("用 Scikit-Learn 进行优化")
from sklearn import linear_model#调用sklearn的线性回归包
model = linear_model.LogisticRegression(penalty='l2', C=1.0)
model.fit(X2, y2.ravel())
print("最终的准确率为", model.score(X2, y2))
print("可以看到这个准确率比我们之前优化的低不少")


"""
File: sin_Taylor_series_diffeq.py
Copyright (c) 2016 Andrew Malfavon
Excerise A.14
License: MIT
Computes a Taylor polynomial approximation for sinx and shows the accuracy in a table
"""
import numpy as np

#computes the approximation based on the exercises in the book
def sin_Taylor(x, n):
    i = 1
    an_prev = x
    sn_prev = 0.0
    while i <= n + 1:
        sn = sn_prev + an_prev
        an = (-x**2 * an_prev) / (((2 * i) + 1) * (2 * i))
        sn_prev = sn
        an_prev = an
        i += 1
    return abs(an), sn

#test for small x=0.0001 and n = 2, sin(x) is zero to the third decimal
def test_sin_Taylor():
    assert round(sin_Taylor(0.0001, 2)[1], 3) == 0.0

#create a table to display the approximations for various n and x
def table():
    print '%15s %15s %20s %8s' % ('x Values:', 'n Values:', 'Aproximations:', 'Exact:')
    x_list = [0.01, np.pi/2, np.pi, 3*np.pi/2]
    n_list = [2, 5, 10, 100]
    for x in x_list:
        for n in n_list:
            table = sin_Taylor(x, n)
            print '%15f %15f %15f %15f' % (x, n, table[1], np.sin(x))


import numpy as np
import random
from matplotlib import pyplot as plt

class GradientDescentPlot(object):

    def __init__(self,
                random_X=True,
                range_X=range(-20, 21),
                learning_rate=0.1,
                function=lambda x: x**2 + 1,
                function_derivative=lambda x: 2*x,
                verbose=False,
                step_delay=0.5,
                use_seaborn=True):

        if use_seaborn:
            import seaborn as sns
            sns.set()
            sns.set_context('paper')

        plt.ion()

        # generating data
        self.X = range_X
        self.learning_rate = learning_rate
        self.function = function
        self.function_derivative = function_derivative
        self.verbose = verbose
        self.step_delay = step_delay

        # init gradient values
        self.gradient_X = random.choice(self.X) if random_X else np.max(self.X)
        self.gradient_Y = self.function(self.gradient_X)

    def show(self, loop_limit=15, save_fig=False, save_img_name='./%d-gradient-descent-plot.png'):
        plt.plot(self.X, [self.function_derivative(x) for x in self.X], color='gray')
        plt.plot(self.X, [self.function(x) for x in self.X], color='black')

        # show the current gradient point
        plt.scatter([self.gradient_X], [self.gradient_Y], color='red')

        if save_fig:
            plt.savefig(save_img_name % 0)

        for i in range(loop_limit):
            self.update_plot()

            if save_fig:
                plt.savefig(save_img_name % (i + 1))

    def update_plot(self):
        self.compute_gradient()
        plt.scatter([self.gradient_X], [self.gradient_Y])
        plt.pause(self.step_delay)

    def compute_gradient(self):
        self.gradient_X += - self.learning_rate * self.function_derivative(self.gradient_X)
        self.gradient_Y = self.function(self.gradient_X)

        if self.verbose:
            print('X:', self.gradient_X)
            print('Y:', self.gradient_Y)
            print()

if __name__ == '__main__':
    plt.title('Quadratic function')
    quadratic = GradientDescentPlot(
        random_X=False,
        step_delay=0.3
    ).show(loop_limit=5)
    plt.clf()

    plt.title('Stucking in local minima with small learning rate.')
    GradientDescentPlot(
        random_X=False,
        range_X=np.arange(-4, 5, 0.08),
        function=lambda x: x**3 + x**2 - x + 1,
        function_derivative=lambda x: 3*(x**2) + 2*x - 1,
        verbose=True,
        learning_rate=0.07
    ).show(loop_limit=7)
    plt.clf()

    plt.title('Going through local minima with bigger learning rate.')
    GradientDescentPlot(
        random_X=False,
        range_X=np.arange(-4, 5, 0.08),
        function=lambda x: x**3 + x**2 - x + 1,
        function_derivative=lambda x: 3*(x**2) + 2*x - 1,
        verbose=True,
        learning_rate=0.0727,
        step_delay=0.25
    ).show(loop_limit=13, save_fig=False, save_img_name='./%d-plot.png')


import numpy as np
import csv

def sigmoid(soma):
    return 1/ (1+np.exp(-soma))

def sigmoidDerivada(sig):
    return sig * (1 - sig)

entradas = np.array([[0,0],
                     [0,1],
                     [1,0],
                     [1,1]])
saidas = np.array([[0],[1],[1],[0]])

pesos0 = 2 * np.random.random((2,3)) - 1
pesos1 = 2 * np.random.random((3,1)) - 1

epocas = 200_000
taxaAprendizagem = 0.6
momento = 1

#Roda o aprendizado a quantidade de epocas configuradas
for e in range(epocas):
    #Recupera os valores da camada de entrada para realizar as operações
    camadaEntrada = entradas

    #Multiplicação e soma da camada entrada com os pesos da camada de entrada para a camada oculta
    #pegando o dotproduct ou Produto escalar dos dois vetores
    somaSinapse0 = np.dot(camadaEntrada, pesos0)

    #Executa uma função para retornar o valor sigmoid da soma das sinapses da camada de entrada
    camadaOculta = sigmoid(somaSinapse0)

    #Recupera o produto escalar dos vetores da camada oculta e seus pesos
    somaSinapse1 = np.dot(camadaOculta, pesos1)

    #Recupera o valor da saida
    camadaSaida = sigmoid(somaSinapse1)

    #Recupera os valores de erro da rede neural
    erroSaida = saidas - camadaSaida

    #Obtem a média de erro da rede
    mediaAbsoluta = np.mean(np.abs(erroSaida))
    if (e % 1000 == 0):
        print("Epoca: " + str(e) + " | Erro: " + str(mediaAbsoluta))

    #Encontrar Delta da camada oculta para calculo do erro
    derivadaSaida = sigmoidDerivada(camadaSaida)
    deltaSaida = erroSaida * derivadaSaida

    pesos1Transposta = pesos1.T
    deltaSaidaXPeso = deltaSaida.dot(pesos1Transposta)
    deltaCamadaOculta = deltaSaidaXPeso * sigmoidDerivada(camadaOculta)

    #Utilizando o método Backpropagation
    #é feito o reajuste dos pesos da camada oculta para a camada de saida
    camadaOcultaTransposta = camadaOculta.T
    pesosNovo1 = camadaOcultaTransposta.dot(deltaSaida)
    pesos1 = (pesos1 * momento) + (pesosNovo1 * taxaAprendizagem)

    #Realiza o reajuste dos pesos da camada de entrada para a camada oculta
    camadaEntradaTransposta = camadaEntrada.T
    pesosNovos0 = camadaEntradaTransposta.dot(deltaCamadaOculta)
    pesos0 = (pesos0 * momento) + (pesosNovos0 * taxaAprendizagem)

print("Taxa de acerto é de aproximadamente " + str(int((1 - mediaAbsoluta)*100)) + "%")


import numpy as np

# removes redundant wrapper arrays generated with found contours
def unwrap_contour(cnt):
    return np.array([point[0] for point in cnt])

# Calculates the distance between two points
def distance(s1, s2):
    return np.linalg.norm(s1 - s2)

# Calculates the angle between 2 vectors
def angle(v1, v2):
    cos_theta = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
    return np.degrees(np.arccos(cos_theta))

# Calculates the angle between 2 edges of a polygon
def edge_angle(dest1, origin, dest2):
    return angle(dest1 - origin, dest2 - origin)

# Calculates all angles of a polygon, given its vertic;e locations
def angles_of_shape(points):
    angles = []
    for offset in range(len(points)):
        adj1, origin, adj2 = np.roll(points, offset, axis=0)[:3]
        angles += [edge_angle(adj1, origin, adj2)]
    return np.array(angles)

# Calculates all edge lengths of a polygon, given its vertic;e locations
def edges_of_shape(points):
    lengths = []
    for offset in range(len(points)):
        p1, p2 = np.roll(points, offset, axis=0)[:2]
        lengths += [np.linalg.norm(p1 - p2)]
    return np.array(lengths)

# Create a contour that represents a perfect circle (used for circle detection)
def perfect_circle_contour(x, y, r, num_points):
    perfect_circle = []
    for theta in range(0, num_points):
        perfect_circle += [[[ r * np.sin(theta) + y, r * np.cos(theta) + x ]]]

    perfect_circle = np.array(perfect_circle, np.int32)
    return perfect_circle


"""
求矩阵的特征值和谱半径

"""
import numpy as np
from sympy.abc import lamda

def eig_and_specradi(A):
    """
    求矩阵M的特征值和谱半径
    :param: A: 待处理的矩阵
    :return: (特征值, 谱半径)
    """
    # 计算出 Lambda * I - A
    A_ = A.tolist()  # array -> list
    I = np.identity(len(A_))  # 单位阵
    print("lambda * I - A：")
    print(lamda * I - A_)
    # 计算出特征值和谱半径
    eigs, _ = np.linalg.eig(A)
    radius = np.max(eigs)
    print("特征值：", eigs)
    print("谱半径：", radius)
    return eigs, radius

if __name__ == '__main__':
    A = np.array([
        [0.1, 1],
        [0.8, -0.1]
    ])
    eigs, r = eig_and_specradi(A)


#!/usr/bin/python

import numpy as np
from matplotlib import pyplot as plt

def linear_lagrange0(xi):
  return 1-xi

def linear_lagrange1(xi):
  return xi

def quadratic_lagrange0(xi):
  return 2*xi*xi - 3*xi + 1

def quadratic_lagrange1(xi):
  return -4*xi*xi + 4*xi

def quadratic_lagrange2(xi):
  return 2*xi*xi - xi

def hermite0(xi):
  return 1 - 3*xi*xi + 2*xi*xi*xi

def hermite1(xi):
  return xi * (xi-1) * (xi-1)

def hermite2(xi):
  return xi*xi * (3 - 2*xi)

def hermite3(xi):
  return xi*xi * (xi-1)

xlist = np.linspace(0,1,100)

# as plot
plt.rcParams.update({'font.size': 20, 'lines.linewidth': 8, 'lines.markersize':8})
plt.figure()
plt.plot(xlist, [linear_lagrange0(x) for x in xlist], label=r"$\phi_1$")
plt.plot(xlist, [linear_lagrange1(x) for x in xlist], label=r"$\phi_2$")
plt.legend(loc="best")
plt.xlabel(r"$\xi$")
plt.savefig("lagrange.pdf")

# as geometry
# linear lagrange
plt.figure(figsize=(5,5))
plt.plot(xlist, [linear_lagrange0(x) for x in xlist], label=r"$\phi_1$")
plt.plot(xlist, [linear_lagrange1(x) for x in xlist], label=r"$\phi_2$")
plt.plot(xlist, [0 for x in xlist], color='k', lw=4)
#plt.legend(loc="best")
plt.xlabel(r"$\xi$")
plt.axis('off')
plt.savefig("lagrange1.pdf")

# quadratic lagrange
plt.figure(figsize=(5,5))
plt.plot(xlist, [quadratic_lagrange0(x) for x in xlist], label=r"$\phi_1$")
plt.plot(xlist, [quadratic_lagrange1(x) for x in xlist], label=r"$\phi_2$")
plt.plot(xlist, [quadratic_lagrange2(x) for x in xlist], label=r"$\phi_3$")
plt.plot(xlist, [0 for x in xlist], color='k', lw=4)
#plt.legend(loc="best")
plt.xlabel(r"$\xi$")
plt.axis('off')
plt.savefig("lagrange2.pdf")

# hermite
plt.figure(figsize=(5,5))
plt.plot(xlist, [hermite0(x) for x in xlist], label=r"$\phi_1$")
plt.plot(xlist, [hermite1(x) for x in xlist], label=r"$\phi_1$")
plt.plot(xlist, [hermite2(x) for x in xlist], label=r"$\phi_1$")
plt.plot(xlist, [hermite3(x) for x in xlist], label=r"$\phi_1$")
plt.plot(xlist, [0 for x in xlist], color='k', lw=4)
#plt.legend(loc="best")
plt.xlabel(r"$\xi$")
plt.axis('off')
plt.savefig("hermite.pdf")

plt.show()


import scipy.io as sio
import matplotlib.pyplot as plt
import os
import numpy as np
from scipy import optimize as opt

def h0x(X, theta):
    return np.dot(X, theta)

def cost_l2(theta, X, y, lamb=0):
    predictions = h0x(X, theta)
    squared_errors = np.sum(np.square(predictions - y))
    regularization = np.sum(lamb * np.square(theta[1:]))
    return (squared_errors + regularization) / (2 * len(y))

def gradient_l2(theta, X, y, lamb):
    predictions = h0x(X, theta)
    gradient = np.dot(X.transpose(), (predictions - y))
    regularization = lamb * theta
    regularization[0] = 0  # because formula for 0 member is different
    return (gradient + regularization) / len(y)

def learning_curves_chart(X_train, y_train, X_val, y_val, lambda_):
    m = len(y_train)
    train_err = np.zeros(m)
    val_err = np.zeros(m)
    for i in range(1, m):
        theta = opt.fmin_cg(cost_l2, np.zeros(X_train.shape[1]), gradient_l2,
                            (X_train[0:i + 1, :], y_train[0:i + 1], lambda_), disp=False)
        train_err[i] = cost_l2(theta, X_train[0:i + 1, :], y_train[0:i + 1])
        val_err[i] = cost_l2(theta, X_val, y_val)
    plt.plot(range(2, m + 1), train_err[1:], c="r", linewidth=2)
    plt.plot(range(2, m + 1), val_err[1:], c="b", linewidth=2)
    plt.xlabel("number of training examples", fontsize=14)
    plt.ylabel("error", fontsize=14)
    plt.legend(["training", "validation"], loc="best")
    plt.axis([2, m, 0, 100])
    plt.grid()
    plt.show()

def polynom(x, degree):
    X_poly = np.zeros(shape=(len(x), degree))
    for i in range(0, degree):
        X_poly[:, i] = x.squeeze() ** (i + 1);
    return X_poly

if __name__ == '__main__':
    # 1
    file_path = 'ex3data1.mat'
    dataset = sio.loadmat(file_path)
    x_train = dataset["X"]
    x_val = dataset["Xval"]
    x_test = dataset["Xtest"]

    # squeeze the target variables into one-dimensional arrays
    y_train = dataset["y"].squeeze()
    y_val = dataset["yval"].squeeze()
    y_test = dataset["ytest"].squeeze()

    # 2
    fig, ax = plt.subplots()
    ax.scatter(x_train, y_train)
    plt.xlabel("change in water level", fontsize=14)
    plt.ylabel("water flowing out of the dam", fontsize=14)
    plt.title("Training sample", fontsize=16)
    plt.show()

    # data preparation
    X_train = np.hstack((np.ones((len(x_train), 1)), x_train))
    theta = np.zeros(X_train.shape[1])

    # 3
    print(cost_l2(theta, X_train, y_train, 0))

    # 4
    print(gradient_l2(theta, X_train, y_train, 0))

    # 5
    theta = opt.fmin_cg(cost_l2, theta, gradient_l2, (X_train, y_train, 1000), disp=False)
    print(theta)

    h = np.dot(X_train, theta)
    fig, ax = plt.subplots()
    ax.scatter(x_train, y_train)
    ax.plot(X_train[:, 1], h, linewidth=2, color='red')
    plt.show()

    # 6
    X_val = np.hstack((np.ones((len(x_val), 1)), x_val))
    plt.title("Learning Curves for Linear Regression", fontsize=16)
    learning_curves_chart(X_train, y_train, X_val, y_val, 0)

    # 7
    x_train_poly = polynom(x_train, 8)
    x_val_poly = polynom(x_val, 8)
    x_test_poly = polynom(x_test, 8)

    # 8
    train_means = x_train_poly.mean(axis=0)
    train_std = np.std(x_train_poly, axis=0, ddof=1)

    x_train_poly = (x_train_poly - train_means) / train_std
    x_val_poly = (x_val_poly - train_means) / train_std
    x_test_poly = (x_test_poly - train_means) / train_std

    X_train_poly = np.hstack((np.ones((len(x_train_poly), 1)), x_train_poly))
    X_val_poly = np.hstack((np.ones((len(x_val_poly), 1)), x_val_poly))
    X_test_poly = np.hstack((np.ones((len(x_test_poly), 1)), x_test_poly))

    # 9
    theta = opt.fmin_cg(cost_l2, np.zeros(X_train_poly.shape[1]), gradient_l2, (X_train_poly, y_train, 0), disp=False)
    x = np.linspace(min(x_train) - 5, max(x_train) + 5, 1000)
    x_polynom = polynom(x, 8)
    print(x_polynom)
    x_polynom = (x_polynom - train_means) / train_std
    x_polynom = np.hstack((np.ones((len(x_polynom), 1)), x_polynom))

    # 10
    fig, ax = plt.subplots()
    plt.scatter(x_train, y_train, color='red')
    plt.plot(x, h0x(x_polynom, theta), linewidth=2)
    plt.xlabel("change in water level", fontsize=14)
    plt.ylabel("water flowing out ", fontsize=14)
    plt.title("Polynomial Fit", fontsize=16)
    plt.show()

    learning_curves_chart(X_train_poly, y_train, X_val_poly, y_val, 0)

    # 11
    # lambda = 1
    theta = opt.fmin_cg(cost_l2, np.zeros(X_train_poly.shape[1]), gradient_l2, (X_train_poly, y_train, 1), disp=False)
    x = np.linspace(min(x_train) - 5, max(x_train) + 5, 1000)
    x_polynom = polynom(x, 8)
    x_polynom = (x_polynom - train_means) / train_std
    x_polynom = np.hstack((np.ones((len(x_polynom), 1)), x_polynom))
    fig, ax = plt.subplots()
    plt.scatter(x_train, y_train, color='red')
    plt.plot(x, h0x(x_polynom, theta), linewidth=2)
    plt.xlabel("change in water level", fontsize=14)
    plt.ylabel("water flowing out ", fontsize=14)
    plt.title("Polynomial Fit (lambda=1)", fontsize=16)
    plt.show()

    learning_curves_chart(X_train_poly, y_train, X_val_poly, y_val, 1)

    # lambda = 100
    theta = opt.fmin_cg(cost_l2, np.zeros(X_train_poly.shape[1]), gradient_l2, (X_train_poly, y_train, 100), disp=False)
    x = np.linspace(min(x_train) - 5, max(x_train) + 5, 1000)
    x_polynom = polynom(x, 8)
    x_polynom = (x_polynom - train_means) / train_std
    x_polynom = np.hstack((np.ones((len(x_polynom), 1)), x_polynom))
    fig, ax = plt.subplots()
    plt.scatter(x_train, y_train, color='red')
    plt.plot(x, h0x(x_polynom, theta), linewidth=2)
    plt.xlabel("change in water level", fontsize=14)
    plt.ylabel("water flowing out ", fontsize=14)
    plt.title("Polynomial Fit (lambda=100)", fontsize=16)
    plt.show()

    learning_curves_chart(X_train_poly, y_train, X_val_poly, y_val, 100)

    # 12
    lambda_values = [0, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1, 3, 10]
    val_err = []
    for lamb in lambda_values:
        theta = opt.fmin_cg(cost_l2, np.zeros(X_train_poly.shape[1]), gradient_l2, (X_train_poly, y_train, lamb),
                            disp=False)
        val_err.append(cost_l2(theta, X_val_poly, y_val))
    plt.plot(lambda_values, val_err, c="b", linewidth=2)
    plt.axis([0, len(lambda_values), 0, val_err[-1] + 1])
    plt.grid()
    plt.xlabel("lambda", fontsize=14)
    plt.ylabel("error", fontsize=14)
    plt.title("Validation Curve", fontsize=16)
    plt.show()

    # 13
    theta = opt.fmin_cg(cost_l2, np.zeros(X_train_poly.shape[1]), gradient_l2, (X_train_poly, y_train, 3), disp=False)
    test_error = cost_l2(theta, X_test_poly, y_test)
    print("Test Error: ", test_error, "| Regularized Polynomial (lambda=3))")


import numpy as np
import scipy as sp
import scipy.stats

def gaussian_loglik(obs, mu, sigma):
    return sp.stats.multivariate_normal.logpdf(obs, mean=mu, cov=sigma) / mu.shape[0]

def gaussian_entropy(sigma):
    return 0.5 * (len(sigma) + np.log(np.linalg.det(sigma)) + np.log(2 * np.pi))

def r2_score(obs, pred):
    return 1 - np.sum((obs - pred) ** 2) / np.sum((obs - np.mean(obs)) ** 2)

def cal_error(obs, mu, sigma, bins=5):
    """
    Unweighted regression calibration error for GP predictions.

    We calculate the mean-squared error between predicted versus observed
    empirical CDFs, for the specified number of equally spaced bins on the
    interval [0,1].

    [Equation (9), Kuleshov et. al. 2018]

    Parameters
    ----------
    obs: m-length array of observations
    mu: m-length array of predicted means
    sigma: m x m array of predicted covariance
    bins: number of bins at which to evaluate

    Returns
    -------
    cal_error: float
    predicted: predicted CDFs corresponding to each bin
    empirical: observed CDFs corresponding to each bin
    """
    sigmas = np.diag(sigma)
    quantiles = sp.stats.norm.cdf(obs, mu, np.sqrt(sigmas))
    predicted = np.arange(1/bins, 1+1/bins, 1/bins)
    empirical = np.array([np.mean(quantiles < p) for p in predicted])
    return np.sum((predicted - empirical) ** 2) / bins, predicted, empirical


#  Created byMartin.cz

import numpy as np

def mean_squared_error(Y, Y_hat):
    """
    Calculates cost and its gradient using Mean Squared Error.

    (1/2m)*sum(Y - Y_hat)^.2

    Args:
        Y:
            Data labels.

        Y_hat:
            Predictions/activations from the output layer.

    Returns:
        cost:
            Mean Squared Error Cost result.

        dY_hat:
            Gradient.
    """

    m = Y.shape[0]

    # calc cost
    cost = (1 / (2 * m)) * np.sum(np.square(Y - Y_hat))
    cost = np.squeeze(cost)

    # calc gradient
    dY_hat = - (1 / m) * (Y - Y_hat)

    return cost, dY_hat

def binary_cross_entropy(Y, Y_hat, epsilon=1e-8):
    """
    Calculates cost and its gradient using Binary Cross Entropy.

    (1/m) * np.sum(-np.log(Y_hat) * Y - np.log(1-Y_hat) * (1-Y))

    Args:
        Y:
            Data labels.

        Y_hat:
            Predictions/activations from the output layer.

        epsilon: float
            Zero corrector.

    Returns:
        cost:
            Binary Cross-Entropy Cost result.

        dY_hat:
            Gradient.
    """

    m = Y.shape[0]

    # make data safe
    Y_hat = np.clip(Y_hat, a_min=epsilon, a_max=(1 - epsilon))

    # calc cost
    cost = (1 / m) * np.nansum(-np.log(Y_hat) * Y - np.log(1 - Y_hat) * (1 - Y))
    cost = np.squeeze(cost)

    # calc gradient
    dY_hat = -(Y / Y_hat) + (1 - Y) / (1 - Y_hat)

    return cost, dY_hat

def cross_entropy(Y, Y_hat, epsilon=1e-8):
    """
    Calculates cost and its gradient using Cross Entropy.

    (-1 / m) * np.sum(Y * np.log(Y_hat))

    Args:
        Y:
            Data labels.

        Y_hat:
            Predictions/activations from the output layer.

        epsilon: float
            Zero corrector.

    Returns:
        cost:
            Cross-Entropy Cost result.

        dY_hat:
            Gradient.
    """

    m = Y.shape[0]

    # make data safe
    Y_hat = np.clip(Y_hat, a_min=epsilon, a_max=(1 - epsilon))

    # calc cost
    cost = (-1 / m) * np.nansum(Y * np.log(Y_hat))
    cost = np.squeeze(cost)

    # calc gradient
    dY_hat = -Y / Y_hat

    return cost, dY_hat


import numpy as np
import matplotlib.pyplot as plt
import integrators as intg

# Parameters mass-spring system
m = 1
b = 0.25
k = 1

#  xdot = f(t, x, u),
#   t: time
#   x: state vector
#   u: input
def f(t, x, u):
    # mass sprint system state space form:
    return np.array([x[1], -k*x[0]/m - b*x[1]/m])

# Initial conditions
t = 0
x0 = np.array([0, 1])
u = 0

# Numerical solution
tf = 30; dt = 0.001; n = int(np.floor(tf/dt));

#integrator = intg.Heun(dt, f)
integrator = intg.RungeKutta4(dt, f)

x = x0
t_history = [0]
x_history = [x]

for i in range(n):

    x = integrator.step(t, x, u)
    t = (i+1) * dt

    t_history.append(t)
    x_history.append(x)

# Convert numerical soln to numpy arrays
t_ = np.asarray(t_history)
x_ = np.asarray(x_history)

# Analytical solution
wn = np.sqrt(k/m)
z = b/(2*m*wn)
sigma = z*wn
wd = wn*np.sqrt(1 - z**2)
A = x0[0]
B = (x0[1] + sigma*x0[0])/wd
x_exact = np.exp(-sigma*t_)*(A*np.cos(wd*t_) + B*np.sin(wd*t_))

# Error
error = x_exact - x_[:,0]

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

plt.figure()
plt.plot(t_history, x_[:,0],'rx', label='RK4')
plt.plot(t_history, x_exact,'g--', label='exact')
plt.xlabel('t')
plt.ylabel('x')
plt.legend()
plt.show()

plt.figure()
plt.plot(t_history,error,'g--')
plt.xlabel('t')
plt.ylabel('error')
plt.show()


import numpy as np
import numpy.linalg as la

from orbit.utils.general import rotation_matrix

def phase_adv_matrix(mu1, mu2):
    """4x4 matrix to rotate x-x' by mu1 and y-y' by mu2, both clockwise."""
    R = np.zeros((4, 4))
    R[:2, :2] = rotation_matrix(mu1)
    R[2:, 2:] = rotation_matrix(mu2)
    return R

def V_matrix_2x2(alpha, beta):
    """2x2 normalization matrix for x-x' or y-y'."""
    return np.array([[beta, 0], [-alpha, 1]]) / np.sqrt(beta)

def V_matrix_4x4_uncoupled(alpha_x, alpha_y, beta_x, beta_y):
    """4x4 normalization matrix for x-x' and y-y'."""
    V = np.zeros((4, 4))
    V[:2, :2] = V_matrix_2x2(alpha_x, beta_x)
    V[2:, 2:] = V_matrix_2x2(alpha_y, beta_y)
    return V

def params_from_transfer_matrix(M):
    """Return dictionary of lattice parameters from a transfer matrix.

    Method is taken from `py/orbit/matrix_lattice/MATRIX_Lattice.py`.

    Parameters
    ----------
    M : ndarray, shape (4, 4)
        A transfer matrix.

    Returns
    -------
    lattice_params : dict
        Dictionary with the following keys: 'frac_tune_x', 'frac_tune_y',
        'alpha_x', 'alpha_y', 'beta_x', 'beta_y', 'gamma_x', 'gamma_y'.
    """
    keys = ['frac_tune_x', 'frac_tune_y',
            'alpha_x', 'alpha_y',
            'beta_x', 'beta_y',
            'gamma_x', 'gamma_y']
    lattice_params = {key: None for key in keys}

    cos_phi_x = (M[0, 0] + M[1, 1]) / 2
    cos_phi_y = (M[2, 2] + M[3, 3]) / 2
    if abs(cos_phi_x) >= 1 or abs(cos_phi_y) >= 1 :
        return lattice_params
    sign_x = sign_y = +1
    if abs(M[0, 1]) != 0:
        sign_x = M[0, 1] / abs(M[0, 1])
    if abs(M[2, 3]) != 0:
        sign_y = M[2, 3] / abs(M[2, 3])
    sin_phi_x = sign_x * np.sqrt(1 - cos_phi_x**2)
    sin_phi_y = sign_y * np.sqrt(1 - cos_phi_y**2)

    nux = sign_x * np.arccos(cos_phi_x) / (2 * np.pi)
    nuy = sign_y * np.arccos(cos_phi_y) / (2 * np.pi)
    beta_x = M[0, 1] / sin_phi_x
    beta_y = M[2, 3] / sin_phi_y
    alpha_x = (M[0, 0] - M[1, 1]) / (2 * sin_phi_x)
    alpha_y = (M[2, 2] - M[3, 3]) / (2 * sin_phi_y)
    gamma_x = -M[1, 0] / sin_phi_x
    gamma_y = -M[3, 2] / sin_phi_y

    lattice_params['frac_tune_x'] = nux
    lattice_params['frac_tune_y'] = nuy
    lattice_params['beta_x'] = beta_x
    lattice_params['beta_y'] = beta_y
    lattice_params['alpha_x'] = alpha_x
    lattice_params['alpha_y'] = alpha_y
    lattice_params['gamma_x'] = gamma_x
    lattice_params['gamma_y'] = gamma_y
    return lattice_params

def is_stable(M, tol=1e-5):
    """Return True if transfer matrix is stable.

    M : ndarray, shape (n, n)
        A transfer matrix.
    tol : float
        The matrix is stable if all eigenvalue norms are in the range
        (1 - tol, 1 + tol).
    """
    for eigval in la.eigvals(M):
        if abs(la.norm(eigval) - 1.0) > tol:
            return False
    return True

def get_eigtunes(M):
    """Compute transfer matrix eigentunes -- cos(Re[eigenvalue]).

    Parameters
    ----------
    M : ndarray, shape (4, 4)
        A transfer matrix.

    Returns
    -------
    ndarray, shape (2,)
        Eigentunes for the two modes.
    """
    return np.arccos(la.eigvals(M).real)[[0, 2]] / (2 * np.pi)

def unequal_eigtunes(M, tol=1e-5):
    """Return True if the eigentunes of the transfer matrix are the same.

    M : ndarray, shape (4, 4)
        A transfer matrix.
    tol : float
        Tunes are equal if abs(mu1 - mu2) > tol.
    """
    mu1, mu2 = get_eigtunes(M)
    return abs(mu1 - mu2) > tol


from __future__ import division
import numpy as np
from LinearSolve import LinearSolver
from utils import Grapher

class Interpolator():
    methods = ['lagrange', 'spline']

    def __init__(self, x, y, xstar):
        self.x = x
        self.y = y
        self.xstar = np.sort(xstar)
        self.ystar = None
        self.N = x.size
        self.s = np.array([0] * self.N)
        self.f = None

    ''' Returns i'th Lagrange polynomial evaluated at pt '''
    @staticmethod
    def L(i, x, pt):
        trans = map(lambda arg: (pt - arg) / (x[i] - arg), x[np.arange(len(x)) != i])
        return np.prod(trans)

    def lagrange(self):
        L = Interpolator.L

        def func(pt):
            return np.sum([L(i, self.x, pt) * self.y[i] for i in range(self.N)])
        self.evaluate(func)
        self.f = func

    def spline(self):
        n, x, y, s = self.N, self.x, self.y, self.s
        a, b = np.ndarray(shape=(n - 2, n)), np.ndarray(shape=(n - 2))

        ''' Divided Diference '''
        def dd(i, j):
            return (y[i] - y[j]) / (x[i] - x[j])

        for i in range(1, n - 1):
            a[i - 1, i - 1:i + 2] = [x[i] - x[i - 1], 2 *
                                     (x[i + 1] - x[i - 1]), x[i + 1] - x[i]]
            b[i - 1] = 6 * (dd(i + 1, i) - dd(i, i - 1))
        a = a[..., 1:n - 1]

        solver = LinearSolver(a, b)
        solver.GE_pivoted()
        s[1:-1] = solver.x

        def S(i, pt):
            return ((x[i + 1] - pt)**3 * s[i] + (pt - x[i])**3 * s[i + 1]) / 6 / (x[i + 1] - x[i]) \
                + (y[i] / (x[i + 1] - x[i]) - (x[i + 1] - x[i]) * s[i] / 6) * (x[i + 1] - pt) \
                + (y[i + 1] / (x[i + 1] - x[i]) - (x[i + 1] - x[i]) * s[i + 1] / 6) * (pt - x[i])

        def func(pt):
            for i, x in enumerate(self.x):
                if pt < x:
                    return S(i - 1, pt)
        self.evaluate(func)
        self.f = func

    def evaluate(self, f):
        self.ystar = np.array([f(i) for i in self.xstar])

def main():
    method_names = ['Lagrange polynomials', 'Natural Cubic Spline']

    for i, name in enumerate(method_names):
        print 'press ', i, 'for ', name
    choice = int(raw_input())

    with open("q1.txt", "r") as f:
        n = int(f.readline())
        a = np.array([map(float, f.readline().split()) for i in range(n)])
        x, y = a[..., 0], a[..., 1]
        m = int(f.readline())
        xstar = np.array([float(f.readline()) for i in range(m)])

    func = Interpolator(x, y, xstar)
    getattr(func, Interpolator.methods[choice])()
    Grapher.plot(func.f, func.x[0], func.x[-1], x=func.x, y=func.y)

    with open("out.txt", "w") as fout:
        if func.ystar is not None:
            formated_y = np.array([func.xstar, func.ystar]).transpose()
            np.savetxt(fout, formated_y, fmt='%f',
                       header=method_names[choice], footer='\n', comments='')

if __name__ == '__main__':
    main()


# Create by Etzion Harari
# https://github.com/EtzionR

# Load libraries:
import numpy as np
import matplotlib.pyplot as plt

# Define useful functions
create_matrix = lambda x, p: np.power(x, np.arange(p).reshape(p, 1)).T
create_polynm = lambda x, b: np.dot(create_matrix(x, len(b)), b)
b_coefficient = lambda x, y: np.dot(np.linalg.inv(np.dot(x.T, x)), np.dot(x.T, y))
rmse = lambda p, y: np.sqrt(np.mean(np.power(p - y, 2)))

# Define Polynom Object
class PolyR:
    """
    Polynomial Regression Object
    Optimize Model by fitting polynom degree
    """
    def __init__(self, max_p, cv=5):
        """
        initilize PolyR object
        :param max_p: max degree to check for fitting
        :param cv: number to K fold cross validation
        """
        self.p = None
        self.betas = None
        self.r_mse = None
        self.folds = None
        self.scores = {}
        self.max_p = max_p
        self.ary_p = np.arange(1, self.max_p + 1)
        self.cv = cv

    def fit(self, x, y):
        """
        fit betas vector for the input data
        :param x: x vector (independent variables)
        :param y: y vector (dependent variable)
        :return: The object
        """
        mat_x = create_matrix(x, self.max_p)
        self.folds = np.arange(mat_x.shape[0]) % self.cv

        for j in self.ary_p:
            self.scores[j] = self.cross_validation(mat_x[:, :j], y)

        self.p = min(self.scores, key=lambda k: self.scores[k])
        self.r_mse = self.scores[self.p]
        self.betas = b_coefficient(create_matrix(x, self.p), y)

        return self

    def predict(self, x):
        """
        predict y values using x values
        :param x: x vector (independent variables)
        :return:  y vector (dependent variable)
        """
        return create_polynm(x, self.betas)

    def cross_validation(self, x, y):
        """
        cross validation function
        using to prevent overfitting of the regression
        :param x: x matrix (independent variables)
        :param y: y vector (dependent variable)
        :return: RMSE for the model
        """
        cross_valid = []
        for k in np.arange(self.cv):
            trn = self.folds != k
            tst = self.folds == k

            x_trn = x[trn, :]
            y_trn = y[trn]
            x_tst = x[tst, :]
            y_tst = y[tst]

            betas = b_coefficient(x_trn, y_trn)
            y_prd = np.dot(x_tst, betas)

            cross_valid.append(rmse(y_prd, y_tst))
        return np.mean(cross_valid)

    def plot_rmse(self, size=10):
        """
        plot rmse result for each polynom degree
        :param size: plot size (default: 10)
        """
        vlu = [*self.scores.values()]
        axe = [*self.scores.keys()]
        plt.figure(figsize=(size, size * .6))
        plt.title(f'RMSE for each Polynomial degree:\n[Optimal degree = {self.p}, range from 1 to {self.max_p}]', fontsize=14)
        plt.plot(axe, vlu, color='r', label='RMSE')
        plt.plot([self.p, self.p], [min(vlu), max(vlu)], color='k', linestyle='dotted', label='Minimum RMSE')
        plt.xlabel('Polynomial degree', fontsize=12)
        plt.ylabel('RMSE', fontsize=12)
        plt.legend(fontsize=12)
        plt.show()

# License
# MIT © Etzion Harari


# 2021.04.10
# Problem Statement:
# https://leetcode.com/problems/maximum-gap/
# Referred to the solution here:
# https://leetcode.com/problems/maximum-gap/discuss/50643/bucket-sort-JAVA-solution-with-explanation-O(N)-time-and-space
import numpy as np

class Solution:
    def maximumGap(self, nums: List[int]) -> int:
        # check corner cases
        if len(nums)<2: return 0
        if len(nums)==2: return abs(nums[0]-nums[1])

        # idea: bucket sort
        # the max gap must >= ceiling[(max - min ) / (len(nums) - 1)],
        # therefore, divide the list into (len(nums)-1) buckets,
        # the max gap will only show between buckets (the max of a smaller bucket and the min of a larger bucket)

        # set the division gap, to divide the list into buckets
        division_gap = (max(nums)-min(nums)) // (len(nums)-1)
        if division_gap != (max(nums)-min(nums)) / (len(nums)-1):
            division_gap = division_gap + 1

        # bucket[i][0] would store the min of bucket number i
        # bucket[i][1] would store the max of bucket number i
        bucket = np.zeros((len(nums)-1, 2))
        for i in range(0, len(nums)-1):
            bucket[i][0] = np.inf # initialization
            bucket[i][1] = -np.inf # initialization

        min_val, max_val = min(nums), max(nums)
        # check corner cases where all numbers are the same
        if min_val == max_val: return 0
        # do the division
        for i in range(0, len(nums)):
            if nums[i] == min_val:
                bucket[0][0] = nums[i]
                bucket[0][1] = max(bucket[0][1], nums[i])
            elif nums[i] == max_val:
                bucket[-1][1] = nums[i]
                bucket[-1][0] = min(bucket[-1][0], nums[i])
            else:
                # check which bucket it belongs to
                idx = (nums[i]-min_val) // division_gap
                if idx == (nums[i]-min_val) / division_gap:
                    idx = idx - 1
                bucket[idx][0] = min(bucket[idx][0], nums[i])
                bucket[idx][1] = max(bucket[idx][1], nums[i])

        # iterate over all buckets to see the max gap
        answer = 0
        prev = bucket[0][1]
        for i in range(0, len(nums)-2):
            if bucket[i+1][0] != np.inf:
                answer = max(answer, bucket[i+1][0]-prev)
                prev = bucket[i+1][1]

        return int(answer)


import numpy as np

def split(inte, h):
    """ Split the interval

    Args:
        inte: list/ndarray, the interval to be splited
        h: the step length of the split

    Returns:
        x: list/ndarray, the split of the interval
    """
    # the endpoints of the interval
    a, b = inte
    n = int((b - a)/h)
    x = np.linspace(a, b, n+1)

    return x

def classical_Runge_Kutta(x, func, ini):
    """ Solve ordinary differential equation by 4-th Runge Kutta Method

    Args:
        x: list/ndarray, the split of the interval
        func: the function which satisifies y'(x) = f(x, y)
        ini: double, the initial value for IVP

    Returns:
        y: list/ndarray, the values of the function y(x) at every point x
    """
    n = len(x)
    h = x[1] - x[0]
    A = np.diag([1/2, 1/2, 1], k=-1)
    B = np.array([1/6, 1/3, 1/3, 1/6])
    C = np.array([0, 1/2, 1/2, 1])

    # begin iteration
    y = np.zeros(n)
    y[0] = ini
    for i in range(1, n):
        k = np.zeros(4)
        for j in range(4):
            y_step = 0
            for m in range(4):
                y_step += A[j, m]*k[m]
            k[j] = func(x[i-1]+C[j]*h, y[i-1]+h*y_step)
        phi = 0
        for j in range(4):
            phi += B[j]*k[j]
        y[i] = y[i-1] + h*phi

    return y

def f1(x, y):
    z = y**2
    return z

def f2(x, y):
    z = x/y
    return z

if __name__ == '__main__':
    h = 0.1
    # solve the ordinary differential equation 1 by 4-th Runge-Kutta method
    interval1 = [0, 0.4]
    x1 = split(interval1, h)
    y1 = classical_Runge_Kutta(x1, f1, 1)
    y1_true = 1/(1 - x1)
    print("The numerical solution, analytic solution and the error of the question 1 are as follows:")
    for item in zip(x1, y1, y1_true):
        print(f"{item[0]:.1f}\t{item[1]:.7f}\t{item[2]:.7f}\t{item[2]-item[1]:.7f}")
    # solve the ordinary differential equation 2 by 4-th Runge-Kutta method
    interval2 = [2, 2.6]
    x2 = split(interval2, h)
    y2 = classical_Runge_Kutta(x2, f2, 1)
    y2_true = np.sqrt(x2**2 - 3)
    print("The numerical solution, analytic solution and the error of the question 2 are as follows:")
    for item in zip(x2, y2, y2_true):
        print(f"{item[0]:.1f}\t{item[1]:.7f}\t{item[2]:.7f}\t{item[2]-item[1]:.7f}")


# -*- coding: utf-8 -*-
import numpy as np

#def secante(x0, x1, f, error = 0.01):

def newtonraphson(f, x0, dx=0.01, error=0.0001, max_iter=100000):

    xn = x0

    for n in range(0, max_iter):

        fn = f(xn)

        if abs(fn) < error:
            print('Solución tras ', n, ' iteraciones.')
            return xn

        dfn = (f(xn + dx) - f(xn - dx)) / (2 * dx)

        if dfn == 0:
            print('Método se indefine (derivada se anula).')
            return None

        xn = xn - fn/dfn

    print('Método no converge (se supera el máximo de iteraciones).')
    return None

def cerof(x, y, error = 0.01):

    x0 = []
    y0 = []

    for i, X in enumerate(x):
        if np.abs(y[i]) <= error:
            x0.append(X)
            y0.append(y[i])

    x0 = np.asarray(x0)
    y0 = np.asarray(y0)

    return x0, y0

def ceros(a, b, f, error=0.01):
    """
    params x: arreglo en el que se quiere encontrar el cero de f
    params f: función a la que se le quiere encontrar el cero
    params a, b: extremos del intervalo que se desea explorar
    params error: nivel de precisión deseado
    returns: valor x en [a, b] en que f(x)=0
    """

    fa = f(a)
    fb = f(b)

    ceros = []
    fceros = []

    if fa * fb > 0:
        print('El método no aplica')
        return ceros, fceros
    elif fa * fb == 0:
        if fa == 0:
            ceros.append(a)
            fceros.append(fa)
        if fb == 0:
            ceros.append(b)
            fceros.append(fb)

        return ceros, fceros

    else:
        while (np.abs(fa).astype(float) > error) and (np.abs(fb).astype(float) > error):
            c = (a + b) / 2
            fc = f(c)

            if fa * fc > 0:
                a = c
                fa = fc

            else:
                b = c
                fb = fc

        if np.abs(fa).astype(float) <= error:
            ceros.append(a)
            fceros.append(fa)

        if np.abs(fb).astype(float) <= error:
            ceros.append(b)
            fceros.append(fb)

        return ceros, fceros

def derivadafun(a, b, f, dx=0.01):
    """
    Devuelve la derivada de una función, considerando que en extremo izquierdo sólo se puede
    aplicar derivada forward, en extremo derecho sólo se puede aplicar derivada backward y
    entremedio puede aplicad derivada central, que es más precisa.

    param x: vector en el que se evaluará la función, tiene largo N.
    param y: vector de la función evaluada en x.
    """

    N = int((b - a) / dx)
    x = np.linspace(a, b, N)

    derivada = []

    for i, X in enumerate(x):

        if i == 0:                   # Extremo izquierdo: usamos derivada forward
            deriv = (f(x[1]) - f(x[0])) / dx
            derivada.append(deriv)

        elif i == N - 1:             # Extremo derecho: usamos derivada backward (recordar que índice de x e y llega hasta N-1
            deriv = (f(x[N - 1]) - f(x[N - 2])) / dx
            derivada.append(deriv)

        else:                        # Extremo izquierdo: usamos derivada forward
            deriv = (f(x[i + 1]) - f(x[i - 1])) / (2 * dx)
            derivada.append(deriv)

    derivada = np.asarray(derivada)

    return derivada

def derivadavec(x, y):
    """
    Devuelve la derivada de una función, considerando que en extremo izquierdo sólo se puede
    aplicar derivada forward, en extremo derecho sólo se puede aplicar derivada backward y
    entremedio puede aplicad derivada central, que es más precisa.

    param x: vector en el que se evaluará la función, tiene largo N.
    param y: vector de la función evaluada en x.
    """

    N = len(x)

    # Calculamos dx, que tendrá N-1 valores
    dx = []

    for i in range(N-1):
            dx.append(x[i + 1] - x[i])

    # OJO: Si el vector x fuese definido de forma equiespaciada, por ejemplo, con linspace
    # , basta con dx = x[1] - x[0].
    # Además, en ese caso, dx[0] = dx[N - 1] = dx y dx[i + 1] + dx[i - 1] = 2*dx

    derivada = []

    for i in range(N):

        if i == 0:                   # Extremo izquierdo: usamos derivada forward
            deriv = (y[1] - y[0]) / dx[0]
            derivada.append(deriv)

        elif i == N - 1:             # Extremo derecho: usamos derivada backward (recordar que índice de x e y llega hasta N-1
            deriv = (y[N - 1] - y[N - 2]) / dx[N - 2]
            derivada.append(deriv)

        else:                        # Centro: usamos derivada central
            deriv = (y[i + 1] - y[i - 1]) / (dx[i] + dx[i - 1])
            derivada.append(deriv)

    derivada = np.asarray(derivada)

    return derivada