#!/usr/bin/env python
'''Module for integer sequence generation and related conditional tests.'''

import math
import itertools as it
import random
import collections
import numpy as np

## SEQUENCE GENERATORS
def prime_stream():
    '''Yield the next prime number starting with 2.'''
    start = 13
    c = 2*3*5*7*11
    yield 2; yield 3; yield 5; yield 7; yield 11

    sieve = {}
    mod = frozenset(i for i in range(c) if math.gcd(i, c) == 1)
    skip = tuple([1 if n % c in mod else 0 for n in range(start, start+c, 2)])

    for n in it.compress(it.count(start, 2), it.cycle(skip)):
        if n not in sieve:
            sieve[n*n] = n; yield n
        else:
            prime = sieve.pop(n)
            m = n + 2*prime
            while m in sieve or m % c not in mod:
                m += 2*prime
            sieve[m] = prime

def composite_stream():
    '''Yield the next composite number starting with 1.'''
    primes = prime_stream()
    next_prime = next(primes)

    for n in it.count(1):
        if n != next_prime:
            yield n
        else:
            next_prime = next(primes)

def polygonal_stream(s):
    '''Yield the next s-gonal number starting with 1.

    Parameters
    ----------
    s : int
        s is the number of sides in the polygon.

    Yields
    ------
    int
        Yield P(s, 1), P(s, 2), P(s, 3)... and so on.
    '''
    for n in it.count(1):
        yield int(((s - 2)*n*n - (s - 4)*n) / 2)

def fibonacci_stream(start = (0, 1)):
    '''Yield the next number in the Fibonacci sequence.

    Parameters
    ----------
    start : tuple or list of intengers, optional
        Integers to initialize the Fibonacci sequence. By changing
        start, other generalizations of the Fibonacci numbers can be
        generated. For instance, with start = (0, 0, 1), the Tribonacci
        numbers will be generated. The default is (0, 1).

    Yields
    ------
    int
        After yielding the initial integers in start, the next value is
        the sum of the preceding values. The length of start determines
        how many preceding values will be summed to generate the next
        value.
    '''
    for n in start:
        yield n

    last = collections.deque(start, len(start))

    while True:
        next_n = sum(last)
        yield next_n
        last.append(next_n)

def negafibonacci_stream(start = (0, 1)):
    '''Yield the next number in the negaFibonacci sequence.

    Parameters
    ----------
    start : tuple or list of integers, optional
        Integers to initialize the negaFibonacci sequence. By changing
        start, other generalizations of the negaFibonacci numbers can
        be generated. For instance, with start = (0, 0, 1), the
        negaTribonacci numbers will be generated. The default is
        (0, 1).

    Yields
    ------
    int
        After yielding F(0), the sequence will proceed into the
        negative index with F(-1), F(-2), and so on. The length of
        start determines how many preceding values will be subtracted
        to generate the next value.
    '''
    yield start[0]

    last = collections.deque(start, len(start))

    while True:
        next_n = last[-1] + last[-1] - sum(last)
        yield next_n
        last.appendleft(next_n)

def lucas_stream(P = 2, Q = -1, start = (0, 1)):
    '''Yield the next number in the (P,−Q)-Lucas sequence.

    Parameters
    ----------
    P : int, optional
        Fixed integer coefficient defining the sequence. By changing P,
        other generalizations of the Lucas sequence can be generated.
        For instance, the 3-Fibonacci sequence can be generated with
        P = 3. The default is 2.
    Q : int, optional
        Fixed integer coefficient defining the sequence. By changing Q,
        other generalizations of the Lucas sequence can be generated.
        For instance, the Jacobsthal numbers can be generated with
        P = 1 and Q = -2. The default is -1.
    start : tuple or list of integers, optional
        Integers to initialize the Lucas sequence. The length of start
        must equal two. By changing start, other generalizations of the
        Lucas sequence can be generated. For instance, Pell–Lucas
        numbers can be generated with start = (2, 2). The default is (0, 1).

    Yields
    ------
    int
        Yields U(0), U(1), ... U(n) where U(n) is defined by

        U(0) = start[0]
        U(1) = start[1]
        U(n + 2) = P*U(n + 1) − Q*U(n)

        By default, the Pell numbers will be generated.
    '''
    for n in start:
        yield n

    n1, n2 = start
    while True:
        next_n = P*n2 - Q*n1
        yield next_n
        n1, n2 = n2, next_n

## TESTS
def is_prime(n):
    '''Test the primality of n by checking potential prime factors.

    Parameters
    ----------
    n : int
        n is the number to be tested.

    Returns
    -------
    bool
        Return True if n is a prime number and False otherwise.

    '''
    if n == 2:
        return True

    if n < 2 or n % 1 != 0:
        return False

    stop = n ** 0.5
    for prime in prime_stream():
        if n % prime == 0:
            return False
        if stop < prime:
            break
    return True

def miller_rabin(n, k = 8):
    '''Perform the Miller-Rabin Primality Test on n.

    Parameters
    ----------
    n : int
        n is the number to be probabilistically tested.
    k : int, optional
        k determines the test's accuracy. It describes the number of
        iterations of the test to be performed. The default is 8.

    Returns
    -------
    bool
        Return True if n passes the Miller-Rabin primality test and
        False otherwise.

    '''
    if n < 2 or n % 1 != 0:
        return False

    if n in (2, 3):
        return True

    r = 0
    d = n - 1
    while d % 2 == 0:
        r += 1
        d //= 2

    for _ in range(k):
        a = random.randint(2, n - 2)
        x = pow(a, d, n)

        if x == 1 or x == n - 1: continue

        cont = False
        for _ in range(r - 1):
            x = pow(x, 2, n)
            if x == n - 1: cont = True; break

        if cont: continue

        return False

    return True

def is_composite(n):
    '''Test if n is a composite by checking potential prime factors.

    Parameters
    ----------
    n : int
        n is the number to be tested.

    Returns
    -------
    bool
        Return True if n is a composite number and False otherwise.
    '''
    if n == 1:
        return True

    if n < 1 or n % 1 != 0 or n == 2:
        return False

    stop = n ** 0.5
    for prime in prime_stream():
        if n % prime == 0:
            return True
        if stop < prime:
            break
    return False

def is_polygonal(n, s):
    '''Test if n is an s-gonal number.

    Parameters
    ----------
    n : int
        n is the number to be tested.
    s : int
        s is the number of sides in the polygon.

    Returns
    -------
    bool
        Return True if n is an s-gonal number and False otherwise.

    '''
    numerator = (8*n*(s - 2) + (s - 4)**2)**0.5 + s - 4
    denominator = 2*(s - 2)
    return (numerator / denominator) % 1 == 0

def is_fibonacci(n, start = (0, 1)):
    '''Test if n is a Fibonacci number.

    Parameters
    ----------
    n : int
        n is the number to be tested.
    start : tuple or list of intengers, optional
        Integers to initialize the Fibonacci sequence. By changing
        start, other generalizations of the Fibonacci numbers can be
        generated. For instance, with start = (0, 0, 1), the Tribonacci
        numbers will be generated. The default is (0, 1).

    Returns
    -------
    bool
        Return True if n is a positive Fibonacci number and False
        otherwise.

    '''
    if n in start:
        return True

    if n < max(start):
        return False

    if n <= nth_fibonacci(1475) and tuple(start) == (0, 1):
        phi = 0.5 + 0.5 * math.sqrt(5.0)
        a = phi * n
        return n == 0 or abs(round(a) - a) < 1.0 / n

    for fib in fibonacci_stream(start=start):
        if n == fib:
            return True
        if fib > n:
            return False

def is_lucas(n, P = 2, Q = -1, start = (0, 1)):
    '''Test if n is within the (P,−Q)-Lucas sequence.

    Parameters
    ----------
    U(n + 2) = P*U(n + 1) − Q*U(n)

    P : int, optional
        Fixed integer coefficient defining the sequence. By changing P,
        other generalizations of the Lucas sequence can be generated.
        For instance, the 3-Fibonacci sequence can be generated with
        P = 3. The default is 2.
    Q : int, optional
        Fixed integer coefficient defining the sequence. By changing Q,
        other generalizations of the Lucas sequence can be generated.
        For instance, the Jacobsthal numbers can be generated with
        P = 1 and Q = -2. The default is -1.
    start : tuple or list of integers, optional
        Integers to initialize the Lucas sequence. The length of start
        must equal two. By changing start, other generalizations of the
        Lucas sequence can be generated. For instance, Pell–Lucas
        numbers can be generated with start = (2, 2). The default is
        (0, 1).

    Returns
    -------
    bool
        Return True if n is within the within the (P,−Q)-Lucas sequence
        and False otherwise.

    '''
    for lucas in lucas_stream(P = P, Q = Q, start = start):
        if n == lucas:
            return True
        if lucas > n:
            return False

## INDEX
def nth_fibonacci(n, start = (0, 1)):
    '''Return the Fibonacci number at index n or F(n).

    Parameters
    ----------
    n : int,
        Index of the Fibonacci number. Negative indexes are supported.
    start : tuple or list of intengers, optional
        Integers to initialize the Fibonacci sequence. By changing
        start, other generalizations of the Fibonacci numbers can be
        found. For instance, with start = (0, 0, 1), the Tribonacci
        number at index n will be returned. The default is (0, 1).

    Returns
    -------
    int
        Return F(n).

    '''
    if 0 <= n < len(start):
        return start[n]

    elif n > 0:
        adjust = len(start) - 1

        a_str = []
        for i in range(0, len(start)):
            if i == 0:
                a_str.append(' '.join('1' for j in start))
            else:
                a_lst = ['1' if i-1 == j else '0' for j in range(len(start))]
                a_str.append(' '.join(a_lst))

        a = np.matrix('; '.join(a_str), dtype=np.object)
        b_lst = [str(i) for i in start[::-1]]
        b = np.matrix('; '.join(b_lst),dtype=np.object)

        return np.matmul(np.linalg.matrix_power(a, n - adjust), b).item(0)

    elif tuple(start) == (0, 1):
        if n % 2 == 0:
            c = -1
        else:
            c = 1

        return c*nth_fibonacci(abs(n), start=start)

    elif tuple(start) == (2, 1):
        if n % 2 == 1:
            c = -1
        else:
            c = 1
        return c*nth_fibonacci(abs(n), start=start)

    else:
        for i, f in enumerate(negafibonacci_stream(start=start)):
            if i == abs(n):
                return f


"""
  Name     : c9_19_Treynor_ratio.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""
import numpy as np
import pandas as pd
import scipy as sp
from scipy.optimize import fmin
from matplotlib.finance import quotes_historical_yahoo_ochl as getData
#
# Step 1: input area
ticker=('IBM','WMT','C')     # tickers
begdate=(1990,1,1)           # beginning date
enddate=(2012,12,31)         # ending date
rf=0.0003                    # annual risk-free rate
betaGiven=(0.8,0.4,0.3)      # given beta's
#
# Step 2: define a few functions
# function 1:
def ret_annual(ticker,begdate,enddte):
    x=getData(ticker,begdate,enddate,asobject=True,adjusted=True)
    logret =sp.log(x.aclose[1:]/x.aclose[:-1])
    date=[]
    d0=x.date
    for i in range(0,sp.size(logret)):
        date.append(d0[i].strftime("%Y"))
    y=pd.DataFrame(logret,date,columns=[ticker])
    return sp.exp(y.groupby(y.index).sum())-1
# function 2: estimate portfolio beta
def portfolioBeta(betaGiven,w):
    #print("betaGiven=",betaGiven,"w=",w)
    return sp.dot(betaGiven,w)
#
# function 3: estimate Treynor
def treynor(R,w):
    betaP=portfolioBeta(betaGiven,w)
    mean_return=sp.mean(R,axis=0)
    ret = sp.array(mean_return)
    return (sp.dot(w,ret) - rf)/betaP
#
# function 4: for given n-1 weights, return a negative sharpe ratio
def negative_treynor_n_minus_1_stock(w):
    w2=sp.append(w,1-sum(w))
    return -treynor(R,w2)        # using a return matrix here!!!!!!
#
# Step 3: generate a return matrix (annul return)
n=len(ticker)              # number of stocks
x2=ret_annual(ticker[0],begdate,enddate)
for i in range(1,n):
    x_=ret_annual(ticker[i],begdate,enddate)
    x2=pd.merge(x2,x_,left_index=True,right_index=True)
# using scipy array format
R = sp.array(x2)
print('Efficient porfolio (Treynor ratio) :ticker used')
print(ticker)
print('Treynor ratio for an equal-weighted portfolio')
equal_w=sp.ones(n, dtype=float) * 1.0 /n
print(equal_w)
print(treynor(R,equal_w))
# for n stocks, we could only choose n-1 weights
w0= sp.ones(n-1, dtype=float) * 1.0 /n
w1 = fmin(negative_treynor_n_minus_1_stock,w0)
final_w = sp.append(w1, 1 - sum(w1))
final_treynor = treynor(R,final_w)
print ('Optimal weights are ')
print (final_w)
print ('final Treynor ratio is ')
print(final_treynor)


'''
@Description: code
@Author: MiCi
@Date: 2020-03-16 21:31:24
@LastEditTime: 2020-03-18 23:36:29
@LastEditors: MiCi
'''

import numpy as np

class Basic1(object):

    def __init__(self):
        return

    def basic_use(self):
        # Numpy主要对象是同种元素的多维数组，维度叫做轴（axes），轴的个数叫做秩（rank）
        # 数组类：ndarray，属性： ndim、shape、size、dtype、itemsize、data
        testArray = np.arange(20).reshape(4, 5)
        print(testArray)
        print(type(testArray))

        # 秩
        print(testArray.ndim)
        # 维度，n排m列（n,2）
        print(testArray.shape)
        # 元素的总个数
        print(testArray.size)
        # 元素总类型对象
        print(testArray.dtype)
        # 元素字节大小
        print(testArray.itemsize)

        # 创建数组，就是用list以及元祖
        testArray_1 = np.array([1, 2, 3])
        print(testArray_1.dtype)
        testArray_2 = np.array([1.2, 2, 3.1])
        print(testArray_2.dtype)
        testArray_3 = np.array([(1.2, 2, 3), (4, 5, 6)])
        print(testArray_3.dtype)

        # 占位符创建数组函数，ones、empty、zeros，默认类型均为float64
        testArray_4 = np.zeros((3, 4))
        print(testArray_4)
        testArray_5 = np.ones((2, 3, 4), dtype='int32')
        print(testArray_5)
        testArray_6 = np.empty((2, 3))
        print(testArray_6)

        # 利用arange返回数组
        testArray_7 = np.arange(10, 20, 2)
        print(testArray_7)
        testArray_8 = np.arange(0, 2, 0.3)
        print(testArray_8)
        # 还有其他诸多函数：rand等

        # np数组打印布局：
        # 最后的轴从左到右打印
        # 次后的轴从顶向下打印
        # 剩下的轴从顶向下打印，每个切片通过一个空行与下一个隔开
        # 一维数组被打印成行，二维数组成矩阵，三维数组成矩阵列表。
        print(np.arange(24).reshape(2, 3, 4))
        print(np.arange(10000).reshape(100, 100))

        return

    def basic_use2(self):
        # 按元素进行运算
        a = np.array([20, 30, 40, 50])
        b = np.arange(4)
        print(a)
        print(b)
        print(a-b)
        print(b**2)
        print(10*np.sin(a))
        print(a < 35)

        # *号按元素计算，矩阵计算用dot函数
        a = np.array([[1, 1],
                      [0, 1]])
        b = np.array([[2, 0],
                      [3, 4]])
        print(a*b)
        print(np.dot(a, b))

        # 运算不同类型数组，结果数组使用更准确类型
        a = np.ones(3, dtype='int32')
        b = np.linspace(0, np.pi, 3)
        print(b.dtype.name)
        print((a+b).dtype.name)

        # 数组求和、最大值、最小值
        a = np.random.random((2, 3))
        print(a)
        print(a.sum())
        print(a.max())
        print(a.min())

        # 指定axis运算位置
        a = np.arange(12).reshape(3, 4)
        print(a)
        print(a.sum(axis=0))
        print(a.min(axis=1))
        print(a.max(axis=1))
        # 累加
        print(a.cumsum(axis=1))
        return

if __name__ == '__main__':
    print('Start Learn Numpy')
    print('============================\n')
    example = Basic1()
    # 最基本信息
    # example.basic_use()
    # 基本运算
    example.basic_use2()


import math

import sympy
from sympy.core.numbers import igcdex

def safe_div(x: int, y: int):
    """
    Computes x / y and fails if x is not divisible by y.
    """
    assert isinstance(x, int) and isinstance(y, int)
    assert y != 0
    assert x % y == 0, f'{x} is not divisible by {y}.'
    return x // y

def div_ceil(x, y):
    assert isinstance(x, int) and isinstance(y, int)
    return -((-x) // y)

def div_mod(n, m, p):
    """
    Finds a nonnegative integer x < p such that (m * x) % p == n.
    """
    a, b, c = igcdex(m, p)
    assert c == 1
    return (n * a) % p

def next_power_of_2(x: int):
    """
    Returns the smallest power of two which is >= x.
    """
    assert isinstance(x, int) and x > 0
    res = 2 ** (x - 1).bit_length()
    assert x <= res < 2 * x, f'{x}, {res}'
    return res

def is_power_of_2(x):
    return isinstance(x, int) and x > 0 and x & (x - 1) == 0

def is_quad_residue(n, p):
    """
    Returns True if n is a quadratic residue mod p.
    """
    return sympy.ntheory.residue_ntheory.is_quad_residue(n, p)

def safe_log2(x: int):
    """
    Computes log2(x) where x is a power of 2. This function fails if x is not a power of 2.
    """
    assert x > 0
    res = int(math.log(x, 2))
    assert 2 ** res == x
    return res

def sqrt(n, p):
    """
    Finds the minimum positive integer m such that (m*m) % p == n.
    """
    return min(sympy.ntheory.residue_ntheory.sqrt_mod(n, p, all_roots=True))


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams

def plot_erros(h, error, error0, figname):
  x = np.log(h)
  y1 = np.log(error)
  y2 = np.log(error0)

  #fitting data into straight line:
  m1, b1 = np.polyfit(x, y1, 1)
  m2, b2 = np.polyfit(x, y2, 1) #error0

  xf = np.linspace(np.min(x), np.max(x), 200)
  y1f =  m1*xf + b1
  y2f =  m2*xf + b2  #error0

  # seting-up Figures parameters:
  rcParams.update({'figure.autolayout': True})
  rcParams["figure.figsize"] = [4.5, 5.5]
  rcParams['text.usetex'] = True

  plt.figure()
  plt.plot(x, y1, 'bo')
  plt.plot(xf, y1f, '--b', label='$||error||_E$')
  plt.plot(x, y2, 'ro')
  plt.plot(xf, y2f, '--r', label='$||error||_0$')
  plt.xlabel('log h')
  plt.ylabel('log error')
  plt.legend()
  plt.savefig(figname)
  #plt.show()

hq = np.array([0.5, 0.25, 0.125, 0.0625, 0.0625/2.])
ht = np.array([1.0, 0.5, 0.25, 0.125, 0.0625])

## Triangle element:
#error order1:
error = np.array([7.84384, 5.66874, 3.43309, 1.80281, 0.914325])
error0 = np.array([0.97281, 0.492347, 0.177818, 0.0491276, 0.0126559])
plot_erros(ht, error, error0, "triangle-order1.eps")

#error order 2:
error = np.array([5.91471, 2.77374, 0.777577, 0.204498, 0.0519473])
error0 = np.array([0.444053, 0.129974, 0.0187519, 0.00247243, 0.000314414])
plot_erros(ht, error, error0, "triangle-order2.eps")

## Quad element:
# error order 1:
error = np.array([7.72681, 4.29193, 2.13392, 1.06749, 0.533907 ])
error0 = np.array([0.872926, 0.278562, 0.0699183, 0.017506, 0.00437865])
plot_erros(hq, error, error0, "quad-order1.eps")

#error order 2:
error = np.array([4.29717, 2.33426, 1.92219, 1.81116, 2.86259])
error0 = np.array([0.310539, 0.138998, 0.0904695, 0.0712541, 0.344019])
plot_erros(hq, error, error0, "quad-order2.eps")

## One-D problem:
#order 1:
h = np.array([1.0, 0.5, 0.25, 0.125, 0.0625])
error = np.array([9.52756, 4.72345, 2.35488, 1.17653, 0.588146])
error0 = np.array([2.81863, 0.733864, 0.185345, 0.0464545, 0.011621])
plot_erros(h, error, error0, "oneD-order1.eps")

#order 2:
error = np.array([1.6458, 0.415373, 0.104096, 0.0260399, 0.00651096])
error0 = np.array([0.209737, 0.0267241, 0.00335691, 0.00042013, 5.25324e-05 ])
plot_erros(h, error, error0, "oneD-order2.eps")


# from https://gist.github.com/Nikolay-Lysenko/06769d701c1d9c9acb9a66f2f9d7a6c7

import numpy as np

def xgb_quantile_eval(preds, dmatrix, quantile=0.5):
    """
    Customized evaluational metric that equals
    to quantile regression loss (also known as
    pinball loss).
    Quantile regression is regression that
    estimates a specified quantile of target's
    distribution conditional on given features.
    @type preds: numpy.ndarray
    @type dmatrix: xgboost.DMatrix
    @type quantile: float
    @rtype: float
    """
    labels = dmatrix.get_label()
    return ('q{}_loss'.format(quantile),
            np.nanmean((preds >= labels) * (1 - quantile) * (preds - labels) +
                       (preds < labels) * quantile * (labels - preds)))

def xgb_quantile_obj(preds, dmatrix, quantile=0.5):
    """
    Computes first-order derivative of quantile
    regression loss and a non-degenerate
    substitute for second-order derivative.
    Substitute is returned instead of zeros,
    because XGBoost requires non-zero
    second-order derivatives. See this page:
    https://github.com/dmlc/xgboost/issues/1825
    to see why it is possible to use this trick.
    However, be sure that hyperparameter named
    `max_delta_step` is small enough to satisfy:
    ```0.5 * max_delta_step <=
       min(quantile, 1 - quantile)```.
    @type preds: numpy.ndarray
    @type dmatrix: xgboost.DMatrix
    @type quantile: float
    @rtype: tuple(numpy.ndarray)
    """
    try:
        assert 0 <= quantile <= 1
    except AssertionError:
        raise ValueError("Quantile value must be float between 0 and 1.")

    labels = dmatrix.get_label()
    errors = preds - labels

    left_mask = errors <= 0
    right_mask = errors > 0

    grad = -quantile * left_mask + (1 - quantile) * right_mask
    hess = np.ones_like(preds)

    return grad, hess

# Example of usage:
# bst = xgb.train(hyperparams, train, num_rounds,
#                 obj=xgb_quantile_obj, feval=xgb_quantile_eval)


import numpy as np
from scipy.optimize import minimize

class RegressionError(Exception):
    pass

class LinearRegression:

    def __init__(self, solver='bfgs'):
        self.solver = solver
        self._coef = None

    def fit(self, X, y):
        # initialise the coefficient vector theta
        m, n = X.shape
        theta = np.random.random(n)

        # find the values of theta that minimise the cost function
        self._coef = minimize(
            self._least_squares_cost_function, theta, args=(X, y),
            jac=self._least_squares_gradient, method=self.solver).x

    def predict(self, X):
        if self._coef is None:
            raise RegressionError
        return X @ self._coef

    @staticmethod
    def _least_squares_cost_function(theta, X, y):
        """Ordinary least squares Linear Regression.

        Args:
            theta [numpy.array]: A vector of size (m, 1) containing the
              parameter values to optimize.
            X [numpy.array]: A matrix of size (m, n) containing the trainig
              data. X(i, j) is the i'th coordinate of the j'th example.
            y [numpy.array]: A vector of size (m, 1) with the value of the
              independed variable. y(j) is the target for example j.
        Returns:
            The value [float] of the objective function as a function of
            theta.
        """
        return 1/2 * (X @ theta - y).T @ (X @ theta - y)

    @staticmethod
    def _least_squares_gradient(theta, X, y):
        """Gradient (first derivative) of a linear regression's loss
        function.

        Args:
            theta [numpy.array]: A vector of size (m, 1) containing the
              parameter values to optimize.
            X [numpy.array]: A matrix of size (m, n) containing the trainig
              data. X(i, j) is the i'th coordinate of the j'th example.
            y [numpy.array]: A vector of size (m, 1) with the value of the
              independed variable. y(j) is the target for example j.
        Returns:
            The gradient (aka derivative or jacobian) as a function of theta.
            The gradient is a [numpy.array] of size (m, 1).
        """
        return (X.T @ X @ theta) - (X.T @ y)


#source: https://people.sc.fsu.edu/~jburkardt/py_src/lorenz_ode/lorenz_ode.html
#scholar: http://journals.ametsoc.org/doi/pdf/10.1175/1520-0469(1963)020%3C0130:DNF%3E2.0.CO;2

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

def lorenz_rhs ( t, m, xyz ):

	omega = 10
	b = 8/3

	rho = omega*(omega+b+3)*np.power((omega-b-1),-1)

	dxdt = np.zeros ( 3 )

	dxdt[0] = omega * ( xyz[1] - xyz[0] )
	dxdt[1] = xyz[0] * ( rho - xyz[2] ) - xyz[1]
	dxdt[2] = xyz[0] * xyz[1] - b * xyz[2]

	return dxdt

def rk4vec ( t0, m, u0, dt, f ):

	#*****************************************************************************80
	#
	## RK4VEC takes one Runge-Kutta step for a vector ODE.
	#
	#  Licensing:
	#
	#    This code is distributed under the GNU LGPL license.
	#
	#  Modified:
	#
	#    24 May 2016
	#
	#  Author:
	#
	#    John Burkardt
	#
	#  Parameters:
	#
	#    Input, real T0, the current time.
	#
	#    Input, integer M, the spatial dimension.
	#
	#    Input, real U0(M), the solution estimate at the current time.
	#
	#    Input, real DT, the time step.
	#
	#    Input, function uprime = F ( t, m, u  )
	#    which evaluates the derivative UPRIME(1:M) given the time T and
	#    solution vector U(1:M).
	#
	#    Output, real U(M), the fourth-order Runge-Kutta solution
	#    estimate at time T0+DT.
	#
	#  Get four sample values of the derivative.
	#
	f0 = f ( t0, m, u0 )

	t1 = t0 + dt / 2.0
	u1 = u0 + dt * f0 / 2.0
	f1 = f ( t1, m, u1 )

	t2 = t0 + dt / 2.0
	u2 = u0 + dt * f1 / 2.0
	f2 = f ( t2, m, u2 )

	t3 = t0 + dt
	u3 = u0 + dt * f2
	f3 = f ( t1, m, u1 )
	#
	#  Combine them to estimate the solution U at time T1.
	#
	u = u0 + dt * ( f0 + 2.0 * f1 + 2.0 * f2 + f3 ) / 6.0

	return u

def main():

	omega = 10
	a_root = .5
	b = 8/3

	r = omega*(omega+b+3)*np.power((omega-b-1),-1)
	print(r)
	roots = np.roots([1.,(omega+b+1),(r+omega)*b,2*omega*b*(r-1)])
	print(roots)

	n = 100000
	t_final = 40.
	dt = t_final/n

	t = np.linspace ( 0.0, t_final, n + 1 )

	x = np.zeros ( n + 1 )
	y = np.zeros ( n + 1 )
	z = np.zeros ( n + 1 )

	#x[0] = 8.0
	#y[0] = 1.0
	#z[0] = 1.0
	x[0] = roots[0]
	y[0] = roots[1]
	z[0] = roots[2]

	for j in range ( 0, n ):

		xyz = np.array ( [ x[j], y[j], z[j] ] )
		xyz = rk4vec ( t[j], 3, xyz, dt, lorenz_rhs )

		x[j+1] = xyz[0]
		y[j+1] = xyz[1]
		z[j+1] = xyz[2]

	plt.plot(t,x)
	plt.plot(t,y)
	plt.plot(t,z)
	plt.legend(['x','y','z'])
	plt.show()

	fig = plt.figure ( )
	ax = fig.gca ( projection = '3d' )
	ax.plot ( x, y, z, linewidth = 2, color = 'b' )
	ax.grid ( True )
	ax.set_xlabel ( '<--- X(T) --->' )
	ax.set_ylabel ( '<--- Y(T) --->' )
	ax.set_zlabel ( '<--- Z(T) --->' )
	plt.show()

if __name__ == '__main__':
	millis = int(round(time.time() * 1000))
	main()
	millis2 = int(round(time.time() * 1000))
	print('sim time:'+str(millis2 - millis))


import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
from matplotlib.colors import Normalize

# import threading
# from multiprocessing import Process

def local_hist_equ(in_img, m_size):
    """
    Implement the local histogram equalization to the input images Q3_3.tif
    """

    row, col = in_img.shape
    out_img = np.zeros(in_img.shape, int)

    L = 2 ** 8
    bins = range(L + 1)

    # def local():
    #     # def local(in_img, out_img, m_size, i, j):
    #     _local_img = in_img[i:i + m_size, j:j + m_size]
    #     _local_hist, _ = np.histogram(_local_img.flat, bins=bins, density=True)
    #     _s = np.array([(L - 1) * _local_hist[:_k + 1].sum() for _k in range(L)])
    #     out_img[i:i + m_size, j:j + m_size] = np.array([_s[r] for r in _local_img], int).reshape([m_size] * 2)
    #     pass

    for i in list(range(0, row - m_size, m_size)) + [row - m_size - 1]:
        for j in list(range(0, col - m_size, m_size)) + [col - m_size - 1]:
            local_img = in_img[i:i + m_size, j:j + m_size]
            local_hist, _ = np.histogram(local_img.flat, bins=bins, density=True)

            s = np.array([(L - 1) * local_hist[:k + 1].sum() for k in range(L)])

            # s = np.zeros((1, L))
            # for k in range(L):
            #     s[0, k] = (L - 1) * local_hist[:k + 1].sum()

            out_img[i:i + m_size, j:j + m_size] = np.array([s[r] for r in local_img], int).reshape([m_size] * 2)

            # tmp = np.zeros([1, m_size ** 2])
            # i = 0
            # for r in local_img:
            #     tmp = s[0, r]
            #     i += 1
            # out_img[i:i + m_size, j:j + m_size] = tmp
            # Process(target=local).start()

    norm = Normalize(vmin=0, vmax=255)
    plt.subplot(121)
    plt.imshow(raw_img, cmap='gray', norm=norm)
    plt.title("Raw")

    plt.subplot(122)
    plt.imshow(out_img, cmap='gray', norm=norm)
    plt.title("Equalized")
    # plt.savefig(file_name + "_comparison.png")
    plt.show()

    return out_img

raw_img = cv.imread("Q3_3.tif", cv.IMREAD_GRAYSCALE)
result_3 = local_hist_equ(raw_img, 3)
# result_5 = local_hist_equ(raw_img, 5)
# result_10 = local_hist_equ(raw_img, 10)
# result_20 = local_hist_equ(raw_img, 20)
#
# norm = Normalize(vmin=0, vmax=255)
#
# plt.subplot(221)
# plt.imshow(result_3, cmap='gray', norm=norm)
# plt.title("m_size = 3")
# plt.xticks([])
#
# plt.subplot(222)
# plt.imshow(result_5, cmap='gray', norm=norm)
# plt.title("m_size = 5")
# plt.xticks([])
#
# plt.subplot(223)
# plt.imshow(result_10, cmap='gray', norm=norm)
# plt.title("m_size = 10")
#
# plt.subplot(224)
# plt.imshow(result_20, cmap='gray', norm=norm)
# plt.title("m_size = 20")
# plt.show()


# Reference Book: Python Data Science Handbook (page:(70-77))
# Date(13 April, 2019) Day-3, Time = 3:25 PM

# This section covers the use of Boolean masks to examine and manipulate values
# within NumPy arrays.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn; seaborn.set() #set plot style

# use Pandas to extract rainfall inches as a NumPy array
rainfall = pd.read_csv('/media/nahid/New Volume/GitHub/Numpy/Seattle2014.csv')['PRCP'].values
# print(rainfall)

inches = rainfall / 254 #1/10mm -> inches
print(inches.shape) #(365,)

# fig = plt.figure()
# plt.hist(inches, 40)
# print(plt.show())
# fig.savefig('rainfallHistogram.png')

'''
This histogram gives us a general idea of what the data looks like: despite its reputa‐
tion, the vast majority of days in Seattle saw near zero measured rainfall in 2014. But
this doesn’t do a good job of conveying some information we’d like to see: for exam‐
ple, how many rainy days were there in the year? What is the average precipitation on
those rainy days? How many days were there with more than half an inch of rain?
Digging into the data
One approach to this would be to answer these questions by hand: loop through the
data, incrementing a counter each time we see values in some desired range. For rea‐
sons discussed throughout this chapter, such an approach is very inefficient, both
from the standpoint of time writing code and time computing the result. We saw in
“Computation on NumPy Arrays: Universal Functions” on page 50 that NumPy’s
ufuncs can be used in place of loops to do fast element-wise arithmetic operations on
arrays; in the same way, we can use other ufuncs to do element-wise comparisons over
arrays, and we can then manipulate the results to answer the questions we have. We’ll
leave the data aside for right now, and discuss some general tools in NumPy to use
masking to quickly answer these types of questions.
'''

a = np.array([1,2,3,4,5])
print(a < 3)  # [ True  True False False False]
# check this apply all of others relational operator
# like
print(a != 3)  # [ True  True False  True  True]

# It is also possible to do an element-by-element comparison of two arrays, and to
# include compound expressions:

print((2 * a) == (a ** 2))  # [False  True False False False]

'''
As in the case of arithmetic operators, the comparison operators are implemented as
ufuncs in NumPy; for example, when you write x < 3 , internally NumPy uses
np.less(x, 3) . A summary of the comparison operators and their equivalent ufunc
is shown here:

Operator  Equivalent ufunc
==         np.equal
!=         np.not_equal
<          np.less
<=         np.less_equal
>          np.greater
>=         np.greater_equal
'''

# Just as in the case of arithmetic ufuncs, these will work on arrays of any size and
# shape. Here is a two-dimensional example:

rng = np.random.RandomState(0)
x = rng.randint(10, size=(3,4))
print(x)
print(x < 5)
'''
[[5 0 3 3]
 [7 9 3 5]
 [2 4 7 6]]
[[False  True  True  True]
 [False False  True False]
 [ True  True False False]]
'''
print(np.count_nonzero(x < 6)) # 8
print(np.sum(x < 6)) # 8

# how many values less than 6 in each row?
print(np.sum(x < 6, axis=1))  # [4 2 2]

# If we’re interested in quickly checking whether any or all the values are true, we can
# use(you guessed it) np.any() or np.all():

print(np.any(x < 8))  #True
print(np.any(x < 0))  #False
print(np.all(x < 10)) #True
print(np.all(x == 6)) # False

# np.all() and np.any() can be used along particular axes as well. For example:
print(np.all(x < 8, axis=1))  # [ True False  True]

# Here all the elements in the first and third rows are less than 8, while this is not the
# case for the second row.

#Boolean operators
print(np.sum((inches > .5) & (inches < 1)))  # 29
#So we see that there are 29 days with rainfall between 0.5 and 1.0 inches.

#Using the equivalence of A AND B and NOT (A OR B)
print(np.sum(~((inches <= 0.5) | (inches >= 1))))  # 29
'''
The following table summarizes the bitwise Boolean operators and their equivalent
ufuncs:
Operator   Equivalent ufunc
&          np.bitwise_and
|          np.bitwise_or
^          np.bitwise_xor
~          np.bitwise_not
'''
print('Number of days without rain        :',np.sum(inches == 0))
print('Number of days with rain           :',np.sum(inches != 0))
print('Days with more than .5 inches      :',np.sum(inches > 0.5))

'''
Number of days without rain        : 215
Number of days with rain           : 150
Days with more than .5 inches      : 37
'''
print(x[x < 5])  # [0 3 3 3 2 4]
# construct a mask of all rainy days
rainy = (inches > 0)
# construct a mask of all summer days (June 21st is the 172nd day)
summer = (np.arange(365) - 172 < 90) & (np.arange(365) - 172 > 0)
print("Median precip on rainy days in 2014 (inches):", np.median(inches[rainy]))
print("Median precip on summer days in 2014 (inches): ",
      np.median(inches[summer]))
print("Maximum precip on summer days in 2014 (inches): ",
      np.max(inches[summer]))
print("Median precip on non-summer rainy days (inches):",
      np.median(inches[rainy & ~summer]))

#Using the Keywords and/or Versus the Operators &/|
print(bool(42), bool(0)) #True False
print(bool(42 and 0)) #False
print(bool(42 or 0)) #True
print(bin(42))  # 0b101010
print(bin(59))  # 0b111011
print(bin(42 | 59))  # 0b111011

a = np.array([1, 0, 1, 0, 1, 0], dtype=bool)
b = np.array([1, 1, 1, 0, 1, 1], dtype=bool)
print(a | b)  # [ True  True  True False  True  True]
#ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
# print( a or b)

python3/numpy/Comparisons Masks and Boolean Logic.py


# -*- coding: utf-8 -*-
"""
Created on Sun Oct  9 19:56:33 2016

@author: revan
"""

import numpy as np
import random

def create_points(number_of_points):
    """ create_points create number_of_points points where the number
    of points is the number of points we want in our dataset
    inputs: number_of_points - the number of points we want in our dataset
    output: points - a 2D numpy array which contain the points """

    x = []
    y = []

    for i in range(number_of_points):
        x_i = random.uniform(-1, 1)
        y_i = random.uniform(-1, 1)
        x.append(x_i)
        y.append(y_i)

    x = np.asarray(x)
    y = np.asarray(y)

    points = [x, y]
    points = np.transpose(points)
    return points

def determine_output(points, number_of_points):
    """ This function decides which points are classified originally as -1 and
    which are classified as 1.
    inputs:  points - the matrix of points
             number_of_points - the number of points in the dataset
    output:  y - the original classification of the points """

    y = np.zeros(number_of_points)
    for i in range(number_of_points):
        y[i] = np.sign(points[i, 0] ** 2 + points[i, 1] ** 2 - 0.6)

    if number_of_points != 0:
        to_flip = np.random.randint(0, 1000, 100)
        for element in to_flip:
            y[element] *= -1
    return y

def add_bias(points, number_of_points):
    """ This function adds bias term in the dataset
    inputs:  points - the dataset which contains the points
             number_of_points - the number of points in the dataset
    output:  points - the augmented dataset """

    points = np.insert(points, 0, 1, axis=1)
    return points

def add_nonlinear_transformation(points, number_of_points):
    """ This function adds the nonlinear transformations x1 * x2, x1 ^ 2 and x2 ^ 2
        inputs: points - the dataset which contains the points
                number_of_points - the number of points in the dataset
        output: points - the new dataset which contains the new features """

    x1x2 = np.multiply(points[:, 1], points[:, 2])
    x1_square = np.multiply(points[:, 1], points[:, 1])
    x2_square = np.multiply(points[:, 2], points[:, 2])
    points = np.insert(points, 3, x1x2, axis=1)
    points = np.insert(points, 4, x1_square, axis=1)
    points = np.insert(points, 5, x2_square, axis=1)
    return points

def compute_error_linear_model(number_of_points_training):
    """ This function computes the error in the training set using a linear regression classifier
    inputs:  number_of_points_training - number of points in the training set
    outputs: error_rate_training - error rate in the training set """

    # generate the training test
    points_training = create_points(number_of_points_training)
    y = determine_output(points_training, number_of_points_training)
    points_training = add_bias(points_training, number_of_points_training)

    # compute the weights
    weights = np.dot(np.linalg.pinv(points_training), y)

    # compute the training error
    output = np.dot(points_training, weights)
    number_of_errors = 0
    for i in range(number_of_points_training):
        if np.sign(output[i]) != y[i]:
            number_of_errors += 1
    error_rate_training = float(number_of_errors)/number_of_points_training

    return error_rate_training

def linear_model():
    """ This function runs the linear regression classifier
    prints: final_error_rate_training - the training error """

    number_of_points_training = 1000
    final_error_rate_training = 0.0

    number_of_iterations = 1000
    for i  in range(number_of_iterations):
        error_rate_training = compute_error_linear_model(number_of_points_training)
        final_error_rate_training += error_rate_training
    final_error_rate_training /= number_of_iterations

    # print train/test error for the linear regression used for classification
    print "Error rate in the training set is: " + str(final_error_rate_training)

def compute_error_linear_model_augmented(number_of_points_training, number_of_points_testing):
    """ This function computes the error in the training set using a linear regression classifier
    inputs:  number_of_points_training - number of points in the training set
    outputs: error_rate_training - error rate in the training set
             output - the output we got by performing the classifier
             points_training - the dataset
             error_rate_testing - error rate in the test set """

    # generate the training test
    points_training = create_points(number_of_points_training)
    y = determine_output(points_training, number_of_points_training)
    points_training = add_bias(points_training, number_of_points_training)
    points_training = add_nonlinear_transformation(points_training, number_of_points_training)

    # compute the weights
    weights = np.dot(np.linalg.pinv(points_training), y)

    # compute the training error
    output = np.dot(points_training, weights)
    number_of_errors = 0
    for i in range(number_of_points_training):
        if np.sign(output[i]) != y[i]:
            number_of_errors += 1
    error_rate_training = float(number_of_errors)/number_of_points_training

    # generate the testing set
    points_testing = create_points(number_of_points_testing)
    y_testing = determine_output(points_testing, number_of_points_testing)
    points_testing = add_bias(points_testing, number_of_points_testing)
    points_testing = add_nonlinear_transformation(points_testing, number_of_points_testing)

    # compute the testing error
    output_testing = np.dot(points_testing, weights)
    number_of_errors_testing = 0
    for i in range(number_of_points_testing):
        if np.sign(output_testing[i]) != y_testing[i]:
            number_of_errors_testing += 1
    error_rate_testing = float(number_of_errors_testing)/number_of_points_testing

    return error_rate_training, output, points_training, error_rate_testing

def linear_model_augmented():
    """ This function solves the exercise Nr.9 and Exercise Nr.10 """

    # define the number of training points and testing points
    number_of_points_training = 1000
    number_of_points_testing = 1000
    final_error_rate_training = 0.0

    number_of_iterations = 1000
    for i  in range(number_of_iterations):
        error_rate_training, output, points_training, final_error_rate_testing =\
            compute_error_linear_model_augmented(number_of_points_training, number_of_points_testing)
        final_error_rate_training += error_rate_training
    final_error_rate_training /= number_of_iterations

    # the given hypothesis'
    missmatches1 = 0
    missmatches2 = 0
    missmatches3 = 0
    missmatches4 = 0
    missmatches5 = 0

    # define the number of missmatches with our hypothesis for every given hypothesis
    for i in range(number_of_points_training):

        if np.sign(-1 * points_training[i, 0] - 0.05 * points_training[i, 1] +\
            0.08 * points_training[i, 2] + 0.13 * points_training[i, 3] +\
            1.5 * points_training[i, 4] + 1.5 * points_training[i, 5]) != np.sign(output[i]):
                missmatches1 += 1

        if np.sign(-1 * points_training[i, 0] - 0.05 * points_training[i, 1] +\
            0.08 * points_training[i, 2] + 0.13 * points_training[i, 3] +\
            1.5 * points_training[i, 4] + 15 * points_training[i, 5]) != np.sign(output[i]):
                missmatches2 += 1

        if np.sign(-1 * points_training[i, 0] - 0.05 * points_training[i, 1] +\
            0.08 * points_training[i, 2] + 0.13 * points_training[i, 3] +\
            15 * points_training[i, 4] + 1.5 * points_training[i, 5]) != np.sign(output[i]):
                missmatches3 += 1

        if np.sign(-1 * points_training[i, 0] - 1.5 * points_training[i, 1] +\
            0.08 * points_training[i, 2] + 0.13 * points_training[i, 3] +\
            0.05 * points_training[i, 4] + 0.05 * points_training[i, 5]) != np.sign(output[i]):
                missmatches4 += 1

        if np.sign(-1 * points_training[i, 0] - 0.05 * points_training[i, 1] +\
            0.08 * points_training[i, 2] + 1.5 * points_training[i, 3] +\
            0.15 * points_training[i, 4] + 0.15 * points_training[i, 5]) != np.sign(output[i]):
                missmatches5 += 1

    print "Testing error is: " + str(final_error_rate_training)
    best_hypothesis = np.asarray([missmatches1, missmatches2, missmatches3, missmatches4, missmatches5])
    print "Best hypothesis is hypothesis No." + str(np.argmin(best_hypothesis) + 1)
    print "Testing error is: " + str(final_error_rate_testing)

# run the algorithms
print "Exercise 8: linear model - results!"
linear_model()
print "-----------------------------------"
print "Exercise 9 and 10: nonlinear transformation results!"
linear_model_augmented()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 24 19:53:09 2019

@author: alankar
"""

import numpy as np
import matplotlib.pyplot as plt

dow = np.loadtxt('dow.txt')
N = len(dow)
t = np.arange(0,N,1)

sampling = 1/(t[1]-t[0])
f = np.linspace(0,sampling,N//2+1)
dow_fft = np.fft.rfft(dow)
plt.figure(figsize=(13,10))
plt.semilogy(f,np.abs(dow_fft)**2)
plt.grid()
plt.xlabel(r'f',size=18)
plt.ylabel(r'Power Spectrum $P(f)$',size=20)
plt.title(r'FFT[DOW]', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('4_1.png')
plt.show()

plt.figure(figsize=(13,10))
plt.plot(t,dow,label='Original Data')
plt.grid()
plt.xlabel(r'Days',size=18)
plt.ylabel(r'DOW',size=20)
plt.title(r'DOW Industrial Average', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('4_2.png')

compress = lambda data,freq,percent_acc: np.piecewise(data,[(freq-freq[0])<=\
                                                       (percent_acc/100)*(freq[-1]-freq[0])],[lambda data: data,0.])

dow_fft1 = compress(dow_fft,f,10)
dow_fft2 = compress(dow_fft,f,2)
dow1 = np.fft.irfft(dow_fft1)
dow2 = np.fft.irfft(dow_fft2)
plt.plot(t,dow1,label=r'%d percent Fourier modes Accepted'%90)
plt.plot(t,dow2,label=r'%d percent Fourier modes Accepted'%2)
plt.legend(loc='best', prop={'size': 16})
plt.savefig('4_3.png')
plt.show()


import numpy as np

def sor(A, b, x0=None, w=1, eps=1e-5, max_steps=5000, verbose=False):
    """逐次超松驰法（Successive over-relaxation, SOR）求解线性方程组:
        A @ x = b

    Args:
        A:  np_array_like, 系数矩阵
        b:  np_array_like, 右端常数
        x0: np_array_like, 迭代初值
            default x0=None means using random values.
        w:  float, 松弛因子 w > 0
            default w=1，即 Gauss–Seidel 迭代
        eps: float, 精度要求
        max_steps: int, 最大迭代次数
        verbose: bool, 如果计算成功，打印出结果及迭代次数

    Returns:
        x: 方程组的解

    Raises:
        ValueError: w 小于等于 0
        ValueError: A 和 b 形状不符合要求
        Expection:  达到最大迭代次数，仍不满足精度
    """
    if w <= 0:
        raise ValueError(f"unexpected w = {w} < 0")

    A = np.array(A)
    b = np.array(b)

    n, m = A.shape
    if n != m or n != len(b):
        raise ValueError(f"Not match: A({n, m}) and b({len(b)},)")

    if not x0:
        x0 = np.random.random(n)

    # A =  D - L - U

    D = np.diag(np.diag(A))
    L = - np.tril(A, -1)
    U = - np.triu(A, 1)

    _inv_DsWL = np.linalg.pinv(D - w * L)

    B = _inv_DsWL @ ((1-w) * D + w * U)
    f = w * _inv_DsWL @ b

    x = x0
    i = 0

    for i in range(int(max_steps)):
        x_prev = np.array(x)  # deep copy

        x = B @ x + f

        if np.all(np.abs(x - x_prev) <= eps):  # 达到精度要求
            break
    else:
        raise Exception(
            f"cannot reach eps ({eps}) after max_steps ({max_steps}). The last result: x = {x}")

    if verbose:
        print(f"sor (w={w}) get result x = {x} after {i} iterations.")

    return x


import numpy as np

print(0.1 + 0.1 + 0.1)
# 0.30000000000000004

a = np.array([0.3, 0.1 + 0.1 + 0.1])
print(a)
# [0.3 0.3]

b = np.array([0.3, 0.3])
print(b)
# [0.3 0.3]

print(a == b)
# [ True False]

np.set_printoptions(precision=18)

print(a)
# [0.3                 0.30000000000000004]

print(np.isclose(a, b))
# [ True  True]

print(np.isclose(a, 0.3))
# [ True  True]

print(np.isclose(0.1 + 0.1 + 0.1, 0.3))
# True

print(np.isclose(100, 101))
# False

print(np.isclose(100, 101, rtol=0, atol=1))
# True

print(np.isclose(np.nan, np.nan))
# False

print(np.isclose(np.nan, np.nan, equal_nan=True))
# True

print(np.isclose(np.nan, 100, equal_nan=True))
# False

a_nan = np.array([np.nan, 1, 2])
print(a_nan)
# [nan  1.  2.]

b_nan = np.array([np.nan, np.nan, 2])
print(b_nan)
# [nan nan  2.]

print(np.isclose(a_nan, b_nan))
# [False False  True]

print(np.isclose(a_nan, b_nan, equal_nan=True))
# [ True False  True]


# Machine Learning course by AndrewNg on Coursera

# import all libraries required here
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from mpl_toolkits.mplot3d import axes3d

# 1.  Print 5 by 5 Identity matrix
id5 = np.identity(5)
print(id5)

# 2.  Plot the data
data = np.loadtxt('ex1data1.txt', delimiter=',')

X = np.c_[np.ones(data.shape[0]),data[:,0]]
y = np.c_[data[:,1]]

#data = pd.read_csv('ex1data1.txt', header=None, names=['Population', 'Profit']) # read from dataset
#print(data.head()) # view first few rows of the data
#print(data.describe())

#X = data.iloc[:,0] # read first column
#y = data.iloc[:,1] # read second column
m = len(y) # number of training example
plt.scatter(X[:,1], y)
plt.xlabel('Population of city in 10,000s')
plt.ylabel('Profit in $10,000s')
plt.show()

# 3.  Cost function
# Linear regression equation: h(x[i]) = theta0 + theta1*x[i]
# Linear regression Cost function, J(theta0, theta1) = (1/2m) * sum[h(x[i]) -
# y[i]]^2, where 1 <= i <= m
# partial-derivative w.r.t.  theta(j): (1/m)*sum[h(x[i]) - y[i]]*x[i], where 1
# <= i <= m

# Gradient descent:
# repeat until convergence {
#   theta(j) := theta(j) + alpha * partial-derivative w.r.t.  theta(j) of
#   J(theta0, theta1), for j = 0 and 1 simultaneously
# }
# Simplified Gradient descent algo:
# repeat until convergence {
#   theta(j) := theta(j) + (alpha/m) * sum[h(x[i]) - y[i]]*x[i], for j = 0 and
#   1 simultaneously and 1 <= i <= m
# }

# Assign initially theta0 and theta1 = 0, alpha = 0.01
theta = np.zeros([2,1])
alpha = 0.01

# add function to compute cost
def computeCost(X,y,theta, m):
    temp = X.dot(theta) - y
    return np.sum(np.power(temp, 2)) / (2 * m)

# Compute cost at current values
J = computeCost(X, y, theta, m)
print(J)

# add functino to calculate gradient descent
def gradientDescent(X, y, theta, alpha, m, iterations = 1500):
    J_history = np.zeros(iterations)

    for iter in np.arange(iterations):
        h = X.dot(theta)
        theta = theta - (alpha/m)*(X.T.dot(h-y))
        J_history[iter] = computeCost(X,y,theta, m)
    return (theta, J_history)

# Compute gradient descent or theta for minimized cost J
theta, Cost_J = gradientDescent(X,y,theta, alpha, m)

print('theta: ', theta.ravel())

plt.plot(Cost_J)
plt.ylabel('Cost J')
plt.xlabel('Iterations')
plt.show()

# Plot the actual data and linear regression plot
xx = np.arange(5,23)
yy = theta[0] + theta[1]*xx

# Plot gradient descent
plt.scatter(X[:,1],y,s=30,c='r',marker='+',linewidths =1)
plt.plot(xx,yy,label='Linear regression (Gradient descent)')

# Compare with Scikit-learn Linear regression
regr = LinearRegression()
regr.fit(X[:,1].reshape(-1,1), y.ravel())
plt.plot(xx, regr.intercept_+regr.coef_*xx, label='Linear regression (Scikit-learn GLM)')

plt.xlim(4,24)
plt.xlabel('Population of City in 10,000s')
plt.ylabel('Profit in $10,000s')
plt.legend(loc=4);
plt.show()

# predict profit for a city with population of 35000 and 70000
print(theta.T.dot([1,3.5])*10000)
print(theta.T.dot([1,7])*10000)


import matplotlib.pyplot as plt
import numpy as np

from load_data import load_data
from multi_cost import multi_cost
from multi_gradient import gradient_descent
from prediction import predict_price

# Linear regression with multiple variables - predicting housing prices

# Load Data

filename = 'housing_data.txt'
housing_data = load_data(filename)

# Feature Normalization

# store the means and standard deviations for predictions
means = housing_data.mean()
stds = housing_data.std()

housing_data = (housing_data - housing_data.mean()) / housing_data.std()

# Inserting intercept parameter

housing_data.insert(0, 'Ones', 1)

# Convert pandas DataFrame to numpy arrays

cols = housing_data.shape[1]

x = housing_data.iloc[:, 0:cols-1]
y = housing_data.iloc[:, cols-1:cols]

x = x.values
y = y.values

# Set initial values

alpha = 0.03
iterations = 1000
theta = np.array([[0, 0, 0]])

# Cost Function

cost = multi_cost(x, y, theta)

# Gradient Descent

new_theta, cost_history = gradient_descent(x, y, theta, alpha, iterations)

# Plotting Data

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(list(range(iterations)), cost_history, 'r')
plt.xlabel("Iterations")
plt.ylabel("Cost")
plt.title("Cost History in Epochs")
plt.show()

# Prediction

sq_feet = 1650
bedrooms = 3
new_price = predict_price(sq_feet, bedrooms, new_theta, means, stds)
print(f"The price for a {bedrooms} bedroom, {sq_feet} sq foot house is:\n\t${new_price}")


import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize

# Metodo con fit
def funzione(x, v0, Rg) :
    return v0/(x+Rg)

def derivata(x, V0, Rg) :
    return -V0/(Rg+x)**2

def chi2(x, popt, y, dy) :
    return np.sum(((y-funzione(x, *popt))/dy)**2)

# Dati
Rj, dRj, Ra, dRa = np.genfromtxt("resistenze.txt", skip_header=1, unpack=True)
Ij, dIj = np.genfromtxt("correnti.txt", skip_header=1, unpack=True)
Ij /= 1000
dIj /= 1000
Rx = Rj+Ra
dRx = (dRj**2+dRa**2)**0.5

# Fit
ndof = 10
dI_eff = dIj
popt, pcov = scipy.optimize.curve_fit(funzione, Rx, Ij, [4, 18], dI_eff, absolute_sigma=False)
chi2_new = chi2(Rx, popt, Ij, dI_eff)
chi2_old = -1
print(chi2_new)
for i in range (10) :
    dI_eff = ((derivata(Rx, *popt)*dRx)**2 + dIj**2)**0.5
    popt, pcov = scipy.optimize.curve_fit(funzione, Rx, Ij, [4, 18], dI_eff, absolute_sigma=False)
    chi2_old = chi2_new
    chi2_new = chi2(Rx, popt, Ij, dI_eff)
    print(chi2_new)

print("\nV0 ", popt[0], pcov[0][0]**0.5)
print("Rg ", popt[1], pcov[1][1]**0.5)
print("chi2 ", chi2_new)
print("chi2 ridotto {}/{}={}".format(chi2_new, ndof, chi2_new/ndof))
print("cov normalizzata", pcov[1][0]/(pcov[0][0]*pcov[1][1])**0.5, "\n")

# Grafici
# Plot funzione
x = np.linspace(1, 6, 4000)
x = 10**x
plt.figure()
plt.subplot(211)
plt.xscale('log')
plt.yscale('log')
plt.errorbar(Rx, Ij, yerr=dIj, xerr=dRx, fmt='.', label="Misure")
plt.plot(x, funzione(x, *popt), label="Fit")
plt.xlabel("R_eq [ohm]")
plt.ylabel("I [A]")
plt.legend()

# Plot residui normalizzati
plt.subplot(212)
plt.xscale('log')
plt.errorbar(Rx, (Ij-funzione(Rx, *popt))/dI_eff, fmt='o', label="Residui normalizzati")
plt.plot(x, x*0)
plt.xlabel("R_eq [ohm]")
plt.legend()
plt.show()

Lab 2/2 resistenza di thevenin e best fit/fit thevenin.py


# Implementation of Sammon's nonlinear mapping stress, a quality measure for NLDR embeddings.
# For more details on the measure, see Sammon, J. W. (1969).
# A nonlinear mapping for data structure analysis. IEEE Transactions on computers, 100(5), 401-409.
# This implementation has been written by Adrien Bibal (University of Namur).

import numpy as np
from scipy.spatial.distance import pdist
from sklearn.preprocessing import scale

# Compute the stress function of NLM (the focus of this stress is on the input space)
# data = high dimensional data
# visu = low dimensional data
# return the NLM stress between data and visu
# The higher the score, the worst is the visualization.
def compute(data, visu):
	# DV = Distance Vector
	DV1 = scale(pdist(data, 'euclidean'))
	DV2 = scale(pdist(visu, 'euclidean'))

	stress = 0
	for i in range(len(DV1)):
		stress += ((DV1[i] - DV2[i])**2) / DV1[i]

	stress *= 1 / np.sum(DV1)

	return stress


"""
author: "Kelly Rivera"
title: "QR Algorithm"
"""

##Import Python Libraries
import numpy as npy
import scipy.linalg as spl  # SciPy Linear Algebra Library

## Householder transformation method
def qr_Algorithm_HH(x, converge_range):
    m, n = x.shape
    Q_last = npy.identity(n)
    diff = 1

    lamb, u = spl.eig(x)
    lamb = max(npy.abs(lamb))

    # QR Algo using
    while diff > converge_range:
        Q, R = npy.linalg.qr(x)
        # This update matrix A, by dot product of reverse QR
        x = npy.dot(R, Q)

        eigenvalue = npy.diag(x)  #outputs only the values on the main diaginal
        eigenvalue = max(npy.abs(eigenvalue))

        diff = npy.abs(lamb - eigenvalue)

        eigenvectors = npy.dot(Q_last, Q)
        Q_last = eigenvectors

    eigenvalues = npy.diag(x)  #outputs only the values on the main diaginal

    return eigenvectors, eigenvalues

## Gram-Schmidt Process
def qr_GS(x):
    m, n = x.shape
    Q = npy.zeros((m, n)) # Q is all zero
    R = npy.zeros((n, n))

    for j in range(n): #range (3) = {0,1,2}
        v = x[:, j] # x[:,0] returns the first* column* of x, x[:,1] reutrns the second column etc

        for i in range(j):

            Q1 = Q[:, i] #when we are here for the the first time, q =
            R[i, j] = npy.dot(Q1, v)
            v = v - R[i, j] * Q1

        vNorm = npy.linalg.norm(v)
        R[j, j] = vNorm
        Q[:, j] = v / vNorm

    return Q, R

def qr_Algorithm_GS(x, converge_range):
    m, n = x.shape
    Q_last = npy.identity(n)
    diff = 1

    lamb, u = spl.eig(x)
    lamb = max(npy.abs(lamb))

    while diff > converge_range:
        Q, R = qr_GS(x)
        # This update matrix A, by dot product of reverse QR
        x = npy.dot(R, Q)

        eigenvalue = npy.diag(x)  #outputs only the values on the main diaginal
        eigenvalue = max(npy.abs(eigenvalue))

        diff = npy.abs(lamb - eigenvalue)

        eigenvectors = npy.dot(Q_last, Q)
        Q_last = eigenvectors

    eigenvalues = npy.diag(x)  #outputs only the values on the main diaginal

    return eigenvectors, eigenvalues

## QR with Shift using the Gram-Schmidt precess
def shiftedQR_Algorithm(x, converge_range):
    m, n = x.shape
    I = npy.identity(n)
    Q_last = npy.identity(n)
    diff = 1

    lamb, u = spl.eig(x)
    lamb = max(npy.abs(lamb))

    while diff > converge_range:
        μ = x[[n-1],[n-1]]         # shift: μ = a_nn
        Q, R = qr_GS((x - (μ*I)))
        # This update matrix A, by dot product of reverse QR
        x = npy.dot(R, Q) + (μ*I)   # Updates matrix, by dot product of reverse QR and adding back the shift

        eigenvalue = npy.diag(x)  #outputs only the values on the main diaginal
        eigenvalue = max(npy.abs(eigenvalue))

        diff = npy.abs(lamb - eigenvalue)

        eigenvectors = npy.dot(Q_last, Q)
        Q_last = eigenvectors

    eigenvalues = npy.diag(x)  #outputs only the values on the main diaginal

    return eigenvectors, eigenvalues


# Hit or miss
# Monte Carlo Integration Method

import numpy as np
import random as rn
N = 1000000
xr = np.random.uniform(1,2,N)
yr = np.random.uniform(0,15,N)
count = 0.
area = 1*15.

for i in range(N):
    if yr[i] <= xr[i]**4. - 2.*xr[i] + 1.:
        count += 1.

integ = count*area/N

print integ


from numba import jit
import numpy as np

@jit
def laplacian_1d(n):
    return np.diag(2 * np.ones(n - 2)) + \
           np.diag(-np.ones(n - 3), 1) + \
           np.diag(-np.ones(n - 3), -1)

@jit
def laplacian_2d(n):
    return np.kron(np.eye(n - 2), laplacian_1d(n)) + \
           np.kron(laplacian_1d(n), np.eye(n - 2))

# from scipy.sparse import diags, kron, eye

# @jit
# def laplacian_1d(n):
#     return diags([2 * np.ones(n - 2), -np.ones(n - 3), -np.ones(n - 3)], [0, 1, -1])

# @jit
# def laplacian_2d(n):
#     return kron(eye(n - 2), laplacian_1d(n)) + \
#            kron(laplacian_1d(n), eye(n - 2))


import numpy as np

def geometric_mean(A: float, B: float) -> float:
    return np.sqrt(A * B)

def arithmetic_mean(A: float, B: float) -> float:
    return 0.5 * (A + B)

def Lorentz_Berthelot(params1, params2) -> tuple:
    """
    Lorentz-Berthelot mixing rules to compute epsilon and sigma parameters of different site types

    Parameters
    ----------

    eps1 : float
        epsilon parameter of site 1

    eps2 : float
        epsilon parameter of site 2

    sig1 : float
        sigma parameter of site 1

    sig2 : float
        sigma parameter of site 2

    Returns
    -------
    eps : float
    Mixed epsilon parameter
    sig : float
    Mixed sigma parameter
    """
    eps1, sig1 = params1
    eps2, sig2 = params2
    eps = geometric_mean(eps1, eps2)
    sig = arithmetic_mean(sig1, sig2)
    return eps, sig

def AB_mixing(A_1: float, A_2: float, B_1: float, B_2: float) -> float:

    A = geometric_mean(A_1, A_2)
    B = geometric_mean(B_1, B_2)
    return A, B


import numpy as np
import math
import matplotlib.pylab as pl
import random

"""
NumRep Exercises 1 and 2
Simon McLaren
"""

# Class for a gaussian function
class MyGaussianPdf:

    # Constructor
    def __init__(self, mean, width):
        self.mean = mean
        self.width = width

    # Return Gaussian width
    def getWidth(self):
        return self.width

    # Return Gaussian mean
    def getMean(self):
        return self.mean

    # Return a random value of x with Gaussian distribution
    def next(self, minx, maxx):
        fmax = 1.
        y2 = 10.
        y1 = 1.
        while y2>y1:
            randomx = minx + (maxx-minx)*random.random()
            y1 = self.evaluate(randomx)
            y2 = fmax*random.random()
        return randomx

    # Evaluate the Gaussian at point x
    def evaluate(self, x):
        return math.exp(-((x-self.mean)**2)/2*self.width**2)

    # Calculate the integral of the gaussian between -inf and inf
    def integralAnalytic(self):
        return self.width*math.sqrt(2*math.pi)

    # Numerical integration by the 0th order rectangle method
    def integralNumericRectangle0thOrder(self, numofrectangles):
        minx = -5*self.width
        maxx = 5*self.width
        rectanglewidth = (maxx-minx)/numofrectangles
        integral = 0.
        evalxpoint = minx + rectanglewidth*0.5 # Midpoint of rectangle
        for i in range(numofrectangles):
            integral += self.evaluate(evalxpoint) * rectanglewidth
            evalxpoint += rectanglewidth
        return integral

    # Numerical integration by the 1st order rectangle method
    def integralNumericRectangle1stOrder(self, numofrectangles):
        minx = -5 * self.width
        maxx = 5 * self.width
        rectanglewidth = (maxx - minx) / numofrectangles
        integral = 0.
        lowerxbound = minx
        higherxbound = minx + rectanglewidth
        for i in range(numofrectangles):
            integral += rectanglewidth * self.evaluate(lowerxbound) + \
                        0.5 * abs(self.evaluate(higherxbound) - self.evaluate(lowerxbound)) * rectanglewidth
            lowerxbound += rectanglewidth
            higherxbound += rectanglewidth
        return integral

if __name__ == "__main__":
    gaussianfunction = MyGaussianPdf(0.,1.)
    # gaussiansampledata = []
    # for i in range(10000):
    #     gaussiansampledata.append(gaussianfunction.next(-4.,4.))
    # pl.hist(gaussiansampledata[:], bins = 50, range = [-4.,4.])
    # pl.savefig("gaussianhist.png")
    # pl.show()
    # data = np.random.normal(0.,1.,10000)
    # pl.hist(data[:], bins = 50, range = [-4.,4.])
    # pl.savefig("numpygaussianhist.png")
    # pl.show()
    integralanalytic = float(gaussianfunction.integralAnalytic())
    integralnumeric = float(gaussianfunction.integralNumericRectangle1stOrder(10000))
    integraldifference = abs(integralanalytic-integralnumeric)
    percentdifference = float(100*integraldifference/integralanalytic)
    print "The analytic integral is %f.  The numeric integral is %f.  The percentage difference between them is %.5f." \
          % (integralanalytic, integralnumeric, percentdifference)


import numpy as np
from scipy.ndimage import interpolation

# нахождеение центра масс изображения
def moments(image):
    # создание сетки
    c0, c1 = np.mgrid[:image.shape[0], :image.shape[1]]
    # сумма пикселей
    total_image = np.sum(image)
    # mu_x - момент по x
    m0 = np.sum(c0 * image) / total_image
    # mu_y - момент по y
    m1 = np.sum(c1 * image) / total_image
    # var(x) - вариация по x
    m00 = np.sum((c0 - m0)**2 * image) / total_image
    # var(y) - вариация по y
    m11 = np.sum((c1 - m1)**2 * image) / total_image
    # covariance(x,y) - ковариация
    m01 = np.sum((c0 - m0) * (c1 - m1) * image) / total_image
    # вектор центов масс - mu_x, mu_y соответственно
    mu_vector = np.array([m0, m1])
    # наблюдается ли схожесть в ковариационных матрицах
    covariance_matrix = np.array([[m00,m01],[m01,m11]])

    return mu_vector, covariance_matrix

# выравнивание
def deskew(image):
    # нахождение моментов
    c, v = moments(image)
    # нахождение коэффициента альфа для матрицы выравнивания
    alpha = v[0, 1] / v[0, 0]
    # матрица выравнивания
    affine = np.array([[1, 0], [alpha, 1]])
    # новый центр масс
    ocenter = np.array(image.shape)/2.0
    # центрирование
    offset = c - np.dot(affine, ocenter)
    # трансформация
    img = interpolation.affine_transform(image, affine, offset=offset)
    return (img - img.min()) / (img.max() - img.min())

# для применени к датасету MNIST из библиотеки TensorFlow
def apply_deskew(data):
    """
	Применение метда deskew (выравивание цифр) ко входным данным.
	:param data: входные данные.
	:return: преобразованные данные.
	"""
    # пустой тензор результатов
    result = np.zeros(data.shape)
    # цикл по каждой записи (картинке с цифрой)
    for i in range(data.shape[0]):
        image = data[i,...].astype('float32') / 255.
        # применение deskew
        processed_image = deskew(image)
        # хапись результатов
        result[i] = processed_image

    return result.reshape(result.shape[0], -1)


import numpy as np
import scipy.stats

def linconfband(xx, yy, a, b, conf=0.95, x_grid=None, need_eps=False):
    """
    Confidence bands for linear regression line

    adopted from
    1. https://gist.github.com/rsnemmen/f2c03beb391db809c90f
    2. https://gist.github.com/rsnemmen/0eb32832c657c19e4d39
    3. https://en.wikipedia.org/wiki/Simple_linear_regression
    """
    alpha = 1.0 - conf  # significance
    N = xx.size      # data sample size

    x_grid = np.linspace(xx.min(), xx.max(), 100) if x_grid is None else x_grid
    y_grid = a*x_grid + b

    eps = yy - (a*xx + b)
    sd = np.sqrt(1/(N-2) * np.sum(eps**2))

    sxx = np.sum((xx - xx.mean())**2)
    sx  = (x_grid - xx.mean())**2

    # Quantile of Student's t distribution for p=1-alpha/2
    alpha = alpha/N  # bonferroni correction?
    q = scipy.stats.t.ppf(1.-alpha/2., N-2)
    dy = q * sd * np.sqrt(1./N + sx/sxx)

    ucb = y_grid + dy  # Upper confidence band
    lcb = y_grid - dy  # Lower confidence band
    res = [lcb, ucb]
    if need_eps:
        res.append(eps)
    return res


import numpy as np

# addition of vector with scalar
one_dim_array = np.array([11, 21, 31, 41]) #create an array
# array([11, 21, 31, 41])

one_dim_array + 1 # element wise addition
# array([12, 22, 32, 42])

one_dim_array ** 2 # element wise exponent
# array([ 121,  441,  961, 1681], dtype=int32)

one_dim_array - 1 # element wise subtraction
# array([10, 20, 30, 40])

# matrix multiplication

matrix1 = np.diag([1,2,3,4])
""" array([[1, 0, 0, 0],
       [0, 2, 0, 0],
       [0, 0, 3, 0],
       [0, 0, 0, 4]]) """

# way 1
matrix1 * matrix1
""" array([[ 1,  0,  0,  0],
       [ 0,  4,  0,  0],
       [ 0,  0,  9,  0],
       [ 0,  0,  0, 16]]) """

# way 2
matrix1.dot(matrix1)
""" array([[ 1,  0,  0,  0],
       [ 0,  4,  0,  0],
       [ 0,  0,  9,  0],
       [ 0,  0,  0, 16]]) """


import numpy as np
from functions import sigmoid, sigmoid_derivative

class GradientDescentAlgorithm(object):
    def __init__(self,eta=3.0,topology=[]):
        self.eta = eta
        self.topology = topology
        pass

    def finalize_epoch(self):
        pass

    def gradient_descent(self, num_of_examples, deltas, weights, activations, biases):
        new_weights = np.zeros(len(weights)).tolist()
        new_biases = np.zeros(len(biases)).tolist()
        scalar = float(self.eta)/num_of_examples

        for layer, output in reversed(list(enumerate(deltas))):
            new_weights[layer] = weights[layer] - scalar * np.dot(deltas[layer].T, activations[layer]).T
            new_biases[layer] = biases[layer] - scalar * np.sum(deltas[layer], axis=0)

        return new_weights, new_biases

    # calculate initial error and back_propagate
    def calculate_deltas(self, activations, outputs, weights, labels):
        deltas = np.zeros(len(outputs)).tolist()
        for layer, output in reversed(list(enumerate(outputs))):
            if layer == (len(outputs) - 1):
                first_term = activations[-1] - labels
            else:
                first_term = np.dot(deltas[layer+1], weights[layer+1].T)

            second_term = sigmoid_derivative(output)

            deltas[layer] = np.multiply(first_term, second_term)

        return deltas

    def feed_forward(self, weights, biases, data, labels):
        # Add data as first layer for activations
        new_activations = [data]
        new_outputs = []
        number_of_labels = labels.shape[0]

        for idx, weight in enumerate(weights):
            outputs, activations = self.feed_forward_layer(idx,weight,new_activations[idx],biases[idx])
            new_outputs.append(outputs)
            new_activations.append( activations )

        return new_activations, new_outputs

    def feed_forward_layer(self,layer,weight,inputs,bias):
        outputs = self.calculate_outputs(layer,inputs,weight,bias)
        activations = self.calculate_activations(layer,outputs)

        return outputs, activations

    def calculate_outputs(self,layer, inputs,weight,bias):
        outputs = np.dot(inputs, weight) + bias

        return outputs

    def calculate_activations(self,layer,outputs):
        return sigmoid(outputs)


import numpy as np

def standardize(X):
    mean = np.mean(X, axis=0)
    std = np.std(X, axis=0)
    std[np.where(std == 0.0)[0]] = 1.0
    return (X - mean) / std

def SVD(X):
    X -= np.mean(X, axis = 0) # zero-center the data (important)
    cov = np.dot(X.T, X) / X.shape[0] # get the data covariance matrix
    U, S, V = np.linalg.svd(cov)
    return U, S, V

def PCA(X, num_retained=1000):
    U,S,V = SVD(X)
    Xrot_reduced = np.dot(X, U[:,:100]) # Xrot_reduced becomes [N x 100]
    return Xrot_reduced

def whiten(X, num_retained=1000):
    U,S,V = SVD(X)
    Xrot = np.dot(X, U) # decorrelate the data
    Xwhite = Xrot / np.sqrt(S + 1e-5)
    return Xwhite


"""
Tool module
===========

Contains some useful numerical tools
"""

import numpy as np

def rotate_towards(vi, vf, theta):
    """
    Rotate a vector `vi` towards another vector `vf` by angle `theta`.
    Return the rotated vector.
    """
    x = np.cross(vi, vf)
    _x_ = np.linalg.norm(x)
    if _x_ < 1e-15:
        return vi
    else:
        x /= _x_
    A = np.array([[ 0, -x[2], x[1] ],
                  [ x[2], 0, -x[0] ],
                  [ -x[1], x[0], 0 ]])
    c, s = np.cos(theta), np.sin(theta)
    R = np.eye(3) + s * A + (1-c) * A.dot(A)

    return R.dot(vi)

def cart2sphere(v):
    """
    Return the spherical angles `theta` and `phi` associated with a unit vector `v`.
    """
    # v needs to be a unit vector
    x,y,z = v
    theta = np.arccos(np.clip(z,-1,1))
    phi = np.arctan2(x,y)

    return theta, phi

def sphere2cart(theta, phi):
    """
    Return the unit vector `v` associated with angles `theta` and `phi`.
    Adjusts `theta` and `phi` such that the transformation is valid
    """
    if theta < 0:
        theta = np.pi + theta
        phi += np.pi
    elif theta > np.pi:
        theta = theta - np.pi
        phi += np.pi
    st, sp = np.sin(theta), np.sin(phi)
    ct, cp = np.cos(theta), np.cos(phi)
    x = st * sp
    y = st * cp
    z = ct

    return np.array([x,y,z])

if __name__=="__main__":

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

    th, ph = cart2sphere(v)
    print(th, ph)

    v_ = sphere2cart(th,ph)

    print(v, v_)


#import time
import numpy as np
#from scipy.special import eval_legendre as lp
import matplotlib.pyplot as plt
from rt_002_polleg import polleg
#==============================================================================
#
if __name__ == "__main__":
#   scattering angle & cosine
    sca = np.linspace(0.0, 180.0, 181)
    x = np.cos(np.radians(sca))
#   Rayleigh phase function
    r = 0.75*(1.0 + x*x)
#   expansion moments for the Rayleigh phase function: 2k+1 is INCLUDED
    xk_r = np.array([1.0, 0.0, 0.5])
    nk = len(xk_r)
#   restore Rayleigh phase function from moments: 3 different options
    pk = polleg(x, nk-1)   # recall, k = 0,1,2 hence nk-1;   not sure if the built-in eval_legendre is better
    rx_opt1 = np.dot(xk_r, pk)  # this looks nice, but NOT efficient because ...
#
    rx_opt2 = np.zeros_like(rx_opt1)
    for ik in range(nk):          # we ALREADY HAVE this loop in polleg!
        for ix in range(len(x)):  # we ALREADY HAVE this loop in polleg AS WELL!!
            rx_opt2[ix] += xk_r[ik]*pk[ik,ix]
#
#   Less nice, a bit harder to understand, but efficient way is:
#      compute Legendre polynomials "on the go"
    rx_opt3 = np.zeros_like(rx_opt1)
    for ix in range(len(x)):
        xi = x[ix]              # avoids addressing to an array
        rxi = 1.0               # xk[k=0]*Pk[k=0] + xk[1]*Pk[k=1] = 1*1 + 0*x
        p0 = 1.0                # for Pk-recurrence
        p1 = xi                 # for Pk-recurrence
        for ik in range(2, nk): #
            p2 = (2.0 - 1.0/ik)*xi*p1 - (1.0 - 1.0/ik)*p0 # recurrence
            rxi += xk_r[ik]*p2                            # accumulation - can not be fully vectorized :(
            p0 = p1             # for Pk-recurrence
            p1 = p2             # for Pk-recurrence
        rx_opt3[ix] = rxi       # avoids addressing to an array inside the ik-loop
#
    plt.figure()
    plt.plot(sca, r, 'k', sca[0::10], rx_opt1[0::10], 'ro',\
       sca[2::10], rx_opt2[2::10], 'go', sca[4::10], rx_opt3[4::10], 'bo')
    plt.title('Rayleigh phase function')
    plt.xlabel('scattering angle, deg.')
    plt.legend(['explicit formula', 'from moments, xk: opt.1', 'from moments, xk: opt.2', 'from moments, xk: opt.3'])
    plt.grid(True)


#!/usr/bin/env python3

import numpy as np

def recoverData(Z, U, K):
    #RECOVERDATA Recovers an approximation of the original data when using the
    #projected data
    #   X_rec = RECOVERDATA(Z, U, K) recovers an approximation the
    #   original data that has been reduced to K dimensions. It returns the
    #   approximate reconstruction in X_rec.
    #

    # You need to return the following variables correctly.
    #X_rec = zeros(size(Z, 1), size(U, 1))

    # ====================== YOUR CODE HERE ======================
    # Instructions: Compute the approximation of the data by projecting back
    #               onto the original space using the top K eigenvectors in U.
    #
    #               For the i-th example Z(i,:), the (approximate)
    #               recovered data for dimension j is given as follows:
    #                    v = Z(i, :)'
    #                    recovered_j = v' * U(j, 1:K)'
    #
    #               Notice that U(j, 1:K) is a row vector.
    #

    X_rec = np.dot(Z, U[:, :K].T)

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

    return X_rec
    #end


from sympy import *

# Verify SymTrace in MultiBspline.hpp

# The first part of this script verifies the expression for the trace of the product of a
# symmetric matrix (H) and a general matrix (G).

# The second part creates body of the SymTrace test in test_multi_spline.cpp.

G = MatrixSymbol('G', 3, 3)
H = MatrixSymbol('H', 3, 3)

h1 = Matrix(H)
print 'H = ',h1
# Symmetrize H
h1 = h1.subs(H[1,0], H[0,1])
h1 = h1.subs(H[2,1], H[1,2])
h1 = h1.subs(H[2,0], H[0,2])
print 'Symmetrized H = ',h1
print

e =  Trace(h1*Matrix(G)).doit()
print 'Trace = ',e

h00 = Symbol('h00')
h01 = Symbol('h01')
h02 = Symbol('h02')
h11 = Symbol('h11')
h12 = Symbol('h12')
h22 = Symbol('h22')
print
e = e.subs(H[0,0], h00)
e = e.subs(H[0,1], h01)
e = e.subs(H[0,2], h02)
e = e.subs(H[1,1], h11)
e = e.subs(H[1,2], h12)
e = e.subs(H[2,2], h22)
print 'Trace = ',e
print
e2 =  e.collect([h00,h01,h02,h12,h11,h22])
print 'Trace =',e2

g = IndexedBase('g')
e2 = e2.subs(G[0,0], g[0])
e2 = e2.subs(G[0,1]+G[1,0], g[1])
e2 = e2.subs(G[0,2]+G[2,0], g[2])
e2 = e2.subs(G[1,1], g[1])
e2 = e2.subs(G[1,2]+G[2,1], g[4])
e2 = e2.subs(G[2,2], g[5])
print
print 'Replace with symmetrized G'
print 'Trace =',e2

vH = Matrix([[1.0, 2.0, 3.0],
             [2.0, 4.4, 1.1],
             [3.0, 1.1, 0.9]])
vG = Matrix([[0.1, 0.2, 0.3],
             [1.4, 2.3, 8.0],
             [0.9, 1.4, 2.3]])

tr = Trace(vH*vG).doit()
print 'Trace = ',tr

print
print 'Concrete values for unit test'
print '// Code from here to the end of the function generate by gen_trace.py'
print
print '  double h00 = %g;'%vH[0,0]
print '  double h01 = %g;'%vH[0,1]
print '  double h02 = %g;'%vH[0,2]
print '  double h11 = %g;'%vH[1,1]
print '  double h12 = %g;'%vH[1,2]
print '  double h22 = %g;'%vH[2,2]
print
print '  double gg[6] = {%g, %g, %g, %g, %g, %g};'%(vG[0,0], vG[0,1] + vG[1,0], vG[0,2] + vG[2,0], vG[1,1], vG[1,2] + vG[2,1], vG[2,2])
print
print '  double tr = SymTrace(h00, h01, h02, h11, h12, h22, gg);'
print '  REQUIRE(tr == Approx(%g));'%tr
print


#!/usr/bin/env python

# Software License Agreement (MIT License)
#
# Copyright (c) 2020, tri_star
# All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Author: Meiying Qin, Jake Brawer

# https://github.com/christophhagen/averaging-quaternions

import numpy
import numpy.matlib as npm

# Q is a Nx4 numpy matrix and contains the quaternions to average in the rows.
# The quaternions are arranged as (w,x,y,z), with w being the scalar
# The result will be the average quaternion of the input. Note that the signs
# of the output quaternion can be reversed, since q and -q describe the same orientation
def averageQuaternions(Q):
    # Number of quaternions to average
    M = Q.shape[0]
    A = npm.zeros(shape=(4,4))

    for i in range(0,M):
        q = Q[i,:]
        # multiply q with its transposed version q' and add A
        A = numpy.outer(q,q) + A

    # scale
    A = (1.0/M)*A
    # compute eigenvalues and -vectors
    eigenValues, eigenVectors = numpy.linalg.eig(A)
    # Sort by largest eigenvalue
    eigenVectors = eigenVectors[:,eigenValues.argsort()[::-1]]
    # return the real part of the largest eigenvector (has only real part)
    return numpy.real(eigenVectors[:,0].A1)

# Average multiple quaternions with specific weights
# The weight vector w must be of the same length as the number of rows in the
# quaternion maxtrix Q
def weightedAverageQuaternions(Q, w):
    # Number of quaternions to average
    M = Q.shape[0]
    A = npm.zeros(shape=(4,4))
    weightSum = 0

    for i in range(0,M):
        q = Q[i,:]
        A = w[i] * numpy.outer(q,q) + A
        weightSum += w[i]

    # scale
    A = (1.0/weightSum) * A

    # compute eigenvalues and -vectors
    eigenValues, eigenVectors = numpy.linalg.eig(A)

    # Sort by largest eigenvalue
    eigenVectors = eigenVectors[:,eigenValues.argsort()[::-1]]

    # return the real part of the largest eigenvector (has only real part)
    return numpy.real(eigenVectors[:,0].A1)


import numpy

def rk_butcher_4():
    A = numpy.array([[0,0,0,0],[.5,0,0,0],[0,.5,0,0],[0,0,1,0]])
    b = numpy.array([1/6, 1/3, 1/3, 1/6])
    return A, b

def ode_rkexplicit(f, u0, butcher=None, tfinal=1, h=.1):

    if butcher is None:
        A, b = rk_butcher_4()
    else:
        A, b = butcher
    c = numpy.sum(A, axis=1)
    s = len(c)
    u = u0.copy()
    t = 0
    hist = [(t,u0)]

    while t < tfinal:
        if tfinal - t < 1.01*h:
            h = tfinal - t
            tnext = tfinal
        else:
            tnext = t + h
        h = min(h, tfinal - t)

        fY = numpy.zeros((len(u0), s))
        for i in range(s):
            Yi = u.copy()
            for j in range(i):
                Yi += h * A[i,j] * fY[:,j]
            fY[:,i] = f(t + h*c[i], Yi)
        u += h * fY @ b
        t = tnext
        hist.append((t, u.copy()))
    return hist

limiters = []

def limappend(lim):
    limiters.append(lim)
    return lim

@limappend
def limit_zero(r):
    return 0*r

@limappend
def limit_none(r):
    return 0*r+1

@limappend
def limit_minmod(r):
    return numpy.maximum(numpy.minimum(2*r, 2*(1-r)), 0)

@limappend
def limit_sin(r):
    return numpy.where(numpy.all((0 < r, r < 1), axis=0),
                      numpy.sin(numpy.pi*r),
                      0)

@limappend
def limit_vl(r):
    return numpy.maximum(4*r*(1-r), 0)

@limappend
def limit_bj(r):
    return numpy.clip(numpy.minimum(4*r, 4*(1-r)), 0, 1)


import numpy as np
from scipy.special import expit

hidden_size=2
input_size=2
classes=1
X=np.array(([[0, 0],[0, 1],[1, 0],[1, 1]]))
Wh=np.random.randn(hidden_size,input_size)
bh=np.random.randn(hidden_size,1)
Wo=np.random.randn(1,hidden_size)
bo=np.random.randn(classes,1)

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

def lossFunc():

	loss=0;

	dbo=np.zeros_like(bo)
	dWo=np.zeros_like(Wo)
	hg12=np.zeros_like(bh)
	dbh=np.zeros_like(bh)
	dh12tmp=np.zeros_like(bh)
	dh12=np.zeros_like(bh)
	dWh=np.zeros_like(Wh)

	for i in xrange(4):

		#forward propogation
		h12 = np.dot(Wh, X[i,:][np.newaxis].T) + bh
		hg=expit(h12)
		h3=np.dot(Wo,hg) + bo
		hg3=expit(h3)
		y=hg3
		loss+=0.5*(T[i]-y)*(T[i]-y)

		#backward propogation
		de=-(T[i]-y)
		dhg3=hg3*(1-hg3)
		dy=dhg3*de
		dbo+=dy
		dWo+=np.dot(dy,hg.T)
		dh12=np.dot(Wo.T,dy)
		dh12tmp=hg*(1-hg)*dh12
		dbh+=dh12tmp
		xsub=X[i,:]
		xsub=np.matrix(xsub)
		dWh+=np.dot(dh12tmp, xsub)

	#np.clip(dWh,-2,2,dWh)
	#np.clip(dWo,-2,2,dWo)
	#np.clip(dbh,-2,2,dbh)
	#np.clip(dbo,-2,2,dbo)
	return loss,dWh,dWo,dbh,dbo

learning_rate=0.2
f2=open("./log_data/sig_0.2.dat",'w')

for ep in xrange(2000):
	loss,dWh,dWo,dbh,dbo=lossFunc()

	Wh+=-learning_rate*dWh
	Wo+=-learning_rate*dWo
	bh+=-learning_rate*dbh
	bo+=-learning_rate*dbo

	if(ep%100==0):
		print 'Loss is %f' %(loss)

		f2.write(str(ep))
		f2.write("\t")
		tmp=str(loss).strip('[]')
		f2.write(str(tmp))
		f2.write("\n")

f2.close()

for i in xrange(4):
		#forward propogation
		h12 = np.dot(Wh, X[i,:][np.newaxis].T) + bh
		hg=expit(h12)
		#print hg
		h3=np.dot(Wo,hg) + bo
		hg3=expit(h3)
		print hg3


import numpy as np

def normalize_vec(vals, minval=None, maxval=None):
    """Normalize values in vec to 0-1 range"""
    minval = vals.min()   if minval is None else minval
    maxval = vals.max()+1 if maxval is None else maxval
    return (vals-minval) / (maxval-minval)

def general_heatmat(xvec, yvec, xsize=100, ysize=100,
            xmin=None, xmax=None, ymin=None, ymax=None):
    """Create a 2D heatmap showing densities of x-y combinations"""
    mask = np.isfinite(xvec) & np.isfinite(yvec)
    xvec = xvec[mask]
    yvec = yvec[mask]
    xint = normalize_vec(xvec, xmin, xmax)
    yint = normalize_vec(yvec, ymin, ymax)
    mask = (xint>=0) & (xint<=1) & (yint>=0) & (yint<=1)
    xint = (xint[mask] * (xsize-1)).astype(int)
    yint = (yint[mask] * (ysize-1)).astype(int)
    ijvec = np.ravel_multi_index((xint,yint),(xsize,ysize))
    return np.bincount(ijvec, minlength=xsize*ysize).reshape(xsize,ysize).T

def heatmat(df, imax=None, jmax=None):
    """Create a 2D heatmap showing densities of x-y combinations"""
    xint = df.xpos.astype(np.int32)
    yint = df.ypos.astype(np.int32)
    if imax is None:
        if hasattr(df, "imax"):
            imax = df.imax
        else:
            imax = xint.max()+1
    if jmax is None:
        if hasattr(df, "jmax"):
            jmax = df.jmax
        else:
            jmax = yint.max()+1
    ijvec = np.ravel_multi_index((xint,yint),(imax,jmax))
    return np.bincount(ijvec, minlength=imax*jmax).reshape(imax,jmax).T

def trajs_to_grid(df, col_name, imax=None, jmax=None, data_source=None):
    """Create a 2D heatmap showing densities of x-y combinations"""
    xint = df.xpos.astype(np.int32)
    yint = df.ypos.astype(np.int32)
    if imax is None:
        if hasattr(df, "imax"):
            imax = df.imax
        else:
            imax = xint.max()+1
    if jmax is None:
        if hasattr(df, "jmax"):
            jmax = df.jmax
        else:
            jmax = yint.max()+1
    ijvec = np.ravel_multi_index((xint,yint),(imax,jmax))

    sum = np.bincount(ijvec, weights=df[col_name], minlength=imax*jmax)
    cnt = np.bincount(ijvec, minlength=imax*jmax)
    return (sum/cnt).reshape(imax,jmax).T


# Setting up program
import cv2
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import multivariate_normal
from matplotlib.patches import Ellipse

# Setting seed for reproducibility
UBIT = 'damirtha'
np.random.seed(sum([ord(c) for c in UBIT]))

# Function to apply GMM clustering given X, Mu, Sigma, K and Pi
def GMMclustering(Mu, Sigma, Pi, K,  X, data = 'X', itr = 5):
    c=0
    for j in range(itr):
        pXgivenC = []
        for i in range(K):
            pXgivenC.append(multivariate_normal.pdf(X, mean = Mu[i], cov = Sigma[i]))
        pXgivenC = np.transpose(np.array(pXgivenC))
        pX = np.array(np.sum(np.multiply(pXgivenC,Pi), axis= 1))
        pCgivenX = np.multiply(np.transpose(np.multiply(pXgivenC,Pi)),pX ** -1)
        for i in range(K):
            Mu[i] = np.average(X, axis = 0, weights = pCgivenX[i])
            Sigma[i] = np.cov(X, rowvar = 0, aweights = pCgivenX[i], ddof = 0)
        Pi = np.sum(pCgivenX, axis = 1)/len(X)
        if data == 'faithful':
            plt.figure()
            plt.plot(X[:,0], X[:,1], 'bo', markersize = 3)
            color = ['r','g','b']
            # Plot a transparent 3 standard deviation covariance ellipse

            for i in range(K):
                plot_cov_ellipse(pos = Mu[i], cov = Sigma[i], nstd=3, alpha=0.5, color= color[i])

            plt.savefig('Results/task3_gmm_iter'+str(j+1)+'.jpg')
        c+=1
    return Mu, Sigma, Pi, c

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

# Initializing GMM parameters
X = np.array([[5.9,3.2],[4.6,2.9],[6.2,2.8],[4.7,3.2],[5.5,4.2],[5.0,3.0],[4.9,3.1],[6.7,3.1],[5.1,3.8],[6.0,3.0]])
K = 3
Mu = np.array([[6.2,3.2],[6.6,3.7],[6.5,3.0]])
Sigma = np.array([[[0.5, 0], [0, 0.5]]]*3)
Pi = np.array([1/3, 1/3, 1/3])

# Computing EM algorithm to get soft clusters
Mu, Sigma, Pi, c = GMMclustering(Mu, Sigma, Pi, K,  X, 'X', 1)
print('Mu for 3.5a:')
print(Mu)

# Bonus task 3.5 b, opening dataset and initializing GMM parameters
file = open('data/faithful.dat','r')
c=0; data=[]
for line in file:
    c+=1
    if c > 26:
        data.append(np.array(line.split()[1:3]))
data = np.array(data)
data = data.astype(np.float)
K = 3
Mu = np.array([[4.0,81],[2.0,57],[4.0, 71]])
Sigma = np.array([[[1.3, 13.98], [13.98, 184.82]]]*K)
Pi = np.array([1/3]*K)

# Computing EM algorithm to get soft clusters
Mu, Sigma, Pi, c = GMMclustering(Mu, Sigma, Pi, K,  data, 'faithful', 5)


from numpy import *
from matplotlib import *
from pylab import *
import matplotlib.lines as mlines

if __name__ == "__main__":
    rc('text', usetex=True)
    rc('font', family='serif')
    fs = 20
    ### Example 1
    x = arange(-5,5,0.1)

    # r > 0
    fig= figure(1)
    ax = fig.add_subplot(313)
    frame1 = plt.gca()
    hold
    r = 10
    xdot = r*x - x**3
    ax.plot(x,xdot,'b-')
    ylim([min(xdot),max(xdot)+3])
    xlim([min(x),max(x)])
    ax.plot(x,zeros_like(x),'k-')
    ax.plot(sqrt(r),0,'bo',fillstyle='full',mec='b')
    ax.plot(-sqrt(r),0,'bo',fillstyle='full',mec='b')
    ax.plot(0,0,'bo',mfc='none',mec='b')
    ax.set_title(r'$r > 0$')
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    ax.annotate('', xy=(-2, 0), xytext=(-1, 0),
             arrowprops=dict(facecolor='black', shrink=0.04, width=1),
             )
    ax.annotate('', xy=(2, 0), xytext=(1, 0),
             arrowprops=dict(facecolor='black', shrink=0.04, width=1),
             )

    # lft = mlines.Line2D.fillStyles
    # r = 0
    ax = fig.add_subplot(312)
    frame1 = plt.gca()
    r = 0
    xdot = r*x - x**3
    ax.plot(x,xdot,'b-')
    ax.plot(0,0,'bo',fillstyle='full',mec='b')
    ylim([min(xdot),max(xdot)+3])
    xlim([min(x),max(x)])
    ax.plot(x,zeros_like(x),'k-')
    ax.set_title(r'$r = 0$')
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    ax.annotate('', xy=(-2, 0), xytext=(-3, 0),
             arrowprops=dict(facecolor='black', shrink=0.04, width=1),
             )
    ax.annotate('', xy=(2, 0), xytext=(3, 0),
                arrowprops=dict(facecolor='black', shrink=0.04, width=1),
                )

    # r < 0
    ax = fig.add_subplot(311)
    frame1 = plt.gca()
    r = -5
    xdot = r*x - x**3
    ax.plot(x,xdot,'b-')
    ax.plot(0,0,'bo',fillstyle='full',mec='b')
    ax.plot(r,0,'bo', mfc='none',mec='b')
    ylim([min(xdot)-1,max(xdot)+3])
    xlim([min(x),max(x)])
    ax.plot(x,zeros_like(x),'k-')
    ax.set_title(r'$r < 0$')
    xlabel(r'$x$',fontsize=fs)
    ylabel(r'$\dot{x}$',fontsize=fs)
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    ax.annotate('', xy=(-2, 0), xytext=(-3, 0),
             arrowprops=dict(facecolor='black', shrink=0.04, width=1),
             )
    ax.annotate('', xy=(2, 0), xytext=(3, 0),
                arrowprops=dict(facecolor='black', shrink=0.04, width=1),
                )

    # bifurcation diagram
    x = arange(-5,5,0.1)
    fig= figure(2)
    ax = fig.add_subplot(111)
    frame1 = plt.gca()
    hold
    rpp = sqrt(arange(0,5,0.1))
    rpn = -1*sqrt(arange(0,5,0.1))
    rn = zeros_like(arange(-5,0,0.1))
    rzp = zeros_like(arange(0,5,0.1))
    rzn = zeros_like(arange(-5,0,0.1))
    ax.plot(arange(-5,0,0.1),rzn,'b-')
    ax.plot(arange(0,5,0.1),rzp,'b--')
    ax.plot(arange(0,5,0.1),rpp,'b-')
    ax.plot(arange(0,5,0.1),rpn,'b-')
    axvline(x=0,color='k')
    ax.set_title(r'$x^* = 0$')
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    # subcritical

    # bifurcation diagram
    x = arange(-5,5,0.1)
    fig= figure(3)
    ax = fig.add_subplot(111)
    frame1 = plt.gca()
    hold
    rpp = sqrt(arange(0,5,0.1))
    rpn = -1*sqrt(arange(0,5,0.1))
    rn = zeros_like(arange(-5,0,0.1))
    rzp = zeros_like(arange(0,5,0.1))
    rzn = zeros_like(arange(-5,0,0.1))
    ax.plot(arange(-5,0,0.1),rzn,'b-')
    ax.plot(arange(0,5,0.1),rzp,'b--')
    ax.plot(arange(-5,0,0.1),rpp[::-1],'b--')
    ax.plot(arange(-5,0,0.1),rpn[::-1],'b--')
    axvline(x=0,color='k')
    ax.set_title(r'$x^* = 0$')
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    # stabilize
    x = arange(-5,5,0.1)
    fig= figure(4)
    ax = fig.add_subplot(111)
    frame1 = plt.gca()
    hold
    rpp = sqrt(arange(0,5,0.1))
    rpn = -1*sqrt(arange(0,5,0.1))
    rn = zeros_like(arange(-8,0,0.1))
    rzp = zeros_like(arange(0,5,0.1))
    rzn = zeros_like(arange(-8,0,0.1))
    rps = sqrt(arange(0,10,0.1)) + rpp[-1]
    rpns = -1*sqrt(arange(0,10,0.1)) + rpn[-1]

    ax.plot(arange(-8,0,0.1),rzn,'b-')
    ax.plot(arange(0,5,0.1),rzp,'b--')
    ax.plot(arange(-5,0,0.1),rpp[::-1],'b--')
    ax.plot(arange(-5,0,0.1),rpn[::-1],'b--')

    ax.plot(arange(-5,5,0.1),rps,'b-')
    ax.plot(arange(-5,5,0.1),rpns,'b-')
    axvline(x=0,color='k')
    ax.set_title(r'$x^* = 0$')
    frame1.axes.get_xaxis().set_visible(False)
    frame1.axes.get_yaxis().set_visible(False)

    show()


import numpy as np
from fft import dft,fft,inv_dft,inv_fft
import time
mat_size = 512
upper_limit = 255
lower_limit=0
matrix = np.random.randint(lower_limit,upper_limit,size=(mat_size,mat_size))

def twodfft(matrix):
    # intialize two matrix of same size of orginially generated random matrix
    # one matrix to store the result of FFT of row vectors
    # another one to store the result of FFT of column vectors of the matrix generated in the previous step
    z_rows = np.zeros((mat_size,mat_size),dtype=complex)
    z_cols = np.zeros((mat_size,mat_size),dtype=complex)
    # print(z_rows)
    # print(z_cols)
    # store the FFT of row vectors in z_rows
    # every row of z_rows contains FFT of row vector of "matrix"
    for i in range(len(matrix)):
        z_rows[i] = fft(matrix[i])

    # apply FFT on column vectors and store the resultant column vector in z_cols as columns
    # every column of z_cols contains the FFT of column vectors of z_rows
    for i in range(len(z_rows[0])):
        z_cols[:,i] = fft(z_rows[:,i])
    return z_cols

#print(z_cols)
def inv_2dfft(z_cols):
    i_rows = np.zeros((mat_size,mat_size),dtype=complex)
    i_cols = np.zeros((mat_size,mat_size),dtype=complex)

    for i in range(len(z_cols[0])):
        i_rows[i] = inv_fft(z_cols[i])
    for i in range(len(z_cols[0])):
        i_cols[:,i] = inv_fft(i_rows[:,i])
    return i_cols

#driver program
z_cols = twodfft(matrix)
i_cols = inv_2dfft(z_cols)
start_time = time.time()
if(np.allclose(z_cols, np.fft.fft2(matrix))):
    print("The output of the 2D fft matches the numpy function's output for the 2D fft")

if(np.allclose(i_cols, np.fft.ifft2(z_cols))):
    print("The output of the 2D fft matches the numpy function's output for the 2D fft")
print(time.time() - start_time)