# -*- coding: utf-8 -*-
"""
Created on Thu May 28 18:24:59 2015

@author: pietro
@website:http://pennestri.me
@mail:pietro.pennestri@gmail.com
"""

import matplotlib.pyplot as plt
import numpy as np
import math
from scipy.optimize import curve_fit
plt.close("all")

ydata = np.array([0.5,0.75,1.1,1.5,1.9])
xdata = np.array([2.92,3.15,3.29,3.5,3.81])
num_elements=len(xdata)

def func(x, a, b):
    return a*x+ b
#
# curve_fit: Math procedure of the scipy math library
# popt:
# Optimal values for the parameters so
#that the sum of the squared error of
# (func-ydata)^2 is minimized
# pcov:
# The diagonals of pcov provide the variance
# of the parameter estimate

popt, pcov = curve_fit(func, xdata, ydata)
print popt

a=popt[0]
b=popt[1]

#error
k=0
s=0
for x in xdata:
    y=func(x,a,b)
    s=s+(y-ydata[k])**2
    k=k+1
s=math.sqrt(s/num_elements)
print "Square root of sum of squares of residuals=",s

print '------'
print pcov
plt.figure()
plt.plot(xdata, ydata, 'ko', label="Original Data")
plt.plot(xdata, func(xdata, *popt), 'r-', label="Fitted Curve")
plt.xlabel('Arduino voltage (V)')
plt.ylabel('Alcohol concentration  (mg/l)')
#plt.xscale('log')#
#plt.yscale('log')#
plt.grid()
plt.show()


import numpy as np
from scipy.fft import fft
from scipy import interpolate

def normalize(lower, upper, vals):
    """
    Normalize all values so that they're between the lower and the
    upper bounds that were passed in.

    :param lower: The lower bound
    :type lower: float

    :param upper: The upper bound
    :type upper: float

    :param vals: A list of numbers
    :type vals: list[float]

    :returns: The list of numbers redistributed across the new bounds
    :rtype: list[float]
    """
    return [(lower + (upper - lower)*v) for v in vals]

def remap(x, x1, x2, y1, y2):
    """
    Remaps value x to a new range.

    :param x: Value to act on

    :param x1: Old Minimum
    :param x2: Old Maximum

    :param y1: New Minimum
    :param y2: New Maximum

    :returns: The original value mapped to the new range.
    """
    if (x2 == x1 and x2 == 0) or (y2 == y1 and y2 == 0):
        return 0
    return (((x - x1) * (y2 - y1)) / (x2 - x1)) + y1

def to_bins(ar, b, xvals=None):
    """
    Given an array ar, and number of buckets b, return a normalized
    set of bins (values in range [0,1])

    :param ar: Array to act on
    :type ar: Python list or numpy array

    :param b: Number of bins to sum over
    :type b: int

    :param xvals: Values to use as x values/coordinates for ar's "y values"
    :type xvals: array of numbers

    :returns: An array of bins that have values between 0 and 1
    :rtype: numpy array
    """
    a = len(ar)
    n = int(a / b)

    if b <= a:
        bins = []
        for i in range(0, b):
            arr = []
            for j in range(i*n, (i+1)*n):
                if j >= a:
                    break
                arr.append(ar[j])
            bins.append(np.sum(arr) / len(arr))
        bins = np.array(bins)
        mx = np.max(bins)
        return bins if mx == 0 else bins / mx
    else:
        # Number of buckets is bigger than array, need to
        # interpolate those values.

        # Using scipy, which has a better interpolation method
        if not xvals is None:
            f = interpolate.interp1d(xvals, ar, fill_value="extrapolate")
        else:
            f = interpolate.interp1d(np.arange(a), ar, fill_value="extrapolate")

        interpolated = f(np.arange(b))
        return interpolated / np.max(interpolated)

def get_spectrum(y, Fs):
    """
    Given a signal, y, compute its frequency domain using a Fast
    Fourier Transform (FFT).  Return the one-sided representation.

    :param y: The signal (list of values)
    :type y: list[float]

    :param Fs: The sampling frequency used for the signal
    :type Fs: int

    :returns: A tuple of the frequency range and the absolute amplitude of
              each frequency.
    :rtype: tuple
    """
    signal_length = len(y)
    k = np.arange(signal_length)
    T = signal_length / Fs
    frq = k / T # two-sides frequency range
    frq = frq[range(signal_length // 2)]    # Convert to one-sided frequency range

    # FFT Computation and Range
    Y = fft(y)
    Y = abs(Y[range(signal_length // 2)])

    # Now want to normalize the values to 0-1
    #Y = np.nan_to_num(Y / np.max(Y))
    Y = Y * 2 / (2 * (signal_length // 2))

    # And set tiny values to 0
    #Y[Y <= 0.001] = 0

    return (frq, Y)


# The code here is a translation of the MATLAB code provided at
# https://www.mathworks.com/matlabcentral/fileexchange/41459-6-functions-for-generating-artificial-datasets

import numpy as np

def spirals(N = 1000, degrees = 570, start = 90, noise = 0.2):
    """
    Generate "two spirals" dataset with N instances.
    Input:
    N -> Number of samples in the dataset
    degrees -> controls the length of the spirals
    start -> determines how far from the origin the spirals start, in degrees
    noise displaces the instances from the spiral. 0 is no noise, at 1 the
    spirals will start overlapping
    Output:
    data -> (Nx3) dataset: (xcoord,ycoord,label)
    """

    deg2grad = (2*np.pi)/360
    start = start*deg2grad

    #Number of samples in spirals 1 and 2
    N1 = int(np.floor(1.0*N/2))
    N2 = N - N1

    #Spiral 1
    n = start + np.sqrt(np.random.rand(N1,1))*degrees*deg2grad
    d1x =  -np.cos(n)*n + np.random.rand(N1,1)*noise
    d1y = np.sin(n)*n + np.random.rand(N1,1)*noise

    spiral1 = np.concatenate((d1x,d1y,\
                             np.zeros((N1,1))),axis = 1)

    # Spiral 2
    n = start + np.sqrt(np.random.rand(N2,1))*degrees*deg2grad
    d2x = np.cos(n)*n+np.random.rand(N2,1)*noise
    d2y = -np.sin(n)*n + np.random.rand(N2,1)*noise
    spiral2 = np.concatenate((d2x,d2y,\
                             np.ones((N2,1))),axis = 1)

    #Concatenating the final data
    data = np.concatenate((spiral1,spiral2),axis = 0)
    return data

def concentric_clusters(N = 1000, r1 = 1, r2 = 5, w1 = 0.8, w2 = 1.0/3, arms = 64):
    """
    Generates two concentric circular clusters.
    Input:
    N -> Number of samples in the dataset
    r1 -> Radius cluster 1
    r2 -> Radius cluster 2
    w1 -> Cluster 1 weight
    w2 -> Cluster 2 weight
    arms -> Number of stripes (arms) in the circle
    Output:
    data -> (Nx3) dataset: (xcoord,ycoord,label)
    """

    #Number of samples in each cluster
    N1 = int(np.floor(1.0*N/2))
    N2 = N - N1

    phi1 = np.random.rand(N1,1) * 2 * np.pi;
    dist1 = r1 + 1.0*(np.random.randint(0,5,size =(N1,1)))/5*w1*r1
    d1x = dist1* np.cos(phi1)
    d1y = dist1* np.sin(phi1)
    cluster1 = np.concatenate((d1x,d1y,\
                             np.zeros((N1,1))),axis = 1)

    perarm = int(np.round(N2/arms))
    N2 = perarm*arms;
    radperarm = (2*np.pi)/arms;
    phi2 = 1.0*(np.arange(1,N2+1,dtype = int) - np.arange(1,N2+1,\
            dtype=int)%perarm)/perarm*(radperarm)
    dist2 = r2 * (1 - w2/2) + (r2*w2*np.arange(1,N2+1,\
            dtype=int)%perarm)/perarm
    d2x = dist2*np.cos(phi2)
    d2y = dist2*np.sin(phi2)
    cluster2 = np.concatenate((d2x[:,np.newaxis],d2y[:,np.newaxis],\
                             np.ones((N2,1))),axis = 1)

    #Concatenating the final data
    data = np.concatenate((cluster1,cluster2),axis = 0)
    return data

def corners(N = 100,scale = 0,gapwidth = 2,cornerwidth = 2):
    """
    Generates for "corner" clusters .
    Input:
    N -> Number of samples in the dataset
    scale -> Corner scale
    gapwidth -> gap between corners
    cornerwidth -> corner width
    Output:
    data -> (Nx3) dataset: (xcoord,ycoord,label)
    """

    N = int(N/8)*8
    per_corner  = N//4

    xplusmin = np.concatenate((np.ones((per_corner,1)),\
                               -np.ones((per_corner,1)),\
                               np.ones((per_corner,1)),\
                               -np.ones((per_corner,1))),\
                               axis = 0)

    yplusmin = np.concatenate((np.ones((per_corner,1)),\
                               -np.ones((2*per_corner,1)),\
                               np.ones((per_corner,1))),\
                               axis = 0)

    #Horizontal edge
    x = xplusmin[::2]*gapwidth+xplusmin[::2]*scale*(np.random.rand(2*per_corner).reshape(-1,1))

    y = yplusmin[::2]*gapwidth + cornerwidth*yplusmin[::2]*(np.random.rand(2*per_corner).reshape(-1,1))

    z = np.floor(np.arange(2*per_corner,dtype = int)/(per_corner*0.5))

    horizontal = np.concatenate((x,y,z[:,np.newaxis]),axis = 1)

    # Vertical edge
    x2 = xplusmin[1::2]*gapwidth + cornerwidth*xplusmin[1::2]*\
                                 (np.random.rand(2*per_corner).reshape(-1,1))

    y2 = yplusmin[1::2]*gapwidth+yplusmin[1::2]*scale*\
                                 (np.random.rand(2*per_corner).reshape(-1,1))

    z2 = np.floor(np.arange(2*per_corner,dtype = int)/(per_corner*0.5))
    vertical = np.concatenate((x2,y2,z2[:,np.newaxis]),axis = 1)

    #Concatenating the final data
    data = np.concatenate((vertical,horizontal),axis = 0)
    return data

def crescent_full_moon(N = 1000,r1 = 5,r2 = 10,r3 = 15):
    """
    Generates 2 clusters. One shape as a full moon and the other as a crescent moon.
    Input:
    N -> Number of samples in the dataset
    r1 ->
    r2 ->
    r3 ->
    Output:
    data -> (Nx3) dataset: (xcoord,ycoord,label)
    """

    N = int(N/4)*4
    N1 = int(N/4)
    N2 = N-N1

    # Full moon
    phi1 = np.random.rand(N1,1)*2*np.pi
    R1 = np.sqrt(np.random.rand(N1, 1))
    moon = np.concatenate((np.cos(phi1)*R1*r1,np.sin(phi1)*R1*r1,\
                           np.zeros((N1,1))),axis = 1)
    # Crescent moon
    d = r3 - r2
    phi2 = np.pi + np.random.rand(N2,1)*np.pi
    R2 = np.sqrt(np.random.rand(N2,1))
    crescent = np.concatenate((np.cos(phi2)*(r2 + R2 * d),\
                               np.sin(phi2)*(r2 + R2 * d),\
                               np.ones((N2,1))),axis = 1)
    #concatenating the final data
    data = np.concatenate((moon,crescent),axis = 0)
    return data


import numpy as np

class SimpleLinearRegressionV1:

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

    def fit(self, x_train, y_train):
        assert x_train.ndim == 1 and y_train.ndim == 1,\
            "needs single feature train data"
        assert len(x_train) == len(y_train),\
            "the size of x and y must be equals"
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)
        self.a_ = np.sum((x_train - x_mean) * (y_train - y_mean)) / np.sum((x_train - x_mean) ** 2)
        self.b_ = y_mean - self.a_ * x_mean
        return self

    def predict(self, x_predict):
        assert isinstance(x_predict, (int, float)) or x_predict.ndim == 1,\
            "needs single feature data to predict"
        assert self.a_ is not None and self.b_ is not None,\
            "must be fit before oredict"
        return self.a_ * x_predict + self.b_

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

class SimpleLinearRegressionV2:

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

    def fit(self, x_train, y_train):
        assert x_train.ndim == 1 and y_train.ndim == 1,\
            "needs single feature train data"
        assert len(x_train) == len(y_train),\
            "the size of x and y must be equals"
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)
        self.a_ = (x_train - x_mean).dot(y_train - y_mean) / (x_train - x_mean).dot((x_train - x_mean))
        self.b_ = y_mean - self.a_ * x_mean
        return self

    def predict(self, x_predict):
        assert isinstance(x_predict, (int, float)) or x_predict.ndim == 1,\
            "needs single feature data to predict"
        assert self.a_ is not None and self.b_ is not None,\
            "must be fit before oredict"
        return self.a_ * x_predict + self.b_

    def score(self, x_test, y_test):
        assert len(x_test) == len(y_test),\
        "the size of y_true must be equal to the size of y_predict"
        return 1 - np.sum((y_test - self.predict(x_test)) ** 2) / len(y_test) / np.var(y_test)

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


from fractions import Fraction
import numpy as np
from numpy.random import randint
import sys

class RationLP:
    def __init__(self, A, b, c):
        self.A = [[Fraction(x) for x in row] for row in A]
        self.b = [Fraction(x) for x in b.flat]
        self.c = [Fraction(x) for x in c.flat]
        self.n = len(self.c)
        self.m = len(self.b)

    def print_row(self, vec, fout):
        for x in vec:
            print(x, end=' ', file=fout)
        print(file=fout)

    def save(self, path):
        with open(path, 'w') as fout:
            print(self.n, file=fout)
            print(self.m, file=fout)
            self.print_row(self.c, fout)
            self.print_row(self.b, fout)
            for row in self.A:
                self.print_row(row, fout)

# Adapted from https://math.stackexchange.com/a/244164/76028
def generate_feasible(n, m):
    v = randint(0, 2 * n, size=(n, 1))
    A = randint(0, n * m, size=(m, n))
    b = A @ v + randint(0, 4, size=(m, 1)) / 4.0
    c = randint(-n, n, size=(n, 1))
    return A, b, c

def generate_unbounded(n, m):
    u = randint(0, 2 * n, size=(n, 1))
    v = randint(0, 2 * n, size=(n, 1))
    v[v == 0] = 1
    A = randint(0, 100, size=(m, n))
    Av = A @ v
    A[Av[:, 0] > 0, :] *= -1
    assert (A@v < 0).all()
    b = A @ u + randint(0, 4, size=(m, 1)) / 4.0
    c = randint(-2 * n, 2 * n, size=(n, 1))
    if c.T @ v <= 0:
        c = -c
    return A, b, c

def generate_infeasible(n, m):
    A, b, c = generate_unbounded(m, n)
    return -A.T, -c, -b

def pack_save(fname, A, b, c):
    prog = RationLP(A, b, c)
    prog.save(fname)

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("The program needs two integer command line arguments")
        sys.exit(1)

    n = int(sys.argv[1])
    m = int(sys.argv[2])

    name_template = 'inputs/test_{{}}_{}x{}.txt'.format(m, n)

    pack_save(name_template.format('feasible'), *generate_feasible(n, m))
    pack_save(name_template.format('infeasible'), *generate_infeasible(n, m))
    pack_save(name_template.format('unbounded'), *generate_unbounded(n, m))


# utf-8
'''**1) Crie dois arrays 1D de forma automática: o primeiro com números pares entre 0 e 8; o segundo com números ı
́mpares entre 1 e 9. Experimente realizar as operações aritméticas de soma,
subtração, multiplicação e divisão sobre eles (primeiro o array par e depois o array ímpar, nessa ordem).**'''

import numpy as np
print()
print('=' * 39)
print('Ex.1')

a = np.arange(0, 10, 2)
print(a)

b = np.arange(1, 10, 2)
print(b)

print(a + b)
print(a - b)
print(a / b)
print(a * b)

'''**2) Construa a matriz ilustrada pela figura abaixo (Dica: pesquise sobre o método `reshape`).
Em seguida, obtenha cada um dos "pedaços" (ou cortes) do array 2D destacados na figura (em cores) e,
para cada um deles, calcule a soma, a média, a variância e o desvio padrão dos valores.**

![Questão 2](aula3-q2.png)'''
print()
print('=' * 39)
print('Ex.2')

p = np.arange(6)
s = np.arange(10, 16)
t = np.arange(20, 26)
qa = np.arange(30, 36)
qi = np.arange(40, 46)
se = np.arange(50, 56)
fig = np.concatenate((p, s, t, qa, qi, se))
fig = fig.reshape(6, 6)
print(fig)

r = fig[2, :]
ro = r[r % 2 == 0]
x = fig[4, :]
xo = x[x % 2 == 0]

roxo = np.concatenate((ro, xo), axis=0, out=None, dtype=int, casting='same_kind')
print(f'\nslice roxo {roxo}')
print(f'média slice roxo {roxo.mean()}')
print(f'variância slice roxo {roxo.var()}')
print(f'desvio padrão slice roxo {roxo.std()}')

azul = fig[:, 2]
print(f'\nslice azul {azul}')
print(f'média slice azul {azul.mean()}')
print(f'variância slice azul {azul.var()}')
print(f'desvio padrão slice azul {azul.std()}')

vermelho = fig[0, 3:5]
print(f'\nslice vermelho {vermelho}')
print(f'média slice vermelho {vermelho.mean()}')
print(f'variância slice vermelho {vermelho.var()}')
print(f'desvio padrão slice vermelho {vermelho.std()}')

verde = (fig[4:, 4:]).reshape(1, 4)
print(f'\nslice verde {verde}')
print(f'média slice verde {verde.mean()}')
print(f'variância slice verde {verde.var()}')
print(f'desvio padrão slice verde {verde.std()}')

'''**3) Com base na matriz abaixo, obtenha:**
  (a) Um vetor cotendo o seno dos elementos da segunda linha;
  (b) A sua inversa, sua transposta e um vetor com os elementos da sua diagonal principal;
  (c) A soma e a média dos seus elementos.'''
print()
print('=' * 39)
print('Ex.3')
ma = [[2, 45, 22, 12], [44, 34, 68, 3], [5, 7, 78, 2], [12, 34, 5, 9]]
ma_arr = np.array(ma)
print(f'\n{ma_arr}')

print('\n(a)')
sen_arr = np.sin(ma_arr[1, :])
print(sen_arr)

print('\n(b.1)')
print(f'{ma_arr[::-1]}')

print('\n(b.2)')
print(ma_arr.T)

print('\n(b.3)')
print(ma_arr.diagonal())

print('\n(b.3)')
print(f'A soma do array é {ma_arr.sum()} e a média {ma_arr.mean()}')


# -*- coding: utf-8 -*-
"""
Created on Sat Dec 11 16:58:25 2021

@author: DELL
"""

import numpy as np

array = np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]) #1*4 vect

print(array.shape)

a = array.reshape(3,5)
print("shape",a.shape)
print("dimension",a.ndim)
print("data type: ",a.dtype.name)
print("size: ",a.size)

print("type",type(a))

array1 = np.array([[1,2,3,4],[5,6,7,8],[9,8,7,5]])

zeros = np.zeros((3,4))

zeros[0,0]=5
print(zeros)
np.ones((3,4))

np.empty((2,3))

a= np.arange(10,50,5)
print(a)

a = np.linspace (10,50,20)
print(a)

#%%
a = np.array([1,2,3])
b = np.array([4,5,6])

print(a+b)
print(a-b)
print(a**2)

print(np.sin(a))

print (a<2)

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

b = np.array([[1,2,3],[4,5,6]])

#element wisw product

print(a*b)

#matrix product
a.dot(b.T)

print(np.exp(a))

a = np.random.random((5,5))

print(a.sum())
print(a.max())
print(a.min())

print(a.sum(axis=0))#sutun toplar
print(a.sum(axis=1)) #satır toplar

print(np.sqrt(a))
print(np.square(a))# a**2

#%%
import numpy as np
array = np.array([1,2,3,4,5,6,7])

print(array[0])

print(array[0:4])

reverse_array = array [::-1]
print(reverse_array)

array1 = np.array([[1,2,3,4,5],[6,7,8,9,10]])
print(array1[1,1])

print(array1[:,1])

print( array1[1,1:4])

print([array1[-1, :]])

print([array1[:, -1]])

#%%
#shape manipilation
array = np.array([[1,2,3],[4,5,6],[7,8,9]])

#flatten

a=array.ravel()

array2 = a.reshape(3,3)

arrayT = array2.T

print(arrayT.shape)

array5 = np.array([[1,2],[3,4],[4,5]])

#%%stacking arrays

array1 = np.array([[1,2],[3,4]])
array2 = np.array([[-1,-2],[-3,-4]])
#vertical
array3 = np.vstack((array1,array2))
#horizontal
array4 = np.hstack((array1,array2))

#%%

liste = [1,2,3,4]

array = np.array(liste)

liste2 = list(array)


import numpy as np
import pandas as pd
import math
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

def cal_bid_spread(bid_price: float, offer_price: float) -> float:
    ''' Function to calculate bid spread

    Bid spread use to calculate ratio of best offer price and best bid price
    0 is the lowest spread

    Args:
        bid_price: Highest price of bidding
        offer_price: Lowest price of offer

    Returns:
        bid spread
    '''

    bid_spread = (offer_price / bid_price) - 1

    return bid_spread

def cal_wap(bid_price: float, bid_size: float, offer_price: float, offer_size: float) -> float:
    ''' Function to calculate weighted averaged price

    weighted averaged price (wap) is a metric to calculate stock price

    Args:
        bid_price: Highest price of bidding
        bid_size: Size of highest bidding price
        offer_price: Lowest price of offer
        offer_size: Size of lowest offering price

    Returns:
        weighted averaged price
    '''

    wap = ((bid_price * offer_size) + (offer_size * bid_size)) / \
        (bid_size + offer_size)

    return wap

def cal_series_wap(book_df: pd.DataFrame) -> pd.DataFrame:
    ''' Function to calculateb weighted averaged price of DataFrame

    weighted averaged price (wap) is a metric to calculate stock price

    Args:
        book_df: DataFrame contains booking information

    Returns:
        DataFrame contains booking information with wap
    '''

    book_df.loc[:, "wap"] = (book_df['bid_price1'] * book_df['ask_size1']
                             + book_df['ask_price1'] * book_df['bid_size1']) \
        / (book_df['bid_size1'] + book_df['ask_size1'])

    return book_df

def cal_log_return(stock_price_t1: float, stock_price_t2: float) -> float:
    ''' Function to calculate log return

    Log return to calculate return of each stock in two point in time.
    Log rerturn is more mathematically desirable compare to raw return

    Args:
        stock_price_t1: stock price at time 1
        stock_price_t2: stock price at time 2

    Returns:
        log returns
    '''

    log_returns = math.log(stock_price_t2 / stock_price_t1)

    return log_returns

def cal_series_log_return(book_df: pd.DataFrame) -> pd.DataFrame:
    ''' Function to calculate log return

    Log return to calculate return of each stock in two point in time.
    Log rerturn is more mathematically desirable compare to raw return

    Args:
        book_df: DataFrame contains booking information

    Returns:
        DataFrame contains booking information with wap and log return
    '''

    book_df = cal_series_wap(book_df)
    book_df.loc[:, "log_return"] = np.log(book_df['wap']).diff()

    # Replace second in bucket = 0 to NaN because there should be nothing to calculate from
    book_df.loc[(book_df["seconds_in_bucket"] == 0), "log_return"] = np.NaN

    return book_df

def cal_realized_volatility(log_return_series: pd.Series) -> float:
    ''' Function to calculate realized_volatility

    Calculate volatility between time 1 and time 2

    Args:
        log_return_series: a series with log returns

    Returns:
        realized_volatility
    '''

    log_return_series = log_return_series.dropna()
    realized_volatility = np.sqrt(np.sum(np.square(log_return_series)))

    return realized_volatility

def root_mean_squared_percentage_error(y_true: np.array, y_pred: np.array, percentage: bool = False) -> float:
    ''' Function to calculate root mean square percentage error

    Calculate root mean square percentage error between two array

    Args:
        y_true: array of actual value
        y_pred: array of prediction by estimator(s)
        percentage: boolean to indicate true for percentege false for percentage/100

    Returns:
        root mean square percentage error
    '''

    rmspe = np.sqrt(np.mean(np.square((y_true - y_pred) / y_true)))

    if percentage is True:
        rmspe = rmspe * 100

    return rmspe

def symmetric_mean_absolute_percentage_error(y_true: np.array, y_pred: np.array) -> float:
    ''' Function to calculate symmetric mean absolute percentage error

    Calculate symmetric mean absolute percentage error between two array

    Args:
        y_true: array of actual value
        y_pred: array of prediction by estimator(s)

    Returns:
        symmetric mean absolute percentage error
    '''

    smape = 100/len(y_true) * np.sum(
        2 * np.abs(y_pred - y_true) / (np.abs(y_true) + np.abs(y_pred)))

    return smape

def evaluation(y_true: np.array, y_pred: np.array) -> dict:
    ''' Function to calculate metrics for models evaluation
    Calculate metrics for evaluate regression including:
    residual, average value, sum_error, mae, rmse, rmspe, smape, r2

    Args:
        y_true: array of actual value
        y_pred: array of prediction by estimator(s)

    Returns:
        Dictionary of metrics
    '''

    residual = y_true - y_pred
    # Average true and prediction
    y_true_mean = np.mean(y_true)
    y_pred_mean = np.mean(y_pred)
    # This call tell if the estimator over/under-predicted
    sum_error = np.sum(residual)
    error_mean = y_true_mean - y_pred_mean

    mae = mean_absolute_error(y_true, y_pred)
    rmse = mean_squared_error(y_true, y_pred, squared=False)
    rmspe = root_mean_squared_percentage_error(y_true, y_pred, percentage=True)
    smape = symmetric_mean_absolute_percentage_error(y_true, y_pred)

    # Again R2 is really problematic and should not be use in this instance,
    # but the example notebook has them
    r2 = r2_score(y_true, y_pred)

    metrics_result = {
        "residual": residual,
        "true_average": y_true_mean,
        "prediction_average": y_pred_mean,
        "sum_error": sum_error,
        "average_error": error_mean,
        "mae": mae,
        "rmse": rmse,
        "rmspe": rmspe,
        "smape": smape,
        "r2": r2
    }

    return metrics_result


def transform(dataset, SX=0.5, SY=0.5, SZ=0.5, noise=15.0):
    """Deblur Images with a Weiner Filter."""

    import numpy as np
    from numpy import exp, square, pi
    from scipy.fftpack import fftn, ifftn, fftshift

    #Import information from dataset
    array = dataset.active_scalars
    dim = array.shape

    #Point Spread Function (Estimated with Gaussian Function)
    def gaussian(SX, SY, SZ):
        x = np.arange(-dim[0]/2, dim[0]/2)
        y = np.arange(-dim[1]/2, dim[1]/2)
        z = np.arange(-dim[2]/2, dim[2]/2)
        [x, y, z] = np.meshgrid(x, y, z, indexing='ij')
        Kx = x/dim[0]
        Ky = y/dim[1]
        Kz = z/dim[2]
        r = square(Kx)+square(Ky)+square(Kz)
        G = exp(-2*square(pi)*SX*SY*SZ*r)

        return G

    #Wiener Transformation/Reconstruction
    def wiener(psf, img, SZ):
        snr = noise
        if snr != 0:
            W = (1/psf)*(square(abs(psf))/(square(abs(psf))+1/snr))
        else:
            W = (1/psf)

        img = abs(ifftn(fftshift(W*(fftn(img)))))
        img = img.real.astype(float, order='F')
        return img

    #Transformation/Reconstruction
    H = gaussian(SX, SY, SZ)
    H = H + np.finfo(complex).tiny
    final = wiener(H, array, SZ)
    final = final/np.amax(final) #Normalize the Image

    #Set the result as the new scalars.
    dataset.active_scalars = np.asfortranarray(final)


"""
reference: https://github.com/RRdmlearning/Machine-Learning-From-Scratch/blob/master/linear_regression/linear_regression.py

    https://towardsdatascience.com/building-a-logistic-regression-in-python-301d27367c24
loss function: l=\frac{1}{m}\sum _{i=1}^{m}(x_iw - y_i)^2
matrix form: l=(y-xw)^T(y-xw)
SGD: \frac{\partial{l}}{\partial w}=x^T(y-xw)
least square method: w^*=(X^TX)^{-1}X^Ty
"""
import numpy as np
from sklearn.datasets import make_regression

def split_train_test(x, y, seed=0, training_ratio=0.8):
    if seed:
        np.random.seed(seed)
    idx = np.arange(x.shape[0])
    np.random.shuffle(idx)
    x, y = x[idx], y[idx]
    train_counts = int(x.shape[0] * training_ratio)
    train_x, train_y = x[: train_counts, :], y[: train_counts]
    test_x, test_y = x[train_counts, :], y[train_counts:]
    return train_x, train_y, test_x, test_y

class L1Regularation(object):
    def __init__(self, alpha=0.1):
        self.alpha = alpha

    def __call__(self, w):
        reg_loss = np.sum(np.fabs(w))
        return reg_loss * self.alpha

    def grad(self, w):
        return self.alpha * np.sign(w)

class L2Regularation(object):
    def __init__(self, alpha=0.1):
        self.alpha = alpha

    def __call__(self, w):
        reg_loss = w.T.dot(w)
        return self.alpha * reg_loss

    def grad(self, w):
        return 2.0 * self.alpha * w

class LinearRegression(object):
    def __init__(self, learning_rate=0.01, n_iterations=2000, solver='sgd', regularization=None):
        """
        :param learning_rate:
        :param n_iterations:
        :param solver: optimize algorithm: [sgd, lsm] least square method/stochastic gradient descent
        :param regularization:
        """
        self.learning_rate = learning_rate
        self.n_iterations = n_iterations
        self.solver = solver
        if regularization is None:
            self.regularization = lambda x: 0
            self.regularization.grad = lambda x: 0
        else:
            self.regularization = regularization
        assert self.solver in ['sgd', 'lsm']

    def init_weights(self, n_features):
        self.w = np.random.normal(size=(n_features, 1))

    def fit(self, X, y):
        """
        :param X: (m, n)
        :param y: (m, 1)
        :return:
        """
        training_errors = list()
        y = np.reshape(y, (-1, 1))
        assert 2 == len(X.shape) and 2 == len(y.shape)
        self.init_weights(n_features=X.shape[1])
        if self.solver == 'sgd':
            for idx in range(1, 1 + self.n_iterations):
                y_pred = X.dot(self.w)
                training_loss = np.mean(0.5 * (y_pred - y) ** 2) + self.regularization(self.w)
                training_errors.append(training_loss)
                grad = X.T.dot(y_pred - y) + self.regularization.grad(self.w)
                self.w = self.w - self.learning_rate * grad
                if idx % 50 == 0:
                    print('training loss=%.4f' % training_loss)
        elif self.solver == 'lsm':
            X, y = np.matrix(X), np.matrix(y)
            if abs(np.linalg.det(X.T.dot(X))) < 1e-8:
                print('the matrix was irreversible.')
                return
            X_T_X_I = (X.T.dot(X)).I
            X_T_Y = X.T.dot(y)
            self.w = X_T_X_I.dot(X_T_Y)
        else:
            raise ValueError('')

    def predict(self, X):
        X = np.matrix(X)
        return X.dot(self.w)

    def coef_(self):
        return self.w.flatten()

if __name__ == '__main__':
    X, y = make_regression(n_samples=100, n_features=10, noise=20)
    # train_x, train_y, test_x, test_y = split_train_test(X, y)
    model = LinearRegression(regularization =L2Regularation())
    # model.fit(train_x, train_y)

    model.fit(X, y)


import openseespy.opensees as ops
from math import pi,ceil
import numpy as np
from numpy import cos,cosh
import matplotlib.pyplot as plt
from PyPonding.structures import wf,wf_shapes

# Define units
inch = 1.0
kip = 1.0
lb  = kip/1000.0
ft  = 12.0*inch
in_per_ft = inch/ft
ksi = kip/inch**2
psf = lb/ft**2
pcf = psf/ft
mm  = 1/25.4*inch
m   = 1000*mm
kN  = 1/4.448222*kip
kNm = kN*m

# Input parameters
Fy      = 50.0*ksi
E       = 29000.0*ksi
Hk      = 1.0e-4*E
TW      = 10*ft
shape_name = 'W14X22';
L       = 40*ft
qD      = 10.0*psf
gamma   = 62.4*pcf

# Lookup shape data
shape_data = wf_shapes[shape_name]
d  = shape_data['d']*inch
bf = shape_data['bf']*inch
tf = shape_data['tf']*inch
tw = shape_data['tw']*inch

# Create wide-flange object
wf_section = wf(d,tw,bf,tf,Fy,E,Hk)

# Set additional properties
wf_section.L        = L
wf_section.gamma    = gamma
wf_section.TW       = TW
wf_section.zi       = 0.0*inch
wf_section.zj       = 0.0*inch
wf_section.wD       = qD*TW # Dead load per length

# Set OpenSees analysis options
wf_section.material_type = 'Elastic'
wf_section.num_steps     = 2000
wf_section.max_volume    = 249450
wf_section.vol_tol       = wf_section.max_volume/wf_section.num_steps/10000.
wf_section.extra_results = True

# Run OpenSees analyses
(data_volume,data_height,extra_results) = wf_section.perform_OpenSees_analysis();
x_OPS = extra_results[0]
y_OPS = extra_results[1]
M_OPS = extra_results[2]

# Compute Closed-form Results
C  = wf_section.C()
I  = wf_section.Iz()
wi = (qD + gamma*(2*inch))*TW

x  = np.linspace(0.0, L, num=100)

y1 = -wi*x*(L**3-2*L*x**2+x**3)/(24*E*I)
M1 = 0.5*wi*x*(L-x)

fact = pi*C**0.25
y2 = -wi/(2*gamma*TW)*( (cos(fact*(0.5-x/L))/cos(fact*0.5) - 1) - (1 - cosh(fact*(0.5-x/L))/cosh(fact*0.5)) )
M2 = (wi*L**2)/(2*pi**2*C**0.5)*( cos(fact*(0.5-x/L))/cos(0.5*fact) - cosh(fact*(0.5-x/L))/cosh(0.5*fact) )

# Plot Results
plt.rc('text',usetex=True)
plt.rc('font',family='serif')
plt.rc('axes',labelsize=8)
plt.rc('axes',titlesize=8)
plt.rc('legend',fontsize=8)
plt.rc('xtick',labelsize=8)
plt.rc('ytick',labelsize=8)

fig = plt.figure(figsize=(7.00,2.50))
ax = fig.add_axes([0.09,0.21,0.40,0.75])
line2, = plt.plot(x/m,y2/mm,'k-')
line3, = plt.plot(x/m,y1/mm,'k--')
line1, = plt.plot(x_OPS/m,y_OPS/mm,'ko')
plt.legend((line1, line2, line3), ('Numerical Results', 'Closed-form Solution', 'No Ponding Effect'),frameon=False)
plt.xlabel('Position Along Length of Beam (m)\n(a)')
plt.ylabel('Displacement (mm)')
plt.xlim(0,L/m)
plt.ylim(-100,0)

ax = fig.add_axes([0.59,0.21,0.40,0.75])
line2, = plt.plot(x/m,M2/kNm,'k-')
line3, = plt.plot(x/m,M1/kNm,'k--')
line1, = plt.plot(x_OPS/m,M_OPS/kNm,'ko')
plt.legend((line1, line2, line3), ('Numerical Results', 'Closed-form Solution', 'No Ponding Effect'),frameon=False)
plt.xlabel('Position Along Length of Beam (m)\n(b)')
plt.ylabel('Bending Moment (kN-m)')
plt.xlim(0,L/m)
plt.ylim(0,100)
plt.savefig('Example_Defl_and_Moment.png',dpi=300)
plt.savefig('Example_Defl_and_Moment.pdf')

plt.show()


from math import floor, ceil
import numpy as np
from scipy import ndimage
from scipy.fftpack import dct
import cv2
import time

def dss(img1, img2, sigma=1.55, C=(1000, 300)):
    """
    DSS DCT Subband Similarity index for measuring image quality
    DSS = DSS_INDEX(IMG1, IMG2) calculates the DCT Subband Similarity (DSS)
    score for image IMG1, with the image IMG2 as the reference. IMG1 and
    IMG2 should be 2D grayscale images, and must be of the same size.
    DSS = DSS_INDEX(IMG1, IMG2, SIGMA, C) calculates the DSS score
    with control over parameters of the computation. Parameters include:
    sigma - Specifies the standard deviation of the Gaussian that
            determines the proportion between the weight given to low
            spatial frequencies and to high spatial frequencies.
            Default value: sigma = 1.55
    C     - Specifies constants in the DSS subband similarity equations
            (see the reference paper below). C should be a vector of
            size [1 2] where the first element in C is for the DC
            equation and the second element in C is for the AC equation.
            Default value: C = [1000 300]
    This function is an implementation of the algorithm described in the
    following paper:
        Amnon Balanov, Arik Schwartz, Yair Moshe, and Nimrod Peleg,
        "Image Quality Assessment based on DCT Subband Similarity," 22nd
        IEEE International Conference on Image PRocessing (ICIP 2015).
    Example
    ---------
    This example shows how to compute DSS score for a noisy image given
    the original reference image.
    img2 = imread('cameraman.tif');
    img1 = imnoise(img2, 'gaussian', 0, 0.001);
    subplot(1,2,1); imshow(img2); title('Reference Image');
    subplot(1,2,2); imshow(img1); title('Noisy Image');
    dss = dss_index(img1,img2);
    fprintf('The DSS score is 0.4f\n',dss);
    Version 1.00
    Copyright(c) 2015, Yair Moshe
    Signal and Image Processing Laboratory (SIPL)
    Department of Electrical Engineering
    Technion - Israel Institute of Technology
    Technion City, Haifa 32000, Israel
    Tel: +(972)4-8294746
    e-mail: yair@ee.technion.ac.il
    WWW: sipl.technion.ac.il
    """

    # Parse inputs
    if img1.ndim != 2:
        raise TypeError("Images must be greyscale")
    if img2.shape != img2.shape:
        raise ValueError("Image 1 and 2 must be of same size")
    try:
        float(sigma)
    except Exception:
        raise TypeError("Sigma must be a scalar")
    if not isinstance(C, tuple):
        raise TypeError("C must be a tuple")

    # Compute DSS
    # Crop images size to the closest multiplication of 8
    nRows, nCols = img1.shape
    nRows = 8*floor(nRows/8)
    nCols = 8*floor(nCols/8)

    img1 = img1[:nRows, :nCols]
    img2 = img2[:nRows, :nCols]

    # Channel decomposition for both images by 8x8 2D DCT
    img1Decomp = dct_decomp(img1)
    img2Decomp = dct_decomp(img2)
    # Create a Gaussian window that will be used to weight subbands scores
    r = np.arange(1, 9, 1)
    X,Y = np.meshgrid(r, r)
    distance = np.sqrt((X-0.5)**2 + (Y-0.5)**2)
    w = np.exp(-((distance**2)/(2*sigma**2))).real

    # Compute similarity between each subband in img1 and img2
    subbandSimilarity = np.zeros((8, 8))
    smallWeightThresh = 1e-2
    for m in range(8):
        for n in range(8):
            # Skip subbands with very small weight
            if(w[m,n] < smallWeightThresh):
                w[m,n] = 0
                continue

            subbandSimilarity[m,n] = subband_similarity(
                img1Decomp[m::8, n::8],
                img2Decomp[m::8, n::8],
                m, n, C)

    # Weight subbands similarity scores
    dss = np.sum(np.sum(subbandSimilarity * (w/np.sum(w))))
    #np.savetxt('img_python.txt', sortedVarLeftTerm, fmt="%.5f", delimiter=",", newline="\n")
    return dss

# Channel decomposition for an image by 8x8 2D DCT
def dct_decomp(img):
    nRows, nCols = img.shape
    D = dctmtx(8)
    doubleImg = img.astype(np.float64)
    decomp = np.zeros(img.shape)
    for blockNumInRow in range(nRows//8):
        for blockNumInColumn in range(nCols//8):
            curBlock = doubleImg[0 + 8*(blockNumInRow) : 8 + 8*(blockNumInRow), 0 + 8*(blockNumInColumn):8 + 8*(blockNumInColumn)]
            decomp[0 + 8*(blockNumInRow) : 8 + 8*(blockNumInRow), 0 + 8*(blockNumInColumn):8 + 8*(blockNumInColumn)] = np.matmul(np.matmul(D,curBlock), D.transpose())
    return decomp

# Discrete cosine transform matrix by matlab
# https://fr.mathworks.com/help/images/discrete-cosine-transform.html#f21-16137
def dctmtx(N):
    p, q = np.ogrid[1:N, 1:2*N:2]
    return np.concatenate((np.sqrt(1/N)*np.ones((1,N)) , np.sqrt(2/N) * np.cos(np.pi/(2*N) * p * q)), axis=0)

def matlab_style_gauss2D(shape=(3,3),sigma=0.5):
    """
    2D gaussian mask - should give the same result as MATLAB's
    fspecial('gaussian',[shape],[sigma])
    """
    m,n = [(ss-1.)/2. for ss in shape]
    y,x = np.ogrid[-m:m+1,-n:n+1]
    h = np.exp( -(x*x + y*y) / (2.*sigma*sigma) )
    h[ h < np.finfo(h.dtype).eps*h.max() ] = 0
    sumh = h.sum()
    if sumh != 0:
        h /= sumh
    return h

# Compute similarity between a subband in img1Subband and img2Subband
def subband_similarity(img1Subband, img2Subband, m, n, Cs):
    size = 3 # constant
    sigma = 1.5 # constant
    percentile = 0.05 # constant

    if((m == 0) and (n == 0)):
        C = Cs[0] # DC
    else:
        C = Cs[1] # AC
    # Compute local variance
    window = matlab_style_gauss2D(shape=(size,size), sigma=sigma)
    mu1 = ndimage.convolve(img1Subband, window, mode="constant")
    mu2 = ndimage.convolve(img2Subband, window, mode="constant")
    sigma1_sq = ndimage.convolve(img1Subband*img1Subband, window, mode="constant") - mu1*mu1
    sigma2_sq = ndimage.convolve(img2Subband*img2Subband, window, mode="constant") - mu2*mu2
    sigma12 = ndimage.convolve(img1Subband*img2Subband, window, mode="constant") - mu1*mu2
    sigma1_sq[sigma1_sq < 0] = 0
    sigma2_sq[sigma2_sq < 0] = 0
    varLeftTerm = (2*np.sqrt(sigma1_sq * sigma2_sq) + C) / (sigma1_sq + sigma2_sq + C)
    # Spatial pooling of worst scores
    percentileIndex = round(percentile * (varLeftTerm.size))
    sortedVarLeftTerm = np.sort(varLeftTerm.flatten())
    similarity = np.mean(sortedVarLeftTerm[:percentileIndex])
    # For DC, multiply by a right term
    if ((m == 0) and (n == 0)):
        varRightTerm = ((sigma12 + C) / (np.sqrt(sigma1_sq * sigma2_sq) + C))
        sortedVarRightTerm = np.sort(varRightTerm.flatten())
        simRightTerm = np.mean(sortedVarRightTerm[:percentileIndex])
        similarity = similarity * simRightTerm
    return similarity


import numpy as np
import matplotlib.pyplot as plt

def pdf(costh, P_mu):
    # define our probability density function
    return 0.5 * (1.0 - 1.0 / 3.0 * P_mu * costh)

def acc_rej(N_measurements, P_mu):
    x = np.random.uniform(-1.0, 1.0, size=N_measurements)

    x_axis = np.linspace(-1.0,1.0, 1000)
    fmax= np.amax(pdf(x_axis,P_mu)) #find the maximum of the function

    u = np.random.uniform(0, fmax, size= N_measurements)

    # we use a mask in order to reject the values we don't want
    data_pdf = x[u < pdf(x,P_mu)]
    return data_pdf

N_measurements=10000000

for P_mu in np.arange(-1.,1.2,0.2):
    data_pdf = acc_rej(N_measurements,P_mu)
    mean = np.mean(data_pdf)
    P_mu_est=-9.*mean
    print(P_mu, abs(P_mu-P_mu_est))
    plt.plot(P_mu, abs(P_mu-P_mu_est), ".")

plt.xlabel(r"$P_\mu$")
plt.ylabel(r'$|P_\mu - \hat{P_\mu}|$')

# plt.savefig(r"C:\Users\Aleix López\Desktop\acc_rej.jpg")
plt.show()

Projects/Probability and Monte Carlo Techniques/_3_dependency.py


import matplotlib.pyplot as plt
import numpy as np
from definitions import *

theta = np.linspace(-np.pi, np.pi-0.01, 100)
# circle include origin
x1, y1 = contour_circle(0, 0, 0.5, theta)
# _, a = to_polar(x1, y1)
# print(a)
u1, v1 = complex_sqrt(x1, y1, polar=False)
u11, v11 = f1(x1, y1, polar=False)

# circle donot include origin
x2, y2 = contour_circle(0, 1, 0.5, theta)
# _, a = to_polar(x2, y2)
# print(a)
u2, v2 = complex_sqrt(x2, y2, polar=False)
u21, v21 = f1(x2, y2, polar=False)

# yet Another contour
x4, y4 = contour_rectangle(-4, 3, 3, 2, len(theta))
# _, a = to_polar(x3, y3)
# print(a)
u4, v4 = complex_sqrt(x4, y4, polar=False)

# Plotting
fig, ax = plt.subplots()
ax.set_aspect("equal")
ax.set_xlim(-4.2, 4.2)
ax.set_ylim(-4, 4)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')

plt.plot(x1, y1, label="z1")
plt.plot(u1, v1, label="sqrt(z1)")
plt.plot(u11, v11, label="sqrt(z1^2+1)")
plt.plot(x2, y2, label="z2")
plt.plot(u2, v2, label="sqrt(z2)")
plt.scatter(u21, v21, s=1.5, label="sqrt(z2^2+1)")
plt.plot(x4, y4, label="z4")
plt.plot(u4, v4, label="sqrt(z4)")
plt.legend()
plt.show()


import numpy as np

def orthogonal(n):  # Schmidt orthogonalization
    mat = np.zeros((n, n))
    matric = np.random.rand(n ** 2).reshape((n, n))
    mat[0, :] = matric[0, :]
    for i in range(1, n):
        l1 = matric[i, :]
        ln = l1
        for j in range(i):  # 获得正交基
            v = mat[j, :]
            ln -= np.dot(np.dot(l1, v) / np.dot(v, v), v)
        mat[i, :] = ln
    for _ in range(n):  # 获得单位基
        mat[_, :] = mat[_, :] / np.linalg.norm(mat[_, :])
    if (np.dot(mat.T, mat).astype(int) - np.eye(n) == 0).all():  # 检查是否为正交矩阵
        return mat
    else:
        return orthogonal(n)

def projection(vec, matric, n):
    try:
        x = np.zeros(n)
        for i in range(n):
            x[i] = np.dot(matric[i, :], vec)
        return x

    except Exception as e:
        return e

n = 5
W = orthogonal(n)
vec = np.random.rand(n)
proj = projection(vec, W, n)
print("生成的一组单位正交基W为：", W, "\n生成的随机向量的投影向量为：", proj)


#!/usr/bin/env python3
"""
Value iteration for a finite markov decision process.
Solving the "badger problem":
    There are n crates and b badgers. The other n-b crates
    have $1 each in them. If you open a badger crate, you lose
    all your earnings. When should you stop opening crates?

"""
# Dependencies
from __future__ import division
import numpy as np; npl = np.linalg

# Crates and badgers
n = 5
b = 1
print("Crates: {}".format(n))
print("Badgers: {}".format(b))

# State and action spaces
S = np.arange(n+2-b)
A = np.arange(2)

# Transition matrix for u=0
P0 = np.eye(len(S), dtype=np.float64)

# Transition matrix for u=1
P1 = np.zeros((len(S), len(S)), dtype=np.float64)
P1[:, -1] = 1.0
for i in S[:-1]:
    l = n - i
    P1[i, i+1] = (l-b)/l
    P1[i, -1] = 1.0 - P1[i, i+1]

# Reward matrix for u=0
R0 = np.zeros((len(S), len(S)), dtype=np.float64)

# Reward matrix for u=1
R1 = np.zeros((len(S), len(S)), dtype=np.float64)
for i in S[:-1]:
    R1[i, i+1] = 1.0
    R1[i, -1] = -i

# Reward expectations
r = np.vstack((np.average(R0, weights=P0, axis=1),
               np.average(R1, weights=P1, axis=1)))

# Discount factor
g = 0.999

# Initial condition and runtime limit
Vlast = np.random.sample(len(S))
imax = 100000
tol = 1e-5

# Value iteration, heavily vectorized
print("Calculating...")
for i in range(imax):
    Q = r + g*np.vstack((P0.dot(Vlast),
                         P1.dot(Vlast)))
    U = np.argmax(Q, axis=0)
    V = Q[U, S]
    if np.allclose(V, Vlast, rtol=tol):
        break
    Vlast = np.copy(V)

# Extract and verify stopping decision
choice = np.count_nonzero(U)
assert np.all(U[:choice])

# Show results
print("(...finished @ iter {} / {})".format(i, imax))
print("Optimal Stop Policy: {}".format(choice))
print("Optimal Expected Value: {}".format(np.round(V[0], 3)))


import argparse
from itertools import product
import os.path as osp

import numpy as np
import pandas as pd
import scipy.spatial
import scipy.optimize
import tqdm

def x_3(array):
    return array ** 3

def module_x(array):
    return np.abs(array - 0.2)

def sin(array):
    return array * np.sin(1 / array)

def brute_force(func, left, right, epsilon=0.001):
    x_array = np.arange(left, right, epsilon)
    values = func(x_array)
    min_f = np.min(values)
    idx_min = np.argmin(values)
    return {"min": x_array[idx_min], "func_min": min_f, "iterations": x_array.shape[0]}

def dichotomy(func, left, right, epsilon=0.001, delta=None):
    if delta is None:
        delta = epsilon / 2
    coords = [[left, right]]
    iters = 0
    while (right - left) > epsilon:
        x1, x2 = (left + right - delta) / 2, (left + right + delta) / 2
        f_x1, f_x2 = map(func, [x1, x2])
        if f_x1 <= f_x2:
            right = x2
        else:
            left = x1
        iters += 2
        coords.append([left, right])
    min_f = func((right - left) / 2 + right)
    return {"min": (right - left) / 2 + right, "func_min": min_f, "iterations": iters, "coords": coords}

def golden_section(func, left, right, epsilon=0.001):
    delta = (3 - np.sqrt(5)) / 2
    coords = [[left, right]]
    x1, x2 = left + delta * (right - left), right - delta * (right - left)
    f_x1, f_x2 = map(func, [x1, x2])
    iters = 2
    while (right - left) > epsilon:
        if f_x1 <= f_x2:
            right = x2
            x2 = x1
            f_x2 = f_x1
            calc_x2 = False
        else:
            left = x1
            x1 = x2
            f_x1 = f_x2
            calc_x2 = True
        coords.append([left, right])

        if calc_x2:
            x2 = right - delta * (right - left)
            f_x2 = func(x2)
        else:
            x1 = left + delta * (right - left)
            f_x1 = func(x1)
        iters += 1
    min_f = func((right - left) / 2 + right)
    return {"min": (right - left) / 2 + right, "func_min": min_f, "iterations": iters, "coords": coords}

def linear_approx(X, a, b):
    return X * a + b

def rational_approx(X, a, b):
    return a / (1 + b * X)

def loss(func, X, a, b, y_true, a_bounds=(0, 1), b_bounds=(0, 1), apply_bounds=False):
    # Artificial bounds are here are for Nelder-Mead algorithm which have no constraints
    #  and tends to find optimal solution outside the bounds for bruteforce algorithm
    if apply_bounds:
        if a < a_bounds[0] or a > a_bounds[1]:
            return 10 ** 10
        if b < b_bounds[0] or b > b_bounds[1]:
            return 10 ** 10
    approx = func(X, a, b)
    return np.sum((approx - y_true) ** 2)

def brute_force_opt(func, X, y_true, a_bounds=(0, 1), b_bounds=(0, 1), epsilon=0.001):
    a_values = np.arange(a_bounds[0], a_bounds[1] + epsilon, epsilon)
    b_values = np.arange(b_bounds[0], b_bounds[1] + epsilon, epsilon)

    min_loss = 10 ** 10
    min_args = None
    for a in a_values:
        for b in b_values:
            loss_value = loss(func, X, a, b, y_true)
            if loss_value < min_loss:
                min_args = {"a": a, "b": b}
                min_loss = loss_value
    return {"loss": min_loss, "args": min_args, "iterations": a_values.shape[0] * b_values.shape[0]}

def gauss_opt(func, X, y_true, a_bounds=(0, 1), b_bounds=(0, 1), epsilon=0.001):
    a, b = map(np.mean, [a_bounds, b_bounds])
    a_prev, b_prev = a_bounds[0], b_bounds[0]
    loss_prev = loss(func, X, a, b, y_true)

    min_loss = 10 ** 10
    iters = 1
    loss_values = []
    coords = [[a, b]]

    while (
            scipy.spatial.distance.euclidean([a_prev, b_prev], [a, b]) > epsilon and np.abs(
        loss_prev - min_loss) > epsilon
    ):
        for opt_var in ["a", "b"]:
            if opt_var == "a":
                aux_func = lambda x: loss(func, X, a=x, b=b, y_true=y_true)
                opt = golden_section(aux_func, a_bounds[0], b_bounds[1], epsilon=epsilon)
                a_prev = a
                a = opt["min"]
            else:
                aux_func = lambda x: loss(func, X, a=a, b=x, y_true=y_true)
                opt = golden_section(aux_func, b_bounds[0], b_bounds[1], epsilon=epsilon)
                b_prev = b
                b = opt["min"]

            iters += opt["iterations"]
            min_loss = opt["func_min"]
            loss_values.append(min_loss)
            coords.append([a, b])
    return {
        "loss": min_loss,
        "args": {"a": a, "b": b},
        "iterations": iters,
        "loss_values": loss_values,
        "coords": coords,
    }

def nelder_mead_opt(func, X, y_true, a_bounds=(0, 1), b_bounds=(0, 1), epsilon=0.001):
    opt_func = lambda x: loss(func, X=X, a=x[0], b=x[1], y_true=y_true, a_bounds=a_bounds, b_bounds=b_bounds,
                              apply_bounds=True)
    a0, b0 = map(np.mean, [a_bounds, b_bounds])
    result = scipy.optimize.minimize(
        opt_func, x0=np.asarray([a0, b0]), method="Nelder-Mead", options={"xatol": epsilon, "fatol": epsilon}
    )
    return {"loss": result.fun, "args": {"a": result.x[0], "b": result.x[1]}, "iterations": result.nfev}

if __name__ == "__main__":
    parser = argparse.ArgumentParser("Gather data for task 2")
    parser.add_argument("--output_1d",
                        default=osp.join(osp.dirname(osp.realpath(__file__)), "..", "data", "task2_1d.csv"),
                        help="Output file")
    parser.add_argument("--output_data_2d",
                        default=osp.join(osp.dirname(osp.realpath(__file__)), "..", "data", "task2_data_2d.csv"),
                        help="Output file")
    parser.add_argument("--output_2d",
                        default=osp.join(osp.dirname(osp.realpath(__file__)), "..", "data", "task2_2d.csv"),
                        help="Output file")
    parser.add_argument("--random_state", type=int, default=111, help="Random state for random generator")
    args = parser.parse_args()

    np.random.seed(args.random_state)

    # 1d optimization
    data = []
    for optimizer in ("brute_force", "dichotomy", "golden_section"):
        for func, interval in zip(("x_3", "module_x", "sin"), ([0, 1], [0, 1], [0.1, 1])):
            result = eval(optimizer)(eval(func), interval[0], interval[1])
            data.append(
                {"optimizer": optimizer, "func": func, "min": result["min"], "func_min": result["func_min"],
                 "iterations": result["iterations"]}
            )
    data = pd.DataFrame(data)
    data.to_csv(args.output_1d, index=False)

    # 2d optimization
    # Generate data
    alpha, beta = np.random.uniform(size=2)
    print(f'Alpha: {alpha}, beta: {beta}')
    X = np.arange(0, 1.01, 0.01)
    deltas = np.random.normal(size=X.shape)
    y_clean = alpha * X + beta
    y = alpha * X + beta + deltas
    opt_data = pd.DataFrame(np.vstack([X, y_clean, y]).T, columns=['X', 'y_clean', 'y'])
    opt_data.to_csv(args.output_data_2d, index=False)

    # Gather optimization data
    data_opt = []

    for method, approx_func in tqdm.tqdm(
            product(["brute_force_opt", "gauss_opt", "nelder_mead_opt"], ["linear_approx", "rational_approx"]),
            total=6,
            desc="Optimizing 2D",
    ):
        opt_res = eval(method)(eval(approx_func), X, y, a_bounds=(-2, 2), b_bounds=(-2, 2))
        data_opt.append(
            {
                "method": method,
                "approx_func": approx_func,
                "loss": opt_res["loss"],
                "a": opt_res["args"]["a"],
                "b": opt_res["args"]["b"],
                "iterations": opt_res["iterations"],
            }
        )

    data_opt = pd.DataFrame(data_opt)
    data_opt.to_csv(args.output_2d, index=False)


from numpy import array, zeros
from numpy.linalg import det

def cramer(A,b):
    n = len(b)
    detsub = zeros((n))
    x = zeros((n))
    detA = det(A)
    for i in range(n):
        Atemp = A.copy()
        Atemp[:,i] = b
        detsub[i] = det(Atemp)
        x[i] = detsub[i]/detA
    return detsub, x

A = array([[5,-1,1],[-1,3,-1],[1,-1,4]])
b = array([6,2,11])
detsub, x = cramer(A,b)

print('det(A) =  %8.4f' % det(A))
print('det(A1) =  %8.4f' % detsub[0])
print('det(A2) =  %8.4f' % detsub[1])
print('det(A3) =  %8.4f' % detsub[2])
print('x1 = %8.4f' % x[0])
print('x2 = %8.4f' % x[1])
print('x3 = %8.4f' % x[2])


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('ggplot')
from mpl_toolkits.mplot3d import Axes3D
import pandas as pd

# warmup_exercise:
def warmup_exercise():
    """ Return a 5x5 identity matrix. """
    return np.eye(5)

# Linear Regression with One Variable
def load_data(fileLocation, featureColumns, responseColumns):
    """ Given a file location, this provides a pandas DataFrame of the data.

    featureColumns and responseColumns are lists of indices corresponding to those data series,
    with the convention that the returned data is from left to right, features and then responses. """
    return pd.read_csv(fileLocation, usecols=featureColumns + responseColumns, header=None)

def plot_scatter(data):
    """ Generates a scatter plot of the provided data. Returns the axes for further plotting ability. """

    data.columns = ['city_population', 'profit']
    axes = data.plot(x='city_population', y='profit', kind='scatter')
    axes.set_xlim(left=data['city_population'].min() - 1)
    axes.set_xlabel('Population of city in 10,000s')
    axes.set_ylabel('Profit in $10,000s')
    return axes

def compute_cost(features, response, theta):
    """ Computes the least square error cost function value. """

    # in this case we know that the last variable is the response, so we ignore it
    cost = np.dot(features, theta) # h_theta(x_i)
    cost -= response # - y_i
    cost *= cost # square it
    cost = cost.sum() / (2 * len(features)) # summation, then divide by 2m
    return cost

def compute_dCost_dTheta(features, response, theta):
    """ Helper function that calculates dJ/dTheta, evaluated at theta. """

    # notice that this will be similar to the general cost function
    cost = np.dot(features, theta) # h_theta(x_i)
    cost -= response # - y_i
    cost = np.broadcast_to(cost, (len(features.columns), len(cost))).T * features # elementwise mult by the remainder of derivative
    cost = np.sum(cost, axis=0) / len(features)
    return cost.values

def execute_gradient_descent(data, learningRate=0.01, maxIters=2000, theta=None, addInterceptFeature=True):
    """ Returns the parameter vector Theta of len(data.columns) + 1 that minimizes least
    squares error loss.

    addInterceptFeature - if True, adds a feature column of 1's corresponding to the Theta_0 parameter
    theta - option to initialize Theta to specific values.
    learningRate - algorithm learning rate parameter
    maxIters - number of iterations after which the algorithm automatically terminates """

    # initialize theta if not given
    if theta is None:
        interceptLength = 0 if addInterceptFeature else -1
        theta = np.zeros(len(data.columns) + interceptLength)

    # insert intercept feature
    data.insert(0, 'intercept', 1)

    # print initial cost, and set up a cost tracking list
    cost = compute_cost(data.iloc[:,:-1], data.iloc[:,-1], theta)
    costProgression = [cost]
    print('Current _cost: {0}'.format(cost))

    for i in range(maxIters):

        theta -= learningRate * compute_dCost_dTheta(data.iloc[:,:-1], data.iloc[:,-1], theta) # dCost_dTheta is a 2-vector

        cost = compute_cost(data.iloc[:,:-1], data.iloc[:,-1], theta)
        costProgression.append(cost)
        print('Current cost: {0}'.format(cost))

    # remove the dummy intercept variable from data, else our scatter plots will be messed up
    data.drop('intercept', axis=1, inplace=True)
    return theta, pd.DataFrame(costProgression)

def plot_fitted_regression(data, fittedTheta):
    """ Plots the fitted regression line on a previous scatter plot. """

    scatter = plot_scatter(data)

    x = np.arange(data['city_population'].min(), data['city_population'].max(), .1) # recall the name of our columns
    scatter.plot(x, fittedTheta[0] + fittedTheta[1] * x)

    scatter.legend(['Training data', 'Linear Regression'])
    return

def predict(observationVector, theta):
    """ Given a fitted theta and observation vector, we can make a regression prediction.
    We assume observationVector and theta are lists. """

    observationVector.insert(0, 1) # add the intercept value
    return sum([observationVector[i] * theta[i] for i in range(len(theta))])

def plot_cost_curves(data):
    """ Plots 3d and contour curves of the cost function in parameter space. This helps us
    visualize the dynamics of the gradient descent algorithm. """

    # initialize our parameter space
    thetaZeroValues = np.linspace(-10, 10, 100)
    thetaOneValues = np.linspace(-1, 4, 100)
    costValues = np.zeros((100, 100))

    # we are going to need to insert the intercept column again
    data.insert(0, 'intercept', 1)

    # get the fuction values over parameter space
    for i in range(100):
        for j in range(100):
            costValues[i,j] = compute_cost(data.iloc[:,:-1], data.iloc[:,-1], [thetaZeroValues[i], thetaOneValues[j]])

    # drop intercept
    data.drop('intercept', axis=1, inplace=True)

    # convert to 3d friendly meshgrid
    thetaZeroValues, thetaOneValues = np.meshgrid(thetaZeroValues, thetaOneValues)

    # time to plot, first the surface and then the contour
    fig = plt.figure(figsize=(15,10))

    # surface plot
    surfax = fig.add_subplot(1, 2, 1, projection='3d')
    surfax.plot_surface(thetaZeroValues, thetaOneValues, costValues.T)

    # contour plot
    contax = fig.add_subplot(1, 2, 2)
    contax.contour(thetaZeroValues, thetaOneValues, costValues.T, 17) # seventeen contour lines for clarity
    contax.set_ylabel('Theta One')
    contax.set_xlabel('Theta Zero')
    return

def required_main():
    """ Main function for required exercises """

    loc = r"C:\Users\ashamlian\Downloads\machine-learning-ex1\ex1\ex1data1.txt"
    foodTruckData = load_data(loc, [0], [1])
    plot_scatter(foodTruckData)

    fittedTheta, _ = execute_gradient_descent(foodTruckData, theta=[0,0])

    print('For a town of {0} people, we expect profits of {1} * $10,000'.format(35000, predict([3.5], fittedTheta)))
    print('For a town of {0} people, we expect profits of {1} * $10,000'.format(70000, predict([7], fittedTheta)))

    plot_fitted_regression(foodTruckData, fittedTheta)
    plot_cost_curves(foodTruckData)
    return

# Optional Exercises
def normalize_features(data):
    """ Apply standardization to the dataset. Pandas makes this very simple """

    data.columns = ['sq_ft', 'bedrooms', 'price']
    tmp = data[['sq_ft', 'bedrooms']]
    tmp = tmp.sub(tmp.mean()).div(tmp.std())
    return pd.concat([tmp, data['price']], axis=1)

def plot_convergence(costProgression, learningRate):
    """ Constructs a plot that demonstrates the convergence rate for a given learning rate. """

    x = np.arange(1, len(costProgression)+1)

    fig, ax = plt.subplots(1, 1, 1)
    ax.plot(x, costProgression)
    ax.set_xlabel('Number of iterations')
    ax.set_ylabel('J(Theta)')
    fig.title('Convergence rate for learning rate alpha = {0}'.format(learningRate))
    return

def plot_convergences(costProgressions, learningRates):
    """ Constructs a plot that compares convergence rates to respective learning rates. """

    learningRates = [str(alpha) for alpha in learningRates] # convert to string for column names
    costProgressions.columns = learningRates
    axes = costProgressions.plot()
    axes.legend(learningRates)
    return

def calculate_normal_equation(data):
    """ Use the analytical method to calculate the optimal parameter vector. """

    # split data for legibility and insert the intercept
    features = data.iloc[:, :-1]
    features.insert(0, 'intercept', 1)
    response = data.iloc[:, -1]

    # compute the normal equation
    # this is kinda like using *= or += but for matrices instead
    theta = np.linalg.pinv(np.dot(features.T, features))
    theta = np.dot(theta, features.T)
    theta = np.dot(theta, response)
    return theta

def optional_main():
    """ Main function for optional exercises."""

    loc = r"C:\Users\ashamlian\Downloads\machine-learning-ex1\ex1\ex1data2.txt"
    housingData = load_data(loc, [0,1], [2])

    normedHousingData = normalize_features(housingData)

    # get a range of alphas and find the cost evolutions for them
    testedLearningRates = [1, 0.3, .1, .03, .01, .003, .001]
    associatedCosts = []

    for alpha in testedLearningRates:

        _, costProgression = execute_gradient_descent(normedHousingData, learningRate=alpha, maxIters=100)
        associatedCosts.append(costProgression)

    associatedCosts = pd.concat(associatedCosts, axis=1) # now we have one nice pandas dataframe
    plot_convergences(associatedCosts, testedLearningRates) # notice the resulting scale of the plot, still HUGE

    # we are going to predict the price of a 1650 sq. ft, 3 bedroom house
    # let's take the theta of the best of the learning rates: 1.0
    # but don't forget that we have to normalize the prediction for this model
    newHouse = [1650, 3]
    normedNewHouse = [-0.4412732002552997, 0.22367546144324044] # i did this manually, using the training mean() and std()
    fittedTheta, _ = execute_gradient_descent(normedHousingData, learningRate=1) # maxiters defaults to 2000
    print('For a 1650 square foot, 3 bedroom house, the predicted price is: {0}'.format(predict(normedNewHouse, fittedTheta)))

    # and compare it to that of the normal equation
    fittedTheta = calculate_normal_equation(housingData)
    print('For a 1650 square foot, 3 bedroom house, the predicted price is: {0}'.format(predict(newHouse, fittedTheta)))

    return

if __name__ == '__main__':

    required_main()
    optional_main()


# fill in the following cost matrix
costs = '''
1 3 5 -1 0 2
2 3 5 6 1 3
1 1 3 7 1 2
7 0 1 1 5 7
5 5 1 1 2 1
-1 3 2 8 -1 8
'''

from scipy.optimize import linear_sum_assignment
import numpy as np

def pp(table, side=None):
    if len(table.shape) == 1:
        for v in table:
            print('{:3}'.format(v), end='')
        print()
    else:
        for i, row in enumerate(table):
            for v in row:
                print('{:3}'.format(v), end='')
            if side is not None:
                print(' | {:3}'.format(side[i][0]))
            else:
                print()

costs = [[int(y) for y in x.split()] for x in (filter(None, costs.split('\n')))]
costs = np.array(costs)

r, c = linear_sum_assignment(costs)
out = []
for row, col in zip(r,c):
    out.append('x{}{} = '.format(row+1, col+1))

print('--------- Solution ----------')
print('{}1'.format(''.join(out)))
print('cost: {}'.format(costs[r,c].sum()))
print('\n --------- Steps ----------')
redo = costs.copy()
print('Row minimums')
min_rows = np.min(redo, axis=1).reshape((-1,1))
pp(redo, min_rows)

redo -= min_rows
print('\nCol minimums')
min_cols = np.min(redo, axis=0)
pp(redo)
print('-'*3*len(redo[0]))
pp(min_cols)
redo -= min_cols
print()
pp(redo)
print('\nCover all zeros')
print('Find min of uncovered cells = x')
print('Subtract x from uncovered cells')
print('Add x to intersection points of lines')
print('Repeat until zero on each row and column')

LP = True
VERBOSE = False
if LP:
    print('\n----- Gurobi Solver -----\n')
    from gurobipy import *
    m = Model()
    m.setParam(GRB.Param.OutputFlag, VERBOSE)

    x = {}
    r, c = costs.shape
    for i in range(r):
        for j in range(c):
            x[i,j] = m.addVar(0, 1, costs[i,j], GRB.BINARY, 'x{}{}'.format(i+1, j+1))

    # Each row must be assigned to one column
    for i in range(r):
        expr = LinExpr()
        for j in range(c):
            expr.addTerms(1, x[i, j])
        m.addConstr(expr, GRB.EQUAL, 1)

    # Each column must be assigned to one row
    for j in range(c):
        expr = LinExpr()
        for i in range(r):
            expr.addTerms(1, x[i, j])
        m.addConstr(expr, GRB.EQUAL, 1)

    m.update()
    m.write('output/match.lp')
    m.optimize()
    print('\nObjective = {}'.format(m.ObjVal))
    for i, j in sorted(x):
        v = x[i, j].x
        if v > 0.5:
            print ('x{}{} = {}'.format(i+1, j+1 , 1))


import numpy as np
from PIL import Image
from matplotlib import pyplot as plt

# %% Q6_1_4 alpha-trimmed mean filter

Q6_1_4 = np.asarray(Image.open("Q6_1_4.tiff"))
plt.show()

def alpha_filter(img_raw, n: int = 3, d=0.1):
    m = (n - 1) // 2
    trimmed = max(int(d * n ** 2), 1)
    row, col = img_raw.shape
    img_pad = np.pad(img_raw, m)

    img_out = np.array([np.mean(np.sort(img_pad[i:i + n, j:j + n].flat)
                                [trimmed:-trimmed])
                        for i in range(row)
                        for j in range(col)])

    return img_out.astype(np.uint8).reshape(row, col)

# %% test different filter size
Q6_1_4_n3 = alpha_filter(Q6_1_4, 3)
Q6_1_4_n5 = alpha_filter(Q6_1_4, 5)
Q6_1_4_n7 = alpha_filter(Q6_1_4, 7)

plt.figure(figsize=(6.4, 7.2))
plt.subplot(221)
plt.title('Raw Q6_1_4.tiff')
plt.imshow(Q6_1_4, cmap='gray')
plt.subplot(222)
plt.title('Alpha-trimmed mean filtered\nwith filter size $n=3$, $d=2$')
plt.imshow(Q6_1_4_n3, cmap='gray')
plt.subplot(223)
plt.title('Alpha-trimmed mean filtered\nwith filter size $n=5$, $d=2$')
plt.imshow(Q6_1_4_n5, cmap='gray')
plt.subplot(224)
plt.title('Alpha-trimmed mean filtered\nwith filter size $n=7$, $d=2$')
plt.imshow(Q6_1_4_n7, cmap='gray')
plt.savefig('Q6_1_4_n.png')
plt.show()

# %% test the d param in alpha_filter
Q6_1_4_d1 = alpha_filter(Q6_1_4, d=1 / 9)
Q6_1_4_d2 = alpha_filter(Q6_1_4, d=2 / 9)
Q6_1_4_d3 = alpha_filter(Q6_1_4, d=3 / 9)

plt.figure(figsize=(6.4, 7.2))
plt.subplot(221)
plt.title('Raw Q6_1_4.tiff')
plt.imshow(Q6_1_4, cmap='gray')
plt.subplot(222)
plt.title('Alpha-trimmed mean filtered\nwith filter size $n=3$, $d=1/9$')
plt.imshow(Q6_1_4_d1, cmap='gray')
plt.subplot(223)
plt.title('Alpha-trimmed mean filtered\nwith filter size $n=3$, $d=2/9$')
plt.imshow(Q6_1_4_d2, cmap='gray')
plt.subplot(224)
plt.title('Alpha-trimmed mean filtered\nwith filter size $n=3$, $d=3/9$')
plt.imshow(Q6_1_4_d3, cmap='gray')
plt.savefig('Q6_1_4_d.png')
plt.show()

# %% test for multiple median filter
Q6_1_4_1 = alpha_filter(Q6_1_4, d=4 / 9)
Q6_1_4_2 = alpha_filter(Q6_1_4_1, d=4 / 9)
Q6_1_4_3 = alpha_filter(Q6_1_4_2, d=4 / 9)
Q6_1_4_4 = alpha_filter(Q6_1_4_3, d=4 / 9)

plt.figure(figsize=(6.4, 7.2))
plt.subplot(221)
plt.title('Median filtered once\nwith filter size $n=3$')
plt.imshow(Q6_1_4_1, cmap='gray')
plt.subplot(222)
plt.title('Median filtered twice\nwith filter size $n=3$')
plt.imshow(Q6_1_4_2, cmap='gray')
plt.subplot(223)
plt.title('Median filtered thrice\nwith filter size $n=3$')
plt.imshow(Q6_1_4_3, cmap='gray')
plt.subplot(224)
plt.title('Median filtered quadrice\nwith filter size $n=3$')
plt.imshow(Q6_1_4_4, cmap='gray')
plt.savefig('Q6_1_4_m.png')
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Sun Sep 26 23:45:50 2021

@author: lukepinkel
"""
import numpy as np

def soft_threshold(x, t):
    s = np.sign(x) * np.maximum(np.abs(x) - t, 0)
    return s

def lasso_obj(beta, X, y, lambda_):
    n = X.shape[0]
    r = y - X.dot(beta)
    f = np.dot(r, r) / (2.0 * n)
    h = lambda_ * np.sum(np.abs(beta))
    return f, h, f+h

def lasso_prox_grad(beta, X, y, Xty, XtX, lambda_, max_eig, n_iters=500,
                           rtol=1e-5, atol=1e-8):
    n = y.shape[0]
    #stepsize in (0, 1/ L] ----> stepsize = midpoint (0+1/L)/2=1/2L
    alpha = 1.0 / (2 * max_eig)
    tau = alpha * lambda_
    fhist = np.array([lasso_obj(beta, X, y, lambda_ )])
    for i in range(n_iters):
        g = (Xty - XtX.dot(beta)) / n
        beta_new = soft_threshold(beta + alpha * g, tau)
        fhist = np.append(fhist, np.array([lasso_obj(beta_new, X, y, lambda_)]), axis=0)
        if fhist[i+1, 2] > fhist[i, 2]:
            break
        dt = np.abs(beta_new - beta)
        bt = np.abs(beta)
        if np.all(dt < bt * rtol) or np.all(dt < bt * rtol + atol):
            beta = beta_new
            break
        else:
            beta = beta_new
    return beta, fhist

def lasso_accelerated_grad(beta, X, y, Xty, XtX, lambda_, max_eig, n_iters=500,
                           rtol=1e-5, atol=1e-8):
    n = y.shape[0]
    alpha = 1.0 / (2 * max_eig)
    tau = alpha * lambda_
    fhist = np.array([lasso_obj(beta, X, y, lambda_ )])
    theta = beta.copy()*0.0
    for i in range(n_iters):
        g = (Xty - XtX.dot(theta)) / n
        beta_new = soft_threshold(theta + alpha * g, tau)
        theta_new = beta_new + (1+i) / (4 + i) * (beta_new - beta)
        fhist = np.append(fhist, np.array([lasso_obj(beta_new, X, y, lambda_)]), axis=0)
        if fhist[i+1, 2] > fhist[i, 2]:
            break
        dt = np.abs(beta_new - beta)
        bt = np.abs(beta)
        if np.all(dt < bt * rtol) or np.all(dt < bt * rtol + atol):
            beta = beta_new
            break
        else:
            beta = beta_new
            theta = theta_new
    return beta, fhist


from autograd import grad
from scipy.optimize import root, fixed_point, newton

machine_eps = 1e-14
tole = 1e-5

maxit = 20

def find_fixpoint(g, fixpoint_cand, fixpoint_res, tolerance=0):
  ptol = "{:1e}".format(tolerance)
  fp = fixpoint_res

  xi = fixpoint_cand
  print(f"Fixed point is at {fp}")
  for i in range(maxit):
    xi = g(xi)
    distance = abs(xi-fp)
    pdis = "{:.4e}".format(distance)
    k = "<" if distance < tolerance else ">"
    pxi = "{:.4e}".format(xi)
    pi = "{:2d}".format(i+1)
    outputstr = \
    f"after iteration {pi}, itres:{pxi} distance from Fixed point:{pdis} {k} tolerance:{ptol}"
    print(outputstr)
    if distance < tolerance:
      break

def NewtonsMethod(f, root_cand, tolerance=0):
  print(f"Newton's Method, initial at {root_cand}, with tolerance {tolerance}")
  df = grad(f)
  g = lambda x: x - (f(x)/df(float(x)))
  find_fixpoint(g, root_cand, (newton(f, root_cand)), tolerance)

def SecantMethod(f, a_1, a_0, fp, tolerance=0):
  ptol = "{:1e}".format(tolerance)
  gx2 = lambda x0, x1 : x1 - (x1 - x0)*(f(x1))/(f(x1) - f(x0))
  g = gx2
  x_i_2 = a_1
  x_i_1 = a_0
  for i in range(maxit):
    xi = g(x_i_2, x_i_1)
    distance = abs(xi-fp)
    pdis = "{:.4e}".format(distance)
    k = "<" if distance < tolerance else ">"
    pxi = "{:.4e}".format(xi)
    pi = "{:2d}".format(i + 1)
    outputstr = \
    f"after iteration {pi}, itres:{pxi} distance from Fixed point:{pdis} {k} tolerance:{ptol}"
    print(outputstr)
    if distance < tolerance:
      break
    x_i_1, x_i_2 = xi, x_i_1

# NewtonsMethod(lambda x: x**3 - 8, 3)
# SecantMethod(lambda x: x**3 - 8, 0, 3, 2)


"""
Module containing simple standard statistics functions
"""
import numpy as np
from scipy.stats.stats import pearsonr

def bias(ref,obs):
    """calculates the bias between two arrays

    Parameters
    ----------
    ref : list
        Reference list
    obs : list
        Comparison list (e.g. Observations)

    Returns
    -------
    bias : float
        Bias from the two lists.
    """
    return np.mean(obs-ref)

def rmse(model,obs):
    """Calculates the Root Mean Square Error

    Parameters
    ----------
    model : list
        List containing model values
    obs : list
        List containing observations (reference)

    Returns
    -------
    rmse : float
        Root mean square error
    """

    N = np.count_nonzero(~np.isnan(obs))
    RMSE = np.sqrt(np.nansum((model-obs)**2)/(N-1))
    return RMSE

def variance(obs):
    """Calculates the variance for a list

    Parameters
    ----------
    obs : list
        list of values (eg. observations)

    Returns
    -------
    var : float
        variance of input list

    """
    return np.var(obs, ddof=1) # ddof = 1 > denominator=n-1

def std(obs):
    """Calculates the standard deviation

    Parameters
    ----------
    obs : list
        List with values (eg. observations)
    Returns
    -------
    std : float
        Standard deviation of input list
    """
    return np.sqrt(variance(obs))

def correlation(x,y):
    """Calculates the pearson correlation coefficient

    Parameters
    ----------
    x : list
        List of values
    y : list
        List of values

    Returns
    -------
    R : float
        Pearson correlation coefficient for input lists
    """
    return pearsonr(x,y)


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

def descend(func, x0, grad, bounds, tol=1e-8, max_iter=100):
    xn = x0
    xnm1 = np.inf
    grad_xn = grad(x0)

    for i in range(max_iter):

        if np.linalg.norm(xn - xnm1) < tol:
            break

        direction = -grad_xn

        xnm1 = xn
        xn = xn + 0.2*direction
        grad_xn = grad(xn)
        yield i, xn, func(xn), grad_xn

def func(x):
    return ((x[0] - 0.5)**2 + (x[1] + 0.5)**2)*np.cos(0.5*x[0]*x[1])

x_r = np.linspace(-1, 1)
y_r = np.linspace(-2, 2)

x, y = np.meshgrid(x_r, y_r)

z = func([x, y])

surf_fig = plt.figure(tight_layout=True)
surf_ax = surf_fig.add_subplot(projection="3d")
surf_ax.tick_params(axis="both", which="major", labelsize=9)
surf_ax.set(xlabel="x", ylabel="y", zlabel="z")
surf_ax.set_title("Objective function")

surf_ax.plot_surface(x, y, z, alpha=0.7)

x0 = np.array([-0.8, 1.3])
surf_ax.plot([x0[0]], [x0[1]], func(x0), "r*")

def grad(x):
    c1 = x[0]**2 - x[0] + x[1]**2 + x[1] + 0.5
    cos_t = np.cos(0.5*x[0]*x[1])
    sin_t = np.sin(0.5*x[0]*x[1])
    return np.array([
        (2*x[0]-1)*cos_t - 0.5*x[1]*c1*sin_t,
        (2*x[1]+1)*cos_t - 0.5*x[0]*c1*sin_t
    ])

cont_fig, cont_ax = plt.subplots()
cont_ax.set(xlabel="x", ylabel="y")
cont_ax.set_title("Contour plot with iterates")
cont_ax.contour(x, y, z, levels=30)

bounds = ((-1, 1), (-2, 2))

xnm1 = x0
for i, xn, fxn, grad_xn in descend(func, x0, grad, bounds):
    cont_ax.plot([xnm1[0], xn[0]], [xnm1[1], xn[1]], "k*--")
    xnm1, grad_xnm1 = xn, grad_xn

print(f"iterations={i}")
print(f"min val at {xn}")
print(f"min func value = {fxn}")

plt.show()


import numpy
from sympy import Rational as frac
from sympy import pi, sqrt

from ..helpers import article, fsd, pm, untangle
from ._helpers import Enr2Scheme

citation = article(
    authors=["A.H. Stroud", "D. Secrest"],
    title="Approximate integration formulas for certain spherically symmetric regions",
    journal="Math. Comp.",
    volume="17",
    year="1963",
    pages="105-135",
    url="https://doi.org/10.1090/S0025-5718-1963-0161473-0",
)

def stroud_secrest_1(n):
    data = [(frac(1, n + 1), sqrt(frac(1, 2)) * _nsimplex(n))]
    points, weights = untangle(data)
    weights *= sqrt(pi) ** n
    return Enr2Scheme("Stroud-Secrest I", n, weights, points, 2, citation)

def stroud_secrest_2(n):
    nu = sqrt(frac(n, 2))
    data = [(frac(1, 2 * n), fsd(n, (nu, 1)))]
    points, weights = untangle(data)
    weights *= sqrt(pi) ** n
    return Enr2Scheme("Stroud-Secrest II", n, weights, points, 3, citation)

def stroud_secrest_3(n):
    nu = sqrt(frac(1, 2))
    data = [(frac(1, 2 ** n), pm(n, nu))]
    points, weights = untangle(data)
    weights *= sqrt(pi) ** n
    return Enr2Scheme("Stroud-Secrest III", n, weights, points, 3, citation)

def stroud_secrest_4(n):
    nu = sqrt(frac(n + 2, 2))
    xi = sqrt(frac(n + 2, 4))
    A = frac(2, n + 2)
    B = frac(4 - n, 2 * (n + 2) ** 2)
    C = frac(1, (n + 2) ** 2)

    data = [(A, numpy.full((1, n), 0)), (B, fsd(n, (nu, 1))), (C, fsd(n, (xi, 2)))]
    points, weights = untangle(data)
    weights *= sqrt(pi) ** n
    return Enr2Scheme("Stroud-Secrest IV", n, weights, points, 5, citation)

def _nsimplex(n):
    # construct the regular n-simplex points with 0 center
    return numpy.array(
        [
            [-sqrt(frac(n + 1, (n + 1 - k) * (n - k))) for k in range(i)]
            + [sqrt(frac((n + 1) * (n - i), n + 1 - i))]
            + (n - i - 1) * [0]
            for i in range(n)
        ]
        + [[-sqrt(frac(n + 1, (n + 1 - i) * (n - i))) for i in range(n)]]
    )


import numpy as np
import math
from .math_lattice import mod2pi

def get_phase_random(N):
    '''
    Uniformly distributed random phase vector - on the interval from 0 to 2pi.
    '''
    phi = 2 * np.pi * np.random.rand(N)
    return phi

def rms(vec):
    '''
    Root mean square; Quadratic averarge
    '''
    return np.sqrt(abs(np.mean(vec * np.conj(vec))))

def cexp_dist(phi1, phi2=0):
    '''
    Distance (normalized by sqrt(ndim), i.e. rms)
    between complex exponents (as vectors in 2N-Euclidean space);
    Equivalent to rms(np.exp(1j * phi1) - np.exp(1j * phi2))
    '''
    return rms(2 * np.sin(0.5 * (phi1 - phi2)))

def cexp(vec):
    '''
    Calculate complex exponent
    '''
    return np.exp(1j * vec)

def phases_to_interval(phi):
    '''
    Take phase vector.
    Return equivalent phase vector with
    phases in the +-2pi interval, centered at the mean of the initial phase vector (mean unchanged)

    Tested  - doesn't break.
    Problem: one vector has more than 1 representation in that interval
    '''
    x = np.array(phi)
    xmin = x.mean() - 2 * np.pi
    xmax = x.mean() + 2 * np.pi
    flag = (x.max() > xmax) or (x.min() < xmin)
    while flag:
        imax = x.argmax()
        imin = x.argmin()
        x[imax] -= 2 * np.pi
        x[imin] += 2 * np.pi

        flag = (x.max() > xmax) or (x.min() < xmin)

    return x

## Global phase
### Circular Average
from scipy.stats import circmean, circstd, circvar

def circ_dist(phi, phi0=0, axis=None):
    '''
    If two phase vectors age given, calculate for dphi=phi-phi0
    Without axis option is equivalent to math.sqrt(- 2 * math.log(abs(np.exp(1j *(phi0 - phi)).mean())))
    '''
    return circstd(phi - phi0, axis=axis)

def complex_exp_mean(phi, phi0=0):
    '''
    Mean of complex exponents of array of phases phi, relative to phi0
    '''
    return np.mean(np.exp(1j * (phi - phi0)))

def define_circular_mean_phase(phi0):
    '''
    :return: function which computes circular mean phase of phi, relative to phi0.
    '''

    def get_phi_global(phi):  # equivalent up to mod 2pi to np.angle(np.mean(np.exp(1j * (phi - phi0))))
        return circmean(phi - phi0)

    return get_phi_global

def order_parameter(phi, phi0=0):
    return abs(complex_exp_mean(phi, phi0))

## Centralized phase difference
def phase_diffs_centralized(phis, phi0):
    '''
    :param phis: list of phase vectors
    :param phi0: phase vector to subtract
    :return: list of phase-vector differences; centralized,
            i.e. circular mean of the phase vector differences equals to zero
            [For movies of phase deviations]
    '''
    dphis = (phis - phi0) % (2  *np.pi)
    for i, dphi in enumerate(dphis): # Centralzie
        dphis[i] -= circmean(dphi)
    return dphis

### Mean phase
def get_mean_phase(phi):  # keep for compatibility
    return np.mean(phi)

def mean_phase(phi):
    return np.mean(phi)

if __name__ == '__main__':
    ## Test RMS
    a = np.array([1, 1])
    print(rms(a))
    print(phases_to_interval(a))
    print(get_phase_random(3))
    ## Test Logger:
    #   logger = setup_logging('a.log')
    # #  logger = setup_logging()
    #
    #   logging.info("THIS IS INFO!!!")
    #   logging.warning("THIS IS WARNING!")
    #   logging.debug("THIS IS A DEBUG MESSAGE! IT SHOULDN't BE DISPLAYED!!")
    #   logger.info("Can also log this")


import numpy as np

def pmi(topic_words, word_frequency, word_frequency_in_documents, n_docs, normalise=False):
    """PMI/NPMI topic quality metric for a topic.

    Calculates the PMI/NPMI topic quality metric for one individual topic based on the topic words.

    Args:
        topic_words: list
            Words that compose one individual topic.
        word_frequency: dict
            Frequency of each word in corpus.
        word_frequency_in_documents: dict
            Frequency of each word for each document in corpus.
        n_docs: int
            Number of documents in the corpus.
        normalise: bool, default=False
            Where to normalise (NPMI) or not (PMI).

    Returns:
        pmi: float
            Resultant PMI metric value for the topic.
        npmi: float
            Resultant NPMI metric value for the topic.
    """
    n_top = len(topic_words)
    pmi = 0.0
    npmi = 0.0

    for j in range(1, n_top):
        for i in range(0, j):
            ti = topic_words[i]
            tj = topic_words[j]

            c_i = word_frequency[ti]
            c_j = word_frequency[tj]
            c_i_and_j = len(word_frequency_in_documents[ti].intersection(word_frequency_in_documents[tj]))

            dividend = (c_i_and_j + 1.0) / float(n_docs)
            divisor = ((c_i * c_j) / float(n_docs) ** 2)
            pmi += np.log(dividend / divisor)

            npmi += -1.0 * np.log((c_i_and_j + 0.01) / float(n_docs))

    npmi = pmi / npmi

    if normalise:
        return npmi
    else:
        return pmi


#!/usr/bin/env python
"""
2d Semicircular canyon mesh
"""
import os
import math
import numpy as np
import matplotlib.pyplot as plt

# parameters
L = 11.0
r0 = 1.0
n1, n2 = 301, 321

# step sizes
n = (n2 - 1) / 2
dy0 = 0.5 * math.pi * r0 / (n2 - 1)
dy1 = L / n

# semicircle canyon
f = np.linspace(0.0, 0.5 * math.pi, n2)
x = np.empty([n1, n2])
y = np.empty([n1, n2])
x[0, :] = np.cos(f) * r0
y[0, :] = np.sin(f) * r0

# outer edge
x[-1, :] = L
x[-1, -n:] = np.arange(n)[::-1] * dy1
y[-1, :] = L
y[-1, :n] = np.arange(n) * dy1

# blend
w = np.cumsum(np.linspace(2.0 * dy0, dy1, n1 - 1))
w = w / w[-1]
for i in range(1, n1-1):
    x[i, :] = (1.0 - w[i-1]) * x[0, :] + w[i-1] * x[-1, :]
    y[i, :] = (1.0 - w[i-1]) * y[0, :] + w[i-1] * y[-1, :]

# print mesh properties
vp = 2.0
dy = y[0, 1] - y[0, 0]
dt = dy * 1.5 / vp / math.sqrt(3.0)
print('shape = ', (n1, n2))
print('nt > ', L / vp / dt)
print('dt < ', dt)
print('L = ', L)
print('L / n = ', L / n)
print('dx00 = ', (x[1, 0] - x[0, 0],     y[0, 1] - y[0, 0]))
print('dx01 = ', (x[0, -2] - x[0, -1],   y[1, -1] - y[0, -1]))
print('dx10 = ', (x[-1, 0] - x[-2, 0],   y[-1, 1] - y[-1, 0]))
print('dx11 = ', (x[-1, -2] - x[-1, -1], y[-1, -1] - y[-2, -1]))

# write files
p = 'repo/Canyon'
os.mkdir(p)
os.chdir(p)
x.T.astype('f').tofile('x.bin')
y.T.astype('f').tofile('y.bin')

# plot
fig = plt.gcf()
fig.clf()
ax = fig.add_subplot(111)
d = 10
x, y = x[::d, ::d], y[::d, ::d]
ax.plot(x, y, 'k-')
ax.plot(-x, y, 'k-')
ax.plot(x.T, y.T, 'k-')
ax.plot(-x.T, y.T, 'k-')
ax.axis('scaled')
ax.axis([-2, 2, 2, -0.2])
fig.savefig('Canyon-Mesh.svg')
fig.show()


# -*- coding: utf-8 -*-
import numpy as np
import numpy.matlib as mt
import matplotlib.pyplot as plt

M = 2
N = 500
dt = 1.0 / 30.
sigma_measure = 0.1
# x = 1/2 a t**2
# assume x = 0.5 in 0.5, i.e. head turns half maximal movement range in 0.5 sec.
# so a = 2 x / t**2 =  4.0
sigma_accel   = 4.0

x = np.zeros(N, dtype = np.float)
x[N/3:] = 1.0 # step input
measurement = x.copy()
measurement += np.random.normal(0., sigma_measure, N)

A = np.matrix([
    [ 1. , dt ],
    [ 0. , 1. ]
])

R = np.matrix([[ sigma_measure**2 ]])

dv = sigma_accel * dt
dp = sigma_accel * 0.5 * dt * dt
Q = np.matrix([
    [ dp*dp, dp*dv ],
    [ dv*dp, dv*dv ]
])

H = np.matrix([
    [ 1., 0. ],
])

I = mt.identity(M, dtype = np.float)

def arrayOfMatrices(n, shape):
    return np.asarray([mt.zeros(shape, dtype = np.float) for i in xrange(n)])

# Base on the scipy-cookbook http://scipy-cookbook.readthedocs.io/items/KalmanFiltering.html
sz_state = (M, 1)
sz_cov   = (M, M)
sz_K     = (M, 1)
xhat= arrayOfMatrices(N, sz_state)      # a posteri estimate of x
P=arrayOfMatrices(N, sz_cov)         # a posteri error estimate
xhatminus=arrayOfMatrices(N, sz_state) # a priori estimate of x
Pminus=arrayOfMatrices(N, sz_cov)    # a priori error estimate
K=arrayOfMatrices(N, sz_K)         # gain or blending factor

P[0] = mt.ones((M,M)) * 100
xhat[0] = measurement[0]

for k in range(1,N):
    # time update
    xhatminus[k] = A * xhat[k-1]
    Pminus[k]    = A * P[k-1] * A.T + Q

    # measurement update
    K[k] = Pminus[k] * H.T * np.linalg.inv( H * Pminus[k] * H.T + R )
    xhat[k] = xhatminus[k] + K[k] * (measurement[k] - H * xhatminus[k])
    P[k] = ( I - K[k]*H ) * Pminus[k]

t = np.arange(N) * dt
plt.figure()
plt.subplot(2,1,1)
plt.plot(t, measurement,'k+',label='noisy measurements')
plt.plot(t, xhat[:,0,0],'b-',label='position estimate')
plt.plot(t, x, 'r-', label='ground truth')

plt.subplot(2,1,2)
plt.plot(t, xhat[:,1,0],'g-',label='velocity estimate')

plt.show()


"""
Code to test fitting a line to some points, including the 95% confidence
interval on both the mean and the slope
"""

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

# make some data to fit
N = 50
x = np.linspace(0,1,N)
y_0 = 5 # y-intercept
dydx = 1 # slope
y_std = .1
y_noise = y_std * np.random.randn(N)
# original points
y = dydx*x + y_0 + y_noise

# Fit the line, following Emery and Thomson (1998) 3.12.4
#
# first make the input arrays
Y = y.reshape(N,1)
X = np.concatenate( (np.ones((N,1)), x.reshape(N,1)), axis=1)
# then do the fit
B = np.linalg.inv(X.T @ X) @ (X.T @ Y) # @ is matrix multiplication
# and unpask the result
yy_0 = B[0,0] # y-intercept
dyydx = B[1,0] # slope
# fit line
yy = dyydx*x + yy_0

# Calculate the 95% confidence interval for the mean and trend
# NOTE the call to the inverse cumulative Student's t-distribution:
# stats.t.ppf(1-.025,100) which is identical to MATLAB tinv(1-.025,100).
ci_pct = 95 # Confidence interval %
ci_fac = (1 - ci_pct/100)/2 # = 0.025 for ci_pct = 95
s_eps = np.sqrt(((y-yy)**2).sum()/(N-2)) # standard error of the estimate
s_x = x.std(ddof=1)
s_y = y.std(ddof=1)
# Condidence interval on mean: Emery and Thomson (3.8.6)
ci_mean = s_y * stats.t.ppf(1-ci_fac,N-1) / np.sqrt(N)
# Confidence interval on slope: Emery and Thomson (3.15.12b) w/typo corrected
ci_trend = s_eps * stats.t.ppf(1-ci_fac,N-2) / np.sqrt((N-1)*s_x*s_x)

# Alternatively, fit the line using python tools
BB = np.polyfit(x,y,1, full=True)
dyydx_alt = BB[0] # slope
yy0_alt = BB[1] # y-intercept
# RESULT: these are identical to the y-intercept and slope
# that I calculate from scratch above.

# plotting
fs = 14
plt.close('all')
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111)
ax.plot(x, y, '*b', label='Data')
ax.plot(x, yy, '-r', label='Fit Line')
lh = ax.legend(loc='lower right')
plt.setp(ax.get_legend().get_texts(), fontsize=fs)
ax.text(.05, .9, '$Mean = %0.5f \pm %0.4f$' % (y.mean(), ci_mean),
    transform=ax.transAxes, size=fs)
ax.text(.05, .85, '$Slope = %0.5f \pm %0.4f$' % (dyydx, ci_trend),
    transform=ax.transAxes, size=fs)
ax.text(.05, .8, '95% Confidence Intervals',
    transform=ax.transAxes, size=fs)
ax.set_title('N = %d' % (N), size=fs+2)
plt.show()


import numpy
import anmichelpers.tools.tools as tools
import math
from anmichelpers.tools.tools import norm
from math import exp

# For all measures see http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2350220/

def overlap(mode_i, mode_j):
    """
    Measure of the similarity between 2 modes.
    Overlap value is in the range [0,1] (1 means maximum overlap / similarity)
    Tama and Sanejouand 2001. Conformational change of proteins arising from normal mode calculations.
    """
    return numpy.abs(numpy.dot(mode_i, mode_j)) / (tools.norm(mode_i) * tools.norm(mode_j))
#     return numpy.abs(numpy.dot(mode_i/tools.norm(mode_i), mode_j/tools.norm(mode_j))) / (tools.norm(mode_i) * tools.norm(mode_j))

def cumulative_overlap(mode, mode_range):
    """
    Measure of similarity between one mode and a range of modes.

    A value of 1 means a perfect match.
    """
    cum_overlap = 0
    for i in range(len(mode_range)):
        o = overlap(mode, mode_range[i])
        cum_overlap += o*o
    return math.sqrt(cum_overlap)

def rmsip(first_mode_range, second_mode_range):
    """
    Root mean square inner product.
    Indicates how well the motion space spanned by the first range of modes is represented by the
    second range of modes.

    "An Analysis of Core Deformations in Protein Superfamilies" Alejandra Leo-Macias,
    Pedro Lopez-Romero, Dmitry Lupyan, Daniel Zerbino and Angel R. Ortiz
    Biophys J. 2005 Feb; 88(2): 1291-1299.

    Must vary between 0 and 1
    http://www.biomedcentral.com/1471-2105/15/399
    Also:  http://www.biomedcentral.com/1471-2105/14/183
    """
    D = len(first_mode_range)
    K = len(second_mode_range)
    rmsip_val = 0.
    for i in range(D):
        for j in range(K):
            ovp = overlap(first_mode_range[i], second_mode_range[j])
            rmsip_val += ovp*ovp
    return math.sqrt(rmsip_val / float(D))

def degree_of_collectivity(mode, normalize = False):
    """
    http://peds.oxfordjournals.org/content/14/1/1.long
    Tama and Sanejouand 2001. Conformational change of proteins arising from normal mode calculations.

    From th article:
    A measure of how collective a protein motion is was proposed by Bruschweiler (1995). In the present study, it was used in order
    to estimate the degree of collectivity of each conformational change considered, reflecting the number of atoms which are
    significantly affected during the conformational change. This degree of collectivity, k, is defined as being proportional to
    the exponential of the 'information entropy' embedded in vector inc_R.
    It is confined to the interval between N^-1 and 1. If k = 1, the conformational change is maximally collective.

    Bruschweiler (1995): http://scitation.aip.org/content/aip/journal/jcp/102/8/10.1063/1.469213

    \kappa = \frac{1}{N} exp \left( -\sum^N_i \alpha \Delta R_i^2 log \left( \alpha \Delta R_i^2 \right) \right)
    """

    # Calculate "displacements of the mode"
    R = numpy.reshape(mode,(len(mode)/3,3))
    N = len(R)
#     print "Degree of collectivity. Max. value: 1 Min. value: ",1./N
    inc_R = norm(R)
    # Calculate alpha
    sum_ri_sq = numpy.dot(inc_R, inc_R)
    alpha = 1./sum_ri_sq
    # Calculate the degree of collectivity
    alpha_ri2 = alpha*(inc_R*inc_R)
    log_alpha_ri2 = numpy.log(alpha_ri2)
    k = (1./N)*exp(-(alpha_ri2*log_alpha_ri2).sum())
    if not normalize:
        return k
    else:
        min_val = 1./N
        return  (k-min_val) / (1-min_val)

# a = [1,2,3,4,5,6] -> degree_of_collectivity = 0.76810859305 (range: 1, 0.5)
# normed -> 0.5362171861


import numpy as np
from scipy.stats import binom

def ExCeeD(train_scores, test_scores, prediction, contamination):

    """
    Estimate the example-wise confidence according to the model ExCeeD provided in the paper.
    First, this method estimates the outlier probability through a Bayesian approach.
    Second, it computes the example-wise confidence by simulating to draw n other examples from the population.
    Parameters
    ----------
    train_scores   : list of shape (n_train,) containing the anomaly scores of the training set (by selected model).
    test_scores    : list of shape (n_test,) containing the anomaly scores of the test set (by selected model).
    prediction     : list of shape (n_test,) assuming 1 if the example has been classified as anomaly, 0 as normal.
    contamination  : float regarding the expected proportion of anomalies in the training set. It is the contamination factor.
    Returns
    ----------
    exWise_conf    : np.array of shape (n_test,) with the example-wise confidence for all the examples in the test set.

    """

    n = len(train_scores)
    n_anom = np.int(n*contamination) #expected anomalies

    count_instances = np.vectorize(lambda x: np.count_nonzero(train_scores <= x))
    n_instances = count_instances(test_scores)

    prob_func = np.vectorize(lambda x: (1+x)/(2+n))
    posterior_prob = prob_func(n_instances) #Outlier probability according to ExCeeD

    conf_func = np.vectorize(lambda p: 1 - binom.cdf(n - n_anom, n, p))
    exWise_conf = conf_func(posterior_prob)
    np.place(exWise_conf, prediction == 0, 1 - exWise_conf[prediction == 0]) # if the example is classified as normal,
                                                                             # use 1 - confidence.

    return posterior_prob, exWise_conf


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

def get_train_data():
    raw_data = np.genfromtxt('train.dat')
    train_x, train_y = raw_data[:, :-1], raw_data[:, -1]
    return train_x, train_y

def get_test_data():
    raw_data = np.genfromtxt('test.dat')
    test_x, test_y = raw_data[:, :-1], raw_data[:, -1]
    return test_x, test_y

def get_distance_matrix(train_data, test_data):
    # data shape: (data_num, feature_dim)
    inner = test_data @ (train_data.T) # (test_size, train_size)
    train_squre = np.diag(train_data @ (train_data.T))
    test_squre = np.diag(test_data @ (test_data.T))
    distance = inner * (-2) + train_squre.reshape(1, -1) + test_squre.reshape(-1, 1)
    return distance

def get_prediction(distance_matrix, y, k= 1):
    # distance_matrix: (test_size, train_size)
    # y : (train_size, )
    test_size = distance_matrix.shape[0]
    score = np.zeros(test_size)
    for i in range(test_size):
        nearest = np.argsort(distance_matrix[i])[:k]
        score[i] = np.sum(y[nearest])
    prediction = np.sign(score)
    return prediction

def get_uniform_prediction(distance_matrix, y, gamma):
    # distance_matrix: (test_size, train_size)
    # y : (train_size, )
    matrix = np.exp((- gamma) * distance_matrix)
    score = matrix @ y
    prediction = np.sign(score)
    return prediction

def get_error_rate(prediction, gt):
    wrong_num = np.sum(prediction != gt)
    return wrong_num / len(gt)

def problem_11():
    k_list = [1, 3, 5, 7, 9]
    train_x, train_y = get_train_data()
    distance = get_distance_matrix(train_x, train_x)
    Ein_list = []
    for k in k_list:
        prediction = get_prediction(distance, train_y, k)
        ein = get_error_rate(prediction, train_y)
        Ein_list.append(ein)

    plt.title('Problem 11')
    plt.xlabel('k value')
    plt.ylabel('Ein rate')
    plt.scatter(k_list, Ein_list)
    plt.savefig('img/problem_11.png')

def problem_12():
    k_list = [1, 3, 5, 7, 9]
    train_x, train_y = get_train_data()
    test_x, test_y = get_test_data()
    distance = get_distance_matrix(train_x, test_x)
    Eout_list = []
    for k in k_list:
        prediction = get_prediction(distance, train_y, k)
        eout = get_error_rate(prediction, test_y)
        Eout_list.append(eout)

    plt.title('Problem 12')
    plt.xlabel('k value')
    plt.ylabel('Eout rate')
    plt.scatter(k_list, Eout_list)
    plt.savefig('img/problem_12.png')

def problem_13():
    gamma_list = [0.001, 0.1, 1, 10, 100]
    train_x, train_y = get_train_data()
    distance = get_distance_matrix(train_x, train_x)
    Ein_list = []
    for gamma in gamma_list:
        prediction = get_uniform_prediction(distance, train_y, gamma)
        ein = get_error_rate(prediction, train_y)
        Ein_list.append(ein)

    plt.title('Problem 13')
    plt.xlabel('gamma')
    plt.ylabel('Ein')
    gamma_list = [str(i) for i in gamma_list]
    plt.scatter(gamma_list, Ein_list)
    plt.savefig('img/problem_13.png')

def problem_14():
    gamma_list = [0.001, 0.1, 1, 10, 100]
    train_x, train_y = get_train_data()
    test_x, test_y = get_test_data()
    distance = get_distance_matrix(train_x, test_x)
    Eout_list = []
    for gamma in gamma_list:
        prediction = get_uniform_prediction(distance, train_y, gamma)
        eout = get_error_rate(prediction, test_y)
        Eout_list.append(eout)

    plt.title('Problem 14')
    plt.xlabel('gamma')
    plt.ylabel('Eout')
    gamma_list = [str(i) for i in gamma_list]
    plt.scatter(gamma_list, Eout_list)
    plt.savefig('img/problem_14.png')

def test():
    train_x, train_y = get_train_data()
    print(train_x.shape[0])
    test_x, test_y = get_test_data()
    print(test_x.shape[0])

if __name__ == '__main__':
    mode = sys.argv[1]
    if mode == 'test':
        test()
    elif mode == '11':
        problem_11()
    elif mode == '12':
        problem_12()
    elif mode == '13':
        problem_13()
    elif mode == '14':
        problem_14()
    else:
        print('Error: Wrong argument !')