from numpy import cos, cosh, sin, sinh
import numpy as np
import matplotlib.pyplot as plt
import time
'''
TASK 5

'''
r1 = 1.87527632324985
#r1 derived in previous task
'''
First Integral, steps 1-3
'''
#phi and psi functions as defined by the project statement
def phi(x, r):
    return (sin(r*x) + sinh(r*x) + ((cos(r)+cosh(r))/(sin(r)+sinh(r)))*(cos(r*x)-cosh(r*x)))

def d2phi(x, r):
    return (r**2)*(-sin(r*x) + sinh(r*x) + ((cos(r)+cosh(r))/(sin(r)+sinh(r)))*(-cos(r*x)-cosh(r*x)))

def psi_h(x, qi):
    return qi*phi(x, r1)

def d2psi_h(x, qi):
    return qi*d2phi(x, r1)

#single integral approximator
def trapezoid(r, qx, a=0, b=1, m=100):
    dx = (b-a)/m

    total = phi(a, r)*(d2psi_h(a, qx))
    total += phi(b, r)*(d2psi_h(b, qx))

    for i in range(1, m-1):
        total += 2*phi(a+i*dx, r)*(d2psi_h(a+i*dx, qx))

    return total*dx/2

#slope of the single integral approximator
def linear_slope(n=100, a=0, b=1):
    def h(x):
        return phi(x, r1)*d2phi(x, r1)

    const = (b-a)/(2*n)

    dx = (b-a)/(n)

    total = h(a) + h(b)

    for i in range(1, n):
        total += 2*h(a + dx*i)

    return const*total

#double integral approximator
def double_int(r, qx, a=0, b=1, ups=1, m=25):
    dx = (b-a)/m
    xs = np.arange(a, b, dx)

    total_x = []
    for x in xs:
        lws = x
        ds = (ups - lws)/m

        total_s = phi(x, r)*cos(psi_h(x, qx) -psi_h(lws, qx))
        total_s += phi(x, r)*cos(psi_h(x, qx) -psi_h(ups, qx))

        for i in range(1, m):
            total_s += 2*phi(x, r)*cos(psi_h(x, qx) -psi_h(lws + i*ds, qx))

        total_x.append(total_s*ds/2)

    ret = 2*sum(total_x) - total_x[0] - total_x[-1]
    return ret*dx/2

#function in equation 2 per project handout
def f(r, qx):
    return 100*double_int(r, qx) + trapezoid(r, qx)

#g function (for fixed point)
def g(r, qx):
    return -(100*double_int(r, qx) + trapezoid(r, qx))/(linear_slope())

# qi = 4.27775
# f1 = f(r1, qi)

n=100
lim = 5
q1 = np.arange(-lim, lim, 2*lim/n)
f1 = []                                 #matrix to hold the value of f1(q1) for all discrete points q1, q2
g1 = []
task7 = []

#evaluate all the points
for qi in q1:
    f1.append(f(r1, qi))
    if qi>=0:
        g1.append(g(r1, qi))

g1.append(g(r1, q1[-1]+2*lim/n))

f1_p = np.array(f1)

#plot function f(q1) in 1d
plt.plot(q1, f1_p)
plt.grid(color='k', linestyle='--', linewidth=0.5)
plt.title("Plot of f(q_1)")
plt.show()

'''
TASK 6
'''
#fixed-point stuff
qs = np.arange(0,5,10/n)
plt.plot(qs, g1)
plt.plot(qs, qs, 'r')
plt.grid(color='k', linestyle='--', linewidth=0.5)
plt.axvline(x=2.5, ymin=0.3, ymax=0.42, color='g', linestyle=':', linewidth=2)
plt.axvline(x=4, ymin=0.3, ymax=0.42, color='g', linestyle=':', linewidth=2)
plt.axhline(y=2.5, xmin=0.51, xmax=0.79, color='g', linestyle=':', linewidth=2)
plt.axhline(y=4, xmin=0.51, xmax=0.79, color='g', linestyle=':', linewidth=2)
plt.title("Fixed-point quadrature scheme")
plt.show()

'''
TASK 7
'''
#custom secant function
def secant(ra, rb, e):
    r = rb - f(r1, rb)*((rb-ra))/(f(r1,rb) - f(r1,ra))

    steps = 1

    while abs(r-rb) > e:
        ra = rb
        rb = r
        r = rb - f(r1, rb)*((rb-ra))/(f(r1,rb) - f(r1,ra))
        steps +=1

    #check how many steps it took
    print("found a solution with error {} in {} steps".format(str(e), str(steps)))
    return [r, steps]

root = secant(4, 4.1, 0.001)

print(root)

'''
CONVERGENCE ANALYSIS ON SINGLE INTEGRAL
'''
#looking at the ROC of our implementation of the trapezoid rule
#assume that with n=10000 trapezoids, we have our 'true' solution
#compare values at different n to this true value
val = linear_slope(10000)

error = []
solution = []

for i in range(1,101):
    v = linear_slope(i)
    error.append((abs((v-val)/val))*100)
    solution.append(v)

plt.plot(np.arange(1,101,1), solution, 'r')
plt.grid(color='k', linestyle='--', linewidth=0.5)
plt.title("Conversion of the trapezoid rule for single integral approximation")
plt.ylabel("Value of integral")
plt.xlabel("Number of trapezoids used")
plt.show()

plt.plot(np.arange(1,101,1), error, 'r')
plt.grid(color='k', linestyle='--', linewidth=0.5)
plt.title("Conversion of the error using trapezoid rule for single integral approximation")
plt.ylabel("Relative error (%)")
plt.xlabel("Number of trapezoids used")
plt.show()

#evaluate the rate of convergence of our implementation of the secant method
eps = np.arange(0.00001, 0.10001, 0.001)
nsteps = []

for e in eps:
    nsteps.append(secant(0,0.1,e)[1])

nsteps = np.array(nsteps)
plt.plot(nsteps, eps, 'r')
plt.grid(color='k', linestyle='dotted', linewidth=0.5)
plt.title("Rate of convergence of error for the secant method in 1D")
plt.ylabel("Error")
plt.xlabel("Number of steps to solve")
plt.show()


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

x1 = np.random.normal(loc=0.5, scale=1 , size =100).reshape(-1,1)
x2 = np.random.normal(loc=10, scale=2 , size =100).reshape(-1,1)
x3 = np.random.normal(loc=0, scale=3 , size =100).reshape(-1,1)
x = np.concatenate([x1 , x2 , x3])

plt.figure()
sns.distplot(x)
plt.title("Gaussian Mixture Model")

def Gaus(x, m, s): #正規分布の値を取得
    g = np.exp( - pow((x-m), 2) /(2*s) )/np.sqrt(2*np.pi * s )
    return g

def calc_gamma(x, pi, mu, sig):#事後分布の計算
    gam = pi*Gaus(x,mu,sig)
    gam/= np.sum(gam, axis=1).reshape(len(x),1)
    return gam

def update_parmas(gamma, x, pi, mu, sig):#パラメーターの更新式
    N_k = np.sum(gamma, axis=0)
    N = np.sum(N_k)
    mu_k = np.sum(x*gamma, axis=0 ) /N_k
    sig_k = np.sum(gamma* pow(x-mu, 2), axis=0) /N_k
    pi_k = N_k/N
    return pi_k , mu_k, sig_k

def iteration(x,mu,sig,pi, I=100, e=0.01):#ε以下になるか、100回計算するまで尤度を更新する関数
    LF=0
    for i in range(I):
        gamma = calc_gamma(x, pi, mu, sig)
        LF_new =np.sum(np.log(np.sum(pi*Gaus(x,mu,sig),axis=1 )) )
        ch = LF_new - LF
        print("LF ={} . change = {}".format(LF_new, ch))
        if np.abs(ch) < e:
            print("Iteration is finished {} iter. ".format(i+1))
            break
        LF=LF_new
        pi, mu, sig = update_parmas(gamma, x, pi, mu, sig)

    return pi, mu, sig

mu =np.array([0, 10, 3])
sig=np.array([1, 5, 10])
pi=np.array([0.1, 0.4, 0.5])

# execute
pi, mu, sig = iteration(x, mu, sig, pi)

# show results
print(pi, mu, np.sqrt(sig) )
y0 = np.random.normal(loc=mu[0], scale=np.sqrt(sig)[0] , size =int(300*pi[0]) ).reshape(-1,1)
y1 = np.random.normal(loc=mu[1], scale=np.sqrt(sig)[1] , size =int(300*pi[1]) ).reshape(-1,1)
y2 = np.random.normal(loc=mu[2], scale=np.sqrt(sig)[2] , size =int(300*pi[2]) ).reshape(-1,1)
y=np.concatenate([y0, y1, y2])

plt.figure()
sns.distplot(y)
plt.title("Predicted GMM")

plt.show()


from __future__ import division

import numpy as np

from scipy.special import digamma, polygamma

def get_noise_distribution(data, method='moments'):
    '''Computes sigma and N from an array of gamma distributed data

    input
    -----
    data
        A numpy array of gamma distributed values
    method='moments' or method='maxlk'
        Use either the moments or maximum likelihood equations to estimate the parameters.

    output
    ------
    sigma, N
        parameters related to the original Gaussian noise distribution
    '''

    data = data[data > 0]

    # If we have no voxel or only the same value
    # it leads to a divide by 0 as an edge case
    if data.size == 0 or np.std(data) == 0:
        return 0, 0

    # First get sigma
    if method == 'moments':
        mdata2 = np.mean(data**2)
        mdata4 = np.mean(data**4)

        p1 = mdata4 / mdata2
        p2 = mdata2
        sigma = np.sqrt(p1 - p2) / np.sqrt(2)
    elif method == 'maxlk':
            sigma = maxlk_sigma(data)
    else:
        raise ValueError('Invalid method name {}'.format(method))

    t = data**2 / (2*sigma**2)

    # Now compute N
    if method == 'moments':
        N = np.mean(t)
    elif method == 'maxlk':
            y = np.mean(np.log(t))
            N = inv_digamma(y)
    else:
        raise ValueError('Invalid method name {}'.format(method))

    return sigma, N

def maxlk_sigma(m, xold=None, eps=1e-8, max_iter=100):
    '''Maximum likelihood equation to estimate sigma from gamma distributed values'''

    sum_m2 = np.sum(m**2)
    K = m.size
    sum_log_m2 = np.sum(np.log(m**2))

    def f(sigma):
        return digamma(sum_m2/(2*K*sigma**2)) - sum_log_m2/K + np.log(2*sigma**2)

    def fprime(sigma):
        return -sum_m2 * polygamma(1, sum_m2/(2*K*sigma**2)) / (K*sigma**3) + 2/sigma

    if xold is None:
        xold = m.std()

    for _ in range(max_iter):

        xnew = xold - f(xold) / fprime(xold)

        if np.abs(xold - xnew) < eps:
            break

        xold = xnew

    return xnew

def inv_digamma(y, eps=1e-8, max_iter=100):
    '''Numerical inverse to the digamma function by root finding'''

    if y >= -2.22:
        xold = np.exp(y) + 0.5
    else:
        xold = -1 / (y - digamma(1))

    for _ in range(max_iter):

        xnew = xold - (digamma(xold) - y) / polygamma(1, xold)

        if np.abs(xold - xnew) < eps:
            break

        xold = xnew

    return xnew


import math
import numpy as np
import scipy
import networkx as nx
from manim import *

config.background_color = WHITE
#config.frame_width=4   # Enlarges everything, including the stroke width. Default: 128/9≈14.222

FG = BLACK

class Tree(Scene):
    def f(self, position = [0,0,0], direction = 0, span = 2*PI, degree=3, length=1):
        step = 2/degree
        start = -1 + step/2
        angles = [ start + k*step for k in range(degree) ]
        angles = [ span * a for a in angles ]
        angles = [ direction + a for a in angles ]
        xs = [ position[0] + length * math.cos(angle) for angle in angles ]
        ys = [ position[1] + length * math.sin(angle) for angle in angles ]
        points = [ [x,y,0] for x,y in zip(xs,ys) ]
        lines = [ Line(position,p).set_stroke(FG) for p in points ]
        return lines

    def g(self, lines, span, degree, length):
        new_lines = []
        for l in lines:
            position = l.end
            x1,y1,_ = l.start
            x2,y2,_ = l.end
            direction = math.atan2( y2-y1, x2-x1 )
            print( direction )
            new_lines += self.f(position, direction, span, degree, length)
        return new_lines

    def h(self, lines):
        self.play( Create( VGroup(*lines) ) )
        #for line in lines:
        #    self.add(line)

    def construct(self):
        lines = self.f(degree=5,length=.8)
        self.h(lines)
        self.wait(1)

        lines = self.g( lines, span = 2*PI / 7, degree=4, length=.8 )
        self.h(lines)
        self.wait(1)

        lines = self.g( lines, span = 2*PI / 12, degree=4, length=.6 )
        self.h(lines)
        self.wait(1)

        lines = self.g( lines, span = 2*PI / 15, degree=4, length=.3 )
        self.h(lines)
        self.wait(10)

class Circles(Scene):
    def construct(self):
        def f(R=1, n=5, offset=0):
            c = Circle().scale(R).set_stroke(GREY, width=10)
            self.play( Create(c) )

            angles = np.linspace(0,2*PI,n+1)[:-1] + offset
            dots = [ Dot([R * math.cos(a), R * math.sin(a),0]).set_color(FG).scale(2) for a in angles ]
            self.play( Create( VGroup(*dots) ) )
            self.wait(1)
            return dots

        def g(a,b):
            lines = []
            def d(u,v):
                dx = ( u.get_center()[0] - v.get_center()[0] ) **2
                dy = ( u.get_center()[1] - v.get_center()[1] ) **2
                dz = ( u.get_center()[2] - v.get_center()[2] ) **2
                return dx + dy + dz
            for u in b:
                print( f"{u.get_center()}" )
                dist = np.inf
                w = None
                for v in a:
                    if d(u,v) < dist:
                        w = v
                        dist = d(u,v)
                lines += Line( w.get_center(), u.get_center() ).set_stroke(FG)
            self.play( Create( VGroup(*lines) ) )
            self.wait(1)

        if True:

                k = 4
                a = [ Dot([0,0,0]).set_color(FG).scale(2) ]
                self.add( a[0] )
                self.wait(1)

                b = f(1,k,0)
                g(a,b)
                c = f(2,k*(k-1),0)
                g(b,c)
                d = f(3,k*(k-1)**2,0)
                g(c,d)
                e = f(4,k*(k-1)**3,0)
                g(d,e)

        text1 = MathTex( r"2\pi", "R" ).set_color(FG).shift(6*LEFT+3*UP)
        text2 = MathTex( r"2\pi", "n" ).set_color(FG).shift(6*LEFT+3*UP)
        self.play(Write(text1))
        self.wait(1)
        self.play(Transform(text1, text2))
        self.wait(1)

        text = MathTex( r"4\times3^{n-1}" ).set_color(FG).shift(6*LEFT+2*UP)
        self.play(Write(text))
        self.wait(1)

        self.wait(10)

class MST(Scene):
    def construct(self):

        def f(A):
            g = nx.Graph()
            for i in range(A.shape[0]):
                g.add_node(i)
            for i in range(A.shape[0]):
                for j in range(A.shape[1]):
                    if ( A[i,j] or A[j,i] ) and i<j:
                        g.add_edge(i,j)
            return g

        k = 100
        x = np.random.normal(size=(k,2*k))
        C = np.corrcoef(x)
        A = scipy.sparse.csgraph.minimum_spanning_tree(1-C)
        g = f(A)
        xy = nx.kamada_kawai_layout(g)
        xy = np.array(list(xy.values()))
        xy = xy - xy.min(axis=0)
        xy = xy / xy.max(axis=0)
        xy = 2 * xy - 1
        xy[:,0] = 6*xy[:,0]
        xy[:,1] = 3*xy[:,1]
        print(xy)
        objects = []
        for i in range(k):
            d = Dot( [ xy[i,0], xy[i,1], 0 ] ).set_color(FG).scale(1)
            objects += d
            for j in range(i):
                if A[i,j] == 0 and A[j,i] == 0:
                    continue
                line = Line( [ xy[i,0], xy[i,1], 0 ], [ xy[j,0], xy[j,1], 0 ] ).set_color(FG)
                objects += line
        self.play( Create(VGroup(*objects)) )

        self.wait(10)

from scipy.optimize import fsolve

def poincare_disk_compute_line( x1,y1, x2,y2 ):
    """
    Given two points (x1,y1), (x2,y2) in the unit disk,
    compute the parameters the circle going through them
    and orthogonal to the unit circle.

    I assume that the line is not a diameter: the circle would be at infinity

    Inputs:  x1,y1: first point (cartesian coordinates, i.e., x^2+y^2<=1)
             x2,y2: second point
    Outputs: x0,y0: center of the circle (outside the unit circle)
             r: radius
             theta1, theta2: angles at which the two points are
    """
    def f(x):
        a,b = x
        return np.array([
            x1**2 + y1**2 + a*x1 + b*y1 + 1,
            x2**2 + y2**2 + a*x2 + b*y2 + 1,
        ])
    x0 = np.random.uniform(-1,1,2)
    a,b = fsolve(f,x0)
    x0, y0 = -a/2, -b/2
    r = math.sqrt( a**2/4 + b**2/4 - 1 )
    theta1 = math.atan2( y1-y0, x1-x0 )
    theta2 = math.atan2( y2-y0, x2-x0 )
    if theta1 <= theta2:
        if theta2 - theta1 > PI:
            theta2 = theta2 - 2*PI
    else:
        if theta1 - theta2 > PI:
            theta1 = theta1 - 2 * PI
    return x0, y0, r, theta1, theta2

def poincare_disk_line(x0, y0, r, scale=1):
    return Circle().scale(r*scale).shift([x0*scale,y0*scale,0]).set_color(GREY)

def poincare_disk_segment(x0, y0, r, theta1, theta2, n=10, scale=1):
    ts = np.linspace( theta1, theta2, n )
    lines = []
    for t1,t2 in zip( ts[:-1], ts[1:] ):
        lines += Line(
            scale * np.array([ x0 + r * math.cos(t1), y0 + r*math.sin(t1), 0 ] ),
            scale * np.array([ x0 + r * math.cos(t2), y0 + r*math.sin(t2), 0 ] ),
        ).set_color(FG)
    return lines

class PoincareDisk(Scene):
    SCALE = 3
    def construct(self):
        c = Circle().scale(self.SCALE).set_color(FG)
        self.play( Create(c) )
        self.wait(1)

        def random_point():
            done = False
            while not done:
                x = np.random.uniform(-1,+1)
                y = np.random.uniform(-1,+1)
                done = x**2 + y**2 <= 1
            return x,y

        for _ in range(5):
            x1,y1 = random_point()
            x2,y2 = random_point()
            dots = [
                Dot( self.SCALE * np.array([x1,y1,0]) ).set_color(FG),
                Dot( self.SCALE * np.array([x2,y2,0]) ).set_color(FG)
            ]
            self.play( Create(VGroup(*dots)) )
            x0, y0, r, theta1, theta2 = poincare_disk_compute_line( x1,y1, x2,y2 )
            c = poincare_disk_line( x0, y0, r, scale=self.SCALE )
            self.play( Create(c) )
            lines = poincare_disk_segment(x0, y0, r, theta1, theta2, n=10, scale=self.SCALE)
            self.play( Create(VGroup(*lines)) )
            self.wait(1)

def poincare_half_plane_compute_line(x1,y1, x2,y2):
    def f(x):
        x0, r, theta1, theta2 = x
        return [
            x0 + r * math.cos(theta1) - x1,
            0  + r * math.sin(theta1) - y1,
            x0 + r * math.cos(theta2) - x2,
            0  + r * math.sin(theta2) - y2,
        ]
    code = 0
    while code != 1:
        x0 = np.array([
            np.random.uniform(x1,x2),
            np.random.uniform(0,3),   # TODO
            np.random.uniform(0,PI),
            np.random.uniform(0,PI),
        ])
        (x0, r, theta1, theta2), _, code, msg= fsolve(f,x0, full_output=True)

    theta1 = theta1 % TAU
    theta2 = theta2 % TAU
    print( f"x0={x0} r={r} theta1={theta1} theta2={theta2}" )
    return x0, r, theta1, theta2

def poincare_half_plane_line( x0, r, shift=0 ):
    return Arc( radius=r, start_angle = 1e-4, angle=PI-2e-4, arc_center=[x0,shift,0] ).set_color(GREY)  # There is also a num_component=9 argument

def poincare_half_plane_segment( x0, r, theta1, theta2, shift=0 ):
    if theta1 > theta2:
        theta1, theta2 = theta2, theta1
    return Arc( radius=r, start_angle=theta1, angle=theta2-theta1, arc_center=[x0,shift,0] ).set_color(FG)

class PoincareHalfPlane(Scene):
    SHIFT = -2.5
    def construct(self):
        l = Line( [-7,self.SHIFT,0], [7,self.SHIFT,0] ).set_color(FG)
        self.play( Create(l) )

        def random_point():
            return np.random.uniform(-6,6), np.random.uniform(0,3)

        for _ in range(5):
            x1,y1 = random_point()
            x2,y2 = random_point()
            dots = [
                Dot( [x1,y1+self.SHIFT,0] ).set_color(FG),
                Dot( [x2,y2+self.SHIFT,0] ).set_color(FG)
            ]
            self.play( Create(VGroup(*dots)) )
            x0, r, theta1, theta2 = poincare_half_plane_compute_line( x1,y1, x2,y2 )
            c = poincare_half_plane_line( x0, r, shift=self.SHIFT )
            self.play( Create(c) )
            lines = poincare_half_plane_segment(x0, r, theta1, theta2, shift=self.SHIFT)
            self.play( Create(VGroup(*lines)) )
            self.wait(1)

        self.wait(10)

class PoincareHalfPlane2(Scene):
    SHIFT = -2.5
    def construct(self):
        l = Line( [-7,self.SHIFT,0], [7,self.SHIFT,0] ).set_color(FG)
        self.play( Create(l) )

        def random_point():
            return np.random.uniform(-6,6), np.random.uniform(0,3)

        def f(x1,y1,x2,y2,run_time=.5):
            x0, r, theta1, theta2 = poincare_half_plane_compute_line( x1,y1, x2,y2 )
            lines = poincare_half_plane_segment(x0, r, theta1, theta2, shift=self.SHIFT)
            self.play( Create(VGroup(*lines)), run_time = run_time )

        x1,y1 = -3,1
        x2,y2 = +3,1
        dots = [
            Dot( [x1,y1+self.SHIFT,0] ).set_color(FG),
            Dot( [x2,y2+self.SHIFT,0] ).set_color(FG),
        ]
        self.play( Create( VGroup(*dots) ) )
        self.wait(1)
        f(x1,y1,x2,y2,1)
        self.wait(1)
        for x in [-6,-5,-4,-2,-1,0,1,2,4,5,6]:
            f(x1,y1,x,1e-4)
        self.play( Create( Line( [x1,self.SHIFT,0], [x1,6,0] ).set_color(FG) ) )
        for x in [10,-10,20,-20,30,-30,-50]:
            f(x1,y1, x,1e-4, .1)
        self.wait(10)

import numpy,import scipy,from scipy,import networkx


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

A = np.array([
    [2, 1],     # 2*x0 + x1 <= 6
    [-1, -1]    # -x0 - x1 <= -4
])
b = np.array([6, -4])

x0_bounds = (-3, 14)  # -3 <= x0 <= 14
x1_bounds = (2, 12)   #  2 <= x1 <= 12

c = np.array([1, 5])

def func(x):
    return np.tensordot(c, x, axes=1)

fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.set(xlabel="x0", ylabel="x1", zlabel="func")
ax.set_title("Values in feasible region")

X0 = np.linspace(*x0_bounds)
X1 = np.linspace(*x1_bounds)
x0, x1 = np.meshgrid(X0, X1)
z = func([x0, x1])

ax.plot_surface(x0, x1, z, alpha=0.3)

Y = (b[0] - A[0, 0]*X0) / A[0, 1]
I = np.logical_and(Y >= x1_bounds[0], Y <= x1_bounds[1])
ax.plot(X0[I], Y[I], func([X0[I], Y[I]]), "r", lw=1.5)

Y = (b[1] - A[1, 0]*X0) / A[1, 1]
I = np.logical_and(Y >= x1_bounds[0], Y <= x1_bounds[1])
ax.plot(X0[I], Y[I], func([X0[I], Y[I]]), "r", lw=1.5)

B = np.tensordot(A, np.array([x0, x1]), axes=1)
II = np.logical_and(B[0, ...] <= b[0], B[1, ...] <= b[1])
ax.plot_trisurf(x0[II], x1[II], z[II], color="b", alpha=0.5)

res = optimize.linprog(c, A_ub=A, b_ub=b, bounds=(x0_bounds, x1_bounds))
print(res)

ax.plot([res.x[0]], [res.x[1]], [res.fun], "k*")

plt.show()


import numpy as np
import cv2

def pos_int(p):
    return (int(p[0]), int(p[1]))

def cubic_spline(x,y,interval=2):
    size = len(y)
    h = [x[i+1]-x[i] for i in range(len(x)-1)]
    A = np.zeros((size, size), dtype=np.float)
    for i in range(size):
        if i==0:
            A[i,0] = 1
            #A[i,0] = -h[i+1]
            #A[i,1] = h[i+1] + h[i]
            #A[i,2] = -h[i]
        elif i==size-1:
            A[i,-1] = 1
            #A[i,-3] = -h[i-1]
            #A[i,-2] = h[i-2] + h[i-1]
            #A[i,-1] = -h[i-1]
        else:
            A[i,i-1] = h[i-1]
            A[i,i] = 2*(h[i-1]+h[i])
            A[i,i+1] = h[i]
    #print(A)

    B = np.zeros((size,1), dtype=np.float)
    for i in range(1,size-1):
        B[i,0] = (y[i+1]-y[i])/h[i] - (y[i]-y[i-1])/h[i-1]
    B = 6*B
    #print(B)

    Ainv = np.linalg.pinv(A)
    m = Ainv.dot(B).T[0].tolist()
    a = [y[i] for i in range(size-1)]
    b = [(y[i+1]-y[i])/h[i] - h[i]*m[i]/2 - h[i]*(m[i+1]-m[i])/6 for i in range(size-1)]
    c = [m[i]/2 for i in range(size-1)]
    d = [(m[i+1]-m[i])/(6*h[i]) for i in range(size-1)]

    y_list = []
    dy_list = []
    ddy_list = []
    i = 0
    x_ = x[0]
    while(True):
        if x_ >= x[-1]:
            x_ = x[-1]
        elif x_ > x[i+1]:
            i += 1
        y_ = a[i] + b[i]*(x_-x[i]) + c[i]*(x_-x[i])**2 + d[i]*(x_-x[i])**3
        dy = b[i] + 2.0*c[i]*(x_-x[i]) + 3.0*d[i]*(x_-x[i])**2
        ddy = 2.0*c[i] + 6.0*d[i]*(x_-x[i])
        y_list.append(y_)
        dy_list.append(dy)
        ddy_list.append(ddy)
        if x_ == x[-1]:
            break
        x_ += interval
    return y_list, dy_list, ddy_list

def cubic_spline_2d(path, interval=2):
    x = [path[i][0] for i in range(len(path))]
    y = [path[i][1] for i in range(len(path))]
    x_diff = [path[i+1][0]-path[i][0] for i in range(len(path)-1)]
    y_diff = [path[i+1][1]-path[i][1] for i in range(len(path)-1)]
    dist = np.hypot(np.array(x_diff),np.array(y_diff))
    dist_cum = np.cumsum(dist).tolist()
    dist_cum.insert(0,0)
    #print(dist, dist_cum)

    x_list, dx_list, ddx_list = cubic_spline(dist_cum,x,interval)
    y_list, dy_list, ddy_list = cubic_spline(dist_cum,y,interval)
    dx, ddx, dy, ddy = np.array(dx_list), np.array(ddx_list), np.array(dy_list), np.array(ddy_list)
    yaw_list = np.rad2deg(np.arctan2(dy,dx))
    curv_list = (ddy * dx - ddx * dy) / ((dx ** 2 + dy ** 2)**(3 / 2))
    path_smooth = [(x_list[i], y_list[i], yaw_list[i], curv_list[i]) for i in range(len(x_list))]

    #print(path_smooth)
    return path_smooth

if __name__ == "__main__":
    path = [(20,50), (40,100), (80,120), (160,60)]
    path_smooth = cubic_spline_2d(path)

    img = np.ones((200,200,3), dtype=np.float)
    for p in path:
        cv2.circle(img, p, 3, (0.5,0.5,0.5), 2)

    for i in range(len(path_smooth)-1):
        cv2.line(img, pos_int(path_smooth[i]), pos_int(path_smooth[i+1]), (1.0,0.4,0.4), 1)

    img = cv2.flip(img,0)
    cv2.imshow("Cubic Spline Test", img)
    cv2.waitKey(0)


#!/usr/bin/env python
"""ascention.py: Python からはじめる数学入門 pp. 209-210, 213-214.

Usage:
  ascention.py
"""
from sympy import sympify, Symbol, diff, solve

class GradientAscentError(Exception): pass

def grad_ascent(x0, f1x, x):
    """Gradient ascent to find the maximum value of a single
    variable function
    """

    if not solve(f1x):
        raise GradientAscentError(
            f'Cannot continue, solution for {f1x} = 0 does not exist')

    eps = 1e-6
    step_size = 1e-4
    x_old = x0
    x_new = x_old + step_size * f1x.subs({x: x_old}).evalf()
    while abs(x_old - x_new) > eps:
        x_old = x_new
        x_new = x_old + step_size * f1x.subs({x: x_old}).evalf()

    return x_new

def main():
    f = input('Enter a function in one variable: ')
    v = input('Enter the variable: ')
    init = float(input('Enter the initial value of the variable: '))
    f = sympify(f)

    v = Symbol(v)
    d = diff(f, v)
    try:
        vmax = grad_ascent(init, d, v)
        print(f'{v.name}: {vmax}')
        print(f'Maximum value: {f.subs({v: vmax})}')
    except GradientAscentError as exc:
        print(exc)

if __name__ == '__main__':
    main()


import numpy as np

def func_levy(params, diff):
    """Levy function N13, which is:
    f(x) = sin^{2}(3\pi x_{1}) + (x_{1}-1)^{2}[1+sin^{2}(c \pi x_2)]
             + (x_2-1)^{2}[1+sin^{2}(2 \pi x_2)]]
    link to the function: https://tinyurl.com/tfn3o7p

    Args:
        params(np.array): 1d numpy array of function arguments
        diff(float): difficulty parameter, controls wiggliness of the function

    Returns:
        func(float): value of the Levy function evaluated at `params` and `diff`

    """
    param1 = params[0]
    param2 = params[1]
    func = (
        (np.sin(3 * np.pi * param1)) ** 2
        + (param1 - 1) ** 2 * (1 + np.sin(diff * param2) ** 2)
        + (param2 - 1) ** 2 * (1 + np.sin(2 * np.pi * param2) ** 2)
    )
    return func

def grad_levy(params, diff):
    """Gradient of Levy functio N13.

    Args:
        params(np.array): 1d numpy array of function arguments
        diff(float): difficulty parameter, controls wiggliness of the function

    Returns:
        grad(np.array): 1d numpy array of Levy function derivative for each
            argument, evaluated at `params` and `diff`

    """
    param1 = params[0]
    param2 = params[1]
    der_param1 = 6 * np.pi * np.cos(3 * np.pi * param1) * np.sin(
        3 * np.pi * param1
    ) + 2 * ((np.sin(diff * param2)) ** 2 + 1) * (param1 - 1)
    der_param2 = (
        2 * diff * (param1 - 1) ** 2 * np.cos(diff * param2) * np.sin(diff * param2)
        + 2 * (param2 - 1) * ((np.sin(2 * np.pi * param2)) ** 2 + 1)
        + 4
        * np.pi
        * (param2 - 1) ** 2
        * np.cos(2 * np.pi * param2)
        * np.sin(2 * np.pi * param2)
    )

    grad = np.array([der_param1, der_param2])
    return grad

def hessian_levy(params, diff):
    """Hessian matrix of Levy function.

    Args:
        params(np.array): 1d array of function arguments
        diff(float): difficulty parameter, controls wiggliness of the function

    Returns:
        hess_mat(np.array): 2d numpy array with the Levy function hessian,
            evaluated at `params` and `diff`

    """
    param1 = params[0]
    param2 = params[1]

    der_param1_param1 = (
        -18 * np.pi ** 2 * (np.sin(3 * np.pi * param1)) ** 2
        + 18 * np.pi ** 2 * (np.cos(3 * np.pi * param1)) ** 2
        + 2 * ((np.sin(diff * param2)) ** 2 + 1)
    )
    der_param1_param2 = (
        4 * diff * (param1 - 1) * np.cos(diff * param2) * np.sin(diff * param2)
    )
    der_param2_param1 = (
        4 * diff * (param1 - 1) * np.cos(diff * param2) * np.sin(diff * param2)
    )
    der_param2_param2 = (
        2 * diff ** 2 * (param1 - 1) ** 2 * np.cos(2 * diff * param2)
        + 8 * np.pi * (param2 - 1) * np.sin(4 * np.pi * param2)
        + (8 * np.pi ** 2 * (param2 - 1) ** 2 - 1) * np.cos(4 * np.pi * param2)
        + 3
    )

    hess_mat =  np.array(
        [[der_param1_param1, der_param1_param2], [der_param2_param1, der_param2_param2]]
    )
    return hess_mat


from math import cos,sin,sqrt,atan2,pi,exp
import numpy as np
# define parameters
alpha1=0.1
alpha2=0.12
alpha3=0.25
alpha4=0.2
alpha5=0.13
alpha6=0.16

def motion_model_velocity(xt, ut, xt_1, prob):
    # xt = [x_dash y_dash theta_dash]  , xt_1 = [x y z], ut=[ut[0]w]
    mu=0.5*((xt_1[0]-xt[0])*cos(xt_1[2])+(xt_1[1]-xt[1])*sin(xt_1[2])) / \
    ((xt_1[1]-xt[1])*cos(xt_1[2])+(xt_1[0]-xt[0])*sin(xt_1[2]))

    xstar=(xt_1[0]+xt[0])/2+mu*(xt_1[1]-xt[1])
    ystar=(xt_1[1]+xt[1])/2+mu*(xt[0]-xt_1[0])
    rstar=sqrt((xt_1[0]-xstar)**2+(xt_1[1]-ystar)**2)

    dtheta=atan2(xt[1]-ystar, xt[0]-xstar)-atan2(xt_1[1]-ystar, xt_1[0]-xstar)

    v_hat=dtheta/dt*rstar
    w_hat=dtheta/dt
    gamma=(xt[2]-xt_1[2])/2-w_hat

    return prob(ut[0]-v_hat, alpha1*abs(ut[0])+alpha2*abs(ut[1]))*prob(ut[1]-w_hat, alpha3*abs(ut[0])+\
                alpha4*abs(ut[1]))*prob(gamma,alpha5*abs(ut[0])+alpha6*abs(ut[1]))

def normal_prob(a, b):
    return 1/sqrt(2*pi*b) * exp(-0.5*a**2/b)

def tri_prob(a,b):
    if abs(a)>sqrt(6*b) :
        return 0
    else :
        return (sqrt(6*b)-abs(a))/(6*b)

def sample_v_model(ut, xt_1, sample):
    v_hat=u[0]+sample(alpha1*abs(u[0])+alpha2*abs(u[1]))
    w_hat=u[1]+sample(alpha3*abs(u[0])+alpha4*abs(u[1]))
    gamma=sample(alpha5*abs(u[0])+alpha6*abs(u[1]))

    xt=np.matrix(np.zeros((3,1)))
    xt[0,0]=xt_1[0]-v_hat/w_hat*sin(xt_1[2])+v_hat/w_hat*sin(xt_1[2]+w_hat*dt)
    xt[1,0]=xt_1[1]+v_hat/w_hat*cos(xt_1[2])-v_hat/w_hat*cos(xt_1[2]+w_hat*dt)
    xt[2,0]=xt_1[2]+w_hat*dt+gamma*dt

    return xt

def normal_sample(b):
    ret=0
    for i in range(1,13):
        ret+=np.random.uniform(-1,1)
    return ret*b/6

def tri_sample(b):
    return b*np.random.uniform(-1,1)*np.random.uniform(-1,1)

################# test ###############################


import numpy as np

def PCA_numpy(data, n_components=2):

    #1nd step is to find covarience matrix
    data_vector = []
    for i in range(data.shape[1]):
        data_vector.append(data[:, i])

    cov_matrix = np.cov(data_vector)

    #2rd step is to compute eigen vectors and eigne values
    eig_values, eig_vectors = np.linalg.eig(cov_matrix)
    eig_values = np.reshape(eig_values, (len(cov_matrix), 1))

    #Make pairs
    eig_pairs = []
    for i in range(len(eig_values)):
         eig_pairs.append([np.abs(eig_values[i]), eig_vectors[:,i]])

    eig_pairs.sort()
    eig_pairs.reverse()

    #This PCA is only for 2 components
    reduced_data = np.hstack((eig_pairs[0][1].reshape(len(eig_pairs[0][1]),1), eig_pairs[1][1].reshape(len(eig_pairs[0][1]),1)))

    return data.dot(reduced_data)


from sympy.matrices.expressions import MatrixExpr
from sympy import S, I, sqrt, exp

class DFT(MatrixExpr):
    """ Discrete Fourier Transform """
    n = property(lambda self: self.args[0])
    shape = property(lambda self: (self.n, self.n))

    def _entry(self, i, j):
        w = exp(-2*S.Pi*I/self.n)
        return w**(i*j) / sqrt(self.n)

    def _eval_inverse(self):
        return IDFT(self.n)

class IDFT(DFT):
    """ Inverse Discrete Fourier Transform """
    def _entry(self, i, j):
        w = exp(-2*S.Pi*I/self.n)
        return w**(-i*j) / sqrt(self.n)

    def _eval_inverse(self):
        return DFT(self.n)


import math
import json
import scipy.stats

def pearson(vector1, vector2):
    n = len(vector1)
    # simple sums
    sum1 = sum(float(vector1[i]) for i in range(n))
    sum2 = sum(float(vector2[i]) for i in range(n))
    # sum up the squares
    sum1_pow = sum([pow(v, 2.0) for v in vector1])
    sum2_pow = sum([pow(v, 2.0) for v in vector2])
    # sum up the products
    p_sum = sum([vector1[i]*vector2[i] for i in range(n)])
    # 分子num，分母den
    num = p_sum - (sum1*sum2/n)
    den = math.sqrt((sum1_pow-pow(sum1, 2)/n)*(sum2_pow-pow(sum2, 2)/n))
    if den == 0:
        return 0.0
    return num/den

def kendalltau(vector1, vector2):
    tau, p_value = scipy.stats.kendalltau(vector1, vector2)
    return tau

if __name__ == "__main__":
    json1_path = "eval/eval-final.json"
    json_target = "data/benchmark.json"

    f1 = open(json1_path, "r")
    f2 = open(json_target, "r")

    f1_dict = json.load(f1)
    f2_dict = json.load(f2)

    f1_list = []
    f2_list = []

    for k, v in f1_dict.items():
        f1_list.append(v["acc"])

    for k, v in f2_dict.items():
        f2_list.append(float(v["acc"]))

    print(f1_list, f2_list)

    print("person:", pearson(f1_list, f2_list))
    print("kendall", kendalltau(f1_list, f2_list))

    f1.close()
    f2.close()


import numpy as np

from fastats.linear_algebra.det import det
from fastats.linear_algebra.matrix_minor import matrix_minor

def inv(A):
    """
    Returns the inverse of A using the adjoint method.

    >>> import numpy as np
    >>> A = np.array([[4, 3], [3, 2]])
    >>> A_inv = inv(A)
    >>> A_inv
    array([[-2.,  3.],
           [ 3., -4.]])
    """
    m, n = A.shape
    co_factors = np.empty_like(A, dtype=np.float64)

    for i in range(n):
        for j in range(m):
            minor = matrix_minor(A, i, j)
            co_factors[i, j] = ((-1) ** (i + j)) * det(minor)

    adjoint = co_factors.T
    return adjoint / det(A)

if __name__ == '__main__':
    import pytest
    pytest.main([__file__])


from math import acos, atan2, cos, sin, sqrt, pi, tan, atan, fmod
from typing import Sequence, Optional

import numpy as np
from numpy.linalg import norm
from scipy.optimize import root_scalar

from constants import z_hat, mu_earth, eps64

def limit_zero_2pi(angle):
    """
    Limit an angle such that it's between 0 and 2pi
    """
    return fmod(angle + 2 * np.pi, 2 * np.pi)

def cartesian_to_kepler(r: Sequence[float], v: Sequence[float], mu: float = mu_earth):
    """
    Convert Cartesian components to Kepler elements.

    Source:
        Wertz, James Richard. "Mission geometry; orbit and constellation
        design and management." Space Technology Library (2001).

    :param r: Position vector.
    :param v: Velocity vector.
    :param mu: Optional, Standard gravitational parameter, by defaults it's Earth's (3.98600441e14).
    :return: Tuple with the semi-major axis (a), eccentricity (e), inclination (i),
             right ascension of the ascending node (Ω), argument of periapsis (ω), true anomaly (θ).
             eccentric anomaly (E) and the mean anomaly (M).
    """
    # Make sure the arguments are numpy arrays.
    r = np.asarray(r)
    v = np.asarray(v)

    r_norm = norm(r)

    # Orbit angular momentum
    h = np.cross(r, v)
    h_hat = h / norm(h)

    # Ascending node
    n = np.cross(z_hat, h_hat)
    n_norm = norm(n)
    # In case of zero inclination there is no acending node and this
    # cross product will return a zero vector.
    # This is because this particular orientation results in a gibal lock
    # between the RAAN and argument of periapsis.
    # For this case we will define both these angles as zero.
    if n_norm > eps64:
        n_hat = n / n_norm
    else:
        n_hat = None

    # Eccentricity vector and eccentricity
    e_vec = np.cross(v, h) / mu - r / r_norm
    e = norm(e_vec)

    # Semi major axis
    a = 1 / (2 / r_norm - norm(v)**2/mu)

    # Inclination
    i = acos(h[2] / norm(h))

    # Right Ascension of the Ascending Node
    if n_hat is not None:
        raan = atan2(n[1], n[0])
        raan = limit_zero_2pi(raan)
    else:
        raan = 0

    # Argument of periapsis
    # There is no periapsis if the orbit is circular, so set it to zero.
    if n_hat is not None:
        e_hat = (e_vec / norm(e_vec))
        omega = acos(e_hat @ n_hat)
        if (np.cross(n_hat, e_vec) @ h) < 0:
            # omega is between pi and 2pi
            omega = 2 * pi - omega
        omega = limit_zero_2pi(omega)
    else:
        omega = 0

    # Anomalies
    true_anomaly = acos(r @ e_vec / (e * r_norm))
    if np.cross(e_vec, r) @ h < 0:
        # true_anomaly is between pi and 2pi
        true_anomaly = 2 * pi - true_anomaly
    true_anomaly = limit_zero_2pi(true_anomaly)

    eccentric_anomaly = atan2(sqrt(1 - e**2) * sin(true_anomaly), e + cos(true_anomaly))
    eccentric_anomaly = limit_zero_2pi(eccentric_anomaly)

    mean_anomaly = eccentric_anomaly - e * sin(eccentric_anomaly)
    mean_anomaly = limit_zero_2pi(mean_anomaly)

    return a, e, i, raan, omega, true_anomaly, eccentric_anomaly, mean_anomaly

def kepler_to_cartesian(a: float, e: float, i: float, raan: float, omega: float,
                        true_anomaly: Optional[float] = None,
                        eccentric_anomaly: Optional[float] = None,
                        mean_anomaly: Optional[float] = None,
                        mu: float = mu_earth):
    """
    Convert to Kepler elements to Cartesian components. At least one of the anomalies is required,
    the rest can be set to `None`.

    Source:
        Wertz, James Richard. "Mission geometry; orbit and constellation
        design and management." Space Technology Library (2001).

    :param a: Semi-major axis.
    :param e: Eccentricity.
    :param i: Inclination.
    :param raan: Right ascension of the ascending node (Omega).
    :param omega: Argument of periapsis.
    :param true_anomaly: True anomaly.
    :param eccentric_anomaly: Eccentric anomaly.
    :param mean_anomaly: Mean anomaly.
    :param mu: Optional, Standard gravitational parameter, by defaults it's Earth's (3.98600441e14).
    :return: Tuple with the cartesian position (r) and velocity (V).
    """

    # We need the true anomaly for the calculations, check if it was given.
    if true_anomaly is None:
        # If not we can calculate it from the eccentric anomaly, check if it was given.
        if eccentric_anomaly is None:
            # If not we can calculate the eccentric anomaly from the mean anomaly.
            # Check if it was given.
            if mean_anomaly is None:
                # We need at least one of the three anomalies. Raise error.
                raise ValueError('At least one of "True anomaly", "Eccentric anomaly" or "Mean anomaly" '
                                 'is required')
            else:
                eccentric_anomaly = eccentric_anomaly_from_mean_anomaly(e, mean_anomaly)

        # We should have a value for the eccentric anomaly at this point.
        true_anomaly = true_anomaly_from_eccentric_anomaly(e, eccentric_anomaly)

    # Semiparameter
    p = a * (1 - e**2)

    # Position in the perifocal coordinate system (pf)
    r_pf = np.zeros(3)
    r_pf[0] = p * cos(true_anomaly) / (1 + e * cos(true_anomaly))
    r_pf[1] = p * sin(true_anomaly) / (1 + e * cos(true_anomaly))

    # Velocity in the perifocal coordinate system
    v_pf = np.zeros(3)
    v_pf[0] = -sqrt(mu / p) * sin(true_anomaly)
    v_pf[1] = sqrt(mu / p) * (e + cos(true_anomaly))

    # Transformation matrix from the perifocal to inertial coordinate system.
    cr = cos(raan)
    co = cos(omega)
    ci = cos(i)
    sr = sin(raan)
    so = sin(omega)
    si = sin(i)

    c_pf = np.array([[cr*co - sr*so*ci, -cr*so - sr*co*ci, sr*si],
                     [sr*co + cr*so*ci, -sr*so + cr*co*ci, -cr*si],
                     [so*si, co*si, ci]])

    # Position and velocity in the inertial frame
    position = c_pf @ r_pf
    velocity = c_pf @ v_pf

    return position, velocity

def eccentric_anomaly_from_mean_anomaly(e: float, mean_anomaly: float):
    """
    Calculates the eccentric anomaly from the mean anomaly using a
    numerical root-finding method.

    :param e: Eccentricity.
    :param mean_anomaly: Mean anomaly.
    :return: Mean anomaly
    """
    # Equation to solve. This is the equation to calculate the mean
    # anomaly from the eccentric anomaly minus the mean anomaly.
    # So the solution will be at the root.
    def f(e_ano):
        return e_ano - e * sin(e_ano) - mean_anomaly

    # Solve it numerically, we're letting scipy choose a method for us here.
    solution = root_scalar(f, x0=mean_anomaly, bracket=[0, 2 * pi])
    return solution.root

def true_anomaly_from_eccentric_anomaly(e: float, eccentric_anomaly):
    """
    Calculates the true anomaly from the eccentric anomaly

    :param e: Eccentricity.
    :param eccentric_anomaly: Eccentric anomaly.
    :return: True anomaly
    """
    numerator = sqrt(1+e) * sin(eccentric_anomaly/2)
    denominator = sqrt(1-e) * cos(eccentric_anomaly/2)
    return 2 * atan2(numerator, denominator)

#     return 2 * atan(sqrt((1+e)/(1-e)) * tan(eccentric_anomaly / 2))


#Hypothesis Testing - ability to ask and quantitatively answer using data(prob.)
#If you formulate two hypothesis how can you identify the true one
#eg: finding whether a player in game has cheated in die roll
#simple QUESTION -> is tommy rolling too many 6?
#How to find out:
#1) Visualise the data
#2) Reduce or quantify the data
#3) Pose our hypothesis(or visualize)
#4) Calculate

import numpy as np
import matplotlib.pyplot as plt

data = np.loadtxt("loaded_500.txt")

unique,counts = np.unique(data,return_counts=True)
print(unique,counts)
plt.hist(data)
plt.show()
#98 times 6s rolled
#plotting done
num_sixes = (data==6).sum()
num_total = data.size

#Pose Hypothesis
#We have two outcomes, if we have a fair die,when we roll it the probability of
#getting 6  = (1/6). As this is discrete with 2 option,we go for binomial dist.

#we also want to know what is the probability of rolling 6 98 times (or more)

from scipy.stats import binom
n = np.arange(num_total)
prob_n = binom.pmf(n,num_total,1/6)
plt.plot(n,prob_n,label="Prob num")
plt.legend()
plt.show()
#now we know there's some problem

prob_n = binom.pmf(n,num_total,1/6)
plt.plot(n,prob_n,label="Prob num")
plt.axvline(num_total/6,ls="--",lw=1,label="Mean num")
plt.axvline(num_sixes,ls=":",color="#ff7272",label="Obs num")
plt.xlabel("Num sizes rolled out of %d rolls".format(num_total))
plt.ylabel("Probability")
plt.legend()
plt.show()
#can't say much from this plot as the observed and mean number are close
#we want the green area under the curve from 98 to 500
#we can do this using survival function OR

d = binom(num_total,1/6)#d is a binomial distribution configured to probability
#and to num_total
plt.plot(n,d.sf(n))
plt.show()

plt.plot(n,d.sf(n))
plt.axvline(num_sixes,ls="--")
sf = d.sf(num_sixes)
plt.axhline(sf,ls="--")
plt.xlabel("num of sixes")
plt.ylabel("Survival function")
plt.show()
print(f"Only {sf * 100:0.1f}% of the time fair die will roll this many or more sixes")
#You would roll more than this many sixes
# CDF <= k ; so SF > k and not >= k
#from this we can see that person is a liar as we can say that 96.3%, his die is
#loaded


from sympy import *
import sympy
from math import pi,e
from math import pow
import numpy as np

# variables
x = Symbol('x')
y = Symbol('y')
z = Symbol('z')

# model
G = sympy.Matrix([3*x-cos(y*z)-3./2.,
                  4.*x**2-625.*y**2+2.*y-1.,
                  sympy.exp(-x*y)+20.*z+(10.*pi-3.)/3.])

# Objective function
F     = G.T*G/2

# Objective functio gradient
gradF = G.jacobian([x,y,z]).T*G

# Step size
gamma = 0.001
precision = 0.00001
objective_function_eval = 1
# Max iteration number
max_iters = 190
iters = 0

# Initial location
cur_x=np.matrix([0.,0.,0.])
prev_x=np.matrix([0.,0.,0.])

while ( objective_function_eval < 100000    ) & \
      ( objective_function_eval > precision ) & \
      ( iters < max_iters ):

    prev_x[:] = cur_x[:]

    # Gradient of the model:
    dF=gamma * gradF.subs([(x,prev_x[0,0]),(y,prev_x[0,1]),(z,prev_x[0,2])])

    # Place the next point in the gradient descent direction
    for i,val in enumerate(dF):
        cur_x[0,i] -= val

    # Evaluaton the objective function at this value
    objective_function_eval = (F.subs([ (x,cur_x[0,0]), (y,cur_x[0,1]), (z,cur_x[0,2]) ]))[0,0]

    print('-----------------')
    print('Iteration: ' + str(iters))
    print('dF(x1): '    + str(dF))
    print('x0:     '    + str(prev_x))
    print('x1:     '    + str(cur_x))
    print('Objective function evaluation: ' + str(objective_function_eval))

    iters+=1


#Program Written by KOUSHAL K H.
import numpy as np
def forward_propagation(X, W1, b1, W2, b2):
    Z1 = np.dot(W1, X)+b1
    A1 = np.tanh(Z1)
    Z2 = np.dot(W2, A1)+b2
    A2 = 1/(1+np.exp(-Z2))  # Sigmoid
    return A1, A2

def nn_model(X, Y, n_h, num_iterations=250, lr=1.2):
    n_x, n_y, m = X.shape[0], Y.shape[0], Y.shape[1]
    W1 = np.random.randn(n_h, n_x) * 0.01
    b1 = np.zeros((n_h, 1))
    W2 = np.random.randn(n_y, n_h) * 0.01
    b2 = np.zeros((n_y, 1))
    for i in range(0, num_iterations):
        #Forward Pass
        A1, A2 = forward_propagation(X, W1, b1, W2, b2)
        # LOSS Y*log(A2) + (1-Y)*log(1-A2)
        logprobs = np.multiply(np.log(A2), Y)+np.multiply(np.log(1-A2), (1-Y))
        cost = (-1/m)*np.sum(logprobs)
        cost = np.squeeze(cost)  # To make sure dimentions are proper
        #BackwardProp
        dZ2 = A2-Y
        dW2 = (1/m)*np.dot(dZ2, A1.T)
        db2 = (1/m)*np.sum(dZ2, axis=1, keepdims=True)
        dZ1 = np.multiply(np.dot(W2.T, dZ2), (1 - np.power(A1, 2)))
        dW1 = (1/m)*np.dot(dZ1, X.T)
        db1 = (1/m)*np.sum(dZ1, axis=1, keepdims=True)
        #Update parameters
        W1 = W1 - lr*dW1
        b1 = b1 - lr*db1
        W2 = W2 - lr*dW2
        b2 = b2 - lr*db2
        print("Cost after iteration %i: %f" % (i, cost))
    return W1, b1, W2, b2

def predict(X, W1, b1, W2, b2):
    A2 = forward_propagation(X, W1, b1, W2, b2)
    predictions = A2[1] > 0.5
    return predictions

#Generating input for NN can replace next 4 lines with any input of your own
X = np.random.randn(2, 400)*15
Y = np.array([(1 if np.sum(a) > 0 else 0) for a in np.rollaxis(X, 1)])
Y[:70] = 1  # Adding Noice. Remove this line or decrease 70 for more accuracy
Y = Y.reshape(1, 400)
n_x, n_h, n_y = 2, 4, 1
W1, b1, W2, b2 = nn_model(X, Y, n_h)  # Train the model
predictions = predict(X, W1, b1, W2, b2)
print("ACUURACY={}%".format(np.sum((predictions == Y), axis=1)/float(Y.size)*100))
"""
NOTE:
If no of layers is more than 2:
    copy and paste the lines of 2nd layer and make it 3
To increase the accuracy:
    increase num_iterations
If any other input data is used:
    Accuracy may differ.
"""


import os
import random
import math
import sys
from Crypto.PublicKey import RSA
from sympy import *

sys.setrecursionlimit(2000)

####Prime Functions####
def millerRabin(m, k):
    """
    Miller Rabin pseudo-prime test
    return True means likely a prime, (how sure about that, depending on k)
    return False means definitely a composite.
    """
    s=1
    d = (m-1)/2
    while d%2 == 0:
        d /= 2
        s += 1
    for r in range(0,k):
        rand_num = random.randint(1,m-1)
        y = pow(rand_num, d, m)
        prime = False
        if (y == 1):
            prime = True
        for i in range(0,s):
            if (y == m-1):
                prime = True
                break
            else:
                y = (y*y)%m
        if not prime:
            return False
    return True

def findAPrime(a, b, k):
    """Return a pseudo prime number roughly between a and b,
    (could be larger than b). Raise ValueError if cannot find a
    pseudo prime after 10 * ln(x) + 3 tries. """
    x = random.randint(a, b)
    for i in range(0, int(10 * math.log(x) + 3)):
        if millerRabin(x, k):
            return x
        else:
            x += 1

    raise ValueError

####Modular Arithmetics####
def gcd(x, y):
    """returns the Greatest Common Divisor of x and y"""
    while y != 0:
        (x, y) = (y, x % y)
    return x

def egcd(a, b):
    """return a tuple of three values: x, y and z, such that x is
    the GCD of a and b, and x = y * a + z * b"""
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modInv(a, m):
    """returns the multiplicative inverse of a in modulo m as a
    positive value between zero and m-1"""
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

def modExp(a, d, n):
    """returns a ** d (mod n)"""
    assert d >= 0
    assert n >= 0
    base2D = int2baseTwo(d)
    base2DLength = len(base2D)
    modArray = []
    result = 1
    for i in range(1, base2DLength + 1):
        if i == 1:
            modArray.append(a % n)
        else:
            modArray.append((modArray[i - 2] ** 2) % n)
    for i in range(0, base2DLength):
        if base2D[i] == 1:
            result *= base2D[i] * modArray[i]
    return result % n

def findInvPow(x,n):
    """Finds the integer component of the n'th root of x,
    an integer such that y ** n <= x < (y + 1) ** n.
    """
    high = 1
    while high ** n < x:
        high *= 2
    low = high/2
    while low < high:
        mid = (low + high) // 2
        if low < mid and mid**n < x:
            low = mid
        elif high > mid and mid**n > x:
            high = mid
        else:
            return mid
    return mid + 1

def int2baseTwo(x):
    """x is a positive integer. Convert it to base two as a list of integers
    in reverse order as a list."""
    assert x >= 0
    bitInverse = []
    while x != 0:
        bitInverse.append(x & 1)
        x >>= 1
    return bitInverse

####RSA Functions#####
class EmbeddedRSA:
    def __init__(self, rsaKey=None):
        if rsaKey == None:
            rsaKey = self._newKey(2 ** 1023, (2 ** 1024) - 1, 100)
        self.key = rsaKey

    def _newKey(self, a, b, k):
        try:
            p = findAPrime(a, b, k)
            while True:
                q = findAPrime(a, b, k)
                if q != p:
                    break
        except:
            raise ValueError
        n = p * q
        m = (p - 1) * (q - 1)
        while True:
            # Optimize for decryption speed
            limit = findInvPow(n, 4) >> 16
            d = random.randint(1, limit)
            if gcd(d, m) == 1:
                break
        e = modInv(d, m)
        return {'n': n, 'e':e, 'd':d}

    def Encrypt(self, msg):
        msgNum = int(msg.encode('hex'), 16)
        msgCrypt = modExp(msgNum, self.key['e'], self.key['n'])
        return msgCrypt

    def Decrypt(self, num):
        msgNum = modExp(num, self.key['d'], self.key['n'])
        msg = ''.join([chr(int(hex(msgNum)[i:i+2], 16)) for i in range(2,len(hex(msgNum))-1,2)])
        return msg

    def GetKey(self):
        return self.key

####MY FUNCTIONS####
def ContFracConv(num, denom):
  frac = []
  numArray = []
  denomArray = []

  q = num // denom
  r = num % denom
  frac.append(q)

  while r != 0:
    num, denom = denom, r
    q = num // denom
    r = num % denom
    frac.append(q)

  for i in range(len(frac)):
    if i == 0: # First Fraction
      numi = frac[i]
      denomi = 1
    elif i == 1: # Second Fraction
      numi = frac[i]*frac[i-1] + 1
      denomi = frac[i]
    else: # All other Fractions
      numi = frac[i]*numArray[i-1] + numArray[i-2]
      denomi = frac[i]*denomArray[i-1] + denomArray[i-2]

    numArray.append(numi)
    denomArray.append(denomi)
    yield (numi, denomi)

def phiN(e, d, k):
  return ((e * d) - 1)//k

def wienerAttack(conv):
  for k, d in  conv:
    if k == 0:
      continue

    # Quadratic Equation is :
    # x^2 - phi*x + n = 0
    phi = phiN(e, d, k)

    x = Symbol('x', integer=True)
    roots = solve(x**2 + (phi - n - 1)*x + n, x)

    if len(roots) == 2:
      pp, pq = roots
      # pp = possible p
      # pq = possible q
      if pp*pq == n:
        if modInv(d, (pp - 1) * (pq - 1)) == e:
          rsa = EmbeddedRSA({'n': n, 'e':e, 'd':d})
          plaintext = rsa.Decrypt(ciphertext)
          return plaintext

if __name__ == "__main__":
  # Read flag.crypt
  # First line is the Public Encryption Key - E
  # Second line is the Modulous - N
  # Third line is the CipherText
  file = open('flag.crypt', 'r')
  lines = file.readlines()

  # Assign values to the lines read out from the file
  e = int(lines[0])
  n = int(lines[1])
  ciphertext = int(lines[2])

  # Find the Convergents and then run WeinersAttack
  print("Your decrypted message is :\n",wienerAttack(ContFracConv(e,n)))


from sympy import symbols
from sympy import Matrix
from sympy import diff
from sympy import linsolve

x, y = symbols('x y')
f1 = x**2 + y**2 - 2.1
f2 = (x**2) / 2 - y**2 - 0.15269
eps = 0.01

def getVectorNorm(v):
    max = abs(v[0])
    for x in v:
        max = abs(x) if abs(x) > max else max
    return max

_J = Matrix([[diff(f1, x), diff(f1, y)], [diff(f2, x), diff(f2, y)]])

def J(x1, x2):
    return _J.subs([(x, x1), (y, x2)])

def F(x1, x2):
    return (Matrix)([f1.subs([(x, x1), (y, x2)]), f2.subs([(x, x1), (y, x2)])])

# Обычный метод Ньютона
count = 0
accuracy = eps + 1
x0 = [1, 1]
while accuracy > eps and count < 50:
    sigmaX = linsolve((J(x0[0], x0[1]), -F(x0[0], x0[1])))
    x1 = [sigmaX.args[0][0] + x0[0], sigmaX.args[0][1] + x0[1]]
    accuracy = getVectorNorm(sigmaX.args[0])
    x0 = x1
    count += 1

print(x0)

# Модифицированный метод Ньютона
count = 0
accuracy = eps + 1
x0 = [1, 1]
x00 = x0
while accuracy > eps and count < 50:
    sigmaX = linsolve((J(x00[0], x00[1]), -F(x0[0], x0[1])))
    x1 = [sigmaX.args[0][0] + x0[0], sigmaX.args[0][1] + x0[1]]
    accuracy = getVectorNorm([x1[0] - x0[0], x1[1] - x0[1]])
    x0 = x1
    count += 1

print(x0)

# Разностный метод Ньютона
h = 0.5

def A(x1, x2):
    return (Matrix)([[(f1.subs([(x, x1 + h), (y, x2)]) - f1.subs([(x, x1), (y, x2)])) / h, (f1.subs([(x, x1 + h), (y, x2)]) - f1.subs([(x, x1), (y, x2)])) / h],
                     [(f2.subs([(x, x1 + h), (y, x2)]) - f2.subs([(x, x1), (y, x2)])) / h, (f2.subs([(x, x1 + h), (y, x2)]) - f2.subs([(x, x1), (y, x2)])) / h]])

count = 0
accuracy = eps + 1
x0 = [1, 1]
while accuracy > eps and count < 50:
    sigmaX = linsolve((A(x0[0], x0[1]), -F(x0[0], x0[1])))
    x1 = [sigmaX.args[0][0] + x0[0], sigmaX.args[0][1] + x0[1]]
    accuracy = getVectorNorm([x1[0] - x0[0], x1[1] - x0[1]])
    x0 = x1
    count += 1

print(x0)


from math import cos, sin
from numpy import array, eye
from numpy.linalg import matrix_power, norm

def skew(k):
    return array([[0.0, -k[2], k[1]], [k[2], 0.0, -k[0]], [-k[1], k[0], 0.0]])

def normalize(k):
    return k / norm(k)

def rodrigues(k, theta):
    """
    Rodrigues rotation formula

    :param k: Axis of rotation
    :param theta: Angle of ratation
    :return: Rotation matrix associated with (v, theta)
    """

    k_normalized = normalize(k)

    return eye(3) + sin(theta) * skew(k_normalized) + (1 - cos(theta)) \
           * matrix_power(skew(k_normalized), 2)


import numpy as np
from scipy.integrate import quad

def payoff(payoff, S,K):
    """
    Payoff function for European options
    """
    if payoff == "call":
        return np.maximum( S - K, 0 )
    elif payoff == "put":
        return np.maximum( K - S, 0 )

def fourierPricer(K,S0,payoff,r,T,cf,xmax=np.inf,limit=1000):

        """
        Lewis integral for European options
        K = strike
        S0 = initial value
        payoff = call/put
        cf = characteristic function
        """
        k = np.log(S0/K)+r*T

        integrand = lambda u: np.real( np.exp(u*k*1j) * cf(u - 0.5j) ) * 1/(u**2 + 0.25)

        call = S0 - np.sqrt(S0 * K) * np.exp(-(r)*T)/np.pi * \
               quad(integrand, 0, xmax, limit=limit)[0]

        if payoff=="call":
            return call

        if payoff=="put":
            return call - S0 + K*np.exp(-(r)*T) # Call-put parity


"""
    Given the real and imaginary parts of a complex number,
    determine if it is a candidate for membership in the Mandelbrot
    set given a fixed number of iterations.

    PSEUDO CODE - Wikipedia

    for each pixel (Px, Py) on the screen do
    x0 = scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1))
    y0 = scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1, 1))
    x := 0.0
    y := 0.0
    iteration := 0
    max_iteration := 1000
    while (x*x + y*y ≤ 2*2 AND iteration < max_iteration) do
        xtemp := x*x - y*y + x0
        y := 2*x*y + y0
        x := xtemp
        iteration := iteration + 1

    color := palette[iteration]
    plot(Px, Py, color)

    Author: Harivinay V
    github: https://github.com/M87K452b

"""
import numpy as np
import matplotlib.pyplot as plt
from numba import jit
from timeit import default_timer as timer

N = 250
r = 4
nx = 2 * 1920
ny = 2 * 1080
xmin = -2.5  # -0.7721582 #Ocean#-0.7318744 #Snowflake#-0.37465401 #Default#-0.05
xmax = 1.0  # -0.7701566 #Ocean#-0.7318878 #Snowflake#-0.37332411 #Default##-0.55
ymin = -1.0  # 0.1143422 #Ocean#0.1845309 #Snowflake#0.659227668 #Default##0.5
ymax = 1.0  # 0.1163601 #Ocean#0.1845413 #Snowflake#0.66020767 #Default##1.0
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
fractal = np.zeros([nx, ny])

@jit(nopython=True)
def mandelbrot(x0, y0, N, r):
    xold = 0
    yold = 0
    z = 0
    rnew = x0**2 + y0**2
    while rnew < r and z < N:
        xnew = xold**2 - yold**2 + x0
        ynew = 2 * xold * yold + y0

        xold = xnew
        yold = ynew

        rnew = xnew**2 + ynew**2

        z += 1
    return z

@jit(nopython=True)
def fractal_plot(xmin, xmax, ymin, ymax, fractal, N, r):
    for i in range(nx):
        for j in range(ny):
            z = mandelbrot(x[i], y[j], N, r)
            fractal[i, j] = z

start = timer()
fractal_plot(xmin, xmax, ymin, ymax, fractal, N, r)
compute_time = timer() - start

print("Mandelbrot computed in %f s" % compute_time)
f = plt.figure(figsize=(12, 16))
plt.imshow(fractal.T, cmap='viridis')
plt.axis('off')
plt.show()

f.savefig("galaxy_spiral.png", bbox_inches='tight', dpi=800)


# -*- coding: utf-8 -*-
"""
Created on Fri Jan 10 16:23:23 2020

@author: Dr. Dr. Danny E. P. Vanpoucke
@web   : https://dannyvanpoucke.be
"""
import pandas as pd
import numpy as np

def Lin1DFunc(x:list, intercept: float, slope: float)-> list:
    """
    Create linear data: y=slope*x + intercept
    """
    return list(np.dot(slope,xi) + intercept for xi in x)

def LinNDFunc(x:list, intercept: float, slope: list)-> list:
    """
    Create linear data: y=slope1*x1+slope2*x2+...+slopeN*xN + intercept
    """
    return list(np.dot(slope,xi) + intercept for xi in x)

def LinPolyNDFunc(x:list, intercept: float, slope: list)-> list:
    """
    Create linear Polynomial data: y=slope1*x^1+slope2*x^2+...+slopeN*x^N + intercept
    """
    npow=[]
    for i in range(len(slope)):
        npow.append(i+1)

    features=[]
    for xi in x:
        features.append(np.power(xi,npow))
#    print("=============================")
#    print("slope=",slope)
#    print("x    =",x)
#    print("xi   =",features)
#    print("intercept=",intercept)
#    print("dot=",list(np.dot(slope,xi) for xi in features))
#    print("=============================")
    return list(np.dot(slope,xi) + intercept for xi in features)

def Exp1DFunc(x:list, intercept: float, slope: float)-> list:
    """
    Create a 1D exponential dataset: exp(slope*x) - intercept
    """
    return list(np.exp(slope*xi)-intercept for xi in x)

def SinFunc(x:list, intercept: float, slope: float)-> list:
    """
    Create a 1D sine dataset: sin(slope*x) + intercept
    """
    return list(np.sin(slope*xi)+intercept for xi in x)

def Create1DData(Func1D, length:int, intercept: float=0.0, coefficient: float=1.0,
             lbound: float=0.0, ubound: float=1.0,
             dist:str = None, dwidth: float=0.2) -> pd.DataFrame:
    """
    Create a noisy 1D dataset using a specific function.

    parameters:
        - length : size of the dataset
        - intercept : the intercept parameter of the function
        - coefficient: the coefficient (slope, factor,...) of the function
        - lbound and ubound: lower and uper bound of the x-range.
        - dist: Type of distribution. String = 'normal' or 'uniform' DEFAULT=normal
        - dwidth : the width of the distribution, Default=0.2
        - Func1D : 1-D function to generate data with. This function returns a list of results.
    """
    #create two lists of the correct length, filled with linear related data
    if (dist==None):
        dist='normal'
    wd=ubound-lbound
    x=lbound+np.random.random_sample(size=length)*wd
    y=list()

    if (dist=='normal'):
        rdn=(np.random.normal(size=length))*dwidth #gaussian  distribution with mean=0, and stdev=1
    elif (dist=='uniform'):
        rdn=(np.random.uniform(size=length)-1.0)*dwidth #uniform distribution from 0..1
    else:
        rdn=(np.random.normal(size=length))*dwidth #gaussian  distribution with mean=0, and stdev=1

    y=Func1D(x,intercept,coefficient)+rdn

    #transform in list of tuples
    xy_tuples=list(zip(x,y))
    #and convert into dataframe
    df=pd.DataFrame(xy_tuples, columns = ['Feat_x','Target_y'])

    return df

#def CreateExponential1D(length:int, intercept: float=0.0, scaleX: float=1.0, lbound: float=0.0, ubound: float=1.0) -> pd.DataFrame:
#    """
#    Create a (gaussian) noisy 1D exponential dataset: exp(slope*x) - intercept
#
#    parameters:
#        - length : size of the dataset
#    """
#    #create two lists of the correct length, filled with linear related data
#    wd=ubound-lbound
#    x=lbound+np.random.random_sample(size=length)*wd
#    y=list()
#
#    rdn=(np.random.normal(size=length))*0.2 #gaussian  distribution with mean=0, and stdev=1
#    y=Exp1DFunc(x,intercept,scaleX)+rdn
#
#    #transform in list of tuples
#    xy_tuples=list(zip(x,y))
#    #and convert into dataframe
#    df=pd.DataFrame(xy_tuples, columns = ['Feat_x','Target_y'])
#
#    return df

#def CreateSin1D(length:int, intercept: float=0.0, scaleX: float=1.0, lbound: float=0.0, ubound: float=1.0) -> pd.DataFrame:
#    """
#    Create a (gaussian) noisy 1D sine dataset: sin(slope*x) + intercept
#
#    parameters:
#        - length : size of the dataset
#    """
#    #create two lists of the correct length, filled with linear related data
#    wd=ubound-lbound
#    x=lbound+np.random.random_sample(size=length)*wd
#    y=list()
#
#    rdn=(np.random.normal(size=length))*0.05 #gaussian  distribution with mean=0, and stdev=1
#    y=SinFunc(x,intercept,scaleX)+rdn
#
#    #transform in list of tuples
#    xy_tuples=list(zip(x,y))
#    #and convert into dataframe
#    df=pd.DataFrame(xy_tuples, columns = ['Feat_x','Target_y'])
#
#    return df

def Lin3DFunc(x:list, intercept: float, slope: list)-> list:
    return slope[0]*x[0,:]+slope[1]*x[1,:]+slope[2]*x[2,:]+intercept

def CreateLinear3D(length:int, intercept: float=0.0, slope: list=[1.0,1.0,1.0], lbound: float=0.0, ubound: float=1.0) -> pd.DataFrame:
    """
    Create a noisy 3D linear dataset

    parameters:
        - length : size of the dataset
    """
    #create two lists of the correct length, filled with linear related data
    wd=ubound-lbound
    x1=lbound+np.random.random_sample(size=length)*wd
    x2=lbound+np.random.random_sample(size=length)*wd
    x3=lbound+np.random.random_sample(size=length)*wd
    x=list()
    x.append(x1)
    x.append(x2)
    x.append(x3)
    y=list()
    rdn=(np.random.normal(size=length))*0.2 #gaussian  distribution with mean=0, and stdev=1
    y=Lin3DFunc(x,intercept,slope)+rdn

    #transform in list of tuples
    xy_tuples=list(zip(x1,x2,x3,y))
    #and convert into dataframe
    df=pd.DataFrame(xy_tuples, columns = ['Feat_x1','Feat_x2','Feat_x3','Target_y'])

    return df


#!/usr/bin/env python
"""
Smoothing by regularization of data using constraints on the smooth
solution.
"""
from __future__ import print_function

import numpy as np
from matplotlib.pyplot import *
from scikits import datasmooth as ds

# create simulated data
npts = 100
xmin = 0
xspan = 1
x = np.linspace(xmin,xmin+xspan,npts)
# give variability to x if desired
x = x + xspan/npts*(np.random.rand(npts)-0.5)
x[0] = xmin # force x[1] to be the defined xmin

yt = 4*x/(1+4*x)
ytp = 4/(4*x+1)**2
yt2p = -32/(4*x+1)**3
stdev = 5e-2*np.max(yt)
y = yt + stdev*np.random.randn(npts)

# perform the smoothing
d = 4
lmbd = 1e-2

# constraints:
# set point 1 at 0
Aeq = np.zeros((1,npts))
Aeq[(0,0)] = 1.0
beq = np.array([0.0])
# make sure yhat is always increasing
D = ds.derivative_matrix(x,1)
bl = np.zeros((npts-1,1))
# make sure that yhat is concave
D2 = ds.derivative_matrix(x,2)
bu2 = np.zeros((npts-2,1))
Ain = np.vstack((-D,D2))
bin = np.vstack((bl,bu2))

yh = ds.smooth_data_constr(x,y,d,lmbd,(Ain,bin),(Aeq,beq))

print('scaled regularization parameter =', lmbd)
print('standard deviation =', np.std(y-yh,ddof=1))

yp = np.dot(ds.derivative_matrix(x,1),y)
y2p = np.dot(ds.derivative_matrix(x,2),y)
yhp = np.dot(ds.derivative_matrix(x,1),yh)
yh2p = np.dot(ds.derivative_matrix(x,2),yh)

figure(1)
cla()
plot(x,y,'ow',x,yh,'-b',x,yt,'-r')
legend(['scattered','smoothed','true'],loc='best',numpoints=1)
xlabel('x')
ylabel('y')
show()
draw()

figure(2)
cla()
plot(x[1:],yp,'ow',x[1:],yhp,'-b',x,ytp,'-r')
ylim( np.min(yhp)-np.abs(np.min(yhp)), np.max(yhp)*2 )
legend(['scattered','smoothed','true'],loc='best',numpoints=1)
xlabel('x')
ylabel("y'")
show()
draw()

figure(3)
cla()
plot(x[1:-1],y2p,'ow',x[1:-1],yh2p,'-b',x,yt2p,'-r')
ylim( np.min(yh2p)-np.abs(np.min(yh2p)), np.max(yh2p)+np.abs(np.min(yh2p)) )
legend(['scattered','smoothed','true'],loc='best',numpoints=1)
xlabel('x')
ylabel("y''")
show()
draw()


import numpy as np
from helpers import *
from costs import *
from gradients import *

def gradient_descent(y, tx, initial_w, max_iters, gamma, verbose = False):
    """
    Gradient descent algorithm.

    Parameters
    ----------
    y : Vector
        Output.
    tx : Matrix
        Input.
    initial_w : Vector
        Initial weights.
    max_iters : Scalar
        Maximum number of iterations.
    gamma : Scalar
        Learning rate.
    verbose : TYPE, optional
        Print GD steps. The default is False.

    Returns
    -------
    vector of loss and vector of weights for each iteration.

    """
    # Define parameters to store w and loss.
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = mse_grad(y, tx, w)
        loss = mse(y, tx, w)
        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}, w0={w0}, w1={w1}".format(
                bi=n_iter, ti=max_iters - 1, l=loss, w0=w[0], w1=w[1]))

    return losses, ws

def gradient_descent_logistic(y, tx, initial_w, max_iters, gamma, verbose = False):
    """
    Gradient descent algorithm for logistic regression.

    Parameters
    ----------
    y : Vector
        Output.
    tx : Matrix
        Input.
    initial_w : Vector
        Initial weights.
    max_iters : Scalar
        Maximum number of iterations.
    gamma : Scalar
        Learning rate.
    verbose : Boolean, optional
        Print GD steps. The default is False.

    Returns
    -------
    losses : Vector
        Loss for each iteration.
    ws : Vector
        Weight vector for each iteration.

    """
    # Define parameters to store w and loss.
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = logistic_grad(y, tx, w)
        loss = logistic_error(y, tx, w)

        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}, w0={w0}, w1={w1}".format(
                bi=n_iter, ti=max_iters - 1, l=loss, w0=w[0], w1=w[1]))

    return losses, ws

def reg_gradient_descent_logistic(y, tx, initial_w, max_iters, gamma, lambda_, reg, verbose=False,
                                  early_stopping=True, tol=0.0001, patience=5):
    """
    Regularized logistic regression with gradient descent.

    Parameters
    ----------
    y : Vector
        Output.
    tx : Matrix
        Input.
    initial_w : Vector
        Inital weights.
    max_iters : Scalar
        Maximum iterations.
    gamma : Scalar
        Learning rate.
    lambda_ : Scalar
        Regularization.
    reg : Integer
        l1 or l2 regularizarion.
    verbose : Boolean, optional
        Print steps. The default is False.
    early_stopping : Boolean, optional
        Stop early if no improvement in loss. The default is True.
    tol : Scalar, optional
        Minimum the loss needs to decrease by. The default is 0.0001.
    patience : Integer, optional
        Change needs to be seen in this number of iterations. The default is 10.

    Returns
    -------
    losses : Vector
        History of losses.
    ws : Vector
        History of weights.

    """

    # Define parameters to store w and loss.
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = reg_logistic_grad(y, tx, w, lambda_, reg)
        loss = reg_logistic_error(y, tx, w, lambda_, reg)

        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}, w0={w0}, w1={w1}".format(
                bi=n_iter, ti=max_iters - 1, l=loss, w0=w[0], w1=w[1]))

        if (early_stopping) and (n_iter > patience):
            # Check if loss has improved by tol in last patience iters
            l_pat = reg_logistic_error(y, tx, ws[-patience], lambda_, reg)
            l_1 = reg_logistic_error(y, tx, ws[-1], lambda_, reg)
            try: # If we get invaluid value in the log.
                if ((l_pat - l_1) < tol):
                    print(f"Stopped after {n_iter} it.")
                    break
            except exception as e:
                print(e)
                break

    return losses, ws


#!/usr/bin/env python
# coding: utf-8

# # Example of performing linear least squares fitting

# In[11]:

get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt
import numpy as np

# ### Now let's generate some random data about a trend line

# In[12]:

#set a random number seed
np.random.seed(119)

#set number of data points
npoints = 50

#set x
x = np.linspace(0,10.,npoints)

#set slope, intercept, and scatter rms
m = 2.0
b = 1.0
sigma = 2.0

#generate y points
y = m*x + b + np.random.normal(scale=sigma,size=npoints)
y_err = np.full(npoints,sigma)
#npfull makes an array that is full to npoints with sigma at every value

# ### Let's just plot the data first

# In[13]:

f = plt.figure(figsize=(7,7))
plt.errorbar(x,y,sigma,fmt='o')
plt.xlabel('x')
plt.ylabel('y')

# ### Method #1, polyfit()

# In[14]:

m_fit, b_fit = np.poly1d(np.polyfit(x,y,1,w=1./y_err)) #weight with uncertainties
#m_fit, b_fiot means that we are taking the first two values given by np.poly1 and assigning them to m and b fit
#polyfit provides polynomial fit to y as a function of x to the order of 1
#only provide one power for "inverse variance weighting"?
print(m_fit, b_fit)

y_fit = m_fit * x + b_fit

# In[15]:

f = plt.figure(figsize=(7,7))
plt.errorbar(x,y,yerr=y_err, fmt='o',label='data')
plt.plot(x,y_fit,label='fit')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc=2,frameon=False)

# ### Method #2, scipy + optimize

# In[17]:

#scipy method is more flexible,
#but the polyfit gives an analytical (formal) answer

# In[18]:

#import optimize from scipy
from scipy import optimize

#define the function to fit
def f_line(x,m,b):
    return m*x + b

#perform the fit
params, params_cov = optimize.curve_fit(f_line,x,y,sigma=y_err)

m_fit = params[0]
b_fit = params[1]
print(m_fit,b_fit)

# ### We can perform much more complicated fits

# In[28]:

#redefine x and y
npoints = 50
x = np.linspace(0,2*np.pi,npoints)

#make y a complicated function
a = 3.4
b = 2.1
c = 0.27
d = -1.3
sig = 0.6

y = a * np.sin(b*x + c) + d + np.random.normal(scale=sig, size=npoints)
y_err = np.full(npoints,sig)

f = plt.figure(figsize=(7,7))
plt.errorbar(x,y,yerr=y_err,fmt='o')
plt.xlabel('x')
plt.ylabel('y')

#He redefined y_err I think, but not sure

# In[29]:

#import optimize from scipy
from scipy import optimize

#define the function to fit
def f_line(x, a, b, c, d):
    return a * np.sin( b*x + c) + d

#perform the fit
params, params_cov = optimize.curve_fit(f_line,x,y,sigma=y_err,p0=[1,2.,0.1,-0.1])

a_fit = params[0]
b_fit = params[1]
c_fit = params[2]
d_fit = params[3]

print(a_fit,b_fit,c_fit,d_fit)

y_fit = a_fit * np.sin(b_fit * x + c_fit) + d_fit

# In[30]:

f = plt.figure(figsize=(7,7))
plt.errorbar(x,y,yerr=y_err,fmt='o',label='data')
plt.plot(x,y_fit,label='fit')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc=0,frameon=False)

# In[ ]:


# Mathematics > Linear Algebra Foundations > Eigenvalue of matrix #4
# Basic problems related to eigenvalues.
#
# https://www.hackerrank.com/challenges/eigenvalues-of-matrix-4/problem
#

import numpy as np

A = np.matrix([[ 2, -1],
               [-1,  2]])

I = np.identity(np.linalg.matrix_rank(A))

print(np.linalg.eigvals(np.linalg.inv(A)))

print(np.linalg.eigvals(A - 4 * I))


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

figure = plt.figure()
# axes = Axes3D(figure)

R = 10
t = np.linspace(-np.pi, np.pi, 100)
X = R * np.cos(t)
Y = R * np.sin(t)
# X, Y = np.meshgrid(X, Y)
# Z = R**2 - X**2 - Y**2
X = R * np.outer(np.cos(t), np.sin(t))
Y = R * np.outer(np.sin(t), np.sin(t))
Z = R * np.outer(np.ones(np.size(t)), np.cos(t))
"""
    不明白为什么这样绘制出来的是球形
"""
axes = figure.add_subplot(121, projection='3d')
axes.plot_surface(X, Y, Z, rstride=4, cstride=4, color='b')
axes = figure.add_subplot(122, projection='3d')
axes.plot_wireframe(X, Y, Z)
plt.show()


import numpy as np
import random

def sigmoid(x): # Definition of sigmoid
    return 1 / (1 + np.exp(-x))

def dsigmoid(y): # definition of derivative of sigmoid, in a format to solve incompatability issues with numpy matricies
    return np.multiply(y, 1.0 - y)

def train_autoencoder(input_data, desired_output, input_layer_size, hidden_layer_size, output_layer_size, L, alpha, iterations):
    '''
    Initial method to train and validate the neural network as an auto encoder by updating weight and bias matrices between layers

    Inputs: training / input data (as matrix); desired output (as matrix); size of input, hidden, output layers
            weight decay parameter lambda (L); learning rate alpha (a); total iterations
    Outputs: final output layer, average sum of errors
    '''
    np.random.seed(4) # Keep random seed constant for testing purposes
    m = input_data.shape[0] # number of training examples

    # Initialize random weights with mean 0
    input_weights = 2 * np.random.random((input_layer_size, hidden_layer_size)) - 1
    hidden_weights = 2 * np.random.random((hidden_layer_size, output_layer_size)) - 1

    # Initialize bias 'matrices' of zeros
    input_bias = np.zeros((input_layer_size,hidden_layer_size))
    hidden_bias = np.zeros((input_layer_size,output_layer_size))

    # Iterate to train weight matrices
    for i in range(0, iterations):

        # input data = input layer; contains all training data, simultaneously
        input_layer = input_data

        # Given the training data and weight matrix, tranform via the dot product, add bias matrix
        input_transform = np.dot(input_layer, input_weights) + input_bias
        # Pass through the activation function
        hidden_layer = sigmoid(input_transform)

        # Again, add bias and propagate predicted values through the weights between hidden + output layers
        hidden_transform = np.dot(hidden_layer, hidden_weights) + hidden_bias
        output_layer = sigmoid(hidden_transform)

        # Get the difference between the desired output and the result from the autoencoder
        output_layer_error = desired_output - output_layer

        # Print the error every so often
        if (i == 0 or (i % (iterations / 10)) == 0):
            print('Iteration: ' + str(i) + '\n\tAverage Error: ' + str(np.mean(np.abs(output_layer_error))) + '\n')

        # Take the error weighted derivative to avoid changing weights that lead to post-sigmoid values close to either 0 or 1
        # Because these are probably starting to converge -> focus effort on changing ambiguous (~ 0.5) values
        output_layer_delta = output_layer_error * dsigmoid(output_layer)

        # Backpropagate to determine the error in the hidden layer
        hidden_layer_error = output_layer_delta.dot(hidden_weights.T)

        # Another error-weighted derivative
        hidden_layer_delta = hidden_layer_error * dsigmoid(hidden_layer)

        # Update the weights for each layer in batch
        hidden_weights += alpha * (((1 / m ) * (hidden_layer.T.dot(output_layer_delta))) + L * hidden_weights)
        hidden_bias += (alpha / m) * output_layer_delta

        input_weights += alpha * (((1 / m ) * (input_layer.T.dot(hidden_layer_delta))) + L * input_weights)
        input_bias += (alpha / m) * hidden_layer_delta

    print('After training the encoder for ' + str(iterations) + ' iterations, \n\tAverage Error: ' + str(np.mean(np.abs(output_layer_error))))
    print('input data: ')
    print(input_data)
    print('output data: ')
    print(output_layer)
    return output_layer, np.mean(np.abs(output_layer_error))

def train_neuralnet(pos_data, neg_data, input_layer_size, hidden_layer_size, L, alpha, iterations, convergence_criteria, print_output = False):
    '''
    Method to train the neural network using both positive and negative training data.
    For a given number of iterations, update weight and bias matrices between layers.
    Serves as a wrapper function for the actual unput of data into the network as defined by its weight and bias matrices.

    Inputs: positive / negative training data; size of input, hidden, output layers
            weight decay parameter lambda (L); learning rate alpha (a); total iterations; convergence criteria delta (improvement in average error)
    Outputs: avg positive error, avg negative error, input & hidden weight matricies, input and hidden bias matrices
    '''
    np.random.seed(4) # Keep random seed constant for testing purposes
    m = pos_data.shape[0] # number of training examples (assume for this case, equal amount of positive and negative data)

    # Initialize random weights with mean 0
    input_weights = 2 * np.random.random((input_layer_size, hidden_layer_size)) - 1
    hidden_weights = 2 * np.random.random((hidden_layer_size, 1)) - 1 # Output layer size is always 1

    # Initialize bias nodes
    input_bias = np.zeros((1,hidden_layer_size)) # One input at a time
    hidden_bias = np.zeros((1, 1)) # Output layer size 1

    # Keep track of running error
    pos_error = 1.0
    neg_error = 1.0

    # Train network one data point at a time, and alternate between positive and negative examples
    for i in range(0, iterations):
        training_order = random.sample(range(0,m), m)  # pick random order to train each iteration

        # Keep track of error accrued every iteration
        pos_errors = []
        neg_errors = []

        for j in training_order: # Train with both positive and negative data iteratively for entirety of both datasets
            sample_input = np.asmatrix(pos_data[j]) # Test one positive input
            last_pos_error, input_weights, hidden_weights, input_bias, hidden_bias = NN_input(sample_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, True, True)

            sample_input = np.asmatrix(neg_data[j]) # Test one negative input
            last_neg_error, input_weights, hidden_weights, input_bias, hidden_bias = NN_input(sample_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, False, True)

            pos_errors.append(last_pos_error)
            neg_errors.append(last_neg_error)

        # Calculate average errors, delta from training cycle
        avg_pos_error = sum(pos_errors) / float(len(pos_errors))
        avg_neg_error = sum(neg_errors) / float(len(neg_errors))
        pos_delta = np.abs(avg_pos_error - pos_error) / pos_error
        neg_delta = np.abs(avg_neg_error - neg_error) / neg_error

        if print_output and (i == 0 or (i % (iterations / 100)) == 0):  # Print progress every so often
            print('Iteration: ' + str(i) + '\n\tAvg Pos Error: ' + str(avg_pos_error) + '\n\tAvg Neg Error: ' + str(avg_neg_error))
            print('\tPos Err Delta: ' + str(pos_delta))
            print('\tNeg Err Delta: ' + str(neg_delta))

        if print_output and pos_delta <= convergence_criteria and neg_delta <= convergence_criteria: # Successive iterations have marginal gains; stop training
            print('Convergence criteria of ' + str(convergence_criteria) + ' met at iteration ' + str(i))
            print('Average Positive Error: ' + str (avg_pos_error))
            print('Average Negative Error: ' + str (avg_neg_error))
            return avg_pos_error, avg_neg_error, input_weights, hidden_weights, input_bias, hidden_bias
        else: # Keep going until convergence or iterations are complete
            pos_error = avg_pos_error
            neg_error = avg_neg_error

    return pos_error, neg_error, input_weights, hidden_weights, input_bias, hidden_bias

def test_neuralnet(pos_data, neg_data, input_weights, hidden_weights, input_bias, hidden_bias, m , L, alpha):
    '''
    Basic funciton to test the average errors in the ANN with both positive and negative test data

    Inputs: positive & negative test data (as 34-bit vectors in rows; sample count must be the same), input & hidden weight matrices, input and bias weight matrices, m, L, alpha
    Outputs: total average positive and negative errors resulting from input data being run through network
    '''
    pos_errors = [] # Store all accrued errors
    neg_errors = []

    # Iterate through all positive and negative test data
    for index in range(0,pos_data.shape[0]):
        pos_input = np.asmatrix(pos_data[index])
        neg_input = np.asmatrix(neg_data[index])

        # Run through neural network, get resulting error
        last_pos_error = NN_input(pos_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, True, False)
        last_neg_error = NN_input(neg_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, False, False)

        # Add errors to list
        pos_errors.append(last_pos_error)
        neg_errors.append(last_neg_error)

    # After all iterations are complete, calculate average errors
    avg_pos_error = sum(pos_errors) / float(len(pos_errors))
    avg_neg_error = sum(neg_errors) / float(len(neg_errors))

    return avg_pos_error, avg_neg_error

def NN_input(sample_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, pos_sample = True, update = False):
    '''
    Funtion to take sample input (one by one) and run them through the neural network as defined by the weight and bias matrices.
    If the update flag is True, then it will update these matrices through back propagation and return them; otherwise it simply returns the error.

    Input:  sample input (vector of 34 bits of 0,1), input & hidden matrices, weight & bias matricies,
            total sample size, lambda, alpha for training purposes, a flag to define if desired output is 0 or 1, update flag
    Output: If updating – output error, updated input and hidden weight matrices, updated input and hidden bias matrices.
            If not updating – output error
    '''
    if pos_sample: # Desired output of positive value = 1, negative value = 0
        desired_output = np.ones((1,1))
    else:
        desired_output = np.zeros((1,1))

    # Given the sample input and weight matrix, tranform via the dot product, add bias matrix
    input_transform = np.dot(sample_input, input_weights) + input_bias

    # Pass through the activation function
    hidden_layer = sigmoid(input_transform)

    # Again, add bias and propagate predicted values through the weights between hidden + output layers
    hidden_transform = np.dot(hidden_layer, hidden_weights) + hidden_bias
    output = sigmoid(hidden_transform)

    # Get the difference between the desired output and the result from the autoencoder
    output_error = desired_output - output

    if update: # Update all weight and bias matrices and return these updates along with the error
        # Take the error weighted derivative to avoid changing weights that lead to post-sigmoid values close to either 0 or 1
        # Because these are probably starting to converge -> focus effort on changing ambiguous (~ 0.5) values
        output_delta = output_error * dsigmoid(output)

        # Backpropagate to determine the error in the hidden layer
        hidden_layer_error = output_delta.dot(hidden_weights.T)

        # Another error-weighted derivative
        hidden_layer_delta = np.multiply(hidden_layer_error, dsigmoid(hidden_layer))

        # Update the weights  and bias matrices for each layer
        hidden_weights += alpha * (((1 / m ) * (hidden_layer.T.dot(output_delta))) + L * hidden_weights)
        hidden_bias += (alpha / m) * output_delta

        input_weights += alpha * (((1 / m ) * (sample_input.T.dot(hidden_layer_delta))) + L * input_weights)
        input_bias += (alpha / m) * hidden_layer_delta

        return np.mean(np.abs(output_error)), input_weights, hidden_weights, input_bias, hidden_bias

    return np.mean(np.abs(output_error))    # Not updating, just return the error

def get_neuralnet_predictions(samples, input_weights, hidden_weights, input_bias, hidden_bias):
    '''
    Given a pre-trained neural network, take a series of samples and provide predictions as to their Rap1 binding likelihood

    Inputs: sample data (as 34-bit vectors in rows), input & hidden weight matrices, input and bias weight matrices
    Outputs: total average positive and negative errors resulting from input data being run through network
    '''
    output_list = [] # Store all predicted output values

    # Iterate through all test data
    for index in range(0,samples.shape[0]):
        sample_input = np.asmatrix(samples[index]) # Pick one row at a time

        # Given the sample input and weight matrix, tranform via the dot product, add bias matrix
        input_transform = np.dot(sample_input, input_weights) + input_bias

        # Pass through the activation function
        hidden_layer = sigmoid(input_transform)

        # Again, add bias and propagate predicted values through the weights between hidden + output layers
        hidden_transform = np.dot(hidden_layer, hidden_weights) + hidden_bias
        output = float(sigmoid(hidden_transform))

        # Get the difference between the desired output and the result from the autoencoder
        output_list.append(output)

    return output_list


#!/usr/bin/env python

import math
import numpy as np

import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt

def norm_pdf(x, mu, sigma):
    """Return the normal probability distribution function."""

    prefac = 1 / (sigma * math.sqrt(2 * math.pi))
    expt = np.exp(-(x - mu)**2 / (2 * sigma**2))

    return prefac * expt

### example normal distributions
### https://en.wikipedia.org/wiki/Normal_distribution

fig, ax = plt.subplots()

x = np.linspace(-5, 5, 300)

ax.plot(x, norm_pdf(x, mu=0, sigma=math.sqrt(0.2)), label=r'$\mu=0,\,\sigma^2=0.2$', color='blue')
ax.plot(x, norm_pdf(x, mu=0, sigma=math.sqrt(1.0)), label=r'$\mu=0,\,\sigma^2=1.0$', color='red')
ax.plot(x, norm_pdf(x, mu=0, sigma=math.sqrt(5.0)), label=r'$\mu=0,\,\sigma^2=5.0$', color='yellow')
ax.plot(x, norm_pdf(x, mu=-2, sigma=math.sqrt(0.5)), label=r'$\mu=-2,\,\sigma^2=0.5$', color='green')

ax.set_xlabel(r'$x$')
ax.set_ylabel(r'$\varphi_{\mu,\sigma^2}(x)$')

ax.legend(fancybox=True, loc='best', framealpha=0.50)
fig.savefig('norm_pdf.pdf', bbox_inches='tight')

plt.close(fig)


import matplotlib.pyplot as plt
import numpy as np

N = 500
k = 3
plt.figure(1)

s = np.exp(1j*2*np.pi*k/N*np.arange(-N/2, N/2))
plt.subplot(1, 2, 1)
plt.plot(np.arange(-N/2, N/2), np.real(s), lw=2)
plt.axvline(0, color='g', lw=2)
plt.axis([-N/2,N/2,-1,1])
plt.title ('cosine (even)')
plt.subplot(1, 2, 2)
plt.plot(np.arange(-N/2, N/2), np.imag(s), lw=2)
plt.axvline(0, color='g', lw=2)
plt.axis([-N/2,N/2,-1,1])
plt.title ('sine (odd)')

plt.tight_layout()
plt.savefig('even-odd.png')
plt.show()


import numpy as np

def gauss_kernel(sigma, sample_rate, duration):
    """
    Creates a Gaussian kernel centered in a vector of given duration.

    :param sigma: the standard deviation of the kernel in seconds
    :param sample_rate: the temporal resolution of the kernel in Hz
    :param duration: the desired duration of the kernel in seconds, (in general at least 4 * sigma)

    :return: the kernel as numpy array
    """
    l = duration * sample_rate
    x = np.arange(-np.floor(l / 2), np.floor(l / 2)) / sample_rate
    y = (1 / (np.sqrt(2 * np.pi) * sigma)) * np.exp(-(x ** 2 / (2 * sigma ** 2)))
    y /= np.sum(y)
    return y


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Date        : Jun 13, 2017 Tue
# Last update :
#
# Required answer: 292,201,338
# Imports
from scipy.misc import comb
import sympy
import math

# How to calculate combinations in python?
def calculate_probability():
    prob1 = comb(69,5)
    prob2 = sympy.binomial(26,1)
    prob = prob1 * prob2

    print('prob =  {0:,}'.format(int(prob)))
    return int(prob)

def years_to_win_lottery():
    prob = calculate_probability()
    century, day = divmod(prob,36500)
    year,days = divmod(prob,365)

    print('If you buy a ticket everyday and you are immortal, you may win\
    \b\b\b\b the jackpot after %d centuries and %d days!'%(century,day))

    print('\nIf you want to count in years, you will win lottery \
    in %d years and %d days.'%(year,days))

if __name__ == '__main__':
    prob = calculate_probability()
    years_to_win_lottery()

# From wikipedia:
# Drawings are held Wednesday and Saturday evenings at 10:59 p.m. Eastern Time.
# Since October 7, 2015, the game has used a 5/69 (white balls) + 1/26
# (Powerballs) matrix from which winning numbers are chosen, resulting
# in odds of 1 in 292,201,338 of winning a jackpot per play.

# 1 yr = 365 days
# 1 century = 100 * 365 = 36500 days
# 1 millinium = 1000 * 365 = 365000


import numpy as np
import time

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

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

def angular_dist(r1, d1, r2, d2):
  a = (np.sin(np.abs(d1 - d2)/2))**2
  b = np.cos(d1)*np.cos(d2)*np.sin(np.abs(r1 - r2)/2)**2
  d = 2*np.arcsin(np.sqrt(a + b))
  return d

def crossmatch(cat1, cat2, max_dist):

  matches = []
  no_matches = []

  start = time.perf_counter()

  cat1 = np.radians(cat1)
  cat2 = np.radians(cat2)
  max_dist = np.radians(max_dist)

  cat2_dec = cat2[:, 1]
  sorted_indexes = np.argsort(cat2_dec)
  sorted_cat2_dec = np.sort(cat2_dec)

  id_1 = 0
  for i in cat1:
      best = (0,0,max_dist+1)

      initial = np.searchsorted(sorted_cat2_dec, i[1] - max_dist, side='left')
      last = np.searchsorted(sorted_cat2_dec, i[1] + max_dist, side='left')

      for j in range(initial,last):

        dist = angular_dist(i[0], i[1], cat2[sorted_indexes[j]][0], cat2[sorted_indexes[j]][1])
        if dist<=best[2]:
          best = (id_1,sorted_indexes[j],dist)

      if best[2]<=max_dist:
         matches.append(best)
      else:
         no_matches.append(id_1)
      id_1+=1

  time_taken = time.perf_counter() - start
  return (matches, no_matches, time_taken)

if __name__ == '__main__':

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

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

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


"""
Objetivo: Resolver questão 1a do terceiro laboratorio.
"""
import matplotlib.pyplot as plt
import numpy as np
from Newton_Raphson import newton_raphson

y = lambda x: x ** 3 - 4 * x + 1  # f(x) = x^3 -4x + 1
y_linha = lambda x: 3 * x ** 2 - 4  # f'(x) = 3x^2 -4
ini = -1  # ponto inicial
#Verificar o arquivo Nexton-Raphson.py para entender o funcionamento dessa função
resp = newton_raphson(ini, y, y_linha, 'q1')
print(f'Para essa situação levamos {resp["iterações"] - 1} iterações e temos como valor de f(x) = {resp["modulo"]}')
print('Abaixo seguem os graficos dos valores de f(x) e dos erros, respectivamente:')

# Grafico com os valores de f(x)
x_a = np.linspace(-1.5, 3.5, 30)    #valores escolhidos para melhor visualização do grafico
y_a = [y(i) for i in x_a]
plt.style.use('ggplot')
plt.figure(figsize=(7, 5))
plt.title('F(x) por X')
plt.xlabel('Valores de x')
plt.ylabel('Valores de f(x)')
#função com valores entre -1.5 e 3.5
plt.plot(x_a, y_a, label='Função exata')
#Nossa aproximação Newton-Raphson por iteração
plt.plot(resp['valores'], resp['valores_função'], label='Valores de Newton-Raphson por iteração')
plt.tight_layout()
plt.legend(loc='best')
plt.show()

# Grafico com os valores dos erros
plt.style.use('ggplot')
plt.figure(figsize=(7, 5))
plt.title('Erro por Iterações')
plt.xlabel('Numero de iterações')
plt.ylabel('Valores dos erros')
plt.plot([i for i in range(1, resp['iterações'])], resp['erros'])
plt.tight_layout()
plt.show()


# from
# http://stackoverflow.com/questions/20924085/python-conversion-between-coordinates

import numpy as np

def rect(r, theta):
    """theta in degrees

    returns tuple; (float, float); (x,y)
    """
    x = r * np.cos(np.radians(theta))
    y = r * np.sin(np.radians(theta))
    return x,y

def polar(x, y):
    """returns r, theta(degrees)
    """
    lx = np.array(x)
    ly = np.array(y)
    r = np.sqrt(lx**2 + ly**2)
    theta = np.degrees(np.arctan(ly / lx))
    return r, theta

class Point(object):
    def __init__(self, x=None, y=None, r=None, theta=None):
        """x and y or r and theta(degrees)
        """
        if x and y:
            self.c_polar(x, y)
        elif r and theta:
            self.c_rect(r, theta)
        else:
            raise ValueError('Must specify x and y or r and theta')
    def c_polar(self, x, y, f = polar):
        self._x = x
        self._y = y
        self._r, self._theta = f(self._x, self._y)
        self._theta_radians = np.radians(self._theta)
    def c_rect(self, r, theta, f = rect):
        """theta in degrees
        """
        self._r = r
        self._theta = theta
        self._theta_radians = np.radians(theta)
        self._x, self._y = f(self._r, self._theta)
    def setx(self, x):
        self.c_polar(x, self._y)
    def getx(self):
        return self._x
    x = property(fget = getx, fset = setx)
    def sety(self, y):
        self.c_polar(self._x, y)
    def gety(self):
        return self._y
    y = property(fget = gety, fset = sety)
    def setxy(self, x, y):
        self.c_polar(x, y)
    def getxy(self):
        return self._x, self._y
    xy = property(fget = getxy, fset = setxy)
    def setr(self, r):
        self.c_rect(r, self._theta)
    def getr(self):
        return self._r
    r = property(fget = getr, fset = setr)
    def settheta(self, theta):
        """theta in degrees
        """
        self.c_rect(self._r, theta)
    def gettheta(self):
        return self._theta
    theta = property(fget = gettheta, fset = settheta)
    def set_r_theta(self, r, theta):
        """theta in degrees
        """
        self.c_rect(r, theta)
    def get_r_theta(self):
        return self._r, self._theta
    r_theta = property(fget = get_r_theta, fset = set_r_theta)
    def __str__(self):
        return '({},{})'.format(self._x, self._y)


import numpy as np

import openmdao.api as om

class LiftEquilibriumComp(om.ExplicitComponent):
    """
    Compute the rates of TAS and flight path angle required to match a given
    flight condition.
    """

    def initialize(self):
        self.options.declare('num_nodes', types=int)

        self._g = 9.80665

    def setup(self):
        n = self.options['num_nodes']

        # Parameter inputs
        self.add_input(name='CT', shape=(n,), desc='thrust coefficient', units=None)
        self.add_input(name='W_total', shape=(n,), desc='total aircraft weight', units='N')
        self.add_input(name='alpha', shape=(n,), desc='angle of attack', units='rad')
        self.add_input(name='gam', shape=(n,), desc='flight path angle', units='rad')
        self.add_input(name='q', shape=(n,), desc='dynamic pressure', units='Pa')
        self.add_input(name='S', shape=(n,), desc='reference area', units='m**2')

        self.add_output(name='CL_eq', shape=(n,),
                        desc='lift coefficient required for steady, level flight',
                        units=None)

        ar = np.arange(n)

        self.declare_partials(of='CL_eq', wrt='CT', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='W_total', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='alpha', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='gam', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='q', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='S', rows=ar, cols=ar)

    def compute(self, inputs, outputs):
        CT = inputs['CT']
        W_total = inputs['W_total']
        alpha = inputs['alpha']
        gam = inputs['gam']
        q = inputs['q']
        S = inputs['S']

        sa = np.sin(alpha)
        cgam = np.cos(gam)

        qS = q * S

        outputs['CL_eq'] = W_total * cgam / qS - CT * sa

    def compute_partials(self, inputs, partials):
        CT = inputs['CT']
        W_total = inputs['W_total']
        alpha = inputs['alpha']
        gam = inputs['gam']
        q = inputs['q']
        S = inputs['S']

        ca = np.cos(alpha)
        sa = np.sin(alpha)
        cgam = np.cos(gam)
        sgam = np.sin(gam)

        qS = q * S

        partials['CL_eq', 'CT'] = -sa
        partials['CL_eq', 'W_total'] = cgam / qS
        partials['CL_eq', 'alpha'] = -CT * ca
        partials['CL_eq', 'gam'] = -W_total * sgam / qS
        partials['CL_eq', 'q'] = -W_total * cgam / (q**2 * S)
        partials['CL_eq', 'S'] = -W_total * cgam / (q * S**2)