# Example from https://roshansanthosh.wordpress.com/2015/02/22/linear-regression-in-theano/
# Objective: Regression in a parametric model. We will be using it to determine the
# coefficients of a line that best fits a given set of points.
# Since we are using a parametric model we make assumptions about the functional form of the
# relation ship between the independent and the dependent variables and in this case we
# assume there is a linear relationship (linear regression does not have to model linear
# relationships) and try to estimate the intersect and slope.
# We use a singel feature for X

import numpy as np
import theano
import theano.tensor as T
import matplotlib.pyplot as plt

rng = np.random
#Training Data
X = np.asarray([3,4,5,6.1,6.3,2.88,8.89,5.62,6.9,1.97,8.22,9.81,4.83,7.27,5.14,3.08])
Y = np.asarray([0.9,1.6,1.9,2.9,1.54,1.43,3.06,2.36,2.3,1.11,2.57,3.15,1.5,2.64,2.20,1.21])

# Coefficients
# Slope = m
# Intercept = c
# Randomly initialize m and c as shared variables in Theano. Shared mean that c and m can be
# used by multiple functions (they have global scope?). Shared variables are the fastes type
# in theano because the GPU can use different optimizations and since c and m are what will
# be updated alot it is a smart idea to have them as shared.

# In Theano thear are symbolic variables and expressions.
m_value = rng.rand()
c_value = rng.rand()

m = theano.shared(m_value, name = 'm')
c = theano.shared(c_value, name = 'c')

x = T.vector('X')
y = T.vector('y')

num_samples = X.shape[0]

# Defining the cost function
# Every machine learning model has a cost parameter which the ml algorithm tries to minimize.
# The cost parameter is essentially the cumulative error predicted by the model for the
# current set of coefficients.

# TODO also use RSS is there a difference?

# Use the randomly assigned values of c and m to predict y
prediction = T.dot(x,m) + c # our line function
# How big is the error in all the samples in x?
cost = T.sum(T.pow(prediction-y,2))/(2*num_samples) # MSE cost

# Optimization
# We use gradient decent
gradm = T.grad(cost,m)
gradc = T.grad(cost,c)

learning_rate = 0.01
training_steps = 10

train = theano.function([x,y],cost,updates = [(m,m-learning_rate*gradm), (c,c-learning_rate*gradc)])
test = theano.function([x],prediction)

estimates = np.zeros(training_steps)
x_vals = np.zeros(training_steps)
for i in range(training_steps):
    estimates[i] = train(X,Y)
    x_vals[i] = i

print("Slope :")
print(m.get_value()) # since m and c are shared we have to use get_value
print("Intercept :")
print(c.get_value())

axes = plt.gca()
axes.set_ylim([np.amin(estimates), np.amax(estimates)])
plt.plot(x_vals, estimates)
plt.show()

# Note: X and Y are the input data vecors while x and y are the Theano variables.
# All the expressions and functions are written in terms of the symbolc variables x and y while
# X and Y is used when calling the functions.
# /Users/henke/anaconda2/envs/theanohacking/bin/frameworkpython -m IPython


"""
Code that was written but that was replaced or became obsolete, and is on its
way to removal, but is stored here in case it becomes relevant again.
"""
import numpy as np
from fractions import gcd

def sieve(n: int):
    """
    Implementation of the Sieve of Eratosthenes algorithm.

    Given an integer `n`, return a list of all primes less than or equal to it.
    """
    candidates = np.ones(n)     # nonzero index ==> possible prime!
    candidates[[0, 1]] = 0      # one and zero aren't prime.
    for i in range(2, n):
        if candidates[i] != 0:
            for j in range(2*i, n, i):
                candidates[j] = 0
    return list(np.nonzero(candidates))

def coprime(n: int):
    """Return a list of all numbers less than `n` that are coprime with `n`."""
    return [i for i in range(1, n) if gcd(i, n) == 1]


import numpy as np
import matplotlib.pyplot as plt
from pandas.io.parsers import read_csv

def calcula_normalizado(valor, med, desv):
    return (valor - med)/desv

def normaliza(X):
    medias = np.array([])
    desvs = np.array([])
    for i in range(1, np.shape(X)[1]):
        medias = np.append(medias, np.mean(X[:,i]))
        desvs = np.append(desvs, np.std(X[:,i]))
        X[:,i] = calcula_normalizado(X[:,i], medias[i-1], desvs[i-1])
    return (medias, desvs)

def carga_csv(file_name):
    valores = read_csv(file_name, header = None).values
    return valores.astype(float)

def hipotesis(X, Theta):
    return np.dot(X,Theta)

def coste(X, Y, Theta):
    H = np.dot(X, Theta)
    aux = (H - Y) ** 2
    return aux.sum() / (2*len(X))

def descenso_gradiente(X, Y, alpha, Theta, n):

    ths = np.array([[0.]])
    temps = np.array([[0.]])
    for i in range(n-1):
        ths = np.vstack((ths, [0.]))
        temps = np.vstack((temps, [0.]))
    costes = np.array([coste(X, Y, Theta)])
    j = 0
    while True:
        j = j + 1
        for i in range(n):
            temps[i] = Theta[i] - alpha*(1/len(X))*np.sum((hipotesis(X,Theta) - Y) * X[:,i:i+1])

        Theta = temps
        np.hstack((ths, temps))
        costes = np.append(costes, coste(X,Y, Theta))

        #print(Theta)
        #print(costes[-1])
        if (costes[-2] - costes[-1]) < 0.001:
            break
    #print(j)
    return (Theta, costes, j)

datos = carga_csv("ex1data2.csv")

X = datos[:, :-1]
Y = datos[:, -1:]

m = np.shape(X)[0]
X = np.hstack([np.ones([m,1]), X])
n = np.shape(X)[1]

Xp = np.copy(X)

medias, desvs = normaliza(X)

Theta = np.array([[0.]])

for i in range(n-1):
    Theta = np.vstack((Theta, [0.]))

#alpha = 0.01

#Theta, ths, costes = descenso_gradiente(X, Y, alpha, Theta, n)

j = 0
alphas = np.array([0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1])

colors = np.array(["blue","green","red","cyan","magenta","yellow","black"])
labels = np.array(["0.001", "0.003", "0.01", "0.03", "0.1", "0.3", "1"])
plt.figure()

for i in range(np.shape(alphas)[0]):
    #j = j+1
    a, costes, iter = descenso_gradiente(X, Y, alphas[i], Theta, n)
    print(iter)
    #print(j, "  ", costes[-1], "  ", i)
    plt.plot(np.arange(iter + 1), costes, c=colors[i], label=labels[i], linestyle='-')

plt.legend()

plt.xlabel("Num iter")
plt.ylabel("Costes")

plt.savefig("alphas.png")

#Xq = np.array([2500, 3])
Xq = np.array([1650,3])

Xq_norm = calcula_normalizado(np.transpose(Xq), medias, desvs)
Xq_norm = np.hstack([np.ones([1]), Xq_norm])

Xq = np.hstack([np.ones([1]), Xq])

Theta, costes, iter = descenso_gradiente(X, Y, 1.2, Theta, n)

Yp = hipotesis(Xq_norm, Theta)

X = Xp
aux = np.dot(np.transpose(X), X)
aux = np.linalg.pinv(aux)
aux = np.dot(aux, X.transpose())
eqTheta = np.dot(aux, Y)

Y = hipotesis(Xq, eqTheta)

print(Yp, Y)

print("FIN")


from manimlib.imports import *
from scipy.optimize import curve_fit

class GraphDensity(GraphScene):
    CONFIG = {
        "x_axis_label" : "$t$",
        "y_axis_label" : "$\\rho(t)$",
        "x_label_direction": DOWN,
        "y_label_direction": LEFT,
        "y_min": -1,
        "y_max": 10,
        "y_axis_height": 6,
        "y_tick_frequency": 1,
    }
    def construct(self):
        self.setup_axes(True)

        the_equation = TexMobject("\\rho(", "t", ")", "=", "k", "\\left(", "t_0", "\\over", "t",  "\\right)", "^2")
        the_equation[1].set_color(RED)
        the_equation[8].set_color(RED)
        the_equation.shift(UP*2 + RIGHT*3)
        self.play(
            Write(the_equation)
        )

        self.wait()

        def get_density_graph(t0=10.0, K=1.0):
            def density(t):
                return K * (t0/t)**(2.0)
            return density

        rho = self.get_graph(get_density_graph(t0=2.0, K=1.0), x_min=0.01, step_size=0.0001, color="#f8961e")
        rho.shift(UP * 0.3) #fudge to make it look nicer
        self.play(
            ShowCreation(rho.reverse_points()), run_time=3.0
        )
        self.wait()

class GraphScaleFactor(GraphScene):
    CONFIG = {
        "x_axis_label" : "$t$",
        "y_axis_label" : "$a(t)$",
        "x_label_direction": DOWN,
        "y_label_direction": LEFT,
    }
    def construct(self):
        self.setup_axes(True)

        the_equation = TexMobject("a(", "t", ")", "=","\\left(", "t", "\\over", "t_0",  "\\right)", "^{\\frac{2}{3}}")
        the_equation[1].set_color(RED)
        the_equation[5].set_color(RED)
        the_equation.shift(UP*2 + RIGHT*3)
        self.play(
            Write(the_equation)
        )

        self.wait()

        def get_scalefactor_graph(t0=10.0, K=1.0):
            def scalefactor(t):
                return K * (t/t0)**(2/3.0)
            return scalefactor

        rho = self.get_graph(get_scalefactor_graph(t0=0.6), x_min=0.01, step_size=0.0001, color="#9a031e")
        self.play(
            ShowCreation(rho.reverse_points()), run_time=3.0
        )
        self.wait()
        origin = self.coords_to_point(0, 0)
        self.play(
            FocusOn(origin, color="#e36414")
        )
        arrow = Arrow(origin + UP*2 + RIGHT, origin, color="#fb8b24")
        self.play(
            GrowArrow(arrow)
        )
        a_at_O = TexMobject("a", "(", "t", "=", "0", ")", "=", "0").scale(0.7).move_to(origin + UP*2 + RIGHT)
        a_at_O[2].set_color(RED)
        self.play(
            Write(a_at_O)
        )
        self.wait()

class Findingrho(Scene):

    def construct(self):

        temp = TexMobject("\\rho", "=")
        rho_of_a = VGroup(temp, TexMobject("k", "\\over", "a", "^3" ).next_to(temp, RIGHT).shift(UP*0.1)).shift(UP)
        # rho_of_a[1][2].set_color(ORANGE)
        a_of_t = TexMobject("a", "=", "\\left(", "t", "\\over", "t_0", "\\right)", "^{\\frac{2}{3}}" ).shift(DOWN)
        a_of_t[3].set_color(RED)

        self.play(
            Write(rho_of_a)
        )
        self.play(
            Write(a_of_t)
        )
        self.wait()
        self.play(
            ApplyMethod(rho_of_a.shift, LEFT*4),
            ApplyMethod(a_of_t.shift, LEFT*4)
        )
        brace = Brace(VGroup(rho_of_a, a_of_t), RIGHT)
        self.play(
            GrowFromCenter(brace)
        )
        implies = TexMobject("\\Rightarrow").next_to(brace, RIGHT)
        self.play(
            FadeInFrom(implies, LEFT)
        )
        rho_of_t_lhs = TexMobject("\\rho", "(", "t", ")", "=")
        rho_of_t_lhs[2].set_color(RED)
        rho_of_t_lhs.next_to(implies, RIGHT)
        self.play(
            TransformFromCopy(rho_of_a[0], rho_of_t_lhs[0]),
        )
        self.play(
            Write(rho_of_t_lhs[1:])
        )
        rho_t_rhs1 = TexMobject("k", "\\over", "\\left[","\\left(", "t", "\\over", "t_0", "\\right)", "^{\\frac{2}{3}}", "\\right]", "^3").next_to(rho_of_t_lhs)
        rho_t_rhs1.shift(DOWN*0.4)
        rho_t_rhs1[4].set_color(RED)
        self.play(
            TransformFromCopy(rho_of_a[1][0], rho_t_rhs1[0]),
        )
        self.play(
            Write(rho_t_rhs1[1]),
            TransformFromCopy(a_of_t, rho_t_rhs1[3:9])
        )
        self.play(
            Write(rho_t_rhs1[2]),
            Write(rho_t_rhs1[-2:])
        )
        rho_t = TexMobject("=", "k", "\\left(", "t_0", "\\over", "t", "\\right)", "^2").next_to(rho_t_rhs1).shift(UP*0.5)
        rho_t[5].set_color(RED)
        self.play(
            Write(rho_t[:2])
        )
        self.play(
            Write(rho_t[2:])
        )

        self.wait()

        self.wait()

class Friedmann(Scene):

    def construct(self):

        lhs = TexMobject("\\left(", "\\dot{a}", "\\over", "a", "\\right)", "^2", color="#fdfcdc")
        eq = TexMobject("=", color="#fdfcdc").next_to(lhs, RIGHT)
        rhs = TexMobject("\\left(", "8", "\\pi", "G", "\\over", "3", "\\right)", "\\rho", color="#fdfcdc").next_to(eq, RIGHT)
        friedmann = VGroup(lhs, eq, rhs).scale(1.5).move_to(ORIGIN)
        title = TextMobject("Friedmann Equation", color="#fb8b24").scale(1.5).to_edge(UP)
        self.play(
            FadeInFromDown(title)
        )
        self.wait()
        self.play(
            Write(lhs)
        )
        self.play(
            Write(eq)
        )
        self.play(
            Write(rhs[:-1])
        )
        self.play(
            Write(rhs[-1])
        )
        self.wait()

        #explain terms
        self.play(
            Indicate(lhs[3], color="#00afb9", scale_factor=2)
        )
        scale_factor = TextMobject("Scale", " Factor", color="#f4d58d").shift(DOWN*2).scale(1.5)
        self.play(
            ShowIncreasingSubsets(scale_factor)
        )
        self.play(
            FadeOut(scale_factor)
        )
        self.wait(0.5)
        self.play(
            Indicate(lhs[1], color="#00afb9", scale_factor=2)
        )
        dadt = TextMobject("Rate", " of", " Change", color="#f4d58d").shift(DOWN*2).scale(1.5)
        self.play(
            ShowIncreasingSubsets(dadt)
        )
        self.play(
            FadeOut(dadt)
        )
        self.wait(0.5)
        self.play(
            Indicate(rhs[3], color="#00afb9", scale_factor=2)
        )
        G = TextMobject("Newton's", " Constant", color="#f4d58d").shift(DOWN*2).scale(1.5)
        self.play(
            ShowIncreasingSubsets(G)
        )
        self.play(
            FadeOut(G)
        )
        self.wait(0.5)
        self.play(
            Indicate(rhs[-1], color="#00afb9", scale_factor=2)
        )
        density = TextMobject("Energy", " Density", color="#f4d58d").shift(DOWN*2).scale(1.5)
        self.play(
            ShowIncreasingSubsets(density)
        )
        self.play(
            FadeOut(density)
        )
        self.wait()

class ScaleFactorIntro(VectorScene, ZoomedScene):

    def construct(self):
        self.add_plane(animate=True, background_line_style= {
            "stroke_color": GREEN,
            "stroke_width": 1,
            "stroke_opacity": 0.4,
        },
        axis_config = {
            "stroke_color" : GREEN,
            "stroke_width" : 0.
        },
        run_time=2)
        self.wait()
        time = TexMobject("time =", "t_0").to_corner(UR)
        self.play(
            Write(time)
        )
        us = Dot(color="#0077b6", radius=0.2)

        self.play(
            FocusOn(us, color=BLUE)
        )
        self.play(
            GrowFromCenter(us)
        )
        self.wait()

        galaxy = Dot(color=GOLD, radius=0.2).shift(4*RIGHT)

        self.play(
            FocusOn(galaxy, color=GOLD)
        )
        self.play(
            GrowFromCenter(galaxy)
        )
        self.wait()
        lines = VGroup()
        for i in range(4):
            l = Line(UP/2.0, DOWN/2.0, color=GREEN).shift((i+1)*RIGHT)
            lines.add(l)

        self.play(
            AnimationGroup(
                *[ShowCreationThenFadeOut(l) for l in lines], lag_ratio=0.5, run_time=1.0
            )
        )
        self.wait()

        brace = Brace(VGroup(us, galaxy), DOWN)
        self.play(
            FadeInFromDown(brace)
        )
        ell = TexMobject("\\ell", "=", "4").next_to(brace, DOWN)
        self.play(
            Write(ell)
        )
        self.wait()
        self.play(
            FadeOut(brace),
            FadeOut(ell)
        )
        self.wait()
        light = Line(ORIGIN, RIGHT*4, color="#ffd60a", stroke_width=8.0)

        self.play(
            ShowCreationThenDestruction(light)
        )
        self.play(
            ShowCreationThenDestruction(light.reverse_points())
        )
        self.wait()

        d1 = TexMobject("d_{phys}(t_0)").move_to(ell).shift(UP * 2)
        self.play(
            Write(d1)
        )
        self.wait()
        self.play(
            FadeOut(d1)
        )
        def justfortime(m, alpha):
            m.restore()
            m.scale((1 - alpha) + alpha * 9/14.)
            m.shift((RIGHT*3.6 + UP*2.1 - m.get_center())*alpha)

        time.save_state()
        self.play(
            ApplyMethod(self.camera_frame.set_width, 9),
            ApplyMethod(us.scale, 9/14.0),
            ApplyMethod(galaxy.scale, 9/14.0),
            UpdateFromAlphaFunc(time, justfortime),
            run_time=2.0
        )
        self.play(
            Transform(time[1], TexMobject("t").scale(9/14.).move_to(time[1]))
        )
        self.wait()
        self.play(
            AnimationGroup(
                *[ShowCreationThenFadeOut(l) for l in lines], lag_ratio=0.5, run_time=1.0
            )
        )
        self.wait()
        ell.scale(9./14.)
        self.play(
            FadeIn(brace),
            Write(ell)
        )
        self.wait()
        self.play(
            FadeOut(brace),
            FadeOut(ell)
        )

        self.wait()
        self.play(
            ShowCreationThenDestruction(light.reverse_points()), run_time=1.5
        )
        self.play(
            ShowCreationThenDestruction(light.reverse_points()), run_time=1.5
        )
        self.wait()

        d2 = TexMobject("d_{phys}(t)", ">", "d_{phys}(t_0)").move_to(ell).shift(UP * 2).scale(9.0/14.0)
        self.play(
            Write(d2)
        )
        self.wait()
        self.play(
            FadeOut(d2)
        )
        self.wait()
        self.play(
            FadeOut(us),
            FadeOut(galaxy),
            FadeOut(time)
        )
        at = TexMobject("d_{phys}", "(", "t", ")", "=", "a", "(","t", ")", "\\ell").scale(9./14)
        self.wait()
        self.play(
            Write(at[0])
        )
        self.play(
            Write(at[-1])
        )
        self.wait()
        self.play(
            Write(at[1:4])
        )
        self.wait()
        self.play(
            Write(at[4:-1])
        )

        self.wait()
        self.play(
            Indicate(at[5:9], color="#38b000", scale_factor=2.0)
        )
        text = TextMobject("Scale", " Factor", color="#1982c4").shift(UP)
        self.play(
            ShowIncreasingSubsets(text)
        )
        self.play(
            FadeOut(text)
        )
        self.wait()
        scale_factor = TexMobject("d_{phys}(t = ","0", ")" "=", "a(t = ", "0", ")", "\\ell").scale(9.0/14)
        scale_factor[1].set_opacity(0.0)
        scale_factor[4].set_opacity(0.0)
        time1 = DecimalNumber(0, num_decimal_places=0).scale(9.0/14).move_to(scale_factor[1])
        time2 = DecimalNumber(0, num_decimal_places=0).scale(9.0/14).move_to(scale_factor[4])
        # self.play(
        #     Write(scale_factor),

        # )
        self.play(
            ReplacementTransform(at, scale_factor)
        )
        self.play(
            Write(time1),
            Write(time2)
        )
        self.wait()
        time1.add_updater(lambda m,dt : m.move_to(scale_factor[1]))
        time2.add_updater(lambda m,dt : m.move_to(scale_factor[4]))
        time1.add_updater(lambda m,dt : m.set_value(m.get_value() + dt*2))
        time2.add_updater(lambda m,dt : m.set_value(m.get_value() + dt*2))
        self.add(time1, time2)

        self.play(
            ApplyMethod(
                self.camera_frame.set_width, 2
            ),
            ApplyMethod(scale_factor.scale, 2.0/9),
            ApplyMethod(time1.scale, 2.0/9),
            ApplyMethod(time2.scale, 2.0/9),
            run_time=4.5,
            rate_func=linear
        )
        time1.set_value(9)
        time2.set_value(9)
        time1.clear_updaters()
        time2.clear_updaters()
        self.wait()

class HubblesLaw(GraphScene):

    CONFIG = {
        "x_min": -1,
        "x_max": 120,
        "x_axis_width": 10,
        "x_tick_frequency": 10,
        "x_leftmost_tick": 0,  # Change if different from x_min
        "x_labeled_nums": [i for i in range(0, 120, 20)],
        "x_axis_label": "$d (Mpc)$",
        "y_min": -1,
        "y_max": 10000,
        "y_axis_height": 6,
        "y_tick_frequency": 1000,
        "y_bottom_tick": None,  # Change if different from y_min
        "y_labeled_nums": [i for i in range(2000, 10000, 2000)],
        "y_axis_label": "v (km/s)",
        "axes_color": GREY,
        "graph_origin": 2.5 * DOWN + 4 * LEFT,
        "exclude_zero_label": False,

        #myedits
        "x_label_direction": DOWN,
        "y_label_direction": LEFT,
    }

    def construct(self):
        self.setup_axes(True)

        #data taken from :: https://iopscience.iop.org/article/10.1086/320638/pdf --- table 7
        D = [89.2, 66.7, 114.9, 62.2, 88.4, 45.1, 74.3, 43.2, 68.2, 85.6, 20.7, 39.5, 15.0, 58.3, 31.3, 38.7, 66.6, 57.3, 50.9, 53.3, 19.8]
        v = [6709, 4730, 8930, 4749, 7016, 3106, 4982, 3272, 4820, 7143, 1607, 3149, 1380, 4061, 2304, 3294, 4924, 4869, 4398, 3545, 1088]
        data_dots = VGroup()
        for i in range(len(D)):
            point = self.coords_to_point(D[i], v[i])
            d = Dot(color="#c5d86d").move_to(point)
            data_dots.add(d)

        ##last point is for Ursa Major
        self.wait()
        self.play(
            FocusOn(self.coords_to_point(0, 0), color=GREEN)
        )
        self.wait()
        self.play(
            GrowFromCenter(data_dots[-1])
        )
        self.wait()
        arrow = Arrow(data_dots[-1].get_center() + UR, data_dots[-1], color=BLUE)
        text = TextMobject("Ursa Major", color=DARK_BLUE).next_to(arrow, UP).shift(RIGHT)
        self.play(
            GrowArrow(arrow),
            Write(text)
        )
        self.wait()
        self.play(
            FadeOut(arrow),
            FadeOut(text)
        )
        self.wait()
        x_line = Line(self.coords_to_point(0, 0), self.coords_to_point(D[-1], 0), color=ORANGE, stroke_width=8.0).add_tip()
        y_line = Line(self.coords_to_point(D[-1], 0), self.coords_to_point(D[-1], v[-1]), color=ORANGE, stroke_width=8.0).add_tip()
        self.play(
            ShowCreationThenFadeOut(x_line)
        )
        self.wait()
        self.play(
            ShowCreationThenFadeOut(y_line)
        )
        self.wait(2)
        self.play(
            AnimationGroup(
                *[GrowFromCenter(d) for d in data_dots[:-1]], lag_ratio=0.5, run_time=1.5
            )
        )

        def line(x, m, c):
            return m*x + c

        def get_line(m, c):
            def y(x):
                return m*x + c
            return y

        params,_ = curve_fit(line, D, v)
        fit = self.get_graph(get_line(*params), color="#1b998b")

        self.wait(2)
        self.play(
            ShowCreation(fit)
        )
        # self.add(data_dots)

        self.wait()

class SolutionDust(Scene):

    def construct(self):
        today = TexMobject("\\text{today, }", "t_0").shift(UP*2 + LEFT*4)
        at0 = TexMobject("a", "(", "t_0", ")", "=", "1").next_to(today, DOWN, buff=MED_LARGE_BUFF)
        later = TexMobject("\\text{later, }", "t", "> t_0").shift(UP*2 + RIGHT*4)
        later[1].set_color(RED)
        at = TexMobject("a", "(", "t", ")", ">", "1").next_to(later, DOWN, buff=MED_LARGE_BUFF)
        at[2].set_color(RED)
        self.play(
            FadeInFromDown(today)
        )
        self.play(
            ShowCreation(Underline(today))
        )
        self.play(
            Write(at0)
        )

        self.play(
            FadeInFromDown(later)
        )
        self.play(
            ShowCreation(Underline(later))
        )
        self.play(
            Write(at)
        )
        self.wait()

        dphyst0 = TexMobject("d_{phys}", "(", "t_0", ")", "=", "d").next_to(at0, DOWN, buff=MED_LARGE_BUFF)
        dphyst = TexMobject("d_{phys}", "(", "t", ")", "=", "a(t)", "d").next_to(at, DOWN, buff=MED_LARGE_BUFF)
        dphyst[2].set_color(RED)
        self.play(
            Write(dphyst0)
        )
        self.wait()
        self.play(
            Write(dphyst)
        )
        self.wait()

        Vphyst0 = TexMobject("V", "(", "t_0", ")", "=", "d", "^3").next_to(dphyst0, DOWN, buff=MED_LARGE_BUFF)
        Vphyst = TexMobject("V", "(", "t", ")", "=", "a^3(t)", "d", "^3").next_to(dphyst, DOWN, buff=MED_LARGE_BUFF)
        Vphyst[2].set_color(RED)
        self.play(
            Write(Vphyst0)
        )
        self.wait()
        self.play(
            Write(Vphyst)
        )
        self.wait()

        rhophyst0 = TexMobject("\\rho", "(", "t_0", ")", "\\propto", "\\frac{1}{d^3}").next_to(Vphyst0, DOWN, buff=MED_LARGE_BUFF)
        rhophyst = TexMobject("\\rho", "(", "t", ")", "\\propto", "\\frac{1}{a^3(t)d^3}").next_to(Vphyst, DOWN, buff=MED_LARGE_BUFF)
        rhophyst[2].set_color(RED)
        self.play(
            Write(rhophyst0)
        )
        self.wait()
        self.play(
            Write(rhophyst)
        )
        self.wait()

        self.play(
            AnimationGroup(
                *[Uncreate(m) for m in self.get_top_level_mobjects()], lag_ratio=0.2
            )
        )
        self.wait()
        rhot = TexMobject("\\rho", "\\propto", "\\frac{1}{a^3}").scale(1.5)
        self.play(
            Write(rhot)
        )
        self.wait()
        self.play(
            Transform(rhot[1], TexMobject("=").move_to(rhot[1])),
            Transform(rhot[2], TexMobject("\\frac{k}{a^3}").move_to(rhot[2]).scale(1.5))
        )
        self.wait()
        self.play(
            Uncreate(rhot)
        )

        lhs = TexMobject("\\left(", "\\dot{a}", "\\over", "a", "\\right)", "^2", color=WHITE)
        eq = TexMobject("=", color=WHITE).next_to(lhs, RIGHT)
        rhs = TexMobject("\\left(", "8", "\\pi", "G", "\\over", "3", "\\right)", "\\rho", color=WHITE).next_to(eq, RIGHT)
        friedmann = VGroup(lhs, eq, rhs).move_to(ORIGIN).scale(1.5)
        self.play(
            Write(friedmann)
        )
        self.wait()
        self.play(
            Transform(rhs[-1], TexMobject("\\frac{k}{a^3}").scale(1.5).move_to(rhs[-1]).shift(UP*.15))
        )
        self.wait()
        self.play(
            Uncreate(rhs)
        )
        rhs = TexMobject("\\sqrt{", "D", "\\over", "a^3", "2", "}").scale(1.5).next_to(eq, RIGHT)
        self.play(
            Write(rhs[2:])
        )
        self.wait()
        self.play(
            CircleIndicate(lhs[-1])
        )
        self.play(
            Uncreate(lhs[0]),
            Uncreate(lhs[-2:]),
            ShowCreation(rhs[0]),
            ShowCreation(rhs[1])
        )
        self.wait()
        self.play(
            Uncreate(lhs),
            Uncreate(eq),
            Uncreate(rhs)
        )
        eq = TexMobject("=").scale(1.5)
        lhs = TexMobject("\\int{", "\\sqrt{a} \\, \\, \\, \\,", "\\dot{a}", "\\, \\, \\, \\, \\, \\,\\, \\, }").scale(1.5).next_to(eq, LEFT)
        rhs = TexMobject("\\sqrt{D}", "\\int{",  "dt", "}").scale(1.5).next_to(eq, RIGHT)
        self.play(
            Write(lhs[1:-1])
        )
        self.play(
            Write(eq),
            Write(rhs[0])
        )
        self.wait()
        self.play(
            Transform(lhs[2], TexMobject("da", "\\over", "dt").scale(1.5).move_to(lhs[2]))
        )
        self.wait()
        self.play(
            ApplyMethod(lhs[2][-1].move_to, rhs[-2])
        )
        self.play(
            Uncreate(lhs[2][1]),
            ApplyMethod(lhs[2][0].next_to, lhs[1], buff=0.1)
        )
        self.play(
            Write(lhs[0]),
            Write(rhs[1])
        )
        self.wait()
        self.play(
            Uncreate(lhs),
            Uncreate(rhs),
            FadeOut(eq)
        )
        self.wait()
        lhs = TexMobject("a", "(", "t", ")").scale(1.5).next_to(eq, LEFT)
        lhs[2].set_color(RED)
        rhs = TexMobject("\\left(", "t", "\\over", "t_0", "\\right)", "^{\\frac{2}{3}}").scale(1.5).next_to(eq, RIGHT)
        rhs[1].set_color(RED)
        self.play(
            Write(lhs)
        )
        self.play(
            Write(rhs),
            Write(eq)
        )
        self.wait()

SCENES_IN_ORDER = [
    HubblesLaw,
    ScaleFactorIntro,
    Friedmann,
    SolutionDust,
    GraphScaleFactor,
    Findingrho,
    GraphDensity
]


'''
Created on 2018年3月28日

@author: IL MARE
'''
import numpy as np

def calShannonEnt(trainLabel):
    m = len(trainLabel)
    uniqueVal = set(trainLabel)
    countDict = {}
    shannonNum = 0.0
    for label in trainLabel:
        countDict[label] = countDict.get(label, 0) + 1
    for label in uniqueVal:
        p = (countDict[label] / m)
        shannonNum -= p * np.log2(p)
    return shannonNum

def splitDataMatrix(dataMatrix, label, axis, value):
    returnMat = []
    labelMat = []
    for row, row1 in zip(dataMatrix, label):
        if row[axis] == value:
            tmp_lst = row[0: axis]
            tmp_lst.extend(row[axis + 1:])
            returnMat.append(tmp_lst)
            labelMat.append(row1)
    return returnMat, labelMat

def chooseBestFeature(trainSet, label):
    m = len(trainSet)
    maxGain = -1
    baseShannonEnt = calShannonEnt(label)
    index = -1
    for i in range(len(trainSet[0])):
        uniqueAttr = set([example[i] for example in trainSet])
        tmp_Ent = 0
        for attr in uniqueAttr:
            subSet, labelMat = splitDataMatrix(trainSet, label, i, attr)
            newShannonEnt = calShannonEnt(labelMat)
            tmp_Ent += float(len(subSet) / m) * newShannonEnt
        gain = baseShannonEnt - tmp_Ent
        if gain > maxGain:
            maxGain = gain
            index = i
    return index

def createDecisionTree(trainSet, trainLabel):
    if trainLabel.count(trainLabel[0]) == len(trainLabel):
        return trainLabel[0]
    if len(trainSet[0]) == 0:
        return "no" if trainLabel.count("no") > trainLabel.count("yes") else "yes"
    index = chooseBestFeature(trainSet, trainLabel)
    Tree = {index:{}}
    uniqueVal = set([elt[index] for elt in trainSet])
    for value in uniqueVal:
        subSet, label = splitDataMatrix(trainSet, trainLabel, index, value)
        Tree[index][value] = createDecisionTree(subSet, label)
    return Tree

def predictByDTModel(data, model):
    if type(model) == str:
        return model
    key = iter(model.keys()).__next__()
    value = data[key]
    res = model[key].get(value, None)
    if res != None:
        return predictByDTModel(data, res)
    else:
        tmp_lst = [item for item in model[key].keys()]
        return predictByDTModel(data, model[key][np.random.choice(tmp_lst, 1)[0]])

def testDTModel(testData, testLabel, model):
    predictLabel = []
    for row in testData:
        predictLabel.append(predictByDTModel(row, model))
    errorCount = 0
    for val, val1 in zip(predictLabel, testLabel):
        if val != val1:
            errorCount += 1
    ratio = float(errorCount) / len(testLabel)
    print("DT:total error ratio is %.3f, correct ratio is %.3f" % (ratio, 1 - ratio))
    return ratio


import numpy as np
import torch

from neuralprocess.data.base import FunctionGenerator

class FourierSeriesGenerator(FunctionGenerator):
    """
    Generate random Fourier series. We use the amplitude-phase representation,
    meaning the instances are of the form

        y(x) = bias + SUM_k(a_k * cos((k*x - phase_k) / frequency_scale))

    Args:
        batch_size (int): Batch size.
        series_length (int): Number of Fourier components, not counting
            bias term. Can be an iterable to draw randomly from.
        amplitude (float): a_k in definition above.
            Can be an iterable to draw randomly from.
        phase (float): phase_k in definition above.
            Can be an iterable to draw randomly from.
        bias (float): bias in definition above.
            Can be an iterable to draw randomly from.
        frequency_scale (float): Scale for x axis, see definition above.
            Can be an iterable to draw randomly from.

    """

    def __init__(
        self,
        batch_size,
        series_length=[1, 10],
        amplitude=[-1, 1],
        phase=[-1, 1],
        bias=[-1, 1],
        frequency_scale=2.0,
        **kwargs
    ):

        super().__init__(batch_size, **kwargs)

        self.series_length = series_length
        self.amplitude = amplitude
        self.phase = phase
        self.bias = bias
        self.frequency_scale = frequency_scale

    def apply(self, x):
        """
        Generate y values for input.

        Args:
            x (np.ndarray): x values, shape (1, N, 1)

        Returns:
            np.ndarray: y values, shape (B, N, 1)

        """

        if hasattr(self.series_length, "__iter__"):
            series_length = np.random.randint(*self.series_length)
        else:
            series_length = self.series_length

        if hasattr(self.bias, "__iter__"):
            y = np.random.uniform(*self.bias, size=(self.batch_size, 1, 1))
        else:
            y = np.ones((self.batch_size, 1, 1)) * self.bias

        if hasattr(self.frequency_scale, "__iter__"):
            frequency_scale = np.random.uniform(
                *self.frequency_scale, size=(self.batch_size, 1, 1)
            )
        else:
            frequency_scale = np.ones((self.batch_size, 1, 1)) * self.frequency_scale

        for i in range(series_length):

            if hasattr(self.amplitude, "__iter__"):
                amplitude = np.random.uniform(
                    *self.amplitude, size=(self.batch_size, 1, 1)
                )
            else:
                amplitude = np.ones((self.batch_size, 1, 1)) * self.amplitude

            if hasattr(self.phase, "__iter__"):
                phase = np.random.uniform(*self.phase, size=(self.batch_size, 1, 1))
            else:
                phase = np.ones((self.batch_size, 1, 1)) * self.phase

            y = y + amplitude * np.cos(((i + 1) * x - phase) / frequency_scale)

        return y.astype(np.float32)


import numpy as np
class QuantityScheduler():
    def __init__(self, init_epoch, final_epoch, init_value, final_value, num_steps_per_epoch):
        # self.schedule_type = schedule_type
        self.init_step = init_epoch * num_steps_per_epoch
        self.final_step = final_epoch * num_steps_per_epoch
        self.init_value = init_value
        self.final_value = final_value
        self.num_steps_per_epoch = num_steps_per_epoch
        assert self.final_step >= self.init_step

    def get_quantity(self, global_step):
        return self.linear_schedule(global_step, self.init_step, self.final_step, self.init_value, self.final_value)

    def linear_schedule(self, step, init_step, final_step, init_value, final_value):
        """Linear schedule."""
        assert final_step >= init_step
        if init_step == final_step:
            return final_value
        rate = float(step - init_step) / float(final_step - init_step)
        linear_value = rate * (final_value - init_value) + init_value
        return np.clip(linear_value, min(init_value, final_value), max(init_value, final_value))

if __name__ == '__main__':
    import pdb
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt

    init_epoch = 20
    final_epoch = 60
    total_epoch = 100
    init_value = 1e-4
    final_value = 1e-1
    num_steps_per_epoch = 128
    schedule = QuantityScheduler(init_epoch, final_epoch, init_value, final_value, num_steps_per_epoch)

    steps = np.arange(total_epoch * num_steps_per_epoch)
    values = []
    for s in steps:
        values.append(schedule.get_quantity(s))
    values = np.array(values)

    plt.plot(steps, values)
    save_file = 'schedule.png'
    plt.savefig(save_file)
    plt.close()
    # pdb.set_trace()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Program: diffusionEq1D_BackwardEuler
Created: Aug 2020
@author: Ryan Clement (RRCC)
         scisoft@outlook.com

Purpose: Solve the

            u_t = alpha * u_xx

on the interval (0,L) with boundary conditions

            u = 10 for x = 0
            and
            u = 0 for x = 1.0

and initial condition

            u(x,0) = 0.0
"""

### IMPORTS
import numpy as np
import scipy.sparse as sp
import scipy.sparse.linalg as la
import matplotlib.pyplot as plt

def analSol(a,x,t):
    """

    Parameters
    ----------
    a : TYPE
        Alpha.
    x : TYPE
        Spatial mesh.
    t : TYPE
        Time.

    Returns
    -------
    TYPE
        Analytic solution array.

    """
    return np.exp(-a * np.pi**2 * t) * np.sin(np.pi*x)

def initCond(x):
    """

    Parameters
    ----------
    x : TYPE
        Spatial mesh.

    Returns
    -------
    TYPE
        Initial condition array.

    """
    return np.sin(np.pi*x)

if __name__ == '__main__':
    ### VARIABLES
    a    = 10.0                           # alpha
    xMin = 0
    xMax = 1
    xP   = 51                             # Number of mesh points
    xN   = xP - 1                         # Number of zones
    x    = np.linspace(xMin,xMax,xP)      # Spatial mesh vector
    dx   = x[1] - x[0]
    dxs  = dx**2
    tsc  = 1.0/4.0                        # Time step control parameter
                                          # Note: a*dt/dx**2 = tsc (dimensionless)
    dt   = dxs*tsc/a
    dpa  = -tsc                           # Dimensionless parameter a
    dpb  = 1.0 - 2.0*dpa                  # Dimensionless parameter b
    uBC0 = 0.0                            # Left boundary condition
    uBCN = 0.0                            # Right boundary condition
    u    = np.zeros(xP)                   # Solution vector
    b    = np.zeros(xP)                   # Old solution vector
    # Set initial condition
    b[1:-1] = initCond(x[1:-1])
    # Tridiagonal matrix vectors
    diag = np.zeros(xP)
    lowr = np.zeros(xN)
    uppr = np.zeros(xN)
    # Load matrix vectors
    diag[1:-1] = dpb
    diag[0]    = 1.0
    diag[-1]   = 1.0
    lowr[:-1]  = dpa
    lowr[-1]   = 0.0
    uppr[1:]   = dpa
    uppr[0]    = 0.0
    # Create coefficient matrix
    A = sp.diags(
        diagonals = [diag,lowr,uppr],
        offsets = [0,-1,1],
        shape = (xP,xP),
        format = 'csr')
    # print(A.todense())
    print('dt = {:0.5f}'.format(dt))
    numSteps = 1000
    time = dt*numSteps
    aSol = analSol(a,x,time)
    for i in range(numSteps):
        u[:] = la.spsolve(A,b)
        b, u = u, b
    plt.plot(x,b,'bo',label='Computed')
    plt.plot(x,aSol,label='Analytic',color='red')
    plt.legend()
    plt.title(r'$u_t=\alpha\cdot u_{xx}$')
    plt.text(0,.35,'Time = {:0.5f}s'.format(time))
    plt.show()


"""A module for generating random quantum states and processes. Pseudocode for many of these
routines can be found in the appendix of the paper by Granade, Combes, and Cory (2016).
https://arxiv.org/pdf/1509.03770.pdf"""
from typing import Optional

import numpy as np
from numpy import linalg as la
from scipy.linalg import sqrtm
from sympy.combinatorics import Permutation
from numpy.random import RandomState

def ginibre_matrix_complex(D, K, rs: Optional[RandomState] = None):
    """
    Given a scalars $D$ and $K$, returns a D × K matrix,
    drawn from the complex Ginibre ensemble, i.e. (N(0, 1) + i · N(0, 1)).

    See: Induced measures in the space of mixed quantum states,
         Zyczkowski and Sommers,
         J. Phys A: Math. and Gen. 34, 7111 (2001).
         https://doi.org/10.1088/0305-4470/34/35/335

    :param D: Hilbert space dimension (scalar).
    :param K: Ultimately becomes the rank of a state (scalar).
    :param rs: Optional random state.
    :return: Returns a D × K matrix, drawn from the Ginibre ensemble.
    """
    if rs is None:
        rs = np.random

    return rs.randn(D, K) + 1j * rs.randn(D, K)

def haar_rand_unitary(dim, rs=None):
    """
    Given a Hilbert space dimension D this function
    returns a unitary operator U ∈ C^D×D drawn from the Haar measure.

    The error is of order 10^-16.

    See: How to generate random matrices from the classical compact groups,
         Mezzadri,
         Notices of the American Mathematical Society 54, 592 (2007).
         http://www.ams.org/notices/200705/fea-mezzadri-web.pdf

    :param dim: Hilbert space dimension (scalar).
    :param rs: Optional random state
    :return: Returns a unitary operator U ∈ C^D×D drawn from the Haar measure.
    """
    if rs is None:
        rs = np.random
    Z = ginibre_matrix_complex(D=dim, K=dim, rs=rs)  # /np.sqrt(2)
    Q, R = np.linalg.qr(Z)
    diag = np.diagonal(R)
    lamb = np.diag(diag) / np.absolute(diag)
    return np.matmul(Q, lamb)

def haar_rand_state(dimension):
    """
    Given a Hilbert space dimension $D$ this function returns a vector
    representing a random pure state operator drawn from the Haar measure.

    :param dimension: Hilbert space dimension (scalar).
    :return: Returns a Dx1 vector drawn from the Haar measure.

    """
    unitary = haar_rand_unitary(dimension)
    fiducial_vec = np.zeros((dimension, 1))
    fiducial_vec[0] = 1
    return np.matmul(unitary, fiducial_vec)

def ginibre_state_matrix(D, K):
    """
    Given a Hilbert space dimension $D$ and a desired rank $K$, returns
    a D × D positive semidefinite matrix of rank K drawn from the Ginibre ensemble.
    For D = K these are staes drawn from the Hilbert-Schmidt measure.

    :param D: Hilbert space dimension (scalar).
    :param K: The rank of a state (scalar).
    :return: Returns a D × K matrix, drawn from the Ginibre ensemble.

    See: Induced measures in the space of mixed quantum states,
         Zyczkowski and Sommers,
         J. Phys A: Math. and Gen. 34, 7111 (2001).
         https://doi.org/10.1088/0305-4470/34/35/335

    """
    if K > D:
        raise ValueError("The rank of the state matrix cannot exceed the dimension.")
        return

    A = ginibre_matrix_complex(D, K)
    M = A.dot(np.transpose(np.conjugate(A)))
    return M / np.trace(M)

def bures_measure_state_matrix(D):
    """
    Given a Hilbert space dimension $D$, returns a D × D positive
    semidefinite matrix drawn from the Bures measure.

    :param D: Hilbert space dimension (scalar).
    :param K: The rank of a state (scalar).
    :return: Returns a D × K matrix, drawn from the Ginibre ensemble.

    See: Random Bures mixed states and the distribution of their purity,
         Osipov, Sommers, and Zyczkowski,
         J. Phys. A: Math. Theor. 43, 055302 (2010).
         https://doi.org/10.1088/1751-8113/43/5/055302

    """
    A = ginibre_matrix_complex(D, D)
    U = haar_rand_unitary(D)
    Udag = np.transpose(np.conjugate(U))
    Id = np.eye(D)
    M = A.dot(np.transpose(np.conjugate(A)))
    P = (Id + U).dot(M).dot(Id + Udag)
    return P / np.trace(P)

def rand_map_with_BCSZ_dist(D, K):
    """
    Given a Hilbert space dimension $D$ and a Kraus rank $K$, returns a
    $D^2 × D^2$ Choi matrix $J(Λ)$ of a channel drawn from the BCSZ distribution
    with Kraus rank $K$.

    :param D: Hilbert space dimension (scalar).
    :param K: The rank of a state (scalar).
    :return: D^2 × D^2 Choi matrix, drawn from the BCSZ distribution with Kraus rank K.

    See: Random quantum operations,
         Bruzda, Cappellini, Sommers, and Zyczkowski,
         Physics Letters A 373, 320 (2009).
         https://doi.org/10.1016/j.physleta.2008.11.043

    """
    # TODO: this ^^ is CPTP, might want a flag that allows for just CP quantum operations.

    X = ginibre_matrix_complex(D ** 2, K)
    rho = X.dot(np.transpose(np.conjugate(X)))
    # Now compute the partial trace using method from:
    # https://www.peijun.me/reduced-density-matrix-and-partial-trace.html
    #
    # [[ TODO: might be better to do: rho_red = np.einsum('jiki->jk', reshaped_dm)
    # ^^ see https://scicomp.stackexchange.com/questions/27496/calculating-partial-trace-of-array-in-numpy/27659 ]]

    rho_tensor = rho.reshape([D, D, D, D])
    rho_red = np.trace(rho_tensor, axis1=0, axis2=2)
    # Now sqrt
    Q = np.kron(np.eye(D), sqrtm(la.inv(rho_red)))
    Z = Q.dot(rho).dot(Q)
    return Z

def permute_tensor_factors(D, perm):
    r"""
    Return a permutation matrix of the given dimension.

    Given a Hilbert space dimension $D$ and an list representing the permutation $perm$ of the
    tensor product Hilbert spaces, returns a $D^len(perm) x D^len(perm)$ permutation matrix.

    E.g. 1) Suppose D=2 and perm=[0,1]
            Returns the identity operator on two qubits

         2) Suppose D=2 and perm=[1,0]
            Returns the SWAP operator on two qubits which
            maps A_1 \otimes A_2 --> A_2 \otimes A_1.

    See: Equations 5.11, 5.12, and 5.13 in https://arxiv.org/pdf/0711.1017.pdf

    This function is used in tests for other functions. However, it can also be useful when
    thinking about higher moment (N>2) integrals over the Haar measure.

    :param D: Hilbert space dimension (scalar).
    :param perm: The rank of a state (list).
    """
    dim_list = [D for i in range(2 * len(perm))]

    Id = np.eye(D ** len(perm), D ** len(perm))

    P = Permutation(perm)
    tran = P.transpositions
    trans = tran()

    temp = np.reshape(Id, dim_list)

    # implement the permutation

    if P == []:
        return Id
    else:
        for pdx in range(len(trans)):
            tdx = trans[pdx]
            answer = np.swapaxes(temp, tdx[0], tdx[1])
            temp = answer

    return np.reshape(answer, [D ** len(perm), D ** len(perm)])

import numpy,from numpy,from scipy,from sympy


"""
Author: Peratham Wiriyathammabhum

"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# import scipy.sparse.linalg as linalg
from numpy import linalg as LA

def fast_ica(Xin, n_components=3):
	"""
	Perform Fast Independent Component Analysis (FastICA) on an input row matrix X.
	X = AS, where A is a mixing matrix and S are signal sources.

	See: IJCNN'99 Independent Component Analysis: A Tutorial by Aapo Hyvrinen and Erkki Oja
		http://www.cs.jhu.edu/~ayuille/courses/Stat161-261-Spring14/HyvO00-icatut.pdf
	"""
	X = Xin.copy() # for safety
	ni, nd = X.shape

	"""
	Note: This X is a row matrix which is different to the original formulas which assume a column matrix.
	"""
	# centering
	X = (X - np.mean(X, axis=0))

	# whitening makes mixing matrix orthogonal
	D, E = LA.eig(np.cov(X.T))
	ind = np.argsort(-D, axis=0) # sorting descending
	D = D[ind]
	E = E[:, ind]
	D = np.power(D, -0.5)
	D = np.diag(D)
	X = E.dot(D).dot(E.T).dot(X.T)
	X = X.T

	# f, g, g' https://en.wikipedia.org/wiki/FastICA
	f = lambda x: np.log(np.cosh(x))
	g = lambda x: np.tanh(x)
	g_prime = lambda x: 1. - np.power(np.tanh(x), 2)

	W = []
	ones_m = np.ones((ni, 1))
	for i in range(n_components):
		print('[Info] Computing component {}...'.format(i))
		w_p = weight_init(nd, init_mode='random')
		w_p = w_p / np.linalg.norm(w_p, ord=2)

		TERMINATE = False
		while not TERMINATE:
			w_old = w_p.copy()
			first_term = (X.T.dot(g(X.dot(w_p))) / ni)
			second_term = ( g_prime(X.dot(w_p)).T.dot(ones_m) * (w_p) / ni)
			w_p = first_term - second_term
			if (len(W) > 0):
				sum_wp = np.zeros((nd, 1))
				for w_j in W:
					sum_wp += w_p.T.dot(w_j) * (w_j)
				w_p = w_p - sum_wp
			w_p = w_p / np.linalg.norm(w_p, ord=2)
			TERMINATE = not check_diff(w_p, w_old)
		W.append(w_p)
	W = np.asarray(W)
	W = W.squeeze()

	S = X.dot(W)
	return W, S

def check_diff(w_p, w_old, threshold=0.999):
	IS_DIFF = True
	cond = w_p.T.dot(w_old)
	print(cond)
	if cond > threshold:
		IS_DIFF = False
	return IS_DIFF

def weight_init(nd, init_mode='random'):
	if init_mode == 'random':
		theta = np.random.randn(nd, 1)
	elif init_mode=='xavier':
		"""
		Glorot, Xavier, and Yoshua Bengio.
		"Understanding the difficulty of training deep feedforward neural networks."
		Proceedings of the thirteenth international conference on artificial
		intelligence and statistics. 2010.
		"""
		pass
	else:
		pass
	return theta

def main():
	# generate data https://scikit-learn.org/stable/auto_examples/decomposition/plot_ica_blind_source_separation.html
	np.random.seed(0)
	n_samples = 2000
	from scipy import signal
	time = np.linspace(0, 8, n_samples)

	s1 = np.sin(2 * time)  # Signal 1 : sinusoidal signal
	s2 = np.sign(np.sin(3 * time))  # Signal 2 : square signal
	s3 = signal.sawtooth(2 * np.pi * time)  # Signal 3: saw tooth signal

	S = np.c_[s1, s2, s3]
	S += 0.2 * np.random.normal(size=S.shape)  # Add noise

	S /= S.std(axis=0)  # Standardize data
	# Mix data
	A = np.array([[1, 1, 1], [0.5, 2, 1.0], [1.5, 1.0, 2.0]])  # Mixing matrix
	X = np.dot(S, A.T)  # Generate observations

	# timer
	from time import time
	t0 = time()
	# signal source separation
	W, S_ = fast_ica(X)
	t1 = time()
	print('[Info] Fast ICA done in {:.2g} sec.'.format(t1 - t0))

	# plot
	plt.figure()

	models = [X, S, S_]
	names = ['Observations (mixed signal)',
			 'True Sources',
			 'ICA recovered signals']
	colors = ['red', 'steelblue', 'orange']

	for ii, (model, name) in enumerate(zip(models, names), 1):
		plt.subplot(3, 1, ii)
		plt.title(name)
		for sig, color in zip(model.T, colors):
			plt.plot(sig, color=color)

	plt.tight_layout()
	plt.show()
	input("Press Enter to continue...")

	return

if __name__ == '__main__':
	main()

import numpy,from numpy,import scipy,from scipy


import numpy as np

rs = np.random.RandomState(1234)
p = 2
n = 200
py1 = 0.6
mean1 = np.r_[1,1.]
mean0 = -mean1

Y = (rs.rand(n) > py1).astype(int)
X = np.zeros((n,p))
X[Y==0] = rs.multivariate_normal(mean0, np.eye(p), size=(Y==0).sum())
X[Y==1] = rs.multivariate_normal(mean1, np.eye(p), size=(Y==1).sum())

import matplotlib.pyplot as plt
plt.plot(X[Y==0,0], X[Y==0,1], 'ob', label='Class 0')
plt.plot(X[Y==1,0], X[Y==1,1], 'sr', label='Class 1')
plt.legend(loc=2, numpoints=1)

plt.show()

def gradient_ascent(w, b, lamb, eta, n_iters):
    # G is P(Y=1|X) under the current parameters.
    G = 1 / (1. + np.exp(-b-np.dot(X,w)))
    # loss is the function we're maximizing
    loss = -lamb * (w**2).sum() + np.log(G[Y==1]).sum() + np.log(1-G[Y==0]).sum()
    print "iteration:", 0, "| loss:", loss, "| accuracy:", ((G>0.5) == Y).mean()

    for iter in range(1, n_iters+1):
        G = 1 / (1. + np.exp(-b-np.dot(X,w)))
        loss = -lamb * 0.5 * (w**2).sum() + np.log(G[Y==1]).sum() + np.log(1-G[Y==0]).sum()
        print "iteration:", iter, "| loss:", loss, "| accuracy:", ((G>0.5) == Y).mean()

        # Now, do gradient ascent.
        ymg    = Y-G
        grad_b = ymg.sum()
        grad_w = -lamb * w + np.dot(ymg,X)

        b += eta*grad_b
        w += eta*grad_w
    return w,b

init_w = rs.randn(X.shape[1])
init_b = rs.rand()*50-25

w,b=gradient_ascent(w=init_w.copy(), b=init_b, lamb=0., eta=0.01, n_iters=10)

print "\n"

print "Parameters found ... w:", w, "b:", b

# Generate some new testing data
Y_ = (rs.rand(n) > py1).astype(int)
X_ = np.zeros((n,p))
X_[Y_==0] = rs.multivariate_normal(mean0, np.eye(p), size=(Y_==0).sum())
X_[Y_==1] = rs.multivariate_normal(mean1, np.eye(p), size=(Y_==1).sum())

G = 1 / (1. + np.exp(-b-np.dot(X_,w)))

print "\n"

print "Accuracy on test data:", ((G>0.5) == Y_).mean()

x,y = np.mgrid[-4:4:61j,-4:4:61j]
xy = np.c_[ x.flatten(), y.flatten() ]

G = 1 / (1. + np.exp(-b-np.dot(xy,w)))

fig = plt.figure()
# This is a color coding of the numerical prediction over the region.
plt.contourf(x, y, G.reshape(x.shape), cmap=plt.cm.bwr, alpha=0.6)

x1 = (-b-4*w[1]) / w[0]
x2 = (-b+4*w[1]) / w[0]
# This draws the decision boundary.
plt.plot( [x1,x2], [4, -4.], '-k', lw=3)

plt.plot(X[Y==0,0], X[Y==0,1], 'ob')
plt.plot(X[Y==1,0], X[Y==1,1], 'sr')

plt.show()


"""
Principal-component analysis class.

"""
import numpy as np
import pickle
import os

##################################################

class PCA:
    def __init__(self):
        self.W = None
        self.b = None

    def analyze(self, data):
        print("PCA: began analysis")
        covar = np.cov(data, rowvar=False)
        eigvals, eigvecs = np.linalg.eigh(covar)
        self.W = eigvecs[:, ::-1]
        self.b = np.mean(data, axis=0)
        print("PCA: finished analysis")
        return eigvals[::-1]

    def compress(self, data, dimensionality):
        assert self.W is not None
        return (data - self.b).dot(self.W[:, :dimensionality])

    def decompress(self, data):
        assert self.W is not None
        data = np.atleast_2d(data)
        if data.shape[1] < self.W.shape[1]:
            data = np.column_stack((data, np.zeros((len(data), self.W.shape[1]-data.shape[1]), dtype=float)))
        return data.dot(self.W.T) + self.b

    def save(self, filename):
        if filename[-4:] != ".pca":
            filename = filename + ".pca"
        with open(filename, 'wb') as file:
            pickle.dump((self.W, self.b), file, pickle.HIGHEST_PROTOCOL)

    def load(self, filename):
        if filename[-4:] != ".pca":
            filename = filename + ".pca"
        with open(filename, 'rb') as file:
            self.W, self.b = pickle.load(file)

##################################################

if __name__ == "__main__":
    print("PCA: began unit-test")

    import seaborn
    seaborn.set()
    from matplotlib import pyplot

    filename = os.path.join(os.path.dirname(__file__), 'datasets', 'IRIS.csv')

    features = np.genfromtxt(fname=filename,
                             dtype=float,
                             comments='#',
                             delimiter=',',
                             skip_header=1,
                             usecols=(0, 1, 2, 3))
    labels = np.genfromtxt(fname=filename,
                           dtype=str,
                           comments='#',
                           delimiter=',',
                           skip_header=1,
                           usecols=(4))

    pca = PCA()
    pca.analyze(features)
    features_compressed = pca.compress(features, 2)

    should_be_features = pca.decompress(pca.compress(features, 4))
    print("PCA: methods compress and decompress are inverses? {0}".format(np.allclose(features, should_be_features)))

    fig = pyplot.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.set_title('KDE of the Iris Data-Set', fontsize=16)

    ax.set_xlim([-4.0, 4.0])
    ax.set_xlabel("PCA Component 0", fontsize=12)
    ax.set_ylim([-1.5, 1.5])
    ax.set_ylabel("PCA Component 1", fontsize=12)

    seaborn.kdeplot(features_compressed[:50, 0], features_compressed[:50, 1], cmap="Greens", shade=True, shade_lowest=False)
    seaborn.kdeplot(features_compressed[50:100, 0], features_compressed[50:100, 1], cmap="Reds", shade=True, shade_lowest=False)
    seaborn.kdeplot(features_compressed[100:, 0], features_compressed[100:, 1], cmap="Blues", shade=True, shade_lowest=False)
    ax.text(1.2, 1.2, "setosa", size=16, color=seaborn.color_palette("Greens")[-2])
    ax.text(-0.5, 0.7, "versicolor", size=16, color=seaborn.color_palette("Reds")[-2])
    ax.text(-3.5, 0.9, "virginica", size=16, color=seaborn.color_palette("Blues")[-2])

    print("PCA: finished unit-test")
    pyplot.show()


import numpy as np
from scipy import stats
from sklearn.metrics import confusion_matrix, f1_score

y1_neg = np.array([0.6, 0.1, 0.2, 0.1])
y1_pos = np.array([0.3, -0.1, -0.3, 0.2, 0.4, -0.2])
y2_neg = np.array(["A", "B", "A", "C"])
y2_pos = np.array(["B", "C", "C", "B", "A", "C"])
y3_neg = np.array([0.2, -0.1, -0.1, 0.8])
y3_pos = np.array([0.1, 0.2, -0.1, 0.5, -0.4, 0.4])
y4_neg = np.array([0.4, -0.4, 0.2, 0.8])
y4_pos = np.array([0.3, -0.2, 0.2, 0.6, -0.7, 0.3])

p_neg = 0.4
p_pos = 0.6

miu_y1_neg = np.mean(y1_neg) #0.24999999999999997
miu_y1_pos = np.mean(y1_pos) #0.049999999999999996

cov_y1_neg = np.cov(y1_neg) #0.05666666666666665
cov_y1_pos = np.cov(y1_pos) #0.08300000000000002

mode_y2_neg = stats.mode(y2_neg)[0][0] #A
mode_y2_pos = stats.mode(y2_pos)[0][0] #C

miu_y34_neg = np.array([np.mean(y3_neg), np.mean(y4_neg)]) #[0.2  0.25]
miu_y34_pos = np.array([np.mean(y3_pos), np.mean(y4_pos)]) #[0.11666667 0.08333333]

cov_y34_neg = np.cov([y3_neg, y4_neg])  #[[0.18 0.18] [0.18 0.25]]
cov_y34_pos = np.cov([y3_pos, y4_pos])  #[[0.10966667 0.12233333] [0.12233333 0.21366667]]

inv_cov_y34_neg = np.linalg.inv(np.cov([y3_neg, y4_neg])) #[[ 19.84126984 -14.28571429] [-14.28571429  14.28571429]]
inv_cov_y34_pos = np.linalg.inv(np.cov([y3_pos, y4_pos])) #[[ 25.23622047 -14.4488189 ] [-14.4488189   12.95275591]]

det_cov_y34_neg = np.linalg.det(np.cov([y3_neg, y4_neg])) #0.0126
det_cov_y34_pos = np.linalg.det(np.cov([y3_pos, y4_pos])) #0.008466666666666664

# P(c|y1,y2,y3,y4) = P(c)P(y1,y2,y3,y4|c)/P(y1,y2,y3,y4) = P(c)P(y1|c)P(y2|c)P(y3,y4|c)/P(y1,y2,y3,y4)

# P(y1,y2,y3,y4) = P(y1,y2,y3,y4 e c=0) + P(y1,y2,y3,y4 e c=1)
# P(y1,y2,y3,y4 e c) = P(c)P(y1,y2,y3,y4|c) = P(c)P(y1|c)P(y2|c)P(y3,y4|c)
def pred_c(
    x: np.array,
    p_pos, p_neg,
    y2_pos, y2_neg,
    miu_uni_pos, miu_uni_neg,
    cov_uni_pos, cov_uni_neg,
    miu_bi_pos, miu_bi_neg,
    cov_bi_pos, cov_bi_neg
    ):
    # P(c)P(y1|c)P(y2|c)P(y3,y4|c)
    prob_num_pos = prob(x, p_pos, y2_pos, miu_uni_pos, cov_uni_pos, miu_bi_pos, cov_bi_pos)
    prob_num_neg = prob(x, p_neg, y2_neg, miu_uni_neg, cov_uni_neg, miu_bi_neg, cov_bi_neg)
    #print(f"x|c=0: {prob_num_neg}")
    #print(f"x|c=1: {prob_num_pos}")

    # P(y1,y2,y3,y4) = P(y1,y2,y3,y4 e c=0) + P(y1,y2,y3,y4 e c=1)
    prob_den = prob_num_pos + prob_num_neg

    result = 1 if prob_num_pos > prob_num_neg else 0
    return prob_num_pos/prob_den, result

# P(c)P(y1|c)P(y2|c)P(y3,y4|c)
def prob(x: np.array, p_c, y2_c, miu_uni, cov_uni, miu_bi, cov_bi):

    p_y1 = prob_y1(x[0],miu_uni,cov_uni)
    p_y2 = prob_y2(x[1],y2_c)
    p_y3y4 = prob_y3y4(x,2,miu_bi,cov_bi)

    return p_c*p_y1*p_y2*p_y3y4

def prob_y1(x, miu, cov):
    return univariate_normal(x,miu,cov)

def prob_y2(x,y):
    """probability of x in y set"""
    total = 0
    for i in range(len(y)):
        if x == y[i]:
            total += 1
    return total/len(y)

def prob_y3y4(x,d,miu,cov):
    return multivariate_normal(np.array([x[2],x[3]]), d, miu, cov)

def univariate_normal(x, mean, variance):
    """pdf of the univariate normal distribution."""
    return ((1. / np.sqrt(2 * np.pi * variance)) *
            np.exp(-(x - mean)**2 / (2 * variance)))

def multivariate_normal(x: np.array, d, mean: np.array, covariance):
    """pdf of the multivariate normal distribution."""
    x_m = x - mean
    return (1. / (np.sqrt((2 * np.pi)**d * np.linalg.det(covariance))) *
            np.exp(-(np.linalg.solve(covariance, x_m).T.dot(x_m)) / 2))

def print_confusion_matrix(true,pred):
    tn, fp, fn, tp = confusion_matrix(true,pred).ravel()
    print("TN: " + str(tn))
    print("FN: " + str(fn))
    print("TP: " + str(tp))
    print("FP: " + str(fp))
    return tn, fp, fn, tp

def find_accuracies(probs_pos, x_c):

    def predict(prob_pos, threshold):
        return 1 if prob_pos > threshold else 0 #Should we use > or >=? Two different optimal thresholds will be returned that way

    size = len(probs_pos)

    # accuracy for each threshold
    accuracies = []
    best_accuracy = 0

    for i in range(size):
        x_pred = []
        for j in range(size):
            x_pred.append(predict(probs_pos[j], probs_pos[i])) #Find prediction of probs_pos[j] using probs_pos[i] as threshold
        tn, fp, fn, tp = confusion_matrix(x_c,x_pred).ravel()
        accuracy = (tn+tp)/size
        accuracies.append(accuracy)
        if accuracy > best_accuracy:
            best_accuracy = accuracy
            best_threshold = probs_pos[i]
    print(f"Accuracies: {accuracies}")
    print(f"Best threshold: {best_threshold}")

if __name__ == "__main__":

    y1 = [0.6, 0.1, 0.2, 0.1,0.3, -0.1, -0.3, 0.2, 0.4, -0.2]
    y2 = ["A", "B", "A", "C","B", "C", "C", "B", "A", "C"]
    y3 = [0.2, -0.1, -0.1, 0.8,0.1, 0.2, -0.1, 0.5, -0.4, 0.4]
    y4 = [0.4, -0.4, 0.2, 0.8,0.3, -0.2, 0.2, 0.6, -0.7, 0.3]

    x_c = [0,0,0,0,1,1,1,1,1,1]
    x_pred = []

    # probability of P(c=1|x)
    probs_pos = []
    probs_neg = []

    for i in range(10):
        x = [y1[i],y2[i],y3[i],y4[i]]
        prob_pos, result = pred_c(
                    x,
                    p_pos,p_neg,
                    y2_pos, y2_neg,
                    miu_y1_pos, miu_y1_neg,
                    cov_y1_pos, cov_y1_neg,
                    miu_y34_pos, miu_y34_neg,
                    cov_y34_pos, cov_y34_neg
                )
        x_pred.append(result)
        probs_pos.append(prob_pos)

    tn, fp, fn, tp = print_confusion_matrix(x_c,x_pred)
    print("F1 Score: " + str(f1_score(x_c, x_pred)))
    print("P(c=1|x) for each x")
    print(probs_pos)
    find_accuracies(probs_pos, x_c)


# Rotating Calipers Algorithms (A.K.A Shamos Algorithm) use to find the diameter of contour
# This algorithm appeared in book (p178-p181) "Shamos, Franco P. Preparata, Michael Ian (1985). Computational Geometry An Introduction"
import cv2
import numpy as np
import math

def area(cnt, i, j, k):
    return cv2.contourArea(np.array([cnt[i], cnt[j], cnt[k]]))

def next_point(cnt, i):
    return (i + 1) % len(cnt)

def distance(p1, p2):
    return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)

def get_all_antipodal_pairs(cnt):
    p0 = 0
    p = len(cnt) - 1
    q = next_point(cnt, p)
    while area(cnt, p, next_point(cnt, p), next_point(cnt, q)) > area(cnt, p, next_point(cnt, p), q):
        q = next_point(cnt, q)

    q0 = q

    while q != p0:
        p = next_point(cnt, p)
        yield p, q

        while area(cnt, p, next_point(cnt, p), next_point(cnt, q)) > area(cnt, p, next_point(cnt, p), q):
            q = next_point(cnt, q)
            if (p, q) != (q0, p0):
                yield p, q
            else:
                break

        if area(cnt, p, next_point(cnt, p), next_point(cnt, q)) == area(cnt, p, next_point(cnt, p), q):
            if (p, q) != (q0, len(cnt) - 1):
                yield p, next_point(cnt, q)
            else:
                break

def get_diameter(cnt):
    diameter = -1.0
    start = None
    end = None

    for i, j in get_all_antipodal_pairs(cnt):
        dist = distance(cnt[i][0], cnt[j][0])
        if dist > diameter:
            diameter = dist
            start = cnt[i][0]
            end = cnt[j][0]

    return diameter, start, end

if __name__ == '__main__':
    cnt = np.array(
        [
            [[10, 6]],
            [[9, 7]],
            [[8, 8]],
            [[7, 9]],
            [[7, 10]],
            [[7, 11]],
            [[7, 12]],
            [[7, 13]],
            [[8, 14]],
            [[8, 15]],
            [[8, 16]],
            [[9, 17]],
            [[10, 17]],
            [[11, 17]],
            [[11, 16]],
            [[12, 15]],
            [[13, 15]],
            [[14, 14]],
            [[15, 14]],
            [[15, 13]],
            [[16, 12]],
            [[16, 11]],
            [[15, 11]],
            [[14, 10]],
            [[14, 9]],
            [[13, 8]],
            [[13, 7]],
            [[12, 6]],
            [[11, 6]],
        ]
    )

    hull = cv2.convexHull(cnt, clockwise=False)

    diameter, start, end = get_diameter(hull)

    print(f"diameter: {diameter} start: {start} end: {end}")


from random import randrange
from random import seed
from sys import exit
from numpy import eye
from numpy import mat
from laff.util.RandomInt import RandomInt

def RandomMatrixForGaussJordan( size, UseSeed=[] ):

    if UseSeed != []:
        seed( UseSeed )
    else:
        seed()

    A = mat( eye( size ) )

    # Scale the rows
    for i in range( 0, size ):
        A[ i,i ] = RandomInt( -2, 2, 'False' )

    # Generate and apply upper triangular Gauss transforms
    for j in range( 1, size ):
        for i in range( 0,j ):
            # multiplier i,j
            mij = RandomInt( -2, 2, 'False' )
            A[ i,: ] += -mij * A[ j,: ]

            # Generate and apply lower triangular Gauss transforms
    for jj in range( 1, size ):
        j = size-jj-1
        for i in range( j+1, size ):
            # multiplier i,j
            mij = RandomInt( -2, 2, 'False' )
            A[ i,: ] += -mij * A[ j,: ]

    return A


import numpy as np

def circular_motion(t, radius=1, z=0):
    radian = t/5
    return np.array([radius*np.cos(radian), radius*np.sin(radian), z])


# -*- coding:utf-8 -*-
__version__ = '0.0.1'
__author__ = 'Dr. Yingfeng, Yu'
from scipy import stats
from math import log, sqrt, exp

class call_option:
    def __init__(self, S0, K, T, t, r, sigma):
        self.S0 = float(S0)
        self.K  = K
        self.T  = T
        self.t  = t
        self.r  = r
        self.sigma = sigma
        self.d1 = (log(S0/K)+(r+0.5*sigma**2)*(T-t))/(sigma*sqrt((T-t)))
        self.d2 = (log(S0/K)+(r-0.5*sigma**2)*(T-t))/(sigma*sqrt((T-t)))

    def __del__(self):
        class_name =self.__class__.__name__
        print(class_name, ':delete our call_option obj')

    def val(self):
        val=(self.S0*stats.norm.cdf(self.d1, 0.0, 1.0)-self.K*exp(-self.r*(self.T-self.t))*stats.norm.cdf(self.d2, 0.0, 1.0))
        return val

    def delta(self):
        delta=stats.norm.cdf(self.d1, 0.0, 1.0)
        return delta

    def gamma(self):
        gamma=stats.norm.pdf(self.d1, 0.0, 1.0)/(self.S0*self.sigma*sqrt(self.T-self.t))
        return gamma

    def theta(self,gamma):
        theta = -self.r*self.K*(sqrt(self.T-self.t))*stats.norm.cdf(self.d2)-0.5*gamma*(self.S0*self.sigma)*(self.S0*self.sigma)
        return theta

    def kappa(self):
        kappa= (stats.norm.cdf(-self.d2)-1.00)*exp(-self.r*(self.T-self.r))
        return kappa

    def rho(self):
        rho=self.K*(self.T-self.t)*stats.norm.cdf(self.d2)*exp(-self.r*(self.T-self.t))
        return rho

    def vega(self):
        vega = self.K * stats.norm.cdf(self.d1, 0.0, 1.0) * sqrt(self.T - self.t)
        return vega

    def impvol(self, C0, sigma_est= 0.2, N= 1000):
        opts = call_option(self.S0, self.K, self.T, self.t, self.r, sigma_est)
        for i in range(N):
            opts.sigma =opts.sigma-(opts.val()-C0)/opts.vega()
        return opts.sigma

    def prt_option_info(self):
        call_price=call_option(self.S0, self.K, self.T, self.t, self.r, self.sigma)
        value=call_price.val()
        veg=call_price.vega()
        delt=call_price.delta()
        gam=call_price.gamma()
        thet=call_price.theta(gam)
        kapp=call_price.kappa()
        rh=call_price.rho()
        iv=call_price.impvol(C0=value)
        print('When we assume \nS0=%f K=%f r=%f sigma=%f' %(self.S0, self.K, self.r, self.sigma))
        print('The length T\t=%f and the start time t\t=%f' %(self.T,self.t))
        print('The price of call option \t=\t %f' %value)
        print ('==============The Greek info================')
        print('The Delta of call option \t=\t %f' % delt)
        print('The Gamma of call option \t=\t %f' % gam)
        print('The Theta of call option \t=\t %f' % thet)
        print('The Kappa of call option \t=\t %f' % kapp)
        print('The Rho of call option \t\t=\t %f' % rh)
        print('The Vega of call option \t=\t %f' %veg)
        print('The implied volatility \t\t=\t %f' %iv)
        #return


from __future__ import print_function

import numpy as np
import matplotlib.pyplot as plt

# For reproducibility
np.random.seed(1000)

nb_samples = 1000

def zero_center(X):
    return X - np.mean(X, axis=0)

def whiten(X, correct=True):
    Xc = zero_center(X)
    _, L, V = np.linalg.svd(Xc)
    W = np.dot(V.T, np.diag(1.0 / L))
    return np.dot(Xc, W) * np.sqrt(X.shape[0]) if correct else 1.0

if __name__ == '__main__':
    # Create the dataset
    X = np.random.normal(0.0, [2.5, 1.0], size=(nb_samples, 2))

    theta = np.pi / 4.0
    R = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])

    Xr = np.dot(X, R)

    # Create a whitened version
    Xw = whiten(Xr)

    # Print the whitened covariance matrix
    print(np.cov(Xw.T))

    # Show original and whitened datasets
    fig, ax = plt.subplots(1, 2, figsize=(15, 5))

    ax[0].scatter(Xr[:, 0], Xr[:, 1])
    ax[0].set_xticks(np.arange(-10, 10), 2)
    ax[0].set_yticks(np.arange(-8, 8), 2)
    ax[0].set_xlabel(r'$x_1$')
    ax[0].set_ylabel(r'$x_2$')
    ax[0].set_title(r'Original dataset')
    ax[0].grid()

    ax[1].scatter(Xw[:, 0], Xw[:, 1])
    ax[1].set_xticks(np.arange(-10, 10), 2)
    ax[1].set_yticks(np.arange(-8, 8), 2)
    ax[1].set_xlabel(r'$x_1$')
    ax[1].set_ylabel(r'$x_2$')
    ax[1].set_title(r'Whitened dataset')
    ax[1].grid()

    plt.show()


import numpy as np

x = np.array([[1,2],[3,4]], dtype=np.float64)
y = np.array([[5,6],[7,8]], dtype=np.float64)

# Elementwise sum; both produce the array
# [[ 6.0  8.0]
#  [10.0 12.0]]
print(x + y)
print(np.add(x, y))

# Elementwise difference; both produce the array
# [[-4.0 -4.0]
#  [-4.0 -4.0]]
print(x - y)
print(np.subtract(x, y))

# Elementwise product; both produce the array
# [[ 5.0 12.0]
#  [21.0 32.0]]
print(x * y)
print(np.multiply(x, y))

# Elementwise division; both produce the array
# [[ 0.2         0.33333333]
#  [ 0.42857143  0.5       ]]
print(x / y)
print(np.divide(x, y))

# Elementwise square root; produces the array
# [[ 1.          1.41421356]
#  [ 1.73205081  2.        ]]
print(np.sqrt(x))

v = np.array([9, 10])
w = np.array([11, 12])

print(v.dot(w))
print(np.dot(v, w))

print(x.dot(y))
print(np.dot(x, y))

x = np.array([[1,2], [3,4]])
print(x)    # Prints "[[1 2]
            #          [3 4]]"
print(x.T)  # Prints "[[1 3]
            #          [2 4]]"

# Note that taking the transpose of a rank 1 array does nothing:
v = np.array([1,2,3])
print(v)    # Prints "[1 2 3]"
print(v.T)  # Prints "[1 2 3]"


"""
Simple Metropolis Hastings sampler taken from Hogg and Foreman-MacKey(2018):

  From Problem 2:
  Sample in a single parameter x and give the sampler
  as its density function p(x) a Gaussian density with
  mean 2 and variance 2. Make the proposal distribution
  q (x'∣x ) a Gaussian pdf for x′ with mean x and
  variance 1. Initialize the sampler with x = 0 and run
  the sampler for more than 10000 steps. Plot the results
  as a histogram with the true density overplotted sensibly.

  Redo Problem 2, but now with an input density that is a
  function of two variables (x, y). For the density function use
  two different functions. (a) The first density function is a
  covariant two-dimensional Gaussian density with variance
  tensor: V = [[2.0, 1.2], [1.2, 2.0]] ... For the proposal
  distribution q (x',y'∣x, y) use a two-dimensional Gaussian
  density with mean at [x, y] and variance tensor set to the
  two-dimensional identity matrix.

"""

import probayes as pb
import numpy as np
import scipy.stats
from pylab import *; ion()

n_steps = 12288
prop_stdv = np.sqrt(1)

def q(**kwds):
  x, xprime = kwds['x'], kwds["x'"]
  y, yprime = kwds['y'], kwds["y'"]
  return scipy.stats.norm.pdf(yprime, loc=y, scale=prop_stdv) * \
         scipy.stats.norm.pdf(xprime, loc=x, scale=prop_stdv)

x = pb.RV('x', vtype=float, vset=(-np.inf, np.inf))
y = pb.RV('y', vtype=float, vset=(-np.inf, np.inf))
process = pb.SP(x & y)
process.set_prob(scipy.stats.multivariate_normal, [0., 0.],
                 [[2.0, 1.2], [1.2, 2.0]])
process.set_tran(q)
lambda_delta = lambda : process.Delta(x=scipy.stats.norm.rvs(loc=0., scale=prop_stdv),
                                      y=scipy.stats.norm.rvs(loc=0., scale=prop_stdv))
process.set_delta(lambda_delta)
process.set_scores('hastings')
process.set_update('metropolis')
sampler = process.sampler({'x': 0., 'y': 1.}, stop=n_steps)
samples = [sample for sample in sampler]
summary = process(samples)
n_accept = summary.u.count(True)
inference = summary.v.rescaled()
xvals, yvals, post = inference['x'], inference['y'], inference.prob

# PLOT DATA
figure()
subplot(2, 2, 1)
c_norm = Normalize(vmin=np.min(post), vmax=np.max(post))
c_map = cm.jet(c_norm(post))
plot(xvals, yvals, '-', color=(0.7, 0.7, 0.7, 0.3))
scatter(xvals, yvals, color=c_map, marker='.', alpha=1.)
xlabel(r'$x$')
ylabel(r'$y$')

ax = subplot(2, 2, 2)
ybins = np.linspace(np.min(yvals), np.max(yvals), 128)
yhist, _ = np.histogram(yvals, ybins)
yprop = yhist / (n_steps * (ybins[1]-ybins[0]))
step(ybins[:-1], yprop, 'b')
ax.yaxis.set_label_position('right')
ax.yaxis.tick_right()
xlabel(r'$y$')
ylabel('Relative frequency')

subplot(2, 2, 3)
xbins = np.linspace(np.min(xvals), np.max(xvals), 128)
xhist, _ = np.histogram(xvals, xbins)
xprop = xhist / (n_steps * (xbins[1]-xbins[0]))
step(xbins[:-1], xprop, 'b')
xlabel(r'$x$')
ylabel('Relative frequency')


import numpy as np

# All of these expect two identically sized numpy arrays as inputs
# and return the same size error output.

class Abs(object):
    @staticmethod
    def __str__():
        return "mean absolute error"

    @staticmethod
    def calc(x):
        return np.mean(np.abs(x))

    @staticmethod
    def calc_d(x):
        return np.sign(x)

class Sqr(object):
    @staticmethod
    def __str__():
        return "mean squared error"

    @staticmethod
    def calc(x):
        return np.mean(x**2)

    @staticmethod
    def calc_d(x):
        return 2 * x


## Tanh activation function

import numpy as np

class Tanh:
    def __init__(self):
        """
        ## This function is used to calculate the sigmoid and the derivative
            of a tanh
        """

    def forward(self, x):
        return np.tanh(x)

    def backward(self, x):
        return 1.0 - np.square(x)


# Sampling from an unnormalized Gamma density via MALA

# %% Import packages

import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
import seaborn as sns
import torch

from torch.utils.data import DataLoader

from eeyore.datasets import EmptyXYDataset
from eeyore.models import DistributionModel
from eeyore.samplers import MALA

# %% Set up unnormalized target density

v = torch.tensor([2., 1.], dtype=torch.float64)

def log_pdf(theta, x, y):
    return (v[0] - 1) * theta - torch.exp(theta) / v[1] + theta # Jacobian

model = DistributionModel(log_pdf, 1, dtype=torch.float64)

# %% Setup MALA sampler

sampler = MALA(
    model,
    theta0=torch.tensor([-1], dtype=model.dtype),
    dataloader=DataLoader(EmptyXYDataset()),
    step=0.25
)

# %% Run MALA sampler

sampler.run(num_epochs=11000, num_burnin_epochs=1000)

# %% For convenience, name the chain list

chain_list = sampler.get_chain()

# %% Compute acceptance rate

print('Acceptance rate: {}'.format(chain_list.acceptance_rate()))

# %% Compute Monte Carlo mean

print('Monte Carlo mean: {}'.format(chain_list.mean()))

# %% Compute Monte Carlo standard error

print('Monte Carlo standard error: {}'.format(chain_list.mc_se()))

# %% Plot traces of simulated Markov chain

chain = chain_list.get_param(0)
plt.figure()
sns.lineplot(x=range(len(chain)), y=chain)
plt.xlabel('Iteration')
plt.ylabel('Parameter value')
plt.title(r'Traceplot of $\theta_{{{}}}$'.format(1))

# %% Plot histograms of marginals of simulated Markov chain

x_hist_range = np.linspace(0.001, 10, 100)

plt.figure()
plot = sns.histplot(chain_list.get_param(0).exp(), stat='density', label='Simulated')
plot.set_xlabel('Parameter value')
plot.set_ylabel('Relative frequency')
plot.set_title(r'Traceplot of $\theta_{{{}}}$'.format(1))
sns.lineplot(x=x_hist_range, y=stats.gamma.pdf(x_hist_range, v[0], scale=v[1]), color='red', label='Target')
plot.legend()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Nov  4 13:30:33 2018

@author: Kazuki
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

N = 64 # データ数
n = np.arange(N)
f1 = 4 # 周期①
f2 = 10 # 周期②
a1 = 1.5 # 振幅①
a2 = 3 # 振幅②
f = a1 * np.sin(f1 * 2 * np.pi * (n/N)) + a2 * np.sin(f2 * 2 * np.pi * (n/N))

# グラフ表示
plt.figure(figsize=(8, 4))
plt.xlabel('n')
plt.ylabel('Signal')
plt.plot(f)

# =============================================================================
#
# =============================================================================
# 高速フーリエ変換(FFT)
F = np.fft.fft(f)
# FFT結果（複素数）を絶対値に変換
F_abs = np.abs(F)
# 振幅を元に信号に揃える
F_abs_amp = F_abs / N * 2 # 交流成分はデータ数で割って2倍する
F_abs_amp[0] = F_abs_amp[0] / 2 # 直流成分（今回は扱わないけど）は2倍不要

# グラフ表示（データ数の半分の周期を表示）
plt.plot(F_abs_amp[:int(N/2)+1])


import numpy as np

def l2_distance(a, b):
    """Computes the Euclidean distance matrix between a and b.

    Inputs:
        A: D x M array.
        B: D x N array.

    Returns:
        E: M x N Euclidean distances between vectors in A and B.

    Author   : Roland Bunschoten
               University of Amsterdam
               Intelligent Autonomous Systems (IAS) group
               Kruislaan 403  1098 SJ Amsterdam
               tel.(+31)20-5257524
               bunschot@wins.uva.nl
    Last Rev : Wed Oct 20 08:58:08 MET DST 1999
    Tested   : PC Matlab v5.2 and Solaris Matlab v5.3

    Copyright notice: You are free to modify, extend and distribute
       this code granted that the author of the original code is
       mentioned as the original author of the code.

    Fixed by JBT (3/18/00) to work for 1-dimensional vectors
    and to warn for imaginary numbers.  Also ensures that
    output is all real, and allows the option of forcing diagonals to
    be zero.

    Basic functionality ported to Python 2.7 by JCS (9/21/2013).
    """

    if a.shape[0] != b.shape[0]:
        raise ValueError("A and B should be of same dimensionality")

    aa = np.sum(a**2, axis=0)
    bb = np.sum(b**2, axis=0)
    ab = np.dot(a.T, b)

    return np.sqrt(aa[:, np.newaxis] + bb[np.newaxis, :] - 2*ab)


"""Add polynomial features to the features set"""

import numpy as np

def add_polynomials(dataset_1, dataset_2, polynomial_degree):
    """Extends data set with polynomial features of certain degree.

    Returns a new feature array with more features, comprising of
    x1, x2, x1^2, x2^2, x1*x2, x1*x2^2, etc.

    :param dataset_1: first data set.
    :param dataset_2: second data set.
    :param polynomial_degree: the max power of new features.
    """

    polynomials = np.empty((dataset_1.shape[0], 0))

    for i in range(1, polynomial_degree + 1):
        for j in range(i + 1):
            polynomial_feature = (dataset_1 ** (i - j)) * (dataset_2 ** j)
            polynomials = np.concatenate((polynomials, polynomial_feature), axis=1)

    return polynomials


from __future__ import division

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

diam = [1,10,100, 300, 500]
diam_32 = np.power(diam, 1.5)

stim = [0.19,5.9,190,990, 2060]

plt.plot(diam_32, stim, 'o', lw = 3)

m, c = np.polyfit(diam_32, stim, 1)
plt.plot(diam_32, m * diam_32 + c, color = 'k', lw = 3)

plt.xlim(-400, 11500)
plt.ylim(-400, 2100)

plt.savefig('8e.jpeg', format = 'jpeg', dpi = 600, bbox_inches = 'tight')
plt.show()

temp = [27.1,27.4,28.3,28.7,29.1]
diam_05 = np.power(diam, 0.5)

plt.plot(diam_05, temp, 'o', lw = 3)
m, c = np.polyfit(diam_05, temp, 1)
plt.plot(diam_05, m * diam_05 + c, color = 'k', lw = 3)

plt.xlim(-0.4, 24)
plt.ylim(27,29.5)

plt.savefig('8f.jpeg', format = 'jpeg', dpi = 600, bbox_inches = 'tight')
plt.show()


import numpy as np
from matplotlib import pyplot as plt

class GamblerProblem:
    def __init__(self, ph, max_capital, threshold):
        self.ph = ph
        self.threshold = threshold
        self.max_capital = max_capital
        self.state_value = np.zeros(max_capital + 1)
        self.optimal_policy = np.zeros(max_capital + 1)
        self.state_value[100] = 1

    def expected_reward(self, curr_state):
        action_state = [0 , min(curr_state, self.max_capital - curr_state)]
        new_state_value = 0
        optimal_next_state = 0
        for i in range(action_state[0], action_state[1]+1):
            dp = self.ph * self.state_value[curr_state + i] + (1 - self.ph) * self.state_value[curr_state - i]
            if(new_state_value < dp):
                new_state_value = dp
                optimal_next_state = i
        return new_state_value, optimal_next_state

    def value_iteration(self):
        iteration_count = 1
        while True:
            delta = 0
            for curr_state in range(1, self.max_capital):
                new_state_value, _ = self.expected_reward(curr_state)
                delta = max(delta, abs(self.state_value[curr_state] - new_state_value))
                self.state_value[curr_state] = new_state_value
            if(iteration_count in [1, 2, 3, 5, 10, 50, 100, 500, 1000]):
                plt.plot(self.state_value, label = 'Sweep '+str(iteration_count))
            print("Value Iteration Count = " + str(iteration_count) + " with delta = " + str(delta))
            iteration_count += 1
            if delta < self.threshold:
                break

    def policy_update(self):
        for curr_state in range(1, self.max_capital):
            _, optimal_next_state = self.expected_reward(curr_state)
            self.optimal_policy[curr_state] = optimal_next_state

    def display_state_value(self):
        plt.plot(self.state_value, label="Final Value Function")
        plt.xlabel('Capital')
        plt.ylabel('Value Estimate')
        plt.title('Value Function')
        plt.legend()
        plt.show()

    def display_optimal_policy(self):
        plt.bar(range(0, self.max_capital+1), self.optimal_policy)
        plt.xlabel('Capital')
        plt.ylabel('Final Policy(Stake')
        plt.title('Final Policy Function')
        plt.show()

# For ph = 0.4, 0.25 the graph isnt very unexpected but something intersting happens at ph=0.55
# Check out yourself
def main(ph = 0.55, max_capital = 100, threshold = 0.000001):
    obj = GamblerProblem(ph, max_capital, threshold)
    obj.value_iteration()
    obj.policy_update()
    obj.display_state_value()
    obj.display_optimal_policy()

if __name__ == '__main__':
    main()


#
# Finding a Minimum of a Function
# A_pyt/j_OPT.py
#
import numpy as np
import scipy.optimize as sco

# Finding a Minimum

def y(x):
    ''' Function to Minimize. '''
    if x < -np.pi or x > 0:
        return 0.0
    return np.sin(x)

gmin = sco.brute(y, ((-np.pi, 0, 0.01), ), finish=None)  # global optimization
lmin = sco.fmin(y, -0.5)  # local optimization

# Result Output
print "Global Minimum is %8.6f" % gmin
print "Local Minimum is  %8.6f" % lmin


import os
from random import randint
import math
import numpy as np
import matplotlib.font_manager as fm
import matplotlib.pyplot as plt

# gs_font = fm.FontProperties(
#                 fname='/System/Library/Fonts/Supplemental/GillSans.ttc')
thisdir = os.path.dirname(os.path.realpath(__file__))
plt.style.use(os.path.join(thisdir, 'style.mplstyle'))

def generate_birthday():
    """Generate an integer in [1, 365] uniform dist"""
    return randint(1, 365)

def count_until_match():
    birthdays = [generate_birthday()]

    while birthdays[-1] not in birthdays[:-1]:
        birthdays.append(generate_birthday())

    return len(birthdays)

def mc(nsamples=1000):
    return [count_until_match() for _ in range(nsamples)]

def prob_no_match(n):
    """analytical result using integers - python can handle arb large numbers"""
    return math.factorial(n)*math.comb(365,n)/(365**n)

def plot_actual(logy=False, show_match=True, show_no_match=True):
    x = range(1, 366)
    y = [prob_no_match(n) for n in x]
    y2 = [1 - prob_no_match(n) for n in x]

    fig, ax1 = plt.subplots()
    plt.title('Birthday problem')

    if show_no_match:
        ax1.plot(x, y, 'r', lw=2, alpha=0.7, label='no match')
    if show_match:
        ax1.plot(x, y2, 'g', lw=2, alpha=0.7, label='match')
    plt.grid(axis='y', alpha=0.75)

    ax1.set_xlabel('People')
    ax1.set_ylabel('Probability')
    xmax = 100
    ax1.set_xlim(1, xmax)
    ax1.set_ylim(0, 1)
    if logy:
        ax1.set_ylim(1e-150, 10)
        ax1.set_yscale('log')
        ax1.set_xlim(1, 365)
    else:
        n50 = 23
        ax1.axhline(0.5, xmin=n50/xmax, color='k', alpha=0.7, ls='--')
        ax1.axvline(n50, ymax=0.5, color='k', alpha=0.7, ls='--')

    plt.legend()

    return fig, [ax1, ax2]

def plot_mc(x, show_true=False, show_approx=False):
    people = days = np.arange(1, 366)
    xmax = 100

    hist, _ = np.histogram(x, bins=people)
    # these are %
    cumulative = np.cumsum(hist)*100./sum(hist)

    fig, ax1 = plt.subplots()

    n, bins, patches = ax1.hist(x=x, bins=people, color='blue',
                                alpha=0.4, rwidth=0.75, ec='black')
    plt.grid(axis='y', alpha=0.75)

    ax1.set_xlabel('Value')
    ax1.set_ylabel('Count')
    plt.title(f'N={len(x)}')
    ax1.set_xlim(1, xmax)

    maxfreq = n.max()
    ax1.set_ylim(ymax=np.ceil(maxfreq / 10) * 10 if maxfreq % 10 else maxfreq + 10)

    ax2 = ax1.twinx()
    miny2, maxy2 = [0.0, 110.]
    ax2.set_ylim(miny2, maxy2)

    ax2.plot(days[:-1], cumulative, c='green', lw=2, alpha=0.8)
    ax2.set_ylabel('% of people until same birthday')

    def mark_prop_line(pct, colour='red'):
        # index of when we cross pct% chance
        ipeople = next((i for i, c in enumerate(cumulative) if c > pct), -1)
        ax2.axhline(pct, xmin=days[ipeople]/xmax, color=colour, alpha=0.7, ls='--')
        ax2.axvline(days[ipeople], ymax=pct/maxy2, color=colour, alpha=0.7, ls='--')
        plt.text(80, pct+1, f'$p={pct}%$, $\mu={days[ipeople]}$')

    mark_prop_line(50, colour='red')
    mark_prop_line(70, colour='orange')
    mark_prop_line(95, colour='yellow')
    mark_prop_line(99, colour='purple')

    if show_true:
        # using approximation of expontential
        approx = (1 - np.exp(-people*people/(2*365)))*100.
        ax2.plot(days, approx, c='pink', lw=1, alpha=0.3)

    if show_approx:
        # using approximation of expontential
        approx = (1 - np.exp(-people*people/(2*365)))*100.
        ax2.plot(days, approx, c='pink', lw=1, alpha=0.3)

    return fig, [ax1, ax2]

N = 1000
values = mc(nsamples=N)

fig, [ax1, ax2] = plot_mc(values)
fig, [ax1, ax2] = plot_actual()
fig, [ax1, ax2] = plot_actual(logy=True, show_match=False)

plt.show()


# pca 1d : visualize variacnce of projecton
# Code is based on chapter 8 of
# https://github.com/ageron/handson-ml2

import numpy as np
import matplotlib.pyplot as plt

from sklearn.decomposition import PCA

angle = np.pi / 5
stretch = 5
m = 200

np.random.seed(3)
X = np.random.randn(m, 2) / 10
X = X.dot(np.array([[stretch, 0],[0, 1]])) # stretch
X = X.dot([[np.cos(angle), np.sin(angle)], [-np.sin(angle), np.cos(angle)]]) # rotate

u1 = np.array([np.cos(angle), np.sin(angle)])
u2 = np.array([np.cos(angle - 2 * np.pi/6), np.sin(angle - 2 * np.pi/6)])
u3 = np.array([np.cos(angle - np.pi/2), np.sin(angle - np.pi/2)])

X_proj1 = X.dot(u1.reshape(-1, 1))
X_proj2 = X.dot(u2.reshape(-1, 1))
X_proj3 = X.dot(u3.reshape(-1, 1))

plt.figure(figsize=(8,4))
plt.subplot2grid((3,2), (0, 0), rowspan=3)
plt.plot([-1.4, 1.4], [-1.4*u1[1]/u1[0], 1.4*u1[1]/u1[0]], "k-", linewidth=1)
plt.plot([-1.4, 1.4], [-1.4*u2[1]/u2[0], 1.4*u2[1]/u2[0]], "k--", linewidth=1)
plt.plot([-1.4, 1.4], [-1.4*u3[1]/u3[0], 1.4*u3[1]/u3[0]], "k:", linewidth=2)
plt.plot(X[:, 0], X[:, 1], "bo", alpha=0.5)
plt.axis([-1.4, 1.4, -1.4, 1.4])
plt.arrow(0, 0, u1[0], u1[1], head_width=0.1, linewidth=5, length_includes_head=True, head_length=0.1, fc='k', ec='k')
plt.arrow(0, 0, u3[0], u3[1], head_width=0.1, linewidth=5, length_includes_head=True, head_length=0.1, fc='k', ec='k')
plt.text(u1[0] + 0.1, u1[1] - 0.05, r"$v_1$", fontsize=22)
plt.text(u2[0] + 0.1, u2[1], r"$v'$", fontsize=22) # KPM
plt.text(u3[0] + 0.1, u3[1], r"$v_2$", fontsize=22)
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$x_2$", fontsize=18, rotation=0)
plt.grid(True)

plt.subplot2grid((3,2), (0, 1))
plt.plot([-2, 2], [0, 0], "k-", linewidth=1)
plt.plot(X_proj1[:, 0], np.zeros(m), "bo", alpha=0.3)
plt.gca().get_yaxis().set_ticks([])
plt.gca().get_xaxis().set_ticklabels([])
plt.axis([-2, 2, -1, 1])
plt.grid(True)

plt.subplot2grid((3,2), (1, 1))
plt.plot([-2, 2], [0, 0], "k--", linewidth=1)
plt.plot(X_proj2[:, 0], np.zeros(m), "bo", alpha=0.3)
plt.gca().get_yaxis().set_ticks([])
plt.gca().get_xaxis().set_ticklabels([])
plt.axis([-2, 2, -1, 1])
plt.grid(True)

plt.subplot2grid((3,2), (2, 1))
plt.plot([-2, 2], [0, 0], "k:", linewidth=2)
plt.plot(X_proj3[:, 0], np.zeros(m), "bo", alpha=0.3)
plt.gca().get_yaxis().set_ticks([])
plt.axis([-2, 2, -1, 1])
plt.xlabel("$z_1$", fontsize=18)
plt.grid(True)

plt.tight_layout()
plt.savefig("../figures/pca_projected_variance.pdf", dpi=300)
plt.show()


# %%
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
from matplotlib.animation import FuncAnimation
# matplotlib parameters to ensure correctness of Chinese characters
plt.rcParams["font.family"] = 'sans-serif'
plt.rcParams['font.sans-serif']=['Arial Unicode MS', 'SimHei'] # Chinese font
plt.rcParams['axes.unicode_minus']=False # correct minus sign

plt.rcParams["font.size"] = 16
plt.rcParams["xtick.labelsize"] = 16
plt.rcParams["ytick.labelsize"] = 16
plt.rcParams["axes.spines.top"] = False
plt.rcParams["axes.spines.right"] = False
#%%
class UpdateFigure:
    def __init__(self,
        ax:plt.Axes, x:np.ndarray, var_name:str, var_range:tuple, color, text_pos:tuple):
        """

        Args:
            ax (plt.Axes): _description_
            x (np.ndarray): _description_
            var_name (str): _description_
            var_range (tuple): _description_
            color (_type_): _description_
            text_pos (tuple): _description_
        """

        self.ax = ax
        self.x = x
        self.mean = var_range[0] if var_name=='mean' else 0.0
        self.std = var_range[0] if var_name=='std' else 1.0
        self.var_name = var_name
        self.var_range = var_range
        self.dx = (var_range[1]-var_range[0])/100.0
        self.c=color
        self.text_pos=text_pos

        y = norm.pdf(self.x, loc=self.mean, scale=self.std)
        self.line = self.ax.plot(self.x, y, color=self.c, zorder=0)

    def update_gauss(self, y):
        self.line[0].set_data(self.x,y)

    def __call__(self, i):
        # This way the plot can continuously run and we just keep
        # watching new realizations of the process
        if i < 101:
            if self.var_name == 'mean':
                y = norm.pdf(self.x,loc=self.mean+i*self.dx, scale=self.std)
            elif self.var_name == 'std':
                y = norm.pdf(self.x,loc=self.mean, scale=self.std+i*self.dx)
            self.update_gauss(y)
        elif i == 101:
            if self.var_name == 'mean':
                text = r'$\mu$ = %2d'%self.var_range[1]
            elif self.var_name == 'std':
                text = r'$\sigma$ = %2d'%self.var_range[1]
            self.ax.text(self.text_pos[0], self.text_pos[1],
                         text, ha='left',
                         fontsize=30, color=self.c,
                         transform=self.ax.transAxes)
        else:
            pass
        return self.line

#! ============================================================
#! evolve std
fig, ax = plt.subplots(1,1, dpi=400)
x = np.linspace(-6, 6, 401)
y = norm.pdf(x, loc=0, scale=1)
ax.plot(x, y, color='b', zorder=1)
ax.text(0.78, 0.88,
        r'$\sigma$ = %2d'%(1.0), ha='left',
        fontsize=30, color='b',
        transform=ax.transAxes)
ax.set_xlim(-6.0, 6.0)
ax.set_xticks([-6,-3,0,3,6])
ax.set_ylim(0, 0.5)
ax.spines['left'].set_position(('data', 0))

# create a figure updater
ud = UpdateFigure(ax, x, 'std', (1,2), 'g', (0.78,0.75))
anim = FuncAnimation(fig, ud, frames=121, blit=True)
anim.save('evolve_norm1.mp4', fps=60, dpi=400, codec='libx264', bitrate=-1, extra_args=['-pix_fmt', 'yuv420p'])

ud = UpdateFigure(ax, x, 'std', (2,3), 'r', (0.78,0.62))
anim = FuncAnimation(fig, ud, frames=121, blit=True)
anim.save('evolve_norm2.mp4', fps=60, dpi=400, codec='libx264', bitrate=-1, extra_args=['-pix_fmt', 'yuv420p'])

#! ============================================================
#! evolve mean
fig, ax = plt.subplots(1,1, dpi=400)
x = np.linspace(-6, 6, 401)
y = norm.pdf(x, loc=0, scale=1)
ax.plot(x, y, color='b', zorder=1)
ax.text(0.78, 0.88,
        r'$\mu$ = %2d'%(0.0), ha='left',
        fontsize=30, color='b',
        transform=ax.transAxes)
ax.set_xlim(-6.0, 6.0)
ax.set_xticks([-6,-3,0,3,6])
ax.set_ylim(0, 0.5)
ax.spines['left'].set_position(('data', 0))

# create a figure updater
ud = UpdateFigure(ax, x, 'mean', (0,2), 'g', (0.78,0.75))
anim = FuncAnimation(fig, ud, frames=121, blit=True)
anim.save('evolve_norm3.mp4', fps=60, dpi=400, codec='libx264', bitrate=-1, extra_args=['-pix_fmt', 'yuv420p'])

ud = UpdateFigure(ax, x, 'mean', (0,-2), 'r', (0.78,0.62))
anim = FuncAnimation(fig, ud, frames=121, blit=True)
anim.save('evolve_norm4.mp4', fps=60, dpi=400, codec='libx264', bitrate=-1, extra_args=['-pix_fmt', 'yuv420p'])
# %%


import math
import numpy as np
from linear_regression import *
from sklearn.datasets import make_regression
# Note: please don't add any new package, you should solve this problem using only the packages above.
#-------------------------------------------------------------------------
'''
    Problem 2: Apply your Linear Regression
    In this problem, use your linear regression method implemented in problem 1 to do the prediction.
    Play with parameters alpha and number of epoch to make sure your test loss is smaller than 1e-2.
    Report your parameter, your train_loss and test_loss
    Note: please don't use any existing package for linear regression problem, use your own version.
'''

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

n_samples = 200
X,y = make_regression(n_samples= n_samples, n_features=4, random_state=1)
y = np.asmatrix(y).T
X = np.asmatrix(X)
Xtrain, Ytrain, Xtest, Ytest = X[::2], y[::2], X[1::2], y[1::2]

#########################################
## INSERT YOUR CODE HERE
w = train(Xtrain, Ytrain, alpha=0.1, n_epoch = 500)
yhat = Xtrain.dot(w)
yhat_test = Xtest.dot(w)
L = compute_L(yhat, Ytrain)
L_test = compute_L(yhat_test, Ytest)
assert np.allclose(L, 0., atol = 1e-2)
assert np.allclose(L_test, 0., atol = 1e-2)
print("Loss on training set:")
print(L)
print("Loss on training set is less than 1e-2:")
print(L<1e-2)
print( )
print("Loss on testing set:")
print(L_test)
print("Loss on testing set is less than 1e-2:")
print(L_test<1e-2)

loss_test = []
al = []
alpha = 0.015
while alpha < 0.1:
    al.append(alpha)
    w = train(Xtrain, Ytrain, alpha, n_epoch = 500)
    yhat = Xtrain.dot(w)
    yhat_test = Xtest.dot(w)
    L = compute_L(yhat, Ytrain)
    L_test = compute_L(yhat_test, Ytest)
    loss_test.append(L_test)
    alpha += 0.0001
print(loss_test)
import matplotlib.pyplot as plt
plt.xlabel("Alpha value")
plt.ylabel("Loss on test set")
plt.plot(al, loss_test, color="r")
plt.show()

# loss_test = []
# epoch = []
# for n_epoch in range(500, 1000):
#     epoch.append(n_epoch)
#     w = train(Xtrain, Ytrain, alpha=0.01, n_epoch=n_epoch)
#     yhat = Xtrain.dot(w)
#     yhat_test = Xtest.dot(w)
#     L = compute_L(yhat, Ytrain)
#     L_test = compute_L(yhat_test, Ytest)
#     loss_test.append(L_test)
# import matplotlib.pyplot as plt
# plt.xlabel("Epochs")
# plt.ylabel("Loss on test set")
# plt.plot(epoch, loss_test, color="b")
# plt.show()
#########################################


import numpy as np

# arange = np.arange(0, 12)
# print(arange)
#
# arange_reshape = arange.reshape((4, 3))
# print(arange_reshape)
#
# print(arange_reshape[2, 1])
#
# a = np.array([1, 2, 3, 4])
# b = np.array([2, 3, 4, 5])
#
# np.linalg.norm((a - b), ord=1)
#
# (a - b[0])
#
# np.linalg.norm((a - b[0]), ord=1)

a = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
b = np.array([[1, 10], [100, 1000], [10000, 100000]])

# print(a.repeat(2, axis=0))
# print(a.repeat(2, axis=1))
# print(a.transpose())
print(b[:, np.newaxis])
print(a.transpose())
print(a @ b.T)

# res0 = a.transpose() * b
# print(res0)
res = a * b[:, np.newaxis]
print(res)

def my_func(arr1, arr2):
    return arr1 * arr2 + 1

v_my_func = np.vectorize(my_func)

res2 = v_my_func(a, b[:, np.newaxis])

print(res2)

def one_test_distance(arr1, arr2):
    # return arr1 + arr2
    return np.linalg.norm((arr1 - arr2), ord=1)

# v_one_test_distance = np.vectorize(one_test_distance)
# v_one_test_distance(np.expand_dims(b, axis=1), a)

b1 = np.expand_dims(b, axis=1)

# print(one_test_distance(a.transpose().repeat(2, 1), b))

# b = np.array([[-1, 0, 1, 2], [-1, 3, 4, 5], [-1, 6, 7, 8]])
# print(b)

# print(b.reshape(1, 4, 3))
#
# a1 = np.arange(0, 60).reshape(5, 4, 3)
#
#
# def myprint(v):
#     print(v)
#     return v + 100
#
#
# a2 = np.apply_along_axis(myprint, 0, a1)
# print(a2)

def test(p1, p2):
    print(p1, p2)

v_test = np.vectorize(test)

res = v_test([[1, 2]], [[7, 8]])
print(res)

f1 = np.array([[0, 1, 2, 3, 4], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
print(f1)
f2 = np.array([[0, 10, 20, 30, 40], [60, 70, 80, 90, 100]])

f1 = np.array([[0, 1, 2, 3, 4], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
print(f1)

print(np.expand_dims(f1, 0))


'''
Example of use a the optimization toolbox of SciPy.
The function optimized here are meaningless, and just given
as example. They ***are not*** related to the robotic models.
'''
import numpy as np
from scipy.optimize import fmin_bfgs, fmin_slsqp

def cost(x):
    '''Cost f(x,y) = x^2 + 2y^2 - 2xy - 2x '''
    x0 = x[0]
    x1 = x[1]
    return -1 * (2 * x0 * x1 + 2 * x0 - x0**2 - 2 * x1**2)

def constraint_eq(x):
    ''' Constraint x^3 = y '''
    return np.array([x[0]**3 - x[1]])

def constraint_ineq(x):
    '''Constraint x>=2, y>=2'''
    return np.array([x[0] - 2, x[1] - 2])

class CallbackLogger:
    def __init__(self):
        self.nfeval = 1

    def __call__(self, x):
        print('===CBK=== {0:4d}   {1: 3.6f}   {2: 3.6f}'.format(self.nfeval, x[0], x[1], cost(x)))
        self.nfeval += 1

x0 = np.array([0.0, 0.0])
# Optimize cost without any constraints in BFGS, with traces.
xopt_bfgs = fmin_bfgs(cost, x0, callback=CallbackLogger())
print('\n *** Xopt in BFGS = %s \n\n\n\n' % str(xopt_bfgs))

# Optimize cost without any constraints in CLSQ
xopt_lsq = fmin_slsqp(cost, [-1.0, 1.0], iprint=2, full_output=1)
print('\n *** Xopt in LSQ = %s \n\n\n\n' % str(xopt_lsq))

# Optimize cost with equality and inequality constraints in CLSQ
xopt_clsq = fmin_slsqp(cost, [-1.0, 1.0], f_eqcons=constraint_eq, f_ieqcons=constraint_ineq, iprint=2, full_output=1)
print('\n *** Xopt in c-lsq = %s \n\n\n\n' % str(xopt_clsq))