import numpy as np
import matplotlib.pyplot as plt

f1 = 5 # Hertz
w1 = 2*np.pi*f1 # angular frequency
f2 = 50 # Hertz
w2 = 2*np.pi*f2 # angular frequency

# Create an array of time points
N = 500
t = np.linspace(0,5,N)

# Create signals
pure = np.sin(w1*t) # pure sine wave
noise = np.sin(w2*t) + np.random.normal(0, 0.2, N) # Normally distributed random noise
 # 0   = mean of the normal distribution
 # 0.2 = standard deviation of the normal distribution
 # N   = number of elements in the array
signal = pure + noise # noisy sine wave

#################################################
### Code your recursive low pass filter below ###
#################################################
###  Use test run to visualize your solution  ###
#################################################
### Then submit it when your done to grade it ###
#################################################

# Recursive low-pass filter
alpha = 0.2 # alpha closer to zero means stronger smoothing effect
          # be sure to try differnt values and observe its effects in the plot below
filtered = np.zeros(N) # initialize array to store values
for i in range(N):
    if i > 1:
        # Fill out the filtered[i] variable with the code for your low pass filer
        filtered[i] = filtered[i-1] + alpha*(signal[i] - filtered[i-1])

fig = plt.figure()
ax1 = fig.add_subplot(311)
ax1.plot(t, pure)
ax1.set_xlabel('Time, (sec)')
ax1.set_ylabel('Amplitude, (m)')
ax1.set_title('5 Hz, Pure Signal')

ax2 = fig.add_subplot(312)
ax2.plot(t, signal)
ax2.set_xlabel('Time, (sec)')
ax2.set_ylabel('Amplitude, (m)')
ax2.set_title('Noisy Signal')

ax3 = fig.add_subplot(313)
ax3.plot(t, filtered)
ax3.set_xlabel('Time, (sec)')
ax3.set_ylabel('Amplitude, (m)')
ax3.set_title('Filtered Signal')

plt.tight_layout()
plt.show()


#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
    Copyright 2018 Bryan Webb

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
    associated documentation files (the "Software"), to deal in the Software without restriction,
    including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

        The above copyright notice and this permission notice shall be included in all copies
        or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
        INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
        AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
        DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""

"""
Adapted from:

https://glowingpython.blogspot.com/2012/03/linear-regression-with-numpy.html
"""

import numpy as np

def linear_least_squares_fit_xy_lists(x_list, y_list):
    """
    Calculate the least squares fit of corresponding lists of X and Y values
    as in:

        y = mx + b

    :param x_list: list of x values
    :param y_list: list of y values to be LS Fit to the list of x values

    :return:    (slope, y-intercept) list
    """
    assert isinstance(x_list, list) and 2 <= len(x_list)
    assert isinstance(y_list, list) and 2 <= len(y_list)
    assert len(x_list) == len(y_list)
    assert all([isinstance(v, float) for v in x_list])
    assert all([isinstance(v, float) for v in y_list])

    try:
        x_matrix = np.array([x_list, np.ones(len(x_list))])
        mb_list = np.linalg.lstsq(x_matrix.T, y_list)[0]
    except:
        mb_list = [None for i in range(len(x_list))]

    return mb_list

def linear_least_squares_fit_tup_list(v_list):
    """
    Calculate the least squares fit of a list of (X, Y) tuples, as in:

        y = mx + b

    :param v_list:  a list of (x, y) tuples, where x and y are floats

    :return:    (slope, y-intercept) tuple
    """
    assert isinstance(v_list, list) and 2 <=len(v_list)
    assert all([isinstance(v, (list, tuple)) and 2 == len(v) for v in v_list])

    x_list = [v[0] for v in v_list]
    y_list = [v[1] for v in v_list]

    assert all([isinstance(v, float) for v in x_list])
    assert all([isinstance(v, float) for v in y_list])

    mb_list = linear_least_squares_fit_xy_lists(x_list, y_list)

    return mb_list

if "__main__" == __name__:
    # Nominal test case
    x_list = []
    y_list = []
    tup_list = []

    for i in range(10):
        x = float(i)
        for j in range(i):
            y = float(j)
            x_list.append(x)
            y_list.append(y)
            tup = (x, y)
            tup_list.append(tup)

    print linear_least_squares_fit_xy_lists(x_list, y_list)
    print linear_least_squares_fit_tup_list(tup_list)

    # Reasonable result
    tup_list = [(0.0, 0.0), (0.0, 1.0), (1.0, 0.0), (1.0, 1.0)]
    x_list = [v[0] for v in tup_list]
    y_list = [v[1] for v in tup_list]

    print linear_least_squares_fit_xy_lists(x_list, y_list)
    print linear_least_squares_fit_tup_list(tup_list)

    # This test case should have yielded infinite slope m
    # If the data is set so that it always yields an ~ 45deg angle, this slope of 0.0 will never happen
    x_list = []
    y_list = []
    tup_list = []

    for i in range(10):
        x = 0.0
        y = float(i)
        x_list.append(x)
        y_list.append(y)
        tup = (x, y)
        tup_list.append(tup)

    print linear_least_squares_fit_xy_lists(x_list, y_list)
    print linear_least_squares_fit_tup_list(tup_list)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Dec  2 15:37:10 2020

Copyright 2020 by Hadrien Montanelli.
"""
# %% Imports.

# Standard library imports:
import matplotlib.pyplot as plt
import numpy as np
import time

# Chebpy imports:
from chebpy.cheb import chebpts, coeffs2vals, multmat, spconvert, vals2coeffs

# %% Multiplication g = f*h on [-1,1].

# Functions:
f = lambda x: np.cos(x)*np.exp(x)
h = lambda x: np.sin(x)*x**2
gex = lambda x: f(x)*h(x)

# Grid:
n = 10000
x = chebpts(n)

# Compute coeffs of f:
F = vals2coeffs(f(x))

# Multiplication by h matrix in coefficient space:
start = time.time()
M = multmat(n, h)
end = time.time()
print(f'Time   (setup): {end-start:.5f}s')
plt.figure()
plt.spy(M)

# Multiply:
start = time.time()
G = M @ F
end = time.time()
print(f'Time (product): {end-start:.5f}s')

# Convert to value space:
g = coeffs2vals(G)

# Error:
error = np.max(np.abs(g - gex(x)))/np.max(np.abs(gex(x)))
print(f'Error  (L-inf): {error:.2e}')

# %% Multiplication g = f*h on [0,2].

# Functions:
f = lambda x: np.cos(x)*np.exp(x)
h = lambda x: np.sin(x)*x**2
gex = lambda x: f(x)*h(x)

# Grid:
n = 10000
x = chebpts(n, [0, 2])

# Compute coeffs of f:
F = vals2coeffs(f(x))

# Multiplication by h matrix in coefficient space:
start = time.time()
M = multmat(n, h, [0, 2])
end = time.time()
print(f'Time   (setup): {end-start:.5f}s')
plt.figure()
plt.spy(M)

# Multiply:
start = time.time()
G = M @ F
end = time.time()
print(f'Time (product): {end-start:.5f}s')

# Convert to value space:
g = coeffs2vals(G)

# Error:
error = np.max(np.abs(g - gex(x)))/np.max(np.abs(gex(x)))
print(f'Error  (L-inf): {error:.2e}')

# %% Multiplication g = f*h in the C^{(lam)} basis.

# Basis lambda:
lam = 2

# Functions:
f = lambda x: np.cos(x)*np.exp(x)
h = lambda x: np.sin(x)*x**2
gex = lambda x: f(x)*h(x)

# Grid:
n = 400
x = chebpts(n)

# Compute coeffs of f and convert to the C^{(lam)} basis:
F = vals2coeffs(f(x))
for k in range(lam):
    S = spconvert(n, k)
    F = S @ F

# Multiplication by h matrix in in the C^{(lam)} basis:
start = time.time()
M = multmat(n, h, [-1, 1], lam)
end = time.time()
print(f'Time   (setup): {end-start:.5f}s')
plt.figure()
plt.spy(M)

# Multiply in the C^{(lam)} basis:
start = time.time()
G = M @ F
end = time.time()
print(f'Time (product): {end-start:.5f}s')

# Exact coefficients in the C^{(lam)} basis:
Gex = vals2coeffs(gex(x))
for k in range(lam):
    S = spconvert(n, k)
    Gex = S @ Gex

# Error:
error = np.max(np.abs(G - Gex))/np.max(np.abs(Gex))
print(f'Error  (L-inf): {error:.2e}')


import numpy as np
from math import floor, cos, radians, degrees, acos

class ColorConverter():

    @staticmethod
    def rgb_to_gray(rgb):
        return np.dot(rgb[...,:3], [0.2989, 0.5870, 0.1140])

    @staticmethod
    def rgb_to_gray_via_weighted_average(r, g, b):
        return (r * 0.2989) + (g * 0.5870) + (b * 0.1140)

    @staticmethod
    def rgb_to_gray_via_simple_average(r, g, b):
        return (r + g + b) / 3

    @staticmethod
    def calculate_hsv_hue(r, g, b):
        max_value = max(r, g, b)
        min_value = min(r, g, b)
        diff = max_value - min_value
        if max_value == min_value:
            hue = 0
        elif max_value == r:
            hue = (60 * ((g - b) / diff) + 360) % 360
        elif max_value == g:
            hue = (60 * ((b - r) / diff) + 120) % 360

        return hue

    @staticmethod
    def calculate_hsv_saturation(r, g, b):
        max_value = max(r, g, b)
        min_value = min(r, g, b)
        diff = max_value - min_value

        if max_value == 0:
            saturation = 0
        else:
            saturation = diff / max_value

        return saturation

    @staticmethod
    def rgb_to_hsv(r, g, b):
        '''
        Convert a RGB image to HSV image.
        Input: R, G, B values are [0, 255].
        Output: H value is [0, 360]. S, V values are [0, 1].
        '''
        r, g, b = r / 255.0, g / 255.0, b / 255.0

        h = ColorConverter.calculate_hsv_hue(r, g, b)
        s = ColorConverter.calculate_hsv_saturation(r, g, b)
        v = max(r, g, b)
        return int(h), s, v

    @staticmethod
    def hsv_to_rgb(h, s, v):
        '''
        Convert a HSV image to RGB image.
        Input: H value is [0, 360]. S, V values are [0, 1].
        Output: R, G, B values are [0, 255].
        '''
        h = float(h)
        s = float(s)
        v = float(v)
        h60 = h / 60.0
        h60f = floor(h60)
        hi = int(h60f) % 6
        f = h60 - h60f
        p = v * (1 - s)
        q = v * (1 - f * s)
        t = v * (1 - (1 - f) * s)
        r, g, b = 0, 0, 0
        if hi == 0:
            r, g, b = v, t, p
        elif hi == 1:
            r, g, b = q, v, p
        elif hi == 2:
            r, g, b = p, v, t
        elif hi == 3:
            r, g, b = p, q, v
        elif hi == 4:
            r, g, b = t, p, v
        elif hi == 5:
            r, g, b = v, p, q
        return ColorConverter.normalize_rgb(r, g, b)

    @staticmethod
    def rgb_to_hsi(r, g, b):
        '''
        Convert a RGB image to HSI image.
        Input: R, G, B values are [0, 255].
        Output: H value is [0, 360]. S, I values are [0, 1].
        '''
        r, g, b = ColorConverter.normalize_to_zero_one(r, g, b)

        # Hue
        numerator = 0.5 * ((r - g) + (r - b))
        denominator = ((r - g) ** 2 + ((r - b) * (g - b))) ** 0.5
        # A small number was added in the donominator to avoid dividing by 0
        theta = degrees(acos(numerator / (denominator + 0.000001)))
        if b <= g:
            h = theta
        else:
            h = 360 - theta

        # Saturation
        s = 1 - ((3 / (r + g + b)) * min([r, g, b]))

        # Intensity
        i = (r + g + b) / 3

        return np.round(h, 2), np.round(s, 2), np.round(i, 2)

    @staticmethod
    def hsi_to_rgb(h, s, i):
        '''
        Convert a HSI image to RGB image.
        Input: H value is [0, 360]. S, I values are [0, 1].
        Output: R, G, B values are [0, 255].
        '''
        r, g, b = [0, 0, 0]

        # RG sector (0 degrees <= hue < 120 degrees)
        if 0 <= h < 120:
            b = i * (1 - s)
            r = i * (1 + ((s * cos(radians(h))) / (cos(radians(60 - h)))))
            g = 3 * i - (r + b)

        # GB sector (120 degrees <= hue < 240 degrees)
        elif 120 <= h < 240:
            h = h - 120
            r = i * (1 - s)
            g = i * (1 + ((s * cos(radians(h))) / (cos(radians(60 - h)))))
            b = 3 * i - (r + g)

        # BR sector (240 degrees <= hue <= 360 degrees)
        elif 240 <= h <= 360:
            h = h - 240
            g = i * (1 - s)
            b = i * (1 + ((s * cos(radians(h))) / (cos(radians(60 - h)))))
            r = 3 * i - (b + g)

        return ColorConverter.normalize_rgb(r, g, b)

    @staticmethod
    def normalize_to_zero_one(r, g, b):
        '''
        Convert RGB values from [0, 255] to [0, 1] range.
        '''
        r = float(r) / 255.0
        g = float(g) / 255.0
        b = float(b) / 255.0

        return r, g, b

    @staticmethod
    def normalize_rgb(r, g, b):
        '''
        Convert RGB values from [0, 1] to [0, 255] range.
        '''
        r = int(r * 255)
        g = int(g * 255)
        b = int(b * 255)

        return r, g, b


from numpy import matrix, linalg

def gcd(a,b):
    a,b = (a,b) if a>b else (b,a)
    rem = a%b
    return gcd(b, rem) if rem != 0 else b

def pulverizer(a,b):
    x,y = (a,b) if a>b else (b,a)
    q = x//y
    rem = x%y
    print (rem,q)
    return pulverizer(y, rem) if rem != 0 else y

def bezout(a,b):
    """Finding Bezout's Identity by Extended Euclid's method"""
    px,py = (a,b)
    a,b = (a,b) if a>b else (b,a)
    x,y = a,b
    q = a//b
    rem = a%b
    s = t = 0
    s_2, s_1 = [1, 0]
    t_2, t_1 = [0, 1]

    while rem != 0:
        #print (rem,q)
        q, rem = a//b, a%b
        a,b = (b,rem)
        s = s_2 - q*s_1
        t = t_2 - q*t_1
        s_2, s_1 = s_1, s
        t_2, t_1 = t_1, t
    assert abs(a*s) == abs(y)
    assert abs(a*t) == abs(x)
    s_2, t_2 = (s_2, t_2) if px>py else (t_2, s_2)
    assert (s_2*px + t_2*py) == a
    #print a, b
    #print s, t
    #print (a*s), (a*t)
    #print x,y
    #print px,py
    return a, s_2, t_2

def multiplicative_inverse(a,n):
    t = 0
    newt = 1
    r = n
    newr = a
    while newr != 0:
        quotient = r // newr
        (t, newt) = (newt, t - quotient * newt)
        (r, newr) = (newr, r - quotient * newr)
    if r > 1: return "a is not invertible"
    if t < 0: t = t + n
    return t

#REWORK - FIX CONCEPT!
def pulverizer_bezout_matrix(a,b):
    """Pulverizing to find Bezout's Identity by Matrix Method"""
    mat_list = []
    a,b = (a,b) if a>b else (b,a)
    x,y = a,b
    q = a//b
    rem = a%b

    while rem != 0:
        #return pulverizer(y, rem) if rem != 0 else y
        #print (rem,q)
        mat_list.append(matrix([[q, 1], [1,0]]))
        a,b = (b,rem) if b>rem else (rem,b)
        q, rem = a//b, a%b

    #print a, b, rem  #b - previous remainder - r[N-1] or r0 when rem == 0
    mat_list.append(matrix([[q, 1], [1,0]]))
    rem_mat = matrix([[b],[rem]])
    m = reduce(lambda m1,m2: (m1*m2), mat_list)
    #print mat_list
    #print rem_mat
    #print m * rem_mat
    mInv = linalg.inv(m)
    #print mInv
    #print  mInv * matrix([[x],[y]])
    assert all([i for i in (mInv * matrix([[x],[y]]) == rem_mat)])
    #print mInv[0,0], mInv[0,1]
    assert long(mInv[0,0]*x + mInv[0,1]*y) == b
    return (mInv[0,0], mInv[0,1], mInv[0,0]*x + mInv[0,1]*y, b)

def euler_tot(n):
    """Euler's Totient Function"""
    return len([i for i in range(1, n) if gcd(n, i)==1])


import B3_VanillaOptions_MC_BlackScholes as B3MC
import B3_VanillaOptionsinBlackScholesWorld as B3BS
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

def delta_call_BSM(spot,strike,maturity,rate,vol,dividend=None):
    if dividend is None:
        dividend = 0
    d1 = (np.log(spot/strike) + (rate - dividend + 0.5*vol**2)*(maturity))/(vol*np.sqrt(maturity))
    Nd1 = norm.cdf(d1)
    return np.exp(-dividend*maturity)*Nd1

def _dN(x):
    return np.exp((-x**2)/2)/(np.sqrt(2*np.pi))

def gamma_call_BSM(spot,strike,maturity,rate,vol,dividend=None):
    if dividend is None:
        dividend = 0
    d1 = (np.log(spot/strike) + (rate - dividend + 0.5*vol**2)*(maturity))/(vol*np.sqrt(maturity))
    dN = _dN(d1)
    return (dN/(spot*vol*np.sqrt(maturity)))*np.exp(-dividend*maturity)

def vega_call_BSM(spot,strike,maturity,rate,vol,dividend=None):
    if dividend is None:
        dividend = 0
    d1 = (np.log(spot/strike) + (rate - dividend + 0.5*vol**2)*(maturity))/(vol*np.sqrt(maturity))
    dN = _dN(d1)
    return np.exp(-dividend*maturity)*spot*np.sqrt(maturity)*dN

def theta_call_BSM(spot,strike,maturity,rate,vol,dividend=None):
    if dividend is None:
        dividend = 0
    d1 = (np.log(spot/strike) + (rate - dividend + 0.5*vol**2)*(maturity))/(vol*np.sqrt(maturity))
    d2 = d1 - vol*np.sqrt(maturity)
    dN = _dN(d1)
    return -np.exp(-dividend*maturity)*spot*dN*vol/(2*np.sqrt(maturity)) + \
           dividend*np.exp(-dividend*maturity)*spot*norm.cdf(d1) - \
           rate*strike*np.exp(-rate*maturity)*norm.cdf(d2)

def rho_call_BSM(spot,strike,maturity,rate,vol,dividend=None):
    if dividend is None:
        dividend = 0
    d1 = (np.log(spot/strike) + (rate - dividend + 0.5*vol**2)*(maturity))/(vol*np.sqrt(maturity))
    d2 = d1 - vol*np.sqrt(maturity)
    return strike*maturity*np.exp(-rate*maturity)*norm.cdf(d2)

def delta_call_finite_difference(spot,strike,maturity,rate,vol,dividend=None):
    if dividend is None:
        dividend = 0
    e = 0.0001  ##this works better than using 0.0001*spot
    call_up = B3BS.europeanCallOptionPrice(spot + e, strike, maturity, rate, dividend, vol)
    call = B3BS.europeanCallOptionPrice(spot, strike, maturity, rate, dividend, vol)
    return (call_up-call)/e

def gamma_call_finite_difference(spot,strike,maturity,rate,vol,dividend=None):
    if dividend is None:
        dividend = 0
    e = 0.0001
    call_up = B3BS.europeanCallOptionPrice(spot + e, strike, maturity, rate, dividend, vol)
    call_dn = B3BS.europeanCallOptionPrice(spot - e, strike, maturity, rate, dividend, vol)
    call = B3BS.europeanCallOptionPrice(spot, strike, maturity, rate, dividend, vol)
    return (call_up - 2*call + call_dn)/e**2

def vega_call_finite_difference(spot, strike, maturity, rate, vol, dividend=None):
    if dividend is None:
        dividend = 0
    e = 0.0001  ##this works better than using 0.0001*spot
    call_up = B3BS.europeanCallOptionPrice(spot , strike, maturity, rate, dividend, vol + e)
    call = B3BS.europeanCallOptionPrice(spot, strike, maturity, rate, dividend, vol)
    return (call_up-call)/e

def rho_call_finite_difference(spot, strike, maturity, rate, vol, dividend=None):
    if dividend is None:
        dividend = 0
    e = 0.0001  ##this works better than using 0.0001*spot
    call_up = B3BS.europeanCallOptionPrice(spot , strike, maturity, rate + e, dividend, vol)
    call = B3BS.europeanCallOptionPrice(spot, strike, maturity, rate, dividend, vol)
    return (call_up-call)/e

def theta_call_finite_difference(spot, strike, maturity, rate, vol, dividend=None):
    if dividend is None:
        dividend = 0
    e = 0.0001  ##this works better than using 0.0001*spot
    call_up = B3BS.europeanCallOptionPrice(spot , strike, maturity + e, rate, dividend, vol)
    call = B3BS.europeanCallOptionPrice(spot, strike, maturity, rate, dividend, vol)
    return (call_up-call)/e


import numpy as np
import matplotlib.pyplot as plt
import scipy.signal as sig
from tools import get_img

class Image_Filters:
    """
    A class simply to hold all image filters for more elegant code
    """

    def __init__(self):
        self.img_store = {}
        return

    # complex filters
    Sobel_0_3 = np.array([[-1, 0, 1],
                         [ -2, 0, 2],
                         [ -1, 0, 1]])
    Sobel_45_3 = np.array([[0, 1, 2],
                          [-1, 0, 1],
                          [-2,-1, 0]])
    Sobel_0_5 = np.array([[1, 2, 3, 2, 1],
                         [ 2, 3, 5, 3, 2],
                         [ 0, 0, 0, 0, 0],
                         [-2,-3,-5,-3,-2],
                         [-1,-2,-3,-2,-1]])
    Sobel_45_5 = np.array([[0, 2, 3, 2, 1],
                          [-2, 0, 3, 5, 2],
                          [-3,-3, 0, 3, 3],
                          [-2,-5,-3, 0, 2],
                          [-1,-2,-3,-2, 0]])
    Sobel_90_3 = Sobel_0_3.T
    Sobel_135_3 = Sobel_45_3.T
    Sobel_90_5 = Sobel_0_5.T
    Sobel_135_5 = Sobel_45_5.T
    Laws_L5 = np.array([1,4,6,4,1])
    Laws_E5 = np.array([-1,-2,0,2,1])
    Laws_S5 = np.array([-1,0,2,0,-1])
    Laws_W5 = np.array([-1,2,0,-2,1])
    Laws_R5 = np.array([1,-4,6,-4,1])

    def avg(self, size):
        return np.ones([size, size]) / (size*size)

    def laplacian_8(self, size):
        assert size%2==1, 'size must be odd'
        lap = np.ones([size, size])
        i = int(size/2)
        lap[i, i] = 1 - size*size
        return lap

    def gabor_fn(self, sigma, theta, Lambda, gamma=1., psi=0.):
        """
        Code from https://en.wikipedia.org/wiki/Gabor_filter#cite_note-8
        sigma: std of the gaussian
        theta: angle of the sinasoid in 2d
        Lambda: sinasoid wavelength
        psi: phase ofset of the sinasoid
        gamma: elpisive factor in the gaussian
        """
        sigma_x = sigma
        sigma_y = float(sigma) / gamma

        # Bounding box
        nstds = 3 # Number of standard deviation sigma
        xmax = max(abs(nstds * sigma_x * np.cos(theta)),
                   abs(nstds * sigma_y * np.sin(theta)))
        xmax = np.ceil(max(1, xmax))
        ymax = max(abs(nstds * sigma_x * np.sin(theta)),
                   abs(nstds * sigma_y * np.cos(theta)))
        ymax = np.ceil(max(1, ymax))
        xmin = -xmax
        ymin = -ymax
        (y, x) = np.meshgrid(np.arange(ymin, ymax + 1),
                             np.arange(xmin, xmax + 1))

        # Rotation
        x_theta = x * np.cos(theta) + y * np.sin(theta)
        y_theta = -x * np.sin(theta) + y * np.cos(theta)

        gauss = np.exp(-(x_theta**2 / sigma_x**2 + y_theta**2 / sigma_y**2) / 2)
        trig = np.cos(2 * np.pi / Lambda * x_theta + psi)
        return gauss*trig

    def gabor_bank(self, n_theta, sigma, Lambda, gamma=1., psi=0.,
                   verbose=False):

        angs = np.linspace(0, 180, n_theta)
        filt_bank = [self.gabor_fn(sigma, np.deg2rad(ang), Lambda, gamma, psi)
                     for ang in angs]

        if verbose:
            x = 4
            y = int(n_theta/x) + 1
            fig, axs = plt.subplots(y, x, figsize=[16, 16])
            for filt, ax, ang in zip(filt_bank,
                                     axs.ravel()[:n_theta],
                                     angs):
                ax.imshow(filt, cmap='gray')
                ax.set(title=ang)
                ax.axis('off')

            # remove unused axis
            for ax in axs.ravel()[n_theta:]:
                fig.delaxes(ax)

        return filt_bank

    def apply_bank(self, filt_bank, grey_img, normed=True, verbose=False):
        output = np.zeros_like(grey_img)
        for filt in filt_bank:
            output += sig.convolve2d(grey_img, filt, mode='same')

        if normed:
            output -= output.min()
            output /= output.max()

        if verbose:
            fig, ax = plt.subplots(figsize=[22, 11])
            col = ax.imshow(output)
            plt.colorbar(col)

        return output

    def mask_outine(self, mask, size=3):
        """
        Take a 2d mask and use a laplacian convolution to find the segment
        outlines for plotting. Option decides if all directions are to be
        included 'full' or just horizontal and vertical ones 'edge'
        """
        conv = sig.convolve2d(mask, self.laplacian_8(size), mode='valid')
        conv = np.pad(conv, 1, 'edge') # ignore edges
        return 1. - np.isclose(conv, 0) # not zero grad (i.e. an edge)

    def sobel_edge_detection(self, grey_img, verbose=False):
        """
        Sobel edge detection convolution in 4 diretions returning a 3d array
        with angles (0, 45, 90, 135) on the final axis.
        """
        assert len(grey_img.shape) == 2, 'grey_img must be 2d'

        zeros = np.zeros_like(grey_img)
        edge_det = np.dstack([zeros, zeros, zeros, zeros])

        for i, filt in enumerate([self.Sobel_0_5, self.Sobel_45_5,
                                  self.Sobel_90_5, self.Sobel_135_5]):
            edge_det[:, :, i] = sig.convolve2d(grey_img, filt, mode='same')

        if verbose:
            for i in range(4):
                plt.figure(figsize=[22,22])
                plt.imshow(edge_det[:,:,i])
                plt.title('angle ' + str(i*45) + ' deg')

        return edge_det

    def rgba(self, mask, color='r', opaqueness=1):
        "Take a 2d mask and return a 4d rgba mask for imshow overlaying"

        # create the transparent mask
        zeros = np.zeros_like(mask)
        rgba = np.dstack([zeros, zeros, zeros, mask*opaqueness])

        # set the correct color channel
        i = ['r', 'g', 'b'].index(color)
        rgba[:, :, i] = mask

        return rgba

    def laws_texture_filters(self, size):

        filts = []
        arrs = [self.Laws_L5, self.Laws_E5, self.Laws_S5,
                self.Laws_W5, self.Laws_R5]
        arrs = [np.repeat(arr, size) for arr in arrs]

        for arr1 in arrs:
            for arr2 in arrs:
                filts.append(np.outer(arr1.T, arr2.T))

        return filts

    def laws_texture_features(self, grey_img, size, verbose=False):

        assert len(grey_img.shape) == 2, 'image must be grey'

        filts = self.laws_texture_filters(size)
        n_filts = len(filts)

        output = np.empty([*grey_img.shape, n_filts])

        for i, filt in enumerate(filts):
            output[:, :, i] = sig.convolve2d(grey_img, filt, mode='same')
            output[:, :, i] -= output[:, :, i].mean()
            output[:, :, i] -= output[:, :, i].std()

            if verbose:
                fig, ax = plt.subplots(figsize=[22,11])
                col = ax.imshow(output[:, :, i])
                plt.colorbar(col)

        return output

if __name__ == '__main__':

    # gabor filter example
    IF_obj = Image_Filters()
    bank = IF_obj.gabor_bank(13, 5, 20, gamma=1., verbose=True)

    img = get_img('/content/images/TX1_white_cropped.tif')
    grey_img = img.mean(axis=2)

    IF_obj.apply_bank(bank, grey_img, verbose=True)

    # sobel_edge_detection example
    IF_obj = Image_Filters()

    img = get_img('/content/images/TX1_white_cropped.tif')
    grey_img = img.mean(axis=2)

    IF_obj.sobel_edge_detection(grey_img, verbose=True)


import numpy as np

class SimpleLinearRegression1:

    def __init__(self):
        self.a_ = None
        self.b_ = None

    def fit(self, x_train, y_train):
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)

        num = 0.0
        d = 0.0
        for i, j in zip(x_train, y_train):
            num += (i - x_mean) * (j - y_mean)
            d += (i - x_mean) ** 2

        self.a_ = num / d
        self.b_ = y_mean - self.a_ * x_mean
        return self

    def predict(self, x_predict):
        """
        :param x_predict: np.array()
        :return:
        """
        return np.array([self._predict(x) for x in x_predict])

    def _predict(self, x_single):
        return self.a_ * x_single + self.b_

    def __repr__(self):
        return "SimpleLinearRegression1()"

class SimpleLinearRegression2:

    def __init__(self):
        self.a_ = None
        self.b_ = None

    def fit(self, x_train, y_train):
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)

        num = (x_train - x_mean).dot(y_train - y_mean)
        d = (x_train - x_mean).dot(x_train - x_mean)

        self.a_ = num / d
        self.b_ = y_mean - self.a_ * x_mean
        return self

    def predict(self, x_predict):
        """
        :param x_predict: np.array()
        :return:
        """
        return np.array([self._predict(x) for x in x_predict])

    def _predict(self, x_single):
        return self.a_ * x_single + self.b_

    def __repr__(self):
        return "SimpleLinearRegression2()"

def test():
    x_train = np.array([1, 2, 3, 4, 5])
    y_train = np.array([1, 3, 2, 3, 5])
    simple1 = SimpleLinearRegression1()
    simple1.fit(x_train, y_train)
    print(simple1.predict(np.array([6, 7, 10])))

    simple2 = SimpleLinearRegression2()
    simple2.fit(x_train, y_train)
    print(simple2.predict(np.array([6, 7, 10])))


# -*- coding: utf-8 -*-
"""
Created on Mon Dec 11 20:02:37 2017

@author: tzave
"""

import numpy as np
import solver

def determinant(A):
    rows = A.shape[0]
    det = 1.  # Det of a triangular matrix is equal
    for i in range(rows):  # to the product of the
        det = det * A[i, i]  # diagonal entries
    return det

A = np.array([[1., 1., 2.], [1., -1., 0.], [-2., 2., 4.]])
B = np.array([7., 1., 2.])
x = np.zeros(3)

result1 = solver.GaussElimination(A, B, 'partial')
det1 = determinant(result1['E'])
print('Partial Pivot Determinant is: ', det1)
print('Partial Pivot Solution is: ', result1['x'])

result2 = solver.GaussElimination(A, B, 'complete')
det2 = determinant(result2['E'])
print('Complete Pivot Determinant is: ', det2)
print('Complete Pivot Solution is: ', result2['x'])


# There exists exactly one Pythagorean triplet for which a + b + c = 1000.
# Find the product abc.

from sympy.solvers import solve
from sympy import Symbol
import math

def is_int(x):
    return int(x) == x

def find_pythag_triplet():
    c = Symbol('c')
    a, b, soln = 0, 0, [0]
    for i in range(4):
        for j in range(4):
            a = 2 ** i * 5 ** j
            r = 1000 // a
            soln = solve(a ** 2 + (r * (500 - c)) ** 2 - c ** 2, c)
            if soln[0].is_Integer:
                b = r * (500 - int(soln[0]))
                break
    return a * b * soln[0], (a, b, soln[0])

def solve_test():
    x = Symbol('x')
    soln = solve(x + 1, x)
    print(soln[0].is_Integer)
    return

def find_pythag_triplet_optimized(s=1000):
    """
    Find the Pythagorean triplet (a, b, c) that satisfies a + b + c = s

    :param s: a + b + c
    :return: (a * b * c, (a, b, c))
    """
    s2 = s // 2
    lim = math.ceil(math.sqrt(s2)) - 1
    for m in range(2, lim + 1):
        if s2 % m == 0:
            s_m = s2 // m
            while s_m % 2 == 0:
                s_m //= 2
            k = m + 1
            if m % 2 == 1:
                k = m + 2
            while k < 2 * m and k <= s_m:
                if s_m % k == 0 and math.gcd(k, m) == 1:
                    d = s2 // (k * m)
                    n = k - m
                    a = d * (m ** 2 - n ** 2)
                    b = 2 * d * m * n
                    c = d * (m ** 2 + n ** 2)
                    return a * b * c, (a, b, c)
                k += 2
    return None

if __name__ == "__main__":
    ans = find_pythag_triplet_optimized()[0]
    a, b, c = find_pythag_triplet_optimized()[1]
    print(f"abc = {ans}")
    print(f"a = {a}, b = {b}, c = {c}")
    # solve_test()


import numpy as np
from numba import njit
from numba import types
from numba.typed import Dict

@njit
def mixing_matrix(edges, in_deg, out_deg, mapping):

    n_degrees = len(mapping)
    M = np.zeros((n_degrees, n_degrees))

    for i in range(edges.shape[1]):
        u = edges[0][i]
        v = edges[1][i]
        du = out_deg[u]
        dv = in_deg[v]
        x = mapping[du]
        y = mapping[dv]
        M[x, y] += 1
        M[y, x] += 1
    return M

def degree_mixing_matrix(adj_matrix, normalize=True):
    out_deg = adj_matrix.sum(1).A1.astype('int64')
    in_deg = adj_matrix.sum(0).A1.astype('int64')
    deg_set = np.union1d(out_deg, in_deg)

    mapping = Dict.empty(
        key_type=types.int64,
        value_type=types.int64,
    )
    for x, y in enumerate(deg_set):
        mapping[y] = x

    edges = np.asarray(adj_matrix.nonzero())

    M = mixing_matrix(edges, in_deg, out_deg, mapping)

    if normalize:
        M /= M.sum()

    return M, mapping

def numeric_ac(M, mapping):
    # M is a numpy matrix or array
    # numeric assortativity coefficient, pearsonr
    x = y = np.array(list(mapping.keys()), dtype='float')
    a = M.sum(0)
    b = M.sum(1)
    # D(x) = E(x^2) - [E(x)]^2
    vara = (a * x**2).sum() - ((a * x).sum())**2
    varb = (b * x**2).sum() - ((b * x).sum())**2
    xy = np.outer(x, y)
    ab = np.outer(a, b)

    return (xy * (M - ab)).sum() / vara

def degree_assortativity_coefficient(adj_matrix):
    """Compute degree assortativity of graph.

    Assortativity measures the similarity of connections
    in the graph with respect to the node degree.

    Parameters
    ----------
    adj_matrix: Scipy sparse adjacency matrix representing a graph

    Returns
    -------
    r : float
        Assortativity of graph by degree.

    Notes
    --------
    This is a faster implementation of
        networkx.degree_assortativity_coefficient

    See Also
    --------
    graphgallery.degree_mixing_matrix
    networkx.degree_assortativity_coefficient

    References
    ----------
    .. [1] M. E. J. Newman, Mixing patterns in networks,
    Physical Review E, 67 026126, 2003
    .. [2] Foster, J.G., Foster, D.V., Grassberger, P. & Paczuski, M.
    Edge direction and the structure of networks, PNAS 107, 10815-20 (2010).
    """
    M, mapping = degree_mixing_matrix(adj_matrix)
    return numeric_ac(M, mapping)


from sympy import gcd

__all__ = ('phi', 'partition_sequences')

def phi(n):
    """ Euler's totient function."""
    assert n >= 0, 'Negative integer.'

    out = 0
    for i in range(1, n + 1):
        if gcd(n, i) == 1:
            out += 1

    return out

def partition_sequences(k):
    """ Generates a set of partition_sequences of size :math:`k`, i.e.
    sequences :math:`(n_i)` such that :math:`\sum_{i=1}^k i n_i = k`."""
    assert k >= 0, 'Negative integer.'

    def f(k, c, i):

        if k == 0:
            yield [0] * c

        elif i == k:
            yield [1] + [0] * (c - 1)

        elif i < k:
            for n in range(0, k // i + 1):
                for ps in f(k - (i * n), c - 1, i + 1):
                    yield [n] + ps

    return f(k, k, 1)


#!/usr/bin/env python3

#courses/udacity_intro_to_tensorflow_for_deep_learning/l08c01_common_patterns.ipynb

import os

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

import tensorflow as tf
keras = tf.keras

from savefit import *

def plot_series(time, series, format="-", start=0, end=None, label=None):
    plt.plot(time[start:end], series[start:end], format, label=label)
    plt.xlabel("Time")
    plt.ylabel("Value")
    if label:
        plt.legend(fontsize=14)
    plt.grid(True)

## Trend and Seasonality
def trend(time, slope=0):
    return slope * time

# Let's create a time series that just trends upward:

time = np.arange(4 * 365 + 1)
baseline = 10
series = baseline + trend(time, 0.1)

plt.figure(figsize=(10, 6))
plot_series(time, series)
plt.show()

#time
#series

#Now let's generate a time series with a seasonal pattern:

def seasonal_pattern(season_time):
    """Just an arbitrary pattern, you can change it if you wish"""
    return np.where(season_time < 0.4,
                    np.cos(season_time * 2 * np.pi),
                    1 / np.exp(3 * season_time))

def seasonality(time, period, amplitude=1, phase=0):
    """Repeats the same pattern at each period"""
    season_time = ((time + phase) % period) / period
    return amplitude * seasonal_pattern(season_time)

amplitude = 40
series = seasonality(time, period=365, amplitude=amplitude)

plt.figure(figsize=(10, 6))
plot_series(time, series)
plt.show()

# Now let's create a time series with both trend and seasonality:

slope = 0.05
series = baseline + trend(time, slope) + seasonality(time, period=365, amplitude=amplitude)

plt.figure(figsize=(10, 6))
plot_series(time, series)
plt.show()

## Noise
#
# In practice few real-life time series have such a smooth signal. They usually
# have some noise, and the signal-to-noise ratio can sometimes be very low. Let's
# generate some white noise:

def white_noise(time, noise_level=1, seed=None):
    rnd = np.random.RandomState(seed)
    return rnd.randn(len(time)) * noise_level

noise_level = 5
noise = white_noise(time, noise_level, seed=42)

plt.figure(figsize=(10, 6))
plot_series(time, noise)
plt.show()

# Now let's add this white noise to the time series:

series += noise

plt.figure(figsize=(10, 6))
plot_series(time, series)
plt.show()

split_time = 1000
time_train = time[:split_time]
x_train = series[:split_time]
time_valid = time[split_time:]
x_valid = series[split_time:]

#courses/udacity_intro_to_tensorflow_for_deep_learning/l08c02_naive_forecasting.ipynb

# All right, this looks realistic enough for now. Let's try to forecast it. We
# will split it into two periods: the training period and the validation period
# (in many cases, you would also want to have a test period). The split will be
# at time step 1000.

## Naive Forecast
naive_forecast = series[split_time - 1:-1]

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid, label="Series")
plot_series(time_valid, naive_forecast, label="Forecast")
plt.show()

# Let's zoom in on the start of the validation period:

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid, start=0, end=150, label="Series")
plot_series(time_valid, naive_forecast, start=1, end=151, label="Forecast")
plt.show()

#You can see that the naive forecast lags 1 step behind the time series.

# Now let's compute the mean absolute error between the forecasts and the
# predictions in the validation period:

errors = naive_forecast - x_valid
abs_errors = np.abs(errors)
mae = abs_errors.mean()
mae

# That's our baseline, now let's try a moving average.

#courses/udacity_intro_to_tensorflow_for_deep_learning/l08c03_moving_average.ipynb

# Now let's compute the mean absolute error between the forecasts and the
# predictions in the validation period:

keras.metrics.mean_absolute_error(x_valid, naive_forecast).numpy()

# That's our baseline, now let's try a moving average.

## Moving Average

def moving_average_forecast(series, window_size):
  """Forecasts the mean of the last few values.
     If window_size=1, then this is equivalent to naive forecast"""
  forecast = []
  for time in range(len(series) - window_size):
    forecast.append(series[time:time + window_size].mean())
  return np.array(forecast)

def moving_average_forecast(series, window_size):
  """Forecasts the mean of the last few values.
     If window_size=1, then this is equivalent to naive forecast
     This implementation is *much* faster than the previous one"""
  mov = np.cumsum(series)
  mov[window_size:] = mov[window_size:] - mov[:-window_size]
  return mov[window_size - 1:-1] / window_size

moving_avg = moving_average_forecast(series, 30)[split_time - 30:]

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid, label="Series")
plot_series(time_valid, moving_avg, label="Moving average (30 days)")
plt.show()

keras.metrics.mean_absolute_error(x_valid, moving_avg).numpy()

# That's worse than naive forecast! The moving average does not anticipate trend
# or seasonality, so let's try to remove them by using differencing. Since the
# seasonality period is 365 days, we will subtract the value at time *t* – 365
# from the value at time *t*.

diff_series = (series[365:] - series[:-365])
diff_time = time[365:]

plt.figure(figsize=(10, 6))
plot_series(diff_time, diff_series, label="Series(t) – Series(t–365)")
plt.show()

#Focusing on the validation period:

plt.figure(figsize=(10, 6))
plot_series(time_valid, diff_series[split_time - 365:], label="Series(t) – Series(t–365)")
plt.show()

# Great, the trend and seasonality seem to be gone, so now we can use the moving average:

diff_moving_avg = moving_average_forecast(diff_series, 50)[split_time - 365 - 50:]

plt.figure(figsize=(10, 6))
plot_series(time_valid, diff_series[split_time - 365:], label="Series(t) – Series(t–365)")
plot_series(time_valid, diff_moving_avg, label="Moving Average of Diff")
plt.show()

# Now let's bring back the trend and seasonality by adding the past values from t – 365:

diff_moving_avg_plus_past = series[split_time - 365:-365] + diff_moving_avg

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid, label="Series")
plot_series(time_valid, diff_moving_avg_plus_past, label="Forecasts")
plt.show()

keras.metrics.mean_absolute_error(x_valid, diff_moving_avg_plus_past).numpy()

# Better than naive forecast, good. However the forecasts look a bit too random,
# because we're just adding past values, which were noisy. Let's use a moving
# averaging on past values to remove some of the noise:

diff_moving_avg_plus_smooth_past = moving_average_forecast(series[split_time - 370:-359], 11) + diff_moving_avg

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid, label="Series")
plot_series(time_valid, diff_moving_avg_plus_smooth_past, label="Forecasts")
plt.show()

keras.metrics.mean_absolute_error(x_valid, diff_moving_avg_plus_smooth_past).numpy()

# That's starting to look pretty good! Let's see if we can do better with a Machine Learning model.

# courses/udacity_intro_to_tensorflow_for_deep_learning/l08c04_time_windows.ipynb

## Setup

## Time Windows

# First, we will train a model to forecast the next step given the previous 20
# steps, therefore, we need to create a dataset of 20-step windows for training.

dataset = tf.data.Dataset.range(10)
for val in dataset:
    print(val.numpy())

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1)
for window_dataset in dataset:
    for val in window_dataset:
        print(val.numpy(), end=" ")
    print()

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1, drop_remainder=True)
for window_dataset in dataset:
    for val in window_dataset:
        print(val.numpy(), end=" ")
    print()

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1, drop_remainder=True)
dataset = dataset.flat_map(asis(lambda window: window.batch(5)))
for window in dataset:
    print(window.numpy())

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1, drop_remainder=True)
dataset = dataset.flat_map(asis(lambda window: window.batch(5)))
dataset = dataset.map(asis(lambda window: (window[:-1], window[-1:])))
for x, y in dataset:
    print(x.numpy(), y.numpy())

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1, drop_remainder=True)
dataset = dataset.flat_map(asis(lambda window: window.batch(5)))
dataset = dataset.map(asis(lambda window: (window[:-1], window[-1:])))
dataset = dataset.shuffle(buffer_size=10)
for x, y in dataset:
    print(x.numpy(), y.numpy())

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1, drop_remainder=True)
dataset = dataset.flat_map(asis(lambda window: window.batch(5)))
dataset = dataset.map(asis(lambda window: (window[:-1], window[-1:])))
dataset = dataset.shuffle(buffer_size=10)
dataset = dataset.batch(2).prefetch(1)
for x, y in dataset:
    print("x =", x.numpy())
    print("y =", y.numpy())

def window_dataset(series, window_size, batch_size=32,
                   shuffle_buffer=1000):
    dataset = tf.data.Dataset.from_tensor_slices(series)
    dataset = dataset.window(window_size + 1, shift=1, drop_remainder=True)
    dataset = dataset.flat_map(asis(lambda window: window.batch(window_size + 1)))
    dataset = dataset.shuffle(shuffle_buffer)
    dataset = dataset.map(asis(lambda window: (window[:-1], window[-1])))
    dataset = dataset.batch(batch_size).prefetch(1)
    return dataset

# Forecasting with machine learning
# courses/udacity_intro_to_tensorflow_for_deep_learning/l08c05_forecasting_with_machine_learning.ipynb

## Forecasting with Machine Learning
# First, we will train a model to forecast the next step given the previous 30 steps, therefore, we need to create a dataset of 30-step windows for training.

### Linear Model

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = window_dataset(x_train, window_size)
valid_set = window_dataset(x_valid, window_size)

model = keras.models.Sequential([
  keras.layers.Dense(1, input_shape=[window_size])
])
optimizer = keras.optimizers.SGD(lr=1e-5, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model, train_set, epochs=100, validation_data=valid_set, verbose=0)

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = window_dataset(x_train, window_size)

model = keras.models.Sequential([
  keras.layers.Dense(1, input_shape=[window_size])
])

lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-6 * 10**(epoch / 30))
optimizer = keras.optimizers.SGD(lr=1e-6, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model, train_set, epochs=100, callbacks=[lr_schedule],verbose=0)

if history:
  plt.semilogx(history.history["lr"], history.history["loss"])
  plt.axis([1e-6, 1e-3, 0, 20])

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = window_dataset(x_train, window_size)
valid_set = window_dataset(x_valid, window_size)

model = keras.models.Sequential([
  keras.layers.Dense(1, input_shape=[window_size])
])
optimizer = keras.optimizers.SGD(lr=1e-5, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
early_stopping = keras.callbacks.EarlyStopping(patience=10)
model,history = savefit(model, train_set, epochs=500,
          validation_data=valid_set,
          callbacks=[early_stopping],verbose=0)

def model_forecast(model, series, window_size):
    ds = tf.data.Dataset.from_tensor_slices(series)
    ds = ds.window(window_size, shift=1, drop_remainder=True)
    ds = ds.flat_map(asis(lambda w: w.batch(window_size)))
    ds = ds.batch(32).prefetch(1)
    forecast = model.predict(ds)
    return forecast

lin_forecast = model_forecast(model
                              , series[split_time - window_size:-1]
                              , window_size)[:, 0]

lin_forecast.shape

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, lin_forecast)
plt.show()

keras.metrics.mean_absolute_error(x_valid, lin_forecast).numpy()

### Dense Model Forecasting

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = window_dataset(x_train, window_size)

model = keras.models.Sequential([
  keras.layers.Dense(10, activation="relu", input_shape=[window_size]),
  keras.layers.Dense(10, activation="relu"),
  keras.layers.Dense(1)
])

lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-7 * 10**(epoch / 20))
optimizer = keras.optimizers.SGD(lr=1e-7, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model, train_set, epochs=100, callbacks=[lr_schedule],verbose=0)

if history:
  plt.semilogx(history.history["lr"], history.history["loss"])
  plt.axis([1e-7, 5e-3, 0, 30])
  plt.show()

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = window_dataset(x_train, window_size)
valid_set = window_dataset(x_valid, window_size)

model = keras.models.Sequential([
  keras.layers.Dense(10, activation="relu", input_shape=[window_size]),
  keras.layers.Dense(10, activation="relu"),
  keras.layers.Dense(1)
])

optimizer = keras.optimizers.SGD(lr=1e-5, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
early_stopping = keras.callbacks.EarlyStopping(patience=10)
model,history = savefit(model, train_set, epochs=500,
          validation_data=valid_set,
          callbacks=[early_stopping],verbose=0)

dense_forecast = model_forecast(
    model,
    series[split_time - window_size:-1],
    window_size)[:, 0]

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, dense_forecast)
plt.show()

keras.metrics.mean_absolute_error(x_valid, dense_forecast).numpy()

# Forecasting with an RNN
# courses/udacity_intro_to_tensorflow_for_deep_learning/l08c06_forecasting_with_rnn.ipynb

## Simple RNN Forecasting

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = window_dataset(x_train, window_size, batch_size=128)

model = keras.models.Sequential([
  keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1),
                      input_shape=[None]),
  keras.layers.SimpleRNN(100, return_sequences=True),
  keras.layers.SimpleRNN(100),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200.0)
])
lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-7 * 10**(epoch / 20))
optimizer = keras.optimizers.SGD(lr=1e-7, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model, train_set, epochs=100, callbacks=[lr_schedule],verbose=0)

if history:
  plt.semilogx(history.history["lr"], history.history["loss"])
  plt.axis([1e-7, 1e-4, 0, 30])

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = window_dataset(x_train, window_size, batch_size=128)
valid_set = window_dataset(x_valid, window_size, batch_size=128)

model = keras.models.Sequential([
  keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1),
                      input_shape=[None]),
  keras.layers.SimpleRNN(100, return_sequences=True),
  keras.layers.SimpleRNN(100),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200.0)
])
optimizer = keras.optimizers.SGD(lr=1.5e-6, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
early_stopping = keras.callbacks.EarlyStopping(patience=50)
model_checkpoint = keras.callbacks.ModelCheckpoint(
    "my_checkpoint", save_best_only=True)
model,history = savefit(model,train_set, epochs=500,
          validation_data=valid_set,
          callbacks=[early_stopping, model_checkpoint],verbose=0)

model = keras.models.load_model("my_checkpoint")

rnn_forecast = model_forecast(
    model,
    series[split_time - window_size:-1],
    window_size)[:, 0]

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, rnn_forecast)
plt.show()

keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()

## Sequence-to-Sequence Forecasting

def seq2seq_window_dataset(series, window_size, batch_size=32,
                           shuffle_buffer=1000):
    series = tf.expand_dims(series, axis=-1)
    ds = tf.data.Dataset.from_tensor_slices(series)
    ds = ds.window(window_size + 1, shift=1, drop_remainder=True)
    ds = ds.flat_map(asis(lambda w: w.batch(window_size + 1)))
    ds = ds.shuffle(shuffle_buffer)
    ds = ds.map(asis(lambda w: (w[:-1], w[1:])))
    return ds.batch(batch_size).prefetch(1)

for X_batch, Y_batch in seq2seq_window_dataset(tf.range(10), 3,
                                               batch_size=1):
    print("X:", X_batch.numpy())
    print("Y:", Y_batch.numpy())

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = seq2seq_window_dataset(x_train, window_size,
                                   batch_size=128)

model = keras.models.Sequential([
  keras.layers.SimpleRNN(100, return_sequences=True,
                         input_shape=[None, 1]),
  keras.layers.SimpleRNN(100, return_sequences=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200)
])
lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-7 * 10**(epoch / 30))
optimizer = keras.optimizers.SGD(lr=1e-7, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model,train_set, epochs=100, callbacks=[lr_schedule],verbose=0)

if history:
  plt.semilogx(history.history["lr"], history.history["loss"])
  plt.axis([1e-7, 1e-4, 0, 30])

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = seq2seq_window_dataset(x_train, window_size,
                                   batch_size=128)
valid_set = seq2seq_window_dataset(x_valid, window_size,
                                   batch_size=128)

model = keras.models.Sequential([
  keras.layers.SimpleRNN(100, return_sequences=True,
                         input_shape=[None, 1]),
  keras.layers.SimpleRNN(100, return_sequences=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200.0)
])
optimizer = keras.optimizers.SGD(lr=1e-6, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
early_stopping = keras.callbacks.EarlyStopping(patience=10)
model,history = savefit(model, train_set, epochs=500,
          validation_data=valid_set,
          callbacks=[early_stopping],verbose=0)

rnn_forecast = model_forecast(model, series[..., np.newaxis], window_size)
rnn_forecast = rnn_forecast[split_time - window_size:-1, -1, 0]

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, rnn_forecast)
plt.show()

keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()

#courses/udacity_intro_to_tensorflow_for_deep_learning/l08c07_forecasting_with_stateful_rnn.ipynb

def sequential_window_dataset(series, window_size):
    series = tf.expand_dims(series, axis=-1)
    ds = tf.data.Dataset.from_tensor_slices(series)
    ds = ds.window(window_size + 1, shift=window_size, drop_remainder=True)
    ds = ds.flat_map(asis(lambda window: window.batch(window_size + 1)))
    ds = ds.map(asis(lambda window: (window[:-1], window[1:])))
    return ds.batch(1).prefetch(1)

for X_batch, y_batch in sequential_window_dataset(tf.range(10), 3):
    print(X_batch.numpy(), y_batch.numpy())

class ResetStatesCallback(keras.callbacks.Callback):
    def on_epoch_begin(self, epoch, logs):
        self.model.reset_states()

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = sequential_window_dataset(x_train, window_size)

model = keras.models.Sequential([
  keras.layers.SimpleRNN(100, return_sequences=True, stateful=True,
                         batch_input_shape=[1, None, 1]),
  keras.layers.SimpleRNN(100, return_sequences=True, stateful=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200.0)
])
lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-8 * 10**(epoch / 30))
reset_states = ResetStatesCallback()
optimizer = keras.optimizers.SGD(lr=1e-8, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model, train_set, epochs=100,
                    callbacks=[lr_schedule, reset_states], verbose=0)

if history:
  plt.semilogx(history.history["lr"], history.history["loss"])
  plt.axis([1e-8, 1e-4, 0, 30])

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = sequential_window_dataset(x_train, window_size)
valid_set = sequential_window_dataset(x_valid, window_size)

model = keras.models.Sequential([
  keras.layers.SimpleRNN(100, return_sequences=True, stateful=True,
                         batch_input_shape=[1, None, 1]),
  keras.layers.SimpleRNN(100, return_sequences=True, stateful=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200.0)
])
optimizer = keras.optimizers.SGD(lr=1e-7, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
reset_states = ResetStatesCallback()
model_checkpoint = keras.callbacks.ModelCheckpoint(
    "my_checkpoint.h5", save_best_only=True)
early_stopping = keras.callbacks.EarlyStopping(patience=50)
model,history = savefit(model, train_set, epochs=500,
          validation_data=valid_set,
          callbacks=[early_stopping, model_checkpoint, reset_states], verbose=0)

model = keras.models.load_model("my_checkpoint.h5")

model.reset_states()
rnn_forecast = model.predict(series[np.newaxis, :, np.newaxis])
rnn_forecast = rnn_forecast[0, split_time - 1:-1, 0]

rnn_forecast.shape

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, rnn_forecast)
plt.show()

keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()

# courses/udacity_intro_to_tensorflow_for_deep_learning/l08c08_forecasting_with_lstm.ipynb
## LSTM RNN Forecasting

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = sequential_window_dataset(x_train, window_size)

model = keras.models.Sequential([
  keras.layers.LSTM(100, return_sequences=True, stateful=True,
                    batch_input_shape=[1, None, 1]),
  keras.layers.LSTM(100, return_sequences=True, stateful=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200.0)
])
lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-8 * 10**(epoch / 20))
reset_states = ResetStatesCallback()
optimizer = keras.optimizers.SGD(lr=1e-8, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model, train_set, epochs=100,
                    callbacks=[lr_schedule, reset_states],verbose=0)

if history:
  plt.semilogx(history.history["lr"], history.history["loss"])
  plt.axis([1e-8, 1e-4, 0, 30])

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = sequential_window_dataset(x_train, window_size)
valid_set = sequential_window_dataset(x_valid, window_size)

model = keras.models.Sequential([
  keras.layers.LSTM(100, return_sequences=True, stateful=True,
                         batch_input_shape=[1, None, 1]),
  keras.layers.LSTM(100, return_sequences=True, stateful=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200.0)
])
optimizer = keras.optimizers.SGD(lr=5e-7, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
reset_states = ResetStatesCallback()
model_checkpoint = keras.callbacks.ModelCheckpoint(
    "my_checkpoint.h5", save_best_only=True)
early_stopping = keras.callbacks.EarlyStopping(patience=50)
model,history = savefit(model, train_set, epochs=500,
          validation_data=valid_set,
          callbacks=[early_stopping, model_checkpoint, reset_states],verbose=0)

model = keras.models.load_model("my_checkpoint.h5")

rnn_forecast = model.predict(series[np.newaxis, :, np.newaxis])
rnn_forecast = rnn_forecast[0, split_time - 1:-1, 0]

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, rnn_forecast)
plt.show()

keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()

# courses/udacity_intro_to_tensorflow_for_deep_learning/l08c09_forecasting_with_cnn.ipynb

## Preprocessing With 1D-Convolutional Layers

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = seq2seq_window_dataset(x_train, window_size,
                                   batch_size=128)

model = keras.models.Sequential([
  keras.layers.Conv1D(filters=32, kernel_size=5,
                      strides=1, padding="causal",
                      activation="relu",
                      input_shape=[None, 1]),
  keras.layers.LSTM(32, return_sequences=True),
  keras.layers.LSTM(32, return_sequences=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200)
])
lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-8 * 10**(epoch / 20))
optimizer = keras.optimizers.SGD(lr=1e-8, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model, train_set, epochs=100, callbacks=[lr_schedule],verbose=0)

if history:
  plt.semilogx(history.history["lr"], history.history["loss"])
  plt.axis([1e-8, 1e-4, 0, 30])

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 30
train_set = seq2seq_window_dataset(x_train, window_size,
                                   batch_size=128)
valid_set = seq2seq_window_dataset(x_valid, window_size,
                                   batch_size=128)

model = keras.models.Sequential([
  keras.layers.Conv1D(filters=32, kernel_size=5,
                      strides=1, padding="causal",
                      activation="relu",
                      input_shape=[None, 1]),
  keras.layers.LSTM(32, return_sequences=True),
  keras.layers.LSTM(32, return_sequences=True),
  keras.layers.Dense(1),
  keras.layers.Lambda(lambda x: x * 200)
])
optimizer = keras.optimizers.SGD(lr=1e-5, momentum=0.9)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])

model_checkpoint = keras.callbacks.ModelCheckpoint(
    "my_checkpoint.h5", save_best_only=True)
early_stopping = keras.callbacks.EarlyStopping(patience=50)
model,history = savefit(model, train_set, epochs=500,
          validation_data=valid_set,
          callbacks=[early_stopping, model_checkpoint],verbose=0)

model = keras.models.load_model("my_checkpoint.h5")

rnn_forecast = model_forecast(model, series[:,  np.newaxis], window_size)
rnn_forecast = rnn_forecast[split_time - window_size:-1, -1, 0]

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, rnn_forecast)
plt.show()

keras.metrics.mean_absolute_error(x_valid, rnn_forecast).numpy()

## Fully Convolutional Forecasting

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 64
train_set = seq2seq_window_dataset(x_train, window_size,
                                   batch_size=128)

model = keras.models.Sequential()
model.add(keras.layers.InputLayer(input_shape=[None, 1]))
for dilation_rate in (1, 2, 4, 8, 16, 32):
    model.add(
      keras.layers.Conv1D(filters=32,
                          kernel_size=2,
                          strides=1,
                          dilation_rate=dilation_rate,
                          padding="causal",
                          activation="relu")
    )
model.add(keras.layers.Conv1D(filters=1, kernel_size=1))
lr_schedule = keras.callbacks.LearningRateScheduler(
    lambda epoch: 1e-4 * 10**(epoch / 30))
optimizer = keras.optimizers.Adam(lr=1e-4)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])
model,history = savefit(model, train_set, epochs=100, callbacks=[lr_schedule],verbose=0)

if history:
  plt.semilogx(history.history["lr"], history.history["loss"])
  plt.axis([1e-4, 1e-1, 0, 30])

keras.backend.clear_session()
tf.random.set_seed(42)
np.random.seed(42)

window_size = 64
train_set = seq2seq_window_dataset(x_train, window_size,
                                   batch_size=128)
valid_set = seq2seq_window_dataset(x_valid, window_size,
                                   batch_size=128)

model = keras.models.Sequential()
model.add(keras.layers.InputLayer(input_shape=[None, 1]))
for dilation_rate in (1, 2, 4, 8, 16, 32):
    model.add(
      keras.layers.Conv1D(filters=32,
                          kernel_size=2,
                          strides=1,
                          dilation_rate=dilation_rate,
                          padding="causal",
                          activation="relu")
    )
model.add(keras.layers.Conv1D(filters=1, kernel_size=1))
optimizer = keras.optimizers.Adam(lr=3e-4)
model.compile(loss=keras.losses.Huber(),
              optimizer=optimizer,
              metrics=["mae"])

model_checkpoint = keras.callbacks.ModelCheckpoint(
    "my_checkpoint.h5", save_best_only=True)
early_stopping = keras.callbacks.EarlyStopping(patience=50)
model,history = savefit(model, train_set, epochs=500,
                    validation_data=valid_set,
                    callbacks=[early_stopping, model_checkpoint],verbose=0)

model = keras.models.load_model("my_checkpoint.h5")

cnn_forecast = model_forecast(model, series[..., np.newaxis], window_size)
cnn_forecast = cnn_forecast[split_time - window_size:-1, -1, 0]

plt.figure(figsize=(10, 6))
plot_series(time_valid, x_valid)
plot_series(time_valid, cnn_forecast)
plt.show()

keras.metrics.mean_absolute_error(x_valid, cnn_forecast).numpy()


# Databricks notebook source
# MAGIC %md
# MAGIC ScaDaMaLe Course [site](https://lamastex.github.io/scalable-data-science/sds/3/x/) and [book](https://lamastex.github.io/ScaDaMaLe/index.html)

# COMMAND ----------

# MAGIC %md
# MAGIC # Analyse the eigenvalue spectrum

# COMMAND ----------

# MAGIC %md
# MAGIC - Load the singular values computed in 03_compute_rsvd, sort them and convert to eigenvalues taking the square
# MAGIC - Plot the spectrum for each graph in a semi-log plot for comparison

# COMMAND ----------

import pyspark.sql.functions as F
import numpy as np
import pandas as pd
import seaborn as sns
%matplotlib inline
import matplotlib.pyplot as plt

# COMMAND ----------

# MAGIC %md
# MAGIC ### Function for getting sorted eigenvalues of graph Laplacian L from singular values of incidence matrix B

# COMMAND ----------

def to_eigen(singular_values):
  singular_values = singular_values.sort_values(by='value', ascending=False)
  eigen_values = np.power(singular_values, 2)
  return eigen_values

# COMMAND ----------

# MAGIC %md
# MAGIC ### Get eigenvalues of Ethereum graph

# COMMAND ----------

data_path = "/projects/group21/test_ethereum_SingularValues"
singular_values_eth = spark.read.format('parquet').load(data_path).toPandas()
eigen_values_eth = to_eigen(singular_values_eth)

# COMMAND ----------

# MAGIC %md
# MAGIC ### Get eigenvalues of Erdös-Renyi graphs

# COMMAND ----------

eigen_values_uniform = []
for i in range(10):
  data_path = "/projects/group21/uniform_random_graph_SingularValues" + str(i)
  singular_values = spark.read.format('parquet').load(data_path).toPandas()
  eigen_values_uniform.append(to_eigen(singular_values))

# COMMAND ----------

# MAGIC %md
# MAGIC ### Get eigenvalues of R-MAT graphs

# COMMAND ----------

eigen_values_rmat = []
for i in range(10):
  data_path = "/projects/group21/rmat_random_graph_SingularValues" + str(i)
  singular_values = spark.read.format('parquet').load(data_path).toPandas()
  eigen_values_rmat.append(to_eigen(singular_values))

# COMMAND ----------

# MAGIC %md
# MAGIC ### Plot sorted eigenvalues for all graphs

# COMMAND ----------

colors = sns.color_palette()
fig, ax = plt.subplots(figsize=(16, 9))
x = np.arange(len(eigen_values_eth))
ax = sns.lineplot(x=x, y=eigen_values_eth.to_numpy().ravel(), color=colors[0], label='ethereum')
for i in range(9):
  ax = sns.lineplot(x=x, y=eigen_values_uniform[i].to_numpy().ravel(), color=colors[1], alpha=0.4)
  ax = sns.lineplot(x=x, y=eigen_values_rmat[i].to_numpy().ravel(), color=colors[2], alpha=0.4)

ax = sns.lineplot(x=x, y=eigen_values_uniform[9].to_numpy().ravel(), color=colors[1], alpha=0.4, label='erdös-renyi')
ax = sns.lineplot(x=x, y=eigen_values_rmat[9].to_numpy().ravel(), color=colors[2], alpha=0.4, label='rmat')
ax.set_yscale('log')
ax.legend()

# COMMAND ----------

# MAGIC %md
# MAGIC ## Conclusion
# MAGIC
# MAGIC We observe a large descrepency in the spectrums between the Erdös-Renyi, R-MAT and Ethereum transaction graphs.
# MAGIC As can be expected, the spectrum of the Erdös-Renyi graphs is almost constant due to the isotropy of the graph topology.
# MAGIC The Ethereum transaction graph has very large eigenvalues compared to the random graphs.
# MAGIC A likely explanation is the presence of nodes of very high degree in the graph.
# MAGIC
# MAGIC We can see that the R-MAT graph lies in between uniform Erdös-Renyi and Ethereum graph.
# MAGIC This is also as expected since the R-MAT model is designed to better mimic the behaviour of real graphs.
# MAGIC In this project we used the default parameters for the R-MAT graph and it is likely that with further experimentation one could find a setting which better fit the spectum of the transaction graph.


# mutlinomial_distribution
# a generalization of binomial distribution
# outcomes of muti-nomial scenarios unlike binomial
# where scenarios must be only one of two.
# e.g. Blood type of a population, dice roll outcome

"""
It has three parameters:
n - number of possible outcomes(e.g. 6 for dice roll).
pvals - list of probabilities of outcomes(e.g. [1/6, 1/6, 1/6, 1/6, 1/6, 1/6] for dice roll)
size - The shape of the returned array
"""

# draw out a sample for dice roll

from numpy import random

x = random.multinomial(n = 6, pvals =[1/6, 1/6, 1/6, 1/6, 1/6, 1/6])

print(x)

# visualization of multinomial distributions

# from numpy import random
import matplotlib.pyplot as plt
import seaborn as sns

sns.distplot(random.multinomial(n = 6, pvals =[1/6, 1/6, 1/6, 1/6, 1/6, 1/6], size = 1000), hist = False)
plt.show()


from utils import util
import numpy as np

# 一维梯度下降
def gd(eta):
    x = 10
    results = [x]
    for i in range(x):
        x -= eta * 2 * x  # 一维目标函数f(x) = x * x
        results.append(x)
    print('epoch 10, x:', x)
    return results

# 显示一维轨迹
def show_trace(res):
    n = max(abs(min(res)), abs(max(res)), 10)
    f_line = np.arange(-n, n, 0.1)
    util.set_figsize()
    util.plt.plot(f_line, [x * x for x in f_line])  # 显示f(x)=x*x的图像
    util.plt.plot(res, [x * x for x in res], '-o')  # 显示梯度下降图像
    util.plt.xlabel('x')
    util.plt.ylabel('f(x)')
    util.plt.show()

# 使用二维梯度下降来训练
def train_2d(trainer):
    x1, x2, s1, s2 = -5, -2, 0, 0
    results = [(x1, x2)]
    for i in range(20):
        x1, x2, s1, s2 = trainer(x1, x2, s1, s2)
        results.append((x1, x2))
    print('epoch %d, x1 %f, x2 %f' % (20, x1, x2))
    return results

# 显示二维轨迹
def show_trace_2d(f, results):
    util.plt.plot(*zip(*results), '-o', color='#ff7f0e')
    x1, x2 = np.meshgrid(np.arange(-5.5, 1.0, 0.1), np.arange(-3.0, 1.0, 0.1))
    util.plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')
    util.plt.xlabel('x1')
    util.plt.ylabel('x2')
    util.plt.show()

# 二维目标函数
def f_2d(x1, x2):
    return x1 ** 2 + 2 * x2 ** 2

# 二维梯度下降
def gd_2d(x1, x2, s1, s2):
    return (x1 - eta * 2 * x1, x2 - eta * 4 * x2, 0, 0)

# 二维随机梯度下降（增加随机噪声来模拟随机梯度下降）
def sgd_2d(x1, x2, s1, s2):
    return (x1 - eta * (2 * x1 + np.random.normal(0.1)),
            x2 - eta * (4 * x2 + np.random.normal(0.1)), 0, 0)

if __name__ == '__main__':
    # 一维梯度下降
    # 学习率eta=0.2, 学习率合适，在x=0.06处已经效果明显
    res = gd(0.2)
    show_trace(res)

    # 学习率eta=0.05, 学习率过小，梯度下降较慢
    res = gd(0.05)
    show_trace(res)

    # 学习率eta=1.1, 学习率过大，左右摇摆
    res = gd(1.1)
    show_trace(res)

    # 二维梯度下降
    eta = 0.1
    f = f_2d
    trainer = gd_2d
    res2 = train_2d(trainer)
    show_trace_2d(f, res2)

    # 二维随机梯度下降
    show_trace_2d(f_2d, train_2d(sgd_2d))


import numpy as np

from optimizer import Optimizer

class Sr1(Optimizer):
    """
    Quasi-Newton algorithm with Symmetric Rank 1 (SR1) update. See
        https://arxiv.org/pdf/2002.00657.pdf
    for a formal description and convergence proof of a similar method.

    The stability condition is from
        p. 145 in (J. Nocedal and S. J. Wright, "Numerical Optimization", 2nd edtion)

    Arguments:
        L (float, optional): an upper bound on the smoothness constant
            to initialize the Hessian estimate
        hess_estim (float array of shape (dim, dim)): initial Hessian estimate
        lr (float, optional): stepsize (default: 1)
        stability_const (float, optional): a constant from [0, 1) that ensures a curvature
            condition before updating the Hessian-inverse estimate (default: 1e-8)
    """

    def __init__(self, L=None, hess_estim=None, lr=1, stability_const=1e-8, *args, **kwargs):
        super(Sr1, self).__init__(*args, **kwargs)
        if L is None and hess_estim is None:
            L = self.loss.smoothness
            if L is None:
                raise ValueError("Either smoothness constant L or Hessian estimate must be provided")
        if not 0 <= stability_const < 1:
            raise ValueError("Invalid stability parameter: {}".format(stability_const))
        self.lr = lr
        self.L = L
        self.B = hess_estim
        self.stability_const = stability_const

    def step(self):
        self.grad = self.loss.gradient(self.x)
        x_new = self.x - self.lr * self.B_inv @ self.grad
        if self.line_search is not None:
            x_new = self.line_search(self.x, x_new)

        s = x_new - self.x
        grad_new = self.loss.gradient(x_new)
        y = grad_new - self.grad
        self.grad = grad_new
        Bs = self.B @ s
        sBs = s @ Bs
        B_inv_y = self.B_inv @ y
        y_B_inv_y = y @ B_inv_y
        y_s = y @ s
        if abs(y_s-sBs) > self.stability_const * self.loss.norm(s) * self.loss.norm(y-Bs) and y_s!=y_B_inv_y:
            self.B += np.outer(y-Bs, y-Bs) / (y_s-sBs)
            self.B_inv += np.outer(s-B_inv_y, s-B_inv_y) / (y_s-y_B_inv_y)
        self.x = x_new

    def init_run(self, *args, **kwargs):
        super(Sr1, self).init_run(*args, **kwargs)
        if self.B is None:
            self.B = self.L * np.eye(self.loss.dim)
            self.B_inv = 1 / self.L * np.eye(self.loss.dim)
        else:
            self.B_inv = np.linalg.pinv(self.B)
        self.grad = self.loss.gradient(self.x)


'''
Arithmetic with numpy arrays
-----------------------------------

Arrays are important because they enable you to express batch operations on data
without writing any for loops. This is also called as vectorization. Any arithmetic
operations between equal-size arrays applies the operation element-wise
'''
import numpy as np

arr = np.array([[1.2,3.4, 5.6],[4,5,6]])
print(arr)

print(arr * arr)
print(1/arr)
print(arr ** 0.5)
print(arr - arr)

arr2 = np.array([[1.22,3.24, 15.6],[14,5,6]])

print(arr2 > arr) # comparing elementwise of similar sized arrays yeilds boolean array


#!/usr/bin/python3
# -*- coding: utf-8 -*-

import numpy as np

a = np.array([
    [1, 1, 1],
    [2, 2, 2],
    [3, 3, 3]
    ])

a1 = [
    [1, 1, 1],
    [2, 2, 2],
    [3, 3, 3]
    ]

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

b1 = [
    [1, 0, 1],
    [2, 0, 2],
    [3, 0, 3]
    ]

# 转变成矩阵
c = np.mat(a)
d = np.mat(b)
f = np.mat(a1)
g = np.mat(b1)

print("a = ", a, "\na1 = ", a1)
print("b = ", b, "\nb1 = ", b1)
print("a*2 = ", a*2, "\na1*2 = ", a1*2) # 这个结果很特别， a1*2元素多了一倍，元素值没有变化
print("a+b = ", a + b)
print("a1+b1 = ", a1 + b1) # 两个数组集合的合并
print("a*b = ", a * b) # 数组相乘， not 矩阵相乘
#  print(a1 * b1) # 错误

print("c = ", c)
print("d = ", d)
print("c*d = ", c * d) # 矩阵相乘
print("f = ", f)
print("g = ", g)
print("f*g = ", f * g)


# -*- coding: utf-8 -*-
"""
Coursework 1: Linear regression
"""

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

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

# Normal equation for exact computation of theta
def ecuacion_normal(X, Y):
    X_t = np.transpose(X)
    Aux = np.dot(X_t,X)
    X_plus = np.linalg.pinv(Aux).dot(X_t)
    return X_plus.dot(Y)

# Normalize dataset
def normalizar(X):
    mu = np.mean(X,  axis=0)
    sigma = np.std(X, axis=0)
    X_norm = (X - mu) / sigma
    return X_norm, mu, sigma

# Compute the gradient of the cost function
def gradiente(X, Y, theta):
    res = np.zeros((np.shape(X)[1],1))
    m = np.shape(X)[0]
    H = np.dot(X,theta)
    res = 1/m * np.dot(np.transpose(X), H-Y)
    return res

# Compute the cost function
def coste(X, Y, theta):
    H = np.dot(X, theta)
    Aux = (H - Y)**2
    return Aux.sum() / (2*len(X))

# Compute gradient descent for a maximum of maxIter iterations or until
# the norm of the gradient is lower than eps
def descenso_gradiente(X, Y, alpha, maxIter, eps):
    theta = np.zeros((np.shape(X)[1],1))
    grad = gradiente(X, Y, theta)
    it = 0
    costes = []
    while np.linalg.norm(grad) > eps and it < maxIter:
        theta = theta - alpha*grad
        grad = gradiente(X, Y, theta)
        costes.append(coste(X, Y, theta))
        it += 1
    return theta, costes

#%%
"""
One variable case
"""
datos = carga_csv('ex1data1.csv')
X = datos[:,:-1]
m = np.shape(X)[0]
Y = datos[:,-1]

# Add the column of 1s
X=np.hstack([np.ones([m,1]),X])
Y = Y[:,np.newaxis]

alpha = 0.01
maxIter = 1500
eps = 1e-3
Thetas, costes = descenso_gradiente(X, Y, alpha, maxIter, eps)

# Plot data an line
x = np.linspace(0,23,23)
plt.scatter(X[:,1],Y,s=15)
plt.plot(x, Thetas[0] + Thetas[1]*x, c='r')
plt.xlabel('Población de la ciudad en 10 000s')
plt.ylabel('Ingresos en $10 000s')

#%%
"""
Plot cost function
"""
# Plot function in 3D
fig = plt.figure()
ax = fig.gca(projection = '3d')

Th0 = np.arange(-10,10,0.05)
Th1 = np.arange(-1,4,0.05)
Th0m,Th1m = np.meshgrid(Th0,Th1)
cost = np.zeros((len(Th1),len(Th0)))
for i in range(len(Th1)) :
    for j in range(len(Th0)):
        cost[i][j] = coste(X,Y, np.array([[Th0[j]],[Th1[i]]]))
curve = ax.plot_surface(Th0m,Th1m,cost, cmap = cm.rainbow)
ax.view_init(elev = 15, azim=230)
plt.xlabel(r'$\theta_{0}$')
plt.ylabel(r'$\theta_{1}$')
ax.set_zlabel(r'$J(\theta)$',rotation=0)

# Plot function as contours
plt.figure()
plt.contour(Th0m,Th1m,cost, np.logspace(-2,3,20))
plt.scatter(Thetas[0],Thetas[1], marker='X',c='r')
plt.xlabel(r'$\theta_{0}$')
plt.ylabel(r'$\theta_{1}$')

#%%
"""
Check for multivariate case different learning rates alpha
"""
datos = carga_csv('ex1data2.csv')
X_old = datos[:,:-1]
m = np.shape(X_old)[0]
Y_old=datos[:,-1]

# Normalize data
X, mu_x, sigma_x = normalizar(X_old)

# Add the column of 1s
X_old = np.hstack([np.ones([m,1]),X_old])
X = np.hstack([np.ones([m,1]),X])
Y = Y_old[:,np.newaxis]

alpha_arr = [0.3,0.1,0.03,0.01,0.003,0.001]
maxIter = 1500
eps = 1e-3
plt.figure()
for alpha in alpha_arr:
    Thetas, costes = descenso_gradiente(X, Y, alpha, maxIter, eps)
    plt.plot(costes, label = ("With alpha = " + str(alpha)))
plt.legend()
plt.xlabel('Iterations')
plt.ylabel(r'$J(\theta)$')
#%%
"""
Multivariate case
"""
datos = carga_csv('ex1data2.csv')
X_old = datos[:,:-1]
m = np.shape(X_old)[0]
Y_old=datos[:,-1]

# Normalize data
X, mu_x, sigma_x = normalizar(X_old)

# Add the column of 1s
X_old = np.hstack([np.ones([m,1]),X_old])
X = np.hstack([np.ones([m,1]),X])
Y = Y_old[:,np.newaxis]

alpha = 0.3
maxIter = 1500
eps = 1e-3
Thetas, costes = descenso_gradiente(X, Y, alpha, maxIter, eps)

# Compute with the normal equation and compare
th = ecuacion_normal(X_old,Y)
test = np.array([1650,3])
test_n = (test - mu_x)/sigma_x
test = np.hstack([1,test])
test_n = np.hstack([1,test_n])
price = np.dot(test,th)[0]
price_n = np.dot(test_n,Thetas)[0]
print("The value computed with the normal equation for x = [1650,3] is y = ", price)
print("The value computed with gradient descent for x = [1650,3] is y = ", price_n)
print("The relative distance between the two values is ", np.abs((price - price_n)/ price))


#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# author: bigfoolliu

"""
神经网络常用的激励函数(激活函数)：

如果不用激励函数那么每一层节点的输入都是上层输出的线性函数，无论层数，与没有隐藏层效果相当，
引入非线性函数作为激励函数，可以使输入逼近任意函数，神经网络的表达能力就更加强大

如何选择激活函数：
1. 深度学习往往需要大量时间来处理大量数据，因此总体上尽量选择具有zero-cntered特点的激活函数，可以加快模型的收敛速度；
2. 使用relu需要小心设置learning rate,否则易出现'dead'神经元；
3. 尽量不用sigmoid，可以试试tanh,预期效果不如relu和maxout
"""
import math
import matplotlib.pyplot as plt
import numpy as np

def sigmoid(z):
    """
    常用非线性激活函数之一
    数学形式：f(z)=1/(1+e ** (-z))
    特点：能够将输入的连续实值变换为0和1之间的输出，如果是非常大的负数，则输出为0，如果是非常大的正数则输出为1
    缺点：深度神经网络中梯度反向传播时导致梯度爆炸(概率较小)和梯度消失(概率较大);输出(output)不是0均值(0-centered);解析式中含有幂运算，
    规模大的深度网络会较大增加训练时间
    :param z:
    :return:
    """
    ret = 1 / (1 + math.e ** (-z))
    return ret

def tanh(x):
    """
    数学形式：tanh(x)=(e ** x - e ** (-x)) / (e ** x + e ** (-x))
    """
    ret = (math.e ** x - math.e ** (-x)) / (math.e ** x + math.e ** (-x))
    return ret

def relu(x):
    """
    数学形式：relu=max(0, x)
    :param x: array
    """
    ret = []
    for i in x:
        ret.append(max(0, i))
    return np.array(ret)

def leaky_relu(x, a=0.01):
    """
    数学形式：f(x)=max(ax, x), a通常为0.01
    :param x: array
    :param a: float
    """
    ret = []
    for i in x:
        ret.append(max(a * i, i))
    return np.array(ret)

def elu(x, a=0.01):
    """
    Exponential Liner Units
    数学形式：f(x)=x (x>0); a(e ** x - 1) (x<=0)
    """
    ret = []
    for i in x:
        if i > 0:
            ret.append(i)
        else:
            ret.append(a * (math.e ** i - 1))
    return np.array(ret)

def max_out():
    """
    是深度学习网络中的一层网络，就像池化层，卷积层一样
    """
    pass

# 测试用
# z = float(input("输入z:"))
# print("[INFO]sigmoid ret:{}".format(sigmoid(z)))

fig = plt.figure()
ax = plt.Axes(fig, [-10, 10, 1, 1])
z = np.linspace(-10, 10, num=500)

plt.subplot(231)
# sigmoid
sigmoid_ret = sigmoid(z)
plt.plot(z, sigmoid_ret, color="r", label="sigmoid")
# tanh
tanh_ret = tanh(z)
plt.plot(z, tanh_ret, color="g", label="tanh")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()

# relu
plt.subplot(232)
relu_ret = relu(z)
plt.plot(z, relu_ret, color="b", label="relu")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()

# leaky_relu
plt.subplot(233)
leaky_relu_ret = leaky_relu(z, 0.02)
plt.plot(z, leaky_relu_ret, color="r", label="leaky_relu")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()

# elu
plt.subplot(234, xlabel="x", ylabel="y")
elu_ret = elu(z, 0.02)
plt.plot(z, elu_ret, color="g", label="elu")
plt.legend()
plt.show()


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

f0 = 440.0

σ = 0.03
Σ = 3.0
K = 5
decr = 1

def repr_classe(f0,fmin,fmax):
    if fmin <= f0 < fmax: return f0
    elif f0 < fmin: return repr_classe(2*f0,fmin,fmax)
    elif f0 >= fmax: return repr_classe(f0/2,fmin,fmax)
f0 = repr_classe(f0,261.0,522.0)

#######################
# Spectre en gaussiennes

n = np.arange(0,16,0.001)
p0 = np.log2(261)
# f0 = 440
freq = np.exp(np.log(2)*n)

E = np.exp(-(n - p0)**2 / (2 * Σ**2))
norm = 0
S = np.zeros(np.shape(n))

for k in range(1,K+1):
    f = repr_classe(k*f0,261.0,522.0)
    p = np.log2(f)
    for i in range(-9,9):
        if 0 < p +i < 16:
            S += (1/k**decr) * np.exp(-(n - (p+i))**2 / (2 * σ**2))
    if np.log2(k).is_integer():
        norm += 1/k**decr

plot = plt.figure()
plt.xscale('log',basex = 2)
plt.plot(freq,norm*E, ls = '--')
# plt.plot(freq,S*E, label = 'Pitch-class {} Hz'.format(int(f0)))
plt.plot(freq,S*E, label = 'Pitch-class La')
plt.xlabel('Frequence (Hz)')
plt.ylabel('Amplitude')
plt.title('Spectre de Shepard' + '\nK : {}, σ : {}, decr : {}'.format(K,σ,decr))
plt.legend(frameon=True, framealpha=0.75)
plt.show()

#######################
# Spectre en pics

# f0 = 440.0
#
# Σ = 3.0
# K, decr = 5, 1
# n = np.arange(0,16,0.001)
# p0 = np.log2(261)
# E = np.exp(-(n - p0)**2 / (2 * Σ**2))
# # f0 = 440
# freq = np.exp(np.log(2)*n)
#
#
#
# def spectre_pic(f0):
#
#     dic_spectre = {}
#     def repr_classe(f0,fmin,fmax):
#         if fmin <= f0 < fmax: return f0
#         elif f0 < fmin: return repr_classe(2*f0,fmin,fmax)
#         elif f0 >= fmax: return repr_classe(f0/2,fmin,fmax)
#     f = repr_classe(f0,261.0,522.0)
#     p0 = np.log2(261)
#     Σ = 3.0
#
#     # Construction de dic_spectre
#     for k in range(1,K+1):
#         f = repr_classe(k*f0,261.0,522.0)
#         p = np.log2(f)
#         for i in range(-8,8):
#             if 0 < p + i < 16:
#                 fp = f*2**i
#                 if fp in dic_spectre:
#                     dic_spectre[fp] += (1/k**decr)* np.exp(-(np.log2(fp) - p0)**2 / (2 * Σ**2))
#                 else : dic_spectre[fp] = (1/k**decr)* np.exp(-(np.log2(fp) - p0)**2 / (2 * Σ**2))
#
#     return dic_spectre
#
# norm = 0
# for k in range(1,K+1):
#     if np.log2(k).is_integer():
#         norm += 1/k**decr
#
# dic_spectre = spectre_pic(f0)
# # print(dic_spectre)
#
# plot = plt.figure()
# plt.xscale('log',basex = 2)
# plt.plot(freq,norm*E, ls = '--')
# leg = False
# for f in dic_spectre:
#     if not leg:
#         # plt.vlines(f, 0, dic_spectre[f], alpha=0.9, label = 'Pitch-class {} Hz'.format(int(f0)))
#         plt.vlines(f, 0, dic_spectre[f], alpha=0.9, label = 'Pitch-class La')
#         leg = True
#     else: plt.vlines(f, 0, dic_spectre[f], alpha=0.9)
# # plt.plot(freq,S*E)
# plt.xlabel('Frequence (Hz)')
# plt.ylabel('Amplitude')
# plt.title('Spectre de Shepard en pics' + '\nK : {}, decr : {}'.format(K,decr))
# plt.legend(frameon=True, framealpha=0.75)
# plt.show()


'''
Distance measures (not necessarily metrics) between probability
distributions.
'''

import numpy as np

def kullback_leibler(p, q):
    '''
    Calculates the Kullback--Leibler divergence between two discrete
    probability distributions.

    :param p: First discrete probability distribution
    :param q: Second discrete probability distribution

    :return: Value of Kullback--Leibler divergence
    '''

    # TODO: this should not be necessary
    p = np.abs(p)
    q = np.abs(q)

    p += 1e-8
    q += 1e-8

    return np.sum(p * np.log(q / p))

    # FIXME: why does this not work? Even if the two conditions are
    # chained together...
    #return np.sum(np.where(p != 0, p * np.log(q / p), 0))

def jensen_shannon(p, q):
    '''
    Calculates the Jensen--Shannon divergence between two discrete
    probability distributions.

    :param p: First discrete probability distribution
    :param q: Second discrete probability distribution

    :return: Value of Jensen--Shannon divergence
    '''

    return 0.5 * (kullback_leibler(p, q) + kullback_leibler(q, p))


# 3.10 矩阵与线性代数运算

import numpy as np
m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])
print(m)

# Return transpose
print(m.T)

# Return inverse
print(m.I)

# Create a vector and multiply
v = np.matrix([[2],[3],[4]])
print(v)
print(m * v)

import numpy.linalg

# Determinant
numpy.linalg.det(m)

# Eigenvalues
numpy.linalg.eigvals(m)

# Solve for x in mx = v
x = numpy.linalg.solve(m, v)
print(x)
print(m * x)
print(v)


# -*- coding: utf-8 -*-

import numpy as np

def compute_PCA(X, mean=True, k=0, A=None, normalize=True):
    """
    PCA w.r.t A-norm, where A is a positive definite matrix.
    X : column-wise data set with size n-by-m, i.e. m samples of n dimension
    k : number of modes required (not include mean)
    normalize : whether to normalize eigenvectors w.r.t A
    Output :
        phi : n-by-(k+1)
        w   : eigenvalues
    """

    n, m = X.shape
    phi = np.zeros((n, k + 1))
    if mean:
        phi[:, 0] = X.mean(1)
        X = X - np.tile(phi[:, 0].reshape((-1, 1)), (1, m))
        s3 = 'Nonzero mean'
    else:
        s3 = 'Zero mean'

    if A is None:
        w, v = np.linalg.eigh(X.transpose() @ X)
        s1 = 'without norm'
    else:
        w, v = np.linalg.eigh(X.transpose() @ A @ X)
        s1 = 'with norm-A'
    w = w[::-1]
    v = v[:, ::-1]
    w = np.sqrt(np.absolute(w))
    if normalize:
        phi[:, 1:] = (X @ v[:, 0:k]) / np.tile(w[0:k].reshape((1, -1)), (n, 1))
        s2 = 'normalized eigenvectors'
    else:
        phi[:, 1:] = X @ v[:, 0:k]
        s2 = 'unnormalized eigenvectors'
    # print('Done PCA : (X %d-by-%d)' % (n, m) + s2 + ' ' + s1 + '. ' + s3 + ' + %d dominant eigenvectors' % (k))
    return phi, w


import numpy as np

########################################Ex-1#####################################

A = np.array([3,4,6,10,24,89,45,43,46,99,100])

div3 = A[A%3!=0]
print("Elements of A not divisible by 3:")
print(div3)

div5 = A[A%5==0]
print("Elements of A divisible by 5:")
print(div5)

print("Elements of A, which are divisible by 3 and 5:")
print(A[(A%3==0) & (A%5==0)])
print("------------------")

#

A[A%3==0] = 42
print("""New values of A after setting the elements of A,
which are divisible by 3, to 42:""")
print(A)

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

#####Calculate prime number between 0 and 100 using boolean array #####################
import numpy as np
is_prime = np.ones((100,), dtype=bool)

# Cross out 0 and 1 which are not primes:
is_prime[:2] = 0

# cross out its higher multiples (sieve of Eratosthenes):
nmax = int(np.sqrt(len(is_prime)))
for i in range(2, nmax):
    is_prime[2*i::i] = False

print(np.nonzero(is_prime))

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


import numpy as np
import sys
import matplotlib.pyplot as pl

## returns the predicted y (based on feature dataset (X) and weights (Theta))
def f(X,th):
    return X.dot(th.T)

## returns the cost (residual error) for predicted y
def J(X,y,th):
  m=X.shape[0]
  y_hat=X.dot(th.T)
  cost=np.sum((y_hat-y)**2)/(2*m)
  return cost

## finds the "good" values for the weigths (Theta)
## Gradient descent (optmization function)
def GD (X,y,th,alpha,niters):
  m=X.shape[0]
  cost=np.zeros((niters,1))
  for i in range(0,niters):
    y_hat=X.dot(th.T)
    error=((y_hat-y)*X).sum(0)/m
    th = th-(alpha*(error))
    cost[i]=J(X,y,th)
  return th,cost

def featureScaling(X):
  X=X-np.min(X,0)
  den=np.max(X,0)-np.min(X,0)
  return X/den

def RMSE(X,y,th):
	m=X.shape[0]
	y_hat=f(X,th)
	error=((y_hat-y)**2).sum()/m
	return np.sqrt(error)

if __name__ == "__main__":
    if len(sys.argv) < 1:
        print('Usage: python %s <dataset file name (full path)>' % sys.argv[0])
        exit(0)

    try:
        fin=open(sys.argv[1]) ## open the dataset file
    except:
        print('Could not open',sys.argv[1])
        exit(0)
    ## rows will be a list of list, e.g., [[example 1],[example 2],...[example m]]
    rows=[l.split(',') for l in fin.readlines()] ## the values are separated by comma
    m=len(rows) ## how many lists rows has, i.e., how many examples in the dataset
    ## how many features are in a given list
    d = len(rows[0])-1 ## we subtract the result from 1 to discard the label (y)
    y = np.array([l[d:d+1] for l in rows],dtype=float) ## vector of dataset labels
    X = np.array([l[0:d] for l in rows],dtype=float) ## matrix of dataset features
    ### Feature scaling
    Xori=X.copy() ## save the original X
    X=featureScaling(X)
    Theta=np.zeros((1,d+1)) ## Initialize Theta with zeros
    X=np.insert(X,0,1,axis=1) ## inserts a column of 1's
    tsize=int(m*0.7) ## size of the training set
    Xtr=X[:tsize,:] ## from the first row to tsize-1, all colmuns
    Xte=X[tsize:,:] ## from the tsize row to the end, all columns
    ytr=y[:tsize]
    yte=y[tsize:]
    ### Call gradient descent to find the "good" values to Theta
    Theta,cost=GD(Xtr,ytr,Theta,0.000001,2000)
    pl.plot(cost)
    pl.show()
    print('RMSE:',RMSE(Xte,yte,Theta))
    if d>1:
        print('Sorry, we cannot plot more than 1 feature')
        exit(0)
    ## predict new labels from training set
    y_hat=Xtr.dot(Theta.T)
    ## Plot the predict y's in relation to the ground-truth (y)
    ## first the X regarding the ground-truth
    pl.plot(Xtr[:,1:],ytr,'.',c='b')
    ## now X regarding the predict values
    pl.plot(Xtr[:,1:],y_hat,'-',c='g')
    pl.show()
    ## Now, let's check the performance on the test set
    ## y_hat is built from Xte, i.e., the test set
    y_hat=Xte.dot(Theta.T)
    pl.plot(Xte[:,1:],yte,'.',c='b')
    ## now X regarding the predict values
    pl.plot(Xte[:,1:],y_hat,'-',c='g')
    pl.show()
    print("Test cost", J(Xte,yte,Theta))


import numpy as np
from scipy.optimize import minimize
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
from mpl_toolkits.mplot3d import Axes3D

def matyas(x):
   return 0.26*(x[0]**2+x[1]**2)-0.48*x[0]*x[1]

#def booth(x):
#   return (x[0]+2*x[1]-7)**2+(2*x[0]+x[1]-5)**2

x = np.linspace(-10,10,100)
y = np.linspace(-10,10,100)
x, y = np.meshgrid(x, y)
z = matyas([x,y])

fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1,
                      cmap=cm.RdBu,linewidth=0, antialiased=False)

ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

fig.colorbar(surf, shrink=0.5, aspect=10)

plt.show()

x0 = np.array([-10, 10])
NelderMeadOptimizeResults = minimize(matyas, x0, method='nelder-mead',
               options={'xatol': 1e-8, 'disp': True})

print(NelderMeadOptimizeResults.x)

x0 = np.array([-10, 10])
PowellOptimizeResults = minimize(matyas, x0, method='Powell',
               options={'xtol': 1e-8, 'disp': True})

print(PowellOptimizeResults.x)


#!/usr/bin/env python
# integeral.py
import numpy as num

def integral(x,y):
  """
ROUTINE:        INTEGRAL

USEAGE:         RESULT = INTEGRAL( X, Y )

PURPOSE:        Integrate tabulated data using Simpson's rule
                with 3-point Lagragian interpolation. Data may be
                regularly sampled in X, or irregularly sampled.

INPUT:
  X             Vector of x axis points.
                (Elements must be unique and monotonically increasing)

  Y             Vector of corresponding Y axis points.

KEYWORD_INPUT:  None.

OUTPUT:         Result of integration.

EXAMPLE:
       Example 1:
       Define 11 x-values on the closed interval [0.0 , 0.8].
       X = [ 0.0, .12, .22, .32, .36, .40, .44, .54, .64, .70, .80 ]

       Define 11 f-values corresponding to x(i).
       F = [ 0.200000, 1.30973, 1.30524, 1.74339, 2.07490, 2.45600, $
              2.84299,  3.50730, 3.18194, 2.36302, 0.231964 ]

       Compute the integral.
       RESULT = INTEGRAL( X, F )

       In this example, the f-values are generated from a known function,
       (f = .2 + 25*x - 200*x^2 + 675*x^3 - 900*x^4 + 400*x^5)

       The Multiple Application Trapezoid Method yields;  result = 1.5648
       The Multiple Application Simpson's Method yields;  result = 1.6036
              IDL User Library INT_TABULATED.PRO yields;  result = 1.6232
                                    INTEGRAL.PRO yields;  result = 1.6274
         The Exact Solution (4 decimal accuracy) yields;  result = 1.6405

AUTHOR:         Liam Gumley, CIMSS/SSEC (liam.gumley@ssec.wisc.edu)
                Based on a FORTRAN-77 version by Paul van Delst, CIMSS/SSEC
                22-DEC-95

REVISIONS:      None.
  """
  n = x.size
  x0 = x[0:n-2]
  x1 = x[1:n-1]
  x2 = x[2:n-0]
  y0 = y[0:n-2]
  y1 = y[1:n-1]
  y2 = y[2:n-0]
  #
  # compute interpolation delta and midpoint arrays
  #
  dx = x1-x0
  xmid = 0.5*(x1+x0)
  #
  # compute 3 point lagrange interpolation
  #
  l0 = ((xmid-x1)/(x0-x1))*((xmid-x2)/(x0-x2))
  l1 = ((xmid-x0)/(x1-x0))*((xmid-x2)/(x1-x2))
  l2 = ((xmid-x0)/(x2-x0))*((xmid-x1)/(x2-x1))
  ymid = y0*l0 + y1*l1 + y2*l2;
  #
  # compute integral sum
  #
  integ = sum(1.0/6.0*dx*(y0+4.0*ymid+y1))
  #
  # handle last 3 points similarly
  #
  x0 = x[n-3]
  x1 = x[n-2]
  x2 = x[n-1]
  y0 = y[n-3]
  y1 = y[n-2]
  y2 = y[n-1]
  dx = x2 - x1
  xmid = 0.5*(x2+x1)
  l0 = ((xmid-x1)/(x0-x1))*((xmid-x2)/(x0-x2))
  l1 = ((xmid-x0)/(x1-x0))*((xmid-x2)/(x1-x2))
  l2 = ((xmid-x0)/(x2-x0))*((xmid-x1)/(x2-x1))
  ymid = y0*l0 + y1*l1 + y2*l2;
  integ = integ + 1.0/6.0*dx*(y1+4.0*ymid+y2)
  return integ

if __name__ == '__main__':
  print(integral.__doc__)
  X = num.array((0.0, .12, .22, .32, .36, .40, .44, .54, .64, .70, .80))
  Y = num.array((0.200000, 1.30973, 1.30524, 1.74339, 2.07490, 2.45600,
                 2.84299,  3.50730, 3.18194, 2.36302, 0.231964))
  i = integral(X,Y)
  print(i)


import numpy as np
from scipy.signal import detrend

def GetPrincipalComponents(x,y,z):

	mux = np.mean(x)
	muy = np.mean(y)
	muz = np.mean(z)

	sxx = np.sum((x-mux)**2)
	sxy = np.sum((x-mux)*(y-muy))
	sxz = np.sum((x-mux)*(z-muz))
	syy = np.sum((y-muy)**2)
	syz = np.sum((y-muy)*(z-muz))
	szz = np.sum((z-muz)**2)

	data = np.array([x-mux,y-muy,z-muz]).transpose()

	#ScatterMatrix=np.array([[sxx,sxy,sxz],[sxy,syy,syz],[sxz,syz,szz]])
	#CoVarMatrix=np.cov([x-mux,y-muy,z-muz])
	#Russell calls the scatter matrix a variance matrix
	#the covariance matrix = variance/(N-1)
	#according to a website these should both provide the same eigenvalues
	#though in this case they don't seem to
	ScatterMatrix = np.array([[sxx,sxy,sxz],[sxy,syy,syz],[sxz,syz,szz]])
	CoVarMatrix = np.cov(data.T)

	# eigenvectors and eigenvalues for the from the covariance matrix
	EigValCov, EigVecCov = np.linalg.eig(CoVarMatrix)

	srt = (EigValCov.argsort())[::-1]
	eigs = EigValCov[srt]
	vecs = EigVecCov[:,srt]

	return (eigs,vecs.T)

def RotateToPrincipalComponents(x,y,z):

	eigs,vecs = GetPrincipalComponents(x,y,z)

	nx = x*vecs[0,0] + y*vecs[0,1] + z*vecs[0,2]
	ny = x*vecs[1,0] + y*vecs[1,1] + z*vecs[1,2]
	nz = x*vecs[2,0] + y*vecs[2,1] + z*vecs[2,2]

	return (nx,ny,nz)

def SlidingPCA(t,x,y,z,Wind,Slip,Detrend=True):

	Tlen = np.size(t)

	Trange = t[Tlen-1]-t[0]
	Nwind = np.int32((Trange-Wind)/Slip)+1

	MinVec = np.zeros((Nwind,3),dtype='float32')+np.float32(np.nan)
	MidVec = np.zeros((Nwind,3),dtype='float32') +np.float32(np.nan)
	MaxVec = np.zeros((Nwind,3),dtype='float32') +np.float32(np.nan)
	Eigs = np.zeros((Nwind,3),dtype='float32')+np.float32(np.nan)

	Twind = np.arange(Nwind,dtype='float32')*Slip + Wind/2.0 + t[0]

	LenW = np.int32(Wind/0.05)/2
	for i in range(0,Nwind):
		use0 = np.int32(i*Slip/0.05)
		use = use0+np.arange(LenW*2)
		if np.max(use) >= np.size(t):
			bad = 1
		else:
			bad = np.where((np.isfinite(x) == False) | (np.isfinite(y) == False) | (np.isfinite(z) == False))[0]
		if np.size(bad) == 0:
			if Detrend:
				eigs,vecs = GetPrincipalComponents(detrend(x[use]),detrend(y[use]),detrend(z[use]))
			else:
				eigs,vecs = GetPrincipalComponents(x[use],y[use],z[use])
			Eigs[i] = eigs
			MaxVec[i] = vecs[0]
			MidVec[i] = vecs[1]
			MinVec[i] = vecs[2]

	return (Twind,Eigs,MinVec,MidVec,MaxVec)

def CovarianceMatrix(x):
	'''
	Input x must have shape (m,n), where m is the number of samples and n is the number of features

	Output is a (n,n) matrix
	'''
	mu = np.mean(x,axis=0)
	cov = np.dot((x-mu).T,(x-mu))/(x.shape[0]-1)

	return cov

def EigenValues(x):

	cov = CovarianceMatrix(x)
	EigVal,EigVec = np.linalg.eig(cov)

	srt = np.argsort(EigVal)[::-1]

	return EigVal[srt],EigVec[:,srt]

def PCA(x,nComp=None,EigVal=None,EigVec=None,ReturnEigVal=False,ReturnEigVec=False):
	if EigVal is None or EigVec is None:
		EigVal,EigVec = EigenValues(x)
	ev = np.abs(EigVal)

	if not nComp is None:
		#either a real or integer
		if np.int32(nComp) == nComp:
			#easy - just select number of components
			EigVal1 = EigVal[:np.int32(nComp)]
			EigVec1 = EigVec[:,:np.int32(nComp)]
			ev1 = ev[:np.int32(nComp)]
		else:
			#hopefully a floating point, in which case assume it is a number between 0.0 and 1.0
			cs = np.cumsum(ev)/np.sum(ev)
			use = np.where(cs >= nComp)[0]
			if use.size > 1:
				ind = use[1]
			elif use.size == 1:
				ind = use[0]
			else:
				ind = ev.size-1
			EigVal1 = EigVal[:ind+1]
			EigVec1 = EigVec[:,:ind+1]
			ev1 = ev[:ind+1]
	else:
		EigVec1 = EigVec
	x_transformed = np.dot(x,EigVec1)

	if not ReturnEigVec and not ReturnEigVal:
		out = x_transformed
	else:
		out = [x_transformed]
	if ReturnEigVal:
		out.append(EigVal)

	if ReturnEigVec:
		out.append(EigVec)

	return out