import numpy as np

def l2_regularization(W, reg_strength):
    loss = reg_strength * np.sum(pow(W, 2))
    grad = 2 * reg_strength * W
    return loss, grad

def softmax(predictions):
    copy_predictions = np.copy(predictions)
    if predictions.ndim == 1:
        copy_predictions -= np.max(predictions) # numerical stability
        probs = np.exp(copy_predictions)/np.sum(np.exp(copy_predictions))
    else:
        copy_predictions -= np.amax(predictions, axis=1, keepdims=True) # numerical stability
        probs = np.exp(copy_predictions)/np.sum(np.exp(copy_predictions), axis=1, keepdims=True)

    return probs

def cross_entropy_loss(probs, target_index):
    if probs.ndim == 1:
        loss = -np.log(probs[target_index])
    else:
        batch_size = probs.shape[0]
        batch_loss = -np.log(probs[np.arange(batch_size), target_index.flatten()]) # return a copy of the array collapsed into one dimension
        loss = np.sum(batch_loss)/batch_size
    return loss

def softmax_with_cross_entropy(predictions, target_index):
    dprediction = softmax(predictions)
    loss = cross_entropy_loss(dprediction, target_index)

    if predictions.ndim == 1:
        dprediction[target_index] -= 1
    else:
        batch_size = predictions.shape[0]
        dprediction[np.arange(batch_size), target_index.flatten()] -= 1
        dprediction /= batch_size

    return loss, dprediction

class Param:
    def __init__(self, value):
        self.value = value
        self.grad = np.zeros_like(value)

class ReLULayer:
    def __init__(self):
        pass

    def forward(self, X):
        self.X = X
        return X * (X > 0)

    def backward(self, d_out):
        d_result = d_out * (self.X > 0)
        return d_result

    def params(self):
        # ReLU Doesn't have any parameters
        return {}

class FullyConnectedLayer:
    def __init__(self, n_input, n_output):
        self.W = Param(0.001 * np.random.randn(n_input, n_output))
        self.B = Param(0.001 * np.random.randn(1, n_output))
        self.X = None

    def forward(self, X):
        self.X = X
        return np.dot(self.X, self.W.value) + self.B.value

    def backward(self, d_out):
        self.W.grad = np.dot(self.X.T, d_out)
        self.B.grad = np.sum(d_out, axis = 0, keepdims = True)
        d_input = np.dot(d_out, self.W.value.T)
        return d_input

    def params(self):
        return {'W': self.W, 'B': self.B}


import numpy as np

def compute_r_squared(data, predictions):
    # Write a function that, given two input numpy arrays, 'data', and 'predictions,'
    # returns the coefficient of determination, R^2, for the model that produced
    # predictions.
    #
    # Numpy has a couple of functions -- np.mean() and np.sum() --
    # that you might find useful, but you don't have to use them.

    # YOUR CODE GOES HERE
    mean = data.mean()
    numerator = np.sum((data - predictions)**2)
    denominator = np.sum((data - mean)**2)
    quotient = numerator/denominator
    r_squared = 1 - quotient
    return r_squared


# 2.4 통계 기반 기법 개선하기

# 2.4.1 상호정보량
# '발생(고빈도 단어)' 횟수라는 특징은 좋은 특징은 아님
'''
    점별 상호정보량(PMI, Pointwise Mutual Information)
    PMI(x, y) = log2(P(x, y) / (P(x)P(y))
    P(x): x가 일어날 확률
    P(y): y가 일어날 확률
    P(x, y): x와 y가 동시에 일어날 확률
    문제점: 두 단어의 동시발생 횟수가 0이면 (-)무한대가 된다
'''
'''
    점별 상호정보량의 문제를 해결하기 위해 양의 상호정보량(PPMI, Positive PMI)를 사용함
    PPMI(x, y) = max(0, PMI(x, y))
    PMI가 음수일 경우 0으로 취급함
'''

# 동시발생 행렬을 PPMI 행렬로 변환하는 함수
# common.util.ppmi()
import numpy as np
from common.util import preprocess, create_co_matrix, cos_similarity, ppmi

text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
vocab_size = len(word_to_id)
C = create_co_matrix(corpus, vocab_size)
W = ppmi(C)

np.set_printoptions(precision=3)    # 유효 자릿수를 세 자리로 표시
print('동시 발생 행렬')
print(C)
print('-' * 50)
print('PPMI')
print(W)

'''
    이 헹렬의 원소 대부분은 0이다.
    즉, 벡터의 원소 대부분 '중요도'가 낮다는 뜻이다.
    이런 벡터는 노이즈에 약하고 견고하지 못하다는 단점이 있다.
    이 문제를 대체하고자 수행되는 기법이 '벡터의 차원 감소' 이다.
'''

# 2.4.2 차원 감소
# 차원 감소(dimensionality redution)는 '중요한 정보'는 최대한 유지하면서 벡터의 차원을 줄이는 방법이다.
'''
    특잇값분해(SVD, Singular Value Decomposition)
    SVD는 임의의 행렬을 세 행렬의 곱으로 분해한다.
    X = U * S * V.T
    U, V: 직교행렬(orthogonal matrix)이고, 그 열벡터를 서로 직교한다.
    S: 대각행렬(diagonal matrix, 대각성분 외에는 모두 0인 행렬)이다.
    U 행렬은 '단어공간', S 행렬은 '특잇값'이 큰 순서로 나열한다.
'''

# 2.4.3 SVD에 의한 차원 감소
# import sys
# sys.path.appen('..')
import numpy as np
import matplotlib.pyplot as plt
from common.util import preprocess, create_co_matrix, ppmi

text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
vocab_size = len(word_to_id)
C = create_co_matrix(corpus, vocab_size,window_size=1)
W = ppmi(C)

# SVD
U, S, V = np.linalg.svd(W)

print(C[0])     # 동시발생 행렬
print(W[0])     # PPMI 행렬
print(U[0])     # SVD
print(U[0, :2])

for word, word_id in word_to_id.items():
    plt.annotate(word, (U[word_id, 0], U[word_id, 1]))

plt.scatter(U[:, 0], U[:, 1], alpha=0.5)
plt.show()

# 2.4.4 PTB 데이터셋
# 펜 트리뱅크(PTB, Penn Treebank): 주어진 기법의 품질을 측정하는 벤치마크로 자주 이용된다.
# 펜 트리뱅크를 사용하는 코드
# import sys
# sys.path.append('..')
from dataset import ptb

corpus, word_to_id, id_to_word = ptb.load_data('train')

print('말뭉치 크기:', len(corpus))
print('corpus[:30]:', corpus[:30])
print()
print('id_to_word[0]:', id_to_word[0])
print('id_to_word[1]:', id_to_word[1])
print('id_to_word[2]:', id_to_word[2])
print()
print("word_to_id['car']:", word_to_id['car'])
print("word_to_id['happy']:", word_to_id['happy'])
print("word_to_id['lexus']:", word_to_id['lexus'])

# 2.4.5 PTB 데이터셋 평가
# PTB 데이터셋 평가 코드
# import sys
# sys.path.append('..')
import numpy as np
from common.util import most_similar, create_co_matrix, ppmi
from dataset import ptb

window_size = 2
wordvec_size = 100

corpus, word_to_id, id_to_word = ptb.load_data('train')
vocab_size = len(word_to_id)
print('동시 발생 수 계산 ...')
C = create_co_matrix(corpus, vocab_size, window_size)
print('PPMI 계산 ...')
W = ppmi(C, verbose=True)

print('SVD 계산 ...')
try:
    # truncated SVD (빠르다!)
    from sklearn.utils.extmath import randomized_svd
    U, S, V = randomized_svd(W, n_components=wordvec_size, n_iter=5, random_state=None)
except ImportError:
    # SVD (느리다)
    U, S, V = np.linalg.svd(W)

word_vecs = U[:, :wordvec_size]

querys = ['you', 'year', 'car', 'toyota']
for query in querys:
    most_similar(query, word_to_id, id_to_word, word_vecs, top=5)


# Starting in the top left corner of a 2×2 grid, and only being able to move to the right and down, there are exactly 6 routes to the bottom right corner.

# How many such routes are there through a 20×20 grid?

import numpy as np
np.set_printoptions(linewidth=240)

# size is the number of complete square on one dimension
# for example a size of 2 would consist of 3x3 dots to make a total of 4 squares
size = 20
z = np.zeros((size+1,size+1), dtype=np.int64)
z[0,:] = 1
z[:,0] = 1

for i in range(1,size+1):
    for j in range(1,size+1):
        z[i,j] = z[i-1,j] + z[i,j-1]
print(np.rot90(np.rot90(z)))
print(z[size,size])


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

class MeanVarOptManager:
    def __init__(self):
        self.backtesting = dict()

    def simulation_portfolio(self, tickers, ticker_names, num_try=10):
        port_returns = []
        port_volatility = []
        sharpe_ratio = []
        stock_weights = []

        portfolio = {'returns': port_returns,
                     'volatility': port_volatility,
                     'sharpe_ratio': sharpe_ratio}

        num_of_tickers = len(ticker_names)

        for i in range(num_try):
            weights = self.get_randomized_weights(num_of_tickers)
            portfolio_summary = self.evaluate_portfolio(tickers, weights)

            port_returns.append(portfolio_summary['return'])
            port_volatility.append(portfolio_summary['volatility'])
            sharpe_ratio.append(portfolio_summary['sharpe'])
            stock_weights.append(weights)

        for idx, symbol in enumerate(ticker_names):
            portfolio[symbol + ' weight'] = [weights[idx] for weights in stock_weights]

        column_order = ['returns', 'volatility', 'sharpe_ratio'] + [stock+' weight' for stock in ticker_names]
        portfolio_df = pd.DataFrame(portfolio)
        return portfolio_df[column_order]

    def evaluate_portfolio(self, tickers, weights, display=False):
        sample_data_returns = tickers.pct_change()

        # variance
        portfolio_variance = self.get_variance(sample_data_returns, weights)

        # standard deviation
        portfolio_volatility = np.sqrt(portfolio_variance)

        # Expected return
        expected_return = self.get_annual_returns(sample_data_returns, weights)
        sharp_ratio = expected_return / portfolio_volatility

        if display:
            print('Variance of Portfolio', str(round(portfolio_variance, 4) * 100) + '%')
            print('Variance of Risk', str(round(portfolio_volatility, 4) * 100) + '%')

            sample_data_returns.plot(figsize=(20,14))

        portfolio_dic = dict()
        selected_tickers_name = [ticker.split('.')[0] for ticker in tickers.columns]

        portfolio_dic['tickers'] = selected_tickers_name
        portfolio_dic['weights'] = weights
        portfolio_dic['volatility'] = portfolio_volatility
        portfolio_dic['return'] = expected_return
        portfolio_dic['sharpe'] = sharp_ratio

        return portfolio_dic

    def get_simulation_summary(self, portfolio_df: pd.DataFrame, ticker_names, display=False):
        min_volatility = portfolio_df['volatility'].min()
        max_sharpe = portfolio_df['sharpe_ratio'].max()

        sharpe_portfolio = portfolio_df.loc[portfolio_df['sharpe_ratio'] == max_sharpe]
        min_variance_port = portfolio_df.loc[portfolio_df['volatility'] == min_volatility]

        if display:
            # use the min, max values to locate and create the two special portfolios

            plt.style.use('seaborn-dark')
            portfolio_df.plot.scatter(x='volatility', y='returns', c='sharpe_ratio',
                                      cmap='RdYlGn', edgecolors='black', figsize = (10, 8), grid=True)
            plt.scatter(x=sharpe_portfolio['volatility'],
                        y=sharpe_portfolio['returns'],
                        c='red', marker='D', s=200)
            plt.scatter(x=min_variance_port['volatility'],
                        y=min_variance_port['returns'],
                        c='blue', marker='D', s=200)

            plt.xlabel('Volatility (Std. Deviation)')
            plt.ylabel('Expected Returns')
            plt.title('Efficient Frontier')
            plt.show()

        simulation_result = {
            'min_volatility_portfolio': min_variance_port.T,
            'max_sharpe_portfolio': sharpe_portfolio.T,
            'min_volatility': min_volatility,
            'max_sharpe': max_sharpe,
            'tickers': ticker_names
        }

        return simulation_result

    def get_ticker_names(self, tickers):
      return [ticker.split('.')[0] for ticker in tickers.columns]

    def get_randomized_weights(self, size):
        randomized_values = np.random.random([size])
        return randomized_values / sum(randomized_values)

    def get_sample_selected_ticker(self, stock_prices: pd.DataFrame, num_of_tickers=None):
        if not num_of_tickers:
            num_of_tickers = len(stock_prices.columns)

        selected_tickers = np.random.permutation(stock_prices.columns)[:num_of_tickers]
        selected_data = stock_prices[selected_tickers]
        exist_range =\
            selected_data.notnull().as_matrix().astype(int).mean(axis=1) == 1
        exist_percent = sum(exist_range)/len(exist_range)

        return selected_data[exist_range]

    def get_variance(self, sample_data_returns, weights):
        cov_matrix_portfolio = sample_data_returns.cov() * 250
        variance = np.dot(weights.T, np.dot(cov_matrix_portfolio, weights))
        return variance

    def get_annual_returns(self, sample_data_returns, weights):
        annual_returns = sample_data_returns.mean() * 250
        expected_return = np.sum(annual_returns * weights)
        return expected_return

if __name__ == '__main__':
    import pprint
    pp = pprint.PrettyPrinter(indent=4)
    mvo_manager = MeanVarOptManager()
    date_rng = pd.date_range(start='1/1/2018', end='1/08/2018')
    sample_data = {'stockA': [3,4,5,], 'stockB': [4,5,6], 'stockC': [1,7,5]}
    sample_stock_price_df = pd.DataFrame(sample_data, index=date_rng[:3])
    for i in range(5):
        selected_ticker = mvo_manager.get_sample_selected_ticker(stock_prices=sample_stock_price_df, num_of_tickers=2)
        # print(selected_ticker)
        ticker_names = mvo_manager.get_ticker_names(tickers=selected_ticker)
        portfolio_df = mvo_manager.simulation_portfolio(selected_ticker, ticker_names=ticker_names, num_try=5)
        res = mvo_manager.get_simulation_summary(portfolio_df=portfolio_df, ticker_names=ticker_names)

        print("min_volatility_portfolio")
        pp.pprint(res['min_volatility_portfolio'])
        print()
        print("max_sharpe_portfolio")
        pp.pprint(res['max_sharpe_portfolio'])
        print('-' * 50)


# -*- coding: utf-8 -*-
"""
Created on 2021/02/11 15:51

@Project -> mi_lombardi.py

@Author: luolei

@Email: dreisteine262@163.com

@Describe: 基于KNN的经典信息熵估计器
"""

__doc__ = """
    参考文献: D. Lombardi, S. Pant: A Non-Parametric K-Nearest Neighbor Entropy Estimator, \
        Physical Review E., 2016.
"""

from sklearn.neighbors import NearestNeighbors
from scipy.special import gamma, psi
import numpy as np

def _nearest_distances(x, k: int, metric: str):
    """Returns the distance to the kth nearest neighbor for every point in X"""
    knn = NearestNeighbors(n_neighbors=k, metric=metric)
    knn.fit(x)
    d, _ = knn.kneighbors(x)
    return d[:, -1]

def _entropy(x: np.ndarray, k: int, metric: str):
    """
    根据X的样本计算对应的信息熵

    Reference:
    1. Kozachenko, Leonenko:
    """
    try:
        N, D = x.shape
    except:
        raise ValueError('X应为二维数组')
    r = _nearest_distances(x, k, metric)
    volume_unit_ball = (np.pi ** (.5 * D)) / gamma(.5 * D + 1)  # 高维空间单位球体积
    return D * np.mean(np.log2(r + np.finfo(x.dtype).eps)) + np.log2(volume_unit_ball) + psi(N) - psi(k)

class KLEstimator(object):
    """
    基于KNN的经典信息熵估计器, 又名KL估计器

    Reference:
    1. D. Lombardi, S. Pant: "A Non-Parametric K-Nearest Neighbor Entropy Estimator", Physical Review E., 2016
    """

    # TODO: 分别针对x为连续型或离散型进行计算.
    def __init__(self, x: np.ndarray, y: np.ndarray):
        self.x, self.y = x.flatten(), y.flatten()

    def cal_mi_cc(self, k: int, metric: str = 'euclidean'):
        x, y = self.x.reshape(-1, 1), self.y.reshape(-1, 1)
        vars = np.hstack((x, y))
        return _entropy(x, k, metric) + _entropy(y, k, metric) - _entropy(vars, k, metric)

# if __name__ == '__main__':

#     import pandas as pd
#     import sys
#     import os

#     BASE_DIR = os.path.abspath(os.path.join(
#         os.path.abspath(__file__), '../../../../'))
#     sys.path.append(BASE_DIR)

#     from src.settings import *
#     from core.dataset.data_generator import DataGenerator

#     # ---- 测试代码 --------------------------------------------------------------------------------

#     from core.dataset.data_generator import FUNC_NAMES

#     N = 5000
#     entropy_results = []
#     for func in FUNC_NAMES:
#         # 生成样本.
#         data_gener = DataGenerator(N=N)
#         x, y, _, _ = data_gener.gen_data(func)

#         # 计算MIC.
#         kl_estimator = KLEstimator(x, y)
#         mic = kl_estimator.cal_mi_cc(k=10)

#         entropy_results.append([func, mic])

#     # 整理结果.
#     entropy_results = pd.DataFrame(
#         entropy_results, columns=['func', 'entropy'])


#!/usr/bin/env python
# coding: utf-8
# ___
#
#
# ___
# *Copyright Pierian Data 2017*
# *For more information, visit us at www.pieriandata.com*
# In[19]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#get_ipython().run_line_magic('matplotlib', 'inline')
# In[20]:
airline = pd.read_csv('airline_passengers.csv',index_col="Month")
# In[21]:
airline.dropna(inplace=True)
airline.index = pd.to_datetime(airline.index)
# In[22]:
airline.head()
# # SMA
# ## Simple Moving Average
#
# We've already shown how to create a simple moving average, for a quick review:
# In[23]:
airline['6-month-SMA']=airline['Thousands of Passengers'].rolling(window=6).mean()
airline['12-month-SMA']=airline['Thousands of Passengers'].rolling(window=12).mean()
# In[24]:
airline.head()
# In[25]:
airline.plot()
# # EWMA
# ## Exponentially-weighted moving average
#
# We just showed how to calculate the SMA based on some window.However, basic SMA has some "weaknesses".
# * Smaller windows will lead to more noise, rather than signal
# * It will always lag by the size of the window
# * It will never reach to full peak or valley of the data due to the averaging.
# * Does not really inform you about possible future behaviour, all it really does is describe trends in your data.
# * Extreme historical values can skew your SMA significantly
#
# To help fix some of these issues, we can use an EWMA (Exponentially-weighted moving average).
# EWMA will allow us to reduce the lag effect from SMA and it will put more weight on values that occured more recently (by applying more weight to the more recent values, thus the name). The amount of weight applied to the most recent values will depend on the actual parameters used in the EWMA and the number of periods given a window size.
# [Full details on Mathematics behind this can be found here](http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows)
# Here is the shorter version of the explanation behind EWMA.
#
# The formula for EWMA is:
# $ y_t =   \frac{\sum\limits_{i=0}^t w_i x_{t-i}}{\sum\limits_{i=0}^t w_i} $
# Where x_t is the input value, w_i is the applied weight (Note how it can change from i=0 to t), and y_t is the output.
#
# Now the question is, how to we define the weight term w_i ?
#
# This depends on the adjust parameter you provide to the .ewm() method.
#
# When adjust is True (default), weighted averages are calculated using weights:
#
# ### $y_t = \frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...
# + (1 - \alpha)^t x_{0}}{1 + (1 - \alpha) + (1 - \alpha)^2 + ...
# + (1 - \alpha)^t}$
# When adjust=False is specified, moving averages are calculated as:
#
# ### $\begin{split}y_0 &= x_0 \\
# y_t &= (1 - \alpha) y_{t-1} + \alpha x_t,\end{split}$
#
# which is equivalent to using weights:
#
#  \begin{split}w_i = \begin{cases}
#     \alpha (1 - \alpha)^i & \text{if } i < t \\
#     (1 - \alpha)^i        & \text{if } i = t.
# \end{cases}\end{split}
# When adjust=True we have y0=x0 and from the last representation above we have yt=αxt+(1−α)yt−1, therefore there is an assumption that x0 is not an ordinary value but rather an exponentially weighted moment of the infinite series up to that point.
#
# One must have 0<α≤1, and while since version 0.18.0 it has been possible to pass α directly, it’s often easier to think about either the span, center of mass (com) or half-life of an EW moment:
# \begin{split}\alpha =
#  \begin{cases}
#      \frac{2}{s + 1},               & \text{for span}\ s \geq 1\\
#      \frac{1}{1 + c},               & \text{for center of mass}\ c \geq 0\\
#      1 - \exp^{\frac{\log 0.5}{h}}, & \text{for half-life}\ h > 0
#  \end{cases}\end{split}
# * Span corresponds to what is commonly called an “N-day EW moving average”.
# * Center of mass has a more physical interpretation and can be thought of in terms of span: c=(s−1)/2
# * Half-life is the period of time for the exponential weight to reduce to one half.
# * Alpha specifies the smoothing factor directly.
# In[30]:
airline['EWMA12'] = airline['Thousands of Passengers'].ewm(span=12).mean()
# In[32]:
airline[['Thousands of Passengers','EWMA12']].plot()
# Great! That is all for now, let's move on to ARIMA modeling!


# code copied from Scipy cookbook
# http://www.scipy.org/Cookbook/SignalSmooth

import numpy

def smooth(x,window_len=11,window='hanning'):
    """smooth the data using a window with requested size.

    This method is based on the convolution of a scaled window with the signal.
    The signal is prepared by introducing reflected copies of the signal
    (with the window size) in both ends so that transient parts are minimized
    in the begining and end part of the output signal.

    input:
        x: the input signal
        window_len: the dimension of the smoothing window; should be an odd integer
        window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
            flat window will produce a moving average smoothing.

    output:
        the smoothed signal

    example:

    t=linspace(-2,2,0.1)
    x=sin(t)+randn(len(t))*0.1
    y=smooth(x)

    see also:

    numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
    scipy.signal.lfilter

    TODO: the window parameter could be the window itself if an array instead of a string
    NOTE: length(output) != length(input), to correct this: return y[(window_len/2-1):-(window_len/2)] instead of just y.
    """

    if x.ndim != 1:
        raise ValueError, "smooth only accepts 1 dimension arrays."

    if x.size < window_len:
        raise ValueError, "Input vector needs to be bigger than window size."

    if window_len<3:
        return x

    if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
        raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'"

    s=numpy.r_[x[window_len-1:0:-1],x,x[-1:-window_len:-1]]
    #print(len(s))
    if window == 'flat': #moving average
        w=numpy.ones(window_len,'d')
    else:
        w=eval('numpy.'+window+'(window_len)')

    y=numpy.convolve(w/w.sum(),s,mode='valid')
    return y

if __name__=='__main__':
    from numpy import *
    from pylab import *

    t=linspace(-4,4,100)
    x=sin(t)
    xn=x+randn(len(t))*0.1
    y=smooth(x)

    ws=31

    subplot(211)
    plot(ones(ws))

    windows=['flat', 'hanning', 'hamming', 'bartlett', 'blackman']

    hold(True)
    for w in windows[1:]:
        eval('plot('+w+'(ws) )')

    axis([0,30,0,1.1])

    legend(windows)
    title("The smoothing windows")
    subplot(212)
    plot(x)
    plot(xn)
    for w in windows:
        plot(smooth(xn,10,w))
    l=['original signal', 'signal with noise']
    l.extend(windows)

    legend(l)
    title("Smoothing a noisy signal")
    show()


import numpy as np
from scipy.special.basic import factorial

class ZernikeGenerator(object):

    def __init__(self, nPixelOnDiameter):
        self._nPixel = nPixelOnDiameter
        self._rhoMap, self._thetaMap = self._polar_array(self._nPixel)
        self._dx = None
        self._dy = None
        self._dictCache = {}
        self._dictDxCache = {}
        self._dictDyCache = {}

    def getRadius(self):
        return self._nPixel / 2.

    def _derivativeCoeffX(self, index):
        if (self._dx is None) or (self._dx.shape[0] < index):
            self._dx = self._computeDerivativeCoeffX(index)
        return self._dx[0:index, 0:index]

    def _derivativeCoeffY(self, index):
        if (self._dy is None) or (self._dy.shape[0] < index):
            self._dy = self._computeDerivativeCoeffY(index)
        return self._dy[0:index, 0:index]

    @staticmethod
    def degree(index):
        n = int(0.5 * (np.sqrt(8 * index - 7) - 3)) + 1
        cn = n * (n + 1) / 2 + 1
        if n % 2 == 0:
            m = int(index - cn + 1) // 2 * 2
        else:
            m = int(index - cn) // 2 * 2 + 1
        radialDegree = n
        azimuthalFrequency = m
        return radialDegree, azimuthalFrequency

    def _rnm(self, radialDegree, azimuthalFrequency, rhoArray):
        n = radialDegree
        m = azimuthalFrequency
        rho = rhoArray
        if (n - m) % 2 != 0:
            raise Exception("n-m must be even. Got %d-%d" % (n, m))
        if abs(m) > n:
            raise Exception("The following must be true |m|<=n. Got %d, %d" %
                            (n, m))
        mask = np.where(rho <= 1, False, True)

        if(n == 0 and m == 0):
            return np.ma.masked_array(data=np.ones(rho.shape), mask=mask)
        rho = np.where(rho < 0, 0, rho)
        Rnm = np.zeros(rho.shape)
        S = (n - abs(m)) // 2
        for s in range(0, S + 1):
            CR = pow(-1, s) * factorial(n - s) / \
                (factorial(s) * factorial(-s + (n + abs(m)) / 2) *
                 factorial(-s + (n - abs(m)) / 2))
            p = CR * pow(rho, n - 2 * s)
            Rnm = Rnm + p
        return np.ma.masked_array(data=Rnm, mask=mask)

    def _polar(self, index, rhoArray, thetaArray):
        n, m = ZernikeGenerator.degree(index)
        rho = rhoArray
        theta = thetaArray

        Rnm = self._rnm(n, m, rho)
        NC = np.sqrt(2 * (n + 1))
        if m == 0:
            return np.sqrt(0.5) * NC * Rnm
        if index % 2 == 0:
            return NC * Rnm * np.cos(m * theta)
        else:
            return NC * Rnm * np.sin(m * theta)

    def _polar_array(self, nPixel):
        X, Y = np.mgrid[-1 + 1. / nPixel: 1 - 1. / nPixel: nPixel * 1j,
                        -1 + 1. / nPixel: 1 - 1. / nPixel: nPixel * 1j]
        r = np.sqrt(X ** 2 + Y ** 2)
        th = np.arccos(np.transpose(X * 1. / r))
        th = np.where(th < 2. * np.pi, th, 0)
        th = np.where(X < 0, 2. * np.pi - th, th)
        return r, th

    def getZernikeDict(self, indexVector):
        ret = {}
        for index in indexVector:
            ret[index] = self.getZernike(index)
        return ret

    def getZernike(self, index):
        if index not in list(self._dictCache.keys()):
            self._dictCache[index] = self._polar(index, self._rhoMap,
                                                 self._thetaMap)
        return self._dictCache[index]

    def __getitem__(self, index):
        return self.getZernike(index)

    def _computeDerivativeCoeffX(self, index):
        jmax = index
        G_mat = np.zeros((jmax, jmax))
        for i in range(1, jmax + 1):
            for j in range(1, jmax + 1):
                ni, mi = ZernikeGenerator.degree(i)
                nj, mj = ZernikeGenerator.degree(j)
                if (
                    (
                        (
                            (
                                mi != 0 and mj != 0
                            ) and (
                                (
                                    _isEven(i) and _isEven(j)
                                ) or (
                                    _isOdd(i) and _isOdd(j)
                                )
                            )
                        ) or (
                            (
                                (mi == 0) and _isEven(j)
                            ) or (
                                (mj == 0) and _isEven(i)
                            )
                        )
                    ) and (
                        (mj == mi + 1) or (mj == mi - 1)
                    ) and (
                        j < i
                    )
                ):
                    G_mat[i - 1, j - 1] = np.sqrt((ni + 1) * (nj + 1))
                    if ((mi == 0) or (mj == 0)):
                        G_mat[i - 1, j - 1] *= np.sqrt(2)
        return G_mat

    def _computeDerivativeCoeffY(self, index):
        jmax = index
        G_mat = np.zeros((jmax, jmax))
        for i in range(1, jmax + 1):
            for j in range(1, jmax + 1):
                ni, mi = ZernikeGenerator.degree(i)
                nj, mj = ZernikeGenerator.degree(j)
                if (
                    (
                        (
                            (
                                mi != 0 and mj != 0
                            ) and (
                                (
                                    _isOdd(i) and _isEven(j)
                                ) or (
                                    _isEven(i) and _isOdd(j)
                                )
                            )
                        ) or (
                            (
                                (mi == 0) and _isOdd(j)
                            ) or (
                                (mj == 0) and _isOdd(i)
                            )
                        )
                    ) and (
                        (mj == mi + 1) or (mj == mi - 1)
                    ) and (
                        j < i
                    )
                ):
                    G_mat[i - 1, j - 1] = np.sqrt((ni + 1) * (nj + 1))
                    if ((mi == 0) or (mj == 0)):
                        G_mat[i - 1, j - 1] *= np.sqrt(2)
                    if (
                        (
                            (
                                (mj == mi + 1) and _isOdd(i)
                            ) or (
                                (mj == mi - 1) and _isEven(i)
                            )
                        ) and (
                            mi != 0
                        )
                    ):
                        G_mat[i - 1, j - 1] *= -1

        return G_mat

    def getDerivativeXDict(self, indexVector):
        ret = {}
        for index in indexVector:
            ret[index] = self.getDerivativeX(index)
        return ret

    def getDerivativeYDict(self, indexVector):
        ret = {}
        for index in indexVector:
            ret[index] = self.getDerivativeY(index)
        return ret

    def getDerivativeX(self, index):
        if index not in self._dictDxCache:
            self._dictDxCache[index] = self._computeDerivativeX(index)
        return self._dictDxCache[index]

    def _computeDerivativeX(self, index):
        coeffX = self._derivativeCoeffX(index)
        dx = self.getZernike(1) * 0.
        for i in range(1, index):
            dx += coeffX[index - 1, i - 1] * self.getZernike(i)
        return dx

    def getDerivativeY(self, index):
        if index not in self._dictDyCache:
            self._dictDyCache[index] = self._computeDerivativeY(index)
        return self._dictDyCache[index]

    def _computeDerivativeY(self, index):
        coeffY = self._derivativeCoeffY(index)
        dy = self.getZernike(1) * 0.
        for i in range(1, index):
            dy += coeffY[index - 1, i - 1] * self.getZernike(i)
        return dy

    @staticmethod
    def radialOrder(j):
        return np.ceil(0.5 * (np.sqrt(8 * j + 1) - 3))

def _isOdd(num):
    return num % 2 != 0

def _isEven(num):
    return num % 2 == 0


import numpy as np

def garch_filter(beta, gamma, v_r, mu, sigma2_inf, sigma2_0):
    '''
    input:
    beta, gamma: garch(1,1) beta & gamma
    v_r: historical return
    mu: drift
    sigma2_inf: sigma_inf squared squared
    sigma2_0: the initial sigma squared

    output: dictionary of
    eps: vector of residuals
    sigma2: vector of sigma squared
    sigma2_next: next sigma squared
    '''

    alpha = sigma2_inf * (1 - beta - gamma)
    v_eps = np.ones_like(v_r)
    v_sigma2 = np.ones_like(v_r)

    sigma2_nxt = sigma2_0

    for n in range(len(v_r)):
        v_sigma2[n] = sigma2_nxt
        v_eps[n] = (v_r[n] - mu)/np.sqrt(sigma2_nxt)
        sigma2_nxt = alpha + beta * v_sigma2[n] + gamma * v_sigma2[n] * (v_eps[n]**2)

    return {'eps':v_eps, 'sigma2':v_sigma2, 'sigma2_next':sigma2_nxt}

def garch_return_simulation(beta, gamma, mu, sigma2_inf, sigma2_0, v_eps):

    v_r = np.zeros_like(v_eps)
    v_sigma2 = np.zeros_like(v_eps)

    sigma2_t = sigma2_0

    a = sigma2_inf * (1.0 - beta - gamma)
    for t in range(v_eps.size):
        eps = v_eps[t]
        v_r[t] = mu + np.sqrt(sigma2_t) * eps
        v_sigma2[t] = sigma2_t
        sigma2_t = a + beta * sigma2_t + gamma * sigma2_t * (eps**2)
    return {'r':v_r, 'sigma2': v_sigma2 }

def garch_mle_obj(v_r, mu, v_sigma2):
    return - np.sum(np.log(v_sigma2) + (v_r-mu)**2/v_sigma2)

def garch_exp_variance(sigma2_1, sigma2_inf, beta, gamma, v_t):
    return sigma2_inf + (1.0 - (beta+gamma)**v_t)/(1-(beta+gamma)) * (sigma2_1 - sigma2_inf)/v_t


from numpy import convolve, ones, mean, append, insert, ndarray, array

def movmean(arr: ndarray, k):
    if k > len(arr):
        raise ValueError("Window size > len(input_array)!")
    conv = convolve(arr, ones((k,)) / k, mode='valid')
    if k == len(conv):
        return conv
    i = k // 2 + 1
    l = len(arr)
    while True:
        conv = insert(conv, 0, mean(arr[0:i]))
        if len(conv) == l: break
        conv = append(conv, mean(arr[-i:]))
        if len(conv) == l: break
        i -= 1
    return conv

if __name__ == '__main__':
    A = array([4, 8, 6, -1, -2, -3, -1, 3, 4, 5])
    # A = normal(10, 3, 1000)
    m = movmean(A, 11)
    print(m, len(m))


#dualMat(mts)           :   Takes list of matrices, computes their dual.

import numpy as np

__all__ = ['dualMat']

def dualMat(mts, lid = False):
    r"""Takes as input a list of matrices, an optional argument stating if
        the matrices are linearly independent and returns a list of matrices
        dual (under Frobenius Norm) to the input list of matrices

    Parameters
    ----------
    mts : list of numpy.ndarrays
          A list of matrices

    lid : bool
          Optional, False by default. If True assumes matrices are
          linearly independent

    Returns
    ----------
    mts : list of numpy.ndarrays
          The dual matrices(under Frobenius Norm) to the set of input matrices.
          If matrices are linearly independent, it uses regular inverse
          otherwise uses(less numerically accurate) pseudo-inverse.
    """
    vc = []
    #Each matrix is a ket, place it as a bra making a row of vecRows matrix
    for mt in mts:
        (d,d) = mt.shape
        vcR = np.ndarray.flatten(mt).conj()
        vc += [np.array([vcR])]
    vecRows =  np.vstack(vc)
    #Construct matrix with dual vectors as its columns
    if lid:
        try:
            dualVecCols = np.linalg.inv(vecRows)
        except:
            raise Exception('Cannot find dual matrices')
    else:
        try:
            dualVecCols = np.linalg.pinv(vecRows)
        except:
            raise Exception('Cannot find dual matrices')

    (row, col) = dualVecCols.shape
    mts = []
    #Reshape the columns as matrices of same size as input
    for i in xrange(col):
        mt = dualVecCols[:,i].reshape(d,d)
        mts += [zerOut(mt, tol = 1e-15)]
    return mts


import matplotlib.pyplot as plt
import numpy as np
from Exercise1.newton_raphson import newton_raphson
from Exercise1.bisection import bisection
from Exercise1.secant import secant
from Exercise1.mult_newton_raphson import mult_newton_raphson
from Exercise1.iterations_comparison import bisection_iterations, newton_iterations, secant_iterations

def g(x):
    return np.e ** ((np.sin(x)) ** 3) + x ** 6 - 2 * (x ** 4) - x ** 3 - 1

def gprime(x):
    return 3 * (np.e ** ((np.sin(x)) ** 3)) * ((np.sin(x)) ** 2) * np.cos(x) + 6 * (x ** 5) - 8 * (x ** 3) - 3 * (
           x ** 2)

def g_second_der(x):
    # return 3 * np.cos(x) * (216 * np.cos(x) ** 2 - 432 * np.cos(x)) * np.sin(x) ** 2 + np.sin(x) * (
    #        576 * np.cos(x) ** 3 * np.sin(x) + 564 * np.cos(x) * np.sin(x)
    #        - 354 * np.sin(x)) + np.sin(x) ** 3 * (432 * np.sin(x) - 432 * np.cos(x) * np.sin(x)) \
    #       + np.cos(x) * (-144 * np.cos(x) ** 4 - 282 * np.cos(x) ** 2 + 354 * np.cos(x) + 24)
    h = 0.001
    return (gprime(x + h) - gprime(x)) / h

def main():
    # sub question a
    print("Bisection")

    root, iterations = bisection(g, -1.5, -1.0)
    print("root = "+str(root))
    print("iterations ="+str(iterations))

    root, iterations = bisection(g, 1.25, 1.75)
    print("root = "+str(root))
    print("iterations ="+str(iterations))

    root, iterations = bisection(g, -0.05, 0.05)
    print("root = "+str(root))
    print("iterations ="+str(iterations))

    root, iterations = bisection(g, -0.5, 0.5)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = bisection(g, 0, 1)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = bisection(g, -1.5, 1.5)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    print()
    print("Newton-Raphson")

    root, iterations = newton_raphson(g, gprime, -1.4)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = newton_raphson(g, gprime, 1.7)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = newton_raphson(g, gprime, 0.25)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = newton_raphson(g, gprime, 0.5)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = newton_raphson(g, gprime, -0.25)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = newton_raphson(g, gprime, -0.5)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = newton_raphson(g, gprime, 0)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    print()
    print("Secant")

    root, iterations = secant(g, -1.5, -1.0)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = secant(g, 1.3, 1.9)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = secant(g, -0.25, 0.25)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = secant(g, -0.5, 0.5)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

    root, iterations = secant(g, 0, 1.5)
    print("root = " + str(root))
    print("iterations =" + str(iterations))

main()


"""CPIE helper functions
"""

import numpy as np

def mean_and_covariance(solutions):
    """Calculate mean vector and covariance matrix of the solutions
    Arguments:
        solutions {list of Solution} -- target
    Returns:
        (numpy array, numpy matrix) -- Mean vector and covariance matrix
    """
    sol_size = len(solutions)
    dimension = solutions[0].x.size
    # calc mean
    mu = sum(s.x for s in solutions) / len(solutions)
    #calc variance
    variance = np.zeros((dimension, dimension))
    for s in solutions:
        diff = s.x - mu
        variance += np.outer(diff, diff)
    variance /= sol_size
    return mu, variance

# ToDo Speed up with numpy
def k_means_mahalanobis(solutions, k=2, max_iter=100):
    """k-means clustering with mahalanobis distance.
    Arguments:
        solutions {list of Solution} -- Clustering target
    Keyword Arguments:
        k {int} -- Number of division (default: {2})
        max_iter {int} -- Max inner iteration (default: {100})
    Returns:
        list of (list of Solution) -- k clustered solutions
    """
    assert k <= len(solutions), "k should be less than or equarl to len(solutions)"
    seed_bases = np.random.choice(solutions, size=k, replace=False)
    seeds = [s.x.copy() for s in seed_bases]
    dimension = seeds[0].size
    BInvs = [np.eye(dimension) for i in range(k)]
    class_indices = [0] * len(solutions)
    for t in range(max_iter):
        is_changed = False
        classes = [[] for i in range(k)]
        # classify solutions
        for i, s in enumerate(solutions):
            distances = [np.linalg.norm(np.dot(BInv, s.x - seed))
                         for seed, BInv in zip(seeds, BInvs)]
            class_index = np.argmin(distances)
            if class_index != class_indices[i]:
                is_changed = True
            class_indices[i] = class_index
            classes[class_index].append(s)
        if not is_changed or t == max_iter - 1:
            break
        # update
        for i, c in enumerate(classes):
            if len(c) == 0:
                seeds[i] = np.zeros(dimension)
                BInvs[i] = np.eye(dimension)
            elif len(c) <= dimension:
                seeds[i] = sum(s.x for s in c) / len(c)
                BInvs[i] = np.eye(dimension)
            else:
                seeds[i], covariance = mean_and_covariance(c)
                BInvs[i] = np.linalg.inv(np.linalg.cholesky(covariance))
    return classes


import numpy as np
from scripts.helpers import sigmoid

def compute_mse(y, tx, w):
    """Function to compute mean square error (MSE).
    Args:
        y  (numpy array): Matrix output of size N x 1.
        tx (numpy array): Matrix input of size N x D.
        w  (numpy array): Matrix weight (parameters of the model) of size D x 1.
    Returns:
        mse (float, scalar) : The mean square error (MSE) for given model w.
    """

    value_error = y - tx.dot(w)
    mse         = np.inner(value_error, value_error)/(2 * y.shape[0])
    return mse

def compute_rmse(y, tx, w):
    """Function to compute root mean square error (RMSE).
    Args:
        y  (numpy array): Matrix output of size N x 1.
        tx (numpy array): Matrix input of size N x D.
        w  (numpy array): Matrix weight (parameters of the model) of size D x 1.
    Returns:
        rmse (float, scalar) : The root mean square error (RMSE) for given model w.
    """

    rmse = np.sqrt(2*compute_mse(y, tx, w))
    return rmse

def compute_loss_logistic(y, tx, w):
    """Function to compute cost of logistic regression by negative log likelihood.
    Args:
        y  (numpy array): Matrix output of size N x 1.
        tx (numpy array): Matrix input of size N x D.
        w  (numpy array): Matrix weight (parameters of the model) of size D x 1.
    Returns:
        loss (float, scalar) : The loss/cost value of logistic regression for given model w.
    """

    y_hat = sigmoid(tx.dot(w))
    loss  = (np.transpose(y)).dot(np.log(y_hat + 1e-5)) + (np.transpose(1 - y)).dot(np.log(1 - (y_hat - 1e-5)))
    loss  = np.squeeze(- loss)
    return loss

def compute_loss_logistic_regularized(y, tx, w, lambda_):
    """Function to compute cost of regularized logistic regression by negative log likelihood.
    Args:
        y       (numpy array): Matrix output of size N x 1.
        tx      (numpy array): Matrix input of size N x D.
        w       (numpy array): Matrix weight (parameters of the model) of size D x 1.
        lambda_ (float)      : Penalty constant.
    Returns:
        loss (float, scalar) : The loss/cost value of regularized logistic regression for given model w.
    """

    y_hat            = sigmoid(tx.dot(w))
    loss             = (np.transpose(y)).dot(np.log(y_hat + 1e-5)) + (np.transpose(1 - y)).dot(np.log(1 - (y_hat - 1e-5)))
    regularized_loss = loss - (lambda_ * np.inner(w, w))
    final_loss       = np.squeeze(- regularized_loss)
    return final_loss


import math

import numpy as np

def bisezione(f, a, b, tol):
    """
    Algoritmo di Bisezione per il calcolo dello zero di una funzione.

    :param f: la funzione di cui calcolare lo zero
    :param a: il valore minimo dell'intervallo
    :param b: il valore massimo dell'intervallo
    :param tol: la tollerenza
    :return: (zero della funzione, numero di iterazioni, iterazioni)
    """

    def sign(value):
        return math.copysign(1, value)

    fa, fb, x = f(a), f(b), None

    if sign(fa) == sign(fb):
        print("sign(fa) == sign(fb) => Non applicabile")
        return None, 0, []

    max_iterazioni = int(math.ceil(math.log2((b - a) / tol)))
    it, xk = 0, []
    while it < max_iterazioni and abs(b - a) >= tol + np.spacing(1) * max(abs(a), abs(b)):
        x = a + (b - a) / 2  # Calcolo il punto medio
        xk.append(x)
        fx = f(x)
        if sign(fx) == sign(fa):
            a, fa = x, fx
        else:
            b, fb = x, fx
        it += 1
    return x, it, xk


import numpy as np
from math import sqrt, floor
from scipy.special import eval_genlaguerre

def gaussian_laguerre(p, l, mode_field_diameter=1, grid=None):
	r'''Creates a Gaussian-Hermite mode.

	This function evaluates a (p,l) order Gaussian-Laguerre mode on a grid.
	The definition of the modes are the following,

	.. math::
		\exp{\left(-\frac{r^2}{w_0^2}\right)} L_p^{|l|}\left(\frac{2r^2}{w_0^2} \right) \left(\sqrt{2}\frac{r}{w_0}\right)

	Here :math:`w_0` is the mode_field_radius, which is :math:`\mathrm{MFD}/2`.
	And :math:`L_p^{|l|}` are the generalized Laguerre Polynomials.
	All modes are numerical normalized to have a total power of 1.

	More details on generalized Laguerre Polynomials can be found on:
	http://mathworld.wolfram.com/AssociatedLaguerrePolynomial.html

	Parameters
	----------
	p : int
		The radial order.
	l : int
		The azimuthal order.
	mode_field_diameter : scalar
		The mode field diameter of the mode.
	grid : Grid
		The grid on which to evaluate the mode.

	Returns
	-------
	Field
		The evaluated mode.
	'''
	from ..field import Field

	if grid.is_separated and grid.is_('polar'):
		R, Theta = grid.separated_coords
	else:
		R, Theta = grid.as_('polar').coords

	# Easy access
	r = 2*R/mode_field_diameter
	r2 = r**2
	# The mode
	lg = (r*sqrt(2))**(abs(l)) * np.exp(-r2) * np.exp(-1j*l*Theta) * eval_genlaguerre(p, abs(l), 2*r2)
	# Numerically normalize the modes
	lg /= np.sum(np.abs(lg)**2 * grid.weights)

	return Field(lg, grid)

# High level functions
def make_gaussian_laguerre_basis(grid, pmax, lmax, mode_field_diameter, pmin=0):
	'''Creates a Gaussian-Laguerre mode basis.

	This function evaluates Gaussian-Laguerre modes. For each radial order
	within [pmin, pmax] it will calculate the azimuthal order [-lmax, lmax] inclusive.
	This function returns a ModeBasis made out of these Gaussian-Laguerre modes.

	Parameters
	----------
	grid : Grid
		The grid on which to evaluate the Gaussian-Laguerre mode.
	pmax : int
		The maximum radial order of the modes.
	lmax : int
		The maximum azimuthal order.
	mode_field_diameter : scalar
		The mode field diameter of the Gaussian-Laguerre mode.
	pmin : int
		The minimal radial order.

	Returns
	-------
	ModeBasis
		The Gaussian-Laguerre modes.
	'''
	from .mode_basis import ModeBasis

	modes = [gaussian_laguerre(pi, li, mode_field_diameter, grid) for li in range(-lmax, lmax + 1) for pi in range(pmin, pmax)]
	return ModeBasis(modes)


# https://leetcode.com/problems/k-closest-points-to-origin/
from typing import List
class Solution:
    # uses python sorting, O(NlogN)
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        # sort by x value
        points = sorted(points, key=lambda x: x[0]**2+x[1]**2, reverse=False)
        return points[:K]

# interesting use of kd-tree for fast kNN search when N is very large but K is small, O(KlogN)
# https://leetcode.com/problems/k-closest-points-to-origin/discuss/576025/Python-3-lines-kNN-search-using-kd-tree-(for-large-number-of-queries)
# https://en.wikipedia.org/wiki/K-d_tree
# idea is to use axis-aligned hyperplanes, each node = 1 axis/dimension (out of k dimensions, in this case k = 2)
# searching is O(logN), kinda like binary tree but extended to k dimensions
# track current best point, then go up one level, compare other side of hyperplane, if dist is less than curr best,
# we MAY find a better point there, so we must search, otherwise, go up 1 more level of recursion, and repeat
from scipy import spatial
class Solution_kdtree:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        tree = spatial.KDTree(points)
		# x is the origin, k is the number of closest neighbors, p=2 refers to choosing l2 norm (euclidean distance)
        distance, idx = tree.query(x=[0,0], k=K, p=2)
        return [points[i] for i in idx] if K > 1 else [points[idx]]

# possible to do faster than O(NlogN), i.e. O(N) on average
# by using divide & conquer, quickselect (quick sort is still O(NlogN))
# on avg we half the array since we dont need to sort the other half (for quick select)
# so, N + N/2 + N/4 + ... ~= 2N = O(N)
class Solution_quickselect:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        dist = lambda i: points[i][0] ** 2 + points[i][1] ** 2

        def partition(array, l, r):
            pivot = array[r] # how to use median of 3?
            a = l
            for i in range(l, r):
                if dist(i) <= dist(r):
                    array[i], array[a] = array[a], array[i]
                    a += 1
            array[a], array[r] = array[r], array[a]
            return a

        def sort(array, l, r, K):
            if l < r:
                pivot = partition(array, l, r)
                if pivot == K:
                    return
                elif pivot < K:
                    sort(array, pivot + 1, r, K)
                else:
                    sort(array, l, pivot - 1, K)

        sort(points, 0, len(points) - 1, K)
        return points[:K]

# minheap also can be used, but won't be as fast as quickselect I think
import heapq
class Solution_heap:
    # TC: O(N * logK) bcos inserting 1 item into heap is logK, and we need to insert N items
    # SC: O(K) since our heap is of size K
    # practically this is actly faster than quickselect soln, i guess bcos of recursive stack
    # https://leetcode.com/problems/k-closest-points-to-origin/discuss/294389/Easy-to-read-Python-min-heap-solution-(-beat-99-python-solutions-)
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        heap = []
        for (x, y) in points:
            dist = -(x*x + y*y)
            if len(heap) == K:
                heapq.heappushpop(heap, (dist, x, y))
            else:
                heapq.heappush(heap, (dist, x, y))

        return [(x,y) for (dist,x, y) in heap]

# priority queue (queue w/ priority system, which in this case is the distance from origin)
# uses heap as underlying data structure?
import queue
class Solution_queue:
    # time: O(N + NlogK)
    # space: O(K)
    def kClosest(self, points, K):
        dist = [point[0]**2 + point[1]**2 for point in points]
        q = queue.PriorityQueue()

        for i in range(len(points)):
            q.put((-dist[i], points[i]))
            if q.qsize() > K:
                q.get() # remove farthest point from queue

        res = []
        while K > 0:
            point = q.get()[1]
            res.append(point)
            K -= 1
        return res


from mpl_toolkits.mplot3d import Axes3D  # noqa: F401 unused import
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
from single_var_gradient_decent import LinearRegressionUsingGD

def plot_result(x, y, y_predicted):
    plt.subplots(1, 1)

    # Actual data points
    plt.scatter(x, y, s=10)
    plt.xlabel('\u03B80')
    plt.ylabel('\u03B81')

    # Predicted data points
    plt.plot(x, y_predicted, color='r')
    plt.show()

def plot_cost_function_2d(theta_1, theta_2, cost):
    fig, ax = plt.subplots(1, 1)
    ax.contourf(theta_1,
                theta_2,
                cost,
                levels=[0, 1, 2, 4, 6, 8, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
                cmap=cm.coolwarm,
                antialiased=True)

    plt.xlabel('\u03B81')
    plt.ylabel('\u03B82')
    plt.show()

def plot_cost_function_3d(theta_1, theta_2, cost):
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    ax.plot_surface(theta_1,
                    theta_2,
                    cost,
                    cmap=cm.coolwarm,
                    linewidth=0, antialiased=True, )

    plt.xlabel('\u03B80')
    plt.ylabel('\u03B81')
    ax.set_zlabel('J(\u03B80, \u03B81)')
    ax.set_zlim(0, 25)
    plt.show()

def get_cost_function(theta_1, theta_2, x, y, points_n):
    m = theta_1.shape[0]
    cost = np.zeros([theta_1.shape[0], theta_1.shape[1]])
    for i in range(points_n):
        residuals = ((theta_1 * x[i] + theta_2) - y[i]) ** 2
        cost += residuals
    cost = cost / (2 * m)
    return cost

def create_mesh_grid():
    theta_1 = np.arange(-10, 14, 0.05)
    theta_2 = np.arange(-100, 100, 0.05)
    theta_1, theta_2 = np.meshgrid(theta_1, theta_2)
    return theta_1, theta_2

def plot_cost_function(x, y, points_n):
    theta_1, theta_2, = create_mesh_grid()
    cost = get_cost_function(theta_1, theta_2, x, y, points_n)
    plot_cost_function_3d(theta_1, theta_2, cost)
    plot_cost_function_2d(theta_1, theta_2, cost)

def plot_raw_data(x, y):
    plt.scatter(x, y, s=10)
    plt.xlabel('\u03B80')
    plt.ylabel('\u03B81')
    plt.show()

def create_data(points_n):
    x = np.random.rand(points_n, 1) * 20
    y = (2 * (x + (2 * np.random.rand(points_n, 1)))) + 1
    return x, y

def main():
    points_n = 50
    x, y = create_data(points_n)

    plot_raw_data(x, y)
    plot_cost_function(x, y, points_n)

    # Model initialization
    # Sci-kit learn implementation:
    # regression_model = LinearRegression()
    regression_model = LinearRegressionUsingGD()

    # Fit the data(train the model)
    regression_model.fit(x, y)

    # Predict
    y_predicted = regression_model.predict(x)

    # model evaluation
    rmse = mean_squared_error(y, y_predicted)
    r2 = r2_score(y, y_predicted)

    # For sci-kit learn implementation:
    # print('Slope:', regression_model.coef_)
    # print('Intercept:', regression_model.intercept_)
    print('Slope:', regression_model.w)
    print('Intercept:', regression_model.b)
    print('Root mean squared error: ', rmse)
    print('R2 score: ', r2)

    # plot
    plot_result(x, y, y_predicted)

if __name__ == '__main__':
    main()


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

INPUT_FILE="blobs.txt"
ITERATIONS=100
#Define label for differnt point group
NOISE = 0
UNASSIGNED = 0
core=-1
edge=-2

dataset = []

def read_dataset():
  """
  Reading dataset
  """
  global INPUT_FILE, dataset
  f = open(INPUT_FILE, "r")
  lines = f.readlines()

  for i in range(len(lines)):
    data = lines[i].split()
    dataset.append(list(map(float, data)))
    # print(data)

  f.close()
  pass

def calc_distance(X1, X2):
    return(sum((X1 - X2)**2))**0.5

def findClosestCentroids(ic, X):
    assigned_centroid = []
    for i in X:
        distance=[]
        for j in ic:
            distance.append(calc_distance(i, j))
        assigned_centroid.append(np.argmin(distance))
    return assigned_centroid

def calc_centroids(clusters, X):
    new_centroids = []
    new_df = pd.concat([pd.DataFrame(X), pd.DataFrame(clusters, columns=['cluster'])],
                      axis=1)
    for c in set(new_df['cluster']):
        current_cluster = new_df[new_df['cluster'] == c][new_df.columns[:-1]]
        cluster_mean = current_cluster.mean(axis=0)
        new_centroids.append(cluster_mean)
    return new_centroids

def k_means(k):
  """
  1. Initialize centroids – This is done by randomly choosing K no of points, the points can be present in the dataset or also random points.
  2. Assign Clusters – The clusters are assigned to each point in the dataset by calculating their distance from the centroid and assigning it to the centroid with minimum distance.
  3. Re-calculate the centroids – Updating the centroid by calculating the centroid of each cluster we have created.
  """
  global dataset
  X = np.array(dataset)

  init_centroids = random.sample(range(0, len(dataset)), 3)

  centroids = []
  for i in init_centroids:
      centroids.append(dataset[i])
  print(centroids)

  # converting to 2D - array
  centroids = np.array(centroids)
  get_centroids = findClosestCentroids(centroids, X)

  # # computing the mean of separated clusters
  # cent={}
  # for k in range(K):
  #     cent[k+1]=np.array([]).reshape(2,0)

  # # assigning of clusters to points
  # for k in range(m):
  #     cent[minimum[k]]=np.c_[cent[minimum[k]],X[k]]
  # for k in range(K):
  #     cent[k+1]=cent[k+1].T

  # # computing mean and updating it
  # for k in range(K):
  #      centroids[:,k]=np.mean(cent[k+1],axis=0)
  prev_centr = []
  for i in range(ITERATIONS):
    get_centroids = findClosestCentroids(centroids, X)
    centroids = calc_centroids(get_centroids, X)
    print(centroids)

    plt.figure()
    plt.scatter(np.array(centroids)[:, 0], np.array(centroids)[:, 1], color='black')
    plt.scatter(X[:, 0], X[:, 1], alpha=0.1)
    plt.show()

if __name__ == "__main__":
  print("Reading file")
  read_dataset()
  # find_nearest_neighbour(4)
  #Set EPS and Minpoint
  epss = [5,10]
  minptss = [5,10]
  k_means(2)
  # Find ALl cluster, outliers in different setting and print resultsw
  # for eps in epss:
  #   for minpts in minptss:
  #     DBSCAN_start(eps, minpts)


'''
Defines function `calc_periodic_kernel`

Examples
--------

'''

import numpy as np

def calc_periodic_kernel(x_QF, x_train_NF=None, length_scale=1.0, period=1.0):
    ''' Evaluate periodic kernel to produce matrix between two datasets.

    Will compute the kernel function for all possible pairs of feature vectors,
    one from the query dataset, one from the reference training dataset.

    Args
    ----
    x_QF : 2D numpy array, shape (Q, F) = (n_query_examples, n_features)
        Feature array for *query* dataset
        Each row corresponds to the feature vector on example

    x_train_NF : 2D numpy array, shape (N, F) = (n_train_examples, n_features)
        Feature array for reference *training* dataset
        Each row corresponds to the feature vector on example

    Returns
    -------
    k_QN : 2D numpy array, shape (Q, N)
        Entry at index (q,n) corresponds to the kernel function evaluated
        at the feature vectors x_QF[q] and x_train_NF[n]
    '''
    assert x_QF.ndim == 2
    assert x_train_NF.ndim == 2

    Q, F = x_QF.shape
    N, F2 = x_train_NF.shape
    assert F == F2

    k_QN = np.zeros((Q, N))
    # TODO compute kernel between rows of x_QF and rows of x_train_NF
    for i in range(Q):
        for  j in range(N):
            k_QN[i][j] = np.exp(-0.5 *(np.square(np.sin((np.pi / period) * (x_train_NF[j] - x_QF[i]))) / np.square(length_scale)))
    # Ensure the kernel matrix positive definite
    # By adding a small positive to the diagonal
    M = np.minimum(Q, N)
    k_QN[:M, :M] += 1e-08 * np.eye(M)
    return k_QN

#
# np.set_printoptions(precision=3, suppress=1)
#
# # Part 1: Simple kernel evaluations with F=1 features
# x_zero_11 = np.asarray([[0.0]])
# x_one_11 = np.asarray([[1.0]])
#
# # Kernel of x=0.0 with itself should be 1.0
# k_11 = calc_periodic_kernel(x_zero_11, x_zero_11, length_scale=2.0, period=0.3)
# print(k_11.ndim)
# # 2
# print(k_11)
# # array([[1.]])
#
# # Kernel of x=1.0 with itself should be 1.0
# print(calc_periodic_kernel(x_one_11, x_one_11, length_scale=2.0, period=0.3))
# # array([[1.]])
#
# # Kernel of x and z=x+period should be 1.0
# p = 0.3
# print(calc_periodic_kernel(x_one_11, x_one_11 + p, length_scale=2.0, period=p))
# # array([[1.]])
# print(calc_periodic_kernel(x_one_11, x_one_11 + 3 * p, length_scale=2.0, period=p))
# # array([[1.]])
#
# # A few other tests
# print(calc_periodic_kernel(x_one_11, x_zero_11, length_scale=0.5, period=0.3))
# # array([[0.223]])
# print(calc_periodic_kernel(x_one_11, x_zero_11, length_scale=2.0, period=0.95))
# # array([[0.997]])
#
# # Part 2: Kernel evaluations with several examples at once (still F=1)
# x_train_31 = np.asarray([[0.0], [1.0], [2.0]])
# print(calc_periodic_kernel(x_train_31, x_train_31, length_scale=2.0, period=0.95))
# # array([[1.   , 0.997, 0.987],
# #        [0.997, 1.   , 0.997],
# #        [0.987, 0.997, 1.   ]])
# x_test_21 = np.asarray([[-0.5], [0.5]])
# print(calc_periodic_kernel(x_test_21, x_train_31, length_scale=2.0, period=0.95))
# # array([[0.883, 0.889, 0.9  ],
# #        [0.883, 0.883, 0.889]])


import numpy as np
from prml.rv.rv import RandomVariable
from prml.rv.dirichlet import Dirichlet

class Categorical(RandomVariable):
    """
    Categorical distribution
    p(x|mu) = prod_k mu_k^x_k
    """

    def __init__(self, mu=None):
        """
        construct categorical distribution

        Parameters
        ----------
        mu : (n_classes,) np.ndarray or Dirichlet
            probability of each class
        """
        super().__init__()
        self.mu = mu

    @property
    def mu(self):
        return self.parameter["mu"]

    @mu.setter
    def mu(self, mu):
        if isinstance(mu, np.ndarray):
            if mu.ndim != 1:
                raise ValueError("dimensionality of mu must be 1")
            if (mu < 0).any():
                raise ValueError("mu must be non-negative")
            if not np.allclose(mu.sum(), 1):
                raise ValueError("sum of mu must be 1")
            self.n_classes = mu.size
            self.parameter["mu"] = mu
        elif isinstance(mu, Dirichlet):
            self.n_classes = mu.size
            self.parameter["mu"] = mu
        else:
            if mu is not None:
                raise TypeError(f"{type(mu)} is not supported for mu")
            self.parameter["mu"] = None

    @property
    def ndim(self):
        if hasattr(self.mu, "ndim"):
            return self.mu.ndim
        else:
            return None

    @property
    def size(self):
        if hasattr(self.mu, "size"):
            return self.mu.size
        else:
            return None

    @property
    def shape(self):
        if hasattr(self.mu, "shape"):
            return self.mu.shape
        else:
            return None

    def _check_input(self, X):
        assert X.ndim == 2
        assert (X >= 0).all()
        assert (X.sum(axis=-1) == 1).all()

    def _fit(self, X):
        if isinstance(self.mu, Dirichlet):
            self._bayes(X)
        elif isinstance(self.mu, RandomVariable):
            raise NotImplementedError
        else:
            self._ml(X)

    def _ml(self, X):
        self._check_input(X)
        self.mu = np.mean(X, axis=0)

    def _map(self, X):
        self._check_input(X)
        assert isinstance(self.mu, Dirichlet)
        alpha = self.mu.alpha + X.sum(axis=0)
        self.mu = (alpha - 1) / (alpha - 1).sum()

    def _bayes(self, X):
        self._check_input(X)
        assert isinstance(self.mu, Dirichlet)
        self.mu.alpha += X.sum(axis=0)

    def _pdf(self, X):
        self._check_input(X)
        assert isinstance(self.mu, np.ndarray)
        return np.prod(self.mu ** X, axis=-1)

    def _draw(self, sample_size=1):
        assert isinstance(self.mu, np.ndarray)
        return np.eye(self.n_classes)[
            np.random.choice(self.n_classes, sample_size, p=self.mu)
        ]


# --------------
import pandas as pd
import numpy as np
import math

#Code starts here
class complex_numbers(object):
    def __init__(self, real=0.00, imag=0.00):
        self.real = real
        self.imag = imag

    def __repr__(self):

        if self.real == 0.0 and self.imag == 0.0:
            return "0.00"
        if self.real == 0:
            return "%.2fi" % self.imag
        if self.imag == 0:
            return "%.2f" % self.real
        return "%.2f %s %.2fi" % (self.real, "+" if self.imag >= 0 else "-", abs(self.imag))

    def __add__(self, other):
        real = self.real + other.real
        imag = self.imag + other.imag
        return complex_numbers(real,imag)

    def __sub__(self, other):
        real = self.real - other.real
        imag = self.imag - other.imag
        return complex_numbers(real,imag)

    def __mul__(self, other):
        real = self.real * other.real - self.imag * other.imag
        imag = self.real * other.imag + self.imag * other.real
        return complex_numbers(real,imag)

    def __truediv__(self, other):
        sr, si, o_r, oi = self.real, self.imag,other.real, other.imag # short forms
        r = float(o_r**2 + oi**2)
        return complex_numbers((sr*o_r+si*oi)/r, (si*o_r-sr*oi)/r)

    def absolute(self):
        return math.sqrt(self.real**2 + self.imag**2)

    def conjugate(self):
        real = self.real
        imag = -self.imag
        return complex_numbers(real,imag)

    def argument(self):
        return math.degrees((math.atan(self.imag/self.real)))

comp_1 = complex_numbers(3,5)
print('Complex Number 1: ',comp_1)

comp_2 = complex_numbers(4,4)
print('Complex Number 2: ',comp_2)

comp_sum= (comp_1+comp_2)
print('Sum of Complex Number 1 & 2: ', comp_sum)

comp_diff= (comp_1-comp_2)
print('Difference of Complex Number 1 & 2: ',comp_diff)

comp_prod= (comp_1*comp_2)
print('Product of Complex Number 1 & 2: ',comp_prod)

comp_quot= (comp_1/comp_2)
print('Quotient of Complex Number 1 & 2: ',comp_quot)

comp_abs=comp_1.absolute()
print('Absolute value of Complex Number 1: ',comp_abs)

comp_conj= comp_1.conjugate()
print('Conjugate value of Complex Number 1: ',comp_conj)

comp_arg=comp_1.argument()
print('Argument value of Complex Number 1: ',comp_arg)


import numpy as np
from load_files import training_ims
import time

# Transform everything into array
training_ims = np.array(training_ims)

# Select quantity of data to use
N_data_total = training_ims.shape[0]
percentage = 0.08
N_to_use = int(percentage * N_data_total)

# Decide how much data to use
X = training_ims[0:N_to_use]

# First we need to calculate the probabilities w and beta

def p_matrix_1(X):
    """
    Calculates the outer product of the matrix with a loop
    """

    average = 0
    for index, pattern in enumerate(X):
        outter = np.outer(pattern, pattern)
        average += outter

    p_matrix = average * 1.0 / X.shape[0]
    return p_matrix

def p_matrix_2(X):
    """
    Calculates the outer product of a function with broadcasting
    """

    average = 0
    for index, pattern in enumerate(X):
        outter = pattern[:, np.newaxis] * pattern[np.newaxis, :]
        average += outter

    p_matrix = average * 1.0 / X.shape[0]

    return p_matrix

def p_matrix_3(X):
    """
    Calculating the outer product using vectorization
    """
    return np.mean(X[:, :, np.newaxis] * X[:, np.newaxis, :], axis=0)

# Now we profile
start = time.time()
p_matrix_1(X)
end = time.time()
print end - start

start = time.time()
p_matrix_2(X)
end = time.time()
print end - start

start = time.time()
p_matrix_3(X)
end = time.time()
print end - start


# -*- coding: utf-8 -*-
"""
Created on Wed Mar  6 10:51:55 2019

Metric and affine rectification of images using parallel lines (in 2D)

@author: Gopiraj
"""

#loading the necessary libraries
import numpy as np
import cv2

#Reading the image
img = cv2.imread('floor.png')

#making the first line
p1 = [171, 244, 1]
p2 = [422, 123, 1]

l1 = np.cross(p1, p2)
l1 = l1/l1[-1]

#making the second line
p3 = [143, 117, 1]
p4 = [338, 247, 1]

l2 = np.cross(p3, p4)
l2 = l2/l2[-1]

#line parallel to l1
p5 = [108, 191, 1]
p6 = [356, 88, 1]

l3 = np.cross(p5, p6)
l3 = l3/l3[-1]

#line parallel to l2
p7 = [226, 87, 1]
p8 = [424, 200, 1]

l4 = np.cross(p7, p8)
l4 = l4/l4[-1]

#calculating the vanishing points
x1 = np.cross(l1, l3)
x1 = x1/x1[-1]

x2 = np.cross(l2, l4)
x2 = x2/x2[-1]

#calculating the vanishing line
vLine = np.cross(x1, x2)
vLine = vLine/vLine[-1]

#Transformation to make vanishing line to infinity
H = np.eye(3)
H[2, 0] = vLine[0]
H[2, 1] = vLine[1]

#removing the projective distortion
res = cv2.warpPerspective(img, H, (img.shape[1], img.shape[0]))

cv2.imwrite('floor_affineRect.png', res)

#detecting pairs of lines in the affine rectified image
p1 = [107, 151, 1]
p2 = [330, 98, 1]

l1_m = np.cross(p1, p2)
l1_m = l1_m/l1_m[-1]

p3 = [113, 91, 1]
p4 = [212, 156, 1]

l2_m = np.cross(p3, p4)
l2_m = l2_m/l2_m[-1]

p5 = [73, 129, 1]
p6 = [295, 75, 1]

l3_m = np.cross(p5, p6)
l3_m = l3_m/l3_m[-1]

p7 = [185, 71, 1]
p8 = [289, 136, 1]

l4_m = np.cross(p7, p8)
l4_m = l4_m/l4_m[-1]

#2 pairs of parallel lines in affine rectified image
lines = [[], []]
lines[0].append(l1_m)
lines[0].append(l3_m)
lines[1].append(l2_m)
lines[1].append(l4_m)

#transforming the parallel lines
nlines = 2
RHS = np.zeros((nlines,1))
A = np.zeros((nlines, 2))
for k in range(0,nlines):
    RHS[k] = -lines[0][k][1]*lines[1][k][1]
for k in range(0,nlines):
    A[k,0] = lines[0][k][0]*lines[1][k][0]
    A[k,1] = lines[0][k][0]*lines[1][k][1] + lines[0][k][1]*lines[1][k][0]
s = np.linalg.lstsq(A,RHS)[0]
S = np.array([[s[0][0], s[1][0]],[s[1][0],1]])

#lines transform as l' = H^-T * l
u,s,vh = np.linalg.svd(S,full_matrices=1,compute_uv=1)

A = np.linalg.cholesky(S)

H1 = np.zeros((3,3))
H1[0:2,0:2] = A
H1[2,2] = 1

res = cv2.warpPerspective(res, np.linalg.inv(H1).T, (img.shape[1], img.shape[0]))

cv2.imwrite('floor_metricRect.png', res)

#combined transformation
Hc = np.linalg.inv(H1).T #affine rectification
Hc[2, :2] = vLine[:2] #projective rectification

#performing rectification on the original image
res = cv2.warpPerspective(img, Hc, (img.shape[1], img.shape[0]))

cv2.imwrite('combined_rect.png', res)

#cv2.namedWindow("res", cv2.WINDOW_FREERATIO)
cv2.imshow("res", res)
cv2.imshow("orig", img)
cv2.waitKey(0)
cv2.destroyAllWindows()


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

#biblioteca para obter a distribuicao normal acumulada
from scipy.stats import norm

def calcula_u(N, M, L, sigma, K, T, vetoriza = True):
    delta_x     = 2*L/N
    delta_tal   = T/M

    u = np.zeros((N+1, M+1))

    if vetoriza:
        #Calculando a base

        for i in range(N+1):
            x_i = i*delta_x-L

            u[i][0] = K*max(np.e**x_i - 1, 0)

        for j in range(M+1):
            tal_j = j * delta_tal

            u[N][j] = K * np.e**(L+sigma**2*tal_j/2)
            u[0][j] = 0

        # Calculando as demais posicoes
        for j in range(M):
            tal_j = j * delta_tal

            u_i_menos_um    = u[0:-2, j]
            u_i             = u[1:-1, j]
            u_i_mais_um     = u[2:  , j]

            u[1:-1, j+1]    = u_i + (delta_tal/delta_x**2)*(sigma**2/2)*(u_i_menos_um-2*u_i+u_i_mais_um)

    else:
        for j in range(M+1):
            tal_j = j * delta_tal
            for i in range(N+1):
                x_i = i*delta_x-L

                if i == 0:
                    u[i][j] = 0

                elif i == N:
                    u[i][j] = K * np.e**(L+sigma**2*tal_j/2)

                elif j==0:
                    u[i][j] = K*max(np.e**x_i - 1, 0)

                else:
                    u[i][j] = u[i][j-1] + (delta_tal/delta_x**2)*(sigma**2/2)*(u[i-1][j-1]-2*u[i][j-1]+u[i+1][j-1])

    return u

def calcula_V(u, N, M, r, T):
    delta_tal   = T/M
    V = np.zeros((N+1, M+1))
    for j in range(M+1):
        tal_j = j * delta_tal
        V[:, j] = u[:, j] * np.e**(-r*tal_j)

    return V

def calcula_S(N, M, L, sigma, K, T, r):
    delta_x     = 2*L/N
    delta_tal   = T/M

    S1 = np.zeros((N+1, M+1)) # e^x_i
    S2 = np.zeros((N+1, M+1)) # e^((r-sigma^2/2)*tal_j)
    S  = np.zeros((N+1, M+1)) # K * (S1/S2)

    for j in range(M+1):
        tal_j = j * delta_tal
        S2[:, j] =  np.e**((r-sigma**2/2)*tal_j)

    for i in range(N+1):
        x_i = i*delta_x-L
        S1[i, :] = np.e**(x_i)

    S = K * (S1/S2)
    return S

def calcula_u_analitico(N, M, L, sigma, K, T):
    delta_x     = 2*L/N
    delta_tal   = T/M

    u = np.zeros((N+1, M+1))
    for j in range(M+1):
        tal_j = j * delta_tal
        for i in range(N+1):
            x_i = i*delta_x-L
            if j==0:
                u[i][j] = K*max(np.e**x_i-1, 0)
            else:
                d1 = (x_i + sigma**2*tal_j)/(sigma*np.sqrt(tal_j))
                d2 = x_i/(sigma*np.sqrt(tal_j))
                N1 = norm.cdf(d1)
                N2 = norm.cdf(d2)

                u[i][j] = K*np.e**(x_i + sigma**2*tal_j/2)*N1-K*N2
    return u

def calcula_aproximacao_V(V, S, t, N, M, L, sigma, K, T, r, interpola = True):
    delta_x     = 2*L/N
    delta_tal   = T/M

    tal = T-t
    x = np.log(S/K) + (r-sigma**2/2)*tal

    i_p = int((x+L)/delta_x)
    j_p = round(tal/delta_tal)

    if interpola:
        x_i         = i_p*delta_x-L
        x_i_mais_um = (i_p+1)*delta_x-L
        opcao =  ((x_i_mais_um-x)*V[i_p][j_p]-(x_i-x)*V[i_p+1][j_p])/(x_i_mais_um-x_i)
        return opcao
    else:
        return V[i_p][j_p]

def cenario_1(show_graph=True):
    arquivo = open("report_cenario_1.txt", "w")
    #============
    #=== Ex 1 ===
    #============
    N       = 1000
    L       = 10
    M       = 100
    K       = 1
    sigma   = 0.01
    T       = 1
    r       = 0.01
    u = calcula_u(N, M, L, sigma, K, T)
    V = calcula_V(u, N, M, r, T)

    S = 1
    t = 0

    preco_opcao_agora = calcula_aproximacao_V(V, S, t, N, M, L, sigma, K, T, r)
    imprime_report(arquivo, N, L, M, K, sigma, T, r, S, t, preco_opcao_agora)

    opcao_compra_1000 = preco_opcao_agora*1000

    print("Precificação da opção de compra de R$1000.00 do ativo para o tempo presente, considerando que o ativo tem preço de R$1.00 hoje: R${:.2f}".format(opcao_compra_1000))

    #============
    #=== Ex 2 ===
    #============

    gera_cenarios_lucro(V, 0.5, N, M, L, sigma, K, T, r, 1000, opcao_compra_1000, show_graph, "Cenários de lucro e prejuízo para diversos preços do ativo em t=0.5", "grafico_1_cenario_1.png")

    # Valor da opcao em funcao do preco do ativo em t=0
    gera_cenarios_preco_do_ativo(V, 0., N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=0", "grafico_2_cenario_1.png")

    # Valor da opcao em funcao do preco do ativo em t=0.5
    gera_cenarios_preco_do_ativo(V, 0.5, N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=0.5", "grafico_3_cenario_1.png")

    # Valor da opcao em funcao do preco do ativo em t=1.0
    gera_cenarios_preco_do_ativo(V, 1., N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=1.0", "grafico_4_cenario_1.png")

    #============
    #=== Ex 3 ===
    #============

    sigma = 0.02
    u = calcula_u(N, M, L, sigma, K, T)
    V = calcula_V(u, N, M, r, T)

    preco_opcao_agora = calcula_aproximacao_V(V, S, t, N, M, L, sigma, K, T, r)
    imprime_report(arquivo, N, L, M, K, sigma, T, r, S, t, preco_opcao_agora)

    opcao_compra_1000 = preco_opcao_agora*1000

    print("Precificação da opção de compra de R$1000.00 do ativo mudando apenas a volatilidade para 0.02: R${:.2f}".format(opcao_compra_1000))

    gera_cenarios_lucro(V, 0.5, N, M, L, sigma, K, T, r, 1000, opcao_compra_1000, show_graph, "Cenários de lucro e prejuízo para diversos preços do ativo em t=0.5 com sigma = 0.02", "grafico_5_cenario_1.png")
    # Valor da opcao em funcao do preco do ativo em t=0
    gera_cenarios_preco_do_ativo(V, 0., N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=0 com sigma = 0.02", "grafico_6_cenario_1.png")

    # Valor da opcao em funcao do preco do ativo em t=0.5
    gera_cenarios_preco_do_ativo(V, 0.5, N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=0.5 com sigma = 0.02", "grafico_7_cenario_1.png")

    # Valor da opcao em funcao do preco do ativo em t=1.0
    gera_cenarios_preco_do_ativo(V, 1., N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=1.0 com sigma = 0.02", "grafico_8_cenario_1.png")

    #============
    #=== Ex 4 ===
    #============

    r = 0.1
    sigma = 0.1
    u = calcula_u(N, M, L, sigma, K, T)
    V = calcula_V(u, N, M, r, T)

    preco_opcao_agora = calcula_aproximacao_V(V, S, t, N, M, L, sigma, K, T, r)
    imprime_report(arquivo, N, L, M, K, sigma, T, r, S, t, preco_opcao_agora)

    opcao_compra_1000 = preco_opcao_agora*1000

    print("Precificação da opção de compra de R$1000.00 do ativo com r = 0.1 e sigma = 0.1: R${:.2f}".format(opcao_compra_1000))

    gera_cenarios_lucro(V, 0.5, N, M, L, sigma, K, T, r, 1000, opcao_compra_1000, show_graph, "Cenários de lucro e prejuízo para diversos preços do ativo em t=0.5 com sigma = 0.1 e r = 0.1", "grafico_9_cenario_1.png")
    # Valor da opcao em funcao do preco do ativo em t=0
    #gera_cenarios_preco_do_ativo(V, 0., N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=0 com sigma = 0.1 e r = 0.1", "grafico_10_cenario_1.png")

    # Valor da opcao em funcao do preco do ativo em t=0.5
    #gera_cenarios_preco_do_ativo(V, 0.5, N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=0.5 com sigma = 0.1 e r = 0.1", "grafico_11_cenario_1.png")

    # Valor da opcao em funcao do preco do ativo em t=1.0
    #gera_cenarios_preco_do_ativo(V, 1., N, M, L, sigma, K, T, r,show_graph, "Valor da opção para diversos preços do ativo em t=1.0 com sigma = 0.1 e r = 0.1", "grafico_12_cenario_1.png")

    arquivo.close()
    return

def cenario_2(show_graph=True):
    arquivo = open("report_cenario_2.txt", "w")
    N       = 1000
    L       = 10
    M       = 1000
    K       = 5.7
    sigma   = 0.1692
    T       = 3/12
    r       = 0.1075
    u = calcula_u(N, M, L, sigma, K, T)
    V = calcula_V(u, N, M, r, T)

    S = 5.6376
    t = 0

    preco_opcao_dezembro = calcula_aproximacao_V(V, S, t, N, M, L, sigma, K, T, r)
    imprime_report(arquivo, N, L, M, K, sigma, T, r, S, t, preco_opcao_dezembro)
    opcao_compra_100_mil = preco_opcao_dezembro*100000
    print("Premio da opção de compra de US$100000.00 para o tempo presente: R${:.2f}".format(opcao_compra_100_mil))

    # Sabendo o valor do dolar em 1 de janeiro de 2022
    S = 5.5780503347
    t = 1/12
    preco_opcao_janeiro = calcula_aproximacao_V(V, S, t, N, M, L, sigma, K, T, r)
    imprime_report(arquivo, N, L, M, K, sigma, T, r, S, t, preco_opcao_janeiro)
    saldo = (preco_opcao_janeiro-preco_opcao_dezembro)*100000
    print("Prejuízo da compra de US$100000.00 em janeiro de 2022: R${:.2f}".format(saldo))

    # Sem saber o valor do dolar em 1 de janeiro de 2022
    gera_cenarios_lucro(V, 1/12, N, M, L, sigma, K, T, r, 100000, opcao_compra_100_mil, show_graph, "Cenários de lucro e prejuízo para diversos preços do dólar em t=1/12", "grafico_1_cenario_2.png")

    arquivo.close()
    return

def imprime_report(arquivo, N, L, M, K, sigma, T, r, S, t, V):
    arquivo.write("Parametros:\n")
    arquivo.write("N        = {}\n".format(N))
    arquivo.write("L        = {}\n".format(L))
    arquivo.write("M        = {}\n".format(M))
    arquivo.write("K        = {:.2f}\n".format(K))
    arquivo.write("sigma    = {:.2f}\n".format(sigma))
    arquivo.write("T        = {:.2f}\n".format(T))
    arquivo.write("r        = {:.2f}\n".format(r))
    arquivo.write("S0       = {:.2f}\n".format(S))
    arquivo.write("t        = {:.2f}\n".format(t))
    arquivo.write("Precificacao opcao:\n")
    arquivo.write("V        = {:.2f}\n".format(V))
    arquivo.write("\n\n")
    return

def gera_cenarios_lucro(V, t, N, M, L, sigma, K, T, r, valor_comprado, gasto_inicial, show_graph, titulo = "", nome_imagem = ""):
    k       = 100
    S_min   = K/2
    S_max   = 3*K/2
    delta_S = (S_max-S_min)/k
    k_i     = np.arange(k)
    S_list  = k_i*delta_S+S_min
    cenarios = []
    for S_i in S_list:
        resultado_i = calcula_aproximacao_V(V, S_i, t, N, M, L, sigma, K, T, r)*valor_comprado-gasto_inicial
        cenarios.append(resultado_i)

    if show_graph:
        fig, ax = plt.subplots(figsize = (11, 6))
        ax.plot(S_list, cenarios)
        ax.grid(visible = True, axis = "y", linestyle = "--")
        ax.set_title(titulo)
        ax.set_xlabel("Preço do ativo")
        ax.set_ylabel("Saldo final")
        plt.savefig(nome_imagem)
        plt.show()
    return cenarios

def gera_cenarios_preco_do_ativo(V, t, N, M, L, sigma, K, T, r, show_graph, titulo = "", nome_imagem = ""):
    k       = 10000
    S_min   = 2*K/3
    S_max   = 4*K/3
    delta_S = (S_max-S_min)/k
    k_i     = np.arange(k)
    S_list  = k_i*delta_S+S_min
    cenarios = []
    for S_i in S_list:
        resultado_i = calcula_aproximacao_V(V, S_i, t, N, M, L, sigma, K, T, r)
        cenarios.append(resultado_i)

    if show_graph:
        fig, ax = plt.subplots(figsize = (11, 6))
        ax.plot(S_list, cenarios)
        ax.grid(visible = True, axis = "y", linestyle = "--")
        ax.set_title(titulo)
        ax.set_xlabel("Preço do ativo")
        ax.set_ylabel("Valor da opção")
        plt.savefig(nome_imagem)
        plt.show()
    return cenarios

def compara_tempo_execucao():
    tic = time.process_time()
    u   = calcula_u(1000,500, 10, 0.2, 1, 1, vetoriza=False)
    toc = time.process_time()
    no_vectorization = (toc-tic)*1000
    tic = time.process_time()
    u   = calcula_u(1000,500, 10, 0.2, 1, 1)
    toc = time.process_time()
    vectorization = (toc-tic)*1000
    print("Tempo de execução do método sem vetorização {:.2f} ms".format(no_vectorization))
    print("Tempo de execução do método com vetorização {:.2f} ms".format(vectorization))
    return

def interface():
    print("===========================================")
    print("============ Escolha uma opcao ============")
    print("===========================================")
    print("c1 - Rodar o cenário 1")
    print("c2 - Rodar o cenário 2")
    print("c3 - Rodar o cenário 3")
    print("ti - Comparar o tempo de execução do método vetorizado com o sem")
    #print("an - Comparar o resultado obtido pelo método numérico com o analítico")
    print("ex - Finalizar o programa")
    res = input("Operação escolhida: ")
    if res == "c1":
        cenario_1()
        print()
        interface()

    elif res == "c2":
        cenario_2()
        print()
        interface()

    elif res == "c3":
        print("não implementado ainda =(")
        print()
        interface()

    elif res == "ti":
        compara_tempo_execucao()
        print()
        interface()

    elif res == "ex":
        print("ex")
        print()

    else:
        print("operacao invalida, selecione outra")
        print()
        interface()

        return

interface()


import numpy as np

"""
create_mujoco_actions create a list of discrete actions to apply to mujoco environments. This list contains all the
permutations with repetitions of each value of the action vector.

dim_actions is the dimension of the action space
high_vals is the array of the maximum value each element of the action vector can take
low_vals is the array of the minimum value each element of the action vector can take
elem_per_dim is the number of elements for each dimension. Must be >= 2 (highest and lowest values should be included)
from low_vals, to high_vals. Should take int values >= 0 and populate.

examples:
create_mujoco_actions(2, [1, 1], [-1, -1], 2)
creates [[-1, -1], [1, -1], [-1, 1], [1, 1]]

create_mujoco_actions(2, [1, 1], [-1, -1], 3)
creates [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, -1]]
"""

def create_grid(high_vals, low_vals, elem_per_dim):
    assert len(high_vals) == len(low_vals)
    assert elem_per_dim >= 2

    discr = (high_vals - low_vals) / (elem_per_dim-1)

    return recursive_helper(0, high_vals, low_vals, discr, [], [])

def recursive_helper(dim, high, low, discr, elem, res):

    for i in np.arange(low[dim], high[dim]+discr[dim], discr[dim]):
        new_elem = elem + [i]
        if dim < len(high) - 1:
            res = recursive_helper(dim + 1, high, low, discr, new_elem, res)
        else:
            res.append(new_elem)

    return res


#We import our libs:
import numpy as np
import matplotlib.pyplot as plt
from numba import jit

@jit
def solver(N):
    # Make the initial guess for solution matrix
    V = np.zeros((N,N))

    # Solver:
    iterations = 0
    eps = 1e-10 # Convergence threshold
    error = 1e4 # Large dummy error
    while iterations < 1e4 and error > eps:
        V_temp = np.copy(V)
        error = 0
        # we make this accumulate in the loop
        for j in range(1,N-1):
            for i in range(1,N-1):
                V[i,j] = 0.25*(V[i+1,j] + V[i-1,j] + V[i,j-1] + V[i,j+1] + rho[i,j]*ds**2)
                error += abs(V[i,j]-V_temp[i,j])
            iterations += 1
    print("iterations =", iterations)
    return V

# Set dimensions of the problem:
L = 1.0
N = 21
ds = L/N

# Define arrays used for plotting:
x = np.linspace(0,L,N)
y = np.copy(x)
X, Y = np.meshgrid(x,y)

# Make the charge density matrix:
rho0 = 1.0
rho = np.zeros((N,N))
rho[int(round(N/2.0)),int(round(N/2.0))] = rho0
# for j in range(round(N/2.0)-int(N/20.0),round(N/2.0)+int(N/20.0)):
#     rho[round(N/2.0)-int(N/30.0),j] = rho0
#     rho[round(N/2.0)+int(N/30.0),j] = -rho0

# Solver:
V = solver(N)

# Plotting:
eps = 3
zoomX = X[round(N/2.0)-eps:round(N/2.0)+eps]
zoomY = Y[round(N/2.0)-eps:round(N/2.0)+eps]
zoomV = V[round(N/2.0)-eps:round(N/2.0)+eps]
plt.figure(figsize=(5,3))
CS = plt.contour(zoomX, zoomY, zoomV, 30) # Make a contour plot
plt.clabel(CS, inline=1, fontsize=10)
plt.title("PDE solution of a point charge")
CB = plt.colorbar(CS, extend="both")
plt.show()

# Print matrix:
print(zoomV)


import numpy as np
import matplotlib.pyplot as plt
from colorsys import hls_to_rgb

def colorize(z):
    zy=np.flipud(z)
    r = np.abs(zy)
    arg = np.angle(zy)

    h = (arg + np.pi)  / (2 * np.pi) + 0.5
    l = 1.0 - 1.0/(1.0 + r**.75)
    s = .8

    c = np.vectorize(hls_to_rgb) (h,l,s) # --> tuple
    c = np.array(c)  # -->  array of (3,n,m) shape, but need (n,m,3)
    c = c.swapaxes(0,2)
    c = c.swapaxes(0,1)
    return c

def plot_complex(f, xbounds, ybounds, res):
    x = np.linspace(xbounds[0], xbounds[1], res)
    y = np.linspace(ybounds[0], ybounds[1], res)
    X,Y = np.meshgrid(x,y)
    Z=f(X+Y*1j)
    plt.pcolormesh(X, Y, np.angle(Z), cmap='hsv', vmin=-np.pi, vmax=np.pi)
    plt.show()

def plot_complex2(f, xbounds, ybounds, res):
    x = np.linspace(xbounds[0],xbounds[1], res)
    y = np.linspace(ybounds[0],ybounds[1], res)
    X,Y = np.meshgrid(x,y)
    Z=f(X+Y*1j)
    Zc = colorize(Z)
    plt.imshow(Zc, extent=(xbounds[0], xbounds[1], ybounds[0], ybounds[1]))
    plt.show()

################################################
################################################
# PLOTS

def check_plot():
    f = lambda x:np.sqrt(x**2+1)
    x = np.linspace(-3, 3, 401)
    y = np.linspace(-3, 3, 401)
    X,Y = np.meshgrid(x,y)
    Z=f(X+Y*1j)
    plt.pcolormesh(X, Y, np.angle(Z), cmap='hsv', vmin=-np.pi, vmax=np.pi)
    plt.gca().set_aspect('equal', adjustable='box')
    plt.savefig('check_plot.png', bbox_inches='tight', pad_inches=0)
    plt.close()

def plot_id():
    f = lambda x:x
    x = np.linspace(-1, 1, 401)
    y = np.linspace(-1, 1, 401)
    X,Y = np.meshgrid(x,y)
    Z=f(X+Y*1j)
    plt.pcolormesh(X, Y, np.angle(Z), cmap='hsv', vmin=-np.pi, vmax=np.pi)
    plt.gca().set_aspect('equal', adjustable='box')
    plt.savefig('Identity.png', bbox_inches='tight', pad_inches=0)
    plt.close()

def zeros():
    f = lambda x:x**3-1j*x**4-3*x**6
    x = np.linspace(-1, 1, 401)
    y = np.linspace(-1, 1, 401)
    X,Y = np.meshgrid(x,y)
    Z=f(X+Y*1j)
    plt.pcolormesh(X, Y, np.angle(Z), cmap='hsv', vmin=-np.pi, vmax=np.pi)
    plt.gca().set_aspect('equal', adjustable='box')
    plt.savefig('zeros.png', bbox_inches='tight', pad_inches=0)
    plt.close()

def essential_pole():
    f = lambda x : np.exp(1/x)
    x = np.linspace(-1, 1, 803)
    y = np.linspace(-1, 1, 803)
    X,Y = np.meshgrid(x,y)
    Z=f(X+Y*1j)
    plt.pcolormesh(X, Y, np.angle(Z), cmap='hsv', vmin=-np.pi, vmax=np.pi)
    plt.gca().set_aspect('equal', adjustable='box')
    plt.savefig('essential_pole.png', bbox_inches='tight', pad_inches=0)
    plt.close()

if __name__=='__main__':
	check_plot()
	plot_id()
	zeros()
	essential_pole()


import numpy as np
import xlsxwriter as xl
import time

# Função utilizada: f(x) = 3 * cos(x) - (e^x)/3 // Intervalo: [-5; 2]// h: 0.75 // Precisão: 10^-5

# Utilizando o método da Secante para o intervalo [-5; -4.25]:

tempo_inicial = time.time()

def test(x):
    x_barra_aplicado = 3 * np.cos(x) - (np.exp(x)/3)
    return x_barra_aplicado

"""# Achando os dois valores de f(x) com os valores iniciais escolhidos:
x_barra_0 = test(-5)  # 0.8487405740566503
x_barra_1 = test(-4.25)    # -1.3430172143777117 """

def secante():
    c = 2
    x_barra = [-5, -4.25]  # Os dois vlores iniciais são os dois extremos de I
    x_barra_aplicado = [0.8487405740566503, -1.3430172143777117]
    def funcao_iteracao():
        resultado = x_barra[-1] - (x_barra_aplicado[-1] * (x_barra[-1] - x_barra[-2])) / (x_barra_aplicado[-1] - x_barra_aplicado[-2])
        return resultado
    while True:
        x_barra_atual = funcao_iteracao()
        x_barra.append(x_barra_atual)
        x_barra_aplicado_atual = test(x_barra_atual)
        x_barra_aplicado.append(x_barra_aplicado_atual)
        if x_barra_aplicado_atual < 0:
            modulo_x_aplicado_atual = x_barra_aplicado_atual * (-1)
        else:
            modulo_x_aplicado_atual = x_barra_aplicado_atual
        if modulo_x_aplicado_atual < 10 ** -5:
            break
        else:
            c+= 1
    return x_barra[-1], x_barra, x_barra_aplicado, c

raiz_aproximada = secante()[0]
lista_x_barra = secante()[1]
lista_x_aplicado = secante()[2]
iteracoes = secante()[3]

print('Questao teste, intervalo [-5; -4.25]:')
print(f'A raiz aproximada é: {raiz_aproximada}')
print(f'Para encontrar essa raiz, foram necessárias {iteracoes} iterações.')
print(lista_x_barra)
print(lista_x_aplicado)

"""# Criando a tabela com os valores obtidos
# Criando o arquivo
outWorkbook = xl.Workbook("Questao_03_1o_intervalo_secante.xlsx")
outSheet = outWorkbook.add_worksheet()

# Declarando os títulos

# Escrevendo os dados no arquivo
outSheet.write("A1", "x_barra")
outSheet.write("B1", "f(x_barra)")

for number, item in enumerate(lista_x_barra):
    outSheet.write(number + 1, 0, item)
for number, item in enumerate(lista_x_aplicado):
    outSheet.write(number + 1, 1, item)
outWorkbook.close()"""

tempo_final = time.time()
tempo_gasto = tempo_final - tempo_inicial
print(f'Foram gastos {tempo_gasto}s.')


import csv
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
def plot(data, cluster_value, k):
    x_val = data[:,0]
    y_val = data[:,1]
    colors = cm.rainbow(np.linspace(0,1,k))
    for i in range(k):
        x = x_val[cluster_value[:] == i]
        y = y_val[cluster_value[:] == i]
        color = colors[i,:]
        plt.scatter(x,y, c=color)
    plt.show()

def read_in(filename):
    data = np.genfromtxt(filename, delimiter=None)
    return data
def kmeans(data, k):
    cluster_value = np.random.randint(0,k, data.shape[0])
    cluster_mean = np.zeros((k,2))
    repeat = True
    previous_loss = 2e10
    while (repeat == True):
        for i in range(10):
            for n in range(k):
                cluster_mean[n,:] = np.mean(data[cluster_value[:] == n], axis=0)
            data_distance = np.zeros((data.shape[0], k))
            loss = 0
            for i in range(data.shape[0]):
                for j in range(k):
                    data_distance[i,j] = np.linalg.norm(data[i] - cluster_mean[j])
            for i in range(data.shape[0]):
                loss += data_distance[i,cluster_value[i]]
            if (previous_loss - loss < 1): #algorithm has converged
                repeat = False
            previous_loss = loss
            for i in range(data.shape[0]):
                cluster_value[i] = np.argmin(data_distance[i,:])
    return cluster_value, previous_loss
def cmeans(data, c, m): #fuzzy c-means
    one = np.ones(c)
    cluster = np.zeros(data.shape[0])
    cluster_value = np.zeros((data.shape[0], c))
    previous_loss = 2e10
    for i in range(data.shape[0]):
        cluster_value[i,:] = np.random.dirichlet(one)
    repeat = True
    while(repeat == True): #repeat until convergence is met
        centroid = np.zeros((c,2))
        for i in range(c):
            for j in range(data.shape[0]):
                centroid[i,:] += np.multiply(np.power(cluster_value[j,i], m), data[j,:])
            sum = 0
            for j in range(data.shape[0]):
                sum += np.power(cluster_value[j,i], m)
            centroid[i,:] = np.divide(centroid[i,:], sum)
        for i in range(data.shape[0]):
            for j in range(c):
                sum = 0
                temp = np.abs(np.linalg.norm(data[i,:] - centroid[j,:]))
                for k in range(c):
                    temp2 = np.abs(np.linalg.norm(data[i,:] - centroid[k,:]))
                    val = temp / temp2
                    val = np.power(val, (2/(m-1)))
                    sum += val
                cluster_value[i,j] = 1 / sum
        for i in range(data.shape[0]):
            cluster[i] = np.argmax(cluster_value[i,:])
        loss = 0
        for i in range(data.shape[0]):
            loss += np.linalg.norm(data[i,:] - centroid[int(cluster[i])]) #calculate loss
        if (previous_loss - loss < 1):
            repeat = False
        previous_loss = loss
    return cluster, loss
def main():
    data = read_in('data/cluster_dataset.txt')
    k = 3
    loss = np.NaN
    cluster_value = None
    for i in range(10):
        new_cluster_value, new_loss = cmeans(data, k, 3)
        if (np.isnan(loss)):
            loss = new_loss
            cluster_value = new_cluster_value
        if (new_loss < loss):
            loss = new_loss
            cluster_value = new_cluster_value
    print("c-means loss: ")
    print(loss)
    plot(data, cluster_value, k)
    loss = np.NaN
    for i in range(10):
        new_cluster_value, new_loss = kmeans(data, k)
        if (np.isnan(loss)):
            loss = new_loss
            cluster_value = new_cluster_value
        if (new_loss < loss):
            loss = new_loss
            cluster_value = new_cluster_value
    print("k-means loss: ")
    print(loss)
    plot(data, cluster_value, k)

if __name__ == '__main__':
    main()


import numpy as np
import matplotlib.pyplot as plt

G_z = 0.08 #T/m
gamma = 2*np.pi*42.58e6 #rad/(sT)
z_0 = 0.001 #m
tau_m = 0.005 #s
omega_0 = gamma*G_z*z_0

t_min = 0
t_max = tau_m
N = 1000
t = np.linspace(t_min, t_max, N) #s
#y = np.sin(omega_0*(t))/(np.pi*gamma*(t)) #center
#y = np.sin(omega_0*(t-tau_m/2))/(np.pi*gamma*(t-tau_m/2)) #case1
#y = np.sin(omega_0/2*(t-tau_m/2))/(np.pi*gamma*(t-tau_m/2)) #case2
#y = 2*np.sin(omega_0/2*(t-tau_m/2))/(np.pi*gamma*(t-tau_m/2))*np.cos(3*omega_0/2*(t-tau_m/2)) #case3
#y = -2*np.sin(omega_0/2*(t-tau_m/2))/(np.pi*gamma*(t-tau_m/2))*np.sin(3*omega_0/2*(t-tau_m/2)) #case4

"""
#signal of rf pulse

plt.plot(t, y, label="$H(t)$", color="blue", linewidth=2)

plt.xlabel("t/s")
plt.ylabel("H(t)/T")

plt.title("rf pulse")

plt.xlim(t_min, t_max)
#plt.ylim(-0.0001, 0.0001)

plt.legend()

plt.show()
"""

#ifft of rf pulse

tt = np.linspace(-tau_m/2, tau_m/2, N) #s
#yy = np.fft.fft(np.sin(omega_0*(tt))/(np.pi*gamma*(tt))) #case1
yy = np.fft.fft(np.sin(omega_0/2*(tt))/(np.pi*gamma*(tt))) #case2, and xx should plus 0.0015
#yy = np.fft.fft(2*np.sin(omega_0/2*(tt))/(np.pi*gamma*(tt))*np.cos(3*omega_0/2*(tt))) #case3

yyf = abs(yy) #case1,2,3

"""
#case4
yy = np.fft.fft(-2*np.sin(omega_0/2*(tt))/(np.pi*gamma*(tt))*np.sin(3*omega_0/2*(tt))) # case4
c = 1
yyf = []
for yyy in yy:
    yyyf = abs(yyy)
    yyyphase = np.arctan(np.imag(yyy)/np.real(yyy))
    if yyyphase>0:
        c = -1
        yyyf = c*yyyf
    yyf.append(yyyf)
"""

xx = np.fft.fftfreq(N,tau_m/N)/gamma/G_z*2*np.pi #z-axis
#print(yyf)

plt.plot(np.fft.fftshift(xx),np.fft.fftshift(yyf), label="$iFFT$", color="blue", linewidth=2)
plt.title("iFFT of rf pulse")
plt.xlim(-0.003, 0.003)
plt.xlabel("z_0/m")
plt.ylabel("Relative Intensity")
plt.show()


import numpy as np
import math

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

def base_sigmoid(x):
    return 1 / (1 + math.exp(-x))

print(base_sigmoid(3))

# 0 - 1之间的函数
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

print(sigmoid(a))

def sigmoid_derivative(x):
    s = 1 / (1 + np.exp(-x))
    ds = s * (1 - s)
    return ds

print("sigmoid_derivative(x) = " + str(sigmoid_derivative(a)))

def image2vector(x):
    return x.reshape(x.shape[0] * x.shape[1] * x.shape[2], 1)

image = np.array([[[0.67826139, 0.29380381],
                   [0.90714982, 0.52835647],
                   [0.4215251, 0.45017551]],

                  [[0.92814219, 0.96677647],
                   [0.85304703, 0.52351845],
                   [0.19981397, 0.27417313]],

                  [[0.60659855, 0.00533165],
                   [0.10820313, 0.49978937],
                   [0.34144279, 0.94630077]]])

print(image2vector(image))

def normalizeRows(x):
    norm = np.linalg.norm(x, axis=1, keepdims=True)
    x = x / norm
    return x

b = np.array([
    [0, 3, 4],
    [1, 6, 4],
])
print(normalizeRows(b))

def L1(y_hat, y):
    loss = np.sum(np.abs(np.subtract(y, y_hat)))
    return loss

def L2(y_hat, y):
    loss = np.sum(np.square(np.subtract(y, y_hat)))
    return loss


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MIT License

Copyright (c) 2022 Jongrae.K

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

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

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

from sympy import symbols, Matrix, simplify, expand

def quat_x_quat(qp, q):
    qp_v = Matrix(qp[0:3])
    qp4 = qp[-1]
    q_v = Matrix(q[0:3])
    q4 = q[-1]

    qxq_v = qp4*q_v  + qp_v*q4 - qp_v.cross(q_v)
    qxq_4 = qp4*q4 - qp_v.dot(q_v)

    qxq = Matrix([qxq_v, qxq_4])
    return qxq

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

q1, q2, q3, q4 = symbols('q1 q2 q3 q4')
w1, w2, w3 = symbols('w1 w2 w3')

q = Matrix([[q1],[q2],[q3],[q4]])
qinv = Matrix([[-q1],[-q2],[-q3],[q4]])

w = Matrix([[w1],[w2],[w3]])
wx = Matrix([[0, -w3, w2],[w3, 0, -w1],[-w2, w1, 0]])

Omega = Matrix([[-wx, w],[-w1,-w2,-w3,0]])

dqdt = (1/2)*Omega@q

# obtain dqinv/dt: Equation (4) in the solution
qinv_dot = -1*simplify(expand(quat_x_quat(qinv,quat_x_quat(dqdt, qinv))))

p1, p2, p3, p4 = symbols('p1,p2,p3,p4')
p = Matrix([[p1],[p2],[p3],[p4]])

# prove dqdt (x) p = (1/2) Omega (q (x) p): Equation (6) in the solution
out1 = simplify(expand(quat_x_quat(dqdt,p)-(1/2)*Omega*quat_x_quat(q,p)))
print('out1 must be equal to zero: ', out1)

r1, r2, r3, r4 = symbols('r1,r2,r3,r4')
r = Matrix([[r1],[r2],[r3],[r4]])

# prove p (x) (q (x) r) = (p (x) q) (x) r: Equation (7) in the solution
out2 = simplify(expand(quat_x_quat(p,quat_x_quat(q,r)) - quat_x_quat(quat_x_quat(p,q),r)))
print('out2 must be equal to zero: ', out2)