import math, cmath
import numpy as np
from numpy import pi

def menu():         #Menu interativo para realizar varias operações com apenas uma execução
    print("""
            Temos as seguintes opções:
	    (1) = Converter de Polar para Retângular
	    (2) = Converter de Retangular para Polar
	    (0) = Para SAIR

	""")
    return int(input('Escolha uma opção: '))

def getRet():       #Função que coleta do usuário os valores reais e imaginários da forma retangular
    real = float(input("Digite a parte real: "))
    imag = float(input("Digite a parte imaginária: "))
    return real, imag

def getPolar():     #Função que coleta do usuário os valores de módulo e fase da forma polar
    mod = float(input("Digite o módulo R: "))
    fase = float(input("Digite o angulo phi: "))
    return mod, fase

def convRet2Pol(x): #Função de conversão de Retangular para Polar
    modulo = abs(x) #retira o módulo de x, tirando a raiz da soma dos quadrados da parte real e da imaginária
    fase = np.arctan2(x.imag,x.real) * 180 / pi     #Para encontrar a fase em graus, fazemos o arctg da parte imaginaria sobre a real e multiplicamos por 180/pi
    return modulo, fase

def convPol2Ret(modulo,fase):   #Função de conversão de Polar para Retangular
    x = modulo * np.cos(math.radians(fase)) #Para achar a parte real devemos multiplicar o módulo pelo cosseno da fase
    y = modulo * np.sin(math.radians(fase)) #Para achar a parte real devemos multiplicar o módulo pelo seno da fase
    retangular = complex(x,y)               #Por fim juntamos os dois valores obtidos em apenas uma variável do tipo complexa
    return retangular

while True:     #laço de repetição do meno
    escolhaMenu = menu()
    if escolhaMenu == 0:    #condição de saída do programa
        print('Até a próxima!!')
        exit()

    elif escolhaMenu == 1:      #Questão 1: Converte da forma Polar Exponencial para a forma Retangular
        modulo, fase = getPolar()   #Chamamos a função para obter o modulo e a fase
        retangular = convPol2Ret(modulo,fase)   #chamamos a função de conversão para obter o numero na forma retangular
        print('Para ',modulo,'e^j',fase,'-> Convertendo para Coordenada Retangular temos:\n ', retangular)

    elif escolhaMenu == 2:      #Questão 2: Converte de Retangular para polar na forma modulo e fase e na formula exponencial
        parteReal, parteImag = getRet() #Chamamos a função para obter as partes reais e imaginária do número
        numero = complex(parteReal,parteImag)   #une ambos números para formar um único numero do tipo complexo
        modulo,fase = convRet2Pol(numero)   #Chama a função de conversão de Retangular para polar
        print('Para',numero,'-> Convertendo para Coordenada Polar temos:\n ',modulo,'/__',fase, '=', modulo,'e^j', fase)

    else:   #Se não digitar uma opção válida, o menu irá se repetir
        print('\nEssa não é uma opção válida! Tente Novamente \n')


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

class NPoints:
    ''' This class is an example code of the paper
    UNIFIED INTERPOLATION AND EXTRAPOLATION FUNCTION BY DIRECT TAYLOR EXPANSION.
    This package predicts the poitns which within the given range of give data and outside the range of same data set with presize points.
    Using very small give data set in very small range this algorthm can predict the point which is extremely far away from given range.

    '''

    def __init__(self,x_domain,y_domain):
        ''' Check len of data'''
        if (len(x_domain)%2==0 and len(y_domain)%2==0 ):
            x_domain=x_domain[:-1]
            y_domain=y_domain[:-1] #drop the last element to get odd number..
            self.x_domain = x_domain  # Store x domain
            self.y_domain = y_domain  # Store y domain
            self.n = int((len(self.y_domain) - 1) / 2) #middle point index
            self.del_x = self.x_domain[1] - self.x_domain[0]
            self.taylor_coef=NPoints.taylor_coef_matrix(self)
            self.taylor_coefficent_matrix = np.matmul(np.linalg.inv(NPoints.taylor_coef_matrix(self)), self.y_domain)
        else:
            self.x_domain=x_domain # Store x domain
            self.y_domain=y_domain # Store y domain
            self.n = int((len(self.y_domain) - 1) / 2)  # middle point index
            self.del_x=self.x_domain[1]-self.x_domain[0]
            self.taylor_coef = NPoints.taylor_coef_matrix(self)
            self.taylor_coefficent_matrix=np.matmul(np.linalg.inv(NPoints.taylor_coef_matrix(self)),self.y_domain)

    @staticmethod
    def fact(num):
        ''' Static function which caluclates the factorial of feeded number'''
        factorial = 1
        if num == 0:
            return factorial
        else:
            for i in range(1, num + 1):
                factorial = factorial * i
            return factorial

    def taylor_coefficients(self,n, delta_x, m):
        coef_array = []
        for i in range(m):
            coef_array.append((n * delta_x) ** (i) / (NPoints.fact(i)))
        return coef_array

    def taylor_coef_matrix(self):
        n = int((len(self.y_domain) - 1) / 2)
        taylor_coef_matr = np.zeros((len(self.y_domain), len(self.y_domain)), dtype=float)
        mirror_array = []
        for i in range(-n, n + 1):
            mirror_array.append(i)

        for i in range(len(taylor_coef_matr)):
            taylor_coef_matr[i] =self.taylor_coefficients(mirror_array[i], self.del_x, len(mirror_array))
        return taylor_coef_matr

    def UIE_function(self,betta):
        alfa = betta - self.x_domain[self.n]
        #f_x = self.y_domain[n]
        f_x=0
        for i in range( len(self.taylor_coefficent_matrix)):
            #f_x += (((alfa ** (i)) / (NPoints.fact(i))) * (self.taylor_coefficent_matrix[i]))
            #f_x += (((alfa ** (i)) / math.factorial(i)) * (self.taylor_coefficent_matrix[i]))
            f_x += (alfa ** (i))* ((self.taylor_coefficent_matrix[i])/ math.factorial(i))
        return f_x

    def analytical_function(self):
        string ="f(x)="
        x_0=self.x_domain[self.n]
        string=string+str(self.y_domain[self.n])
        for i in range(1,len(self.taylor_coefficent_matrix)):
            string+="+(x-"+str(x_0)+")^("+str(i)+")*"+str((float(self.taylor_coefficent_matrix[i]))/(NPoints.fact(i)))

        return string


import numpy as np
from pylab import *

style.use('postgkyl.mplstyle')

def buneman_k2w_cold(k, m):
    """
    Dispersion relation of two-fluid Bunenman instability in
    a cold plasma.

    Compute complex frequency w as roots of DR=0. DR is
    the dispersion relation equation rearranged as a
    polynommial equation where w is the unknown.
    The roots are ordered by imagninary parts (growth rate)
    first and then by real parts, both from large to small.

    Set se and si to zero for a cold plasma.

    Args:
        k: k*v0/wpe
        m: mi/me

    Returns:
        ws: Complex frequency for the given k.
    """
    ws = np.roots((m, -2 * k * m, k**2 * m - (m + 1), 2 * k,  -k**2))
    return ws[np.argsort(ws.imag + 1j * ws.real)[::-1]]

nk = 500
ks = np.linspace(0.01, 1.75, nk)

ws25 = np.zeros((nk, 4), dtype=np.complex128)
for i, k in enumerate(ks):
    ws25[i,:] = buneman_k2w_cold(k, 25.0)

ws200 = np.zeros((nk, 4), dtype=np.complex128)
for i, k in enumerate(ks):
    ws200[i,:] = buneman_k2w_cold(k, 200.0)

ws1836 = np.zeros((nk, 4), dtype=np.complex128)
for i, k in enumerate(ks):
    ws1836[i,:] = buneman_k2w_cold(k, 1836.2)

# plot imag part of dispersion relation
figure(1)
plot(ks, imag(ws25[:,0]), 'r.', markersize=2)
plot(ks, imag(ws25[:,1]), 'r.', markersize=2)
plot(ks, imag(ws25[:,2]), 'r.', markersize=2)
plot(ks, imag(ws25[:,3]), 'r.', markersize=2)

plot(ks, imag(ws200[:,0]), 'k.', markersize=2)
plot(ks, imag(ws200[:,1]), 'k.', markersize=2)
plot(ks, imag(ws200[:,2]), 'k.', markersize=2)
plot(ks, imag(ws200[:,3]), 'k.', markersize=2)

plot(ks, imag(ws1836[:,0]), 'm.', markersize=2)
plot(ks, imag(ws1836[:,1]), 'm.', markersize=2)
plot(ks, imag(ws1836[:,2]), 'm.', markersize=2)
plot(ks, imag(ws1836[:,3]), 'm.', markersize=2)

xlabel(r'$k V_0/\omega_{pe}$')
ylabel(r'$\gamma/\omega_{pe}$')
grid()
savefig('gamma-vs-k-cold.png', dpi=150)

figure(2)
plot(real(ws25[:,0]), imag(ws25[:,0]), 'r.', markersize=2)
plot(real(ws25[:,1]), imag(ws25[:,1]), 'r.', markersize=2)
plot(real(ws25[:,2]), imag(ws25[:,2]), 'r.', markersize=2)
plot(real(ws25[:,3]), imag(ws25[:,3]), 'r.', markersize=2)

plot(real(ws200[:,0]), imag(ws200[:,0]), 'k.', markersize=2)
plot(real(ws200[:,1]), imag(ws200[:,1]), 'k.', markersize=2)
plot(real(ws200[:,2]), imag(ws200[:,2]), 'k.', markersize=2)
plot(real(ws200[:,3]), imag(ws200[:,3]), 'k.', markersize=2)

plot(real(ws1836[:,0]), imag(ws1836[:,0]), 'm.', markersize=2)
plot(real(ws1836[:,1]), imag(ws1836[:,1]), 'm.', markersize=2)
plot(real(ws1836[:,2]), imag(ws1836[:,2]), 'm.', markersize=2)
plot(real(ws1836[:,3]), imag(ws1836[:,3]), 'm.', markersize=2)

axis('image')
gca().set_xlim([0.0, 0.5])
grid()
xlabel(r'$\omega/\omega_{pe}$')
ylabel(r'$\gamma/\omega_{pe}$')

savefig('omega-vs-gamma-cold.png', dpi=150)

show()


# 切片索引
import numpy as np
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8])
print(arr[1:6]) # output: [1 2 3 4 5]

# 对于二维数组,切片方式稍显不同
arr2 = np.array([[1, 2, 3],
					[4, 5, 6],
					[7, 8, 9]])
print(arr2[:2])
'''
output:
[[1 2 3]
 [4 5 6]]
'''
print(arr2[:, 1:])
'''
output:
[[2 3]
 [5 6]
 [8 9]]
'''
arr2[1:, 1:] = 0
print(arr2)

# 数组转置和轴对换
arr = np.arange(15).reshape((3, 5))
print(arr)
print(arr.T)
'''
output:
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]]

[[ 0  5 10]
 [ 1  6 11]
 [ 2  7 12]
 [ 3  8 13]
 [ 4  9 14]]
'''
# 在进行矩阵计算时,经常需要用到转置操作,例如利用np.dot计算矩阵內积
arr = np.random.randn(6, 3)
print(arr)
print(np.dot(arr.T, arr))
'''
output:
[[-1.27395567  0.00955796 -1.28750516]
 [-0.08475422  0.52563736 -2.05030602]
 [ 1.17591993 -0.015422    0.88881672]
 [ 2.15608896  0.27987763 -0.92061775]
 [-0.11040883 -0.82964402 -0.06859076]
 [-1.66814933  0.36754     0.32394515]]

[[  1.04565659e+01   7.06805306e-03   3.41424263e-01]
 [  7.06805306e-03   1.17835017e+00  -1.18542228e+00]
 [  3.41424263e-01  -1.18542228e+00   7.60860167e+00]]
'''
# 对于高维数组, transpose需要得到一个由轴编号组成的元组才能对这些轴进行转置
arr = np.arange(16).reshape((2, 2, 4))
print(arr)
'''
output:
[[[ 0  1  2  3]
  [ 4  5  6  7]]

 [[ 8  9 10 11]
  [12 13 14 15]]]
'''
print(arr.transpose((1, 0, 2))) # 0轴变成了1轴, 1轴换成了0轴, 2轴不变
'''
output:
[[[ 0  1  2  3]
  [ 8  9 10 11]]

 [[ 4  5  6  7]
  [12 13 14 15]]]
'''

print(arr.transpose((1, 2, 0)))
'''
[[[ 0  8]
  [ 1  9]
  [ 2 10]
  [ 3 11]]

 [[ 4 12]
  [ 5 13]
  [ 6 14]
  [ 7 15]]]
'''
# 简单的转置可以使用.T,就是进行轴对换.
# ndarray还有一个swapaxes方法, 它需要接受一个轴编号
print(arr)
'''
output:
[[[ 0  1  2  3]
  [ 4  5  6  7]]

 [[ 8  9 10 11]
  [12 13 14 15]]]
'''
print(arr.swapaxes(1, 2))
'''
output:
[[[ 0  4]
  [ 1  5]
  [ 2  6]
  [ 3  7]]

 [[ 8 12]
  [ 9 13]
  [10 14]
  [11 15]]]
'''
print(arr.transpose(0, 2, 1))
'''
output:
[[[ 0  4]
  [ 1  5]
  [ 2  6]
  [ 3  7]]

 [[ 8 12]
  [ 9 13]
  [10 14]
  [11 15]]]
'''

# 用于数组的文件输入输出
'''
Numpy能够读写次爬上的文本数据或二进制数据.
np.save和np.load是读写磁盘数组数据的两个主要函数.
默认情况下, 数组是以未亚索的原始二进制格式保存在扩展名为.npy文件中的
'''
arr = np.arange(10)
np.save('some_array', arr)
# 如果文件路径末尾没有扩展名.npy,则该扩展名会被自动添加
#　然后就可以通过np.load读取磁盘上的数组
print(np.load('some_array.npy'))# output: [0 1 2 3 4 5 6 7 8 9]
# 通过np.savez可以将多个数组保存到一个未压缩的文件中, 将数组以关键字参数的形式传入即可
np.savez('array_achive.npz', a=arr, b=arr)
# 加载.npz文件时,会得到一个类似字典的对象,该对象会对各个数组进行延迟加载
arch = np.load('array_achive.npz')
print(dir(arch))
print(arch.items(), '\n', arch['b'])
'''
output:
[('a', array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])), ('b', array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))]
 [0 1 2 3 4 5 6 7 8 9]
'''
# 如果数据压缩的很好,就可以使用numpy.savez_compressed:
np.savez_compressed('arrays_compressed.npz', a=arr, b=arr.T)
arch = np.load('arrays_compressed.npz')
print(arch['a'])
print('\n')
print(arch['b'])
print(arch['a'] * arch['b'])


import io, sys
import numpy as np
from heapq import *

def load_vectors(filename):
    fin = io.open(filename, 'r', encoding='utf-8', newline='\n')
    n, d = map(int, fin.readline().split())
    data = {}
    for line in fin:
        tokens = line.rstrip().split(' ')
        data[tokens[0]] = np.asarray([float(x) for x in tokens[1:]])
    return data

## This function computes the cosine similarity between vectors u and v

def cosine(u, v):
    return np.dot(u,v)/(np.linalg.norm(u)*np.linalg.norm(v))

## This function returns the word corresponding to
## nearest neighbor vector of x
## The list exclude_words can be used to exclude some
## words from the nearest neighbors search

def nearest_neighbor(x, word_vectors, exclude_words=[]):
    best_score = -1.0
    best_word = ''

    for k in word_vectors.keys():
        score = cosine(x,np.array(word_vectors[k]))

        if score > best_score and k not in exclude_words :
            best_score = score
            best_word = k

    return best_word

## This function return the words corresponding to the
## K nearest neighbors of vector x.
## You can use the functions heappush and heappop.

def knn(x, vectors, k):
    heap = []

    exclude_words=[]
    for i  in range(k+1):
        best_word = nearest_neighbor(x, word_vectors,exclude_words)
        exclude_words.append(best_word)
        heap.append((cosine(x,word_vectors[best_word]),best_word))

    return heap[1:]

## This function return the word d, such that a:b and c:d
## verifies the same relation

def analogy(a, b, c, word_vectors):
    result_word = None
    result_score = float('-Inf')

    x_a = word_vectors[a]/np.linalg.norm(word_vectors[a])
    x_b = word_vectors[b]/np.linalg.norm(word_vectors[b])
    x_c = word_vectors[c]/np.linalg.norm(word_vectors[c])
    for key in word_vectors:
        if True in [i in key for i in [a,b,c]] :
            continue
        normalized = word_vectors[key]/np.linalg.norm(word_vectors[key])
        result = np.dot((x_c+x_b-x_a),normalized)

        if result > result_score:
            result_score = result
            result_word = key

    return result_word

## Compute the association strength between:
##   - a word w
##   - two sets of attributes A and B

def association_strength(w, A, B, vectors):
    strength_a = 0.0
    strength_b = 0.0
    ## FILL CODE
    for a in A:
        strength_a += ((1/len(A))*cosine(vectors[w],vectors[a]))
    for b in B:
        strength_b += ((1/len(B))*cosine(vectors[w],vectors[b]))

    return strength_a - strength_b

## Perform the word embedding association test between:
##   - two sets of words X and Y
##   - two sets of attributes A and B

def weat(X, Y, A, B, vectors):
    score_x = 0.0
    score_y = 0.0
    ## FILL CODE
    for i in X:
        score_x += association_strength(i,A,B,vectors)
    for i in Y:
        score_y += association_strength(i,A,B,vectors)

    return score_x - score_y

######## MAIN ########

print('')
print(' ** Word vectors ** ')
print('')

word_vectors = load_vectors(sys.argv[1])

print('similarity(apple, apples) = %.3f' %
      cosine(word_vectors['apple'], word_vectors['apples']))
print('similarity(apple, banana) = %.3f' %
      cosine(word_vectors['apple'], word_vectors['banana']))
print('similarity(apple, tiger) = %.3f' %
      cosine(word_vectors['apple'], word_vectors['tiger']))

print('')
print('The nearest neighbor of cat is: ' +
      nearest_neighbor(word_vectors['cat'], word_vectors))

knn_cat = knn(word_vectors['cat'], word_vectors, 5)
print('')
print('cat')
print('--------------')
for score, word in knn(word_vectors['cat'], word_vectors, 5):
    print( word + '\t%.3f' % score)

print('')
print('france - paris + rome = ' + analogy('paris', 'france', 'rome', word_vectors))

## A word about biases in word vectors:

print('')
print('similarity(genius, man) = %.3f' %
      cosine(word_vectors['man'], word_vectors['genius']))
print('similarity(genius, woman) = %.3f' %
      cosine(word_vectors['woman'], word_vectors['genius']))

## Replicate one of the experiments from:
##
## Semantics derived automatically from language corpora contain human-like biases
## Caliskan, Bryson, Narayanan (2017)

career = ['executive', 'management', 'professional', 'corporation',
          'salary', 'office', 'business', 'career']
family = ['home', 'parents', 'children', 'family',
          'cousins', 'marriage', 'wedding', 'relatives']
male = ['john', 'paul', 'mike', 'kevin', 'steve', 'greg', 'jeff', 'bill']
female = ['amy', 'joan', 'lisa', 'sarah', 'diana', 'kate', 'ann', 'donna']

print('')
print('Word embedding association test: %.3f' %
      weat(career, family, male, female, word_vectors))


"""State purity."""
import numpy as np

from toqito.matrix_props import is_density

def purity(rho: np.ndarray) -> float:
    r"""
    Compute the purity of a quantum state [WikPurity]_.

    The negativity of a subsystem can be defined in terms of a density matrix :math:`\rho`: The
    purity of a quantum state :math:`\rho` is defined as

    .. math::
        \text{Tr}(\rho^2),

    where :math:`\text{Tr}` is the trace function.

    Examples
    ==========

    Consider the following scaled state defined as the scaled identity matrix

    .. math::
        \rho = \frac{1}{4} \begin{pmatrix}
                         1 & 0 & 0 & 0 \\
                         0 & 1 & 0 & 0 \\
                         0 & 0 & 1 & 0 \\
                         0 & 0 & 0 & 1
                       \end{pmatrix} \in \text{D}(\mathcal{X}).

    Calculating the purity of :math:`\rho` yields :math:`\frac{1}{4}`. This can be observed using
    :code:`toqito` as follows.

    >>> from toqito.state_props import purity
    >>> import numpy as np
    >>> purity(np.identity(4) / 4)
    0.25

    Calculate the purity of the Werner state.
    >>> from toqito.states import werner
    >>> rho = werner(2, 1 / 4)
    >>> purity(rho)
    0.2653

    References
    ==========
    .. [WikPurity] Wikipedia page for purity (quantum mechanics):
        https://en.wikipedia.org/wiki/Purity_(quantum_mechanics)

    :param rho: A density matrix of a pure state vector.
    :return: A value between 0 and 1 that corresponds to the purity of
            :math:`\rho`.
    """
    if not is_density(rho):
        raise ValueError("Purity is only defined for density operators.")
    # "np.real" get rid of the close-to-0 imaginary part.
    return np.real(np.trace(np.linalg.matrix_power(rho, 2)))


# -------------------------------------------------------------------------------------- #
# -------------------------------------------------------------------------------------- #
# Vignette #1
# APPM 4720/5720 Randomized Algorithms, Spring 2019
# Stephen Becker (original MATLAB) & Jake Knigge (Python)
# This is not a practical algorithm, since it only works if the matrix is *exactly* low
# rank.
# -------------------------------------------------------------------------------------- #
import numpy as np                      # import NumPy package
import time as time                     # import time package
# -------------------------------------------------------------------------------------- #
np.set_printoptions(precision = 4)      # display only four digits
np.random.seed(seed = 2)                # set seed for reproducibility
n = np.int(4e3); m = n                  # dimension of problem
r = np.int(100)                         # rank of matrix
mu, sigma = 0, 1                        # mean and standard deviation
zz = np.random.normal(mu, sigma, n*r)   # generate (normal) random numbers
Z = zz.reshape(n,r)                     # reshape to matrix
A = np.matmul(Z, Z.T)                   # compute outerproduct matrix
# -------------------------------------------------------------------------------------- #
# Find its SVD with conventional methods
t = time.time()                         # time SVD calculation
U, S, Vh = np.linalg.svd(A); V = Vh.T   # compute SVD of A and transpose V
elapsed = time.time() - t
print('The full SVD took', round(elapsed, 4), 'seconds.')
# -------------------------------------------------------------------------------------- #
# Find its SVD with a randomized method
tt = time.time()
t = time.time(); Omega = np.random.normal(mu, sigma, (n, r)); print(round(time.time() - t, 4), 'seconds')
t = time.time(); Y     = np.matmul(A, Omega);                 print(round(time.time() - t, 4), 'seconds')
t = time.time(); Q, R  = np.linalg.qr(Y, mode='reduced');     print(round(time.time() - t, 4), 'seconds')
t = time.time(); QtA   = np.matmul(Q.T, A);                   print(round(time.time() - t, 4), 'seconds')
tm  = time.time() - tt
print('The approximate SVD took', round(tm, 4), 'seconds.')
# -------------------------------------------------------------------------------------- #
A_estimate = np.matmul(Q, QtA)
err = np.linalg.norm( np.ravel(A) - np.ravel(A_estimate) ) / np.linalg.norm( np.ravel(A) )
print('The error ||A-A_estimate||_F/||A||_F is ', '{:0.4e}'.format(err), '.', sep = '')
# -------------------------------------------------------------------------------------- #
# -------------------------------------------------------------------------------------- #


"""
@Author: 	yshhuang@foxmail.com
@Date: 2020-06-10 14:42:16
@LastEditors: 	yshhuang@foxmail.com
@LastEditTime: 2020-06-10 17:43:14
@FilePath: /d2l-zh/optimization-algorithm/gradient-descent.py
"""
import d2lzh as d2l
import math
from mxnet import nd
import numpy as np

def gd(eta):
    x = 10
    results = [x]
    for i in range(10):
        x -= eta*2*x
        results.append(x)
    print('epoch 10,x:', x)
    return results

def show_trace(res):
    n = max(abs(min(res)), abs(max(res)), 10)
    f_line = np.arange(-n, n, 0.1)
    d2l.set_figsize()
    d2l.plt.plot(f_line, [x * x for x in f_line])
    d2l.plt.plot(res, [x * x for x in res], '-o')
    d2l.plt.xlabel('x')
    d2l.plt.ylabel('f(x)')
    d2l.plt.show()

if __name__ == "__main__":
    res = gd(0.2)
    show_trace(res)


from typing import Callable

import numpy as np

def trapezoid(
    func: Callable[..., float], a: float, b: float, eps: float = 0.0001, *args, **kwargs
) -> float:

    """
    Metode integrasi trapesium mengganti bentuk segiempat
    yang sebelumnya digunakan untuk mengintegrasi menjadi
    bentuk trapesium. Maka dari itu, pada trapesium, nilai
    f(x) dievaluasi pada masing2 sisi trapesium.

    Parameter:
    f   = fungsi input
    a   = batas bawah integrasi
    b   = batas atas integrasi
    eps = error relatif maksimal

    >>> trapezoid(lambda x: x**5, 0, 1)
    0.16670751153457503
    >>> trapezoid(lambda x: x**2, 0, 2)
    2.6667973728065877
    """
    try:
        # Iterasi pertama
        n = 100
        h = (b - a) / n
        fx = [func(a + i * h, *args, **kwargs) for i in range(n + 1)]
        L0 = h / 2 * (fx[0] + fx[-1]) + h * sum(fx[1:-1])

        # Optimasi
        err = 1
        while err > eps:
            n += 1
            h = (b - a) / n
            fx = [func(a + i * h, *args, **kwargs) for i in range(n + 1)]
            L1 = h / 2 * (fx[0] + fx[-1]) + h * sum(fx[1:-1])
            err = np.abs(L1 - L0) / np.abs(L1)
            L0 = L1
    except Exception:
        raise RuntimeError("Integrasi gagal, pastikan fungsi anda benar!")
    return L1

if __name__ == "__main__":

    def f(x: float) -> float:
        """
        Test Function
        """
        return x ** 4

    import doctest

    doctest.testmod()


"""
How much accuracy is lost using Strassen? On N(0,1) iid matrices.
"""

import numpy as np
import numpy.random as npr

d0 = 2048

def strassen(depth, A, B):
  """
  return AB, using strassen to recursion level `depth'
  """
  if depth == 0:
    return np.dot(A, B)
  else:
    d = A.shape[0]
    A11 = A[0:d/2, 0:d/2]
    A12 = A[0:d/2, d/2::]
    A21 = A[d/2::, 0:d/2]
    A22 = A[d/2::, d/2::]

    B11 = B[0:d/2, 0:d/2]
    B12 = B[0:d/2, d/2::]
    B21 = B[d/2::, 0:d/2]
    B22 = B[d/2::, d/2::]

    M1 = strassen(depth-1, A11 + A22, B11 + B22)
    M2 = strassen(depth-1, A21 + A22, B11)
    M3 = strassen(depth-1, A11, B12 - B22)
    M4 = strassen(depth-1, A22, B21 - B11)
    M5 = strassen(depth-1, A11 + A12, B22)
    M6 = strassen(depth-1, A21 - A11, B11 + B12)
    M7 = strassen(depth-1, A12 - A22, B21 + B22)

    C = np.zeros((d, d), dtype = A.dtype)
    C[0:d/2, 0:d/2] = M1 + M4 - M5 + M7
    C[0:d/2, d/2::] = M3 + M5
    C[d/2::, 0:d/2] = M2 + M4
    C[d/2::, d/2::] = M1 - M2 + M3 + M6

    return C

Ad = -0.2 + npr.randn(d0,d0)
Bd = 0.1 + npr.randn(d0,d0)

Af = np.array(Ad, dtype = np.float32)
Bf = np.array(Bd, dtype = np.float32)

C0d = strassen(0, Ad, Bd)
C0d2 = np.dot(Ad, Bd)

C0f = strassen(0, Af, Bf)
err0 = np.abs(C0d - np.array(C0f, dtype = np.float64))
mu0 = err0.mean()
ma0 = err0.max()

print "log2(mean absolute difference) : ", np.log2(mu0)
print "log2(max  absolute difference) : ", np.log2(ma0)
print "Depth \t\tmean/mean0 \tmax/max0"

for depth in [0,1,2,3,4,5]:
  Cxf = strassen(depth, Af, Bf)
  err1 = np.abs(C0d - np.array(Cxf, dtype = np.float64))
  print depth, "   :    ", err1.mean()/mu0, '\t', err1.max()/ma0


"""Module computes the mean and the mean square of independent variable y and dependent variable x."""

import numpy as np

def mean_meansqr(data):
    """Get the mean and mean square errors for each column (x and y data)"""
    mean_squares = np.array([])

    mean = np.mean(data,dtype=np.float64,axis=0) # an array containing the mean per each column
    for l in range(mean.shape[0]):
        # compute mean squared error for each column
        diff_squares = (data[:,l] - mean[l])**2 # differences of squares
        elements = np.mean(diff_squares)
        mean_squares = np.concatenate((mean_squares,np.array([elements])))

    return (mean, mean_squares)


# Importing the libraries
import random
import numpy as np

"""
Generate some random numbers.
"""

mylist = []
totalNumbers = 100
minNumber = 0
maxNumber = 10

for i in range(0, totalNumbers):   # [start, end) =>  [0, totalNumbers)
    x = random.randint(minNumber,maxNumber)  # [minNumber, maxNumber]
    mylist.append(x)

print(mylist)

"""
Generate a 10 x 4 data matrix X, initially polulated with zeros.
"""

rowCount = 10
colCount = 4
X = np.zeros((rowCount, colCount), dtype = int)
#X = np.zeros((rowCount, colCount), dtype = float)

print (X)

"""

Populate the First column with indentpendly identically distributed (i.i.d.)
random variables N(125, 16) (simulating height),
and the second column with i.i.d. randomN(125 variables , 64) (simulating weight).

"""
height = X[:, 0]
weight = X[:, 1]

print (height)
print (weight)

whRatioList = [0.0] * (rowCount) # Generating a list of size rowCount
whRatioArray = np.asarray(whRatioList) # Converting the list into array
print (whRatioArray.size)

for i in range(whRatioArray.size):
    height[i] = np.random.normal(loc=125, scale= 4, size=1)
    weight[i] = np.random.normal(loc=125, scale= 8, size=1)
    whRatioArray[i] = weight[i]/height[i]

print (weight)
print (height)

for i in range(whRatioArray.size):
    print("Weight[%d] = %d " % (i, weight[i]))
    print("Height[%d] = %d " % (i, height[i]))
#    print("Weight[%d] / Height[%d] = %d / %d = %.2f" % (i, i, weight[i], height[i], (weight[i]/height[i])))
    print("Weight[%d] / Height[%d] = %d / %d = %f" % (i, i, weight[i], height[i], whRatioArray[i]))

Matrix and Vector Operations/MatrixOperations_WithRandomlyGeneratedData.py


import scipy.integrate as spi
import autograd.numpy as anp   # Thinly-wrapped version of Numpy
import autograd as ag

print('Compute length of an arc of planar parametric curve by SciPy quad')
print('Example 1-04 quad arclength')
print('Length of arc of parametric curve x(t)=cos^3(t) and y(t)=sin^3(t) from t=0 to t=2pi')

x = lambda t : anp.cos(t) ** 3
y = lambda t : anp.sin(t) ** 3
dx_dt = ag.grad(x)
dy_dt = ag.grad(y)

integrand = lambda t : anp.sqrt(dx_dt(t) ** 2 + dy_dt(t) ** 2)
a = 0.
b = 2 * anp.pi

result, error = spi.quad(integrand, a, b)
print('Result is ', result, 'with error ', error)


import matplotlib.pyplot as plt
import numpy as np
import time
from FTCS_Pythran import loop_ftcs_pythran
from FTCS_Cython import loop_ftcs_cython
from FTCS_Cython_OMP import loop_ftcs_cython_omp

nx = 128
ny = 128

#Cell sizes
dx = 2.0*np.pi/float(nx)
dy = 2.0*np.pi/float(ny)

#Indices
index_range = np.arange(0,nx+2)
x, y = np.meshgrid(dx*index_range-dx,dy*index_range-dy)

#diffusion parameter
alpha = 0.8
#Stable timestep
dt = 0.8*dx*dx/(4.0*alpha)

#Initialize
u = np.sin(x+y)

#length of solution
ft = 1.0

def plot_field(x,y,u,title):
	#levels = np.linspace(-1, 1, 20)
	fig, ax = plt.subplots(nrows=1,ncols=1)
	ax.set_title(title)
	p1 = ax.contourf(x,y,u)
	plt.colorbar(p1,ax=ax)#,format="%.2f"
	plt.show()

def loop_ftcs(u):

	nt = int(ft/dt)
	const_mult = alpha*dt/(dx*dx)

	t = 0
	while t<nt:
		t = t+1
		utemp = np.copy(u)

		#FTCS timestep
		for i in range(1,nx+1):
			for j in range(1,ny+1):
				u[i,j] = utemp[i,j] + const_mult*(-4.0*utemp[i,j]+(utemp[i-1,j]+utemp[i+1,j]+utemp[i,j-1]+utemp[i,j+1]))

		#Periodic boundary condition update
		for j in range(1,ny+1):
			u[0,j] = u[nx,j]
			u[nx+1,j] = u[1,j]

		for i in range(0,nx+2):
			u[i,0] = u[i,ny]
			u[i,ny+1] = u[i,1]

		del(utemp)

	return u

def vectorized_ftcs(u):

	global dx,dy,dt,alpha,ft
	nt = int(ft/dt)
	const_mult = alpha*dt/(dx*dx)

	t = 0
	while t<nt:
		t = t+1
		utemp = np.copy(u)
		u[1:nx+1,1:ny+1] = utemp[1:nx+1,1:ny+1] + const_mult*(-4.0*utemp[1:nx+1,1:ny+1]+(utemp[0:nx,1:ny+1]+
							utemp[2:nx+2,1:ny+1]+utemp[1:nx+1,0:ny]+utemp[1:nx+1,2:ny+2]))

		#Update BCS
		#Need to update BCs - x direction
		u[0:1,1:ny+1] = u[nx:nx+1,1:ny+1]
		u[nx+1:nx+2,1:ny+1] = u[1:2,1:ny+1]

		#Need to update BCs - y direction
		u[0:nx+2,0:1] = u[0:nx+2,ny:ny+1]
		u[0:nx+2,ny+1:ny+2] = u[0:nx+2,1:2]

		del(utemp)

	return u

if __name__ == '__main__':

	compile_option = int(input('Enter 0 for native, 1 for vectorized, 2 for Cython build, 3 for Pythran build, 4 for Cython_OMP: '))

	if compile_option == 0:
		print('This will take some time for large values of nx. Be warned.')
		plot_field(x,y,u,'Initial Condition')
		t1 = time.time()
		u = loop_ftcs(u)
		print('CPU time taken for for loop based FTCS: ',time.time()-t1)
		plot_field(x,y,u,'Final solution')
	elif compile_option == 1:
		plot_field(x,y,u,'Initial Condition')
		t1 = time.time()
		u = vectorized_ftcs(u)
		print('CPU time taken for vectorized FTCS: ',time.time()-t1)
		plot_field(x,y,u,'Final solution')
	elif compile_option == 2:
		plot_field(x,y,u,'Initial Condition')
		t1 = time.time()
		u = loop_ftcs_cython(nx,ny,dx,dy,dt,ft,alpha,u)
		print('CPU time taken for Cython build: ',time.time()-t1)
		plot_field(x,y,u,'Final solution')
	elif compile_option == 3:
		plot_field(x,y,u,'Initial Condition')
		t1 = time.time()
		u = loop_ftcs_pythran(nx,ny,dx,dy,dt,ft,alpha,u)
		print('CPU time taken for for Pythran build: ',time.time()-t1)
		plot_field(x,y,u,'Final solution')
	elif compile_option == 4:
		plot_field(x,y,u,'Initial Condition')
		t1 = time.time()
		u = loop_ftcs_cython_omp(nx,ny,dx,dy,dt,ft,alpha,u)
		print('CPU time taken for Cython OMP build: ',time.time()-t1)
		plot_field(x,y,u,'Final solution')


#!/usr/bin/env python3

import numpy as np
from sklearn.metrics import confusion_matrix

def confusion_matrix_(y_true, y_pred, labels=None):
    """
    Compute confusion matrix to evaluate the accuracy of a classification.
    Args:
        y_true: a scalar or a numpy ndarray for the correct labels
        y_pred: a scalar or a numpy ndarray for the predicted labels
        labels: optional, a list of labels to index the matrix. This may be used to reorder or select a subset of labels. (default=None)
    Returns:
        The confusion matrix as a numpy ndarray.
        None on any error.
    Raises:
        This function should not raise any Exception.
    """
    tp = 0
    tn = 0
    fp = 0
    fn = 0
    if labels == None:
        values = list(set(y_true))
    else:
        values = labels
    if (len(values)) != 2:
        return None
    for i, elem in enumerate(y_true):
        if y_pred[i] == values[1] and y_true[i] == y_pred[i]:
            tp += 1
        elif y_pred[i] == values[1] and y_true[i] != y_pred[i]:
            fp += 1
        elif y_pred[i] == values[0] and y_true[i] == y_pred[i]:
            tn += 1
        elif y_pred[i] == values[0] and y_true[i] != y_pred[i]:
            fn += 1
    matrix = np.array([[tp, fp], [fn, tn]])
    return matrix

if __name__ == "__main__":

    y_pred = np.array(['norminet', 'dog', 'norminet', 'norminet', 'dog', 'bird'])
    y_true = np.array(['dog', 'dog', 'norminet', 'norminet', 'dog', 'norminet'])

    print(confusion_matrix_(y_true, y_pred))
    print(confusion_matrix_(y_true, y_pred, labels=['dog','norminet']))


from abc import ABC, abstractmethod
import numpy as np
import re

class ActivationBase(ABC):

    def __init__(self, **kwargs):
        super().__init__()

    def __call__(self, z):
        if z.ndim == 1:
            z = z.reshape(1, -1)
        return self.forward(z)

    @abstractmethod
    def forward(self, z):
        raise NotImplementedError

    @abstractmethod
    def grad(self, x, **kwargs):
        raise NotImplementedError

class Sigmoid(ActivationBase):
    """
    Sigmoid(x) = 1 / (1 + e^(-x))
    """

    def __init__(self):
        super().__init__()

    def __str__(self):
        return "Sigmoid"

    def forward(self, z):
        return 1 / (1 + np.exp(-z))

    def grad(self, x):
        return self.forward(x) * (1 - self.forward(x))

class Tanh(ActivationBase):
    """
    Tanh(x) = (e^x - e^(-x)) / (e^x + e^(-x))
    """

    def __init__(self):
        super().__init__()

    def __str__(self):
        return "Tanh"

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

    def grad(self, x):
        return 1 - np.tanh(x) ** 2

class ReLU(ActivationBase):
    """
    ReLU(x) =
            x   if x > 0
            0   otherwise
    """

    def __init__(self):
        super().__init__()

    def __str__(self):
        return "ReLU"

    def forward(self, z):
        return np.clip(z, 0, np.inf)

    def grad(self, x):
        return (x > 0).astype(int)

class LeakyReLU(ActivationBase):
    """
    LeakyReLU(x) =
            alpha * x   if x < 0
            x           otherwise
    """

    def __init__(self, alpha=0.3):
        self.alpha = alpha
        super().__init__()

    def __str__(self):
        return "Leaky ReLU(alpha={})".format(self.alpha)

    def forward(self, z):
        _z = z.copy()
        _z[z < 0] = _z[z < 0] * self.alpha
        return _z

    def grad(self, x):
        out = np.ones_like(x)
        out[x < 0] *= self.alpha
        return out

class Affine(ActivationBase):
    """
    Affine(x) = slope * x + intercept
    """

    def __init__(self, slope=1, intercept=0):
        self.slope = slope
        self.intercept = intercept
        super().__init__()

    def __str__(self):
        return "Affine(slope={}, intercept={})".format(self.slope, self.intercept)

    def forward(self, z):
        return self.slope * z + self.intercept

    def grad(self, x):
        return self.slope * np.ones_like(x)

class SoftPlus(ActivationBase):
    """
    SoftPlus(x) = log(1 + e^x)
    """

    def __init__(self):
        super().__init__()

    def __str__(self):
        return "SoftPlus"

    def forward(self, z):
        return np.log(np.exp(z) + 1)

    def grad(self, x):
        return np.exp(x) / (np.exp(x) + 1)

class ELU(ActivationBase):
    """
    ELU(x) =
            x                   if x >= 0
            alpha * (e^x - 1)   otherwise
    """

    def __init__(self, alpha=1.0):
        self.alpha = alpha
        super().__init__()

    def __str__(self):
        return "ELU(alpha={})".format(self.alpha)

    def forward(self, z):
        return np.where(z > 0, z, self.alpha * (np.exp(z) - 1))

    def grad(self, x):
        return np.where(x >= 0, np.ones_like(x), self.alpha * np.exp(x))

class Exponential(ActivationBase):
    """
    Exponential(x) = e^x
    """

    def __init__(self):
        super().__init__()

    def __str__(self):
        return "Exponential"

    def forward(self, z):
        return np.exp(z)

    def grad(self, x):
        return np.exp(x)

class SELU(ActivationBase):
    """
    SELU(x) = scale * ELU(x, alpha)
            = scale * x                     if x >= 0
              scale * [alpha * (e^x - 1)]   otherwise
    """

    def __init__(self):
        self.alpha = 1.6732632423543772848170429916717
        self.scale = 1.0507009873554804934193349852946
        self.elu = ELU(alpha=self.alpha)
        super().__init__()

    def __str__(self):
        return "SELU"

    def forward(self, z):
        return self.scale * self.elu.forward(z)

    def grad(self, x):
        return np.where(
            x >= 0, np.ones_like(x) * self.scale, np.exp(x) * self.alpha * self.scale
        )

class HardSigmoid(ActivationBase):
    """
    HardSigmoid(x) =
            0               if x < -2.5
            0.2 * x + 0.5   if -2.5 <= x <= 2.5.
            1               if x > 2.5
    """

    def __init__(self):
        super().__init__()

    def __str__(self):
        return "Hard Sigmoid"

    def forward(self, z):
        return np.clip((0.2 * z) + 0.5, 0.0, 1.0)

    def grad(self, x):
        return np.where((x >= -2.5) & (x <= 2.5), 0.2, 0)

class ActivationInitializer(object):

    def __init__(self, acti_name="affine(slope=1, intercept=0)"):
        self.acti_name = acti_name

    def __call__(self):
        acti_str = self.acti_name.lower()
        if acti_str == "relu":
            acti_fn = ReLU()
        elif acti_str == "tanh":
            acti_fn = Tanh()
        elif acti_str == "sigmoid":
            acti_fn = Sigmoid()
        elif "affine" in acti_str:
            r = r"affine\(slope=(.*), intercept=(.*)\)"
            slope, intercept = re.match(r, acti_str).groups()
            acti_fn = Affine(float(slope), float(intercept))
        elif "leaky relu" in acti_str:
            r = r"leaky relu\(alpha=(.*)\)"
            alpha = re.match(r, acti_str).groups()[0]
            acti_fn = LeakyReLU(float(alpha))
        else:
            raise ValueError("Unknown activation: {}".format(acti_str))
        return acti_fn


import numpy as np
import time

class Newton(object):
    def __init__(self, f=None, fp=None, **extras):
        """
        f : a function f(x) that returns 'y', a float
        fp : the first derivative of function f(x), returning a float
        """
        self.f = f
        self.fp = fp

    def solve(self, init_a=None, tol=None, max_it=50, **extras):
        """fast implementation of Newton's method
        init_a : initial estimate
        tol : maximum absolute error after which the method should stop. (e.g. 1e-15)
        max_it : maximum number of iterations after which the method should stop
        """
        x = init_a
        i = 0
        while i < max_it:
            xn = x - ( self.f(x) / self.fp(x) )
            if abs(xn - x) < tol:
                break
            x = xn
            i+=1
        return x

    def vsolve(self, init_a=None, tol=None, max_it=50, **extras):
        """verbose implementation of Newton's method
        init_a : initial estimate
        tol : maximum absolute error after which the method should stop. (e.g. 1e-15)
        max_it : maximum number of iterations after which the method should stop
        """

        x = init_a
        xo = 0
        iteration_list = []
        dist_list = []
        estimate_list = []

        i = 0
        while i < max_it:
            dist = abs(x-xo)
            iteration_list.append(i)
            dist_list.append(dist)
            estimate_list.append(x)
            xo = x
            xn = x - ( self.f(x) / self.fp(x) )
            if abs(xn - x) < tol:
                break
            x = xn
            i+=1

        return iteration_list, estimate_list, dist_list

    def __repr__(self):
        # return "Newton "
        raise NotImplementedError

    def __str__(self):
        return "Newton method"


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

''' polynomial regression
It is a form of regression analysis in which
the relationship between the independent variable x
and the dependent variable y is modelled as an
nth degree polynomial in x.

y = c0 * x^0 + c1 * x^1 +
    c2 * x^2 + c3 * x^3 + ..... + cn * x ^n
n = degree

PolynomialFeatures :

It generate a new feature matrix
consisting of all polynomial combinations of the
features with degree less than or equal to the
specified degree
'''

import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures

''' sample polynomial equation with degree=3'''
''' t =    24.0 * x^0
         + 12.0 * x^1
         -  0.5 * x^2
         +  9.0 * x^3
'''

constant     = 24
error_induce = 0.01

def fx1(x1):
    res   = (12.0  * x1)
    error = 0.0
    error = res * random.uniform(-error_induce, error_induce)
    return res + error
def fx2(x2):
    res   = (-0.50 * x2 * x2)
    error = 0.0
    error = res * random.uniform(-error_induce, error_induce)
    return res + error
def fx3(x3):
    res   = (09.0 * x3 * x3 * x3)
    error = 0.0
    error = res * random.uniform(-error_induce, error_induce)
    return res + error

''' data preparation '''
max_sample_value = 50
total_samples    = 800
train_sample_cnt = int((total_samples * 60.0 / 100.0))
test_sample_cnt  = total_samples - train_sample_cnt

y1_samples       = fx1(np.arange(total_samples))
y2_samples       = fx2(np.arange(total_samples))
y3_samples       = fx3(np.arange(total_samples))

t_samples        = y1_samples + y2_samples + y3_samples

t_samples        = t_samples + constant

''' splitting samples into train data and test data '''
y1_samples_train, y1_samples_test = np.split(
                                     y1_samples, [train_sample_cnt,])
y2_samples_train, y2_samples_test = np.split(
                                     y2_samples, [train_sample_cnt,])
y3_samples_train, y3_samples_test = np.split(
                                     y3_samples, [train_sample_cnt,])
t_samples_train, t_samples_test   = np.split(
                                     t_samples, [train_sample_cnt,])

''' combining all variables in column structure '''
xyz_samples_train   = {'colx1' : y1_samples_train,
                       'colx2' : y2_samples_train,
                       'colx3' : y3_samples_train}
dfxyz_samples_train = pd.DataFrame(data=xyz_samples_train)
dft_samples_train   = pd.DataFrame(data=t_samples_train)

xyz_samples_test    = {'colx1' : y1_samples_test,
                       'colx2' : y2_samples_test,
                       'colx3' : y3_samples_test}

dfxyz_samples_test  = pd.DataFrame(data=xyz_samples_test)
dft_samples_test    = pd.DataFrame(data=t_samples_test)

''' use PolynomialFeatures to fit and transform a model'''
poly     = PolynomialFeatures(degree=3, include_bias=False)
poly_fit = poly.fit_transform(
            np.arange(train_sample_cnt)[:, np.newaxis])

''' prepare a linear regression model finally for prediction '''
lm            = LinearRegression().fit(poly_fit, dft_samples_train)
predictions   = lm.predict(dfxyz_samples_test)

print("predictions   = ", predictions)
print("lm.score(X,y) = ", lm.score(dfxyz_samples_test,
                                   dft_samples_test))
print("lm.coef_      = ", lm.coef_)
print("lm.intercept_ = %.2f" %lm.intercept_)

''' now we are going to plot the points
    just the difference between actual and expected '''
prediced_difference = np.subtract(
                            dft_samples_test.values,
                            predictions)
plt.title('Polynomial Regression - difference between actual and expected', fontsize=16)
plt.xlabel('test sample count', fontsize=14)
plt.ylabel('difference value' , fontsize=14)

plt.plot(np.arange(prediced_difference.size),
         prediced_difference,
         color='b')
plt.show()


# The toy problem in http://arxiv.org/abs/1403.4890
import numpy as np

def main(job_id, params):
  x1 = params['x']
  x2 = params['y']

  f  = x1 + x2
  c1 = 1.5 - x1 - 2.0*x2 - 0.5*np.sin(2*np.pi*(x1**2 - 2.0*x2))
  c2 = x1**2 + x2**2 - 1.5
  c1 = -c1
  c2 = -c2

  return {'f':f, 'c1':c1, 'c2':c2}

"""
Everything below this point is optional. It is used to specify
the true solution so that one can plot the error curve using
progress_curve.py in the visualizations/ directory.
"""
def true_val():
    return 0.5998
def true_sol():
    return {'x' : 0.1954, 'y' : 0.4044}
true_func = main


#!/usr/bin/env python3
import numpy as np

def vxadd(v1, v2):
    """vector addition
    :param v1: first vector
    :param v2: second vector
    :return v_out: v_out = v1 + v2
    """
    v_out = np.zeros(len(v1))
    for ii in range(len(v1)):
        v_out[ii] = v1[ii]+v2[ii]
    return np.array(v_out)

def vdotv(v1, v2):
    """vector dot product
    :param v1: first vector
    :param v2: second vector
    :return: v_out = sum of (v1[ii] * v2[ii])
    """
    v_out = 0
    v1 = np.array(v1)
    v2 = np.array(v2)
    for ii in range(len(v1)):
        v_out += v1[ii]*v2[ii]
    return np.array(v_out)

def norm(v1):
    """returns the magnitude of the two vectors
    in work
    """
    return np.array(np.linalg.norm(v1))

def vcrossv(v1, v2):
    """cross product of two vectors of length 3
    :param v1: first vector (top)
    :param v2: second vector (bottom)
    :return v_out: v_out = v1xv2
    """
    v_out = np.zeros(len(v1))
    v_out[0] = v1[1]*v2[2] - v1[2]*v2[1]
    v_out[1] = -(v1[0]*v2[2] - v1[2]*v2[0])
    v_out[2] = v1[0]*v2[1] - v1[1]*v2[0]
    return np.array(v_out)

def vxvT(v1, v2):
    """vector multiplication
    :param v1: first row vector;
    :param v2: second row vector;
    :return m_out: matrix representing vector product;
                   m_out = v1*v2.T
    """
    m_out = np.zeros((len(v1), len(v1)))
    for ii in range(len(v1)):
        for jj in range(len(v1)):
            m_out[ii][jj] = v1[ii]*v2[jj]
    return np.array(m_out)

def v_cross(v1):
    """returns skew symmetric matrix
    """
    v_tilde = [[    0., -v1[2],  v1[1]],
               [ v1[2],     0., -v1[0]],
               [-v1[1],  v1[0],     0]]
    return np.array(v_tilde)

def vxs(scalar, v1):
    """scales a vector by a scalar
    :param scalar: scalar component to multiply
    :param v1: vector to be scaled
    :return v_out: resultant vector; v_out = scalar*v1
    """
    v_out = np.zeros((len(v1)))
    for ii in range(len(v1)):
        v_out[ii] = scalar * v1[ii]
    return np.array(v_out)

def get_dir_cosines(v1):
    """returns the directional cosines for a given vector
    :param v1: unit vector:
    :return: cosines and sines of vector;
    in work
    """
    cosa = v1[0]
    cosb = v1[1]
    cosc = v1[2]
    sina = np.sqrt(v1[1]**2+v1[2]**2)
    sinb = np.sqrt(v1[2]**2+v1[0]**2)
    sinc = np.sqrt(v1[0]**2+v1[1]**2)
    return cosa, cosb, cosc, sina, sinb, sinc

if __name__ == "__main__":

    pass


# Cowboy Linear Algebra python library at Video #7
# By: Nick the Space Cowboy

import numpy as np

def back_sub(Utri, b):
	# back substitution alogrithm for solving upper triangular system
	n = len(Utri) # row dimension of the Utri matrix
	x = np.zeros_like(b, dtype=np.float64)
	for i in range(n - 1, -1, -1):	# loop to iterate through row index
		x[i] += b[i] / Utri[i,i]
		for j in range(n-1, i, -1):	# loop to iterate through the off diagonal Sum part
			x[i] += (- (Utri[i, j] * x[j])) / Utri[i,i]
	return x

def SGE(A, b=None):
	'''
	function to perform structured gaussian elimination
	If a b vector isn't passed through, a LU decomposition is returned
	'''
	n = len(A)
	L = np.identity(n, dtype=np.float64)
	for i in range(0, n, 1):
		for j in range(i+1, n, 1):
			L[j,i] = A[j,i] / A[i,i]
			A[j] = A[j] - (L[j,i] * A[i])
			if b == None:
				pass
			else:
				b[j] = b[j] - (L[j,i] * b[i])
	if b==None:
		return L, A
	else:
		return A, b

def LU(A):
	'''
	Function to perform LU decomposition
	'''
	return SGE(A)

def LDV(A):
	'''
	Function to perform a LDV Matrix decomposition
	'''
	L, U = LU(A)
	n = len(A)
	D = np.identity(n, dtype=np.float64)
	for i in range(0, n, 1):
		D[i, i] = U[i, i]
	V = np.dot(np.linalg.inv(D), U)
	return L, D, V

def pos_def_check(A):
	'''
	Checks if a matrix is positive definate
	'''
	pd_state = True
	n = len(A)
	for i in range(1, n):
		if (A[i, i - 1] ** 2) >= A[i, i]:
			raise Exception ("Marix is not positive definate")
			pd_state =  False
	return pd_state

def cholesky(A):
	'''
	Function to perform a Cholesky decompostion
	'''
	pd = pos_def_check(A)
	if pd == True:
		n = len(A)
		L = np.zeros_like(A, dtype=np.float64)
		for i in range(0, n):
			for j in range(0, i + 1):
				sum_part = 0
				if i == j:
					for k in range(0, j):
						sum_part += L[j, k] ** 2
					L[i, j] = np.sqrt(A[j, j] - sum_part)
				else:
					for k in range(0, j):
						sum_part += L[i, k] * L[j, k]
					L[i, j] = (A[i, j] - sum_part) / L[j, j]
	else:
		L = f"Matrix is not positive definate"
	return L

def LDLT(A):
	'''
	Function to perform the LDL^T matrix decomposition
	'''
	C = cholesky(A)
	n = len(A)
	D = np.identity(n, dtype=np.float64)
	L = C
	for i in range(0, n):
		D[i, i] = C[i, i]**2
		L[:, i] = C[:, i] * D[i, i] ** (-1/2)
	return L, D


import numpy as np
from numpy.random import rand
import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')
from matplotlib import pyplot as plt

from solutions import sqrt64

def invsqrt64(A, reps):
    Ac = A.copy()
    if 0 < reps:
        Ac2 = A.copy()
        Ac2 /= - 2
        Ac3 = np.empty_like(Ac)
    I = Ac.view(dtype=np.int64)
    I >>= 1
    I *= -1
    I += 0x5fe6ec85e7de30da #hexadecimal representation of the constant
    for j in xrange(reps):
        Ac3[:] = Ac
        Ac3 *= Ac
        Ac3 *= Ac2
        Ac3 += 1.5
        Ac *= Ac3
    return Ac

def sqrt0():
    X = np.linspace(0, 3, 501)
    plt.plot(X, sqrt64(X, 0), X, np.sqrt(X))
    plt.savefig("sqrt0.pdf")
    plt.clf()

def sqrt1():
    X = np.linspace(0, 3, 501)
    plt.plot(X, sqrt64(X, 1), X, np.sqrt(X))
    plt.savefig("sqrt1.pdf")
    plt.clf()

def invsqrt0():
    X = np.linspace(.1, 3, 291)
    plt.plot(X, invsqrt64(X, 0), X, 1./np.sqrt(X))
    plt.savefig("invsqrt0.pdf")
    plt.clf()

def invsqrt1():
    X = np.linspace(.1, 3, 291)
    plt.plot(X, invsqrt64(X, 1), X, 1./np.sqrt(X))
    plt.savefig("invsqrt1.pdf")
    plt.clf()

if __name__ == "__main__":
    sqrt0()
    sqrt1()
    invsqrt0()
    invsqrt1()


import numpy             as np
import scipy.sparse      as sparse
import scipy.linalg      as linear
import scipy.special     as fun

dtype = np.float128

def sparse_symm_to_banded(matrix):
    """Convert sparse symmetric to upper-banded form."""
    diag = matrix.todia()
    B, I  = max(abs(diag.offsets)), diag.data.shape[1]
    banded = np.zeros((B+1, I), dtype=diag.dtype)
    for i, b in enumerate(diag.offsets):
        if b >= 0:
            banded[B-b] = diag.data[i]
    return banded

def grid_guess(Jacobi_matrix,symmetric=True):
    """Returns reasonable Gauss quadrature grid as the Jacobi matrix eigenvalues.
       For P(z) = [p_{0}(z),...,p_{N}(z)], J.P(z) = z P(z); or p_{N+1}(z) = 0.

       Parameters
       ----------
       Jacobi_matrix: square self-adjoint tri-diagonal matrix (arbitrary metric)
       symmetric: T/F
       A normalised metric implies a self-adjoint matrix is symmetric.

    """

    if symmetric:
        J_banded = sparse_symm_to_banded(Jacobi_matrix)
        return (np.sort(linear.eigvals_banded(J_banded).real)).astype(dtype)
    else:
        J_dense = Jacobi_matrix.todense()
        return (np.sort(linear.eigvals(J_dense).real)).astype(dtype)

def remainders(Jacobi_matrix,grid):
    """Given length-N three-term recursion, returns P_{N+1}(grid) and P'_{N+1}(grid)

       Parameters
       ----------
       Jacobi_matrix: square self-adjoint tri-diagonal matrix (arbitrary metric)
       grid: float array

    """

    J, z, N = sparse.dia_matrix(Jacobi_matrix).data, grid, len(grid)

    P = np.zeros((N,N),dtype=dtype)
    D = np.zeros((N,N),dtype=dtype)

    P[0] = np.ones(N,dtype=dtype)
    D[1] = P[0]/J[-1][1]

    if np.shape(J)[0] == 3:
        P[1]  = (z-J[1][0])*P[0]/J[-1][1]
        for n in range(2,N):
            P[n] = ((z-J[1][n-1])*P[n-1] - J[0][n-2]*P[n-2])/J[-1][n]
            D[n] = ((z-J[1][n-1])*D[n-1] - J[0][n-2]*D[n-2])/J[-1][n] + P[n-1]/J[-1][n]
        return ((z-J[1][N-1])*P[N-1] - J[0][N-2]*P[N-2]), ((z-J[1][N-1])*D[N-1] - J[0][N-2]*D[N-2]+P[N-1])

    P[1]  = z*P[0]/J[-1][1]
    for n in range(2,N):
        P[n] = (z*P[n-1] - J[0][n-2]*P[n-2])/J[-1][n]
        D[n] = (z*D[n-1] - J[0][n-2]*D[n-2])/J[-1][n] + P[n-1]/J[-1][n]

    return (z*P[N-1] - J[0][N-2]*P[N-2]), (z*D[N-1] - J[0][N-2]*D[N-2] + P[N-1])

def gauss_quadrature(Jacobi_matrix,mass=1,niter=3,guess=None,report_error=False):
    """Returns accurate grid and weights for general Gauss quadrature.

       Parameters
       ----------
       Jacobi_matrix: square self-adjoint tri-diagonal matrix (arbitrary metric)
       mass: float
       mass = integral_{-1}^{+1} w(z)dz
       niter: int
       Number of times to run the Newton iteration for P_{N+1}(grid) = 0
       guess: float array
       Optional grid estimate
       report_error: T/F
       Show the error estimate in the grid values.

    """

    if guess == None:
        z = grid_guess(Jacobi_matrix)
    else:
        z = guess

    #Newton iteration
    for i in range(niter):
        P, D = remainders(Jacobi_matrix,z)
        if report_error: print(np.max(np.abs(P/D)))
        z = z - P/D

    w = 1/((1-z**2)*D**2)
    w = (mass/np.sum(w))*w

    return z, w

def three_term_recursion(Jacobi_matrix,grid,max_degree,init):
    """Returns weighted orthogonal polynomials on a grid from a three-term recursion.
       P_{-1}(z) = 0; P_{0}(z) = init(z)
       P_{n}(z)  = (A_{n}*z + B_{n})*P_{n-1}(z) + C_{n}*P_{n-2}(z)

       Parameters
       ----------
       Jacobi_matrix: square self-adjoint tri-diagonal matrix (arbitrary metric)
       grid: float array
       max_degree: int
       Returns max_degree + 1 weighted polynomials
       init: float array
       len(init) = len(grid)

    """

    if max_degree==0: return np.array([init])

    J, z, N = sparse.dia_matrix(Jacobi_matrix).data, grid, max_degree+1

    P     = np.zeros((N,len(grid)),dtype=dtype)
    P[0]  = init

    if np.shape(J)[0] == 3:
        P[1]  = (z-J[1][0])*P[0]/J[-1][1]
        for n in range(2,N):
            P[n] = ((z-J[1][n-1])*P[n-1] - J[0][n-2]*P[n-2])/J[-1][n]
        return P

    P[1]  = z*P[0]/J[-1][1]
    for n in range(2,N):
        P[n] = (z*P[n-1] - J[0][n-2]*P[n-2])/J[-1][n]
    return P

def normalise(functions,weights):
    """unit-normalise functions on a grid according to weighted L2 inner product. """
    for k in range(len(functions)):
        functions[k] = functions[k]/np.sqrt(np.sum(weights*functions[k]**2))

def quadrature(max_degree,a,b,**kw):
    """Returns accurate grid and weights for specific Gauss-Jacobi quadrature.

        Parameters
        ----------
        max_degree: int
        Integrates polynomials on (-1,+1) exactly up to degree = 2*max_degree+1.
        a,b: int
        Jacobi polynomial parameter for generating Jacobi matrix
        niter: int
        Number of times to run the Newton iteration for P_{N+1}(grid) = 0
        guess: float array
        Optional grid estimate
        report_error: T/F
        Show the error estimate in the grid values.

    """

    mu = mass(a,b)
    J  = operator('J',max_degree,a,b)
    return gauss_quadrature(J,mass=mu,**kw)

def envelope(a,b,a0,b0,grid):
    """Returns sqrt( ((1-z)**(a-a0)) * ((1+z)^(b-b0)) / mass(a,b) )
       Integral envelope(z)**2 (1-z)**a0 (1+z)**b0 dz = 1

       Parameters
       ----------
       a,b,a0,b0: float > -1
       grid: float array

    """

    mu = mass(a,b)
    return np.exp( ((a-a0)/2)*np.log(1-grid) + ((b-b0)/2)*np.log(1+grid) )/np.sqrt(mu)

def recursion(max_degree,a,b,grid,init):
    """Returns weighted Jacobi polynomials on a grid from (a,b) three-term recursion

       Parameters
       ----------
       max_degree: int
       a,b: float > -1

    """

    if max_degree==0: return np.array([init])

    J  = operator('J',max_degree,a,b)
    return three_term_recursion(J,grid,max_degree,init)

def push(op,data):
    """Pushforward"""
    return (op).dot(data)

def pull(op,data):
    """Pullback"""
    return (op.transpose()).dot(data)

def mass(a,b):
    """Returns int_{-1}^{+1} (1-z)^{a} (1+z)^b dz.

       Parameters
       a,b:  float > -1, or string = 'inf', 'inf'. 'inf' returns value for a or b --> infinity.
       grid: float array
    """

    if a=='inf' and b=='inf': return np.sqrt(np.pi)
    if a=='inf'             : return np.exp(fun.gammaln(b+1))
    if              b=='inf': return np.exp(fun.gammaln(a+1))
    return np.exp( (a+b+1)*np.log(2) + fun.gammaln(a+1) + fun.gammaln(b+1) - fun.gammaln(a+b+2) )

def operator(op,max_degree,a,b,format='csr',rescale=None):
    """ Returns various martix operators for acting on coefficients
        in a Jacobi polynomial series.

        Parameters
        ----------
        Jacobi polynomial parameter
        max_degree: int
        a,b: float > -1
        Operator options
        op: string = '0', 'I', 'A-', 'A+', 'B-', 'B+', 'C-', 'C+', 'D-', 'D+', 'J', 'z=+1', 'z=-1'
        0  : 0 <a,b| = <a,b| 0
        I  : <a,b| = <a,b| I
        A- : (1-z) <a,b| = <a-1,b| A-
        A+ : <a,b| = <a+1,b| A+
        B- : (1+z) <a,b| = <a,b-1| B-
        B+ : <a,b| = <a,b+1| B+
        C- : ( a - (1-z)*d/dz ) <a,b| = <a-1,b+1| C-
        C+ : ( b + (1+z)*d/dz ) <a,b| = <a+1,b-1| C+
        D- : ( a(1+z) - b(1-z) - (1-z^2)*d/dz ) <a,b| = <a-1,b-1| D-
        D+ : d/dz <a,b| = <a+1,b+1| D+
        J  : z <a,b| = <a,b| J
        z=+1 : <a,b|z=+1>
        z=-1 : <a,b|z=-1>
        format:  string = 'dia', 'csr'
        rescale: float, multiply operator by factor on output

    """

    def diag(bands,locs):
        return sparse.dia_matrix((bands,locs),shape=(len(bands[0]),len(bands[0])))

    N = max_degree+1
    n = np.arange(0,N,dtype=dtype)
    na, nb, nab, nnab = n+a, n+b, n+a+b, 2*n+a+b

    # 0 <a,b| = <a,b| 0
    if op == '0': out = diag([np.zeros(N,dtype=dtype)],[0])

    # 1 <a,b| = <a,b| I
    if op == 'I': out = diag([np.ones(N,dtype=dtype)],[0])

    # (1-z) <a,b| = <a-1,b| A-
    if op == 'A-' and (a>0):
        if a+b==0:
            middle = na/(2*n+1)
            lower  = (nb+1)/(2*n+1)
            middle[0]  = 2*a
        else:
            middle = 2*na*nab/(nnab*(nnab+1))
            lower  = 2*(n+1)*(nb+1)/((nnab+1)*(nnab+2))
        out = diag([-np.sqrt(lower),np.sqrt(middle)],[-1,0])

    # <a,b| = <a+1,b| A+
    if op == 'A+':
        if a+b == 0 or a+b == -1:
            middle = (na+1)/(2*n+1)
            upper  = nb/(2*nab+1)
            middle[0], upper[0] = (1+a)*(1-(a+b)), 0
        else:
            middle = 2*(na+1)*(nab+1)/((nnab+1)*(nnab+2))
            upper  = 2*n*nb/(nnab*(nnab+1))
        out = diag([np.sqrt(middle),-np.sqrt(upper)],[0,+1])

    # (1+z) <a,b| = <a,b-1| B-
    if op == 'B-' and (b > 0):
        if a+b == 0:
            middle = nb/(2*n+1)
            lower  = (na+1)/(2*n+1)
            middle[0] = 2*b
        else:
            middle = 2*nb*nab/(nnab*(nnab+1))
            lower  = 2*(n+1)*(na+1)/((nnab+1)*(nnab+2))
        out = diag([np.sqrt(lower),np.sqrt(middle)],[-1,0])

    # <a,b| = <a,b+1| B+
    if op == 'B+':
        if a+b == 0 or a+b == -1:
            middle = (nb+1)/(2*n+1)
            upper  = na/(2*nab+1)
            middle[0], upper[0] = (1+b)*(1-(a+b)), 0
        else:
            middle = 2*(nb+1)*(nab+1)/((nnab+1)*(nnab+2))
            upper  = 2*n*na/(nnab*(nnab+1))
        out = diag([np.sqrt(middle),np.sqrt(upper)],[0,+1])

    # ( a - (1-z)*d/dz ) <a,b| = <a-1,b+1| C-
    if op == 'C-' and (a > 0):
        out = diag([np.sqrt(na*(nb+1))],[0])

    # ( b + (1+z)*d/dz ) <a,b| = <a+1,b-1| C+
    if op == 'C+' and (b > 0):
        out = diag([np.sqrt((na+1)*nb)],[0])

    # ( a(1+z) - b(1-z) - (1-z^2)*d/dz ) <a,b| = <a-1,b-1| D-
    if op == 'D-' and (a > 0) and (b > 0):
        out = diag([np.sqrt((n+1)*nab)],[-1])

    # d/dz <a,b| = <a+1,b+1| D+
    if op == 'D+':
        out = diag([np.sqrt(n*(nab+1))],[+1])

    # z <a,b| = <a,b| J
    if op == 'J':
        A, B = operator('A+',max_degree,a,b), operator('B+',max_degree,a,b)
        out = 0.5*( pull(B,B) - pull(A,A) )

    # <a,b|z=+1>
    if op == 'z=+1':
        n = np.arange(0,N,dtype=np.float64)
        out = np.sqrt(2*n+a+b+1)*np.sqrt(fun.binom(n+a,a))*np.sqrt(fun.binom(n+a+b,a))
        if a+b==-1:
            out[0] = np.sqrt(np.sin(np.pi*np.abs(a))/np.pi)

    # <a,b|z=-1>
    if op == 'z=-1':
        n = np.arange(0,N,dtype=np.float64)
        out = ((-1)**n)*np.sqrt(2*n+a+b+1)*np.sqrt(fun.binom(n+b,b))*np.sqrt(fun.binom(n+a+b,b))
        if a+b==-1:
            out[0] = np.sqrt(np.sin(np.pi*np.abs(b))/np.pi)

    if (op != 'z=+1') and (op != 'z=-1'):
        if format == 'dia': out = sparse.dia_matrix(out)
        if format == 'csr': out = sparse.csr_matrix(out)

    if rescale == None: return out

    return rescale*out


import sympy as sp

def calc_taylor_series(equation, xInit, a, n:int):
    """
    Method to estimate a function using taylor series

    Parameters:
    equation: The equation f(x)
    xInit: Initial value of x
    a: Another value of x
    n: number of derivatives
    """
    #Variables and settings
    x = sp.Symbol('x')
    fOri = equation
    xVal = xInit  #Initial value of x
    derivatives = []

    #Create derivatives
    derivatives.append(fOri)
    for i in range(1, n+1):
        derivatives.append(sp.diff(derivatives[i-1]))

    #Calculate derivatives
    for i in range(len(derivatives)):
        derivatives[i] = derivatives[i].evalf(subs={x: a})

    #Calculate series
    result = 0
    for i in range(len(derivatives)):
        result += (derivatives[i] * (xVal - a)**i)/sp.factorial(i)

    return result

def calc_maclaurin_series(equation, xInit, n:int):
    """
    Method to estimate a function using maclaurin series

    Parameters:
    equation: The equation f(x)
    xInit: Initial value of x
    n: number of derivatives
    """
    return calc_taylor_series(equation, xInit, 0, n)

def get_derivatives(equation, n:int):
    derivatives = []

    for _ in range(n):
        derivatives.append(sp.diff(equation))

    return derivatives

def get_n_derivative(equation, n:int):
    return get_derivatives(equation, n)[-1]

def example():
    x = sp.Symbol('x');
    ori = (sp.sin(x))**3
    print(calc_taylor_series(ori, 0.1, 1.5, 2))


import warnings
import math
import numpy as np
from scipy.stats import norm

def make_data(mu1, sigma1, mu2, sigma2, n_per_group, percent_outliers=0, sd_outlier_factor=2.0, rand_seed=None):
    """
    Auxiliary function for generating random values from a mixture of normal distributions.
    :param mu1: mean of first group
    :param sigma1: standard deviation of first group
    :param mu2: mean of second group
    :param sigma2: standard deviation of second group
    :param n_per_group: number of samples per group
    :param percent_outliers: percentage of outliers to generate
    :param sd_outlier_factor: factor to apply to sigma1, sigma2 to define outlier standard deviation
    :param rand_seed (optimal), int
    :return: y1, y2
    """
    if rand_seed is not None:
        np.random.seed(int(rand_seed))

    n_outliers = math.ceil(n_per_group * percent_outliers / 100)  # Number of outliers.
    n = n_per_group - n_outliers  # Number from main distribution.

    _validate_input(n, n_outliers, percent_outliers)

    y1 = _generate_data(mu1, sigma1, n, n_outliers, sd_outlier_factor)
    y2 = _generate_data(mu2, sigma2, n, n_outliers, sd_outlier_factor)

    return y1, y2

def _validate_input(n, n_outliers, percent_outliers):
    if percent_outliers > 100 or percent_outliers < 0:
        raise ValueError("percent_outliers must be between 0 and 100.")
    if 0 < percent_outliers < 1:
        raise ValueError("percent_outliers is specified as percentage 0-100, not proportion 0-1.")
    if percent_outliers > 50:
        warnings.warn("percent_outliers indicates more than 50% outliers; did you intend this?")
    if n_outliers < 2 and percent_outliers > 0:
        raise ValueError("Combination of nPerGrp and pcntOut yields too few outliers.")
    if n < 2:
        raise ValueError("Too few non-outliers.")

def _generate_data(mu, sigma, n, n_outliers, sd_outlier_factor):
    y = norm.rvs(loc=mu, scale=sigma, size=n)
    if n_outliers > 0:
        y_out = norm.rvs(size=n_outliers)  # Random values for outliers
        y_out = ((y_out - np.mean(y_out)) / np.std(y_out)) * (sigma * sd_outlier_factor) + mu  # Realize exactly.
        y = np.concatenate([y, y_out])
    return y


import numpy as np
# Painless algorithm using numpy

def solutionM(x):
    sar = np.sum(x)
    esar = np.sum(np.arange(1,len(x)+2,1))-sar
    if esar >= 1:
        return int(esar)
    else:
        return None

# Painless algorithm using only Python standard library
def solution(x):
    actual_sum = 0
    for i in x:
        actual_sum += i
    max_number = len(x) +1
    expected_sum = (max_number * (max_number+1)//2)
    return expected_sum - actual_sum

print (solutionM([2,3,1,5,4,9,8,6,7,11,12,13,14,15,16,26,17,10,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,34,35,36,37,39]))
print (solution([2,3,1,5,4,9,8,6,7,11,12,13,14,15,16,26,17,10,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,34,35,36,37,39]))
print (solutionM([]))
print (solution([]))
print (solutionM([1,2,3,4]))
print (solution([1,2,3,4,]))


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

def split(start_pt, end_pt, segments):
    """
        Segmenta la linea que va desde el punto start_pt a end_pt en la
        cantidad de segmentos definida por segments
    :param (tuple) start_pt:
        punto de partida
    :param (tuple) end_pt:
        punto final
    :param (int) segments:
        cantidad de segmentos que se desean
    :return (list(tuple)):
        lista con todos los puntos de las divisiones de los segmentos de la
        linea
    """
    # Define el incremento que hay que hacer en el eje x y eje y por cada segmento
    x_delta = (end_pt[0] - start_pt[0]) / float(segments)
    y_delta = (end_pt[1] - start_pt[1]) / float(segments)
    points = []
    # Añade cada punto de cada segmento a la lista points
    for i in range(1, segments):
        points.append((round(start_pt[0] + i * x_delta), round(start_pt[1] + i * y_delta)))
    # Agrega puntos inicial y final a points y retorna esto
    return [start_pt] + points + [end_pt]

def angle_line(point, angle, length):
    """
        Genera una recta en el punto point con un ángulo igual a
        angle y una longitud de 2*length. Retorna el punto inicial
        y el final de la recta
    :param (tuple) point:
        punto donde se quiere generar la recta
    :param (number) angle:
        ángulo de la recta (con respecto a la horizontal)
    :param (int) length:
        longitud de cada brazo de la recta
    :return (tuple,tuple):
        el punto inicial y el final de la recta
    """
    x, y = point
    # Define cual es el desface (absoluto) de los límite
    # de la recta con respecto al punto origen
    delta_y = length * math.sin(math.radians(angle))
    delta_x = length * math.cos(math.radians(angle))

    # Devuelve una recta con longitud 2*length
    return (x-delta_x,y-delta_y),(x+delta_x,y+delta_y)

def line_intersection(line1, line2):
    """
        Encuentra la intersección entre las rectas generadas por los pares
        de puntos line1 y line2, si es que lo hay
    :param (tuple(tuple)) line1:
        par de puntos de primera recta
    :param (tuple(tuple)) line2:
        par de puntos de segunda recta
    :return (int,int) o (None):
        las coordenadas x e y de la intersección. Si no hay intersección
        retorna un None
    """
    # Define las diferencias de coordenadas entre los puntos de cada par
    # de puntos de las lineas
    xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
    ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])
    def det(a, b):
        """
            Retorna la determinante entre la matriz formada
            por la concatenación de los vectores a y b
        :param (tuple) a:
            primer vector
        :param (tuple) b:
            segundo vector
        :return (int):
            la determinante
        """
        return a[0] * b[1] - a[1] * b[0]

    # Define la determinante entre los deltas de coordenadas de
    # cada par de puntos
    div = det(xdiff, ydiff)
    if div == 0:
       return None
    d = (det(*line1), det(*line2))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div
    return int(x), int(y)

def check_in_segment(pt,line):
    """
        Chequea si es que el punto pt pertenece al segmento de linea que une
        el par de puntos line
    :param (tuple) pt:
        punto cuya pertenencia a segmento se quiere verificar
    :param (tuple(tuple)) line:
        par de puntos que define el segmento de linea
    :return (bool):
        si el punto pertenece o no al segmento de linea
    """
    # Este parámetro define un margen de error (pixeles) para verificar la pertenencia del
    # punto en el segmento
    error = 1
    # Verifica si las coordenadas en x del punto e encuentran en el rango horizontal definido
    # por el par de puntos
    check1 = min(line[0][0], line[1][0])-error  <= pt[0] <= max(line[0][0], line[1][0])+error
    # Verifica si las coordenadas en y del punto e encuentran en el rango vertical definido
    # por el par de puntos
    check2 = min(line[0][1], line[1][1])-error  <= pt[1] <= max(line[0][1], line[1][1])+error
    # Se cumple pertenencia si ambas verificaciones de rango se cumplen
    return check1 and check2

def find_polygon_divisions(line_pts, polygon_pts, angle):
    """
        Esta función primero genera una recta, con un ángulo definido por angle, en cada
        punto contenido dentro de line_pts. Luego, para cada recta generada, encuentra la intersección
        con cada mitad del polígono definido en polygon_pts. Retorna los puntos de intersección encontrados
    :param list(tuples) line_pts:
        puntos a partir de los cuales se generan las rectas de intersección
    :param list(list(tuples),list(tuples)) polygon_pts:
        vértices del polígono almacenados como una lista de dos listas. la primera de estas listas
        contiene los vértices del polígono de los lados donde empiezan las hileras y la segunda de
        estas listas los vértices del polígono de los lados donde terminan las hileras
    :param (number) angle:
        ángulo, con respecto a la horizontal, con la cual se generan las rectas de intersección
    :return list(tuples), list(tuples):
        las coordenadas de los puntos de intersección encontrados para cada lado del polígono
    """
    # Vértices de los lados donde empiezan las hileras
    start_poly_vertices = polygon_pts[0]

    # Vértices de los lados donde terminan las hileras
    end_poly_vertices = polygon_pts[1]

    # Largo de los brazos de cada recta de intersección generada
    line_length = 5000
    # Se inicializan los primeros par de vértices, tanto para los vértices finales
    # como iniciales, donde se evaluará intersección
    start_v_line = (start_poly_vertices[0], start_poly_vertices[1])
    end_v_line = (end_poly_vertices[0], end_poly_vertices[1])
    start = []
    end = []
    # Las variables siguientes se ocupan para ir cambiando los pares de puntos de cada lado
    # en caso de no encontrar intersección
    start_pt_indx = 0
    end_pt_indx = 0
    for pt in line_pts:
        # Genera una recta con ángulo angle y de longitud 2*length sobre el punto pt
        pt_line = angle_line((pt[0],pt[1]),angle,line_length)
        # Encuentra la intersección de la recta anterior con el lado donde empiezan las hileras
        # y el lado donde terminan
        start_intersection = (line_intersection(start_v_line,pt_line))
        end_intersection = (line_intersection(end_v_line,pt_line))
        start_sign = 1
        # Mientras no se encuentre alguna intersección con el lado de inicio o la intersección
        # encontrada no pertenezca al perímetro del polígono, se sigue intentando con otro lado
        # de inicio
        while start_intersection is None or not check_in_segment(start_intersection,start_v_line):
            # Indice del primer vertice del lado inicial
            start_pt_indx += 1*start_sign
            # Si el índice es igual o mayor al largo de la lista de vértices, se invierte
            # el sentido de exploración de esta. Lo mismo para si es menor a 0
            if start_pt_indx >= len(start_poly_vertices)-1:
                start_pt_indx -=2
                start_sign = -1
            elif start_pt_indx < 0:
                start_pt_indx += 2
                start_sign = 1
            # Actualiza el lado inicial para el cual se evaluará la intersección
            start_v_line = (start_poly_vertices[start_pt_indx],\
                            start_poly_vertices[start_pt_indx+1])
            start_intersection = (line_intersection(start_v_line,pt_line))
        end_sign = 1
        # Hace lo mismo que el loop anterior pero para los lados de fin de la hilera
        while end_intersection is None or not check_in_segment(end_intersection,end_v_line):
            end_pt_indx += 1*end_sign
            if end_pt_indx >= len(end_poly_vertices)-1:
                end_pt_indx -=2
                end_sign=-1
            elif end_pt_indx < 0:
                end_pt_indx += 2
                end_sign = 1
            end_v_line = (end_poly_vertices[end_pt_indx],\
                        end_poly_vertices[end_pt_indx+1])
            end_intersection = (line_intersection(end_v_line,pt_line))
        # Agrega el punto de intersección con el lado inicial y con el lado final
        # de la hilera a las lista correspondiente
        start.append(start_intersection)
        end.append(end_intersection)
    # Elimina los puntos de intersección encontrados para los puntos iniciales
    # y finales de cada parte del polígono (generan hileras muy chicas)
    start = start[1:-1]
    end = end[1:-1]
    return start, end

if __name__ == '__main__':
    # Algunas pruebas
    polygon = [[(641,38),(402,38),(388,613)],[(641,38),(649,610),(388,613)]]
    hilera_or = [polygon[0][2],polygon[1][0]]
    pts = split(hilera_or[0],hilera_or[1],113)
    start, end = find_polygon_divisions(pts,polygon,70)
    print(len(start))
    print(len(end))
    fig,ax = plt.subplots()
    for pt in start:
        ax.plot(start_pt[0],start_pt[1],"*")
    for pt in end:
        ax.plot(end_pt[0],end_pt[1],"*")

    img = plt.imread("./satImages/curicoMap.jpg")
    ax.imshow(img)
    plt.show()


import csv
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
import numpy as np
from scipy.special import expit

csvfile=open("logisticX.csv", 'r')
x = list(csv.reader(csvfile))
csvfile=open("logisticY.csv", 'r')
y = list(csv.reader(csvfile))

m=len(x)
n=2
x3=[]
y2=[]
for i in range(m):
    x3.append([float(x[i][0]),float(x[i][1])])
    y2.append(float(y[i][0]))

# normalise the data
meanx=[0,0]
# meanx[0]=sum(x3[0])/m
# meanx[1]=sum(x3[1])/m

for i in range(m):
    meanx[0]+=x3[i][0]
    meanx[1]+=x3[i][1]
meanx[0]/=m
meanx[1]/=m

v=[0,0]
for i in range(m):
    temp=x3[i][0]-meanx[0]
    v[0]+=temp*temp
    temp=x3[i][1]-meanx[1]
    v[1]+=temp*temp
v[0]/=m
v[0]=math.sqrt(v[0])
v[1]/=m
v[1]=math.sqrt(v[1])
for i in range(m):
    x3[i][0]=(x3[i][0]-meanx[0])/v[0]
    x3[i][1]=(x3[i][1]-meanx[1])/v[1]

x2=[]
# for y=1
x4=[]
x5=[]
# for y=0
x6=[]
x7=[]

for i in range(m):
    x2.append(np.array([1,x3[i][0],x3[i][1]]))

    if(y2[i]==1):
        x4.append(float(x3[i][0]))
        x5.append(float(x3[i][1]))
    else:
        x6.append(float(x3[i][0]))
        x7.append(float(x3[i][1]))

X=np.array(x2)
Y=np.array(y2)

epsilon=0.000001
t=0
theta=np.array([0,0,0])

while(True):

    X3=np.matmul(X,theta)
    X2=expit(np.matmul(X,theta))

    gradient=np.dot(X.T,(Y-X2))

# calulate Hessian matrix
    H=np.zeros((n+1,n+1))
    for j in range(m):
        x2=X3[j]
        H=H-((expit(x2)*(1-expit(x2)))*(np.outer(X[j].T,X[j])))

    t+=1

# update theta
    thetaold=theta
    theta=theta-np.matmul(np.linalg.inv(H),gradient)

# check if converged
    convergence=np.linalg.norm(theta-thetaold)
    if(convergence<epsilon):
        break

print("Number of iterations={}".format(t))
print("theta={}".format(theta))

plt.ioff()
fig=plt.figure()
ax1 = fig.add_subplot(1,1,1)

# plots the training data & decision boundary
def pl():
    ax1.clear()
    ax1.scatter(x4, x5, label= "y=1 Training Data", color= "red",
                marker= ".", s=10)
    ax1.scatter(x6, x7, label= "y=0 Training Data", color= "blue",
                marker= ".", s=10)

    xvalues=np.linspace(min(min(x4),min(x6)),max(max(x4),max(x6)),100)

    the=theta
    yvalues=-1*(the[1]*xvalues+the[0])/the[2]

    ax1.plot(xvalues, yvalues, label="Decision boundary learned",color ="#66cc00")

    plt.xlabel('x1')
    plt.ylabel('x2')
    plt.legend()
    plt.title('Q3 (b)')
    plt.show()
    plt.pause(0.001)

pl()


import numpy as np

def kernelList(restrictiveU):
    if restrictiveU:
        return ['uniform', 'triangle', 'cosinus', 'epanechnikov1', 'epanechnikov2', 'epanechnikov3']
    else:
        return ['gaussian', 'cauchy', 'picard']

def kernel(kernelString):
    if kernelString == 'gaussian':
        return gaussianKernel
    elif kernelString == 'cauchy':
        return cauchyKernel
    elif kernelString == 'picard':
        return picardKernel
    elif kernelString == 'uniform':
        return uniformKernel
    elif kernelString == 'triangle':
        return triangleKernel
    elif kernelString == 'cosinus':
        return cosKernel
    elif kernelString == 'epanechnikov1':
        return epanichnikov1
    elif kernelString == 'epanechnikov2':
        return epanichnikov1
    elif kernelString == 'epanechnikov3':
        return epanichnikov1
    else:
        raise NameError('Kernel function not found! Use a valid kernel function.')

def gaussianKernel(u, derivative):
    K = 1 / np.sqrt(2 * np.pi) * np.exp(-np.power(u, 2) / 2)
    if derivative == 0:
        return K
    else:
        dK = -u * K

        if derivative == 1:
            return K, dK
        else:
            ddK = (np.power(u, 2) - 1) * K

            return K, dK, ddK

def cauchyKernel(u, derivative):
    K = 1 / np.sqrt(np.pi * (1+np.power(u, 2)))
    if derivative == 0:
        return K
    else:
        dK = -2*u /(1+np.power(u, 2)) * K

        if derivative == 1:
            return K, dK
        else:
            ddK = (-2*np.power(1+np.power(u, 2),2) + 8*np.power(u, 2)) / \
                  (np.pi * np.power(1+np.power(u, 2),3))

            return K, dK, ddK

def picardKernel(u, derivative):
    K = 1 / 2 * np.exp(-np.abs(u))
    if derivative == 0:
        return K
    else:
        dK = -np.sign(u) * K

        if derivative == 1:
            return K, dK
        else:
            ddK = K

            return K, dK, ddK

def uniformKernel(u, derivative):
    indeces_u = np.abs(u) > 1

    K = 1 / 2 * np.ones(u.shape)
    K[indeces_u] = 0

    if derivative == 0:
        return K
    else:
        dK = np.zeros(u.shape)

        if derivative == 1:
            return K, dK
        else:
            ddK = np.zeros(u.shape)

            return K, dK, ddK

def triangleKernel(u, derivative):
    indeces_u = np.abs(u) > 1

    K = 1 - np.abs(u)
    K[indeces_u] = 0

    if derivative == 0:
        return K
    else:
        dK = -np.sign(u)
        dK[indeces_u] = 0

        if derivative == 1:
            return K, dK
        else:
            ddK = np.zeros(u.shape)

            return K, dK, ddK

def cosKernel(u, derivative):
    indeces_u = np.abs(u) > 1

    K = np.pi/4 * np.cos(np.pi/2*u)
    K[indeces_u] = 0

    if derivative == 0:
        return K
    else:
        dK = -np.power(np.pi,2)/8 * np.sin(np.pi/2*u)
        dK[indeces_u] = 0

        if derivative == 1:
            return K, dK
        else:
            ddK = -np.power(np.pi,3)/16 * np.cos(np.pi/2*u)
            ddK[indeces_u] = 0
            return K, dK, ddK

def epanechnikovKernel(u, derivative, p):
    indeces_u = np.abs(u) > 1

    if p == 1:
        Cp = 3/4
    elif p == 2:
        Cp = 15/16
    elif p == 3:
        Cp = 35/32
    else:
        raise ValueError('Wrong p! Use 1, 2 or 3.')

    K = Cp * np.power(1-np.power(u,2),p)
    K[indeces_u] = 0

    if derivative == 0:
        return K
    else:
        dK = -2*p*Cp*u*np.power(1-np.power(u,2),p-1)
        dK[indeces_u] = 0

        if derivative == 1:
            return K, dK
        else:
            ddK = 2*p*Cp*(2*np.power(u,2)*np.power(1-np.power(u,2),p-2)\
                           -np.power(1-np.power(u,2),p-1))
            ddK[indeces_u] = 0
            return K, dK, ddK

def epanichnikov1(u, derivative):
    return epanechnikovKernel(u, derivative, p = 1)

def epanichnikov2(u, derivative):
    return epanechnikovKernel(u, derivative, p = 2)

def epanichnikov3(u, derivative):
    return epanechnikovKernel(u, derivative, p = 3)

def nadarayaWatsonEstomator(u_feature, y_feature, kernelstring, h, scaleKernel=True, \
                            derivative=0):
    m_u = u_feature.shape

    u = u_feature / h

    kernelFunction = kernel(kernelstring)

    if derivative == 0:
        K = kernelFunction(u, derivative)
    elif derivative == 1:
        K, dK = kernelFunction(u, derivative)
    else:
        K, dK, ddK = kernelFunction(u, derivative)

    # print(f'a={a}')
    # print(f'b={b}')
    # print(f'K={K}')
    # print(f'm_u={m_u}')
    # print(f'u_feature={u_feature}')

    if scaleKernel:
        Kh = K / h

        a = np.dot(Kh, y_feature)
        b = np.sum(Kh, axis=1)
    else:
        a = np.dot(K, y_feature)
        b = np.sum(K, axis=1)

    # print(f'b={b}')
    # print(f'a={a}')
    # print(f'Kh={Kh}')
    # m = a / b
    m = np.divide(a, b)

    if np.any(np.isnan(m)):
        if scaleKernel:
            m[np.isnan(m)] = np.sum(y_feature / h) / np.sum(1 / h)
        else:
            m[np.isnan(m)] = np.sum(y_feature)

    if derivative == 0:
        return m, b / K.shape[1]
    else:
        if scaleKernel:
            dKh = -(K + u * dK) / np.power(h, 2)
            da = dKh * y_feature
            db = dKh
        else:
            dK = -u / h * dK
            da = dK * y_feature
            db = dK

        if h.size == 1:
            da = np.sum(da, axis=1)
            db = np.sum(db, axis=1)
        else:
            a = a.reshape((m_u[0], 1))
            b = b.reshape((m_u[0], 1))

        dm = da / b - a * db / np.power(b, 2)

        if np.any(np.isnan(dm)):
            dm[np.isnan(dm)] = 0

        if np.any(np.isinf(dm)):
            dm[np.isinf(dm)] = 0

        if derivative == 1:
            return m, dm
        else:
            if scaleKernel:
                ddKh = -(u * dK + K) / np.power(h, 3) - u / h * (dKh + u * ddK)
                dda = ddKh * y_feature
                ddb = ddKh
            else:
                ddK = u / np.power(h, 2) * (2 * dK + u * ddK)
                dda = ddK * y_feature
                ddb = ddK

            if h.size == 1:
                dda = np.sum(dda, axis=1)
                ddb = np.sum(ddb, axis=1)

            ddm = (dda * b - 2 * da * db - a * ddb) / np.power(b, 2) + \
                  (a * np.power(db, 2) / np.power(b, 3))

            if np.any(np.isnan(ddm)):
                if scaleKernel:
                    ddm[np.isnan(ddm)] = -np.sum(y_feature / h) / np.sum(1 / h)
                else:
                    ddm[np.isnan(ddm)] = -np.sum(y_feature)

            return m, dm, ddm


import numpy as np
import math

def multiply_veca_vecb(vec_a: np.ndarray, vec_b: np.ndarray) -> float:
    """returns :math:`a \\cdot b`. this dot means inner product.

    Parameters
    ----------
    vec_a : np.ndarray
        vector a.
    vec_b : np.ndarray
        vector b.

    Returns
    -------
    float
        value of :math:`a \\cdot b`.

    Raises
    ------
    ValueError
        dimension of vec_a is not 1.(not vector)
    ValueError
        dimension of vec_b is not 1.(not vector)
    """
    if vec_a.ndim != 1:
        raise ValueError(
            f"dimension of vec_a must be 1. dimension of vec_a is {vec_a.ndim}"
        )
    if vec_b.ndim != 1:
        raise ValueError(
            f"dimension of vec_b must be 1. dimension of vec_b is {vec_b.ndim}"
        )

    val = np.dot(vec_a, vec_b)
    return val

def multiply_veca_vecb_matc(
    vec_a: np.ndarray, vec_b: np.ndarray, mat_c: np.ndarray
) -> float:
    """returns :math:`a \\cdot C b`. this dot means inner product.

    Parameters
    ----------
    vec_a : np.ndarray
        vector a.
    vec_b : np.ndarray
        vector b.
    mat_c : np.ndarray
        matrix C.

    Returns
    -------
    float
        value of :math:`a \\cdot C b`.

    Raises
    ------
    ValueError
        dimension of vec_a is not 1.(not vector)
    ValueError
        dimension of vec_b is not 1.(not vector)
    ValueError
        dimension of mat_c is not 2.(not matrix)
    """
    if vec_a.ndim != 1:
        raise ValueError(
            f"dimension of vec_a must be 1. dimension of vec_a is {vec_a.ndim}"
        )
    if vec_b.ndim != 1:
        raise ValueError(
            f"dimension of vec_b must be 1. dimension of vec_b is {vec_b.ndim}"
        )
    if mat_c.ndim != 2:
        raise ValueError(
            f"dimension of mat_c must be 2. dimension of mat_c is {mat_c.ndim}"
        )

    val = np.dot(vec_a, mat_c @ vec_b)
    return val

def project_to_traceless_matrix(A: np.ndarray) -> np.ndarray:
    """returns a matrix projected to a trace-less matrix subspace.

    Parameters
    ----------
    A : np.ndarray
        Square matrix

    Returns
    ----------
    B : np.ndarray
        Square matrix, :math:`B = A - \\mathrm{Tr}[b_0 A] b_0`, where :math:`b_0 = \\frac{I}{\\sqrt{d}}`.

    """
    if A.shape[0] != A.shape[1]:
        raise ValueError(f"A must be square matrix.")

    d = A.shape[0]
    b0 = np.eye(d) / math.sqrt(d)
    tr = np.trace(b0 @ A)
    B = A - tr * b0
    return B


import argparse
import numpy as np
from scipy.constants import h, hbar, m_e, pi
from scipy.linalg import eigh
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerTuple
import seaborn as sns
#sns.set()
from utils.plotting import latexconfig
from utils.metrics import rmse

parser = argparse.ArgumentParser(description='Takes the number of grid points.')
parser.add_argument('-n', '--num_of_points',
                    type=int,
                    metavar='',
                    help='Finite Difference approximation of the wave equation for\
                        an electron in the infinite quantum well based on the given\
                        number of grid points.',
                    default=10)

def psi_close_form(x, n, L):
    """analyitcal solution for 1-D wave eqn"""
    amp = np.sqrt(2/L)
    return amp * np.sin(n * pi / L * x)

def pdf_close_form(x, n, L):
    r"""probability of locating an electron in x \in (0, L)"""
    return psi_close_form(x, n, L)**2

def V(x):
    """Potential function"""
    return 0. * x

def fdm(N, xmin, L, bcs):
    """(- hbar**2/(2*m_e) * d^2/dx^2) psi = E * psi"""
    dx = (L - xmin)/(N-1)           # distance between grid points
    xmesh = np.linspace(xmin, L, N) # mesh

    # hamiltonian formulation
    d_xx = np.diag(np.full(N, 2), k=0) +\
          np.diag(np.full(N-1, -1), k=-1) +\
          np.diag(np.full(N-1, -1), k=+1)
    H = hbar/(2 * m_e * dx**2) * d_xx + np.diag(np.full(N, V(xmesh)), k=0)

    # applying dirichlet conds
    if bcs == (0, 0):
        H = H[1:-1, 1:-1]
        # eigenvalues (E) and eigenvector (psi)
        E, psi = eigh(H)
        psi = np.r_[np.zeros((1, psi.shape[1])), psi]
        psi = np.r_[psi, np.zeros((1, psi.shape[1]))]
    else:
        raise ValueError

    # eigenvector normalization
    psi = psi * np.linalg.norm(psi)

    # probability density function |psi|^2
    pdf = psi * np.conj(psi)
    return E, psi[::-1], pdf

def main():
    args = parser.parse_args()
    N = args.num_of_points          # number of grid points
    xmin = 0.0                      # left boundary
    L = 1.0                         # right boundary
    psi_0 = 0                       # left Dirichlet bc
    psi_L = 0                       # right Dirichlet bc
    x = np.linspace(xmin, L, N)
    _x = np.linspace(xmin, L, 1000)

    principal_quantum_numbers = list(range(1, 4))
    nonreletivistic_energies = []
    for n in principal_quantum_numbers:
        E = n**2 * h**2 / (8 * m_e * L)
        nonreletivistic_energies.append(E)

    # H psi = E psi
    # H = - hbar**2/(2*m_e) * d^2/dx^2 -> eigenvalue problem
    E, psi, pdf = fdm(N, xmin, L, bcs=(psi_0, psi_L))

    fig, axs = plt.subplots(len(principal_quantum_numbers), 1, sharex='all', figsize=(7, 9))
    for i, (n, E_analytic) in enumerate(zip(principal_quantum_numbers, nonreletivistic_energies)):
        l1, = axs[i].plot(_x, psi_close_form(_x, n, L), 'b-')
        l2, = axs[i].plot(_x, pdf_close_form(_x, n, L), 'r-')
        l3, = axs[i].plot(x, psi[:, i], 'bo', markersize=5, alpha=0.8)
        l4, = axs[i].plot(x, pdf[:, i], 'ro', markersize=5, alpha=0.8)

        pdf_analytic = pdf_close_form(x, n, L)
        axs[i].set_title(f'RMSE = {np.round(rmse(pdf_analytic, pdf[:, i]), 5)}')
        axs[i].legend([l1, l2, (l3, l4)], [r'$\psi(x)$', r'$|\psi(x)|^2$', 'FDM'],
                      handler_map={tuple: HandlerTuple(ndivide=None)})
    plt.xlabel(r'$x$')
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    latexconfig()
    main()


# %% Time results using fft
import numpy as np
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N * T, N)
y = np.sin(50.0 * 2.0 * np.pi * x) + 0.5 * np.sin(80.0 * 2.0 * np.pi * x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0 / (2.0 * T), int(N / 2))

plt.plot(x, y, ".")

fig, ax = plt.subplots()
ax.plot(xf, 2.0 / N * np.abs(yf[: N // 2]))
plt.show()

temp.loc[:, ["datetimeint", "count"]]
norm_count = (temp["count"] - temp["count"].mean()).to_numpy()
norm_count.shape
yf = scipy.fftpack.fft(norm_count,)
xf = temp["datetimeint"]
temp["datetimeint"].max() - temp["datetimeint"].min()
# np.linspace(0.0, 1.0/(2.0*T), int(N/2))

fig, ax = plt.subplots()
ax.plot(xf, 2.0 / N * np.abs(yf))
ax.plot(xf, 2.0 / N * np.abs(yf[: N // 2]))
plt.show()

plt.plot(temp["datetimeint"].diff())

temp["datetimeint"].diff()[1]

temp["datetimeint"][:2]

Y = np.fft.fft(norm_count)
freq = np.fft.fftfreq(len(norm_count), temp["datetimeint"].diff()[1])

plt.figure()
plt.plot(freq, np.abs(Y), ".")
plt.figure()
plt.plot(freq, np.angle(Y))
plt.show()

# %% [markdown]
# Convert to jupyter notebook -> Export current (no output)
# # Convert to markdown file
# `jupyter nbconvert data_proc.ipynb --to markdown`


#!/usr/bin/env python3
"""
Optimization Module
"""
import numpy as np

def moving_average(data, beta):
    """
    Calculates the WMA using bias correction

    data is the list of data to calculate the moving average of
    beta is the weight used for the moving average

    Returns: a list containing the moving averages of data
    """
    vt = 0
    ema = []
    for i in range(len(data)):
        vt = (beta * vt) + ((1 - beta) * data[i])
        ema.append(vt / (1 - (beta ** (i + 1))))
    return ema


import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp

def henon_heiles_rhs(t, s):
    x, y, px, py = s
    Fx = - x - 2*x*y
    Fy = - y - (x**2 - y**2)
    return px, py, Fx, Fy

def henon_heiles_system(initial_pos, initial_vel,
        time_span=100, num_samples=1000):
    sol = solve_ivp(henon_heiles_rhs, (0, time_span),
            tuple(initial_pos) + tuple(initial_vel),
            t_eval=np.linspace(0, time_span, num_samples), method="BDF")
    plt.plot(sol.y[0, :], sol.y[1, :])
    plt.plot([initial_pos[0]], [initial_pos[1]], "or")
    plt.title("Henon Heiles System")
    plt.xlabel("x")
    plt.ylabel("y")
    plt.show()
    return sol

if __name__ == "__main__":
    henon_heiles_system((0, 1), (0.01, 0))


import numpy as np
from numpy.linalg import inv
from InputFunction import func, gradf, hesf
from numpy.linalg import norm as L2

def hessupd(Bi, yi, yj):
    p=yj-yi
    q=gradf(yj)-gradf(yi)
    r1=np.dot(p,q)
    r2=np.dot(q, np.dot(Bi, q))
    t1=np.outer(p, p)/r1
    t2=np.dot(np.dot(Bi, np.outer(q, q)), Bi)/r2
    return (Bi+t1-t2)
#def newton(X, mxiter=30, err=0.001, miter_LS=30,rho_LS=0.15, sigma_LS=0.30, err_LS=10**-4):
def DFP(X, mxiter=30, DIH=1, dimf=2, err=0.0001, LSf='1', LSP={}):
    valsX = [X]
    l=[]
    vals = []
    ovfs = []
    if(DIH==0):
        Bi=B0=inv(hesf(X))
    elif(DIH==1):
        Bi=B0=np.identity(dimf, dtype = float)
    Y=X
    i, j, k=0, 1, 1
    while (i < mxiter):
        i += 1
        vals.append(Y)
        ovfs.append(func(Y))
        gr=gradf(Y)
        if(L2(gr)<err):
            break;

        d=np.dot(Bi, gr)
        l.append(np.array([Y, -d]))
        if(LSf=='1'):
            step=0.10
        else:
            if(bool(LSP)):
                step=LSf(Y, -d, LSP)
            else:
                step=LSf(Y, -d)

        temp = Y-step*d
        if (np.abs(func(temp)-func(Y))<err):
            break;

        if(j==dimf):
            k+=1
            j=1
            Bi=B0
            X=temp
            Y=X
            valsX.append(X)

        elif(j<dimf):
            Bj=hessupd(Bi, Y, temp)
            j+=1
            Bi=Bj
            Y=temp
    if(i==mxiter):
        print("Algorithm failed to converge in. Increasing 'maXiter' maXimum of iteration may give better result")

    return (np.array(valsX), np.array(vals), np.array(ovfs), i, np.array(l))


#!/usr/bin/env python3
# coding=utf-8

import numpy as np

from householder_method.householder_algorithm import householder_algorithm
from QR_method.QR_algorithm import QR_algorithm

INFO = '''
TAREFA 4.1:
(a) Tarefa 4.1.a: Calculo dos auto valores e autovetores da matriz:
    [2., 4., 1., 1.]
    [4., 2., 1., 1.]
    [1., 1., 1., 2.]
    [1., 1., 2., 1.]

(b) Tarefa 4.1.b: Calculo dos auto valores e autovetores da matriz:
    [ n  n-1 n-2 ...  2   1 ]
    [n-1 n-1 n-2 ...  2   1 ]
    [n-2 n-2 n-2 ...  2   1 ]
    [ :   :   :  ...  :   : ]
    [ 2   2   2   2   2   1 ]
    [ 1   1   1   1   1   1 ]
'''
def make_matrixB(n):
    B = np.zeros((n,n),dtype = float)
    for i in range (2,n+2,1):
        add = (i-1)*np.ones((n-i+2,n-i+2),dtype=float)
        B[0:n-i+2,0:n-i+2] = add[0:n-i+2:,0:n-i+2]
    return B

def make_matrixB_alt(n):
    B= np.zeros((n,n))
    B[0,0] = n
    for i in range (1,len(B),1):
        B[i,0:i] = n-i
        B[0:i,i] = n-i
        B[i,i] = n-i
    return B

def get_eigenvalues(n):
    lamb = np.array([])
    eigenvaluesi = lambda i: ((1/2)*((1 - np.cos((2*i-1)*np.pi/(2*n+1)))**(-1)))
    for i in range(1,n+1):
        lamb = np.append(lamb,eigenvaluesi(i))
    return lamb

def assignment_1_a(debug = False):
    np.set_printoptions(precision=5,threshold=5) #print options
    A = np.array([[2., 4., 1., 1.],
                  [4., 2., 1., 1.],
                  [1., 1., 1., 2.],
                  [1., 1., 2., 1.]])

    A = householder_algorithm(A)
    V,lamb = QR_algorithm(A)[0:2]
    print("autovetores =\n",V)
    print("autovalores =\n",np.diagonal(lamb),"\n")

    if(debug == True):
        for i in range(0,len(A)):
            print('teste {j}'.format(j=i))
            print(A@V[:,i],"\n",np.diagonal(lamb)[i]*V[:,i],"\n")

def assignment_1_b():
    np.set_printoptions(precision=3,threshold=200,linewidth=1000) #print options
    n = 20
    B = make_matrixB(n)
    B = householder_algorithm(B)
    V,lamb = QR_algorithm(B)[0:2]
    eigenvalues = get_eigenvalues(n)

    print("autovetores =\n",V)
    print("autovalores =\n",np.diagonal(lamb))
    print("autovalores esperados =\n",eigenvalues)

def assignment_1():
    print(INFO,"\n")
    item = input("Gostaria de ver qual item? ")
    if ((item == "a") or (item == "4.1.a")):
        assignment_1_a()
        print("\n")
    elif ((item == "b") or (item == "4.1.b")):
        assignment_1_b()
        print("\n")
    else:
        print("your input isn't valid \nplease, try again\n")

if __name__ == "__main__":
    try:
        assignment_1()

    except KeyboardInterrupt:
        print("\n Better luck next time")