# -*- coding: utf-8 -*-
"""
Created on Sun Oct 30 17:04:38 2016

@author: Owner
"""

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

# ===============  Gauss Elimination =============== #

def Gaussian_elimination(n, A, b):
    x = np.zeros(n)

    # Forward elimination
    for k in range(1, n):  # e.g. for column 1
        for i in range(k + 1, n + 1):  # from line 2 to n
            w = A[i-1, k-1]/A[k-1, k-1]

            for j in range(k, n + 1):  # from line 1 to n in column
                A[i-1, j-1] = A[i-1, j-1] - w*A[k-1, j-1]

            b[i-1] = b[i-1] - w*b[k-1]

    x[n-1] = b[n-1]/A[n-1, n-1]
    # print np.rint(A)#でAを整数(四捨六入で５は偶数になるほうに丸める)に変換して表示（上三角)

    # Back substitution
    for i in range(n - 1, 0, -1):
        for j in range(i + 1, n + 1):
            b[i-1] = b[i-1] - A[i-1, j-1]*x[j-1]

        x[i-1] = b[i-1]/A[i-1, i-1]

    return x
    print(x)
# ==================================================== #

# ============== Gauss Elimination w/ Partial Pivoting ============= #

def Gaussian_elimination_pivot(n, A, b):
    x = np.zeros(n)

    piv = np.zeros(n).astype(int)  # numpyオブジェクト.astype(型)でまとめて型変換
    for i in range(0, n):
        piv[i] = i  # piv = [0,1,2,...,n-1]

    for k in range(0, n):
        amax = A[piv[k], k]  # k列の(k,k)以下の行の要素のなかで最大値を探す
        imax = k
        for i in range(k+1, n):
            if amax < abs(A[piv[i], k]):
                amax = abs(A[piv[i], k])
                imax = i
            else:
                pass
            if imax != k:  # Aの行を直接は入れ替えず、pivで入れ替えを記憶
                a = piv[k]
                piv[k] = piv[imax]
                piv[imax] = a
            else:
                pass
        for i in range(k+1, n):
            w = A[piv[i], k]/A[piv[k], k]
            for j in range(k+1, n):
                A[piv[i], j] = A[piv[i], j] - w*A[piv[k], j]

            b[piv[i]] = b[piv[i]] - w*b[piv[k]]

    x[n-1] = b[piv[n-1]]/A[piv[n-1], n-1]

    for i in range(n-2, -1, -1):
        for j in range(i+1, n):
            b[piv[i]] = b[piv[i]] - A[piv[i], j]*x[j]
        x[i] = b[piv[i]]/A[piv[i], i]

    return x
    print(x)
# ===================================================== #

# ================== Jacobi method ====================== #

def Jacobi_method(n, A, b, eps):
    # A = L + D + Uと分解し、Dx = (-L-U)x + bを収束させる。
    e = 1.0  # 誤差ベクトル（epsより大きければなんでもいい）
    list_e = []

    x = np.zeros(n)
    for i in range(0, n):
        x[i] = b[i]/A[i, i]
        # Aの対角成分が支配的となることがおおいので、解の初期値は何の情報もない場合はこうしておく。
    D = np.diagflat(np.diag(A))
    T = -(A - D)

    while e > eps:
        list_e.append(e)
        x_old = x
        x = np.dot(np.diagflat(1/np.diag(A)), np.dot(T, x_old) + b)
        e = np.linalg.norm(x - x_old)

    return x
    print(x)
    print("Jacobi法ーー誤差:{0}".format(e))

    plt.plot(list_e)
    plt.show()

# ================================================================= #

# ============================= Gauss-Seidel method ======================= #

def Gauss_Seidel(n, A, b, eps):
    # A = L + D + Uと分解し、(D+L)x = -Ux + bを収束させる。
    e = 1.0  # 誤差ベクトル（epsより大きければなんでもいい）
    list_e = []

    x = np.zeros(n)
    for i in range(0, n):
        x[i] = b[i]/A[i, i]
        # Aの対角成分が支配的となることがおおいので、解の初期値は何の情報もない場合はこうしておく。

    S = np.tril(A)  # S = L + D
    T = -(A - S)
    S_inv = np.linalg.inv(S)

    while e > eps:
        list_e.append(e)
        x_old = x
        x = np.dot(S_inv, np.dot(T, x_old) + b)
        e = np.linalg.norm(x-x_old)

    return x
    print(x)
    print("Gauss-Seidel法ーー誤差:{0}".format(e))

    plt.plot(list_e)
    plt.show()

# ================================================================ #

# ====================== SOR method ==================== #

def SOR(n, A, b, eps, omega):
    # A = L + D + Uと分解し、(D + omega*L)x = ((1-omega)D - U)x + omega*bを収束させる。
    e = 1.0  # 誤差ベクトル（epsより大きければなんでもいい）
    list_e = []

    x = np.zeros(n)
    for i in range(0, n):
        x[i] = b[i]/A[i, i]
        # Aの対角成分が支配的となることがおおいので、解の初期値は何の情報もない場合はこうしておく。

    L = np.tril(A, -1)  # 下三角行列を返す関数。parameter<0で対角成分含まず下のみ抽出。
    U = np.triu(A, 1)  # 上三角行列を返す関数。parameter>0で対角成分含まず上のみ抽出。
    D = np.diagflat(np.diag(A))

    S = D + omega*L
    T = (1-omega)*D - omega*U
    S_inv = np.linalg.inv(S)

    while e > eps:
        list_e.append(e)
        x_old = x
        x = np.dot(S_inv, np.dot(T, x_old) + omega*b)
        e = np.linalg.norm(x-x_old)

    return x
    print(x)
    print("SOR法ーー誤差:{0}".format(e))

    plt.plot(list_e)
    plt.show

# =========================================================== #

if __name__ == '__main__':

    # 1 Benchmark solution by numpy.linalg.solve()
    start = time.time()

# initialization
    A = np.array([[5., -3., -1., 0., 2., 1.],
                  [0., -5., 4., 1., 0., 2.],
                  [-1., 3., -5., -2., 3., 3.],
                  [-1., 0., 3., 4., -2., -1.],
                  [0., 3., 3., -1., 3., -4.],
                  [2., 3., 2., 3., 2., -5.]])

    b = np.array([12., 6., 3., 9., 12., 21.])

# ----------------------main routine----------------------- #
    x = np.linalg.solve(A, b)
    print(x)

    elapsed_time = time.time() - start
    print("numpy.linalg.solve()")
    print("  Execution time: {0}".format(elapsed_time))
# --------------------------------------------------------- #

# 2 Gauss Elimination (normal)
    start = time.time()

# initialization
    n = 6

    A = np.array([[5., -3., -1., 0., 2., 1.],
                  [0., -5., 4., 1., 0., 2.],
                  [-1., 3., -5., -2., 3., 3.],
                  [-1., 0., 3., 4., -2., -1.],
                  [0., 3., 3., -1., 3., -4.],
                  [2., 3., 2., 3., 2., -5.]])

    b = np.array([12., 6., 3., 9., 12., 21.])

# -------------------------main routine------------------ #
    Gaussian_elimination(n, A, b)

    elapsed_time = time.time() - start
    print("Gauss Elimination（normal):")
    print("  Execution time: {0}".format(elapsed_time))
# ------------------------------------------------------------- #

# 3 Gauss Elimination（w/ partial pivot)
    start = time.time()

# initialization
    n = 6

    A = np.array([[5., -3., -1., 0., 2., 1.],
                  [0., -5., 4., 1., 0., 2.],
                  [-1., 3., -5., -2., 3., 3.],
                  [-1., 0., 3., 4., -2., -1.],
                  [0., 3., 3., -1., 3., -4.],
                  [2., 3., 2., 3., 2., -5.]])

    b = np.array([12., 6., 3., 9., 12., 21.])

# ------------------main routine----------------------- #
    Gaussian_elimination_pivot(n, A, b)

    elapsed_time = time.time() - start
    print("Gauss Elimination（w/ partial pivot):")
    print("  Execution time: {0}".format(elapsed_time))
# -------------------------------------------------------- #

# 4 Jacobi
    start = time.time()

# initialization
    n = 6

    A = np.array([[5., -3., -1., 0., 2., 1.],
                  [0., -5., 4., 1., 0., 2.],
                  [-1., 3., -5., -2., 3., 3.],
                  [-1., 0., 3., 4., -2., -1.],
                  [0., 3., 3., -1., 3., -4.],
                  [2., 3., 2., 3., 2., -5.]])

    b = np.array([12., 6., 3., 9., 12., 21.])

# ----------------------main routine-------------------------- #
    Jacobi_method(n, A, b, 1e-10)

    elapsed_time = time.time() - start
    print("Jacobi method:")
    print("  Execution time: {0}".format(elapsed_time))

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

# 5 Gauss-Seidel
    start = time.time()

# initialization
    n = 6

    A = np.array([[5., -3., -1., 0., 2., 1.],
                  [0., -5., 4., 1., 0., 2.],
                  [-1., 3., -5., -2., 3., 3.],
                  [-1., 0., 3., 4., -2., -1.],
                  [0., 3., 3., -1., 3., -4.],
                  [2., 3., 2., 3., 2., -5.]])

    b = np.array([12., 6., 3., 9., 12., 21.])

# ---------------- main routine ----------------- #
    Gauss_Seidel(n, A, b, 1e-10)

    elapsed_time = time.time() - start
    print("Gauss-Seidel")
    print("  Execution time: {0}".format(elapsed_time))

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

# 6 SOR
    start = time.time()

# initialization
    n = 6

    A = np.array([[5., -3., -1., 0., 2., 1.],
                  [0., -5., 4., 1., 0., 2.],
                  [-1., 3., -5., -2., 3., 3.],
                  [-1., 0., 3., 4., -2., -1.],
                  [0., 3., 3., -1., 3., -4.],
                  [2., 3., 2., 3., 2., -5.]])

    b = np.array([12., 6., 3., 9., 12., 21.])

# ---------------------- main routine --------------------- #
    SOR(n, A, b, 1e-10, 0.8)

    elapsed_time = time.time() - start
    print("SOR method")
    print("  Execution time: {0}".format(elapsed_time))

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


import numpy as np
from fft import dft,inv_dft,inv_fft,fft

def multiply(A, B):
    m=len(A)
    n=len(B)
    prod = [0] * (m + n - 1);
    for i in range(m):
        for j in range(n):
            prod[i + j] += A[i] * B[j];
    return prod;

#driver code
#Randomly generating the two coefficient arrays with size 4
param1 = list(np.random.randint(low = 0, high = 1000, size = 4))
param2 = list(np.random.randint(low = 0, high = 1000, size = 4))
#The result generated by "convolution for loop" method
print("The answer obtained by brute force multiplication: \n", multiply(param1,param2),"\n")

#degree bound extension
l = [0 for i in range(0,4)]
param1.extend(l)
param2.extend(l)

PA = fft(param1)
PB = fft(param2)

#pointwise multiply
C = []
for i in range(PA.shape[0]):
    C.append(PA[i]*PB[i])

#print the answer obtained by inverse fft
print("The answer obtained by calling inverse fft function \n",inv_fft(C),"\n")


'''
Use o método RK45 com passo variável para resolver o problema:

dx/dt = 1 + x^2 ;  x(0)=0

Use uma tolerância de 10^{-5} para o controle do erro.

Coloque em uma tabela os resultados para o tempo, o valor resultante com a integração RK45, a solução exata e o erro
com relação a solução exata.

Note que a solução diverge em \pi/2, portanto use um tempo final menor do que este valor.

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

def passovarRK5(x0, tf, dt, tol):
    df = lambda x: 1 + x**2
    xs = [x0]
    ts = [0]
    for t in np.arange(dt, tf+dt, dt):
        ts.append(t)
        k1 = dt * df(x0)
        k2 = dt * df(x0 + ((k1 * dt)/4))
        k3 = dt * df(x0 + ((3 * dt * k1)/32) + ((9 * dt * k2)/32))
        k4 = dt * df(x0 + ((1932 * dt * k1)/2197) - ((7200 * dt * k2)/2197) + ((7296 * dt * k3)/2197))
        k5 = dt * df(x0 + ((439 * dt * k1)/216) - (8 * dt * k2) + ((3680 * dt * k3)/513) - ((845 * dt * k4)/4104))
        k6 = dt * df(x0 - ((8 * dt * k1)/27) + (2 * dt * k2) - ((3544 * dt * k3)/2565) + ((1859 * dt * k4)/4104) - ((11 * dt * k5)/40))

        x5, x6 = x0, x0

        x5 = x5 + ((25 * k1)/216) + ((1408 * k3)/2565) + ((2197 * k4)/4101) - (k5/5)
        x6 = x6 + ((16 * k1)/135) + ((6656 * k3)/12825) + ((28561 * k4)/56430) - ((9 * k5)/50) + ((2 * k6)/55)

        dtnovo = dt*(tol/(abs(x5 - x6)))**(1/6)
        if dtnovo > 2 * dt:
            dt = 2 * dt
        if dtnovo < dt / 2:
            dt = dt / 2
        x0 = x5
        xs.append(x0)
    return ts, xs

x0 = 0
tf = 1.51
tol = 1e-5
dt = 0.01

t = np.linspace(0, tf, 100)
fx = np.tan(t)

plt.plot(passovarRK5(x0, tf, dt, tol)[0], passovarRK5(x0, tf, dt, tol)[1], label='RK45')
plt.plot(t, fx, label='Analítica')
plt.grid()
plt.legend()
plt.xlabel('t')
plt.ylabel('f(t)')
plt.savefig('passovarRK5.png', dpi=300)

tt = np.array(passovarRK5(x0, tf, dt, tol)[0])
ft = np.array(passovarRK5(x0, tf, dt, tol)[1])
fan = np.tan(tt)
err = abs(ft - fan)

print(f'|t   |RK45   |Exata  |Erro')
for tmp, rk, an, er in zip(tt, ft, fan, err):
    print(f'|{tmp:.2f}|{rk:<7.3f}|{an:<7.3f}|{er:.3f}')


"""
Here, the definition of activation functions

Functions must be used with numpy arrays!
"""
from __future__ import annotations

from enum import Enum
from typing import Callable
from typing import Optional

import numpy as np

def _relu_function(x):
    return np.maximum(x, 0)

def _relu_derivative(x):
    x[x <= 0] = 0
    x[x > 0] = 1
    return x

def _sigmoid_function(x):
    return 1 / (1 + np.exp(-x))

def _sigmoid_derivative(x):
    f = _sigmoid_function
    return f(x) * (1 - f(x))

def _softplus_function(x):
    return np.log(1 + np.power(np.e, x))

_softplus_derivative = _sigmoid_function

class ActivationFunction(Enum):
    """
    A enumeration of activation functions. Each element contains the function and the derivative of the function
    """
    ReLu = ("Rectified linear unit", _relu_function, _relu_derivative)
    Sigmoid = ("Soft step", _sigmoid_function, _sigmoid_derivative)
    SoftPlus = ("Softplus", _softplus_function, _softplus_derivative)

    def __init__(self, nickname: str, f: Callable, df: Callable):
        self.nickname = nickname
        self.f = f
        self.df = df

    @classmethod
    def get_by_name(cls, name: str) -> Optional[ActivationFunction]:
        """
        Get ActivationFunction object by name or None, if there is no function with this name
        """
        activation_function = next((f for f in cls if f.name.lower() == name.lower()), None)
        if not activation_function:
            raise ValueError(f"Non-existing function name: {name}")
        return activation_function


#
# @lc app=leetcode id=935 lang=python3
#
# [935] Knight Dialer
#

# @lc code=start
import numpy as np

class Solution:
    def knightDialer(self, n: int) -> int:
        """
        A is adjency matrix, number of n-length paths for i -> j equals to (i, j) in A^n
        """

        adj = np.array(
            [
                [0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 0, 1, 0],
                [0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
                [0, 0, 0, 0, 1, 0, 0, 0, 1, 0],
                [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                [1, 1, 0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
                [0, 1, 0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 1, 0, 1, 0, 0, 0, 0, 0],
            ]
        )

        counter, N, mod = np.identity(10, int), n - 1, 10 ** 9 + 7
        while N:
            if N % 2:
                counter = counter @ adj % mod
            adj = adj @ adj % mod
            N //= 2

        return 10 if n == 1 else np.sum(counter) % mod

# @lc code=end


from __future__ import division
import math

import numpy as np
from numpy import pi

import numdifftools.fornberg as ndf

@np.vectorize
def CxDerivative(f, z0, n=1, contour=None, absIntegrationTol=1e-10, verbose=False):
    r"""
    Compute the derivaive of an analytic function using Cauchy's
    Integral Formula for Derivatives.

    .. math::

        f^{(n)}(z_0) = \frac{n!}{2\pi i} \oint_C \frac{f(z)}{(z-z_0)^{n+1}} dz

    Parameters
    ----------
    f : function
        Function of a single variable f(x).
    z0 : complex
        Point to evaluate the derivative at.
    n : int
        The order of the derivative to evaluate.
    contour : :class:`Contour <cxroots.Contour.Contour>`, optional
        The contour, C, in the complex plane which encloses the point z0.
        By default the contour is the circle |z-z_0|=1e-3.
    absIntegrationTol : float, optional
        The absolute tolerance required of the integration routine.
    verbose : bool, optional
        If True runtime information will be printed.  False be default.

    Returns
    -------
    f^{(n)}(z0) : complex
        The nth derivative of f evaluated at z0
    """
    if contour is None:
        from .contours.Circle import Circle
        C = lambda z0: Circle(z0, 1e-3)
    else:
        C = lambda z0: contour

    integrand = lambda z: f(z)/(z-z0)**(n+1)
    integral = C(z0).integrate(integrand, absTol=absIntegrationTol, verbose=verbose)
    return integral * math.factorial(n)/(2j*pi)

def find_multiplicity(root, f, df=None, rootErrTol=1e-10, verbose=False):
    """
    Find the multiplicity of a given root of f by computing the
    derivatives of f, f^{(1)}, f^{(2)}, ... until
    |f^{(n)}(root)|>rootErrTol.  The multiplicity of the root is then
    equal to n.  The derivative is calculated with `numdifftools <http://numdifftools.readthedocs.io/en/latest/api/numdifftools.html#numdifftools.fornberg.derivative>`_
    which employs a method due to Fornberg.

    Parameters
    ----------
    root : complex
        A root of f, f(root)=0.
    f : function
        An analytic function of a single complex variable such that
        f(root)=0.
    df : function, optional
        The first derivative of f.  If not known then df=None.
    contour : Contour, optional
        The integration contour used to evaluate the derivatives.
    rootErrTol : float, optional
        It will be assumed that f(z)=0 if numerically |f(z)|<rootErrTol.
    verbose : bool, optional
        If True runtime information will be printed.  False be default.

    Returns
    -------
    multiplicity : int
        The multiplicity of the given root.
    """
    if abs(f(root)) > rootErrTol:
        raise ValueError("""
            The provided 'root' is not a root of the given function f.
            Specifically, %f = abs(f(root)) > rootErrTol = %f
            """%(abs(f(root)), rootErrTol))

    n = 1
    while True:
        if df is not None:
            if n==1:
                err = abs(df(root))
            else:
                # ndf.derivative returns an array [f, f', f'', ...]
                err = abs(ndf.derivative(df, root, n-1)[n-1])
        else:
            err = abs(ndf.derivative(f, root, n)[n])

        if verbose:
            print('n', n, '|df^(n)|', err)

        if err > rootErrTol:
            break

        n += 1

    return n


import numpy as np
import pandas as pd

def median(df, values_col, weights_col, dropna = True):
    _df = df[[values_col, weights_col]]
    _df.dropna(subset=[values_col], inplace = dropna)
    _df = _df.sort_values(values_col).reset_index()
    _df['cumsum'] = _df[weights_col].cumsum()
    median_cumsum = _df[weights_col].sum() / 2
    median_iloc = np.searchsorted(_df['cumsum'], median_cumsum)
    return _df[values_col].iloc[median_iloc]

# def median2(df, values_col, weights_col, dropna = True):
#     _df = df[[values_col, weights_col]]
#     _df.dropna(subset=[values_col], inplace = dropna)
#     values = _df[values_col].to_numpy()
#     weights = _df[weights_col].to_numpy()
#     sort_index = values.argsort()
#     cumsum_arr = np.cumsum([weights[i] for i in sort_index])
#     median_iloc = np.searchsorted(cumsum_arr, cumsum_arr[-1]/2)
#     return np.array([values[i] for i in sort_index])[median_iloc]

def quantile(df, values_col, weights_col, q, dropna = True):
    _df = df[[values_col, weights_col]]
    _df.dropna(subset=[values_col], inplace = dropna)
    _df = _df.sort_values(values_col).reset_index()
    # _df['cumsum'] = _df[weights_col].cumsum()
    # quantile_cumsum = np.quantile(_df['cumsum'], q)
    cumsum = _df[weights_col].cumsum().to_numpy()
    quantile_iloc = np.searchsorted(cumsum, np.quantile(cumsum, q))
    return _df[values_col].iloc[quantile_iloc]


#!/usr/bin/env python

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import sys
import argparse

class Parabola() :
    def __init__(self,npts,size):
        mpl.rcParams['toolbar'] = 'None'
        self.size = size
        self.npts = npts
        self.fig = plt.figure(figsize=(8,8))
        self.ax = self.fig.add_subplot()
        self.ax.set_xlim(-size,size)
        self.ax.set_ylim(-size,size)

    def drawVertical(self,invert,color) :
        self.t = np.linspace(-self.size,self.size,self.npts)
        self.x = self.t
        if not invert :
            self.y = self.size*self.x**2
        else :
            self.y = -self.size*self.x**2
        self.ax.plot(self.x,self.y,color)

    def drawHorizontalRight(self,invert,color) :
        if not invert :
            self.t = np.linspace(0,self.size,self.npts)
            self.y = self.t
            self.x = self.size*self.t**2
        else :
            self.t = np.linspace(0,self.size,self.npts)
            self.y = -self.t
            self.x = self.size*self.t**2
        self.ax.plot(self.x,self.y,color)

    def drawHorizontalLeft(self,invert,color) :
        if not invert :
            self.t = np.linspace(-self.size,0,self.npts)
            self.y = self.t
            self.x = -self.size*self.t**2
        else :
            self.t = np.linspace(-self.size,0,self.npts)
            self.y = -self.t
            self.x = -self.size*self.t**2
        self.ax.plot(self.x,self.y,color)

    def drawAxis(self) :
        points = (-self.size,self.size)
        zeros = (0,0)
        self.ax.plot(points,zeros,'black',linestyle = 'dotted')
        self.ax.plot(zeros,points,'black',linestyle = 'dotted')

    def display(self) :
        plt.show()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='-vb -vt -hr -hl : default all')
    parser.add_argument('-vb', action="store_true")
    parser.add_argument('-vt', action="store_true")
    parser.add_argument('-hr', action="store_true")
    parser.add_argument('-hl', action="store_true")
    args = parser.parse_args()
    if not(args.vb) and not(args.vt) and not(args.hr) and not(args.hr) :
        args.vb = True; args.vt = True; args.hr = True; args.hl = True;
    parabola = Parabola(1000,1.01)
    if args.vb :
        parabola.drawVertical(True,'darkblue')
    if args.vt :
        parabola.drawVertical(False,'blue')
    if args.hr :
        parabola.drawHorizontalRight(True,'darkred')
        parabola.drawHorizontalRight(False,'red')
    if args.hl :
        parabola.drawHorizontalLeft(True,'limegreen')
        parabola.drawHorizontalLeft(False,'green')
    parabola.drawAxis()
    parabola.display()


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

import matplotlib.pyplot as plt
import numpy as np
from numpy import linalg as la
from scipy import optimize

## Metoda Gradient

## Date: Q, q
## f(x) = 1/2 x^TQx + q^Tx
Q = np.array([[1.52,1.5],[1.5,1.5]])
q = np.array([2,2])
f = lambda x: 0.5*x.T@Q@x + q.T@x

V = la.eigvals(Q)

print("")
print("")
print("#######################")
print("f (x) = 0.5*x^T Q x + q.T x")
print("Valori proprii Q:",V[0],V[1] )
print("Numar conditionare L/sigma: ", V[0]/V[1])
print("#######################")
print("")
print("Metoda Gradient cu pas constant alpha = 1/Lips")
print("")
print("#######################")

k1 = 0
eps = 10e-7
x0 = np.array([-9,9])
grad = Q@x0+q
grad0 = grad

### Constanta Lipschitz a gradientului
Lips = np.max(la.eigvals(Q))
mu = np.min(la.eigvals(Q))
nr_cond = Lips/mu

alpha = 1/Lips

all_x_i = [x0[0]]
all_y_i = [x0[1]]
all_f_i = [f(x0)]
criteriu_stop = la.norm(grad)
x = x0

while (criteriu_stop > eps):

    x = x - alpha*grad
    grad = Q@x+q

    criteriu_stop = la.norm(grad)
    k1 = k1+1

    all_x_i.append(x[0])
    all_y_i.append(x[1])
    all_f_i.append(f(x))

solMG = x

id_all_x_i = [x0[0]]
id_all_y_i = [x0[1]]
id_all_f_i = [f(x0)]
criteriu_stop = la.norm(grad0)
x = x0
k2 = 0

while (criteriu_stop > eps):

    grad = Q@x+q
    alpha = (la.norm(grad)**2)/(grad.T@Q@grad)
    x = x - alpha*grad

    criteriu_stop = la.norm(grad)
    k2 = k2+1

    id_all_x_i.append(x[0])
    id_all_y_i.append(x[1])
    id_all_f_i.append(f(x))

solMG_id = x
sol = -la.inv(Q)@q

#### Plot ####

x, y = np.mgrid[-10:10:0.1, -10:10:0.1]
x = x.T
y = y.T

fig = plt.figure(1, figsize=(4, 4))
plt.clf()
plt.axes([0, 0, 1, 1])

fcont = 0.5*Q[0,0]*(x**2) + 0.5*Q[1,1]*(y**2) + Q[1,0]*x*y + q[0]*x+ q[1]*y
contours = plt.contour(fcont, extent=[-10, 10, -10, 10],
                    cmap=plt.cm.gnuplot)

#Etichete pentru multimile izonivel
plt.clabel(contours, inline=1,
                fmt='%1.1f', fontsize=10)

plt.plot(id_all_x_i, id_all_y_i, 'm-', linewidth=2)
plt.plot(id_all_x_i, id_all_y_i, 'g+')
plt.plot(all_x_i, all_y_i, 'b-', linewidth=2)
plt.plot(all_x_i, all_y_i, 'k+')
plt.plot(sol[0], sol[1], 'rx', markersize = 14)

fig.savefig('MG_quad_cs.pdf')

print("#######################")
print("")
print("Numar conditionare problema: ",nr_cond)
print("")
print("Numar de iteratii pas constant: ",k1)
print("Numar de iteratii pas ideal: ",k2)
print("")
print("Solutie gasita de MG: ",'(',solMG[0],solMG[1],')')
print("Solutie optima: ",'(',sol[0],sol[1],')')
print("Valoare optima: ",f(sol))
print(0.5*(Q[0,0]*(sol[0]**2) + Q[1,1]*(sol[1]**2)) + Q[1,0]*sol[0]*sol[1] + q[0]*sol[0]+ q[1]*sol[1])
#print("Solutie gasita de MG: ",'(',solMG[0],solMG[1],')')
print("")
print("#######################")

Tehnici de Optimizare (TO)/Seminar/2/metoda_gradient.py


import numpy as np

def softmax(L):
    expL = np.exp(L)
    sumExpL = sum(expL)
    result = []
    for i in expL:
        result.append(i*1.0/sumExpL)
    return result

    # Note: The function np.divide can also be used here, as follows:
    # def softmax(L):
    #     expL(np.exp(L))
    #     return np.divide (expL, expL.sum())


import numpy as np

def ackley(coordinate: np.ndarray) -> float:
    """
    n-dimensional Ackley function. Bounded by -30 <= coordinate[i] <= 30
    :param coordinate: n-dimensional numpy array with dtype float
    :return: function value at the given coordinate
    """
    assert np.all((-30 <= coordinate) & (coordinate <= 30)), 'Coordinates have to be in [-30, 30]'
    first_sum = 0.0
    second_sum = 0.0
    for c in coordinate:
        first_sum += c ** 2.0
        second_sum += np.cos(2.0 * np.pi * c)
    n = float(len(coordinate))
    return -20.0 * np.exp(-0.2 * np.sqrt(first_sum / n)) - np.exp(second_sum / n) + 20 + np.e

def main():
    import numpy as np
    from matplotlib import pyplot
    from mpl_toolkits.mplot3d import Axes3D
    # define range for input
    r_min, r_max = -5.0, 5.0
    # sample input range uniformly at 0.1 increments
    xaxis = np.arange(r_min, r_max, 0.1)
    yaxis = np.arange(r_min, r_max, 0.1)
    # create a mesh from the axis
    x, y = np.meshgrid(xaxis, yaxis)
    # compute targets
    results = ackley(np.array([x, y]))
    # create a surface plot with the jet color scheme
    figure = pyplot.figure()
    axis = figure.gca(projection='3d')
    axis.plot_surface(x, y, results, cmap='jet')
    # show the plot
    pyplot.show()

if __name__ == '__main__':
    main()


import numpy as np
#------------------------------------------------------------------------------------------------
def convolucion(A,B):
    filas = len(A) - len(B) + 1
    columnas = len(A[0]) - len(B[0]) + 1
    C = np.zeros((filas,columnas))
    for i in range(len(C)):
        for j in range(len(C[0])):
            suma = 0
            for k in range(len(B)):
                for l in range(len(B[0])):
                    suma += A[k + i][l + j]*B[k][l]
            C[i][j] = suma
    return C
#------------------------------------------------------------------------------------------------
matriz1 = np.array([[6,9,0,3],[8,4,9,1],[4,1,3,12],[3,2,1,100]])
kernell1 = np.array([[1,0,2],[5,0,9],[6,2,1]])
matriz2 = np.array([[1,2,3,4,5,6],[7,8,9,10,11,12],[0,0,1,16,17,18],[0,1,0,7,23,24],[1,7,6,5,4,3]])
kernell2 = np.array([[1,1,1],[0,0,0],[2,10,3]])
#------------------------------------------------------------------------------------------------
print("Primer ejemplo: ","\n",convolucion(matriz1,kernell1))
print("Segundo ejemplo: ","\n",convolucion(matriz2,kernell2))


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

# In[3]:

import pandas as pd
import numpy as np

from scipy import stats

def black_76(days_to_expiry, futures_price, strike_price, implied_volatility,
             interest_rate, option_type="call"):

    if option_type not in ["call", "put"]:
        raise ValueError("Must provide 'call' for European call or 'put' for European put option.")

    F = futures_price
    K = strike_price
    sigma = implied_volatility
    r = interest_rate

    time_to_expiry = (days_to_expiry) / 365

    d_1 = (np.log(F / K) + 0.5 * (time_to_expiry) * sigma ** 2) / (sigma * np.sqrt(time_to_expiry))
    d_2 = d_1 - sigma * np.sqrt(time_to_expiry)

    if option_type == "call":
        C = np.exp(-r * (time_to_expiry)) * (F * stats.norm.cdf(d_1) - K * stats.norm.cdf(d_2))

        return C
    else:
        P = np.exp(-r * (time_to_expiry)) * (K * stats.norm.cdf(-d_2) - F * stats.norm.cdf(-d_1))

        return P

def tune(F_vect, delta=1/12):

    if type(F_vect) != np.ndarray:
        raise TypeError("Must provide numpy array.")

    n = len(F_vect) - 1

    x, y = F_vect[0:-1], F_vect[1:]

    s_x, s_y = np.sum(x), np.sum(y)

    s_xx = np.sum(x ** 2)
    s_xy = np.sum(x * y)
    s_yy = np.sum(y ** 2)

    a = (n * s_xy - s_x * s_y) / (n * s_xx - s_x **2)
    b = (s_y - a * s_x) / n
    sd = np.sqrt((n * s_yy - s_y ** 2 - a * (n * s_xy - s_x * s_y)) / (n * (n-2)))

    lambda_par = - np.log(a) / delta
    mu = b / (1 - a)

    return mu, lambda_par

def gbm_path_gen(t, T, F_init, sigma, n=12):

    path = np.zeros(n)
    ttm = T - t
    dt = (ttm.days / (n * 365))

    path[0] = F_init

    for i in range(n - 1):
        path[i+1] = path[i] + path[i] * sigma * np.sqrt(dt) * np.random.randn()

    return path

def vasicek_path_gen(t, T, F_init, lambda_par, mu, sigma, n=12):

    path = np.zeros(n)
    ttm = T - t
    dt = (ttm.days / (n * 365))

    path[0] = F_init

    for i in range(n - 1):
        path[i+1] = path[i] + lambda_par * (mu - path[i]) * dt + sigma * np.sqrt(dt) * np.random.randn()

    return path

def garch_path_gen(t, T, F_init, lambda_par, mu, sigma, n=12):

    path = np.zeros(n)
    ttm = T - t
    dt = (ttm.days / (n * 365))

    path[0] = F_init

    for i in range(n - 1):
        path[i+1] = path[i] + lambda_par * (mu - path[i]) * dt + sigma * np.sqrt(dt) * path[i] * np.random.randn()

    return path

def monte_vasicek(t, T, F, K, sigma, r, lambda_par, mu, m=1e5, n=12):

    sim_start_date, sim_final_date = pd.to_datetime(t), pd.to_datetime(T)
    monte_mat = np.zeros((m, n))
    a_val = 0

    for j in range(m):
        vasicek_mat[j, :] = vasicek_path_gen(pd.to_datetime(sim_start_date), pd.to_datetime(sim_final_date), F,
                                             lambda_par, mu, sigma, n=n)
        a_val += max((vasicek_mat[j, -1] - K), 0)

    price = (a_value / m) * (np.exp(-r * (10/365)))

    return price

def monte_garch(t, T, T_e, F, K, sigma, r, lambda_par, mu, m=1000, n=12):

    sim_start_date, sim_final_date = pd.to_datetime(t), pd.to_datetime(T)
    monte_mat = np.zeros((m, n))
    a_val = 0

    for j in range(m):
        monte_mat[j, :] = garch_path_gen(sim_start_date, sim_final_date, F, lambda_par, mu, sigma, n=n)

        a_val += max((monte_mat[j, -1] * (np.exp(-r * (10 / 365))) - K, 0))

    price = (a_val / m) * (np.exp(-r * (T_e / 365)))

    return price


import numpy as np
from math import log

def ld_stat(l, n_sources, n_snapshots):
    """Computes the sufficient statistic for source number detection in MDL/AIC.

    Args:
        l (~numpy.ndarray): A 1D vector of the eigenvalues of the covariance
            matrix in ascending order.
        n_sources (int): Number of sources.
        n_snapshots (int): Number of snapshots.

    References:
        [1] H. L. Van Trees, Optimum array processing. New York: Wiley, 2002.
    """
    if l.ndim != 1:
        raise ValueError('A 1D numpy vector expected.')
    n_sensors = l.size
    diff = n_sensors - n_sources
    l = l[:diff]
    return n_snapshots * diff * log(np.sum(l) / diff / (np.prod(l)**(1./diff)))

def aic(x, n_snapshots):
    """Detects source numbers using AIC.

    Args:
        x (~numpy.ndarray): A 1D vector of the eigenvalues of the covariance
            matrix in ascending order, or the covariance matrix itself.
        n_snapshots (int): Number of snapshots.

    Notes:
        AIC is inconsistent, and tends to asymptotically overestimate the
        number of sources. However, it tends to give a higher probability of a
        correct decision.

    References:
        [1] H. L. Van Trees, Optimum array processing. New York: Wiley, 2002.
    """
    if x.ndim > 1:
        x = np.linalg.eigvalsh(x)
    n_sensors = x.size
    ld = np.zeros((n_sensors, 1))
    for i in range(n_sensors):
        ld[i] = ld_stat(x, i, n_snapshots) + i * (2 * n_sensors - i)
    return np.argmin(ld)

def mdl(x, n_snapshots):
    """Detects source number using MDL.

    Args:
        x (~numpy.ndarray): A 1D vector of the eigenvalues of the covariance
            matrix in ascending order, or the covariance matrix itself.
        n_snapshots (int): Number of snapshots.

    Notes:
        MDL is consistent.

    References:
        [1] H. L. Van Trees, Optimum array processing. New York: Wiley, 2002.
    """
    if x.ndim > 1:
        x = np.linalg.eigvalsh(x)
    n_sensors = x.size
    ld = np.zeros((n_sensors, 1))
    for i in range(n_sensors):
        ld[i] = ld_stat(x, i, n_snapshots) + 0.5 * (i * (2 * n_sensors - i) + 1) * log(n_snapshots)
    return np.argmin(ld)

def sorte(x):
    r"""Detects source number using SORTE.

    Arg:
        x: (~numpy.ndarray): A 1D vector of the eigenvalues of the covariance
            matrix in ascending order, or the covariance matrix itself.

    Refereces:
        [1] Z. He, A. Cichocke, S. Xie, and K. Choi, "Detecting the number of
        clusters in n-way probabilistic clustering," IEEE Trans. Pattern
        Anal. Mach. Intell., vol. 32, pp. 2006-2021, Nov. 2010.
    """
    if x.ndim > 1:
        x = np.linalg.eigvalsh(x)
    # SORTE requires descending order.
    x = x[::-1]
    n = x.size
    if n < 4:
        raise ValueError('At least four eigenvalues required.')
    diffs = x[:-1] - x[1:]
    sigmas = [np.var(diffs[k:]) for k in range(n - 1)]
    s = np.zeros((n - 2,))
    for k in range(n - 2):
        if np.abs(sigmas[k]) < np.finfo(np.float_).eps:
            s[k] = np.inf
        else:
            s[k] = sigmas[k + 1] / sigmas[k]
    return np.argmin(s[:-1]) + 1


# -*- coding: utf-8 -*-
from numpy import array
from numpy import eye,dot,zeros,sort
from scipy.linalg import svd,inv,eig
if __name__=='__main__':
    m=array([[1,0,0,0,2],
             [0,0,3,0,0],
             [0,0,0,0,0],
             [0,4,0,0,0]])
    #py的svd得出的d是一个数组
    u,d,vt=svd(m)
    print '---test svd---'
    print 'u',u
    print 'd',d
    print 'vt',vt
    #为了验证,构造dz矩阵,其为m*n的(4*5)
    #这里注意dz的构造方式
    dz=zeros(vt.shape[0])
    #print 'vt.shape[0]', vt.shape[0], vt.shape[1]
    dz[:len(d)]+=d
    print 'dz', dz
    #ndarray的*和matrix*是不同的,matrix的*对应dot
    print eye(len(d),vt.shape[0])*dz
    t=dot(u,eye(len(d),vt.shape[0])*dz)
    print dot(t,vt) #should be equal to m
    print '---test inv---'
    print inv(u)
    print inv(vt)
    print '---test mt*m---'
    ms=dot(m.T,m)
    print ms
    #eig可以求方阵的特征值与特征向量
    evals, evecs = eig(ms)
    #
    sevals=sort(evals)[::-1]
    v=vt.T
    print sevals
    print dz**2
    print '---test at*t=lamb_i*vi---'
    for i in range(len(sevals)):
        print dot(ms,vt[i])
        print sevals[i]*vt[i]
        print ''


"""
This is the Python HW 5
for PSTAT 160B
Prof Ichiba
TA: Mousavi
Section: W 1:00 - 1:50pm
"""

# Import libraries
from __future__ import division
import random
import math
import matplotlib.pyplot as plt
import numpy as np

# In the class we see the simple symmetric random walk approximates the standard Brownian motion

def Part_A():
	# Simulate a Brownian motion-like sample path
	# Graphing (ti, U(ti)) from i = 0, 1, ... N where 2 ^ N
	# Where ti := i / N
	# And U(ti) = sum of iid normal random variables with mean 0 and variance 1 / N
	# Define constants
	N = (2 ** 10)
	mean = 0
	var = (1 / N)
	sd = math.sqrt(var)
	Ti = [0] # Initialize first time as zero
	values = [0] # Initialize the first point as zero

	# Loop through 0 to N
	# Get time values and generate iid normal random variables
	for i in range(1, N):
		Ti.append(i / N)
		cospi = np.random.normal(mean, sd)
		values.append(cospi)

	# Get the cumulative sum and save as a list
	# Thank you numpy
	# These U(ti) values represent B(ti) values
	UTi = np.cumsum(values)

	# Plot the sample path
	plt.plot(Ti, UTi)
	plt.show()

def Part_B():
	#  Repeat this simulation 10000 times to estimate the joint probability
	# P((max B(s) from (0 <= s <= 1) > b), B(1) < a) for b >= a, b >= 0
	# Can choose appropriate range of (b, a)
	# Here a = 0, b = 2
	a = 0
	b = 1
	count = 0

	num_sim = 10000
	current_sim = 0
	# Define constants (taken from part A)
	N = (2 ** 10)
	mean = 0
	var = (1 / N)
	sd = math.sqrt(var)

	# Run simulations
	while current_sim < num_sim:
		# Taken from part A
		Ti = [0] # Initialize first time as zero
		values = [0] # Initialize the first point as zero

		# Loop through 0 to N
		# Get time values and generate iid normal random variables
		for i in range(1, N):
			Ti.append(i / N)
			cospi = np.random.normal(mean, sd)
			values.append(cospi)

		# Get the cumulative sum and save as a list
		# Thank you numpy
		# These U(ti) values represent B(ti) values
		UTi = np.cumsum(values)

		# Check conditions: if max value is greater than b
		# And if the last value is less than a
		# If so increment the count
		maximum = max(UTi)
		last_point = UTi[-1]
		if maximum > b and last_point < a:
			count += 1

		# Increment count
		current_sim += 1

	# Print the probability
	prob = count / num_sim
	print("Part B:")
	print("The probability that P((max B(s) from (0 <= s <= 1) > b), B(1) < a) for b >= a, b >= 0 is %f" %prob)

def Part_C():
	# Let us denote T by the uniform random variable on the unit interval [0, 1]
	# independent of Brownian motion.
	# As in part b, estimate the tail probability P(|B(T)| >= x) ; x > 0

	# First we'll repeat Part A
	# Simulate a Brownian motion-like sample path
	# Graphing (ti, U(ti)) from i = 0, 1, ... N where 2 ^ N
	# Where ti := i / N
	# And U(ti) = sum of iid normal random variables with mean 0 and variance 1 / N
	# Define constants
	N = (2 ** 10)
	mean = 0
	var = (1 / N)
	sd = math.sqrt(var)
	Ti = [0] # Initialize first time as zero
	values = [0] # Initialize the first point as zero
	# Pick a value for x, x > 0
	x = 0.5

	num_sim = 1000
	current_sim = 0
	count = 0

	# Run simulations
	while current_sim < num_sim:
		# Taken from part A
		# Loop through 0 to N
		# Get time values and generate iid normal random variables
		for i in range(1, N):
			Ti.append(i / N)
			cospi = np.random.normal(mean, sd)
			values.append(cospi)

		# Get the cumulative sum and save as a list
		# Thank you numpy
		# These U(ti) values represent B(ti) values
		UTi = np.cumsum(values)

		# Generate uniform random variable
		T = np.random.uniform(0, 1)

		# Since time is linear and represents a proportion from 0 to 1
		# And t(0) corresponds to B(0), t(N) corresponds to B(N)
		# t(T) corresponds to B(T * N)
		TN = math.floor(T * N)

		# Now we index B(s) (or UTi) for the appropropriate value with T plugged in
		# We want the absolute value as well
		BT = abs(UTi[TN])

		# Check if the the absolute value is greater than x
		if BT > x:
			count += 1

		# Increment count
		current_sim += 1
		if current_sim % 100 == 0:
			print(current_sim)

	# Print results
	prob = count / num_sim
	print("Part C:")
	print("Our estimate of the tail probability P(|B(T)| >= x) ; x > 0 is %f" %prob)

Part_A()
Part_B()
Part_C()


from __future__ import division, print_function
from sympy import *

_inverse_relations = { Ge: Le, Le: Ge, Gt: Lt, Lt: Gt }

def add_equations(equation1,equation2):
    r"""
    Adds each side of two equations and returns the resulting equation.

    Parameters
    ==========
    equation1 : Equality
    equation2 : Equality

    Examples
    ========
    ```python
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x,y = symbols('x,y',real=True)

    >>> eq1 = Eq(x+10, 4*y-2)
    >>> eq2 = Eq(sin(x)+5*x, -y**2)
    >>> add_equations(eq1,eq2)
    Eq(-4*x - sin(x) + 10, y**2 + 4*y - 2)
    ```
    """
    return Eq( equation1.lhs + equation2.lhs, equation1.rhs+equation2.rhs )

def sub_equations(equation1,equation2):
    r"""
    Subtracts each side of two equations and returns the resulting equation.

    Parameters
    ==========
    equation1 : Equality | minuend
    equation2 : Equality | subtrahend

    Example
    =======
    ```python
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x,y = symbols('x,y',real=True)

    >>> eq1 = Eq(x+5, y-3)
    >>> eq2 = Eq(x, 10*y)
    >>> sub_equations(eq1,eq2)
    Eq(5, -9*y - 3)
    ```
    """
    return Eq( equation1.lhs - equation2.lhs, equation1.rhs-equation2.rhs )

def mul_equations(equation1,equation2):
    r"""
    Multiplies each side of two equations and returns the resulting equation.

    Parameters
    ==========
    equation1 : Equality
    equation2 : Equality

    Example
    =======
    ```python
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x,y = symbols('x,y',real=True)

    >>> eq1 = Eq(x+5, x**2)
    >>> eq2 = Eq(y+10*x, 6)
    >>> mul_equations(eq1,eq2)
    Eq((x + 5)*(10*x + y), 6*x**2)
    ```
    """
    return Eq( equation1.lhs * equation2.lhs, equation1.rhs*equation2.rhs )

def div_equations(equation1,equation2):
    r"""
    Divides each side of two equations and returns the resulting equation.

    Parameters
    ==========
    equation1 : Equality | dividend
    equation2 : Equality | divisor

    Example
    =======
    ```python
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x,y,th = symbols(r'x,y,theta',real=True)

    >>> eq1 = Eq(sin(th), y)
    >>> eq2 = Eq(cos(th), x)
    >>> div_equations(eq1,eq2)
    Eq(sin(theta)/cos(theta), y/x)
    ```
    """
    return Eq( equation1.lhs/equation2.lhs, equation1.rhs/equation2.rhs )

def invert_ineq(ineq):
    r"""
    Changes the direction of an inequality

    Parameters
    ==========
    ineq : Relational

    Returns
    =======
    Relational, direction inverted

    Example
    =======
    ```
    >>> from sympy import *
    >>> from sympy_relational_tools import *
    >>> x = symbols('x',real=True)

    >>> ineq = Ge(x,3)
    >>> invert_ineq(ineq)
    -x <= -3
    ```
    """
    return _inverse_relations[ineq.func]( -ineq.lhs, -ineq.rhs, evaluate=False)

def _both_sides_add_ineq(relation,argument):
    r"""
    Adds `argument` to each side of `relation`, according to the property
    $ x>y \implies x + c > y + c $, where $c \in \mathbb R$.
    """
    return relation.func( relation.lhs+argument, relation.rhs+argument, evaluate=False )

def _both_sides_mul_ineq(relation,argument,interval,variable):
    r"""
    Multiplies `argument` to each side of `relation`.

    If `interval` is not present, global assumptions are used (properties is_positive, is_negative).

    If `interval` is not present, the function returns the resulting inequality.

    Rules are made according to: $x \le y \implies xc \le yc$ if $c>0$ and $xc \ge yc$ if $c<0$.

    If `interval` is present, `variable` must be too, and `solveset` is used to determine
    the intervals where the inequality must or not change sign (solveset supports only univariable
    inequalities).

    If `interval` is present, the function returns a dictionary with intervals and solutions.
    """
    if interval is None:
        if argument.is_positive == True:
            return relation.func( relation.lhs*argument, relation.rhs*argument, evaluate=False )
        elif argument.is_negative == True:
            return _inverse_relations[relation.func]( relation.lhs*argument, relation.rhs*argument, evaluate=False )
        elif argument == 0:
            return relation.func(0,0)
        else:
            raise ValueError("Couldn't determine sign of the argument")
    else:
        if variable is None:
            raise TypeError("If `interval` is given, a variable must be specified. Only univariable inequalities are supported")
        # Determine where in the given interval the argument is positive, negative and zero:
        positive_interval = solveset(argument>0,variable,domain=interval)
        negative_interval = solveset(argument<0,variable,domain=interval)
        zeros = solveset(argument,variable,domain=interval)
        # The resulting inequalities depend on the interval of the argument
        ans_if_positive = relation.func( relation.lhs*argument, relation.rhs*argument, evaluate=False )
        ans_if_negative = _inverse_relations[relation.func]( relation.lhs*argument, relation.rhs*argument, evaluate=False )
        ans_if_zero = relation.func(0,0)
        return {positive_interval: ans_if_positive, negative_interval: ans_if_negative, zeros: ans_if_zero }

def _both_sides_pow_ineq(relation,argument,interval,variable):
    r"""
    Applies a power to both sides of an inequality. The only implemented power is -1 (reciprocal of both sides).
    If `interval` is not present, global assumptions are used (properties is_positive, is_negative).

    If `interval` is not present, the function returns the resulting inequality.

    The rules implemented are:
    $ x > y \implies 1/x < 1/y$ if $x>0,y>0$ or $x<0,y<0$, and $1/x>1/y$ if $x>0,y<0$ or $x<0,y>0$

    If `interval` is present, `variable` must be too, and `solveset` is used to determine
    the intervals where the inequality must or not change sign (solveset supports only univariable
    inequalities).

    If `interval` is present, the function returns a dictionary with intervals and solutions.

    `solveset` can be useful in order to determine an adequate interval where the original inequality holds.

    Example
    =======
    ```python
    >>> from sympy_relational_tools import *
    >>> x = symbols('x',real=True)
    >>> ie1 = Ge(x,x**2,evaluate=False); ie1
    x >= x**2
    >>> both_sides(ie1,Pow,-1,Interval(-oo,oo),x)
    {Interval.open(0, oo): 1/x <= x**(-2), Interval.open(-oo, 0): 1/x >= x**(-2)}
    ```
    """
    if argument == -1:
        if interval is None:
            # Method 1: global assumptions
            if variable is None:
                raise TypeError("If `interval` is given, a variable must be specified. Only univariable inequalities are supported")
            if (relation.lhs.is_positive == True) and (relation.rhs.is_positive == True):
                return relation.func( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            elif (relation.lhs.is_positive == True) and (relation.rhs.is_negative == True):
                return relation.func( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            elif (relation.lhs.is_negative == True) and (relation.rhs.is_positive == True):
                return relation.func( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            elif (relation.lhs.is_negative == True) and (relation.rhs.is_negative == True):
                return _inverse_relations[relation.func]( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            elif (relation.lhs == 0):
                raise ZeroDivisionError("Can't take reciprocals with one side zero")
            elif (relation.rhs == 0):
                raise ZeroDivisionError("Can't take reciprocals with one side zero")
            else:
                raise ValueError("Couldn't determine sign of the expressions")
        else:
            # Method 2: solveset
            # Determine where are both sides positive or negative
            lhs_positive_interval = solveset(relation.lhs>0,variable,domain=interval)
            rhs_positive_interval = solveset(relation.rhs>0,variable,domain=interval)
            lhs_negative_interval = solveset(relation.lhs<0,variable,domain=interval)
            rhs_negative_interval = solveset(relation.rhs<0,variable,domain=interval)

            if (relation.lhs == 0):
                raise ZeroDivisionError("Can't take reciprocals with one side zero")
            elif (relation.rhs == 0):
                raise ZeroDivisionError("Can't take reciprocals with one side zero")

            # For both sides positive or negative, the relation is inversed
            pp_interval = lhs_positive_interval.intersect(rhs_positive_interval)
            nn_interval = lhs_negative_interval.intersect(rhs_negative_interval)
            eqsigns = _inverse_relations[relation.func]( 1/relation.lhs, 1/relation.rhs, evaluate=False )
            # For both sides of different signs, the relation is not inversed
            pn_interval = lhs_positive_interval.intersect(rhs_negative_interval)
            np_interval = lhs_negative_interval.intersect(rhs_positive_interval)
            difsigns = relation.func( 1/relation.lhs, 1/relation.rhs, evaluate=False )

            result = {}
            if nn_interval != EmptySet():
                result[nn_interval] = eqsigns
            if pp_interval != EmptySet():
                result[pp_interval] = eqsigns
            if pn_interval != EmptySet():
                result[pn_interval] = difsigns
            if np_interval != EmptySet():
                result[np_interval] = difsigns

            return result

    else:
        raise NotImplementedError("Only reciprocals (Pow,-1) are implemented")

def both_sides(relation,function,argument=None,interval=None,variable=None):
    r"""
    Applies a `function` with an `argument` to a `relation` and returns the resulting
    Relational.

    Parameters
    ==========
    relation: Relational
    function: sympy function to be applied
        currently implemented: Add,Mul,Pow,factor,simplify,collect,expand,together,apart
    argument: Sympy expression (optional)
    interval: Interval() (optional)
        interval in which the relation holds (intended to be used with inequalities)
    variable: Sympy expression (optional)
        if `interval` is specified, variable must also be specified. (Intended to be
        used with inequalities; only univariable inequalities are implemented)

    Warnings
    ========
    * Specifying an `interval` causes the fucntion to use `solveset`, which is slow.
    * Differentiation and integration are not implemented for equalities.
    * Unequalities are not supported.

    Rules implemented
    =================
    * Equality: any function applied to both sides preserves equality.
    * Addition to inequalities: $ x>y \implies x + c > y + c $, where $c \in \mathbb R$.
    * Multiplication to inequalities: $x \le y \implies xc \le yc$ if $c>0$ and $xc \ge yc$ if $c<0$.
    * Reciprocal of an inequality: $ x > y \implies 1/x < 1/y$ if $x>0,y>0$
      or $x<0,y<0$, and $1/x>1/y$ if $x>0,y<0$ or $x<0,y>0$
    * Subtraction and division of a quantity are achieved with addition of the inverse
      and multiplication of the reciprocal.

    Examples
    ========
    ```python
    >>> from sympy_relational_tools import *
    >>> x,y = symbols('x,y',real=True)
    >>> a = symbols('a',positive=True)
    >>> equation = Eq(5*x+x**2, x+5)
    >>> both_sides(equation,Add,-5*x)
    Eq(x**2, -4*x + 5)

    >>> equation1 = Eq(sin(x),x+1)
    >>> equation2 = both_sides(equation1,Pow,-2)
    >>> equation1
    Eq(sin(x), x + 1)
    >>> both_sides(equation2,expand)
    Eq(sin(x)**(-2), 1/(x**2 + 2*x + 1))

    >>> inequality = Ge(x+1,0,evaluate=False)
    >>> both_sides(inequality,Mul,a)
    a*(x + 1) >= 0

    >>> inequality = Ge(x+1,x**2,evaluate=False)
    >>> both_sides(inequality,Pow,-1,Interval(-1,1),x)
    {Union(Interval.open(-1, 0), Interval.Lopen(0, 1)): 1/(x + 1) <= x**(-2)}
    ```
    """
    if relation.func == Eq:
        return Eq( function(relation.lhs,argument), function(relation.rhs,argument) )
    else:
        if function == Add:
            return _both_sides_add_ineq(relation,argument)
        elif function == Mul:
            return _both_sides_mul_ineq(relation,argument,interval,variable)
        elif function == Pow:
            return _both_sides_pow_ineq(relation,argument,interval,variable)
        elif function in [factor,simplify,collect,expand,together,apart]:
            return relation.func( function(relation.lhs), function(relation.rhs), evaluate=False )
        else:
            raise NotImplementedError("Function not yet implemented")


#!/usr/bin/python
"""
author: Bhekimpilo Ndhlela
author: 18998712
module: Applied Mathematics(Numerical Analysis) TW324
task  : computer assignment 01
since : Friday-09-02-2018
"""

def question3a(debug=False):
    # get approximation values [J0, J1] respectively
    J0 = get_appr3A(0)
    J1 = get_appr3A(1)

    #get built in values for [J0, J1] respectively
    blt_val0 = ss.jn(0, 1)
    blt_val1 = ss.jn(1, 1)

    #get absolute error [abs_err0, abs_err1] respectively
    abs_err0 = abs(J0 - blt_val0)
    abs_err1 = abs(J1 - blt_val1)

    if debug is True:
        print "(appro_val0, blt_val0, abs_err0) = (" + str(J0) + ", " + str(blt_val0) + ", " + str(abs_err1)+")"
        print "(appro_val1, blt_val1, abs_err1) = (" + str(J1) + ", " + str(blt_val1) + ", " + str(abs_err1)+")"

def get_appr3A(n, x=1):
    J = 0
    for k in xrange(0, 4): # "4" is exclusive
        N = pow(-1, k) * pow(float(x)/2, n + (2 * k))
        D = sm.factorial(k) * sm.factorial(n + k)
        J = J + (N / D)
    return J

def question3b(x=1, debug=True):
    vector_J_A = []     #vector to store approximations
    vector_J_B = []     #vector to store built in value
    vector_J_A.append(get_appr3A(0))
    vector_J_A.append(get_appr3A(1))

    for n in xrange(1, 7):
        vector_J_A.append((((2 * n) / x) * vector_J_A[n]) - vector_J_A[n - 1])
    for n in xrange(0, 8):
        vector_J_B.append(ss.jn(n, 1))

    print len(vector_J_A), len(vector_J_B)
    if debug is True:
        print vector_J_A
        print vector_J_B

import scipy.special as ss
#import this for scipy misc factorial [for efficient factorial]
import scipy.misc as sm

question3a()
question3b()


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

x=np.array([10,1,5,6,2,4,6,3,7,6,0,4,2,3,6,4,5,9,1,2,8,7]) #Hours studied
y=np.array([1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,0,1,0,1,1,1]) #Pass=1 and Fail=0

plt.scatter(x,y,color="r",marker="*",s=100)

W=0
B=0
alpha = 0.01

def Sigmoid(z):
    G_Z = float(1.0 / float((1.0 + math.exp(-1.0*z))))
    return G_Z

def Gradient_Descent(a,b,x,y,hours):
    for i in range(10000):
        gradient1=0
        gradient2=0
        for j in range(len(x)):
            gradient1+=((Sigmoid(a+(b*x[j]))-y[j]))
            gradient2+=((Sigmoid(a+(b*x[j]))-y[j])*x[j])
        a-=(alpha*gradient1)
        b-=(alpha*gradient2)
    return a,b,Sigmoid((b*hours)+a)

def graph(a,b):
    y=[]
    x=np.array(range(0,12))
    for i in x:
        y.append(Sigmoid(b*i+a))
    plt.plot(x,y)
    plt.show()

hr=int(input('Enter no. of hours studied: '))
W,B,percentage=Gradient_Descent(W,B,x,y,hr)
print('\nThere is '+str(round(percentage*100,2))+'% chance that you will pass in the exam.')

graph(W,B)


#import our library
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

#Get our dataset
train_data = np.genfromtxt('iris-Train.data', delimiter=',', names=True)
test_data = np.genfromtxt('iris-Test.data', delimiter=',', names=True)

#Define our features and target
x1 = np.transpose(np.asmatrix(train_data['x1']))
x2 = np.transpose(np.asmatrix(train_data['x2']))
x = np.append(x1, x2, 1)
y = np.transpose(np.asmatrix(train_data['y']))

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

xtest1 = np.transpose(np.asmatrix(test_data['x1']))
xtest2 = np.transpose(np.asmatrix(test_data['x2']))
xtest = np.append(xtest1, xtest2, 1)
ytest = np.transpose(np.asmatrix(test_data['y']))

#Define theta and x_0
ones = np.ones((x.shape[0],1))
onestest = np.ones((xtest.shape[0],1))
one_x = np.append(ones, x, 1)
one_xtest = np.append(onestest, xtest, 1)

m, n = np.shape(one_x)
theta = np.zeros((1,n))
theta = np.transpose(theta)
theta_new = theta

#main function for binary classification(logistic Regression)

#Define our iteration & Learning Rate
loops= 1000
alpha = 0.002
#--------------------------------

#Define our MSE function with Gradient
mse = np.zeros((loops,2))
r = 0

while r < loops:

    h_theta = 1/(1+np.exp(-1*(one_x*theta)))
    gradient = one_x.T*(h_theta-y)
    theta = theta - alpha*gradient
    y_pred = 1/(1+np.exp(-1*(one_x*theta)))

    loop_mse = 0
    for i in range(0, m):

        loop_mse += -(y.A1[i]*np.log(1/(1+np.exp(-1*(one_x[i]*theta))))) - ((1-y.A1[i])*np.log(1-(1/(1+np.exp(-1*(one_x[i]*theta))))))
    loop_mse = loop_mse/(m)
    mse[r,0] = r
    mse[r,1] = loop_mse

    r += 1

print('Number of iteration = ',loops)
print('Learning Rate = ',alpha)
print('Theta[0] = ', theta.A1[0])
print('Theta[1] = ', theta.A1[1])
print('Theta[2] = ', theta.A1[2])

#Calculate our h(theta)
y_pred = 1/(1+np.exp(-1*(one_x*theta)))
y_pred_test = 1/(1+np.exp(-1*(one_xtest*theta)))

#calculate train MSE
train_mse = 0
m = y.shape[0]
for i in range(m):
    train_mse += ((y.A1[i] - y_pred.A1[i])**2)
train_mse = train_mse/(2*m)
print("Train MSE = ", train_mse)

#calculate test MSE
test_mse = 0
m = ytest.shape[0]
for i in range(m):
    test_mse += ((ytest.A1[i] - y_pred_test.A1[i])**2)
test_mse = test_mse/(2*m)
print("Test MSE = ", test_mse)

#classification our dataset in 2 class
minx1 = np.min(x1)
maxx1 = np.max(x1)
x2_1 = -(theta.A1[0] + theta.A1[1]*minx1)/theta.A1[2]
x2_2 = -(theta.A1[0] + theta.A1[1]*maxx1)/theta.A1[2]

#Plot our dataset and MSE
plt.scatter(train_data['x1'],train_data['x2'], c=train_data['y'], s = 10, label = 'Train data')
plt.scatter(train_data['x1'],train_data['x2'], c=train_data['y'], s = 10, label = 'Train data')
plt.legend(loc = 'upper left')
plt.plot((minx1,maxx1), (x2_1,x2_2))
plt.show()

plt.scatter(test_data['x1'],test_data['x2'], c=test_data['y'], s = 10, label = 'Test data')
plt.scatter(test_data['x1'],test_data['x2'], c=test_data['y'], s = 10, label = 'Test data')
plt.legend(loc = 'upper left')
plt.plot((minx1,maxx1), (x2_1,x2_2))
plt.show()

plt.plot(mse.T[0], mse.T[1], color = "black", label = 'MSE')
plt.legend(loc = 'upper right')
plt.show()

Binary Classification Logistic regression/LogisticRegression (Prart 1(A)).py


import sympy as sy
from sympy import pi, cos, sin, tan

q1 = sy.Symbol("q1")
q2 = sy.Symbol("q2")
q3 = sy.Symbol("q3")

qvec = sy.Matrix([[q1, q2, q3,]]).T

l1 = sy.Symbol("l1")
l2 = sy.Symbol("l2")
l3 = sy.Symbol("l2")
l4 = sy.Symbol("l2")

# l1 = 1.0
# l2 = 1.0
# l3 = 1.0
# l4 = 1.0

D1 = sy.Matrix([
        [cos(q1), -sin(q1), 0.0],
        [sin(q1), cos(q1), 0.0],
        [0.0, 0.0, 1.0],
    ])

D2 = sy.Matrix([
        [1.0, 0.0, l1],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0],
    ])

D3 = sy.Matrix([
        [cos(q2), -sin(q2), 0.0],
        [sin(q2), cos(q2), 0.0],
        [0.0, 0.0, 1.0],
    ])

D4 = sy.Matrix([
        [1.0, 0.0, l2],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0],
    ])

theta = 270/180*pi
D5 = sy.Matrix([
        [cos(theta), -sin(theta), 0.0],
        [sin(theta), cos(theta), 0.0],
        [0.0, 0.0, 1.0],
    ])

D6 = sy.Matrix([
        [1.0, 0.0, l3],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0],
    ])

D7 = sy.Matrix([
        [cos(q3), -sin(q3), 0.0],
        [sin(q3), cos(q3), 0.0],
        [0.0, 0.0, 1.0],
    ])

D8 = sy.Matrix([
        [1.0, 0.0, l4],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0],
    ])

D_end = D1 * D2 * D3 * D4 * D5 * D6 * D7 * D8
D_end = sy.simplify(D_end)

print(D_end[0, 2])
print(D_end[1, 2])
print(D_end[2, 2])

# #x, y, phi = sy.symbols('x, y, phi')

# x = 2.9
# y = -0.9
# phi = float(pi)/2

# fx = x - D_end[0, 2]
# fy = y - D_end[1, 2]
# fq = q1 + q2 + q3 -phi -2*float(pi)

# sol = sy.solve([fx, fy, fq], [q1, q2, q3])
# print(sol)


import numpy as np

def cagr(risk_returns,Rebalancement_frequency):
        """

        Compute the Compound Annual Growth Rate (CAGR)
        Parameters
        ----------
        risk_returns : np.ndarray
        Rebalancement_frequency : np.float64
        Returns
        ----------
        float64, CAGR

        """
        Value_earned = Cumulative_ret(risk_returns)[-1]
        N = risk_returns.size / Rebalancement_frequency
        cagr = Value_earned ** (1/N) - 1
        return cagr

def Cumulative_ret(risk_returns):
        """
        Compute the Cumulative return across time
        Parameters
        ----------
        risk_returns : np.ndarray
        Returns
        ----------
        np.ndarray, CAGR

        """
        return np.cumprod(1 + risk_returns)


# -*- coding: utf-8 -*-
"""
Created on Wed Feb 13 13:31:03 2019

@author: Aditya's HP Omen 15
"""

# this program if for line fitting using OLS (oridnary least squares)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
import pickle
from statistics import mean
from numpy import linalg as LA

style.use('fivethirtyeight')

# DATA1
f = open('data1_new.pkl','rb')
data1 = pickle.load(f)
f.close()
da = np.asarray(data1)
X = da[:,0]
Y = da[:,1]
plt.axis([-150,150,-100,100])

def TLS(X,Y):
    numerator = (np.sum(Y**2)- Y.shape[0]*(mean(Y)**2))-(np.sum(X**2)- X.shape[0]*(mean(X)**2))
    denominator = X.shape[0]*mean(X)*mean(Y) -np.sum(np.multiply(X,Y))
    B = 0.5*numerator/denominator
    m = -B+ np.sqrt(B*B+1)
    c = mean(Y) -m*mean(X)
    return m,c

TLS_m,TLS_c = TLS(X,Y)
TLS_regression_line = [(TLS_m*x)+TLS_c for x in X]
plt.figure(1)
plt.scatter(X,Y, color = 'blue')
plt.plot(X,TLS_regression_line, color = 'red', label = "TLS")

def OLS(X,Y):
    m = (np.multiply(mean(X),mean(Y)) -mean(np.multiply(X,Y) ))/((mean(X))**2-mean((X)**2))
    c = mean(Y) - m*mean(X)
    return m,c
m,c = OLS(X,Y)
regression_line = [(m*x)+c for x in X]
plt.scatter(X,Y, color = 'blue')
plt.plot(X,regression_line, color = 'green', label = "OLS")
plt.title("Line fitting with vertical and orthogonal least squares on data 1")

plt.legend()
plt.axis([-150,150,-100,100])
plt.show()

#DATA2
f = open('data2_new.pkl','rb')
data1 = pickle.load(f)
f.close()
da = np.asarray(data1)
X = da[:,0]
Y = da[:,1]
plt.axis([-150,150,-100,100])

def TLS(X,Y):
    numerator = (np.sum(Y**2)- Y.shape[0]*(mean(Y)**2))-(np.sum(X**2)- X.shape[0]*(mean(X)**2))
    denominator = X.shape[0]*mean(X)*mean(Y) -np.sum(np.multiply(X,Y))
    B = 0.5*numerator/denominator
    m = -B+ np.sqrt(B*B+1)
    c = mean(Y) -m*mean(X)
    return m,c

TLS_m,TLS_c = TLS(X,Y)
TLS_regression_line = [(TLS_m*x)+TLS_c for x in X]
plt.figure(2)
plt.scatter(X,Y, color = 'blue')
plt.plot(X,TLS_regression_line, color = 'red', label = "TLS")

def OLS(X,Y):
    m = (np.multiply(mean(X),mean(Y)) -mean(np.multiply(X,Y) ))/((mean(X))**2-mean((X)**2))
    c = mean(Y) - m*mean(X)
    return m,c
m,c = OLS(X,Y)
regression_line = [(m*x)+c for x in X]
plt.plot(X,regression_line, color = 'green', label = "OLS")
plt.title("Line fitting with vertical and orthogonal least squares on data 1")
plt.legend()
plt.axis([-150,150,-100,100])
plt.show()

#DATA3
f = open('data3_new.pkl','rb')
data1 = pickle.load(f)
f.close()
da = np.asarray(data1)
X = da[:,0]
Y = da[:,1]
plt.axis([-150,150,-100,100])

def TLS(X,Y):
    numerator = (np.sum(Y**2)- Y.shape[0]*(mean(Y)**2))-(np.sum(X**2)- X.shape[0]*(mean(X)**2))
    denominator = X.shape[0]*mean(X)*mean(Y) -np.sum(np.multiply(X,Y))
    B = 0.5*numerator/denominator
    m = -B+ np.sqrt(B*B+1)
    c = mean(Y) -m*mean(X)
    return m,c

TLS_m,TLS_c = TLS(X,Y)
TLS_regression_line = [(TLS_m*x)+TLS_c for x in X]
plt.figure(3)
plt.scatter(X,Y, color = 'blue')
plt.plot(X,TLS_regression_line, color = 'red', label = "TLS")

def OLS(X,Y):
    m = (np.multiply(mean(X),mean(Y)) -mean(np.multiply(X,Y) ))/((mean(X))**2-mean((X)**2))
    c = mean(Y) - m*mean(X)
    return m,c
m,c = OLS(X,Y)
regression_line = [(m*x)+c for x in X]
plt.plot(X,regression_line, color = 'green', label = "OLS")
plt.title("Line fitting with vertical and orthogonal least squares on data 3")
plt.legend()
plt.axis([-150,150,-100,100])
plt.show()


#Imports
import numpy as np
from numpy.fft import rfft2, irfft2 #Importing built in 2D Fast Fourier Transforms: rfft2 is 2D real fast fourier transform, irfft2 is 2D inverse real fast fourier transform

InputArray = np.zeros((100,100)) #Array to be fourier transformed
for i in range(100):
    for j in range(100):
        InputArray[i,j] = i+j
print InputArray[10,10]

FT = rfft2(InputArray) #Fourier transform function takes array as input and outputs an array of 1/2 N +1 size. e.g. If InputArray is 100x100, FT will be 100x51. FT is array of coefficients of sinusoidal functions.
print FT[10,10]

#FT array can be manipulated e.g. Removing coefficients for smoothing

IFT = irfft2(FT) #Inverse fourier transform function takes array as input and outputs an array of 2(N-1) size e.g. If FT is 100x51, IFT will be 100x100
print IFT[10,10]

#An explicit Discrete Fourier Transform code is supplied in the slides, but is more complex and less efficient than the in built fast fourier transforms.


import math
import numpy as np
from itertools import combinations

def factorize(n):
	result = []
	for i in range(1,int(np.sqrt(n)+1)):
		if (n%i==0):
			result.append(i)
			result.append(int(n/i))
		result.sort()
	return result

def sumoffactors(n):
	factors = factorize(n)
	sum = 0
	for factor in factors:
		sum +=factor
	return sum-n

#print(factorize(220))
#print(sumoffactors(220))
#print(factorize(284))
#print(sumoffactors(284))

def amicable(a, b):
	if sumoffactors(a) == b and sumoffactors(b) ==a:
		return True
	else:
		return False
comb = combinations(range(2,10001),2)
sum = 0
for item in comb:
	a,b = item
	#print(item)
	if amicable(a,b):
		print(a,b)
		sum+= a+b
print(sum)


import math
import base64
import sympy
import numpy as np
from matplotlib.figure import Figure
from io import BytesIO

def graphic_method(f, f_str, fp, fp_str, x_a, x_b, step=100):

    x = [x for x in np.linspace(x_a, x_b, step)]
    y = [f(x) for x in np.linspace(x_a, x_b, step)]
    yp = [fp(x) for x in np.linspace(x_a, x_b, step)]

    fig = Figure()
    ax = fig.subplots()

    # ax.title = "Metodo grafico"
    ax.xlabel = "x"
    ax.ylabel = "y"
    ax.plot(x, y, label=f"f(x)={f_str}")
    ax.plot(x, yp, "--", label=f"f'(x)={fp_str}")
    ax.legend()
    ax.grid()

    # Save it to a temporary buffer.
    buf = BytesIO()
    fig.savefig(buf, format="png")

    return base64.b64encode(buf.getbuffer()).decode("ascii")

def bi(x_inicial, f, delta=0.001, n_iter_max=1000):

    x_a = x_inicial
    f_a = f(x_a)

    raiz = None
    message = None
    results = [[x_a, f_a]]

    if f_a == 0:
        message = f"{x_a} es raiz de f(x)"
        raiz = x_a

    x_b = x_inicial + delta
    f_b = f(x_b)

    for _ in range(n_iter_max):
        if f_a * f_b < 0:
            message = f"Existe una raiz de f(x) entre {x_a} y {x_b}"
            raiz = [x_a, x_b]

        x_a = x_b
        f_a = f_b

        results.append([x_a, f_a])

        x_b += delta
        f_b = f(x_b)

        if f_b == 0:
            message = f"{x_b} es raiz de f(x)"
            raiz = x_b

    else:
        print(f"Fracaso en {n_iter_max} iteraciones ")

    return raiz, message, results

def biseccion(x_a, x_b, f, tol=1e-6, err="abs", regla_falsa=False, n_iter_max=1000):

    f_a = f(x_a)
    f_b = f(x_b)

    raiz = None
    message = None
    results = []

    if f_a == 0:
        message = f"{x_a} es raiz de f(x)"
        raiz = x_a

    elif f_b == 0:
        message = f"{x_b} es raiz de f(x)"
        raiz = x_b

    elif f_a * f_b > 0:
        message = f"No existe raiz en el intervalo [{x_a}, {x_b}]"

    if message is not None:
        return raiz, message, results

    x_m = (f_b * x_a - f_a * x_b) / (f_b - f_a) if regla_falsa else (x_a + x_b) / 2
    f_m = f(x_m)
    results = [[x_m, f_m, 100]]

    for _ in range(n_iter_max):
        if f_a * f_m < 0:
            x_b = x_m
            f_b = f(x_b)
        else:
            x_a = x_m
            f_a = f(x_a)

        x_c = (f_b * x_a - f_a * x_b) / (f_b - f_a) if regla_falsa else (x_a + x_b) / 2
        error = abs((x_c - x_m) / (x_m if err == "rel" else 1))
        x_m = x_c
        f_m = f(x_m)

        results.append([x_m, f_m, error])

        if f_m == 0:
            message = f"{x_m} es raiz de f(x)"
            raiz = x_m
            break

        if error < tol:
            message = f"{x_m} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            raiz = x_m
            break
    else:
        message = f"Fracaso en {n_iter_max} iteraciones "

    return raiz, message, results

def n_iter_biseccion(x_a, x_b, x_raiz, x_mf):

    return math.floor(math.log(abs((x_b - x_a) / (x_mf - x_raiz)), 2))

def punto_fijo(x_a, f, g, tol=1e-6, err="abs", n_iter_max=1000):

    f_a = f(x_a)

    # x_a, f_a, err_a
    results = [[x_a, f_a, 100]]

    for _ in range(n_iter_max):
        x_b = g(x_a)
        error = abs((x_b - x_a) / (x_a if err == "rel" else 1))

        x_a = x_b
        f_a = f(x_a)

        results.append([x_a, f_a, error])

        if f_a == 0:
            message = f"{x_a} es raiz de f(x)"
            break
        elif error < tol:
            message = f"{x_a} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            break

    else:
        message = f"Fracaso en {n_iter_max} iteraciones"

    return x_a, message, results

def n_iter_punto_fijo(x_a, x_b, k, x_0, error):

    return math.floor(np.log(error / max(x_0 - x_a, x_b - x_0)) / np.log(k))

def newton_raphson(x_a, f, fp, m=1, tol=1e-6, err="abs", n_iter_max=1000):

    f_a = f(x_a)
    fp_a = fp(x_a)
    results = [[x_a, f_a, fp_a, 100]]
    message = None

    for _ in range(n_iter_max):
        if fp_a == 0:
            message = f"La derivada de la f(x) en {x_a} es cero"
            break

        x_b = x_a - m * f_a / fp_a

        error = abs((x_b - x_a) / (x_a if err == "rel" else 1))

        x_a = x_b
        f_a = f(x_a)
        fp_a = fp(x_a)

        results.append([x_a, f_a, fp_a, error])

        if f_a == 0:
            message = f"{x_a} es raiz de f(x)"
            break

        elif error < tol:
            message = f"{x_a} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            break
    else:
        message = f"Fracaso en {n_iter_max} iteraciones"

    return x_a, message, results

def newton_raphson_multiples_raices(
    x_a, f, fp, fpp, tol=1e-6, err="abs", n_iter_max=1000
):

    f_a = f(x_a)
    fp_a = fp(x_a)
    fpp_a = fpp(x_a)
    results = [[x_a, f_a, fp_a, fpp_a, 100]]
    message = None

    for _ in range(n_iter_max):
        if fp_a == 0:
            message = f"La derivada de la f(x) en {x_a} es cero"
            break

        x_b = x_a - f_a * fp_a / (fp_a ** 2 - f_a * fpp_a)

        error = abs((x_b - x_a) / (x_a if err == "rel" else 1))

        x_a = x_b
        f_a = f(x_a)
        fp_a = fp(x_a)

        results.append([x_a, f_a, fp_a, fpp_a, error])

        if f_a == 0:
            message = f"{x_a} es raiz de f(x)"
            break

        elif error < tol:
            message = f"{x_a} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            break
    else:
        message = f"Fracaso en {n_iter_max} iteraciones"

    return x_a, message, results

def secante(x_a, x_b, f, tol=1e-6, err="abs", n_iter_max=1000):
    # x_a debe ser menor que x_b

    f_a = f(x_a)
    f_b = f(x_b)
    results = [[x_a, f_a, 100], [x_b, f_b, 100]]
    message = None

    for _ in range(n_iter_max):
        x_c = x_b - f_b * (x_b - x_a) / (f_b - f_a)

        error = abs((x_c - x_b) / (x_b if err == "rel" else 1))

        x_a = x_b
        x_b = x_c
        f_a = f_b
        f_b = f(x_c)

        results.append([x_b, f_b, error])

        if f_a == 0:
            message = f"{x_a} es raiz de f(x)"
            break

        elif error < tol:
            message = f"{x_a} es una aproximacion de una raiz de f(x) con una tolerancia {tol}"
            break
    else:
        message = f"Fracaso en {n_iter_max} iteraciones"

    return x_a, message, results

def check_n_raices(f, x):
    fp = sympy.diff(get_expr(f))

    n_raices = 1
    while parse_function(fp)(x) == 0:
        n_raices += 1
        fp = sympy.diff(fp)

    return n_raices


# -*- coding: utf-8 -*-
"""
Created on Thu May 23 12:13:20 2019

@author: Tin
"""

import numpy as np
from numpy.linalg import inv

options = " Matrix Addition, Matrix Subtraction, Matrix Multiplication, Matrix Division, Dot Product, Matrix Scalar, Matrix Transpose, Matrix Inverse, Pseudo Inverse Matrix, Quit".split(",")

def input_X_matrix():
    R = int(input("Enter the number of rows: "))
    C = int(input("Enter the number of columns: "))
    print("Input the entries in a single line (separated by space): ")
    x = list(map(int, input().split()))
    X_matrix = np.array(x).reshape(R, C)
    print(X_matrix)
    return X_matrix

def input_Y_matrix():
    R = int(input("Enter the number of rows: "))
    C = int(input("Enter the number of columns: "))
    print("Input the entries in a single line (separated by space): ")
    y = list(map(int, input().split()))
    Y_matrix = np.array(y).reshape(R, C)
    print(Y_matrix)
    return Y_matrix

def matrix_addition():
    add = input_X_matrix() + input_Y_matrix()
    return add

def matrix_subtraction():
    sub = input_X_matrix() - input_Y_matrix()
    return sub

def matrix_multiplication():
    multiply = input_X_matrix() * input_Y_matrix()
    return multiply

def matrix_divison():
    divison = input_X_matrix() / input_Y_matrix()
    return divison

def dot_product():
    A = input_X_matrix()
    B = input_Y_matrix()
    C = A.dot(B)
    print('___Dot Product___')
    print(C)
    return C

def matrix_scalar():
    A = input_X_matrix()
    b = float(input("Enter a number: "))
    c = A*b
    print('__Matrix-Scalar Multiplication___')
    print(c)
    return c

def matrix_transpose():
    mt = input_X_matrix()
    T_m = mt.T
    print('___Matrix Transpose___')
    print(T_m)
    return

def matrix_inverse():
    mt = input_X_matrix()
    I_m = inv(mt)
    print('___Matrix Inverse___')
    print(I_m)
    return

def p_inverse():
    m = input_X_matrix()
    pim = np.linalg.pinv(m)
    print('___Pseudo Inverse Matrix___')
    print(pim)
    return

def main():
    run_program = True
    while run_program:
        print("\n")
        print("Choose Option:")
        print("Mathematics for Machine Learning Linear Algebra")
        for i in range(1, len(options)+1):
            print("{} - {}".format(i, options[i-1]))
        choice = int(input())

        if choice == 1:
             print('___Matrix Addition___')
             matrix_addition()
        elif choice == 2:
             print('___Matrix Subtraction___')
             matrix_subtraction()
        elif choice == 3:
             print('___Matrix Multiplication___')
             matrix_multiplication()
        elif choice == 4:
             print('___Matrix Division___')
             matrix_divison()
        elif choice == 5:
             dot_product()
        elif choice == 6:
             matrix_scalar()
        elif choice == 7:
             matrix_transpose()
        elif choice == 8:
             matrix_inverse()
        elif choice == 9:
             p_inverse()
        elif choice == 10:
             run_program = False

if __name__ == "__main__":
    main()


import numpy as np

def simpson(function, p0, n : int):
    x,y=p0
    dx=np.abs(x-y)/n
    r=0
    for i in range(n):
        r=r+(1/6)*(x+dx-x)*(function(x)+function(x+dx)+4*function(x+dx/2))
        x=x+dx
    return r

# find the value of pi by integrating sin(x)^2 from 0 to 2pi
f = lambda x : np.sin(x)**2
x = simpson(f, (0, 2*np.pi), int(1e6))
print("The value of pi is : {}".format(x))


import numpy as np

def dft(x):
    size = len(x)
    a = np.zeros(size, dtype=np.complex)
    for k in xrange(size):
        for j in xrange(size):
            a[k] += x[j] * np.exp(-1j * 2 * np.pi * k * j / size)
    return a

def idft(x):
    size = len(x)
    a = np.zeros(size, dtype=np.complex)
    for j in xrange(size):
        for i in range(size):
            a[j] += x[i] * np.exp(1j * 2 * np.pi * i * j / size)
    return a / size


import numpy as np
a = np.array([0.3, 2.9, 4.0])

def softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a/ sum_exp_a
    return y

def softmax2(a):
    c = np.max(a)
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a/ sum_exp_a
    return y

y = softmax(a)
y2 = softmax2(a)
print(y)
print(np.sum(y))
print('\n', y2)
print(np.sum(y2))


from matplotlib.pyplot import (legend, plot, scatter, show)
from numpy import (array, mean, sqrt, square)

data_set = array([
    [32, 31], [53, 68], [60, 62], [47, 71], [59, 87],
    [55, 78], [52, 79], [39, 59], [48, 75], [52, 71],
    [45, 55], [54, 82], [44, 62], [58, 75], [56, 81],
    [48, 60], [44, 82], [60, 97], [45, 48], [38, 56],
    [66, 83], [65, 11], [47, 57], [41, 51], [51, 75]
])

x_list = data_set[:, 0]
y_list = data_set[:, 1]

count = len(data_set)
intercept = 0
slope = 0

def line_equation(x):
    return slope * x + intercept

learning_rate = 0.0001
iteration_cycles = 1000

for _ in range(iteration_cycles):
    new_y = list(map(line_equation, x_list))
    y_difference = y_list - new_y

    intercept_gradient = sum(-2 / count * y_difference)
    slope_gradient = sum(-2 / count * y_difference * x_list)

    intercept -= learning_rate * intercept_gradient
    slope -= learning_rate * slope_gradient

predicted_y = list(map(line_equation, x_list))

rmse = sqrt(mean(square(y_list - predicted_y)))

print("intercept =", intercept)
print("slope =", slope)
print("rmse =", rmse)

test_x = [44, 32, 63]
test_y = list(map(line_equation, test_x))

scatter(x_list, y_list, label='original')
scatter(test_x, test_y, label='prediction')
plot(x_list, predicted_y, label='line of best fit')
legend()
show()

Gradient Descent for Linear Regression.py


import numpy as np
import matplotlib.pyplot as plt

def signal(sinuses):
    ''' This function creates a function f(x) = sin(a*pi*2)+sin(b*p*2)+...
     plots the graph of f(x) and saves it

    Args:
        sinuses(Float) : The list of constants a,b,c,...

    Returns:
        t (numpy array): X axis
        s (numpy array): Y axis
    '''
    # Create the singal as a sum of different sinuses
    t = np.linspace(0, 0.5, 800)
    s=0
    for i in range(len(sinuses)):
        s += np.sin(sinuses[i] * 2 * np.pi * t)

    # Plot the signal
    plt.style.use('seaborn')
    fig = plt.figure(figsize=(8,4))
    ax = fig.add_subplot(1,1,1)

    ax.plot(t, s, label = r'$y=f(x)$')

    ax.set_title(" Signal ", fontsize = 20)
    ax.set_ylabel("Amplitude")
    ax.set_xlabel("Time [s]")

    ax.legend(loc='best')
    ax.grid(True)
    plt.show()
    fig.savefig('signal.png')
    return s, t

def Fourier(s, t, alg = "False"):
    '''This function performs the FFT in the function f(x) defined in signal function, plots and saves the figure

    Args:
        t (numpy array): X axis
        s (numpy array): Y axis
        alg (string): Variable to determine whether to use my FFT or numpy's
    '''

    #Perform the Fourier Transform
    if alg == "True":
        fft = FFT(s)
    else:
        fft = np.fft.fft(s)

    T = t[1] - t[0]  # sample rate
    N = s.size

    # 1/T = frequency
    f = np.linspace(0, 1 / T, N)

    # Plot the signal Decomposition
    plt.style.use('seaborn')
    fig = plt.figure(figsize=(8,4))
    ax = fig.add_subplot(1,1,1)

    ax.set_title(" Decomposed Signal ", fontsize = 20)
    ax.set_ylabel("Amplitude")
    ax.set_xlabel("Frequency [Hz]")

    ax.bar(f[:N // 2], np.abs(fft)[:N // 2] * 1 / N, width=1.5)  # 1 / N is a normalization factor
    ax.grid(False)
    plt.show()
    fig.savefig("Decomposed_signal.png")

def FFT(x):
    """Compute the discrete Fourier Transform of the signal x

    Args:
        x (numpy array): The function f(x)

    Returns:
        M (numpy array): The FFT
    """

    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    n = np.arange(N)
    k = n.reshape((N, 1))
    M = np.exp(-2j * np.pi * k * n / N)
    return np.dot(M, x)


import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
import numpy.ma as ma
from numpy.random import uniform, seed
from matplotlib import cm

u = np.asarray([0.,0.])
sig = np.asarray(
    [
        [1.8 , 0],
        [0 , 1.8]
    ]
)

def gauss(x,y,Sigma,mu):
    X=np.vstack((x,y)).T
    mat_multi=np.dot((X-mu[None,...]).dot(np.linalg.inv(Sigma)),(X-mu[None,...]).T)
    return  np.diag(np.exp(-1*(mat_multi)))

def plot_countour(x,y,z):
    # define grid.
    xi = np.linspace(-2.1, 2.1, 100)
    yi = np.linspace(-2.1, 2.1, 100)
    ## grid the data.
    zi = griddata((x, y), z, (xi[None,:], yi[:,None]), method='cubic')
    levels = [0.2, 0.4, 0.6, 0.8, 1.0]
    # contour the gridded data, plotting dots at the randomly spaced data points.
    CS = plt.contour(xi,yi,zi,len(levels),linewidths=0.5,colors='k', levels=levels)
    #CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
    CS = plt.contourf(xi,yi,zi,len(levels),cmap=cm.Greys_r, levels=levels)
    plt.colorbar() # draw colorbar
    # plot data points.
    # plt.scatter(x, y, marker='o', c='b', s=5)
    plt.xlim(-2, 2)
    plt.ylim(-2, 2)
    plt.title('griddata test (%d points)' % npts)
    plt.show()

# make up some randomly distributed data
seed(1234)
npts = 1000
x = uniform(-2,2,npts)
y = uniform(-2,2,npts)

z = gauss(x, y, Sigma=sig, mu=u)
plot_countour(x, y, z)


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

class imageTransform:

    def log_transform(self, impath):
      c = 35
      inp = cv2.imread(impath,0)
      opt = np.zeros((inp.shape[0], inp.shape[1]))
      for m in range(0, inp.shape[0]):
          for n in range(0, inp.shape[1]):
              opt[m, n] = c * np.log(1 + inp[m, n])
      return opt

    def power_law_transform(self,impath):
      gama = 1.3
      constant = 1
      inp = cv2.imread(impath,0)
      opt = np.zeros((inp.shape[0], inp.shape[1]))
      for m in range(0, inp.shape[0]):
          for n in range(0, inp.shape[1]):
              opt[m, n] = constant * np.power(inp[m, n], gama)
      return opt

    def histogram_equalization(self,impath):
        inp = cv2.imread(impath,0)
        opt = np.zeros((inp.shape[0], inp.shape[1]))
        hist_vals = np.zeros([1,256])
        for m in range(0,inp.shape[0]):
            for n in range(0,inp.shape[1]):
                hist_vals[0][inp[m,n]] +=1
        normalized_hist = hist_vals/(inp.shape[0]*inp.shape[1])
        for m in range(0,inp.shape[0]):
            for n in range(0,inp.shape[1]):
                 indices = np.arange(inp[m,n])
                 cdf = normalized_hist[0][indices].sum()
                 opt[m,n] = 255*cdf
        return opt

    def negative_image(self,impath):
        inp = cv2.imread(impath,0)
        L = 256
        opt = np.zeros((inp.shape[0], inp.shape[1]));
        for m in range(0, inp.shape[0]):
            for n in range(0, inp.shape[1]):
                opt[m, n] = L - 1 - inp[m, n]
        return opt


import numpy as np
import scipy.optimize as opt

from scipy.interpolate import InterpolatedUnivariateSpline
from scipy.special import binom
from typing import Union, List, Tuple, Optional

def cls(
    x: Union[float, List[float], np.ndarray], n1: float, n2: float, norm: bool = True
) -> np.ndarray:
    """
    Compute class function.

    Parameters
    ----------
    x : array_like
        Points to evaluate class function for
    n1, n2 : int
        Class function parameters
    norm : bool, optional
        True (default) if the class function should be normalized

    Returns
    -------
    np.array
        Class function value for each given point

    Notes
    -----
    It is assumed that 0 <= x <= 1.
    """
    c = (x ** n1) * ((1.0 - x) ** n2)
    c /= (
        1.0
        if not norm or n1 == n2 == 0
        else (((n1 / (n1 + n2)) ** n1) * ((n2 / (n1 + n2)) ** n2))
    )
    return c

def bernstein(x: Union[float, List[float], np.ndarray], r: int, n: int) -> np.array:
    """
    Compute Bernstein basis polynomial.

    Parameters
    ----------
    x : array_like
        Points to evaluate the Bernstein polynomial at
    r, n : int
        Bernstein polynomial index and degree

    Returns
    -------
    np.array
        Values of the Bernstein polynomial at the given points

    Notes
    -----
    It is assumed that 0 <= x < 1.
    It is assumed that r <= n.
    """
    return binom(n, r) * (x ** r) * ((1.0 - x) ** (n - r))

def cst(
    x: Union[float, List[float], np.ndarray],
    a: Union[List[float], np.ndarray],
    delta: Tuple[float, float] = (0.0, 0.0),
    n1: float = 0.5,
    n2: float = 1.0,
) -> np.ndarray:
    """Compute coordinates of a CST-decomposed curve.

    This function uses the Class-Shape Transformation (CST) method to compute the y-coordinates as a function of a given
    set of x-coordinates, `x`, and a set of coefficients, `a`. The x-coordinates can be scaled by providing a length
    scale, `c`. The starting and ending points of the curve can be displaced by providing non-zero values for `delta`.
    Finally, the class of shapes generated can be adjusted with the `n1` and `n2` parameters. By default, these are 0.5
    and 1.0 respectively, which are good values for generating airfoil shapes.

    Parameters
    ----------
    x : float or array_like
        x-coordinates.
    a : array_like
        Bernstein coefficients.
    delta : tuple of two floats
        Vertical displacements of the start- and endpoints of the curve. Default is (0., 0.).
    n1, n2 : float
        Class parameters. These determine the general "class" of the shape. They default to n1=0.5 and n2=1.0 for
        airfoil-like shapes.

    Returns
    -------
    y : np.ndarray
        Y-coordinates.

    Notes
    -----
    It is assumed that 0 <= x <= 1.

    References
    ----------
    [1] Brenda M. Kulfan, '"CST" Universal Parametric Geometry Representation Method With Applications to Supersonic
     Aircraft,' Fourth International Conference on Flow Dynamics, Sendai, Japan, September 2007.
    """
    # Ensure x is a numpy array
    x = np.atleast_1d(x)

    # Bernstein polynomial degree
    n = len(a) - 1

    # Compute Class and Shape functions
    _class = cls(x, n1, n2)
    _shape = sum(a[r] * bernstein(x, r, n) for r in range(len(a)))

    # Compute y-coordinates
    y = _class * _shape + (1.0 - x) * delta[0] + x * delta[1]
    return y

def fit(
    x: Union[List[float], np.ndarray],
    y: Union[List[float], np.ndarray],
    n: int,
    delta: Optional[Tuple[float, float]] = None,
    n1: float = 0.5,
    n2: float = 1.0,
) -> Tuple[np.ndarray, Tuple[float, float]]:
    """Fit a set of coordinates to a CST representation.

    Parameters
    ----------
    x, y : array_like
        X- and y-coordinates of a curve.
    n : int
        Number of Bernstein coefficients.
    delta : tuple of two floats, optional
        Manually set the start- and endpoint displacements.
    n1, n2 : float
        Class parameters. Default values are 0.5 and 1.0 respectively.

    Returns
    -------
    A : np.ndarray
        Bernstein coefficients describing the curve.
    delta : tuple of floats
        Displacements of the start- and endpoints of the curve.

    Notes
    -----
    It is assumed that 0 <= x <= 1.
    """
    # Ensure x and y are np.ndarrays
    x = np.atleast_1d(x)
    y = np.atleast_1d(y)

    # Make sure the coordinates are sorted by x-coordinate
    ind = np.argsort(x)
    x = x[ind]
    y = y[ind]

    if delta is None:
        f = InterpolatedUnivariateSpline(x, y)
        delta = (f(0.0), f(1.0))

    def f(_a):
        return np.sqrt(np.mean((y - cst(x, _a, delta=delta, n1=n1, n2=n2)) ** 2))

    # Fit the curve
    res = opt.minimize(f, np.zeros(n))

    return res.x, delta


import pandas as pd
import numpy as np

class LinearRegression():
    def __init__(self):
        self.weights = None
        self.residuals = None
        self.feature_names = None
        self.train_preds = None
        self.r_squared = None

    def train_test_split(self,X,y,test_proportion=0.2):

        assert f"Input data needs to be type pandas DataFrame \
                    or numpy matrix, not {type(data)}"

        num_samples = data.shape[0]

        test_ind = np.random.choice(range(num_samples),
                                    int(num_samples*test_proportion),
                                    replace=False)
        train_ind = np.array(list(set(range(num_samples)) - set(test_ind)))

        X_test = X.iloc[test_ind]
        y_test = y.iloc[test_ind]

        X_train = X.iloc[train_ind]
        y_train = y.iloc[train_ind]

        return X_train, y_train, X_test, y_test

    def fit(self,X,y):

        if not isinstance(X,pd.DataFrame) and not isinstance(X, np.matrix):
            assert f"Input X data needs to be type pandas DataFrame \
                    or numpy matrix, not {type(X)}"

        if not isinstance(y,pd.DataFrame) and not isinstance(y, np.matrix) and not isinstance(y, pd.Series):
            assert f"Input y data needs to be type pandas DataFrame \
                    or pandas Series or numpy matrix, not {type(y)}"

        if isinstance(X,pd.DataFrame):
            self.feature_names = list(X.columns)

        # add column of ones for intercept
        X = np.concatenate([np.ones((X.shape[0],1)),X],axis=1)

        #weights = np.linalg.inv(X.transpose() @ X) @ (X.transpose() @ y)
        weights = np.linalg.solve(X.transpose() @ X), (X.transpose() @ y)

        self.weights = weights.reshape(-1,1)

        train_preds = self.predict(X[:,1:])

        self.residuals = train_preds - y.to_numpy().reshape(-1,1)

        # calculate r-squared
        y_bar = np.mean(y_train)
        sst = sum([(y-y_bar)**2 for y in y])
        ssreg = sum([(y-y_bar)**2 for y in train_preds])
        self.r_squared = np.round(float(sst/ssreg),decimals=4)

    def predict(self,X):

        if not isinstance(X,pd.DataFrame) and not isinstance(X, np.matrix):
            assert f"Input data needs to be type pandas DataFrame \
                    or numpy matrix, not {type(X)}"

        # add column of ones for intercept
        X = np.concatenate([np.ones((X.shape[0],1)),X],axis=1)

        return X @ self.weights

    def summary(self):

        print("R-squared:",self.r_squared,'\n')

        if self.feature_names:
            df = pd.DataFrame(lr.weights,
                              columns=['Weight'],
                              index=['Intercept']+self.feature_names)
            print(df)

if __name__ == '__main__':
    data = pd.read_csv('USA_Housing.csv')
    X = data.iloc[:,:-2]
    y = data.Price
    lr = LinearRegression()
    X_train, y_train, X_test, y_test = lr.train_test_split(X,y)
    lr.fit(X_train,y_train)
    preds = lr.predict(X_test)
    lr.summary()


# -*- coding: utf-8 -*-
"""
Created on Thu Feb  4 16:09:56 2016

@author: edouard.duchesnay@cea.fr
"""
import numpy as np
import scipy
import matplotlib.pyplot as plt
import seaborn as sns
#%matplotlib inline

'''
Mahalanobis distance
====================
'''

from matplotlib.patches import Ellipse
def plot_cov_ellipse(cov, pos, nstd=2, ax=None, **kwargs):
    """
    Plots an `nstd` sigma error ellipse based on the specified covariance
    matrix (`cov`). Additional keyword arguments are passed on to the
    ellipse patch artist.

    Parameters
    ----------
        cov : The 2x2 covariance matrix to base the ellipse on
        pos : The location of the center of the ellipse. Expects a 2-element
            sequence of [x0, y0].
        nstd : The radius of the ellipse in numbers of standard deviations.
            Defaults to 2 standard deviations.
        ax : The axis that the ellipse will be plotted on. Defaults to the
            current axis.
        Additional keyword arguments are pass on to the ellipse patch.

    Returns
    -------
        A matplotlib ellipse artist
    """
    def eigsorted(cov):
        vals, vecs = np.linalg.eigh(cov)
        order = vals.argsort()[::-1]
        return vals[order], vecs[:,order]

    if ax is None:
        ax = plt.gca()

    vals, vecs = eigsorted(cov)
    theta = np.degrees(np.arctan2(*vecs[:,0][::-1]))

    # Width and height are "full" widths, not radius
    width, height = 2 * nstd * np.sqrt(vals)
    ellip = Ellipse(xy=pos, width=width, height=height, angle=theta, **kwargs)

    ax.add_artist(ellip)
    return ellip

n_samples, n_features = 100, 2
mean0, mean1 = np.array([0, 0]), np.array([0, 2])
Cov = np.array([[1, .8],[.8, 1]])
np.random.seed(42)
X0 = np.random.multivariate_normal(mean0, Cov, n_samples)
X1 = np.random.multivariate_normal(mean1, Cov, n_samples)

x = np.array([2, 2])

plt.scatter(X0[:, 0], X0[:, 1], color='b')
plt.scatter(X1[:, 0], X1[:, 1], color='r')
plt.scatter(mean0[0], mean0[1], color='b', s=200, label="m0")
plt.scatter(mean1[0], mean1[1], color='r', s=200, label="m2")
plt.scatter(x[0], x[1], color='k', s=200, label="x")
plot_cov_ellipse(Cov, pos=mean0, facecolor='none', linewidth=2, edgecolor='b')
plot_cov_ellipse(Cov, pos=mean1, facecolor='none', linewidth=2, edgecolor='r')
plt.legend(loc='upper left')

#
d2_m0x = scipy.spatial.distance.euclidean(mean0, x)
d2_m0m2 = scipy.spatial.distance.euclidean(mean0, mean1)

Covi = scipy.linalg.inv(Cov)
dm_m0x = scipy.spatial.distance.mahalanobis(mean0, x, Covi)
dm_m0m2 = scipy.spatial.distance.mahalanobis(mean0, mean1, Covi)

print('Euclidean dist(m0, x)=%.2f > dist(m0, m2)=%.2f' % (d2_m0x, d2_m0m2))
print('Mahalanobis dist(m0, x)=%.2f < dist(m0, m2)=%.2f' % (dm_m0x, dm_m0m2))

'''
## Exercise

- Write a function `euclidean(a, b)` that compute the euclidean distance
- Write a function `mahalanobis(a, b, Covi)` that compute the euclidean
  distance, with the inverse of the covariance matrix. Use `scipy.linalg.inv(Cov)`
  to invert your matrix.
'''
def euclidian(a, b):
    return np.sqrt(np.sum((a - b) ** 2))

def mahalanobis(a, b, cov_inv):
    return np.sqrt(np.dot(np.dot((a - b), cov_inv),  (a - b).T))

assert mahalanobis(mean0, mean1, Covi) == dm_m0m2
assert euclidian(mean0, mean1)  == d2_m0m2

mahalanobis(X0, mean0, Covi)
X = X0
mean = mean0
covi= Covi

np.sqrt(np.dot(np.dot((X - mean), covi),  (X - mean).T))

def mahalanobis(X, mean, covi):
    """
    from scipy.spatial.distance import mahalanobis
    d2= np.array([mahalanobis(X[i], mean, covi) for i in range(X.shape[0])])
    np.all(mahalanobis(X, mean, covi) == d2)
    """
    return np.sqrt(np.sum(np.dot((X - mean), covi) *  (X - mean), axis=1))


import numpy as np
from timeit import default_timer as timer

def matrix_prod(A, B, display = False):
    """
    Computes the matrix product of two matrices using array slicing and vector operations.
    """
    if A.shape[1] != B.shape[0]:
        raise ValueError("Dimensions not compatible.")

    # Not allowed!?
    #matrix = A.dot(B)

    # Dotproduct of each A.row*B.clm
    matrix = np.array([[np.sum(A[i,:]*B[:,j]) for j in range(B.shape[1])]
                       for i in range(A.shape[0])])

    if display:
        print(matrix)

    return matrix

def matrix_prod2(A, B, display = False):
    """
    Computes the matrix product of two matrices using python loops.
    """
    if A.shape[1] != B.shape[0]:
        raise ValueError("Dimensions not compatible.")

    matrix = np.zeros((A.shape[0], B.shape[1]))
    for i in range(A.shape[0]):
        for j in range(B.shape[1]):
            for l in range(A.shape[1]):
                matrix[i,j] += A[i,l]*B[l,j]

    if display:
        print(matrix)

    return matrix

def main():
    # Test functions
    A = np.array([[1,2], [3,4]])
    B = np.array([[1,2,3], [4,5,6]])
    print(A.dot(B))
    matrix_prod(A, B, True)
    matrix_prod2(A, B, True)

    #---------------------------------------------------------
    print("### Test performance ###")
    # Generate large random matrices
    np.random.seed(0)
    A = np.random.randint(low=0, high=100, size=(100,500))
    B = np.random.randint(low=0, high=100, size=(500,200))

    # Method #0: numpy.dot()
    tic = timer()
    A.dot(B)
    toc = timer()
    print("Reference - np.dot(): ", toc - tic, "s")

    # Method #1: using slicing
    tic = timer()
    matrix_prod(A, B)
    toc = timer()
    print("Method #1 - Slicing: ", toc - tic, "s")

    # Method #2: using loops
    tic = timer()
    matrix_prod2(A, B)
    toc = timer()
    print("Method #2 - Looping: ", toc - tic, "s")

    """ Result (on my machine)
    Reference - np.dot():       0.0094825 s
    Method #1 - Slicing:        0.1890397 s
    Method #2 - Looping:       11.7723318 s

    Interpretation:
    Slicing > looping through elements. In-built function are best.
    """

if __name__ == "__main__":
    main()


"""Auxiliary functions for approximation lecture."""
import numpy as np

def spline_basis(x, degree, h, a):
    """Return spline."""
    nu = a + (degree - 1) * h

    if np.abs(x - nu) <= h:
        return 1 - (np.abs(x - nu)) / h
    else:
        return 0.0

    return nu

def get_uniform_nodes(n, a=-1, b=1):
    """Return uniform nodes."""
    return np.linspace(a, b, num=n)

def get_chebyshev_nodes(n, a=-1, b=1):
    """Return Chebyshev nodes."""
    nodes = np.tile(np.nan, n)

    for i in range(1, n + 1):
        nodes[i - 1] = (a + b) / 2 + ((b - a) / 2) * np.cos(((n - i + 0.5) / n) * np.pi)

    return nodes

def compute_interpolation_error(error):
    """Compute interpolation error."""
    return np.log10(np.linalg.norm(error, np.inf))

def compute_interpolation_error_df(df):
    """Compute interpolation error from a pandas.DataFrame."""
    return np.log10(np.linalg.norm(df["Error"], np.inf))


def taylorarctan(indicies,x):
    import math
    sum = 0
    lastIndex = len(indicies) - 1
    for n in range(indicies[0],indicies[lastIndex]):
        middle = ((2*n)+1)
        sum = sum + ((-1)**n)*((x**(middle)) / (middle))
    return sum

import math, time
import numpy as np

precision = 131072
indicies = list(range(precision))
numJobs = 16
Time = 0
nums = (-0.9999999999,0.9999999999,0.0000000001,0.7853194857,-0.82,0.4792378463)
joblist = np.array_split(indicies,numJobs)

for x in nums:
    results = 0
    start_time = time.time_ns()

    for job in joblist:
        results = results + taylorarctan(job,x)

    end_time = time.time_ns()
    Time = ((end_time - start_time)/1000000000)

    print("Arctan of %s" % x)
    print("Taylor Approximation at %s precision: %s" % (precision, results))
    realResults = np.arctan(x)
    print("Function Results: %s" % realResults)
    difference = realResults - results
    dPercentage = (1-abs(difference/realResults)) * 100
    print("Similarity: %s %% " % dPercentage)
    print("Time taken: %s" % Time)
    print("")


import numpy as np
from transforms import decay
import matplotlib.pyplot as plt

plt.rcParams["figure.figsize"]=(8, 8)
plt.rcParams['figure.dpi'] = 80

BETA_INIT = 1
T_MIN = 0
T_MAX = 100

# Plot a single decay trajectory
rho = 0.9
beta = BETA_INIT

Xs = list(range(T_MIN, T_MAX))
Ys = []

for t in range(T_MIN, T_MAX):
    beta = decay(rho, BETA_INIT, t / (T_MAX - 1))
    Ys.append(beta)

plt.plot(Xs, Ys)
plt.title('Rho = {}'.format(rho))
plt.show()

# Plot mutltiple trajectory shapesd (values for rho)
N = 20
RHOs = np.linspace(0, 1, N)

Xs = np.array((range(T_MIN, T_MAX))) / T_MAX
Ys = []
plt.gca().set_prop_cycle(plt.cycler('color', plt.cm.turbo(np.linspace(0, 1, len(RHOs)))))

# Generate values
for RHO in RHOs:

    Y = []
    beta = BETA_INIT

    for t in range(T_MIN, T_MAX):
        beta = decay(RHO, BETA_INIT, t / (T_MAX - 1))
        Y.append(beta)

    Ys.append(Y)

# Plot
for i, Y in enumerate(Ys):
    plt.plot(Xs, Y, label='rho = {}'.format(round(RHOs[i], 2)))

plt.legend(loc='center right')
plt.ylabel('Parameter value')
plt.xlabel('Progress (t/T)')
plt.title('Decay algorithm Rosokha & Younge (2019) for initial value of {} and rho in [0,1]'.format(BETA_INIT))
plt.show()