#This problem was asked by LinkedIn.[Hard]

#Given a list of points and an integer k, find the nearest k points from the origin.

# For example, given the list of points [(0, 0), (5, 4), (3, 1)] and k = 2, return [(0, 0), (3, 1)].
#Solution By Sort:
from typing import List

class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        points.sort(key = lambda P:P[0]**2+P[1]**2)
        return points[:K]
sol=Solution()
points = [[3, 3], [5, -1], [-2, 4]]
#points=[[0, 0], [5, 4], [3, 1]]
K = 2
print(f"The nearest {K} neighbors {points} from the origin are{sol.kClosest(points, K)}")
#result1=[[-2, 4], [3, 3]]
#result2=[(0, 0), (3, 1)]

#Time complexity: O(NlogN)
#Sort the list according to the distance to origin.
#Apparently, we did more than the question asked. We sorted all the distance, the question only ask for top k.
#To improve time complexity, we need to think about how to get we ask without extra effort. This is where heap data structure comes in.

# Solution by Max Heap Approach:
import heapq
from typing import List
class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:

        heap = []
        for point in points:
            dist = point[0] * point[0] + point[1] * point[1]
            heapq.heappush(heap, (-dist, point))
            if len(heap) > K:
                heapq.heappop(heap)

        return [tuple[1] for tuple in heap]
sol=Solution()
#points2 = [[3, 3], [5, -1], [-2, 4]]
points1=[[0, 0], [5, 4], [3, 1]]
K = 2
print(f"The nearest {K} neighbors {points} from the origin are{sol.kClosest(points, K)}")
#result1=[[-2, 4], [3, 3]]
#result2=[(0, 0), (3, 1)]

# Time compiexity: O(NlogK)
# heapq is a binary heap, with O(log n) push and O(log n) pop. n is the size of the minimum heap. In this case, n = K.
# So the time complexity is Nlog(K)
#Solution by quick sort:
class Solution:
    def kClosest(self, points, K):
        self.sort(points, 0, len(points)-1, K)
        return points[:K]

    def sort(self, points, l, r, K):
        if l < r:
            p = self.partition(points, l, r)
            if p == K:
                return
            elif p < K:
                self.sort(points, p+1, r, K)
            else:
                self.sort(points, l, p-1, K)

    def partition(self, points, l, r):
        pivot = points[r]
        a = l
        for i in range(l, r):
            if (points[i][0]**2 + points[i][1]**2) <= (pivot[0]**2 + pivot[1]**2):
                points[a], points[i] = points[i], points[a]
                a += 1
        points[a], points[r] = points[r], points[a]
        return a
sol=Solution()
points = [[3, 3], [5, -1], [-2, 4]]
#points=[[0, 0], [5, 4], [3, 1]]
K = 2
print(f"The nearest {K} neighbors {points} from the origin are{sol.kClosest(points, K)}")
#result1=[[-2, 4], [3, 3]]
#result2=[(0, 0), (3, 1)]
#Time Complexity: Quick Sort - O(N) on average

#Solution by Kd-tree Approach for large no. of queries:
# What if I have 10 million points now and I have to perform the search 10000 times? How would you optimize it?
from scipy import spatial
from typing import List

class Solution:
    def kClosest(self, points: List[List[int]], K: int) -> List[List[int]]:
        tree = spatial.KDTree(points)
		# x is the origin, k is the number of closest neighbors, p=2 refers to choosing l2 norm (euclidean distance)
        distance, idx = tree.query(x=[0,0], k=K, p=2)
        return [points[i] for i in idx] if K > 1 else [points[idx]]
sol=Solution()
#points = [[3, 3], [5, -1], [-2, 4]]
points=[[1, 3], [5, 4], [3, 1]]
K = 2

print(f"The nearest {K} neighbors {points} from the origin are{sol.kClosest(points, K)}")
#result1=[[-2, 4], [3, 3]]
#result2=[(1, 3), (3, 1)]

# kd-tree has the following comeplexity:

# Build the tree - O(NlogN), building the tree requires presorting the points and find the medians (but we only need to do this once).
# Search, Insert, Delete - O(logN), similar to how a normal binary tree works (with a tree balancing mechanism)
# Now, as we can see, it greatly reduces the time complexity for each nearest neighbor query to O(logN), and if we need to find the K closest points, the total complexity will be O(KlogN).
# This is great if we have a lot of points and we are only interested in a few neighbors.


# coding: utf-8

# # Principal Component Analysis (PCA)

# The function **pca** computes the principal components transformation matriz of a given data set.
# The input is the data set organized as one row per sample e columns with attributes of each sample.
# The output are three: the eigenvector matriz, the diagonal of the covariance matriz and the sample mean.

# ## Function definition

# In[11]:

import numpy as np

def pca(X):
    '''
    features are in the columns
    samples are in the rows
    '''
    n, dim = X.shape
    mu = X.mean(axis=0)
    Xc = X - mu                    # 0 mean

    C = (Xc.T).dot(Xc)/(n-1)        # Covariance matrix
    e,V = np.linalg.eigh(C)       # eigenvalues and eigenvectors of the covariance matrix
    indexes = np.argsort(e)[::-1] # sorting eigenvalues from largest
    e  = e [indexes]              # update e and V
    V = V[:,indexes]
    return V,e,mu

# ## Example -  Bidimensional Gaussian distribution

# In[1]:

testing = (__name__ == "__main__")

if testing:
    get_ipython().system(' jupyter nbconvert --to python pca.ipynb')
    import numpy as np
    import sys,os
    get_ipython().magic('matplotlib inline')
    import matplotlib.image as mpimg
    import matplotlib.pyplot as plt
    ea979path = os.path.abspath('../../')
    if ea979path not in sys.path:
        sys.path.append(ea979path)
    import ea979.src as ia

# ### Creating the data:

# A matrix of 50 samples of (x,y) coordinates is created. The samples are
# generated from a Gaussian distribution rotated by 60 degrees.
#

# In[2]:

if testing:
    np.random.seed(58)
    mean = [1,3]
    cov = np.array([
          [ 0.14,     0.2078461],
          [ 0.2078461,0.38     ]
          ])
    X = np.random.multivariate_normal(mean,cov,50)
    print(X.shape)
    print(X[:5,:])

# In[3]:

if testing:
    Xm = X.mean(axis=0)
    Cr = ((X-Xm).T).dot(X-Xm)/(X.shape[0]-1)  # Covariance matrix (explicit)
    Crr= np.cov(X,rowvar=False)               # Covariance matrix (NumPy)
    print('Xm:\n',Xm)
    print('Cr == Crr?:',np.allclose(Cr,Crr))
    print('Cr:\n',Crr)

# In[4]:

if testing:
    plt.scatter(X[:,0],X[:,1], c='r', marker='x')
    plt.axis('equal')
    plt.title('Gaussian distribution')
    plt.show()

# ## Computing the matrix transformation

# In[5]:

if testing:
    V,e,mu_X = ia.pca(X)
    sigma = np.sqrt(e)

    # Plotando os dados e seus autovetores no sistema de coordenadas original
    arrowlist = [[mu_X[0], mu_X[1], sigma[0]*V[0,0], sigma[0]*V[1,0]],
                 [mu_X[0], mu_X[1], sigma[1]*V[0,1], sigma[1]*V[1,1]]]

    plt.scatter(X[:,0],X[:,1],
                     c = 'r',
                     marker = 'x')
    for arrow in arrowlist:
        plt.arrow(*arrow,head_width=0.05, head_length=0.1, fc='k', ec='k')

    plt.axis('equal')
    plt.title('eigenvectors plotted')
    plt.show()

# In[6]:

if testing:
    print('V:\n',V)
    print('e:\n',e)
    print('sigma:\n',sigma)
    print('mu_X\n',mu_X)

    Xr = (X - mu_X).dot(V)               # Transforming X

    Cr= np.cov(Xr,rowvar=False)         # Covariance matrix (NumPy)

    print('sigma**2', sigma**2)
    print('Cr:\n', Cr)

# In[7]:

if testing:
    Vr, e_r, mu_Xr = ia.pca(Xr)
    sigma_r = np.sqrt(e_r)

    # Plotando os dados e seus autovetores no sistema de coordenadas original
    arrowlist = [[mu_Xr[0], mu_Xr[1], sigma_r[0]*Vr[0,0], sigma_r[0]*Vr[1,0]],
                 [mu_Xr[0], mu_Xr[1], sigma_r[1]*Vr[0,1], sigma_r[1]*Vr[1,1]]]

    plt.scatter(Xr[:,0],Xr[:,1],
                     c = 'r',
                     marker = 'x')
    for arrow in arrowlist:
        plt.arrow(*arrow,head_width=0.05, head_length=0.1, fc='k', ec='k')

    plt.axis('equal')
    plt.title('Transformed distribution with eigenvectors')
    plt.show()

# ## Method

# 1. Build matrix $X_{(n \times m)}$, where n (rows) gives the number of samples and m (columns) gives the number of attributes.
#    m gives the dimensionality of the problem:
#
# $$   X = \begin{bmatrix} X_{11} & \cdots & X_{1m} \\
#    \vdots & \ddots & \vdots \\
#    X_{n1} & \cdots & X_{nm} \\
#    \end{bmatrix}
# $$

# 2. Compute the mean values of every attributes of all samples:
#
# $$   \overline{X} = \frac{1}{n} \begin{bmatrix}  \sum_{i = 1}^n X_{i1} &  \sum_{i = 1}^n X_{i2}  & \cdots &
#    \sum_{i = 1}^n X_{im} \end{bmatrix}
# $$

# 3. Compute the covariance matrix:
#
# $$
#    C = \frac{1}{n-1}(X - \overline{X})^T(X - \overline{X})
# $$

# 4. Compute the eigenvalues ($e_i$) and the eigenvectors ($V_i$) which diagonalizes the covariance matrix ($\Sigma$):
#
# $$
#    \text{ Eigen values } = |C - eI|  = 0 \\
#    \text{ Eigen vectors } = C V_i = e_i V_i
# $$

# 5. The transformation matrix (V) of the PCA is built as following: the first column is the eigenvector related to the largest eigenvalue,
#    the second column with the eigenvector related to the second largest eigenvalue and so.

# The data transformation to the new coordinates system is given by:
#
# $$   X_{new} = (X - \overline{X}) V $$
#
# The eigenvalues $e_i$ give the variance of the data projected in the new coordinates system.
# To compute the variance percentage related to each eigenvector direction of the transformed coordinates system,
# divide the corresponding eigenvalue by the sum of all eigenvalues:

# $$   Var\%(e_j) = \frac{e_j}{\sum_{i=1}^m e_i}  $$
#
#

# ## References

# - [PCA - Wikipedia](http://en.wikipedia.org/wiki/Principal_component_analysis)
# - [Karhunen-Loeve theorem](https://en.wikipedia.org/wiki/Karhunen%E2%80%93Lo%C3%A8ve_theorem)
# - [Eigen decomposition of a matrix - Wikipedia](http://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix)

# ## Contributions
#
# - Roberto Medeiros de Souza, Sept 2013

# In[ ]:


import numpy as np

def coding_problem_31(s, t, debt=0):
    """
    Given two strings, compute the edit distance between them.
    The edit distance between two strings refers to the minimum number of character insertions, deletions, and
    substitutions required to change one string to the other.
    Example:

    >>> coding_problem_31("kitten", "sitting")  # k>>s, e>>i, +g
    3
    >>> coding_problem_31("kitten", "cat")  # k>>c, i>>a, -ten
    5
    >>> coding_problem_31("black", "white")
    5
    >>> coding_problem_31("top", "dog")
    2
    """
    if not s or not t:
        return len(s) + len(t) + debt

    i = coding_problem_31(s, t[1:], debt + 1)  # insertion
    d = coding_problem_31(s[1:], t, debt + 1)  # deletion
    s = coding_problem_31(s[1:], t[1:], debt + (s[0] != t[0]))  # substitution / matching character
    return min(i, d, s)

def coding_problem_32(exchange_matrix):
    """
    Suppose you are given a table of currency exchange rates, represented as a 2D array. Determine whether there is a
    possible arbitrage: that is, whether there is some sequence of trades you can make, starting with some amount A of
    any currency, so that you can end up with some amount greater than A of that currency.
    There are no transaction costs and you can trade fractional quantities.

    >>> em = [[1, 2, 3], [1./2, 1, 3./2], [1./3, 2./3, 1]]
    >>> coding_problem_32(em)
    True

    >>> em[0][2] = 2.98
    >>> coding_problem_32(em)
    False

    Note: idea is that given a single row in the currency exchange matrix, it is possible to generate the entire 2D
    array. Any difference between the given currency exchange matrix and the computed one implies the possibility of
    arbitration.

    For example, for five currencies and given the exchange rates from the first to the other 4 [b, c, d, e]:

      |  A   B   C   D   E
    --+------------------
    A |  1   b   c   d   e
    B | 1/b  1  c/b d/b e/b
    C | 1/c b/c  1  d/b e/c
    D | 1/d b/d c/d  1  e/d
    E | 1/e b/e c/e d/e  1

    Since floating point quantities are involved, we need to test for approximate equality.
    """
    em = np.array(exchange_matrix)  # ideally, we should test if the exchange_matrix is well-formed
    cem = np.vstack([em[0, :] / em[0, n] for n in range(len(em))])  # computed exchange_matrix
    return np.allclose(em, cem)

def coding_problem_33(arr):
    """
    Compute the running median of a sequence of numbers. That is, given a stream of numbers, print out the median of
    the list so far on each new element. Recall that the median of an even-numbered list is the average of the two
    middle numbers. Example:

    >>> coding_problem_33([2, 1, 5, 7, 2, 0, 5])
    [2.0, 1.5, 2.0, 3.5, 2.0, 2.0, 2.0]

    Note: cheating with numpy below, but I see no reason to make it more complicated given the request. An efficient
    implementation would keep sorted in place the first n elements and do an insertion sort pass each time.
    """
    return [np.median(arr[:n + 1]) for n in range(len(arr))]

def coding_problem_34(s):
    """
    Given a string, find the palindrome that can be made by inserting the fewest number of characters as possible
    anywhere in the word. If there is more than one palindrome of minimum length that can be made, return the
    lexicographically earliest one (the first one alphabetically).
    Examples:

    >>> coding_problem_34("race")
    'ecarace'
    >>> coding_problem_34("google")
    'elgoogle'
    >>> coding_problem_34("aibohphobia")
    'aibohphobia'

    Note: this is similar to #31.
    For each given word w, there are 2*len(w)-1 possible palindromes made using as centers either a character (len(w))
    or the location between two characters (len(w)-1).
    """
    def recurse(palindrome, before, after):
        if not before or not after:
            return after[::-1] + before + palindrome + before[::-1] + after

        if before[-1] == after[0]:
            return recurse(after[0] + palindrome + after[0], before[:-1], after[1:])

        from_before = recurse(before[-1] + palindrome + before[-1], before[:-1], after)
        from_after = recurse(after[0] + palindrome + after[0], before, after[1:])
        if len(from_before) == len(from_after):
            return min(from_before, from_after)  # same length, pick lexicographically smaller

        return (from_before, from_after)[len(from_before) > len(from_after)]  # pick shortest

    def pivots(word):
        for index in range(len(word)):
            yield (word[index], word[:index], word[index+1:])
        for index in range(1, len(word)):
            yield ('', word[:index], word[index:])

    candidates = [recurse(palindrome, before, after) for palindrome, before, after in pivots(s)]
    return min(filter(lambda candidate: len(candidate) == min(map(len, candidates)), candidates))

def coding_problem_35(rgbs):
    """
    Given an array of strictly the characters 'R', 'G', and 'B', segregate the values of the array so that all the 'R's
    come first, the 'G's come second, and the 'B's come last. You can only swap elements of the array.
    Do this in linear time and in-place. For example:

    >>> coding_problem_35(['G', 'B', 'R', 'R', 'B', 'R', 'G'])
    ['R', 'R', 'R', 'G', 'G', 'B', 'B']

    The problem can be solved by (insertion) sorting as follows.
    This solution does not take advantage of the expected high number of equal value elements.

    >>> rgbs = ['G', 'B', 'R', 'R', 'B', 'R', 'G']
    >>> for i in range(len(rgbs) - 1):
    ...     for j in range(i + 1, len(rgbs)):
    ...         if rgbs[i] < rgbs[j]:  # 'R' > 'G' > 'B'
    ...             rgbs[i], rgbs[j] = rgbs[j], rgbs[i]
    >>> rgbs
    ['R', 'R', 'R', 'G', 'G', 'B', 'B']

    Sorting as described above has a complexity of O(n^2).
    The following solution does at most two passes, and therefore is O(n).
    """
    left_index, right_index = 0, len(rgbs) - 1
    while True:  # move Rs to front

        while rgbs[left_index] == 'R' and left_index < right_index:  # advance to first non R
            left_index += 1

        while rgbs[right_index] != 'R' and left_index < right_index:  # regress to last R
            right_index -= 1

        if left_index >= right_index:
            break

        rgbs[left_index], rgbs[right_index] = rgbs[right_index], rgbs[left_index]

    right_index = len(rgbs) - 1
    while True:  # move Bs to tail

        while rgbs[left_index] != 'B' and left_index < right_index:  # advance to first B
            left_index += 1

        while rgbs[right_index] == 'B' and left_index < right_index:  # regress to last non B
            right_index -= 1

        if left_index >= right_index:
            break

        rgbs[left_index], rgbs[right_index] = rgbs[right_index], rgbs[left_index]

    return rgbs

if __name__ == '__main__':

    import doctest
    doctest.testmod(verbose=True)


import numpy as np

def find_pairs(f, step, a, b):
    x = a
    pairs = []
    while (x + step < b):
        if (f(x+step)/f(x) < 0):
             pairs.append([x, x+step])
        x += step
    return pairs

def bisection(f, pairs, tolerance):
    zeros = []
    for pair in pairs:
        mid = (pair[1]-pair[0])/2 + pair[0]
        while (abs(f(mid)) > tolerance):
            if (f(mid)/f(pair[0]) <0): pair[1] = mid
            else: pair[0] = mid
            mid = (pair[1]-pair[0])/2 + pair[0]
        zeros.append(mid)
    return zeros

def sinc(x):
    if (x ==0):
        return 1
    else:
        return np.sin(x)/x

pairs = find_pairs(sinc, 0.1, 0, 10)
print(pairs)
zeros = bisection(sinc, pairs, 1E-10)
print(zeros)
print(np.pi, 2.0*np.pi, 3.0*np.pi)


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

def x1_t(t,x1,x2,k,m):
    """Exact solution for mass 1 at time t based on positions
    x1, x2, and spring constant k. Assumes initial velocity
    is zero"""

    w = np.sqrt(k/m)
    a1 = (x1 + x2)/2.0
    a2 = (x1 - x2)/2.0
    return a1*np.cos(w*t) + a2*np.cos(np.sqrt(3)*w*t)

def x2_t(t,x1,x2,k,m):
    """Exact solution for mass 2 at time t based on positions
    x1, x2, and spring constant k. Assumes initial velocity
    is zero"""

    w = np.sqrt(k/m)
    a1 = (x1 + x2)/2.0
    a2 = (x1 - x2)/2.0
    return a1*np.cos(w*t) - a2*np.cos(np.sqrt(3)*w*t)

def vectorfield(w, t, p):
    """Defines the differential equations for the coupled
    spring-mass system. Arguments:
        w :  vector of the state variables: w = [x1,v1,x2,v2]
        t :  time
        p :  vector of the parameters: p = [m,k]
    """
    x1, v1, x2, v2 = w
    m, k = p

    # Create f = (x1',y1',x2',y2'):
    f = [v1, (-k * x1 + k * (x2 - x1)) / m, v2, (-k * x2 - k * (x2 - x1)) / m]

    return f

def plot_result(stoptime, x01, x02, k, m):

    # "vectorize" solutions to act on an array of times
    x1_sol = np.vectorize(x1_t)
    x2_sol = np.vectorize(x2_t)

    t, x1, v1, x2, v2 = np.loadtxt('coupled_masses.dat', unpack=True)
    plt.figure(1, figsize=(10, 3.5))
    plt.xlabel('t')
    plt.ylabel('x')
    plt.grid(True)
    plt.hold(True)

    # plot exact solutions
    time = np.linspace(0,stoptime,500)

    plt.plot(time, x1_sol(time,x01,x02,k,m), 'b-', linewidth=1)
    plt.plot(time, x2_sol(time,x01,x02,k,m), 'g-', linewidth=1)

    # plot numerical solutions
    plt.plot(t, x1, 'r*')
    plt.plot(t, x2, 'mo')

    plt.legend(('$x_{1,exact}$', '$x_{2,exact}$', '$x_{1,num}$',
                '$x_{2,num}$'))
    plt.title('Mass Displacements for the\nCoupled Spring-Mass System')
    plt.show()

def main():

    # Parameters and initial values
    # Mass and spring constant
    m = 1.0
    k = 1.0
    # Initial displacements
    x01 = 0.5
    x02 = 0.0
    # Initial velocities (remember exact solution relies on
    # v01 = v02 = 0)
    v01 = 0.0
    v02 = 0.0

    # ODE solver parameters
    abserr = 1.0e-8
    relerr = 1.0e-6
    stoptime = 10.0
    numpoints = 50

    # Create time samples for the output of the ODE solver
    t = np.linspace(0, stoptime, numpoints)

    # Pack up the parameters and initial conditions as lists/arrays:
    p =  [m, k]
    w0 = [x01, v01, x02, v02]

    # Call the ODE solver

    wsol = odeint(vectorfield, w0, t, args=(p,), atol=abserr, rtol=relerr)

    with open('coupled_masses.dat', 'w') as f:
        for t1, w1 in zip(t, wsol):
            print(f, t1, w1[0], w1[1], w1[2], w1[3])

    plot_result(stoptime, x01, x02, k,  m)

    return 0

if __name__ == "__main__":
    main()


import numpy as np
from numba import njit

@njit
def pairwise_square_l2_distance(Z):
    """
    Compute the pairwise L^2-distance matrix between all points in Z.
    inputs: Z is (mn,d) array
    output: (mn,mn) array of pairwise squared distances (L^2)
    https://stackoverflow.com/questions/53376686/what-is-the-most-efficient-way-to-compute-the-square-euclidean-distance-between/53380192#53380192
    faster than scipy.spatial.distance.cdist(Z,Z,'sqeuclidean')
    """
    mn, d = Z.shape
    dist = np.dot(Z, Z.T)
    TMP = np.empty(mn, dtype=Z.dtype)
    for i in range(mn):
        sum_Zi = 0.0
        for j in range(d):
            sum_Zi += Z[i, j] ** 2
        TMP[i] = sum_Zi
    for i in range(mn):
        for j in range(mn):
            dist[i, j] = -2.0 * dist[i, j] + TMP[i] + TMP[j]
    return dist

@njit
def pairwise_l1_distance(Z):
    """
    Compute the pairwise L^1-distance matrix between all points in Z.
    inputs: Z is (mn,d) array
    output: (mn,mn) array of pairwise squared distances (L^1)
    """
    mn, d = Z.shape
    output = np.zeros((mn, mn))
    for i in range(mn):
        for j in range(mn):
            temp = 0.0
            for u in range(d):
                temp += np.abs(Z[i, u] - Z[j, u])
            output[i, j] = temp
    return output

@njit
def kernel_matrices(X, Y, kernel_type, bandwidth, bandwidth_multipliers):
    """
    Compute kernel matrices for several bandwidths.
    inputs: kernel_type: "gaussian" or "laplace"
            X is (m,d) array (m d-dimensional points)
            Y is (n,d) array (n d-dimensional points)
            bandwidth is (d,) array
            bandwidth_multipliers is (N,) array such that:
                collection_bandwidths = [c*bandwidth for c in bandwidth_multipliers]
            kernel_type: "gaussian" or "laplace" (as defined in Section 5.3 of our paper)
    outputs: list of N kernel matrices for the pooled sample with the N bandwidths
    """
    m, d = X.shape
    Z = np.concatenate((X / bandwidth, Y / bandwidth))
    if kernel_type == "gaussian":
        pairwise_sq_l2_dists = pairwise_square_l2_distance(Z)
        prod = np.prod(bandwidth)
        output_list = []
        for c in bandwidth_multipliers:
            output_list.append(np.exp(-pairwise_sq_l2_dists / (c ** 2)))
        return output_list
    elif kernel_type == "laplace":
        pairwise_l1_dists = pairwise_l1_distance(Z)
        prod = np.prod(bandwidth)
        output_list = []
        for c in bandwidth_multipliers:
            output_list.append(np.exp(-pairwise_l1_dists / c))
        return output_list
    else:
        raise ValueError(
            'The value of kernel_type should be either "gaussian" or "laplace"'
        )

@njit
def mutate_K(K, approx_type):
    """
    Mutate the kernel matrix K depending on the type of approximation.
    inputs: K: kernel matrix of size (m+n,m+n) consisting of
               four matrices of sizes (m,m), (m,n), (n,m) and (n,n)
               m and n are the numbers of samples from p and q respectively
            approx_type: "permutation" (for MMD_a estimate Eq. (3))
                         or "wild bootstrap" (for MMD_b estimate Eq. (6))

    output: if approx_type is "permutation" then the estimate is MMD_a (Eq. (3)) and
               the matrix K is mutated to have zero diagonal entries
            if approx_type is "wild bootstrap" then the estimate is MMD_b (Eq. (6)),
               we have m = n and the matrix K is mutated so that the four matrices
               have zero diagonal entries
    """
    if approx_type == "permutation":
        for i in range(K.shape[0]):
            K[i, i] = 0
    if approx_type == "wild bootstrap":
        m = int(K.shape[0] / 2)  # m = n
        for i in range(m):
            K[i, i] = 0
            K[m + i, m + i] = 0
            K[i, m + i] = 0
            K[m + i, i] = 0


#!./bin/python
'''
Code By Michael Sherif Naguib
Liscence: MIT open source
Date: 2/23/19
@University of Tulsa
Description:  Code to calculate some patterns i was curious about in the golden ratio and the lucas sequence
              it is known that the  phi^n is approx the nth lucas number... round(phi^n) = lucas(n)
              and that the lucas sequence is similar to the fibonacci sequence instead starting at 2
              2 1 3 4 7 11 18 29 .... (Lucas) Fn = Fn-1 + Fn-2
              (The next is the sum of the two previous in the sequence)

              What this program aims to calculate is the difference or gaps that exist between the numbers...

'''

#imports
from decimal import *
getcontext()
import numpy as np
import matplotlib.pyplot as plt
import tqdm
import copy
import random

if __name__ == "__main__":
    #Settings:
    digitPrecision = 100    #the number of phi digits after the decimal point to use... max = 20k (not != calculation precision....)
    goldenRatioFilename = "GOLDEN_RATIO_20k_Places.txt" #the file storing the ratio... credit to https://www.goldennumber.net/phi-million-places/
    numberQuantity = 100   #How many numbers should be generated... (note this will double computationally in the calculation)

    #Read the golden ratio from the file (NOTE the file has newlines) ~~NumberPhile~~
    print("Reading: {0} for {1} digit-precision".format(goldenRatioFilename,digitPrecision))
    GOLDENRATIO = None
    with open(goldenRatioFilename,"r") as numberFile:
        #Read, get rid of newlines, combine, then take the desired digit count
        lines = numberFile.read().splitlines()
        numText = "".join(lines)
        partialDigits = numText[0:2+digitPrecision]
        #assign the Golden Ratio its value
        GOLDENRATIO = Decimal(partialDigits)

    #Calculate the desired quantity
    print("Generating: Lucas Approximates")
    lucasApproximates = []
    for n in tqdm.tqdm(range(0,numberQuantity)):
        lucasApproximates.append(GOLDENRATIO**Decimal(n))

    #generate a duplicate list to round
    print("Generating: Lucas Exacts")
    lucasApproximatesCopy = copy.deepcopy(lucasApproximates)
    lucasNums = list(map(lambda l: l.to_integral_exact(),lucasApproximatesCopy))

    #Calculate the Differences:
    print("Calculating: deltas")
    lucasDeltas=[]
    for i in tqdm.tqdm(range(0,len(lucasNums))):
        lucasDeltas.append(lucasNums[i] - lucasApproximates[i])
    lucasDeltasFloated = list(map(lambda l: float(l),lucasDeltas))
    print(lucasDeltasFloated)

    #Format for cordinates
    print("Generating Plot:")
    x_cords = np.array(list(range(0,len(lucasDeltas))))
    y_cords = np.array(lucasDeltasFloated)
    plt.title("Lucas Deltas")
    plt.xlabel("Index")
    plt.ylabel("Deltas (log scale)")
    #plt.xscale("log")
    # Plot the points
    plt.scatter( x_cords,y_cords,c=[(random.random(), random.random(), random.random())], s=np.pi* 3,alpha=0.5)
    plt.show()


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

winningScore = 5
alpha = 0.25
C = 750.0
pMLTable = {}

def pMLLookup(score, winningScore):
	global pMLTable
	if not winningScore in pMLTable:
		# initialize the whole table for this winning score
		pMLTable[winningScore] = {}
		for n in range(winningScore):
			score1 = (n, winningScore)
			score2 = (winningScore, n)
			pMLTable[winningScore][score1] = pML(score1)
			pMLTable[winningScore][score2] = pML(score2)
	return pMLTable[winningScore][score]

def pML(score):
	# intentionally omitting 0 and 1 from the range
	p = [(n+1)/101.0 for n in range(100)]
	p.sort(key = lambda p: -1*scoreP(score, p))
	return p[0]

# final score probability!
def scoreP(score, p):
	(nA, nB) = score
	winningScore = max([nA, nB])
	memo = {}
	# nA, nB and p captured in function scope
	def f(mA, mB):
		# if this is a final score
		if mA == winningScore or mB == winningScore:
			# check whether it is valid
			return (1.0 if nA == mA and nB == mB else 0.0)

		# dynamic programming trick, memoize
		if (mA, mB) not in memo:
			memo[(mA, mB)] = p*f(mA+1, mB) + (1-p)*f(mA, mB+1)

		return memo[(mA,mB)]

	return f(0,0)

# update two ratings, RA and RB with mixing parameter alpha
def update(RA, RB, score, verbose = True, correctionFactor = 1.0):
	# the correction factor is a rating difference adjustment equal to the slope of a line correcting the bias
	# as calculated below
	dR = (RB - RA)

	# compute point spread
	pExp = 1.0/(1.0 + 10.0 ** (dR/(C * correctionFactor))) # discount dR by the correctionFactor
	if verbose:
		print("Based on the initial ratings")
		print("Team A is expected to score each point with probability " + str(pExp))
		print("Team A is expected to win with probability " + str(pWin(pExp, 5)))

	pEst = pMLLookup(score, winningScore)
	dRObs = C * math.log10((1.0/pEst) - 1)# the value of RB-RA that gives pEst
	dRObs = correctionFactor * dRObs # use the bias correction factor to adjust the estimate # use the bias correction factor to adjust the estimate

	# RA <- alpha RA + (1-alpha)RA'
	# RB <- alpha RA + (1-alpha)RB'
	# really, we're updating the estimate dREst = alpha * dR + (1-alpha) * dRObs
	dRNew = (1 - alpha) * dR + alpha * dRObs

	# and split the difference to update A and B
	RANew = round(RA + dR/2 - dRNew/2)
	RBNew = round(RB - dR/2 + dRNew/2)
	if verbose:
		print("New ratings")
		print("RA = " + str(RANew))
		print("RB = " + str(RBNew))
	return RANew, RBNew

# compute probability of first to N if probability of success per trial is p
def pWin(p,N):
	# note the following check:
	# pWin(p ,N) = sum([scoreP(N,n,p) for n in range(N)])
	# but this method is more efficient

	# use the recurrence that pWin(n = j, m = k) = p * pWin(j+1,k) + (1-p)*pWin(j,k+1)
	# very similar to score distribution calc above, but different boundary conditions
	memo = {}
	def f(j,k):
		if j == N:
			return 1.0
		if k == N:
			return 0.0

		if (j,k) not in memo:
			memo[(j,k)] = p*f(j+1, k) + (1-p)*f(j,k+1)

		return memo[(j,k)]

	return f(0,0)

# simulate two teams' rating convergence and fluctuation for 100 games:
def oneGame(p,N):
	#simulate one game, return the score
	sA = 0
	sB = 0
	rnd = np.random.rand(2*N)
	i = 0
	while (sA < N and sB < N):
		if rnd[i] < p:
			sA += 1
		else:
			sB += 1
		i += 1
	return sA, sB

def simulate(p, N, silent = False, correctionFactor = 1.0):
	rA = np.zeros(N)
	rB = np.zeros(N)

	# initial ratings
	rA[0] = 1500
	rB[0] = 1500

	# true rating
	dR = C * math.log10((1.0/p) - 1)
	rATrue = 1500 - dR/2
	rBTrue = 1500 + dR/2

	for i in range(N-1):
		rANew, rBNew = update(rA[i], rB[i], oneGame(p, winningScore), verbose = False, correctionFactor = correctionFactor)
		rA[i+1] = rANew
		rB[i+1] = rBNew

	if not silent:
		pl.plot(range(N), rA, 'r.-', range(N), rB, 'b.-')
		pl.legend(['Team A', 'Team B'])
		pl.axhline(rATrue, color = 'r')
		pl.axhline(rBTrue, color = 'b')
		pl.title('Rating simulation for scoring probability p = {:}\nTrue rating difference = {:.0f}'.format(p,-dR))
		pl.xlabel('Games played')
		pl.ylabel('Rating')
		pl.grid()

	return rA, rB, dR

def plotBias(n = 100, correctionFactor = 0.7):
	# n: number of p values to simulate
	# m: slope of correction line
	p = [pp/(n+1.0) for pp in range(n+1)][1:] # drop first element, since p = 0 behaves badly
	trueRating = []
	estRating = []

	for pp in p:
		ra,rb,dr = simulate(pp, 1000, True, correctionFactor)
		trueRating.append(dr)
		estRating.append(np.median(rb[500:] - ra[500:]))

	pl.plot(trueRating, estRating, 'r.')
	pl.plot([-1500,1500], [-1500, 1500], 'k-')
	pl.xlabel('True rating difference')
	pl.ylabel('Estimated rating difference')
	pl.title('Rating bias from simulation (correction factor = {:})'.format(correctionFactor))
	pl.grid()


__all__ = ["graph_x", "graph_x2", "graph_chain", "graph_saddle"]

from typing import Tuple

import numpy as np
import plotly.offline as py
import plotly.graph_objs as go

def graph_x(val: int = 20):
    x = np.linspace(-0.5 * val, 1.5 * val, num=200)
    y = np.power(x, 1)

    data = [go.Scatter(x=x, y=y)]

    layout = go.Layout(
        title="Graph of f(x) = x",
        autosize=True,
        margin=dict(l=65, r=50,
                    b=65, t=90,)
    )

    return py.iplot(go.Figure(data=data, layout=layout))

def graph_x2(val: int = 10):
    x = np.linspace(-1. * val, 1. * val, num=200)
    y = np.power(x, 2) - x + 4

    data = [go.Scatter(x=x, y=y)]

    layout = go.Layout(
        title="Graph of f(x) = x^2 - x + 4",
        autosize=True,
        margin=dict(l=65, r=50,
                    b=65, t=90,)
    )

    return py.iplot(go.Figure(data=data, layout=layout))

def graph_chain(val: int = 10):
    x = np.linspace(-1. * val, 1. * val, num=200)
    y = np.power(3 * np.power(x, 2) + x - 4, 3)

    data = [go.Scatter(x=x, y=y)]

    layout = go.Layout(
        title="Graph of f(x) = (3x^2 + x - 4)^3",
        autosize=True,
        margin=dict(l=65, r=50,
                    b=65, t=90,)
    )

    return py.iplot(go.Figure(data=data, layout=layout))

def graph_saddle(val: int = 10):
    x = np.linspace(-1. * val, 1. * val, num=200)
    y = np.linspace(-1. * val, 1. * val, num=200)
    y_grid, x_grid = np.meshgrid(x, y)
    z = (y_grid * np.power(x_grid, 2)) - 3 * np.power(y_grid, 2)

    data = [go.Surface(x=x, y=y, z=z)]

    layout = go.Layout(
        title="Graph of f(x, y) = yx^2 - 3y^2",
        autosize=True,
        margin=dict(l=65, r=50,
                    b=65, t=90,)
    )

    return py.iplot(go.Figure(data=data, layout=layout))


# this file contains all necessary functions to generate covariance matrices for all relevant GP

import numpy

# Brownian Bridge with start and end point different from each other
def gen_BB2_cov(nlen, fac=0.1):

	sigma_p = numpy.zeros((nlen, nlen))
	for i in range(nlen):
		for j in range(i):
			sigma_p[i, j] = (1 - i/nlen)*(1 - j/nlen) + (i*j)/(nlen*nlen) + (fac*fac)*(j - (i*j)/nlen)
			sigma_p[j, i] = sigma_p[i, j]
		sigma_p[i, i] = (1 - i/nlen)*(1 - i/nlen) + (i*i)/(nlen*nlen) + (fac*fac)*i*(1 - i/nlen)

	return sigma_p, numpy.linalg.det(sigma_p)

# Brownian Bridge with same start and end point
def gen_BB_cov(nlen, fac=0.11):

	sigma_p = numpy.zeros((nlen, nlen))
	for i in range(nlen):
		for j in range(i):
			sigma_p[i, j] = 1 + (fac*fac)*(j - (i*j)/nlen)
			sigma_p[j, i] = sigma_p[i, j]
		sigma_p[i, i] = 1 + (fac*fac)*i*(1 - i/nlen)

	return sigma_p, numpy.linalg.det(sigma_p)

# Ornstein–Uhlenbeck process
def gen_OU_cov(nlen, fac=0.1, alpha=1):

	b = (fac * fac) / (2 * alpha)

	sigma_p = numpy.zeros((nlen, nlen))
	for i in range(nlen):
		for j in range(i):
			sigma_p[i, j] = (1 + b * (numpy.exp(2 * alpha * j) - 1)) * numpy.exp(-alpha * (i + j))
			sigma_p[j, i] = sigma_p[i, j]
		sigma_p[i, i] = b + (1 - b) * numpy.exp(-2 * alpha * i)

	return sigma_p, numpy.linalg.det(sigma_p)

# Fractional Brownian Motion
def gen_frac_cov(H, nlen, fac=0.3):

	sigma_p = numpy.zeros((nlen, nlen))
	for i in range(nlen):
		for j in range(i):
			sigma_p[i, j] = 1 + (fac * fac) * 0.5 * (i**(2*H) + j**(2*H) - (i - j)**(2*H))
			sigma_p[j, i] = sigma_p[i, j]
		sigma_p[i, i] = 1 + (fac * fac) * i**(2*H)

	return sigma_p, numpy.linalg.det(sigma_p)

# Slow features (taken from paper: Disentangling Space and Time in Video with Hierarchical Variational Auto-encoders)
def gen_slowfeature(nlen, fac=0.1):

	sigma_p = numpy.ones((nlen, nlen))
	for i in range(1, nlen):
		sigma_p[i, i] += fac * fac
	return sigma_p, numpy.linalg.det(sigma_p)

def covariance_function(p_type, nlen, H = None):

	if (p_type == 'frac'):
		if (H == None):
			raise Exception("Invalid Argument H")
		return gen_frac_cov(H, nlen)

	elif (p_type == 'bb'):
		return gen_BB_cov(nlen)

	elif(p_type == 'bb2'):
		return gen_BB2_cov(nlen)

	elif(p_type == 'ou'):
		return gen_OU_cov(nlen)

	elif(p_type == 'slow'):
		return gen_slowfeature(nlen)

	else:
		raise Exception('Gaussian Process {}, H = {} not implemented'.format(p_type, H))

if __name__ == "__main__":
	s, d = gen_frac_cov(0.1, 5)
	#s, d = gen_slowfeature(8)
	#s, d = gen_OU_cov(5)
	#s, d = gen_BB_cov(5)
	#s, d = gen_BB2_cov(5)

	print(s)
	print(d)


# Source:
# https://peterroelants.github.io/posts/neural-network-implementation-part01/

import matplotlib
matplotlib.use('TkAgg')

import sys

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

sns.set_style('darkgrid')
np.random.seed(seed=13)

def print_versions():
    print('Python: {}.{}.{}'.format(*sys.version_info[:3]))
    print('numpy: {}'.format(np.__version__))
    print('matplotlib: {}'.format(matplotlib.__version__))
    print('seaborn: {}'.format(sns.__version__))
# print_versions()

def f(x):
    # Generate the target values t from x with small Gaussian noise
    # so that the estimation won't be perfect.
    # This function represents the line that generates t without noise.
    # f(x) = 2x
    return x * 2

def nn(x, w):
    # Output function y = x * w
    return x * w

def loss(y, t):
    # MSE loss function
    return np.mean((t - y)**2)

###
# Plot the samples and regression line
###

# Vector of input samples
# 20 values sampled from uniform dist between 0 and 1
x = np.random.uniform(0, 1, 20)

# Generate the Guassian noise error for each sample in x
noise = np.random.randn(x.shape[0]) * 0.2
# Create targets using the formula: f(x) + N(0, 0.2)
t = f(x) + noise

def plot_samples():
    # Plot the target t versus the input x
    plt.figure(figsize=(5, 3))
    plt.plot(x, t, 'o', label='$t$')
    # Plot the initial line
    plt.plot([0, 1], [f(0), f(1)], 'b--', label='$f(x)$')
    plt.xlabel('$x$', fontsize=12)
    plt.ylabel('$t$', fontsize=12)
    plt.axis((0, 1, 0, 2))
    plt.title('inputs (x) vs targets (t)')
    plt.legend(loc=2)
    plt.show()
# plot_samples()

###
# Plot the loss vs. the given weight w
###

# Vector of weights for which we want to plot the loss
ws = np.linspace(0, 4, num=100)  # weight values
# Create a vectorized function
vectorized_fn = np.vectorize(lambda w: loss(nn(x, w), t))
# Calculate the loss for each weight in ws
loss_ws = vectorized_fn(ws)

def plot_loss_function():
    plt.figure(figsize=(5, 4))
    plt.plot(ws, loss_ws, 'r--', label='loss')
    plt.xlabel('$weights$', fontsize=12)
    plt.ylabel('$MSE$', fontsize=12)
    plt.title('loss function with respect to $w$')
    plt.xlim(0, 4)
    plt.legend()
    plt.show()
# plot_loss_function()

###
# Plot gradient descent updates on the loss function
###

def gradient(w, x, t):
    """Gradient function to multiply the learning rate by.
    Note: y = nn(x, w) = x * w.

    Gradient: 2x(y_pred - y_true)
    """
    return 2 * x * (nn(x, w) - t)

def delta_w(w_k, x, t, learning_rate):
    """Full weight update function for a batch of samples."""
    return learning_rate * np.mean(gradient(w_k, x, t))

# Initial weight parameter
w = np.random.rand()
# Set the learning rate
learning_rate = 0.9

# Perform the gradient descent updates and print the weights and loss
# Number of gradient descent updates
nb_of_iterations = 4
# Keep track of weight and loss values; include initial values
w_loss = [(w, loss(nn(x, w), t))]

for i in range(nb_of_iterations):
    # Delta w update
    dw = delta_w(w, x, t, learning_rate)
    print(f'gradient at w({i}): {dw:.4f}')
    # Update the current weight parameter in the negative direction of the
    # gradient
    w = w - dw
    # Loss value
    loss_value = loss(nn(x, w), t)
    # Save weight and loss
    w_loss.append((w, loss_value))

# Print the final weight and loss
# Target weight value is around 2.0
for i in range(0, len(w_loss)):
    w = w_loss[i][0]
    l = w_loss[i][1]
    print(f'w({i}): {w:.4f}\tloss: {l:.4f}')

def plot_gradient_updates():
    # Visualise the gradient descent updates
    plt.figure(figsize=(6, 4))
    plt.plot(ws, loss_ws, 'r--', label='loss')  # Loss curve
    # Plot the updates
    for i in range(0, len(w_loss) - 1):
        w1, c1 = w_loss[i]
        w2, c2 = w_loss[i + 1]
        plt.plot(w1, c1, 'bo')
        plt.plot([w1, w2], [c1, c2], 'b-')
        plt.text(w1, c1 + 0.05, f'${i}$')
    plt.plot(w2, c2, 'bo', label='$w(k)$')
    plt.text(w2, c2 + 0.05, f'${i+1}$')
    # Show figure
    plt.xlabel('$w$', fontsize=12)
    plt.ylabel('$\\xi$', fontsize=12)
    plt.title('Gradient descent updates plotted on loss function')
    plt.xlim(0, 4)
    plt.legend(loc=1)
    plt.show()
# plot_gradient_updates()

###
# Plot the final results
###

w = np.random.rand()
nb_of_iterations = 10

for i in range(nb_of_iterations):
    dw = delta_w(w, x, t, learning_rate)
    w = w - dw

def plot_final_result():
    # Note that there is no bias term as both lines pass through the origin
    # Plot the fitted line agains the target line
    plt.figure(figsize=(6, 4))
    # Plot the target t versus the input x
    plt.plot(x, t, 'o', label='$t$')
    # Plot the initial line
    plt.plot([0, 1], [f(0), f(1)], 'b--', label='$f(x)$')
    # plot the fitted line
    plt.plot([0, 1], [0 * w, 1 * w], 'r-', label='$y = w * x$')
    plt.xlabel('$x$', fontsize=12)
    plt.ylabel('$t$', fontsize=12)
    plt.title('input vs target')
    plt.legend(loc=2)
    plt.ylim(0, 2)
    plt.xlim(0, 1)
    plt.show()
plot_final_result()


import numpy as np
import matplotlib.pyplot as plt

#step1
def initialize_parameters(lenw):
    w=np.random.randn(1,lenw)
    #w=np.zeroes((1,lenw))
    b=0
    return w,b

#step2
def forward_prop(X,w,b): #w-->1xn , X-->nxm
    z=np.dot(w,X)+b #z--> 1xm b_vector = [b b b b .....]
    return z

#step 3
def cost_function(z,y):
    m=y.shape[1]
    J=(1/(2*m))*np.sum(np.square(z-y))
    return J

#step4
def back_prop(X,y,z):
    m=y.shape[1]
    dz = (1/m)*(z-y)
    dw = np.dot(dz,X.T) #dw --> 1xn
    db = np.sum(dz)
    return dw,db

#step5
def gradient_descent_update(w,b,dw,db,learning_rate):
    w=w-learning_rate*dw
    b=b-learning_rate*db
    return w,b

#step6
def linear_regression_model(X_train,y_train,X_val,y_val,learning_rate,epochs,threshold):

    lenw = X_train.shape[0]
    w,b= initialize_parameters(lenw) #step1
    th=threshold
    iteration=2

    costs_train = []
    costs_val   = []
    m_train     = y_train.shape[1]
    m_val       = y_val.shape[1]

    for i in range(1,epochs+1):
        z_train    = forward_prop(X_train,w,b) #step2
        cost_train = cost_function(z_train,y_train) #step3
        dw,db      = back_prop(X_train,y_train,z_train) #step4
        w,b        = gradient_descent_update(w,b,dw,db,learning_rate) #step5

        count=i
        #store trining cost in a list for plotting purpose
        #if i%10==0:
        costs_train.append(cost_train)

        #MAE_train
        MAE_train = (1/m_train)*np.sum(np.abs(z_train-y_train))

        #cost_val,MAE val
        z_val    = forward_prop(X_val,w,b)
        cost_val = cost_function(z_val,y_val)
        MAE_val  = (1/m_val)*np.sum(np.abs(z_val-y_val))
        #if i%10==0:
        costs_val.append(cost_val)

        if count>1:
            diff = (costs_train[-2]-costs_train[-1])
            iteration=iteration+1
            if diff <th:
                break
            else:
                continue

        #print out cost_train,cost_val,MAE_train,MAE_val

        #print('Epochs '+str(i)+'/'+str(epochs)+': ')
        #print('Training cost '+str(cost_train)+'|'+'Validation cost '+str(cost_val))
        #print('Training MAE '+str(MAE_train)+'|'+'Validation MAE '+str(MAE_val))
        #print('Coefficients '+str(w))

    #plt.plot(costs_train,label='tc_lr='+str(learning_rate))
    #plt.plot(costs_val,label='vc_lr='+str(learning_rate))
    #plt.xlabel('Iterations(per tens)')
    #plt.ylabel('Cost')
    #plt.title('Learning rate : ')
    #plt.legend()
    #plt.show

    #print('Coefficients '+str(w))

    return costs_train,costs_val,w,iteration


import math
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error, r2_score

def get_regression_metrics(y_ref,y_pred):
    mse = mean_squared_error(y_ref, y_pred)
    rmse = math.sqrt(mse)
    r2 = r2_score(y_ref, y_pred)

    metrics = {
        "mse":mse,
        "rmse":rmse,
        "r2":r2,
    }

    return metrics

def plot_comparing(y_ref,y_pred,outpath:str):
    bins = np.linspace(-10, 10, 100)
    plt.hist(y_ref, bins, alpha=0.5, label='y_ref')
    plt.hist(y_pred, bins, alpha=0.5, label='y_pred')
    plt.legend(loc='upper right')
    plt.savefig(outpath)


from __future__ import division
from __future__ import print_function
# Preprocessing of Option Quotes
# ==============================
#
# This notebook demonstrates the preprocessing of equity options, in preparation for the estimation of the parameters of a stochastic model.
# A number of preliminary calculations must be performed:
#
# 1. Calculation of implied risk-free rate and dividend yield, and derivation of forward prices
# 2. Calculation of forward at-the-money volatility. There is probably no option struck at the forward price, so this item must be computed by interpolation.
# 3. Calculation of the Black-=Scholes implied bid and ask volatility, given bid and ask option prices.
# 4. Calculation of 'Quick Delta': this is a common measure of moneyness, useful for representing the volatility smile.
#
# Each step is now described.
#
# Calculation of implied dividend yield and risk-free rate
# --------------------------------------------------------
#
# Recall the put-call parity relationship with continuous dividends:
#
# $$
# C_t - P_t = S_t e^{-d (T-t)} - K e^{-r (T-t)}
# $$
#
# where
#
# * $C_t$ price of call at time $t$
# * $P_t$ price of put at time $t$
# * $S_t$ spot price of underlying asset
# * $d$ continuous dividend yield
# * $r$ risk-free rate
# * $T$ Expity
#
# For each maturity, we estimate the linear regression:
#
# $$
# C_t - P_t = a_0 + a_1 K
# $$
#
# which yields
#
# $$
# r = - \frac{1}{T} \ln (-a_1)
# $$
# $$
# d = \frac{1}{T} \ln \left( \frac{S_t}{a_0} \right)
# $$
#
# Calculation of forward at-the-money volatility
# ----------------------------------------------
#
# We next want to estimate the implied volatility of an option struck at the forward price. In general, such option is not traded, and the volatility must therefore be estimated. The calculation involves 3 steps, performed separately on calls and puts:
#
# 1. Estimate the bid ($\sigma_b(K)$) and ask ($\sigma_a(K)$) Black-Scholes volatility for each quote.
# 2. Compute a mid-market implied volatility for each quote:
# $$
# \sigma(K) = \frac{\sigma_b(K)+\sigma_a(K)}{2}
# $$
# 3. Let $F$ be the forward price, the corresponding mid-market implied volatility is computed by linear interpolation between the two quotes bracketing $F$.
#
# The forward ATM volatility is the average of the volatilities computed on calls and puts.
#
# Quick Delta
# -----------
#
# Recall that the delta of a European call is defined as $N(d_1)$, where
#
# $$
# d_{1} = \frac{1}{\sigma \sqrt{T}} \left[ \ln \left( \frac{S}{K} \right) + \left( r + \frac{1}{2}\sigma^2 \right)T \right]
# $$
#
# The "Quick Delta" (QD) is a popular measure of moneyness, inspired from the definition of delta:
#
# $$
# QD(K) = N \left( \frac{1}{\sigma \sqrt{T}} \ln \left( \frac{F_T}{K} \right) \right)
# $$
#
# Note that $QD(F_T)=0.5$, for all maturities, while the regular forward delta is a function of time to expiry. This property of Quick Delta makes it convenient for representing the volatility smile.
#
# Data Filters
# ------------
#
# A number of filters may be applied, in an attempt to exclude inconsistent or erroneous data.
#
# 1. Exclusion of maturities shorter than $tMin$
# 2. Exclusion of maturities with less than $nMin$ quotes
# 3. Exclusion of quotes with Quick Delta less than $QDMin$ or higher than $QDMax$
#
# Implementation
# --------------
#
# This logic is implemented in the function `Compute_IV`, presented below. The function takes as argument a `pandas DataFrame` and returns another
# `DataFrame`, with one row per quote and 14 columns:
#
# 1. Type: 'C'/'P'
# 2. Strike
# 3. dtExpiry
# 4. dtTrade
# 5. Spot
# 6. IVBid: Black-Scholes implied volatility (bid)
# 7. IVAsk: Black-Scholes implied volatility (ask)
# 8. QD: Quick Delta
# 9. iRate: risk-free rate (continuously compounded)
# 10. iDiv: dividend yield (continuously compounded)
# 11. Fwd: Forward price
# 12. TTM: Time to maturity, in fraction of years (ACT/365)
# 13. PBid: Premium (bid)
# 14. PAsk: Premium (ask)

# <codecell>

import pandas
import dateutil
import re
import datetime
import numpy as np
from pandas import DataFrame
from scipy.interpolate import interp1d
from scipy.stats import norm
from scipy.linalg import lstsq

import quantlib.pricingengines.blackformula
from quantlib.pricingengines.blackformula import blackFormulaImpliedStdDev
from quantlib.instruments.option import Call, Put

def Compute_IV(optionDataFrame, tMin=0, nMin=0, QDMin=0, QDMax=1, keepOTMData=True):

    """
    Pre-processing of a standard European option quote file.
    - Calculation of implied risk-free rate and dividend yield
    - Calculation of implied volatility
    - Estimate ATM volatility for each expiry
    - Compute implied volatility and Quick Delta for each quote

    Options for filtering the input data set:
    - maturities with less than nMin strikes are ignored
    - maturities shorter than tMin (ACT/365 daycount) are ignored
    - strikes with Quick Delta < qdMin or > qdMax are ignored
    """

    grouped = optionDataFrame.groupby('dtExpiry')

    isFirst = True
    for spec, group in grouped:
        print('processing group %s' % spec)

        # implied vol for this type/expiry group

        indx = group.index

        dtTrade = group['dtTrade'][indx[0]]
        dtExpiry = group['dtExpiry'][indx[0]]
        spot = group['Spot'][indx[0]]
        daysToExpiry = (dtExpiry-dtTrade).days
        timeToMaturity = daysToExpiry/365.0

        # exclude groups with too short time to maturity

        if timeToMaturity < tMin:
            continue

        # exclude groups with too few data points

        df_call = group[group['Type'] == 'C']
        df_put = group[group['Type'] == 'P']

        if (len(df_call) < nMin) | (len(df_put) < nMin):
            continue

        # calculate forward, implied interest rate and implied div. yield

        df_C = DataFrame((df_call['PBid']+df_call['PAsk'])/2,
                         columns=['PremiumC'])
        df_C.index = df_call['Strike']
        df_P = DataFrame((df_put['PBid']+df_put['PAsk'])/2,
                         columns=['PremiumP'])
        df_P.index = df_put['Strike']

        # use 'inner' join because some strikes are not quoted for C and P
        df_all = df_C.join(df_P, how='inner')
        df_all['Strike'] = df_all.index
        df_all['C-P'] = df_all['PremiumC'] - df_all['PremiumP']

        y = np.array(df_all['C-P'])
        x = np.array(df_all['Strike'])
        A = np.vstack((x, np.ones(x.shape))).T

        b = np.linalg.lstsq(A, y, rcond=None)[0]
        # intercept is last coef
        iRate = -np.log(-b[0])/timeToMaturity
        dRate = np.log(spot/b[1])/timeToMaturity

        discountFactor = np.exp(-iRate*timeToMaturity)
        Fwd = spot * np.exp((iRate-dRate)*timeToMaturity)

        print('Fwd: %f int rate: %f div yield: %f' % (Fwd, iRate, dRate))

        # mid-market ATM volatility

        def impvol(cp, strike, premium):
            try:
                vol = blackFormulaImpliedStdDev(cp, strike,
                        forward=Fwd, blackPrice=premium, discount=discountFactor,
                    TTM=timeToMaturity)
            except RuntimeError:
                vol = np.nan
            return vol/np.sqrt(timeToMaturity)

        # implied bid/ask vol for all options

        df_call = df_call.assign(IVBid = [impvol(Call, strike, price) for strike, price
                                          in zip(df_call['Strike'], df_call['PBid'])],
                                 IVAsk = [impvol(Call, strike, price) for strike, price
                                          in zip(df_call['Strike'], df_call['PBid'])])
        df_call = df_call.assign(IVMid = (df_call.IVBid + df_call.IVAsk)/2)

        df_put = df_put.assign(IVBid = [impvol(Put, strike, price) for strike, price
                                        in zip(df_put['Strike'], df_put['PBid'])],
                               IVAsk = [impvol(Put, strike, price) for strike, price
                                        in zip(df_put['Strike'], df_put['PAsk'])])
        df_put = df_put.assign(IVMid = (df_put['IVBid'] + df_put['IVAsk'])/2)

        f_call = interp1d(df_call['Strike'].values, df_call['IVMid'].values)
        f_put = interp1d(df_put['Strike'].values, df_put['IVMid'].values)

        atmVol = (f_call(Fwd)+f_put(Fwd))/2
        print('ATM vol: %f' % atmVol)

        # Quick Delta, computed with ATM vol
        rv = norm()
        df_call = (df_call.
                   assign(QuickDelta=
                          rv.cdf(np.log(Fwd/df_call.Strike.values) / (atmVol*np.sqrt(timeToMaturity)))))
        df_put = (df_put.
                  assign(QuickDelta=
                         rv.cdf(np.log(Fwd/df_put.Strike.values)/(atmVol*np.sqrt(timeToMaturity)))))

        # keep data within QD range

        df_call = df_call[(df_call['QuickDelta'] >= QDMin) & \
                          (df_call['QuickDelta'] <= QDMax) ]

        df_put = df_put[  (df_put['QuickDelta'] >= QDMin) & \
                          (df_put['QuickDelta'] <= QDMax) ]

        # final assembly...

        df_cp = df_call.append(df_put,  ignore_index=True)
        df_cp['iRate'] = iRate
        df_cp['iDiv'] = dRate
        df_cp['ATMVol'] = atmVol
        df_cp['Fwd'] = Fwd
        df_cp['TTM'] = timeToMaturity
        df_cp['CP'] = [1 if t == 'C' else -1 for t in df_cp['Type']]

        # keep only OTM data ?
        if keepOTMData:
            df_cp = df_cp[((df_cp['Strike']>=Fwd) & (df_cp['Type'] == 'C')) |
                          ((df_cp['Strike']<Fwd) & (df_cp['Type'] == 'P'))]

        if isFirst:
            df_final = df_cp
            isFirst = False
        else:
            df_final = df_final.append(df_cp, ignore_index=True)

    return df_final

# <markdowncell>

# Example
# -------
#
# Using the SPX data set found in the data folder, the above procedure generates a `DataFrame` suited for use in a calibration program.

# <codecell>

if __name__ == '__main__':

    option_data_frame = pandas.read_pickle('../data/df_SPX_24jan2011.pkl')

    df_final = Compute_IV(option_data_frame, tMin=1.0/12, nMin=6, QDMin=.2, QDMax=.8)

    # save a csv file and pickled data frame
    df_final.to_csv('../data/df_options_SPX_24jan2011.csv', index=False)
    df_final.to_pickle('../data/df_options_SPX_24jan2011.pkl')


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

x_vals = numpy.linspace(0,10,1000)

y_vals = numpy.linspace(0,10,1000)

xx, yy = numpy.meshgrid(x_vals, y_vals)

z_vals = numpy.sin(xx) + numpy.cos(yy)

gradients = numpy.gradient(z_vals)

y_grads = gradients[0] #axis 0 is rows, which is y dimension

x_grads = gradients[1] #axis 1 is columns, which is x direction

dim = 2

fig = plt.figure(figsize=(16,10))

if dim == 3:

    axes_data = fig.add_axes([.025,.1,.275,.8], projection='3d')

    axes_x_grad = fig.add_axes([.35,.1,.275,.8], projection='3d')

    axes_y_grad = fig.add_axes([.675,.1,.275,.8], projection='3d')

    axes_data.plot_surface(xx, yy, z_vals, cmap='coolwarm')

    axes_x_grad.plot_surface(xx, yy, x_grads, cmap='coolwarm')

    axes_y_grad.plot_surface(xx, yy, y_grads, cmap='coolwarm')

elif dim == 2:

    axes_data = fig.add_axes([.025,.1,.275,.8])

    axes_x_grad = fig.add_axes([.35,.1,.275,.8])

    axes_y_grad = fig.add_axes([.675,.1,.275,.8])

    axes_data.contour(xx, yy, z_vals, 20, cmap='coolwarm')

    axes_x_grad.contour(xx, yy, x_grads, 20, cmap='coolwarm')

    axes_y_grad.contour(xx, yy, y_grads, 20, cmap='coolwarm')

axes_data.set_title("Input Data")
axes_x_grad.set_title("X gradients")
axes_y_grad.set_title("Y gradients")

plt.show()


from sympy.physics.pring import wavefunction, energy
from sympy.core.numbers import (I, pi)
from sympy.functions.elementary.exponential import exp
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.integrals.integrals import integrate
from sympy.simplify.simplify import simplify
from sympy.abc import m, x, r
from sympy.physics.quantum.constants import hbar

def test_wavefunction():
    Psi = {
        0: (1/sqrt(2 * pi)),
        1: (1/sqrt(2 * pi)) * exp(I * x),
        2: (1/sqrt(2 * pi)) * exp(2 * I * x),
        3: (1/sqrt(2 * pi)) * exp(3 * I * x)
    }
    for n in Psi:
        assert simplify(wavefunction(n, x) - Psi[n]) == 0

def test_norm(n=1):
    # Maximum "n" which is tested:
    for i in range(n + 1):
        assert integrate(
            wavefunction(i, x) * wavefunction(-i, x), (x, 0, 2 * pi)) == 1

def test_orthogonality(n=1):
    # Maximum "n" which is tested:
    for i in range(n + 1):
        for j in range(i+1, n+1):
            assert integrate(
                wavefunction(i, x) * wavefunction(j, x), (x, 0, 2 * pi)) == 0

def test_energy(n=1):
    # Maximum "n" which is tested:
    for i in range(n+1):
        assert simplify(
            energy(i, m, r) - ((i**2 * hbar**2) / (2 * m * r**2))) == 0


import numpy as np
from numpy.random import MT19937
from numpy.random import RandomState, SeedSequence
from scipy import ndimage
#from datetime import datetime
from time import time

def generate_perlin_noise_2d(shape, res):
    def f(t):
        return 6*t**5 - 15*t**4 + 10*t**3

    delta = (res[0] / shape[0], res[1] / shape[1])
    d = (shape[0] // res[0], shape[1] // res[1])
    grid = np.mgrid[0:res[0]:delta[0],0:res[1]:delta[1]].transpose(1, 2, 0) % 1
    # Gradients
    angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
    gradients = np.dstack((np.cos(angles), np.sin(angles)))
    g00 = gradients[0:-1,0:-1].repeat(d[0], 0).repeat(d[1], 1)
    g10 = gradients[1:  ,0:-1].repeat(d[0], 0).repeat(d[1], 1)
    g01 = gradients[0:-1,1:  ].repeat(d[0], 0).repeat(d[1], 1)
    g11 = gradients[1:  ,1:  ].repeat(d[0], 0).repeat(d[1], 1)
    # Ramps
    n00 = np.sum(np.dstack((grid[:,:,0]  , grid[:,:,1]  )) * g00, 2)
    n10 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1]  )) * g10, 2)
    n01 = np.sum(np.dstack((grid[:,:,0]  , grid[:,:,1]-1)) * g01, 2)
    n11 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1]-1)) * g11, 2)
    # Interpolation
    t = f(grid)
    n0 = n00*(1-t[:,:,0]) + t[:,:,0]*n10
    n1 = n01*(1-t[:,:,0]) + t[:,:,0]*n11
    return np.sqrt(2)*((1-t[:,:,1])*n0 + t[:,:,1]*n1)

def generate_fractal_noise_2d(shape, res, octaves=1, persistence=0.5):
    #np.random.seed(seed=datetime.now())
    #np.random.seed(seed=int(round(time() * 1000)))
    rs = RandomState( MT19937(SeedSequence(int(round(time() * 1000)))) )
    noise = np.zeros(shape)
    frequency = 1
    amplitude = 1
    for m_i in range(octaves):
        #print("Generate subfield {}: shape={}, resolution={} with res={} and frequency={}".format(m_i, shape, (frequency*res[0], frequency*res[1]), res, frequency))
        noise += amplitude * generate_perlin_noise_2d(shape, (frequency*res[0], frequency*res[1]))
        frequency *= 2
        amplitude *= persistence
    return noise

def generate_fractal_noise_temporal2d(shape, tsteps, res, octaves=1, persistence=0.5, max_shift=((-1, 1), (-1, 1))):
    #np.random.seed(seed=datetime.now())
    #np.random.seed(int(round(time() * 1000)))
    rs = RandomState( MT19937(SeedSequence(int(round(time() * 1000)))) )
    noise = np.zeros(shape)
    frequency = 1
    amplitude = 1
    for m_i in range(octaves):
        #print("Generate subfield {}: shape={}, resolution={} with res={} and frequency={}".format(m_i, shape, (frequency*res[0], frequency*res[1]), res, frequency))
        noise += amplitude * generate_perlin_noise_2d(shape, (frequency*res[0], frequency*res[1]))
        frequency *= 2
        amplitude *= persistence

    ishape = (tsteps, )+shape
    result = np.zeros(ishape)
    timage = np.zeros(noise.shape)
    for i in range(0, tsteps):
        result[i,:,:] = noise
        sx = np.random.randint(max_shift[0][0], max_shift[0][1], dtype=np.int32)
        sy = np.random.randint(max_shift[1][0], max_shift[1][1], dtype=np.int32)
        ndimage.shift(noise, (sx, sy), timage, order=3, mode='mirror')
        noise = timage
    return result

if __name__ == '__main__':
    import matplotlib.pyplot as plt

    np.random.seed(0)
    noise = generate_perlin_noise_2d((256, 256), (8, 8))
    plt.imshow(noise, cmap='gray', interpolation='lanczos')
    plt.colorbar()

    np.random.seed(0)
    noise = generate_fractal_noise_2d((256, 256), (8, 8), 5)
    #noise = generate_fractal_noise_2d((2048, 512), (64, 16), 8, 0.8)
    plt.figure()
    plt.imshow(noise, cmap='gray', interpolation='lanczos')
    plt.colorbar()
    plt.show()


import numpy as np
from numpy.linalg import det,inv
#1 Write a NumPy program to compute the multiplication of two given matrixes
def mul(x,y):
    return x.dot(y)

def determinant(array):
    return np.linalg.det(array)

#3 Write a NumPy program to compute the sum of the diagonal element of a given array
def sum_of_diagonal_elements(array):
    return np.trace(array)

#4Write a NumPy program to compute the inverse of a given matrix
def inverse_matrix(array):
    return np.linalg.inv(array)

#5Write a NumPy program to generate matrix and write it to a file, then again read from file that matrix.

def create_file(array):
    np.save("some_array",array)
    return np.load("some_array.npy")

def main():
    arr = np.random.randint(5, size=(2, 4))
    x1 = np.array([[2, 2, 6], [1, 9, 10]])
    y1 = np.array([[2, 2], [1, 4], [5, 6]])
    print(mul(x1,y1))
    print(sum_of_diagonal_elements(x1))
    print(create_file(arr))
main()


import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.linear_model import Perceptron

# load data
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target

df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
df.label.value_counts()

plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal length')
plt.legend()
plt.show()

# 提取前100行，第一第二列和最后一列
data = np.array(df.iloc[:100, [0, 1, -1]])
X, y = data[:, :-1], data[:, -1]
y = np.array([1 if i == 1 else -1 for i in y])

clf = Perceptron(fit_intercept=False, max_iter=1000, shuffle=False)
clf.fit(X, y)

# w
print(clf.coef_)
# b
print(clf.intercept_)

x_points = np.arange(4, 8)
y_ = -(clf.coef_[0][0]*x_points + clf.intercept_)/clf.coef_[0][1]
plt.plot(x_points, y_)

plt.plot(data[:50, 0], data[:50, 1], 'bo', color='blue', label='0')
plt.plot(data[50:100, 0], data[50:100, 1], 'bo', color='orange', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()


'''
Code for Shi-Tomsai Corners(Modified form of Harris corners)
Author: hasanlatif.pk@gmail.com
'''

import numpy as np
import cv2
from scipy import signal
import numpy.linalg as LA

img = cv2.imread('test.jpg',0)   # reading image in gray scale mode,'0' flag mean reading image in grayscale mode
new_img = np.zeros((img.shape[0],img.shape[1]))  ## making new image of the same shape as of orignal image
img = np.float32(img)/255       # Normalizing imaging to make values in bound
I_x = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1)  ## Applying First dervative along x-direction
I_y = cv2.Sobel(img,cv2.CV_32F,0,1,ksize=1) ### Applying first derivative aling y-direction
I_x_2 = I_x*I_x   ## element of structed matrixx
I_y_2 = I_y*I_y   ## Element of Structed matrix
I_xy = I_x*I_y    ## Element of structured matrix
I_yx = I_y*I_x   ## Element of Structed matrix
kernel = np.ones((3,3))/9   ## As we are using weight as 1.so defining kernal as 1 and as element in each window is 9. . Simply using box filter does the same
I_x_sum = signal.convolve2d(I_x,kernel,mode='same')
I_x_2_sum =signal.convolve2d(I_x_2,kernel,mode='same')
I_y_sum =signal.convolve2d(I_y,kernel,mode='same')
I_y_2_sum = signal.convolve2d(I_y_2,kernel,mode='same')
I_xy_sum = signal.convolve2d(I_xy,kernel,mode='same')
I_yx_sum = signal.convolve2d(I_yx,kernel,mode='same')
'''
print("[Info] I_x_2_sum",I_x_2_sum)
print("[Info] I_y_2_sum",I_y_2_sum.shape)
print("[Info] I_xy_sum",I_xy_sum.shape)
print("[info],I_yx",I_yx.shape)
'''
list1=[]
for row in range(0,img.shape[0]):
    for col in range(0,img.shape[1]):
        Hessian_matrix = np.array([[I_x_sum[row][col] , I_xy_sum[row][col]],[I_yx_sum[row][col], I_y_2_sum[row][col]]])  ##Finding Hessian Matrx and its eigen value
        eigen_value=LA.eigvals(Hessian_matrix)  # Eigen value
        R = np.min(eigen_value)   ## SHi-tomsai criteria
        if (R>img[row][col]):   ## If score is bigger then that pixel is image
            new_img[row][col]=img[row][col]

cv2.putText(new_img,"Detected Corners",(100,50),cv2.FONT_HERSHEY_SIMPLEX,1,(255,255,255),2,cv2.LINE_AA)
cv2.putText(img,"original Image",(100,50),cv2.FONT_HERSHEY_SIMPLEX,1,(255,255,255),2,cv2.LINE_AA)
cv2.line(img,(0,0),(0,511),(255,0,0),5)
cv2.imshow("Detected Corners + Original image",np.hstack((new_img*255,img)))
cv2.waitKey(0)


import numpy as np

def point_lagrange3D(x, y, z, pointX, pointY):
    n = len(x)
    m = len(y)
    s = 0
    for i in range(n):
        for j in range(m):
            L_x = product(x, pointX, i)
            L_y = product(y, pointY, j)
            s += z[i, j] * L_x * L_y
    return s

def lagrange3D(x, y, z, vectX, vectY):
    dimX, dimY = len(vectX), len(vectY)
    la = np.zeros((dimX, dimY))
    for indexX in range(dimX):
        for indexY in range(dimY):
            la[indexX, indexY] = point_lagrange3D(x, y, z, vectX[indexX], vectY[indexY])
    return la

def product(vec, p, i):
    n = len(vec)
    prod = 1
    for index in range(n):
        if index != i:
            num = p - vec[index]
            den = vec[i] - vec[index]
            prod *= num/den
    return prod


import numpy as np
import scipy.stats

# References for KDE
# https://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.stats.gaussian_kde.html

_filename_png = 'kde_1d.png'
# caibrate the kde
mu = 0.0
sigma = 1.0
_loc = mu
_scale = sigma
_norm_rv = scipy.stats.norm(loc=_loc,scale=_scale)
data = _norm_rv.rvs(size=100)

print('simulation_data:{}'.format(str(type(data))))
print(data)

kde = scipy.stats.gaussian_kde(data)
print('kde_object:{}'.format(str(type(kde))))

# create the range of value for which we want to plot the kde
x_min = -5
x_max = 5
x_npoints = 1000
x = np.linspace(x_min,x_max,x_npoints)

# value of the KDE evaluate on the grid defined by x
kde_pdf_x = kde(x)
print('kde_pdf_x:{}'.format(str(type(kde_pdf_x))))

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x,kde_pdf_x)
fig.savefig(_filename_png)


import numpy as np
import warnings
warnings.filterwarnings("ignore")

def ps_scale_for_lat(lat):
    '''
    This function calculates the length scaling factor for a polar stereographic
       projection (ie. SSM/I grid) which can be used to correct area calculations.
       The scaling factor is defined (Snyder, 1982) as:

        k = (mc/m)*(t/tc), where

        m = cos(lat)/sqrt(1 - e2*sin(lat)^2)
        t = tan(Pi/4 - lat/2)/((1 - e*sin(lat))/(1 + e*sin(lat)))^(e/2)
        e2 = 0.006693883 is the earth eccentricity (Hughes ellipsoid)
        e = sqrt(e2)
        mc = m at the reference latitude (70 degrees)
        tc = t at the reference latitude (70 degrees)

        For the special case of the pole:
        kp = 1/2*mc*sqrt((1+e)^(1+e)*(1-e)^(1-e))/tc

        The ratio mc/tc is precalculated and stored in the variable mc_rc.
        See https://pubs/usgs.gov/pp/1395/report.pdf

        Areas calculated in polar stereographic need to be multiplied by
        k**2

        ref:Snyder, 1982, Map Projections used by the U.S. Geological Survey
            Page 160
            https://pubs/usgs.gov/pp/1395/report.pdf
    '''

    if np.nanmean(lat) > 0:
        hemisphere=1
    else:
        hemisphere=-1

    if ( hemisphere==1):
        #mc=0.3430355367529768
        #tc=0.17744189713382205
        mc_tc = 1.9332273960882438
    else:
        #mc/tc calculated at -71 degrees
        #mc=0.32654678137878085
        #tc=0.16840732452916296
        mc_tc=1.939029565915543 ; # for 71 deg, southern PS

    # for WGS84, a=6378137, 1/f = 298.257223563 -> 1-sqrt(1-e^2) = f
    # -> 1-(1-f)^2 = e2 =    0.006694379990141
    e2=0.006694379990141;  #from WGS84 parameters
    e = np.sqrt(e2);

    # absolute value of latitude in radians
    latr = np.abs(lat*np.pi/180.)
    # sin and cos of latitude
    slat = np.sin(latr)
    clat = np.cos(latr)

    m = clat/np.sqrt(1. - e2*slat**2)
    #print(m)
    t = np.tan(np.pi/4. - latr/2.)/((1.-e*slat) / (1.+e*slat))**(e/2.);
    #print(t)

    # distance scaling including special case of the pole
    k = t/m *mc_tc
    kp = 0.5*mc_tc*np.sqrt(((1.0+e)**(1.0+e))*((1.0-e)**(1.0-e)))
    scale = np.where(np.isclose(latr,np.pi/2.0),1.0/kp,1.0/k)
    return scale
    #
    # check: at S pole (this comes out right!)
    # if False:
    #     a=6378137.
    #     slat=np.sin(-90*np.pi/180)
    #     # radius of curvature
    #     Rc=a*(1-e2)/(1-e2*slat**2)**(3/2)
    #     L=Rc*.01*np.pi/180;
    #     A_e=L**2
    #     D=pc.data().from_dict({'latitude':np.array([-90, -89.99, -89.99]), 'longitude':np.array([0., 0., 90.])}).get_xy(EPSG=3031)
    #     A_ps = (D.x[2]-D.x[0])*(D.y[1]-D.y[0])
    #     print(A_e/A_ps)
    #     print(pc.ps_scale_for_lat(-89.9999)**2)


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

# dx/dt = x(1-x)
# x(0) = x0
def logistic(x, t):
    return x*(1 - x)

# closed form solution
def logistic_integral(x0, t):
    if x0 == 0:
        return 0*np.exp(-t)

    A = 1/x0 - 1
    return 1/(1 + A*np.exp(-t))

fig, axes = plt.subplots(2, 1)

ts = np.linspace(0, 10, 100)
ics = np.linspace(0.0, 5.0, 100)
for x0 in ics:
    xs = odeint(logistic, x0, ts)
    axes[0].plot(ts, xs)

    xs = logistic_integral(x0, ts)
    axes[1].plot(ts, xs)

plt.show()


# Value Function Iteration with IID Income
# Greg Kaplan 2017
# Translated by Tom Sweeney Dec 2020

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve
from scipy.interpolate import interp1d
from discrete_normal import discrete_normal
from lininterp1 import lininterp1

# PARAMETERS

## preferences
risk_aver = 2
beta = 0.95

## returns
r = 0.03
R = 1+r

## income risk: discretized N(mu,sigma^2)
mu_y = 1
sd_y = 0.2
ny = 5

## asset grids
na = 50
amax = 50
borrow_lim = 0
agrid_par = 0.5 # 1 for linear, 0 for L-shaped

## computation
max_iter = 1000
tol_iter = 1.0e-6
Nsim = 50000
Tsim = 500

## mpc options
mpcamount1  = 1.0e-10 # approximate thoeretical mpc
mpcamount2  = 0.10 # one percent of average income: approx $500

# OPTIONS
Display = 1
DoSimulate = 1
MakePlots = 1
ComputeMPC  = 1

# DRAW RANDOM NUMBERS

np.random.seed(2020)
yrand = np.random.rand(Nsim,Tsim)

# SET UP GRIDS

## assets
agrid = np.linspace(0,1,na).reshape(na,1)
agrid = agrid**(1/agrid_par)
agrid = borrow_lim + (amax-borrow_lim)*agrid

## income: disretize normal distribution
width = fsolve(lambda x: discrete_normal(ny,mu_y,sd_y,x)[0],2)
temp, ygrid, ydist = discrete_normal(ny,mu_y,sd_y,width)
ycumdist = np.cumsum(ydist)

# UTILITY FUNCTION

u = lambda c: (c**(1-risk_aver)-1)/(1-risk_aver)
u1 = lambda c: c**(-risk_aver)
u1inv = lambda u: u**(-1/risk_aver)

# INITIALIZE CONSUMPTION FUNCTION

conguess = np.zeros((na,ny))
for iy in range(0,ny):
    conguess[:,iy] = (r*agrid+ygrid[iy])[:,0]

# ITERATE ON EULER EQUATION WITH ENDOGENOUS GRID POINTS

con = conguess.copy()

Iter = 0
cdiff = 1000

while Iter<=max_iter and cdiff>tol_iter:
    Iter = Iter + 1
    sav = np.zeros((na,ny))
    ass1 = np.zeros((na,ny))

    conlast = con.copy()

    emuc = u1(conlast) @ ydist
    muc1 = beta*R*emuc
    con1 = u1inv(muc1)

    ## loop over income
    for iy in range(0,ny):

        ass1[:,iy] = ((con1 + agrid - ygrid[iy])/R)[:,0]

        ## loop over current period ssets
        for ia in range(0,na):
            if agrid[ia]<ass1[0,iy]: # borrowing constraint binds
                sav[ia,iy] = borrow_lim
            else: # borrowing constraint does not bind
                sav[ia,iy] = lininterp1(ass1[:,iy],agrid[:,0],agrid[ia])

        con[:,iy] = (R*agrid+ygrid[iy])[:,0] - sav[:,iy]

    cdiff = np.max(abs(con-conlast))
    if Display>=1:
        print('Iteration no. ' + str(Iter), ' max con fn diff is ' + str(cdiff))

# SIMULATE
if DoSimulate==1:
    yindsim = np.zeros((Nsim,Tsim), dtype=int)
    asim = np.zeros((Nsim,Tsim))

    ## create interpolating function
    savinterp = list()
    for iy in range(0,ny):
        savinterp.append(interp1d(agrid[:,0],sav[:,iy],'linear'))

    ## loop over time periods
    for it in range(0,Tsim):
        if Display >= 1 and (it+1)%100 == 0:
            print(' Simulating, time period ' + str(it+1))

        ## income realization: note we vectorize simulations at once because
        ## of matlab, in other languages we would loop over individuals
        yindsim[yrand[:,it]<=ycumdist[0],it] = 0
        for iy in range(1,ny):
            yindsim[np.logical_and(yrand[:,it]>ycumdist[iy-1], yrand[:,it]<=ycumdist[iy]),it] = iy

        ## asset choice
        if it < Tsim-1:
            for iy in range(0,ny):
                asim[yindsim[:,it]==iy,it+1] = savinterp[iy](asim[yindsim[:,it]==iy,it])

    ## assign actual income values
    ysim = ygrid[yindsim]

# MAKE PLOTS
if MakePlots==1:

    ## consumption policy function
    plt.plot(agrid,con[:,0],'b-',label = 'Lowest income state')
    plt.plot(agrid,con[:,ny-1],'r-', label = 'Highest income state')
    plt.grid()
    plt.xlim((0,amax))
    ### plt.title('Consumption Policy Function')
    plt.title('Consumption')
    plt.legend()
    plt.show()

    ## savings policy function
    plt.plot(agrid,sav[:,0]-agrid[:,0],'b-')
    plt.plot(agrid,sav[:,ny-1]-agrid[:,0],'r-')
    plt.plot(agrid,np.zeros((na,1)),'k',linewidth=0.5)
    plt.grid()
    plt.xlim((0,amax))
    ### plt.title('Savings Policy Function (a''-a)')
    plt.title('Savings')
    plt.show()

    ## nice zoom
    xlimits = (0,1)
    xlimind = np.ones(na, dtype=bool)
    if np.min(agrid) < xlimits[0]:
        xlimind = np.logical_and(xlimind,(agrid[:,0]>=np.max(agrid[agrid<xlimits[0]])))
    elif np.min(agrid) > xlimits[1]:
        xlimind = 0
    if np.max(agrid) > xlimits[1]:
        xlimind = np.logical_and(xlimind,(agrid[:,0]<=np.min(agrid[agrid>xlimits[1]])))
    elif np.max(agrid) < xlimits[0]:
        xlimind = 0

    ## consumption policy function: zoomed in
    plt.plot(agrid[xlimind],con[xlimind,0],'b-o',linewidth=2)
    plt.plot(agrid[xlimind],con[xlimind,ny-1],'r-o',linewidth=2)
    plt.grid()
    plt.xlim(xlimits)
    plt.title('Consumption: Zoomed')
    plt.show()

    ## savings policy function: zoomed in
    plt.plot(agrid[xlimind],sav[xlimind,0]-agrid[xlimind,0],'b-o',linewidth=2)
    plt.plot(agrid[xlimind],sav[xlimind,ny-1]-agrid[xlimind,0],'r-o',linewidth=2)
    plt.plot(agrid,np.zeros((na,1)),'k',linewidth =0.5)
    plt.grid()
    plt.xlim(xlimits)
    plt.title('Savings: Zoomed (a\'-a)')
    plt.show()

    ## income distribution
    plt.hist(ysim[:,Tsim-1],len(ygrid),facecolor=(0,0.5,0.5),edgecolor='blue')
    plt.ylabel('')
    plt.title('Income distribution')
    plt.show()

    ## asset distribution
    plt.hist(asim[:,Tsim-1],100,facecolor=(.7,.7,.7),edgecolor='black')
    plt.ylabel('')
    plt.title('Asset distribution')
    plt.show()

    ## convergence check
    plt.plot(range(0,Tsim),np.mean(asim,0),'k-',linewidth=1.5)
    plt.xlabel('Time Period')
    plt.title('Mean Asset Convergence')
    plt.show()

    ## asset distribution statistics
    aysim = asim[:,Tsim-1]/np.mean(ysim[:,Tsim-1])
    print('Mean assets: ' + str(np.mean(aysim)))
    print('Fraction borrowing constrained: ' + str(np.sum(aysim==borrow_lim)/Nsim * 100) + '%')
    print('10th Percentile: ' + str(np.quantile(aysim,.1)))
    print('50th Percentile: ' + str(np.quantile(aysim,.5)))
    print('90th Percentile: ' + str(np.quantile(aysim,.9)))
    print('99th Percentile: ' + str(np.quantile(aysim,.99)))

# COMPUTE MPCs
if ComputeMPC==1:

    ## theoretical mpc lower bound
    mpclim = R*((beta*R)**(-1/risk_aver))-1

    coninterp = list()
    mpc1 = np.zeros((na,ny))
    mpc2 = np.zeros((na,ny))

    for iy in range(0,ny):
        ## create interpolating function
        coninterp.append(interp1d(agrid[:,0],con[:,iy],'linear',fill_value='extrapolate'))

        mpc1[:,iy] = ( coninterp[iy](agrid[:,0]+mpcamount1) - con[:,iy] ) / mpcamount1
        mpc2[:,iy] = ( coninterp[iy](agrid[:,0]+mpcamount2) - con[:,iy] ) / mpcamount2

    ## mpc functions
    plt.plot(agrid,mpc1[:,0],'b-',linewidth=1,label='Lowest income state: amount 1')
    plt.plot(agrid,mpc2[:,0],'b--',linewidth=1,label='Lowest income state: amount 2')
    plt.plot(agrid,mpc1[:,ny-1],'r-',linewidth=1,label='Highest income state: amount 1')
    plt.plot(agrid,mpc2[:,ny-1],'r--',linewidth=1,label='Highest income state: amount 2')
    plt.plot(agrid,mpclim*np.ones(np.shape(agrid)),'k:',linewidth=1,label='Theoretical MPC limit = '+str(mpclim))
    plt.grid()
    plt.xlim((0,10))
    plt.title('MPC Function')
    plt.legend()
    plt.show()

    ## mpc distribution
    mpc1sim = np.zeros((Nsim,1))
    mpc2sim = np.zeros((Nsim,1))
    for iy in range(0,ny):
        mpc1sim[yindsim[:,Tsim-1]==iy,0] = ( coninterp[iy](asim[yindsim[:,Tsim-1]==iy,Tsim-1]+mpcamount1) - coninterp[iy](asim[yindsim[:,Tsim-1]==iy,Tsim-1]) ) / mpcamount1
        mpc2sim[yindsim[:,Tsim-1]==iy,0] = ( coninterp[iy](asim[yindsim[:,Tsim-1]==iy,Tsim-1]+mpcamount2) - coninterp[iy](asim[yindsim[:,Tsim-1]==iy,Tsim-1]) ) / mpcamount2

    plt.hist(mpc1sim,np.linspace(0,1.5,76),facecolor=(.7,.7,.7),edgecolor='black',linestyle='-')
    plt.grid()
    plt.ylabel('')
    plt.title('MPC distribution')

    # mpc distribution statistics
    print('Mean MPC amount 1: ' + str(np.mean(mpc1sim)))
    print('Mean MPC amount 2: ' + str(np.mean(mpc2sim)))
    plt.show()


from .model_base import Model
import numpy as np

class LinearRegressionModel(Model):
    """
    A model for Linear Regression.
    Arguments:
        input_features (int)
        output_features (int)
        normalize (bool)
    """

    def __init__(self, input_features, output_features, normalize=False):
        self.input_features = input_features
        self.output_features = output_features
        self.weights = np.zeros((input_features + 1, output_features))
        self.normalize = normalize
        if normalize:
            self.std = np.zeros(input_features)
            self.mean = np.zeros(input_features)

    @staticmethod
    def design_matrix(X):
        """Take dataset X and return the design-matrix (dm_X)"""
        return np.c_[np.ones(X.shape[0]), X]

    def normalize_matrix(self, X, fit):
        """Take dataset X and return the normalize matrix"""
        if fit:
            self.mean = X.mean(axis=0)
            self.std = X.std(axis=0)
            if 0 in self.std:
                raise ValueError(
                    'The variance of the data is 0, meaning prediction has no meaning.')

        return (X - self.mean) / self.std

    def predict(self, X):
        """Predict the output after training by given dataset X"""
        if self.normalize:
            return self.design_matrix(self.normalize_matrix(X, False)) @ self.weights

        # Predict without normalization
        return self.design_matrix(X) @ self.weights

    def fit(self, X, Y, epochs=None, learn_rate=None):
        """
        Training the model by dataset X and the true values of Y.
        Arguments:
            X (np.ndarray): Dataset.
            Y (np.ndarray): True values.
            Arguments for 'Gradient Descent' method:
                epochs (int): Num of iteration on GD function.
                learn_rate (float): The rate of the learning of the model.
        """
        if self.normalize:  # Normalize the matrix
            dm_X = self.design_matrix(self.normalize_matrix(X, True))
        else:
            dm_X = self.design_matrix(X)

        # 'Gradient Descent' method if learn_rate and epochs was given
        if learn_rate and epochs:
            for _ in range(epochs):  # Calculate the new weights by vectorization approach
                self.weights -= (learn_rate /
                                 Y.shape[0]) * dm_X.T @ (dm_X @ self.weights - Y)

        # 'Normal equation' method if learn_rate or epochs wasn't given
        else:
            self.weights = np.linalg.inv(dm_X.T @ dm_X) @ dm_X.T @ Y

    def loss(self, Y_prediction, Y_true):
        """ Mean Squared Error (MSE) loss.

                      1
        MSE(Y, Y') = --- sum(i=0,N) ||y_i - y_i'||^2
                     2N

        Where ||x|| is the L2 norm of a vector 'x' of dimension C,
        y_i, y_i' are i-th samples from the batches Y, Y' respectively.
        This simplifies to:

                      1
        MSE(Y, Y') = --- sum(i=0,N) sum(k=1,C) (y[i,k] - y'[i,k])^2
                     2N
        """
        batch_size = Y_true.shape[0]
        return 1 / (2*batch_size) * np.sum((Y_true - Y_prediction)**2)

class LogisticRegressionModel(Model):
    """
    A model for Logistic Regression i.e. classification.
    Arguments:
        input_features (int)
        normalize (bool)
    """

    def __init__(self, input_features, normalize=False):
        self.input_features = input_features
        self.weights = np.zeros((input_features + 1, 1))
        self.normalize = normalize
        if normalize:
            self.std = np.zeros(input_features)
            self.mean = np.zeros(input_features)

    @staticmethod
    def design_matrix(X):
        """Take dataset X and return the design-matrix (dm_X)"""
        return np.c_[np.ones(X.shape[0]), X]

    @staticmethod
    def sigmoid_fn(z):
        """Calculate the Sigmoid-function of an array or a scalar"""
        return 1/(1+np.exp(-z))

    def normalize_matrix(self, X, fit):
        """Take dataset X and return the normalize matrix"""
        if fit:
            self.mean = X.mean(axis=0)
            self.std = X.std(axis=0)
            if 0 in self.std:
                raise ValueError(
                    'The variance of the data is 0, meaning prediction has no meaning.')

        return (X - self.mean) / self.std

    def predict(self, X, prob=False):
        """
        Predict the output after training by given dataset X
        Arguments:
            X (np.ndarray): dataset.
            prob (bool):
                True - return the probability of each case to be true (1).
                False - return the prediction of each case 1 or 0.
        """
        if self.normalize:  # Normalize the matrix
            dm_X = self.design_matrix(self.normalize_matrix(X, False))
        else:
            dm_X = self.design_matrix(X)

        # Calculate the probability of each case to be True (1)
        prob_matrix = self.sigmoid_fn(dm_X @ self.weights)
        # Return 1 if probability > 0.5 else 0
        return prob_matrix if prob else np.round(prob_matrix)

    def fit(self, X, y, epochs=None, learn_rate=None):
        """
        Training the model by dataset X and the true values of y.
        Arguments:
            X (np.ndarray): Dataset.
            Y (np.ndarray): True values.
            epochs (int): Num of iteration on GD function.
            learn_rate (float): The rate of the learning of the model.
        """
        if self.normalize:  # Normalize the matrix
            dm_X = self.design_matrix(self.normalize_matrix(X, True))
        else:
            dm_X = self.design_matrix(X)

        # 'Gradient Descent' method
        if learn_rate and epochs:
            for _ in range(epochs):  # Calculate the new weights by vectorization approach
                self.weights -= (learn_rate /
                                 y.shape[0]) * dm_X.T @ (self.sigmoid_fn(dm_X @ self.weights) - y)

    def loss(self, y_prediction, y_true):
        """ Cross entropy loss i.e. logistic loss."""
        return -np.mean(y_true * np.log(y_prediction) + (1-y_true) * np.log(1-y_prediction))


#%%
"""
Created on July 05  2021
The BSHW model and implied volatilities term structure computerion

This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""

import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
import scipy.stats as st
import enum
import scipy.optimize as optimize

# set i= imaginary number
i   = np.complex(0.0,1.0)

# time-step needed for differentiation
dt = 0.0001

# This class defines puts and calls
class OptionType(enum.Enum):
    CALL = 1.0
    PUT = -1.0

# Black-Scholes Call option price
def BS_Call_Option_Price(CP,S_0,K,sigma,tau,r):
    if K is list:
        K = np.array(K).reshape([len(K),1])
    d1    = (np.log(S_0 / K) + (r + 0.5 * np.power(sigma,2.0))
    * tau) / float(sigma * np.sqrt(tau))
    d2    = d1 - sigma * np.sqrt(tau)
    if CP == OptionType.CALL:
        value = st.norm.cdf(d1) * S_0 - st.norm.cdf(d2) * K * np.exp(-r * tau)
    elif CP == OptionType.PUT:
        value = st.norm.cdf(-d2) * K * np.exp(-r * tau) - st.norm.cdf(-d1)*S_0
    return value

# Implied volatility method
def ImpliedVolatilityBlack76(CP,frwdMarketPrice,K,T,frwdStock):
    func = lambda sigma: np.power(BS_Call_Option_Price(CP,frwdStock,K,sigma,T,0.0) - frwdMarketPrice, 1.0)
    impliedVol = optimize.newton(func, 0.2, tol=1e-9)
    #impliedVol = optimize.brent(func, brack= (0.05, 2))
    return impliedVol

def BSHWVolatility(T,eta,sigma,rho,lambd):
    Br= lambda t,T: 1/lambd * (np.exp(-lambd*(T-t))-1.0)
    sigmaF = lambda t: np.sqrt(sigma * sigma + eta * eta * Br(t,T) * Br(t,T)\
                               - 2.0 * rho * sigma * eta * Br(t,T))
    zGrid = np.linspace(0.0,T,2500)
    sigmaC = np.sqrt(1/T*integrate.trapz(sigmaF(zGrid)*sigmaF(zGrid), zGrid))
    return sigmaC

def BSHWOptionPrice(CP,S0,K,P0T,T,eta,sigma,rho,lambd):

    frwdS0 = S0 / P0T
    vol = BSHWVolatility(T,eta,sigma,rho,lambd)
    # As we deal with the forward prices we evaluate Black's 76 prices
    r = 0.0
    BlackPrice = BS_Call_Option_Price(CP,frwdS0,K,vol,T,r)
    return  P0T * BlackPrice

def mainCalculation():
    CP  = OptionType.CALL

    # HW model settings
    lambd = 0.1
    eta   = 0.01
    sigma = 0.2
    rho   = 0.3
    S0    = 100

    # Strike equals stock value, thus ATM
    K = [100]
    K = np.array(K).reshape([len(K),1])

    # We define a ZCB curve (obtained from the market)
    P0T = lambda T: np.exp(-0.05*T)

    # Maturitires at which we compute implied volatility
    TMat = np.linspace(0.1,5.0,20)

    # Effect of lambda
    plt.figure(2)
    plt.grid()
    plt.xlabel('maturity, T')
    plt.ylabel('implied volatility')
    lambdV = [0.001,0.1,0.5,1.5]
    legend = []
    for lambdaTemp in lambdV:
       IV =np.zeros([len(TMat),1])
       for idx in range(0,len(TMat)):
           T = TMat[idx]
           val = BSHWOptionPrice(CP,S0,K,P0T(T),T,eta,sigma,rho,lambdaTemp)
           frwdStock = S0 / P0T(T)
           valFrwd = val / P0T(T)
           IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K,T,frwdStock)
       plt.plot(TMat,IV*100.0)
       legend.append('lambda={0}'.format(lambdaTemp))
    plt.legend(legend)

    # Effect of eta
    plt.figure(3)
    plt.grid()
    plt.xlabel('maturity, T')
    plt.ylabel('implied volatility')
    etaV = [0.001,0.05,0.1,0.15]
    legend = []
    for etaTemp in etaV:
       IV =np.zeros([len(TMat),1])
       for idx in range(0,len(TMat)):
           T = TMat[idx]
           val = BSHWOptionPrice(CP,S0,K,P0T(T),T,etaTemp,sigma,rho,lambd)
           frwdStock = S0 / P0T(T)
           valFrwd = val/P0T(T)
           IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K,T,frwdStock)
       plt.plot(TMat,IV*100.0)
       legend.append('eta={0}'.format(etaTemp))
    plt.legend(legend)

    # Effect of sigma
    plt.figure(4)
    plt.grid()
    plt.xlabel('maturity, T')
    plt.ylabel('implied volatility')
    sigmaV = [0.1,0.2,0.3,0.4]
    legend = []
    for sigmaTemp in sigmaV:
       IV =np.zeros([len(TMat),1])
       for idx in range(0,len(TMat)):
           T = TMat[idx]
           val = BSHWOptionPrice(CP,S0,K,P0T(T),T,eta,sigmaTemp,rho,lambd)
           frwdStock = S0 / P0T(T)
           valFrwd = val / P0T(T)
           IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K,T,frwdStock)
       plt.plot(TMat,IV*100.0)
       legend.append('sigma={0}'.format(sigmaTemp))
    plt.legend(legend)

    # Effect of rho
    plt.figure(5)
    plt.grid()
    plt.xlabel('maturity, T')
    plt.ylabel('implied volatility')
    rhoV = [-0.7, -0.3, 0.3, 0.7]
    legend = []
    for rhoTemp in rhoV:
       IV =np.zeros([len(TMat),1])
       for idx in range(0,len(TMat)):
           T = TMat[idx]
           val = BSHWOptionPrice(CP,S0,K,P0T(T),T,eta,sigma,rhoTemp,lambd)
           frwdStock = S0 / P0T(T)
           valFrwd = val / P0T(T)
           IV[idx] = ImpliedVolatilityBlack76(CP,valFrwd,K,T,frwdStock)
       plt.plot(TMat,IV*100.0)
       legend.append('rho={0}'.format(rhoTemp))
    plt.legend(legend)

mainCalculation()

Lecture 09-Hybrid Models and Stochastic Interest Rates/Materials/BSHW_ImpliedVolatility.py


"""Link functions related to log."""

# author: Benjamin Cross
# email: btcross26@yahoo.com
# created: 2019-08-26

import numpy as np

from .base_class import BaseLink

class LogLink(BaseLink):
    """Implementation of the log link function."""

    def __init__(self, summand: float = 0.0):
        """
        Class initializer.

        Extends the BaseLink class intializer.

        Parameters
        ----------
        summand: float
            Summand of the log in the link implementation - i.e.,
            link = log(y + summand).
        """
        super().__init__()
        self.summand_ = summand

    def _link(self, y: np.ndarray) -> np.ndarray:
        """
        Get the link, eta, as a function of y.

        Overrides BaseLink._link.
        """
        return np.log(y + self.summand_)

    def _inverse_link(self, eta: np.ndarray) -> np.ndarray:
        """
        Get the target, y, as a function of the link, `eta`.

        Overrides BaseLink._inverse_link.
        """
        return np.exp(eta) - self.summand_

    def dydeta(self, y: np.ndarray) -> np.ndarray:
        """
        Get the derivative of `y` with respect to the link as a function of y.

        Overrides BaseLink.dydeta.
        """
        return y + self.summand_

    def d2ydeta2(self, y: np.ndarray) -> np.ndarray:
        """
        Get the second derivative of `y` with respect to the link as a function of y.

        Overrides BaseLink.d2ydeta2.
        """
        return y + self.summand_

class Logp1Link(LogLink):
    """Log plus 1 link function implementation."""

    def __init__(self) -> None:
        """Class initializer extends LogLink initializer, setting summand equal to 1.0."""
        super().__init__(summand=1.0)