import numpy as np
from sklearn.utils import check_random_state
from ..utils import rand_orthog, param_as_list

def sample_joint_factor_model(
    n_views,
    n_samples,
    n_features,
    joint_rank=1,
    noise_std=1,
    m=1.5,
    random_state=None,
    return_decomp=False,
):
    """
    Samples from a low rank, joint factor model where there is one set of
    shared scores.

    Parameters
    -----------
    n_views : int
        Number of views to sample

    n_samples : int
        Number of samples in each view

    n_features: int, or list of ints
        Number of features in each view. A list specifies a different number
        of features for each view.

    joint_rank : int (default 1)
        Rank of the common signal across views.

    noise_std : float (default 1)
        Scale of noise distribution.

    m : float (default 1.5)
        Signal strength.

    random_state : int or RandomState instance, optional (default=None)
        Controls random orthonormal matrix sampling and random noise
        generation. Set for reproducible results.

    return_decomp : boolean, default=False
        If ``True``, returns the ``view_loadings`` as well.

    Returns
    -------
    Xs : list of array-likes or numpy.ndarray
        - Xs length: n_views
        - Xs[i] shape: (n_samples, n_features_i)
        List of samples data matrices

    U: (n_samples, joint_rank)
        The true orthonormal joint scores matrix. Returned if
        ``return_decomp`` is True.

    view_loadings: list of numpy.ndarray
        The true view loadings matrices. Returned if
        ``return_decomp`` is True.

    Notes
    -----
    For b = 1, .., B
        X_b = U @ diag(svals) @ W_b^T + noise_std * E_b

    where U and each W_b are orthonormal matrices. The singular values are
    linearly increasing following (Choi et al. 2017) section 2.2.3.
    """
    rng = check_random_state(random_state)
    n_features = param_as_list(n_features, n_views)

    view_loadings = [rand_orthog(d, joint_rank, random_state=rng)
                     for d in n_features]

    svals = np.arange(1, 1 + joint_rank).astype(float)
    svals *= m * noise_std * (n_samples * max(n_features)) ** (1 / 4)
    U = rng.normal(size=(n_samples, joint_rank))
    U = np.linalg.qr(U)[0]

    # Random noise for each view
    Es = [noise_std * np.random.RandomState(seed).normal(size=(n_samples, d))
          for d, seed in zip(n_features, rng.permutation(np.arange(n_views)))]
    Xs = [(U * svals) @ view_loadings[b].T + Es[b] for b in range(n_views)]

    if return_decomp:
        return Xs, U, view_loadings
    else:
        return Xs


import math
from random import random
from scipy.stats import expon
from scipy.stats import norm

# METHODS #

def exponential_distr (lamda, N = 1, use_scipy = False):

    #Using the inverse of the CDF
    if not use_scipy:
        ans = []
        for i in range (0,N):
            ans.append(-(1 / lamda) * math.log(random()))
        if N == 1: return ans [0]
        return ans

    # Using scipy
    else:
        if N == 1: return expon.rvs(scale = 1/lamda, size = 1)[0]
        else: return expon.rvs(scale = 1/lamda, size = N)

def normal_distr (O_2 = 1, u_ = 0, use_scipy = False):

    #Box-Muller
    u1 = random()
    u2 = random()
    _std = math.sqrt(O_2)
    if not use_scipy:
        sample = abs(math.cos((2*math.pi*u1)*math.sqrt(2*math.log(1/u2))))
        return sample / _std + u_

    # Using scipy
    else:
        return abs(norm.rvs(loc = u_, scale = _std))


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error

# Data preprocessing
data = pd.read_csv("RealEstate.csv")

# Converting Pandas dataframe to numpy array
X = data.Size.values
Y = data.Price.values

# Mean X and Y
mean_x = np.mean(X)
mean_y = np.mean(Y)

m = X.shape[0]  #  number of samples

# Using the OLS (Ordinary Least Squares)
# formula to calculate theta0 and theta1
numer = 0
denom = 0
for i in range(m):
    numer += (X[i] - mean_x) * (Y[i] - mean_y)
    denom += (X[i] - mean_x) ** 2
theta1 = numer / denom
theta0 = mean_y - (theta1 * mean_x)

# Printing coefficients
print(f"Coefficients theta0 = {theta0}, theta1 = {theta1} ")

# Predicted Values
Y_pred = theta0 + theta1 * X

# Model Evaluation
def rmse(Y, Y_pred):
    rmse = np.sqrt(sum((Y - Y_pred) ** 2) / Y.shape[0])
    return rmse

def r2_score(Y, Y_pred):
    mean_y = np.mean(Y)
    ss_tot = sum((Y - mean_y) ** 2)
    ss_res = sum((Y - Y_pred) ** 2)
    r2 = 1 - (ss_res / ss_tot)
    return r2

# Print Scores
print("RMSE = ", rmse(Y, Y_pred))
print("R2 Score = ", r2_score(Y, Y_pred))

# Visualization
# Ploting Regression Line
plt.plot(X, Y_pred, color='#c93e4e', label='Regression Line')
# Ploting Scatter Points
plt.scatter(X, Y, c='#54a774', label='Scatter Plot')
plt.xlabel('Size')
plt.ylabel('Price')
plt.legend()
plt.show()


import scipy.stats

def t(alpha, n):
    # if n samples pass n-1
    return scipy.stats.t.ppf(1-(alpha), n)

def norm(alpha):
    # if n samples pass n-1
    return scipy.stats.norm.ppf(1-(alpha))

ls = [3.27,
      16.25,
      15.19,
      7.24,
      2.93]

mean = sum(ls) / len(ls)
se = (sum([(x - sum(ls) / len(ls)) **
      2 for x in ls]) / (len(ls)-1)) ** 0.5 / (len(ls) ** 0.5)

print("mean:", mean)

print("standard deviation S:",
      (sum([(x - sum(ls) / len(ls)) ** 2 for x in ls]) / (len(ls)-1)) ** 0.5)

print("standard error S / (R^.5):", se)

level = t(0.025, len(ls)-1)
print("Confidence interval:", mean - level * se, "to", mean + level * se)


from math import sqrt
import matplotlib.pyplot as plt
import numpy as np
import random

def main():
    p = 1.5
    q = -10

    x = []
    y = []

    for a in range(0, 100, 5):
        x.append(a)

    for a in range(0, 100, 5):
        # y = (mx + c) + random value
        # The predicted line should have m close to p, c close to q
        y.append(p*a + q + float(random.randint(-100, 100)/10))

    # --------------------------------------------------------------------------
    # Predicted Line
    # y = mx + c
    m = 0
    c = 0

    learning_rate = 0.0000005
    epochs = 1000
    loss_value = epochs
    prev_loss = 0
    loss_diff = 1

    loss = []
    n_epoch = []

    # for e in range(epochs):
    e = 0
    while(loss_diff > 0.00001):
        dm = 0
        dc = 0
        sum1 = 0
        sum2 = 0
        err = 0
        prev_loss = loss_value
        e = e+1

        for k in range(len(x)):
            sum1 = sum1 + (x[k]*(y[k]-(m*x[k]+c)))
            sum2 = sum2 + (y[k]-(m*x[k]+c))
            err = err + ((y[k]-(m*x[k]+c))*(y[k]-(m*x[k]+c)))

        dm = (-2/len(x))*sum1
        dc = (-2/len(x))*sum2
        loss_value = err/len(x)
        m = m-learning_rate*dm
        c = c-learning_rate*dc

        n_epoch.append(e+1)
        loss.append(loss_value)
        loss_value = sqrt(loss_value)

        loss_diff = prev_loss - loss_value
        print(f"Epoch: {e}, loss: {loss_value}")

    print(f"m= {m}, c= {c}")

    y_preds = []
    for a in range(len(x)):
        y_preds.append(float(m*x[a] + c))

    # print(len(x), len(y_preds))
    plt.figure(1)
    plt.plot(n_epoch, loss, c="blue")
    plt.xlabel("Epochs")
    plt.ylabel("Loss")
    plt.title("Loss Curve")
    plt.grid(True)

    plt.figure(2)
    plt.plot(x, y_preds, c="red")
    plt.scatter(x, y, c="blue")
    plt.xlabel("X")
    plt.ylabel("Y")
    plt.title("Linear Regression")
    plt.grid(True)
    plt.show()

if __name__ == "__main__":
    main()
    # x = [1, 2, 3, 4, 5]
    # y = [2, 4, 6, 8, 10]
    # plt.plot(x, y)

    # plt.xlabel("X-Axis")
    # plt.ylabel("Y-Axis")
    # plt.title("Linear Regression")
    # plt.grid(True)
    # plt.show()


import math
import numpy as np
import vg

def test_almost_unit_length():
    unit_vectors = np.array(
        [
            [1.0, 0.0, 0.0],
            [0.0, -1.0, 0.0],
            [0.0, 0.0, 1.000000001],
            [1.0 / math.sqrt(3), 1.0 / math.sqrt(3), 1.0 / math.sqrt(3)],
        ]
    )

    for v in unit_vectors:
        assert vg.almost_unit_length(np.array(v)) == True  # noqa: E712

    assert vg.almost_unit_length(np.array(unit_vectors)).tolist() == [
        True,
        True,
        True,
        True,
    ]

    non_unit_vectors = np.array(
        [[1.0, 1.0, 0.0], [-1.0, -1.3, 0.0], [2.0, 2.0, 1.0], [1.000001, 1.0, 0.0]]
    )

    for v in non_unit_vectors:
        assert vg.almost_unit_length(np.array(v)) == False  # noqa: E712

    assert vg.almost_unit_length(np.array(non_unit_vectors)).tolist() == [
        False,
        False,
        False,
        False,
    ]

    assert vg.almost_unit_length(
        np.vstack([unit_vectors, non_unit_vectors])
    ).tolist() == [True, True, True, True, False, False, False, False]


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
from scipy.optimize import curve_fit
import matplotlib.colors as mcolors

#Write with LaTeX
rc('text', usetex=True)
rc('font', family='serif')

def func(x, a, b):
    return (a * x) + b

# Data
B1 = np.array([9.38, 12.46, 15.57])
dB1 = np.array([0.04, 0.04, 0.04])
r1 = np.array([0.217, 0.28, 0.38])
dr1 = np.array([0.024, 0.04, 0.07])

B2 = np.array([9.38, 12.46, 15.57])
dB2 = np.array([0.04, 0.04, 0.04])
r2 = np.array([0.2, 0.2500, 0.33])
dr2 = np.array([0.02, 0.03, 0.06])

# Fitting
x = np.linspace(0.15, 0.4, 5)

popt1, pcov1 = curve_fit(func, r1, B1, sigma=1./(dB1*dB1))
perr1 = np.sqrt(np.diag(pcov1))
popt2, pcov2 = curve_fit(func, r2, B2, sigma=1./(dB2*dB2))
perr2 = np.sqrt(np.diag(pcov2))

# Plot
fig, ax = plt.subplots(1, 1)

# B1 = B1(1/r1)
ax.errorbar(r1, B1, xerr = dr1, yerr = dB1, capsize=3, color='black', elinewidth=1, markeredgewidth=1, linestyle='None', marker='o', label='Calculated \n Values of $B_1$')
ax.plot(x, func(x, *popt1), color='orange', label='$B1 = B1(1/r_1)$', linewidth=1.5)

# B2 = B2(1/r2)
ax.errorbar(r2, B2, xerr = dr2, yerr = dB2, capsize=3, color='black', elinewidth=1, markeredgewidth=1, linestyle='None', marker='s', label='Calculated \n Values of $B_2$')
ax.plot(x, func(x, *popt2), color='royalblue', label='$B2 = B2(1/r_2)$', linewidth=1.5)

# Figure Specifications
ax.set_ylabel('$B$ $(\mathrm{10^{-4}\,\mathrm{T}})$')
ax.set_xlabel('$1/r$ $(\mathrm{1/\mathrm{cm}})$')

ax.legend(loc = 'upper left', prop={'size': 11})

# Show the major grid lines with dark grey lines
ax.grid(b=True, which='major', color='#666666', linestyle='--')

# Show the minor grid lines
ax.minorticks_on()
ax.grid(b=True, which='minor', color='#999999', linestyle='--', alpha=0.2)

# fix quality
fig.tight_layout()

plt.show()

# Print lines' slopes and constant coefficients
print(f"\n\n a1 = {'%0.5f'%popt1[0]} ± {'%0.5f'%perr1[0]}", f",b1 = {'%0.5f'%popt1[1]} ± {'%0.5f'%perr1[1]}")
print(f"\n\n a2 = {'%0.5f'%popt2[0]} ± {'%0.5f'%perr2[0]}", f",b2 = {'%0.5f'%popt2[1]} ± {'%0.5f'%perr2[1]}")


import numpy as np
from os import path

def calcZScore(data):
    # # of std deviation away from mean
    mn = np.mean(data)
    sd = np.std(data)
    return mn - data/sd

file_name = path.join(path.dirname(__file__),'../../data/housing-data.csv')

data = np.genfromtxt(file_name, delimiter=',', skip_header=1)
x = data[:,0]
y = data[:,1]
s_size = len(x)
x_zsore = calcZScore(x)
y_zscore = calcZScore(y)
x_mean = np.mean(x)
y_mean = np.mean(y)

cor_coff = (x_zsore * y_zscore).sum()/s_size

print(cor_coff)

# y = mx + b
# m = corelation_coff * y_std/x_std

# Correlation Coefficent
# 1/observations * (x_zscore * y_zscore).sum()


import numpy as np
from scipy.special import gamma

# package
from grAdapt.utils.sampling import bounds_range

def volume_hypersphere(dim, radius=1):
    """Calculates the volume of a hypersphere given the radius and the dimension

    Parameters
    ----------
    dim : int
    radius : float

    Returns
    -------
    float
    """
    return (radius ** dim) * np.pi**(dim/2) / (gamma(dim/2 + 1))

def volume_hyperrectangle(bounds):
    """Calculates the volume of a hyperrectangle of any dimension

    Parameters
    ----------
    bounds : list of tuples.
        Each tuple in the list defines the bounds for the corresponding variable
        Example: [(1, 2), (2, 3), (-1, 4)...]

    Returns
    -------

    """
    return np.prod(list(map(bounds_range, bounds)))


import numpy as np
import matplotlib.pyplot as plt

def f(x):
    return (x**2)-3*x-10

# a:left bound
# b:right bound
# eps: the distance between a and b
def bisection(a,b,eps):
    #calculate the iteration time
    iteration=0
    while(1):
        x_0=0.5*(a+b)
        if f(a)*f(x_0)<0:
            b=x_0
        else:
            a=x_0

        iteration+=1

        if abs(a-b)<eps:
            break

    return a, iteration

#to generate the different eps
N=30
error=[]
for i in range(1,N,1):
    #eps from 10^-1 ~ 10^-N
    error.append((10**-i))

#to stroe the different iteration with the different eps
iteration_eps=[]

#to store the root of different eps
root=[]

#calculate the iteration times in different eps and the root
for i in error:
    rt,it=bisection(4.2,6.3,i)
    root.append(rt)
    iteration_eps.append(it)

print("the root in different eps is:")
print(root)
print("the iteration times in different eps is:")
print(iteration_eps)

#visualize the graph
x=np.arange(1,N,1)
plt.plot(x,iteration_eps)
plt.title("different eps $vs$ iteration times")
plt.xlabel("$-log(eps)$")
plt.ylabel("iteration times")
plt.show()

#conclusion:
#because the graph is almost a straight line with the x-axis in log scale
#therefore, we can figure that the iteration is almost increase in power scale


import numpy as np

# Define conversions in x and y from pixels space to meters
ym_per_pix = 30/720 # meters per pixel in y dimension
xm_per_pix = 3.7/700 # meters per pixel in x dimension

def measure(ploty, leftx, rightx):
	'''
	Calculates the curvature of polynomial functions in meters.
	'''

	left_fit_cr = np.polyfit(ploty*ym_per_pix, leftx*xm_per_pix, 2)
	right_fit_cr = np.polyfit(ploty*ym_per_pix, rightx*xm_per_pix, 2)

	# Define y-value where we want radius of curvature
	# We'll choose the maximum y-value, corresponding to the bottom of the image
	y_eval = np.max(ploty)

	##### Implement the calculation of R_curve (radius of curvature) #####
	left_curverad = ((1 + (2*left_fit_cr[0]*y_eval*ym_per_pix + left_fit_cr[1])**2)**1.5) / np.absolute(2*left_fit_cr[0])
	right_curverad = ((1 + (2*right_fit_cr[0]*y_eval*ym_per_pix + right_fit_cr[1])**2)**1.5) / np.absolute(2*right_fit_cr[0])

	return left_curverad, right_curverad

def distance(image, left_fitx, right_fitx):
	# get the center of the image
	image_size_x = image.shape[1]

	# get the last values of x from left and right
	left_x = left_fitx[-1]
	right_x = right_fitx[-1]

	# compute the value of the center of the lane
	center_x = left_x + ((right_x - left_x) / 2)

	# compute the offset and ocnvert it to meters
	offset = ((image_size_x / 2) - center_x) * xm_per_pix

	return offset


import numpy as np
import time

def job_pb1():
   f = open("jobs.txt","r")
   params = [line.rstrip('\n')  for line in f]
   f.close()

   job_list = []
   for ln in params[1:int(params[0])+1]:
       weight = float(ln.split()[0])
       length = float(ln.split()[1])
       job_list.append((weight - length,weight,length))

   # setting reverse=True sorts in descending order
   # key used for sorting can be a tuple. 1st element of tuple is used for sorting, 2nd element is used in case of tie to break the tie
   # Adding "-" before a tuple element reverses direction of sorting
   job_list.sort(reverse=True, key=lambda tuplee : (tuplee[0],tuplee[1]))
   sum = 0
   completion_time = 0
   for i in job_list:
       completion_time = completion_time + i[2]
       sum = sum + i[1]*completion_time

   return sum

def job_pb2():
   f = open("jobs.txt","r")
   params = [line.rstrip('\n')  for line in f]
   f.close()

   job_list = []
   for ln in params[1:int(params[0])+1]:
       weight = float(ln.split()[0])
       length = float(ln.split()[1])
       job_list.append((weight/length,weight,length))

   job_list.sort(reverse=True, key=lambda tuplee : (tuplee[0],tuplee[1]))

   sum = 0
   completion_time = 0
   for i in job_list:
       completion_time = completion_time + i[2]
       sum = sum + i[1]*completion_time

   return sum

def prim_pb3():
    f = open("edges.txt","r")
    params = [line.rstrip('\n')  for line in f]
    f.close()
    num_nodes = params[0].split()[0]
    num_edges = params[0].split()[1]

    graph = np.NaN*np.ones((int(num_nodes)+1,int(num_nodes)+1))
    for ln in params[1:len(params)]:
        start_node = int(ln.split()[0])
        end_node = int(ln.split()[1])
        edge_weight = int(ln.split()[2])
		# key concept: undirect graph adjacency matrix is symmetric, need to add edge_weight in both direction
		# Note that same will hold for an adjacency list
        graph[start_node][end_node] = edge_weight
        graph[end_node][start_node] = edge_weight

    X = {i for i in range(1,int(num_nodes)+1)}
    start_node = 1
    V = {start_node}
    X.remove(start_node)
    total_weight = 0
    while len(V) != int(num_nodes):
        min_weight = 1000000000000000
        for i in V:
            for j in X:
                if (np.isnan(graph[i][j]) == False):
                    if (graph[i][j] < min_weight):
                       min_weight = graph[i][j]
                       min_end_node = j
                       min_start_node = i
        V.add(min_end_node)
        X.remove(min_end_node)
        total_weight = total_weight + min_weight
        #print(total_weight, min_weight, min_end_node)

    return total_weight

def Swap(heap,idx1,idx2):
    temp = heap[idx1]
    heap[idx1] = heap[idx2]
    heap[idx2] = temp

def Insert(heap,ele):
    heap.append(ele)
    eidx = heap.index(ele)
    pidx = int((eidx-1)/2)
    while(heap[pidx][0] > heap[eidx][0]):
        Swap(heap,pidx,eidx)
        eidx = pidx
        pidx = int((eidx-1)/2)

def Extract_Min(heap):
    Swap(heap,0,len(heap)-1)
    ele = heap.pop()
    pidx = 0
    cidx1 = 1
    cidx2 = 2
    while(1):
       smallest = pidx
       if (cidx1 < len(heap)) and (heap[pidx][0] > heap[cidx1][0]):
          smallest = cidx1

       if (cidx2 < len(heap)) and (heap[smallest][0] > heap[cidx2][0]):
          smallest = cidx2

       if (smallest != pidx):
          Swap(heap,smallest,pidx)
          pidx = smallest
          cidx1 = 2*pidx+1
          cidx2 = 2*pidx+2
       else:
          break

    return ele

def Delete(heap,ele):
    # compute index in heap of node to be deleted
    k = int([i[1] for i in heap].index(ele))
    Swap(heap,k,len(heap)-1)
    ele = heap.pop()
    if (len(heap) == 0) or (k == len(heap)):
       return ele
    pidx = int((k-1)/2)
    cidx1 = 2*k+1
    cidx2 = 2*k+2
	# shift-up
    if heap[k][0] < heap[pidx][0]:
        eidx = k
        while(heap[pidx][0] > heap[eidx][0]):
            Swap(heap,pidx,eidx)
            eidx = pidx
            pidx = int((eidx-1)/2)
    # shift-down
    else:
        pidx = k
        cidx1 = 2*pidx+1
        cidx2 = 2*pidx+2
        while(1):
            smallest = pidx
            if (cidx1 < len(heap)) and (heap[pidx][0] > heap[cidx1][0]):
                smallest = cidx1

            if (cidx2 < len(heap)) and (heap[smallest][0] > heap[cidx2][0]):
                smallest = cidx2

            if (smallest != pidx):
                Swap(heap,smallest,pidx)
                pidx = smallest
                cidx1 = 2*pidx+1
                cidx2 = 2*pidx+2
            else:
                break

    return ele

def heapify(X,V,graph):
    heap = []
    for j in X:
        min_distance = 10000000
        for i in V:
            if(np.isnan(graph[i][j]) == False):
               min_distance = graph[i][j]
        Insert(heap, (min_distance,j))

    return heap

def prim_heap_impl():
    f = open("edges.txt","r")
    params = [line.rstrip('\n')  for line in f]
    f.close()
    num_nodes = params[0].split()[0]
    num_edges = params[0].split()[1]

    graph = np.NaN*np.ones((int(num_nodes)+1,int(num_nodes)+1))
    for ln in params[1:len(params)]:
        start_node = int(ln.split()[0])
        end_node = int(ln.split()[1])
        edge_weight = int(ln.split()[2])
		# key concept: undirect graph adjacency matrix is symmetric, need to add edge_weight in both direction
		# Note that same will hold for an adjacency list
        graph[start_node][end_node] = edge_weight
        graph[end_node][start_node] = edge_weight

    start_node = 1
    X = {i for i in range(1,int(num_nodes)+1)}
    X.remove(start_node)
    V = {start_node}
    heap = heapify(X,V,graph)

    total_weight = 0
    while len(V) != int(num_nodes):
        (dist, node) = Extract_Min(heap)  #find node with min distance
        V.add(node)     #add this node to set V
        X.remove(node)  #remove this node from set X
        total_weight = total_weight + dist
        #print(total_weight, dist, node)
        for j in X:
            if (np.isnan(graph[node][j]) == False):
                ele = Delete(heap, j) #delete from heap any nodes connected to node added to V
                min_distance = min(ele[0],graph[node][j]) #re-compute min_distance for node j
                Insert(heap, (min_distance, j)) #add node back to heap

    return total_weight

print(job_pb1())
print(job_pb2())
start = time.time()
print(prim_pb3())
print(time.time() - start)
start = time.time()
print(prim_heap_impl())
print(time.time() - start)

#5 - for every G and H, these edges are contained in some minimum spanning tree of H
#4 - T is always minimum spanning tree and P may not be always shortest path
#3 - Algorithm A always correctly determines whether a given graph is perfect matching or not; B does not
#2 - Schedule the requests in increasing order of deadline d^j
#1 - At each request pick the remaining request with the earliest finish time


from collections import Sequence

import numpy as np

def random_gaussian(layer_sizes: Sequence) -> tuple[list, list]:
    """Initializes the weights and biases using Gaussian random variables

    Assumes that the first layer of neurons is an input layer,
    and uses mean 0 and standard deviation 1.

    :returns a tuple with the weights list and biases list
    """
    biases = [np.random.randn(y, 1) for y in layer_sizes[1:]]
    # [2, 5, 4] -> [(2, 5), (5, 4)]
    layer_size_pairs = zip(layer_sizes[:-1], layer_sizes[1:])
    weights = [np.random.randn(y, x) for x, y in layer_size_pairs]
    return weights, biases

def sqrt_connections_ratio(layer_sizes: Sequence) -> tuple[list, list]:
    """Weights are initialized using Gaussian divided by the square root of the number of connections

    Assumes that the first layer of neurons is an input layer.

    Initializes the weights input to a neuron are initialized as
    Gaussian random variables with mean 0 and standard deviation 1
    divided by the square root of the number of connections input
    to the neuron. Also in this method we'll initialize the biases,
    using Gaussian random variables with mean 0 and standard deviation 1

    :returns a tuple with the weights list and biases list
    """
    biases = [np.random.randn(y, 1) for y in layer_sizes[1:]]
    # [2, 5, 4] -> [(2, 5), (5, 4)]
    layer_size_pairs = zip(layer_sizes[:-1], layer_sizes[1:])
    weights = [np.random.randn(y, x) / np.sqrt(x) for x, y in layer_size_pairs]
    return weights, biases

def dnn_parameters_initializer(layer_dims: list[int]) -> dict:
    """Initialized weights using Gaussian divided by the square root of the number of connections

    Assumes that the first layer of neurons is an input layer.

    Initializes the weights input to a neuron are initialized as
    Gaussian random variables with mean 0 and standard deviation 1
    divided by the square root of the number of connections input
    to the neuron. Also in this method we'll initialize the biases,
    using Gaussian random variables with mean 0 and standard deviation 1

    Args:
        layer_dims: python array (list) containing the
                    dimensions of each layer in our network

    Returns:
        parameters, a python dictionary containing your parameters
        "W1", "b1", ..., "WL", "bL":
            Wl -- weight matrix of shape (layer_dims[l], layer_dims[l-1])
            bl -- bias vector of shape (layer_dims[l], 1)
    """

    parameters = {}
    num_layers = len(layer_dims)  # number of layers in the network

    for layer in range(1, num_layers):
        parameters["W" + str(layer)] = np.random.randn(layer_dims[layer], layer_dims[layer - 1]) * 0.01
        parameters["b" + str(layer)] = np.zeros((layer_dims[layer], 1))
    return parameters


# plots.py
"""Volume 1A: SVD and Image Compression. Plotting file."""
import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file("../../../matplotlibrc")

import matplotlib.pyplot as plt
from scipy.linalg import svd
import numpy as np

def circles():
	A = np.array([[3,1],[1,3]])
	U, S, V = svd(A,full_matrices=False)
	S = np.diag(S)

	t = np.linspace(0,2*np.pi,100)
	pts = np.array([np.cos(t),np.sin(t)])
	v= V.dot(pts)
	sv = S.dot(v)
	a = U.dot(sv)

	unit_vecs = np.array([[1,0],[0,1]])
	vu = V.dot(unit_vecs)
	svu = S.dot(vu)
	au = U.dot(svu)

	plt.figure()
	plt.plot(pts[0],pts[1],'b')
	plt.plot([0,unit_vecs[0,0]],[0,unit_vecs[1,0]],'g')
	plt.plot([0,unit_vecs[0,1]],[0,unit_vecs[1,1]],'g')
	plt.axis('equal')
	plt.savefig('unit_circle.pdf')

	plt.figure()
	plt.plot(v[0],v[1],'b')
	plt.plot([0,vu[0,0]],[0,vu[1,0]],'g')
	plt.plot([0,vu[0,1]],[0,vu[1,1]],'g')
	plt.axis('equal')
	plt.savefig('vcircle.pdf')

	plt.figure()
	plt.plot(sv[0],sv[1],'b')
	plt.plot([0,svu[0,0]],[0,svu[1,0]],'g')
	plt.plot([0,svu[0,1]],[0,svu[1,1]],'g')
	plt.axis('equal')
	plt.savefig('svcircle.pdf')

	plt.figure()
	plt.plot(a[0],a[1],'b')
	plt.plot([0,au[0,0]],[0,au[1,0]],'g')
	plt.plot([0,au[0,1]],[0,au[1,1]],'g')
	plt.axis('equal')
	plt.savefig('full_transformation.pdf')

def svals(img):
    U, s, Vt = svd(img)
    s = np.sort(s)[::-1]
    plt.semilogy(s)
    plt.xlim([0,670])
    plt.xlabel('Matrix rank')
    plt.ylabel('Singular values')
    plt.savefig('hubble_svals.pdf')
    plt.close()

def lowrank(img, rankvals):
    U, s, Vt = svd(img)

    for n in rankvals:
        u1, s1, vt1 = U[:,0:n], np.diag(s[0:n]), Vt[0:n,:]
        plt.imsave("rank{}.png".format(n), u1.dot(s1).dot(vt1))

    plt.close()

if __name__ == "__main__":
    img = plt.imread('hubble_red.jpg')
    svals(img)
    lowrank(img, [1, 14, 27, 40])
    circles()
    plt.close()


from __future__ import division
import numpy as np
from field import Field, Space

def get_wave_field():
    #x = np.linspace(-2*np.pi, 2*np.pi, 100)
    x = np.linspace(0, np.pi, 100)
    y = np.linspace(-2*np.pi, 2*np.pi, 100)
    X, Y = np.meshgrid(x, y, indexing='ij')
    space = Space([x, y])
    space.set_elements_names(['x', 'y'])
    wave_field = Field([np.sin(X) * np.exp(Y / 10)], space)
    wave_field.set_elements_names(['u'])
    return wave_field

def get_randomly_spaced_wave_field():
    x = np.concatenate((np.linspace(-2*np.pi, -1.8*np.pi, 50), np.linspace(-1.8*np.pi + 0.01, -1.6*np.pi, 10), np.linspace(-1.6*np.pi + 0.01, 1.8*np.pi, 50), np.linspace(1.8*np.pi + 0.01, 2*np.pi, 50)))
    y = np.concatenate((np.linspace(-2*np.pi, -1.8*np.pi, 50), np.linspace(-1.8*np.pi + 0.01, -1.6*np.pi, 10), np.linspace(-1.6*np.pi + 0.01, 1.8*np.pi, 50), np.linspace(1.8*np.pi + 0.01, 2*np.pi, 50)))
    X, Y = np.meshgrid(x, y, indexing='ij')
    space = Space([x, y])
    space.set_elements_names(['x', 'y'])
    wave_field = Field([np.sin(X) * np.exp(Y / 10)], space)
    wave_field.set_elements_names(['u'])
    return wave_field

def get_time_dependent_wave_fields():
    x = np.linspace(-2*np.pi, 2*np.pi, 100)
    y = np.linspace(-2*np.pi, 2*np.pi, 100)
    X, Y = np.meshgrid(x, y, indexing='ij')
    space = Space([x, y])
    space.set_elements_names(['x', 'y'])

    y_coeffs = np.linspace(1, 10, 100)
    fields = []
    for y_coeff in y_coeffs:
        u = np.sin(X) * np.exp(Y / y_coeff)
        u = u / np.amax(u)
        wave_field = Field([u], space)
        wave_field.set_elements_names(['u'])
        fields.append(wave_field)
    return fields

def get_time_dependent_circular_flow_fields():
    x = np.linspace(-5, 5, 100)
    y = np.linspace(-2, 2, 100)
    a = 1
    U_inf = 10
    K = 0
    X, Y = np.meshgrid(x, y, indexing='ij')
    R_sq = X**2 + Y**2
    space = Space([x, y])
    space.set_elements_names(['x', 'y'])

    K_coeffs = np.linspace(0, 2 * U_inf * a, 100)
    fields = []
    for K in K_coeffs:
        u = U_inf * (1 - (a**2 * R_sq - 2 * a**2 * Y**2) / R_sq**2) - K * a * Y / R_sq
        v = - 2 * U_inf * a**2 * X * Y / R_sq**2 + K * a * X / R_sq
        for xi in range(len(x)):
            for yi in range(len(y)):
                if x[xi]**2 + y[yi]**2 < a**2:
                    u[xi, yi] = 0.
                    v[xi, yi] = 0.

        circ_field = Field([u, v], space)
        circ_field.set_elements_names(['u', 'v'])
        fields.append(circ_field)
    return fields

def get_simple_1D_field():
    x = np.linspace(0, 5, 100)
    space = Space([x])
    space.set_elements_names(['x'])
    field = Field([x**2], space)
    field.set_elements_names(['u'])

    return field

def get_simple_2D_field():
    x = np.linspace(0, 2, 100)
    y = np.linspace(0, 2, 100)
    X, Y = np.meshgrid(x, y, indexing='ij')
    space = Space([x, y])
    space.set_elements_names(['x', 'y'])
    field = Field([X**2 + Y**3], space)
    field.set_elements_names(['u'])
    return field

def get_simple_3D_field():
    x = np.linspace(0, 2, 80)
    y = np.linspace(0, 2, 100)
    z = np.linspace(0, 2, 120)
    X, Y, Z = np.meshgrid(x, y, z, indexing='ij')
    space = Space([x, y, z])
    space.set_elements_names(['x', 'y', 'z'])
    field = Field([X**2 + Y**3 + Z**4], space)
    field.set_elements_names(['u'])
    return field


import sympy
from transfer_func import PolyTransferFunc

s, gamma = sympy.symbols('s gamma')

tf_1dof = (s - sympy.I * gamma) / (s + sympy.I * gamma)
tf_2dof = (s**2 / gamma + s - sympy.I * gamma) / (-s**2 / gamma + s + sympy.I * gamma)

def test_expr_gives_expected_result():
    poly_fraction = PolyTransferFunc.from_transfer_function(tf_1dof, s)
    assert poly_fraction.expr == tf_1dof

def test_poly_fraction_from_transfer_func_gives_expected_result():
    poly_fraction = PolyTransferFunc.from_transfer_function(tf_1dof, s)
    assert poly_fraction.numer.equals(s - sympy.I * gamma)
    assert poly_fraction.denom.equals(s + sympy.I * gamma)
    poly_fraction = PolyTransferFunc.from_transfer_function(tf_2dof, s)
    assert poly_fraction.numer.equals(s**2 / gamma + s - sympy.I * gamma)

def test_getting_coefficients_gives_expected_result():
    poly_fraction = PolyTransferFunc.from_transfer_function(tf_2dof, s)
    assert poly_fraction.numer_coeffs == [1 / gamma, 1, -sympy.I * gamma]
    assert poly_fraction.denom_coeffs == [-1 / gamma, 1, sympy.I * gamma]

def test_controllable_canonical_form_gives_expected_state_space():
    poly_tf = PolyTransferFunc.from_transfer_function(tf_2dof, s)
    state_space = poly_tf.as_ccf_state_space

    assert state_space.num_inputs == state_space.num_outputs == 1
    assert state_space.num_dof == 2

    a_mat = sympy.Matrix([[0, 1], [sympy.I * gamma**2, gamma]])
    b_mat = sympy.Matrix([[0], [1]])
    c_mat = sympy.Matrix([[0, -2*gamma]])
    d_mat = sympy.Matrix([[-1]])

    assert state_space.a == a_mat
    assert state_space.b == b_mat
    assert state_space.c == c_mat
    assert state_space.d == d_mat

    assert state_space.siso_transfer_function.equals(tf_2dof)

def test_ccf_for_the_coupled_cavity_transfer_function(run_slow):
    """ This is to test using the work being done locally in coupled-cavity.nb """

    s = sympy.symbols("s")
    omega_s, Omega, tau_1, gamma_1, gamma_1_prime =\
        sympy.symbols("omega_s Omega tau_1 gamma_1 gamma_1'", real=True, positive=True)
    I = sympy.I

    prefactor = I * tau_1 * (gamma_1 - gamma_1_prime)

    tf = (-prefactor * Omega**2 + 2 * gamma_1 * Omega + prefactor * omega_s**2) /\
         (+prefactor * Omega**2 + 2 * gamma_1 * Omega - prefactor * omega_s**2)

    tf_normalized_freq = tf.subs(Omega, -I*omega_s*s)  # do we need to do this?

    prefactor = 2 * gamma_1 / (tau_1 * (gamma_1 - gamma_1_prime) * omega_s)

    # testing my re-arranging skills
    assert sympy.simplify(tf_normalized_freq - ((s**2 - prefactor * s + 1)/(-s**2 - prefactor*s - 1))) == 0

    poly_tf = PolyTransferFunc.from_transfer_function(tf_normalized_freq, s)

    ss = poly_tf.as_ccf_state_space

    assert ss.a == sympy.Matrix([[0, 1], [-1, -prefactor]])
    assert ss.b == sympy.Matrix([[0], [1]])
    assert ss.c == sympy.Matrix([[0, 2 * prefactor]])
    assert ss.d == sympy.Matrix([[-1]])

    # the simplifications make these tasks pretty slow
    if run_slow:
        assert sympy.simplify(ss.siso_transfer_function.subs(s, I * Omega / omega_s) - tf) == 0
        assert sympy.simplify(ss.siso_transfer_function - tf_normalized_freq) == 0
        assert PolyTransferFunc.from_transfer_function(ss.siso_transfer_function, s) == poly_tf


""" 2)  RUN_prob-ode: The purpose of this code to demonstrate, how to simulate,
        solve and visualize differential equations, utilizing probabilistic models."""
#-----------------------------------------------------------------------------------------------------------------------------------
__author__ = "Christian Simonis"
__copyright__ = "Copyright 2021"
__version__ = "1.0"
__maintainer__ = "Christian Simonis"
__email__ = "christian.Simonis.1989@gmail.com"
__status__ = "work in progress"

# Approach: The class  ODE_Analysis enables the simulation of Lotka Volterra ODEs and applying additional observation noise and offsets.
# A domain-driven time integration approach is applied, which is combined with a probabilistic correction model.
# This model is optimized by tuning the data-driven part. The corresponding parameters are optimized via maximum marginal likelihood.
# Visualizations are provided to illustrate results.

#-----------------------------------------------------------------------------------------------------------------------------------
# Name                                Version                      License
# numpy                               1.19.5                       BSD,                                         Copyright (c) 2005-2020, NumPy Developers: https://numpy.org/doc/stable/license.html#:~:text=Copyright%20(c)%202005%2D2020%2C%20NumPy%20Developers.&text=THIS%20SOFTWARE%20IS%20PROVIDED%20BY,A%20PARTICULAR%20PURPOSE%20ARE%20DISCLAIMED.
# tensorflow-probability              0.12.2                       Apache Software License , Version 2.0,       Copyright 2018 The TensorFlow Probability Authors.  All rights reserved.https://github.com/tensorflow/probability/blob/master/LICENSE
# tensorflow                          2.5.0                        Apache Software License,                     Copyright 2019 The TensorFlow Authors.  All rights reserved.https://github.com/tensorflow/tensorflow/blob/master/LICENSE
# matplotlib                          3.4.2                        Python Software Foundation License,          Copyright (c) 2002 - 2012 John Hunter, Darren Dale, Eric Firing, Michael Droettboom and the Matplotlib development team; 2012 - 2021 The Matplotlib development team: https://matplotlib.org/stable/users/license.html
# seaborn                             0.11.1                       BSD 3-Clause "New" or "Revised" License,     Copyright (c) 2012-2021, Michael L. Waskom: https://github.com/mwaskom/seaborn/blob/master/LICENSE
# scipy                               1.5.2                        BSD 3-Clause "New" or "Revised" License,     Copyright (c) 2001-2002 Enthought, Inc.  2003-2019, SciPy Developers: https://github.com/scipy/scipy/blob/master/LICENSE.txt
# lmfit/lmfit-py                      1.0.2                        BSD-3                                        Copyright 2021 Matthew Newville, The University of Chicag, Renee Otten, Brandeis University, Till Stensitzki, Freie Universitat Berlin, A. R. J. Nelson, Australian Nuclear Science and Technology Organisation, Antonino Ingargiola, University of California, Los Angeles, Daniel B. Allen, Johns Hopkins University, Michal Rawlik, Eidgenossische Technische Hochschule, Zurich, https://github.com/lmfit/lmfit-py/blob/master/LICENSE
# app (Lotka Volterra simulation)       -                          MIT License,                                 Copyright (c) 2018 Mostapha Kalami Heris / Yarpiz Team, https://github.com/smkalami/lotka-volterra-in-python/blob/master/app.py
# scipy/scipy-cookbook                  -                          Copyright (c) 2001, 2002 Enthought, Inc.,    All rights reserved., Copyright (c) 2003-2017 SciPy Developers., All rights reserved., https://github.com/scipy/scipy-cookbook/blob/master/LICENSE.txt
#-----------------------------------------------------------------------------------------------------------------------------------
import numpy as np
import tensorflow_probability as tfp
import tensorflow.compat.v2 as tf
import matplotlib.pyplot as plt
import seaborn as sns
import scipy
import lmfit
import random                                                      # https://docs.python.org/3/library/random.html                                            # https://docs.python.org/3/library/datetime.html
#-----------------------------------------------------------------------------------------------------------------------------------
# '''
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
#BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
#OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
#THE POSSIBILITY OF SUCH DAMAGE.
#"""
#-----------------------------------------------------------------------------------------------------------------------------------

# Defined Lotka-Volterra Model parameters for creating time series data
alpha = 1.0
beta = 0.55
gamma = 1.15
delta = 0.75

# Simulation Time
N = 800
till = 15
t = np.linspace(0,till,N)
dt = t[1] - t[0]

# Initial Values
x0 = 1.3
y0 = 0.5

#Observability, assuming a noisy signal
noise_factor_x = 0.2
noise_factor_y = 0.3
offset_x = 0.9 #Preys that do not take part in the game
offset_y = 1.2 #Predators that do not  not take part in the game

#Probabilistic analysis
sigma_eval = 3 #Evaluation at 3 sigma

#Visualization of trajectory samples
nr_samples = 10

#-----------------------------------------------------------------------------------------------------------------------------------
#import ODE_Analysis
from ODE_Analysis import ODE_Analysis
#-----------------------------------------------------------------------------------------------------------------------------------

#Random number generator for repeatabe results
random.seed(0)

#call class ODE_Analysis
Lotka_Volterra = ODE_Analysis()

#visualization of pure theoretic ODE part based on parameters
nb_points   = 20
Lotka_Volterra.visualize_ODE(t, alpha, beta, gamma, delta, nb_points)
plt.show()

#create data with noisy observations and apply term which ODE model cannot describe (offset)
data = Lotka_Volterra.create_time_series(alpha, beta, gamma, delta, t, x0, y0, noise_factor_x, noise_factor_y, offset_x, offset_y)
x_init = data[0,0]
y_init = data[0,1]

#optimization: Fit differential eqaution model
lim_min = 0
lim_max = 20
fitted_mdl_result, residuum_ode = Lotka_Volterra.fit_ODE(t, data, x_init, y_init, lim_min, lim_max)
x_res_ode = residuum_ode[:,0]
y_res_ode = residuum_ode[:,1]

# plot data and fitted curves
plt.plot(t, data, 'o',label='Noisy observations',alpha = 0.2)
plt.plot(t, fitted_mdl_result, '-.', linewidth=5 ,label='Model Fit')
plt.xlabel('Time', fontsize=16)
plt.ylabel('Population', fontsize=16)
plt.legend(loc = 'upper right')
plt.show()

# for X (Preys)
ODE_x = fitted_mdl_result[:,0]
observation_x = data[:,0]
Lotka_Volterra.evaluate_prob_mdl(t,x_res_ode,ODE_x, observation_x, sigma_eval)

# for Y (Predators)
ODE_y = fitted_mdl_result[:,1]
observation_y = data[:,1]
Lotka_Volterra.evaluate_prob_mdl(t,y_res_ode,ODE_y, observation_y, sigma_eval)

#Optimization input
observation_index_points = t.reshape(-1,1)
index_points = t.reshape(-1,1)

#Call function for X optimization
print("--------------Optimization for Prey class X--------------")
obs_X = -x_res_ode
observation_noise_variance_X = np.std(obs_X)
GP_opt_X, x_opt_mean, x_opt_up, x_opt_low = Lotka_Volterra.optimize_mdl_prm(index_points,observation_index_points, obs_X, observation_noise_variance_X, sigma_eval)

#probabilistic correction for X
x_opt = x_opt_mean + ODE_x
x_opt_up = x_opt_up + ODE_x
x_opt_low = x_opt_low + ODE_x

#Visualization
Lotka_Volterra.visualize_results(t, x_opt, ODE_x, observation_x)

#Call function for Y optimization
print("-----------Optimization for Predator class Y-------------")
obs_Y = -y_res_ode
observation_noise_variance_Y = np.std(obs_Y)
GP_opt_Y, y_opt_mean, y_opt_up, y_opt_low = Lotka_Volterra.optimize_mdl_prm(index_points,observation_index_points, obs_Y, observation_noise_variance_Y, sigma_eval)

#probabilistic correction for Y
y_opt = y_opt_mean + ODE_y
y_opt_up = y_opt_up + ODE_y
y_opt_low = y_opt_low + ODE_y

#Visualization
Lotka_Volterra.visualize_results(t, y_opt, ODE_y, observation_y)
print("-----------------Optimization finished-------------------")

#Time series plot
plt.plot(t, data, 'o',label='Noisy observations',alpha = 0.2)
plt.plot(t, x_opt, c='r', alpha=1, linewidth=3, label = 'Model for X (Prey class)')
plt.fill_between(t, x_opt_up, x_opt_low, color='r', alpha=.1)
plt.plot(t, y_opt, c='k', alpha=1, linewidth=3, label = 'Model for Y (Predator class)')
plt.fill_between(t, y_opt_up, y_opt_low, color='k', alpha=.1)
plt.legend(ncol=2)
plt.xlabel('Time', fontsize=16)
plt.ylim(0,8)
plt.ylabel('Population', fontsize=16)
plt.title('Optimized probabilistic model, combined with ODE model')
plt.show()

#Sampling from probabilistic model
samples_X = GP_opt_X.sample(nr_samples).numpy()
samples_Y = GP_opt_Y.sample(nr_samples).numpy()

#Diagram with trajectories
plt.plot(x_opt, y_opt, c='b', alpha=.5, linewidth=7, label = 'Model Mean')
plt.plot(samples_X[0,:]  + ODE_x, samples_Y[0,:]  + ODE_y, c='b', alpha=.1, label = 'Model samples') #for creating one legend entry
plt.plot(samples_X.T + ODE_x.reshape(-1,1), samples_Y.T + ODE_y.reshape(-1,1), c='b', alpha=.1, label = '_nolegend_') # no legend entrys hier
plt.legend(loc = 'upper right')
plt.xlabel('Preys', fontsize=16)
plt.ylabel('Predators', fontsize=16)
plt.title('Trajectory diagram w/ {} drawn samples from probabilistic model'.format(nr_samples))
plt.show()


import numpy as np

def gaussjordanInv(A):
    '''
    Inverse a matrix using elimination Gauss Jordan method

    This method takes a matrix as parameter, then it returns the inverse of the given matrix.

    Parameters
    -------

    A : matrix
        a numpy matrix (vector of vectors) of any dimension
    '''
    B = A.copy()
    n,m = len(B), len(B[0])
    I = np.eye(n)
    for i in range(n):
        for j in range(n):
            if i != j:
                ratio = B[j,i]/B[i,i]
                for k in range(m):
                    B[j,k] = B[j,k] - ratio * B[i,k]
                    I[j,k] = I[j,k] - ratio * I[i,k]
    for i in range(len(B)):
        divisor = B[i,i]
        for j in range(len(B)):
            B[i,j] = B[i,j]/divisor
            I[i,j] = I[i,j]/divisor
    return I

def linearGauss(A,b):
    '''
    Solve a linear system

    This method takes a matrix and a vector as parameter, then it returns the vector solution of the given matrix using gauss jordan method.

    Parameters
    -------

    A : matrix
        a numpy matrix (vector of vectors) of any dimension
    b : list
        a list or a numpy vector
    '''
    n=len(A)
    for i in range(n):
        for j in range(n):
            if i!=j:
                ratio =A[j,i]/A[i,i]
                for k in range(n):
                    A[j,k]=A[j,k]-ratio *A[i,k]
                b[j]=b[j]-ratio *b[i]
    x=b
    for i in range(n-1,-1,-1):
        x[i]=x[i]/A[i,i]
        A[i,i] /= A[i,i]
    return x

def _decompositionLU(A):
    n=len(A)
    L=np.identity(n)
    U=A
    for i in range(n):
        for j in range(n):
            s=0
            for k in range(i):
                s+=(L[i,k]*U[k,j])
            U[i,j]=A[i,j]-s
        for j in range(i,n):
            if i==j:
                L[i,i]=1
            else:
                s=0
                for k in range(i):
                    s+=(L[j,k]*U[k,i])
                L[j,i]=(A[j,i]-s)/U[i,i]
    return (L,U)

def _choleski(A):
    n=len(A)
    L=np.zeros((n,n))
    for i in range(n):
        for j in range(i+1):
            s=0
            if j==i:
                for k in range(j):
                    s+=pow(L[j,k],2)
                L[j,j]=np.sqrt(A[j,j]-s)
            else:
                for k in range(j):
                    s+=L[i,k]*L[j,k]
                L[i,j]=(A[i,j]-s)/L[j,j]
    return L

def LUdecomp(A,b):
    '''
    Solve a linear system

    This method takes a matrix and a vector as parameter, then it returns the vector solution of the given matrix using Choleski
    Parameters
    -------

    A : matrix
        a numpy matrix (vector of vectors) of any dimension
    b : list
        a list or a numpy vector
    '''
    L, U = _decompositionLU(A)
    y = linearGauss(L,b)
    x = linearGauss(U,y)
    return x
def cholsekidecomp(A,b):
    A = _choleski(A)
    B = A.T
    y = linearGauss(A,b)
    x = linearGauss(B,y)
    return x


'''
Busca binária iterativa
Exercício: Lista 1.1

Requerido: python 3+, pip install -r requirements.txt
Executar: python busca_binaria.py
'''

from numpy import integer
#import logging

#LOGGER = logging.getLogger(__name__)

def busca_binaria(lista, valor):
    '''
    Dada uma lista de números e um número, retorna o índice do número na lista
    ou -1 se o número não estiver na lista.

      :param lista: a lista que você deseja pesquisar
      :param valor: o valor que está procurando

      Issue:
      Return rightmost, not leftmost
      https://en.wikipedia.org/wiki/Binary_search_algorithm#Duplicate_elements

    Run: pytest busca_binaria.py --hypothesis-show-statistics
    '''
    tamanho = len(lista)
    fim = tamanho - 1
    inicio = 0
    #chute = tamanho // 2
    #print(chute)
    #print((inicio + fim) // 2)
    #while (fim - inicio) >= 0:
    while fim >= inicio:
        chute = (inicio + fim) // 2
        if valor == lista[chute]:
            return chute
        if valor > lista[chute]:
            inicio = chute + 1
        else:
            fim = chute - 1
        #chute = (inicio + fim) // 2
    return -1

if __name__ == "__main__":
    assert(busca_binaria([1, 2, 3, 4, 5, 6], 4) == 3)
    assert(busca_binaria([1, 2, 3, 4, 5, 6], 1) == 0)
    assert(busca_binaria([1, 2, 3, 4, 5, 6], 6) == 5)
    assert(busca_binaria([23, 32, 44, 45, 45, 60], 44) == 2)
    assert(busca_binaria([23, 32, 44, 45, 45, 60], 45) == 3)
    assert(busca_binaria([23, 32, 44, 45, 45, 60], 100) == -1)
    assert(busca_binaria([23, 32, 44, 45, 45, 60], 23) == 0)
    assert(busca_binaria([23, 32, 44, 45, 45, 60], 60) == 5)
    assert(busca_binaria([23, 32, 44, 45, 45, 60], 32) == 1)
    assert(busca_binaria([0, 0], 0) == 0)

# https://learning.oreilly.com/library/view/robust-python/9781098100650/ch23.html
from hypothesis import given, event, strategies as st
import random

@given(st.lists(st.integers(min_value=0, max_value=1000), min_size=1, unique=True).map(sorted))
def test_search(xs):
    #test_list = [23, 32, 44, 45, 45, 60]
    element_to_search = random.choice(xs)
    #LOGGER.warning(f'{xs}, {element_to_search}')
    event(f'{xs}, {element_to_search}')
    assert(busca_binaria(xs, element_to_search) == xs.index(element_to_search))


# -*- coding: utf-8 -*-
"""
Created on Sun Mar 31 18:54:17 2019

@author: RAJDEEP PAL
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
#%%
# Function to return gcd of a and b
def gcd(a, b):   # want a > b

    if (a == b):
        return a
    elif ( b == 0 ):
        return a
    elif (a < b):
        return gcd(b, a)
    return gcd(b, b%a)

#%%
def gcd(a, b):

    if (a == 0):
        return b
    return gcd(b % a, a)

#"""  Euler Totient Function  """
def phi(n):

    result = 1
    for i in range(2, n):
        if (gcd(i, n) == 1):
            result+=1
    return result

#%%
# Driver Code
totient_vals = np.zeros(1000)
x = np.zeros(1000)
for i in range(1, 1001): # i = 1 to 1000
    #print (i)
    x[i-1] = i
    totient_vals[i-1] = phi(i)
    print ( phi(i) )
#%%
y = totient_vals
temp = x-1
font = {
 #   'family' : 'Bitstream Vera Sans',
    'family' : 'DejaVu Sans',
    'weight' : 'bold',
    'size'   : 18
}
matplotlib.rc('font', **font)

plt.figure(figsize=(20, 20))

plt.plot(x, y, 'ro')

plt.title("Euler's Totient Function for the first 1000 positive integers")
#plt.legend(loc='upper right', shadow=True)
plt.ylabel('Totient values : phi(x)')
plt.xlabel('Integers : x')

plt.savefig("Totient Fuction")

# This code is contributed
# by Smitha


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Dec 22 10:54:50 2020

@author: Jane Hsieh (Hsing-Chuan, Hsieh)

@article:
    Generate random sample of dice (with Faif and Loaded dice used interchangeably)
"""
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

#0. ================================================ Functions ===============================================

def InitialState(pi_0=0.5):
    '''
    function to generate initial state S_0 out of {F,L} of the dice with prob-p(S_0=F) = p(F)=pi_0 (0:F; 1:L)
    Input:
        pi_0: defaul= 0.5 which is the initial prob of hiddent state p(S_0 = 0)  [0: fair dice; 1: Loaded dice]
                (pi1 := p(S_0 = 1) = 1-pi_0)
    Output:
        S_0: initial generated hidden state
    '''
    S_0 = np.random.choice(2, 1, p=[pi_0, 1-pi_0])[0] #Default: replace=True
    return S_0

def EmitObs(S_t, Dice, pF, pL):
    '''
    function to generate observation O_t according to the mechanism of hidden state S_t --> Emit Observation
    Input:
        S_t: current hidden state
        Dice: face point of dice, e.g., [1,2,...,6]
        pF: distributions of Fair Dice, say, with point = [1,2,...,6]
        pL: distributions of Loaded Dice, say, with point = [1,2,...,6]
    '''
    if S_t == 0:
        #Fair dice state with prob = pF
        O_t = np.random.choice(Dice, 1, replace=True, p=pF)[0]
    if S_t == 1:
        #Loaded dice state with prob = pL
        O_t = np.random.choice(Dice, 1, replace=True, p=pL)[0]
    return O_t

def TransitState(S_t, p01, p10):
    '''
    function to generate the new hidden state S_t according to the mechanism of previous hidden state S_t
    Input:
        S_t: current hidden state
        p01: Transition prob from 0(F) to 1(L)); p00 =  1- p01
        p10: Transition prob from 1(L) to 0(F); p11 = 1-p10
    '''
    if S_t == 0:
        #Fair dice state with prob = pF
        S_new = np.random.choice(2, 1, replace=True, p=[1-p01, p01])[0]
    if S_t == 1:
        #Loaded dice state with prob = pL
        S_new = np.random.choice(2, 1, replace=True, p=[p10, 1-p10])[0]
    return S_new

def GenerateFLDice(N, p01, p10, pL, pF=[1/6 for i in range(6)], Dice = [i+1 for i in range(6)], pi_0=0.5, seed=None):
    '''
    Generate random sample of dice (with predefined Fair(R) and Loaded(L) dice used interchangeably)

    Input:
        N: sample size
        seed: random seed– None or int
        Dice: face point of the dice; default = [1,2,...,6]
        pF, pL: distributions of Fair vs Loaded Dice (length of pF, pL must be the same with Dice)
        pi_0: initial probability of hidden state S_0 – p(S_0 = 0)  [0: fair dice; 1: Loaded dice]
        p01, p10: Transition prob of hidden state from 0(F) to 1(L)) & from 1(L) to 0(F), respectively
    Output:
        Sample_S: sequence of hidden states {S_t} from t=0,...,N-1
        Sample_O: sequence of observations {O_t} from t=0,...,N-1

    '''
    np.random.seed(seed)
    Sample_S = []
    Sample_O = []

    t=0
    # Generate initial hidden state S_t & the Observed state O_t out of it
    S_t = InitialState(pi_0)
    O_t = EmitObs(S_t, Dice, pF, pL)

    Sample_S.append(S_t)
    Sample_O.append(O_t)

    t += 1

    while t < N:
        #Generate New S_t at next time (i.e., t+1) point according to previous mechanism of S_t;
        #and O_t out of New S_t
        S_t = TransitState(S_t, p01, p10)
        O_t = EmitObs(S_t, Dice, pF, pL)

        Sample_S.append(S_t)
        Sample_O.append(O_t)

        t += 1

    return Sample_S, Sample_O

Hidden Markov Model (HMM)/Dice Simulation Data/Step3. HMM Analysis/Generate_Dice_Sample.py


# %%
import numpy as np
import pandas as pd

# %% [md]
# # Pandas DataFrames: Pivot
#
# The `pivot()` method reorganizes a table based on its values.
# Builds a new data frame using the unique values of one or more indices or columns.

# %%
df = pd.DataFrame({
    "col_1": ["1_1"] * 4 + ["1_2"] * 4 + ["1_3"] * 4,
    "col_2": ["2_1"] * 3 + ["2_2"] * 3 + ["2_3"] * 3 + ["2_4"] * 3,
    "col_3": "3_1 3_1 3_2 3_2 3_3 3_3 3_4 3_4 3_5 3_5 3_6 3_6".split(),
    "col_4": "4_1 4_2 4_3 4_4 4_5 4_6 4_7 4_8 4_9 4_A 4_B 4_C".split(),
})

# %%
pivot_1 = df.pivot(columns="col_1", values="col_2")
pivot_2 = df.pivot(columns="col_1", values="col_3")
pivot_3 = df.pivot(index="col_4", columns="col_1", values="col_2")
pivot_4 = df.pivot(columns="col_4", values="col_1")

# %% [md]
# # Stacking vectors/arrays using numpy.
#

# %%
vec_1 = np.array([11, 12, 13, 14])
vec_2 = np.array([21, 22, 23, 24])
vec_3 = np.array([31, 32, 33, 34])

# %%
vec_c2 = np.c_[vec_1, vec_2]
vec_c3 = np.c_[vec_1, vec_2, vec_3]

# %%
mat_1 = np.array([[111, 112, 113], [121, 122, 123]])
mat_2 = np.array([[211, 212, 213], [221, 222, 223]])
mat_3 = np.array([[311, 312, 313], [321, 322, 323]])

# %%
vec_c22 = np.c_[mat_1, mat_2]
vec_c23 = np.c_[mat_1, mat_2, mat_3]

# %%
mat_4 = np.array([[111, 112], [121, 122], [131, 132], [141, 142]])
vec_c_mixed = np.c_[vec_1, mat_4, vec_2]

# %%


# -*- coding: utf-8 -*-
"""
Created on Wed May 29 09:11:47 2019

@author: Thiago
"""
'''
Modelos estocásticos
'''

import numpy as np
import pylab as pl
import sympy as sp

#%%

def fun(x):
    return x*x

a, b, M, N = 0, 1, 1, 500
k = 0
u = np.linspace(a, b, 100)
pl.plot(u, fun(u))

for i in range(N):
    x, y = np.random.rand(2)
    pl.scatter(x, y, c='b')
    if(y<fun(x)):
        k += 1
        pl.scatter(x, y, c='r')

pl.show()
I1 = M*(b-a)*k/N

x = sp.var('x')

I0 = sp.Integral(fun(x), (x,a,b))
display(I0, I0.evalf(), I1)

#%%

for N0 in [10, 100, 500, 1000, 5000, 50000, 100000]:
    I1=[]
    for j in range(10):
        k = 0
        for i in range(N0):
            x, y = np.random.rand(2)
            if(y<fun(x)):
                k += 1

        I1.append(M*(b-a)*k/N0)

    print(N0, np.mean(I1), np.std(I1))

#%%


# get the numerical gradient of multivariable function f(x1, x2, ...)
import sys
import os
sys.path.append(os.pardir)
import numpy as np
from ch04.numerical_diff import show
from ch04.numerical_gradient import numerical_gradient

def numerical_gradient_batch(f, x):
    """
    Returns the gradient of f at x[0], at x[1], ... .
    x is a matrix(including array).
    """
    h = 1e-4  # rule of thumb

    if x.ndim == 1: # if x is an array
        gradient = numerical_gradient(f, x)
    else: # if x is an matrix(not array)
        gradient = np.zeros_like(x)
        for index, x_i in enumerate(x):
            gradient[index] = numerical_gradient(f, x_i)
    return gradient

def get_unit_vector(dimension, index):
    vector = np.zeros(dimension)
    vector[index] = 1
    return vector

if __name__ == '__main__':
    print('z = x ** 2 + y ** 2')

    def f1(x): return x[0] ** 2 + x[1] ** 2
    show(f1, np.array([3.0, 4.0]), numerical_gradient,
         'numerical_gradient', np.array([6.0, 8.0]))
    show(f1, np.array([5.0, 0.0]), numerical_gradient,
         'numerical_gradient', np.array([10.0, 0.0]))
    show(f1, np.array([0.0, 0.0]), numerical_gradient,
         'numerical_gradient', np.array([0.0, 0.0]))

    def f2(x): return x[0] + x[1] ** 2 + x[2] ** 3
    show(f2, np.array([1.0, 2.0, 3.0]), numerical_gradient,
         'numerical_gradient', np.array([1.0, 4.0, 27.0]))

    # x can be a matrix
    print(numerical_gradient_batch(f1, np.array([[3.0, 4.0], [5.0, 0.0], [0.0, 0.0]])))


#!/usr/bin/env python3
#
# Script to test various ODE solvers for the test problem
#    u' = (u+t^2-2)/(t+1),   t in [0,5]
#    u(0) = 2.
#
# Daniel R. Reynolds
# SMU Mathematics
# Math 4315

# imports
import numpy
import matplotlib.pyplot as plt
from ivp_methods import *

# ODE RHS and Jacobian, initial condition, etc.
def f(t,u):
    return (u+t*t-2)/(t+1)
def f_u(t,u):
    return (1.0/(t+1))
def utrue(t):
    return (t**2 + 2*t + 2 - 2*(t+1)*numpy.log(t+1))
u0  = 2.0
t0  = 0.0
Tf  = 5.0
hvals = [0.5**2, 0.5**3, 0.5**4, 0.5**5, 0.5**6, 0.5**7, 0.5**8]

# initialize error storage arrays
err_fE     = numpy.zeros((len(hvals),1), dtype=float)
err_RK2    = numpy.zeros((len(hvals),1), dtype=float)
err_Heun   = numpy.zeros((len(hvals),1), dtype=float)
err_AB2    = numpy.zeros((len(hvals),1), dtype=float)
err_bE     = numpy.zeros((len(hvals),1), dtype=float)
err_trap   = numpy.zeros((len(hvals),1), dtype=float)

# iterate over our h values
for j, h in enumerate(hvals):

    # create time output array
    N = int((Tf-t0)/h)
    tvals = numpy.linspace(t0, Tf, N+1)

    # initialize solutions
    u_fE     = u0
    u_RK2    = u0
    u_Heun   = u0
    u_AB2    = u0
    u_bE     = u0
    u_trap   = u0

    # try out our methods
    for i in range(N):

        t = tvals[i]
        tnew = t + h

        u_fE = fwd_Euler_step(f, t, u_fE,  h)
        err_fE[j] = max(err_fE[j], abs(u_fE-utrue(tnew)))

        u_RK2 = RK2_step(f, t, u_RK2, h)
        err_RK2[j] = max(err_RK2[j], abs(u_RK2-utrue(tnew)))

        u_Heun = Heun_step(f, t, u_Heun, h)
        err_Heun[j] = max(err_Heun[j], abs(u_Heun-utrue(tnew)))

        # for first AB2 step, store fold and use RK2 for step; subsequently use AB2
        if (i == 0):
            fold_AB2 = f(t, u_AB2)
            u_AB2 = RK2_step(f, t, u_AB2, h)
        else:
            u_AB2, fold_AB2 = AB2_step(f, t, u_AB2, h, fold_AB2)
        err_AB2[j] = max(err_AB2[j], abs(u_AB2-utrue(tnew)))

        u_bE = bwd_Euler_step(f, f_u, t, u_bE,  h)
        err_bE[j] = max(err_bE[j], abs(u_bE-utrue(tnew)))

        u_trap = trap_step(f, f_u, t, u_trap,  h)
        err_trap[j] = max(err_trap[j], abs(u_trap-utrue(tnew)))

# output convergence results
print('Results for Fwd Euler:')
err = err_fE
print("   h = %10g,  err = %.2e" % (hvals[0], err[0]))
for i in range(1,len(hvals)):
    print("   h = %10g,  err = %.2e,  rate = %g" %
          (hvals[i], err[i], numpy.log(err[i]/err[i-1])/numpy.log(hvals[i]/hvals[i-1])) )

print('Results for RK2:')
err = err_RK2
print("   h = %10g,  err = %.2e" % (hvals[0], err[0]))
for i in range(1,len(hvals)):
    print("   h = %10g,  err = %.2e,  rate = %g" %
          (hvals[i], err[i], numpy.log(err[i]/err[i-1])/numpy.log(hvals[i]/hvals[i-1])) )

print('Results for Heun:')
err = err_Heun
print("   h = %10g,  err = %.2e" % (hvals[0], err[0]))
for i in range(1,len(hvals)):
    print("   h = %10g,  err = %.2e,  rate = %g" %
          (hvals[i], err[i], numpy.log(err[i]/err[i-1])/numpy.log(hvals[i]/hvals[i-1])) )

print('Results for AB2:')
err = err_AB2
print("   h = %10g,  err = %.2e" % (hvals[0], err[0]))
for i in range(1,len(hvals)):
    print("   h = %10g,  err = %.2e,  rate = %g" %
          (hvals[i], err[i], numpy.log(err[i]/err[i-1])/numpy.log(hvals[i]/hvals[i-1])) )

print('Results for Bwd Euler:')
err = err_bE
print("   h = %10g,  err = %.2e" % (hvals[0], err[0]))
for i in range(1,len(hvals)):
    print("   h = %10g,  err = %.2e,  rate = %g" %
          (hvals[i], err[i], numpy.log(err[i]/err[i-1])/numpy.log(hvals[i]/hvals[i-1])) )

print('Results for Trapezoidal:')
err = err_trap
print("   h = %10g,  err = %.2e" % (hvals[0], err[0]))
for i in range(1,len(hvals)):
    print("   h = %10g,  err = %.2e,  rate = %g" %
          (hvals[i], err[i], numpy.log(err[i]/err[i-1])/numpy.log(hvals[i]/hvals[i-1])) )

# display true solution
plt.figure()
plt.plot(tvals, utrue(tvals))
plt.xlabel('$t$')
plt.ylabel('$u(t)$')
plt.show()

# end of script


# -*- coding: utf-8 -*-
"""
Created on Fri May 24 09:02:39 2019

@author: Training28
"""

1. Write a Python code to import NumPy.

import numpy as np

2. Write a Python program to create 3x3 matrix with values ranging from 5 to 13.

ab=np.arange(5,14).reshape(3,3)
print(ab)

3. Write a Python program to create a null vector of size 10 and update sixth value to 11.

a=np.zeros(10)
a[5]=11
print(a)

4. Write a Python program to create a array with values ranging from 12 to 38.

ac=np.arange(12,39)
print(ac)

6. Write a Python program to reverse an array so that first element becomes last.

reversed=ac[::-1]
print(reversed)

7. Write a Python program to an array converted to a float type.

y=ac.astype(np.float)
type(y)
print(y)

8. Write a Python program to create a 2d array with 1 on the border and 0 inside.

border=np.zeros((5,5))
print(border)
inside=np.pad(border, pad_width=1, mode='constant', constant_values=1)
print(inside)

9. Write a Python program to create a 8x8 matrix and fill it with a checkerboard pattern.
x=np.ones((3,3))
x=np.zeros((8,8),dtype=int)
#odd rows even columns
x[1::2,::2]=1
#even rows odd columns
x[::2,1::2]=1
print(x)

10. Write a Python program to convert a list and tuple into arrays.

a=[1,2,3]
b=(1,2,3)
c=np.asarray(a)
print(c)
type(c)
d=np.asarray(b)
print(d)
type(d)

11. Write a Python program to append values to the end of an array.

a=np.append(a,[[2,3]])
print(a)

12. Write a Python program to create an empty and a full array.

a=np.empty((3,4))
print(a)
y=np.full((3,4),0)
print(y)

13. Write a Python program to convert the values of Centigrade degrees into Fahrenheit degrees. Centigrade values are stored into a NumPy array.
centigrate=[0, 12, 45.21, 34, 99.91]
values=np.array(centigrate)
print(values)
type(values)
print('The value in centigrade degrees:')
print(5*values/9 - 5*32/9)

14. Write a Python program to find the real and imaginary parts of an array of complex numbers.
x = np.sqrt([1+0j])
y = np.sqrt([0+1j])
print(x.real)
print(y.real)
print(x.imag)
print(y.imag)

15. Write a Python program to test whether each element of a 1-D array is also present in a second array.
a1=np.array([1,2,3,4,5])
a2=np.array([1,2,2])
print(np.isin(a1,a2))

16. Write a Python program to find common values between two arrays.
print(np.intersect1d(a1, a2))

17. Write a Python program to get the unique elements of an array.

print(np.unique(a2))

18. Write a Python program to test if all elements in an array evaluate to True.

# 0 evaluates to false in python
print(np.all([10, 20, 0, -50]))

19. Write a Python program to test whether any array element along a given axis evaluates to True.
#checks for all elements in an array to be same and then responds the value
print(np.any([10, 20, 0, -50]))

20. Write a Python program to repeat elements of an array.
a=np.repeat(3,4)
print(a)
ab=np.array([[1,0],[1,3]])
print(np.repeat(ab,3))

21. Write a Python program to find the indices of the maximum and minimum values along the given axis of an array.
check=np.array([1,2,3,5,6,7,8,9])
print('max indicies',np.argmax(check))
print('min indicies',np.argmin(check))

22. Write a Python program compare two arrays using numpy.

a=np.array([1,2,3,4,5])
b=np.array([1,2])
print(np.array_equal(a,b))
print(np.array_equiv(a,b))
print(np.allclose(a,b))

23. Write a Python program to save a NumPy array to a text file.
a=np.array([1,2,3,4,5])
b=np.arange(1.0,2.0,22.3)
np.savetxt('file.out',a,delimiter=',')
np.savetxt('text.csv',b,delimiter=',')

24. Write a Python program to create a contiguous flattened array.
a=np.array([[12,12,3],[23,232,23],[232,21,32]])
b=np.ravel(a)
print(b)

25.Write a program to perform addition,minimum and maximum values from a 3D array.
arr = np.arange(36).reshape(3, 4, 3)
print(arr)
print("\nSum of arr : ", np.sum(arr))
print("Sum of arr(axis = 0) : ", np.sum(arr, axis = 0))
print("Sum of arr(axis = 1) : ", np.sum(arr, axis = 1))
print("Sum of arr(axis = 1) : ", np.sum(arr, axis = 2))

print("\nSum of arr (keepdimension is True): \n",
      np.sum(arr, axis = 1, keepdims = True))

print('minimum',np.min(arr))
print('max value',np.max(arr))


import pandas as pd
from sklearn import linear_model
import statsmodels.api as sm

Stock_Market = {'Year': [2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2016,2016,2016,2016,2016,2016,2016,2016,2016,2016,2016,2016],
                'Month': [12, 11,10,9,8,7,6,5,4,3,2,1,12,11,10,9,8,7,6,5,4,3,2,1],
                'Interest_Rate': [2.75,2.5,2.5,2.5,2.5,2.5,2.5,2.25,2.25,2.25,2,2,2,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75],
                'Unemployment_Rate': [5.3,5.3,5.3,5.3,5.4,5.6,5.5,5.5,5.5,5.6,5.7,5.9,6,5.9,5.8,6.1,6.2,6.1,6.1,6.1,5.9,6.2,6.2,6.1],
                'Stock_Index_Price': [1464,1394,1357,1293,1256,1254,1234,1195,1159,1167,1130,1075,1047,965,943,958,971,949,884,866,876,822,704,719]
                }

df = pd.DataFrame(Stock_Market,columns=['Year','Month','Interest_Rate','Unemployment_Rate','Stock_Index_Price'])

X = df[['Interest_Rate','Unemployment_Rate']] # here we have 2 variables for multiple regression. If you just want to use one variable for simple linear regression, then use X = df['Interest_Rate'] for example.Alternatively, you may add additional variables within the brackets
Y = df['Stock_Index_Price']

# with sklearn
regr = linear_model.LinearRegression()
regr.fit(X, Y)

print('Intercept: \n', regr.intercept_)
print('Coefficients: \n', regr.coef_)

# prediction with sklearn
New_Interest_Rate = 2.75
New_Unemployment_Rate = 5.3
print ('Predicted Stock Index Price: \n', regr.predict([[New_Interest_Rate ,New_Unemployment_Rate]]))

# with statsmodels
X = sm.add_constant(X) # adding a constant

model = sm.OLS(Y, X).fit()
predictions = model.predict(X)

print_model = model.summary()
print(print_model)


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
import statsmodels.api as sm
from statsmodels.formula.api import ols
from ipywidgets import *
from IPython.display import display, HTML
sns.set(color_codes=True)

dataurl = 'https://raw.githubusercontent.com/ming-zhao/Business-Analytics/master/data/regression/'

# printlvl = 0: no output or plot only return result
# printlvl = 1: no output only regplot
# printlvl = 2: no output, but plots: regplot and residual
# printlvl = 3: no output, all plots: regplot and residual and Q-Q
# printlvl = 4: summary, all plots: regplot and residual and Q-Q
# printlvl = 5: summary + ANOVA, all plots: regplot and residual and Q-Q

def analysis(df, y, x, printlvl):
    result = ols(formula=y+'~'+'+'.join(x), data=df).fit()
    if printlvl>=4:
        display(result.summary())
        print('\nstandard error of estimate:{:.5f}\n'.format(np.sqrt(result.scale)))

    if printlvl>=5:
        print("\nANOVA Table:\n")
        display(sm.stats.anova_lm(result, typ=2))

    if printlvl>=1:
        if len(x)==1:
            fig, axes = plt.subplots(1,1,figsize=(8,5))
            sns.regplot(x=x[0], y=y, data=df,
                        ci=None,
                        line_kws={'color':'green',
                                  'label':"$Y$"+"$={:.2f}X+{:.2f}$\n$R^2$={:.3f}".format(result.params[1],
                                                                                result.params[0],
                                                                                result.rsquared)},
                        ax=axes);
            axes.legend()

    if printlvl>=2:
        fig, axes = plt.subplots(1,3,figsize=(20,6))
        axes[0].relim()
        sns.residplot(result.fittedvalues, result.resid, lowess=False, scatter_kws={"s": 80},
                      line_kws={'color':'r', 'lw':1}, ax=axes[0])
        axes[0].set_title('Residual plot')
        axes[0].set_xlabel('Fitted values')
        axes[0].set_ylabel('Residuals')
        axes[1].relim()
        stats.probplot(result.resid, dist='norm', plot=axes[1])
        axes[1].set_title('Normal Q-Q plot')
        axes[2].relim()
        sns.distplot(result.resid, ax=axes[2]);
        if printlvl==2:
            fig.delaxes(axes[1])
            fig.delaxes(axes[2])
    plt.show()
    if printlvl>2:
        display(stats.kstest(result.resid, 'norm'))
    return result

def draw_sample(flag=1, id=1):
    size = 50
    replicate = 100
    np.random.seed(123)
    x = np.linspace(0, 5, size)
    x = np.repeat(x, replicate)
    y = 10 + 3*x+ 4*np.random.normal(size=size*replicate)

    sample_size = 20
    np.random.seed(id+111)
    fig, axes = plt.subplots(1, 1, figsize = (14,8))

    df_pop = pd.DataFrame({'X':x,'Y':y})

    slope, intercept, r_value, p_value, std_err = stats.linregress(df_pop.X,df_pop.Y)
    sns.regplot(x=df_pop.X, y=df_pop.Y, data=df_pop, marker='o', label='population (assumed)',
                    line_kws={'color':'maroon',
                              'label':"$\overline{Y}$"+"$={:.2f}X+{:.2f}$".format(slope, intercept)},
                    scatter_kws={'s':2})

    if flag==1:
        if id==1:
            df_sample = df_pop.loc[
                ((df_pop['Y'] >= 16 + 3 * df_pop['X']) & (df_pop['X'] <= 2.5))
                |((df_pop['Y'] <= 4 + 4 * df_pop['X']) & (df_pop['X'] > 2.5))
            ].sample(n=sample_size)
        if id==2:
            df_sample = df_pop.loc[
                ((df_pop['Y'] <= 2 + 3 * df_pop['X']) & (df_pop['X'] <= 2.5))
                |((df_pop['Y'] >= 15 + 4 * df_pop['X']) & (df_pop['X'] > 2.5))
            ].sample(n=sample_size)
        if id==3:
            df_sample = df_pop.sample(n=sample_size)

        df_sample = df_sample.groupby('X').first()
        df_sample.reset_index(level=0, inplace=True)

        est_slope, est_intercept, r_value, p_value, std_err = stats.linregress(df_sample.X,df_sample.Y)
        sns.regplot(x=df_sample.X, y=df_sample.Y, data=df_sample, marker='o', label='sample', ci=False,
                        line_kws={'color':'orange',
                                  'label':"$Y={:.2f}X+{:.2f}$".format(est_slope, est_intercept)},
                        scatter_kws={'s':35, 'color':'green'})

        axes.vlines(df_sample.X-.02,df_sample.Y - .2*np.sign(df_sample.Y-slope*df_sample.X-intercept),
                    slope*df_sample.X+intercept, colors='darkred', label='error')
        axes.vlines(df_sample.X+.03,df_sample.Y - .2*np.sign(df_sample.Y-est_slope*df_sample.X+est_intercept),
                    est_slope*df_sample.X+est_intercept, colors='darkorange', label='residual')

        plt.title('estimated regression line with different samples')
    else:
        plt.title('population regression line joining means')

    plt.legend()
    plt.show()

def draw_qq(dist):
    np.random.seed(123)
    fig, ax = plt.subplots(1, 2, figsize = (16,5))
    if dist=='normal':
        residuals = np.random.normal(loc=20, scale=6, size=1000)
    if dist=='student_t':
        residuals = np.random.standard_t(df=4, size=1000)
    if dist=='uniform':
        residuals = np.random.uniform(0,20,size=1000)
    if dist=='triangular':
        residuals = np.random.triangular(-2, 0, 9, 1000)

    stats.probplot(residuals, dist="norm", plot=ax[0])
    sns.distplot(residuals, ax=ax[1]);
    plt.show()

def plot_hist(column):
    fig, axes = plt.subplots(1, 1, figsize = (12,6))
    df_salary = pd.read_csv(dataurl+'salaries.csv', header=0, index_col='employee')
    ax = sns.countplot(x=column, data=df_salary, ax=axes)
    total = len(df_salary[column])
    nbars = len(ax.patches)
    for p in ax.patches:
        percentage = '{:.1f}%'.format(100 * p.get_height()/total)
        shift=.12
        if nbars<=2:
            shift=.1
        elif nbars>=10 and nbars<20:
            shift=.25
        elif nbars>=20:
            shift=.5
        x = p.get_x() + p.get_width()/2 - shift
        y = p.get_y() + p.get_height() + .5
        ax.annotate(percentage, (x, y))
    plt.show()

def GL_Ftest(df, y, fullX, reducedX, alpha, typ=2):
    result = analysis(df, y, fullX, printlvl=0)
    if len(reducedX)>0:
        sse_f = sm.stats.anova_lm(result, typ=typ).sum_sq['Residual']
        df_f  = sm.stats.anova_lm(result, typ=typ).df['Residual']
        result = analysis(df, y, reducedX, printlvl=0)
        sse_r = sm.stats.anova_lm(result, typ=typ).sum_sq['Residual']
        df_r  = sm.stats.anova_lm(result, typ=typ).df['Residual']
        F = (sse_r-sse_f)/(df_r - df_f) / (sse_f/df_f)
        p = 1 - stats.f.cdf(F, df_r - df_f, df_f)
    else:
        F = result.fvalue
        p = result.f_pvalue
    if p<alpha:
        print('The F-value is {:.3f} and p-value is {:.3f}. Reject null hypothesis.\
              \nAt least one variable in {} has nonzero slope.'.format(F, p, set(fullX)-set(reducedX)))
    else:
        print('The F-value is {:.3f} and p-value is {:.3f}. Don\'t Reject null hypothesis.\
              \nAll variables in {} have zero slope.'.format(F, p, set(fullX)-set(reducedX)))
    return p

import numpy,from scipy,import statsmodels,from statsmodels


# -*- coding: utf-8 -*-
"""Linear_Regression_Prac_2.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/17xMIzRimWyYhJmmGi6Vl6eHnSpAlJInK

## Linear Regression Prac 2

This notebook will make use of Scikit-learn's Linear Regression class. The documentation is found here: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html

Credits:

The notebooks on linear regression are modifications of various sources which include:
https://scikit-learn.org/stable/auto_examples/linear_model/plot_ols.html
https://medium.com/analytics-vidhya/linear-regression-using-python-ce21aa90ade6
https://www.kdnuggets.com/2019/03/beginners-guide-linear-regression-python-scikit-learn.html/2

## Various Python imports
"""

import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, mean_absolute_error
from sklearn.model_selection import train_test_split

"""## Load the dataset"""

diabetes = datasets.load_diabetes()

"""## Take a look at what has been downloaded"""

diabetes

"""## Print out the feature names"""

diabetes['feature_names']

"""## Take a look at the shape of the data"""

diabetes['data'].shape

"""## Take a look at the shape of the targets (what we are predicting for)"""

diabetes['target'].shape

"""## Use all training features

In this notebook we are interested in creating a multiple variable regression model. So in this case, we will use all variables.

Typically, features are denoted as X and targets as Y (some authors will use 'y' instead).
"""

X = diabetes.data
y = diabetes['target']

"""## Check the shape!"""

X.shape

y.shape

"""## Split the data into training and testing sets"""

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=4278) # 70% training and 30% test

"""## Check the training features shape"""

X_train.shape

"""## Check the testing features shape"""

X_test.shape

"""## Define a linear regression model"""

model = LinearRegression()

"""## Look at what has just being initialised"""

model

"""## Train the model/fit the model"""

model.fit(X_train, y_train)

"""## Print the coefficients"""

model.coef_

"""## Print the intercept"""

model.intercept_

"""## Task: can you write out the model given the above outputs?
y = 152 -48d1 -208d2 + 538d3 + 317d4 -412d5 +192d6 -7d7 + 210d8 + 608d9 + 49d10
...

## Apply the model to the testing features and get the predictions
"""

Y_pred = model.predict(X_test)

"""## Determine the performance of the model on the testing data

There are various options, but commonly used one include:

* mean squared error
* mean absolute error
* root mean squared error

There can be implemented using scikit-learn.

### Mean squared error
"""

mean_squared_error(y_test, Y_pred)

"""### Mean absolute error"""

mean_absolute_error(y_test, Y_pred)

"""### Root mean squared error"""

np.sqrt(mean_squared_error(y_test, Y_pred))

"""## Task: is the performance better than using a single variable?

Yes
"""


import math
import numpy as np

def segmentLineIntersection(seg_start, seg_end, line_p1, line_p2):
    """
    Return None if there is no intersection between the line and the segment or
    if the lines are coincident, otherwise, return the intersection point
    """
    # Line-segment intersection from points
    # https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
    x1 = seg_start[0]
    y1 = seg_start[1]
    x2 = seg_end[0]
    y2 = seg_end[1]
    x3 = line_p1[0]
    y3 = line_p1[1]
    x4 = line_p2[0]
    y4 = line_p2[1]
    num = (x1-x3)*(y3-y4)-(y1-y3)*(x3-x4)
    den = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)
    if (den == 0):
        # Line + segment are parallel or coincident
        return None
    t = num / den
    if t < 0 or t > 1:
        return None
    return np.array([x1+t*(x2-x1), y1+t*(y2-y1)])

def segmentCircleIntersection(seg_start, seg_end, circle_center, circle_radius):
    """
    Return None if there is no intersection between the segment and the
    circle. If there is an interesection, return the first intersection with the
    circle.
    """
    # First intersect segment with the normal line passing through center of the circle
    seg_dir = seg_end - seg_start
    seg_dir = seg_dir / np.linalg.norm(seg_dir)
    normal_line_dir = np.array([-seg_dir[1], seg_dir[0]])
    normal_intersection = segmentLineIntersection(seg_start, seg_end,
                                                  circle_center, circle_center + normal_line_dir)
    # Intersection is outside of segment or too far from circle_center
    if normal_intersection is None:
        return None
    dist = np.linalg.norm(circle_center - normal_intersection)
    if dist > circle_radius:
        return None
    # Intersection is inside the circle, now going to the border in opposite direction of seg_dir
    offset_length = math.sqrt(circle_radius ** 2 - dist ** 2)  # Pythagore
    return normal_intersection - offset_length * seg_dir

def linePointProjection(seg_start, seg_end, p):
    e1 = seg_end - seg_start
    e2 = p - seg_start
    return seg_start + e1 * e1.dot(e2) / np.sum(e1**2)

def segmentPointProjection(seg_start, seg_end, p):
    # Step 1: checking that point is on the appropriate side
    e1 = seg_end - seg_start
    e2 = p - seg_start
    if e1.dot(e2) < 0:
        return seg_start
    # Step 2: projecting point on segment
    proj = seg_start + e1 * e1.dot(e2) / np.sum(e1**2)
    # Step 3: checking that point is closer
    if np.linalg.norm(e1) < np.linalg.norm(proj - seg_start):
        return seg_end
    return proj


import unittest
import numpy as np
import calculus as calc

class CalcTests(unittest.TestCase):
    def test_euler(self):
        x = np.pi / 4.
        y_est, ys, ds = calc.euler(dy_dx = lambda x,y: np.cos(x),
                                   y0 = 0,
                                   h = 1e-4,
                                   start = 0,
                                   end = x)
        self.assertAlmostEqual(y_est, np.sin(x), places = 3,
                               msg = "Estimate should be close to actual sin of value")

    def test_newton(self):
        fx = lambda x: -4*x**3 - 2*x**2 - 2*x - 3
        dx = lambda x: -12*x**2 - 4*x - 2
        x_n = calc.newton(iter = 10, x0 = -2, fx = fx, dx = dx)
        self.assertAlmostEqual(x_n[2], -1.058263, places = 4,
                               msg = "Estimate of root does not match answer")

    def test_mvt(self):
        a = 1
        b = 4
        fx = lambda x: x**3
        f_c = calc.mvt(a = a, b = b, fx = fx)
        self.assertEqual(f_c, 21, msg = "MVT of c should be 21")


#setup: import numpy as np ; np.random.seed(0); N = 100000 ; a = np.random.random(N); b = 0.1; c =1.1
#run: log_likelihood(a, b, c)
#from: http://arogozhnikov.github.io/2015/09/08/SpeedBenchmarks.html
import numpy

#pythran export log_likelihood(float64[], float64, float64)
def log_likelihood(data, mean, sigma):
    s = (data - mean) ** 2 / (2 * (sigma ** 2))
    pdfs = numpy.exp(- s)
    pdfs /= numpy.sqrt(2 * numpy.pi) * sigma
    return numpy.log(pdfs).sum()


import numpy as np

class LinearRegressionModel(object):
    """
    This class performs Linear regression on a set of input data
    """
    def __init__(self, featureCount) -> None:
        self.featureCount = featureCount
        self.thetas = np.random.randn(featureCount,1)

    def _hypothesis(self, X):
        """
        X: (m x n) matrix
        thetas: (n x 1) vector
        hypothesis(X) = X dot (theta's)
        """
        return np.dot(X, self.thetas)

    def _computeCost(self, y, prediction):
        """
        y: (m x 1) vector of true y values
        prediction: (m x 1) vector of predicted y values
        cost = (1/2m) * sum((prediction - y)^2)
        """
        m = y.shape[0]
        cost = np.sum(np.square(prediction - y))
        return cost / (2 * m)

    def _calculateNewThetas(self, X, y, prediction):
        """
        X: (m x n) matrix of feature data
        y: (m x 1) vector of true y values
        prediction: (m x 1) vector of predicted y values
        dTheta = sum((prediction-y)^T dot X) / m
        """
        m = y.shape[0]
        dTheta = np.sum(np.dot(np.transpose(prediction - y), X), axis=0) / m
        return dTheta

    def _updateThetas(self, dTheta, alpha):
        """
        dTheta: (n x 1) vector of updated theta values
        alpha: scalar learning rate to scale down value to update thetas
        newThetas = oldThetas - alpha(dTheta)
        """
        self.thetas = self.thetas - alpha * np.reshape(dTheta, (self.featureCount,1))

    def train(self, X_train, Y_train, alpha, iterations, display=1):
        """
        X_train: (m x n) matrix of feature data
        Y_train: (m x 1) vector of true y values
        alpha: scalar learning rate
        iterations: scalar number of iterations Gradient Decent should run
        repeat until converge:{
            1. Make a prediction
            2. Calculate new theta values
            3. Update current theta values
            4. Compute and save cost
                - Cost should decrease with every iteration
        }
        """
        costs = []
        cost = 1
        i = 0
        for i in range(iterations):
            prediction = self._hypothesis(X_train)
            dTheta = self._calculateNewThetas(X_train, Y_train, prediction)
            self._updateThetas(dTheta, alpha)
            cost = self._computeCost(Y_train, prediction)
            costs.append(cost)
            if display:
                if i % 100 == 0:
                    print('Iter: {}, Current loss: {:.4f}'.format(i, cost))
            i += 1
        return costs

    def test(self, X_test, Y_test, display=1):
        """
        X_test: (m x n) matrix of feature values
        Y_test: (m x 1) vector of true y values
        To test our Model{
            1. Make a prediction based off input X_test[i]
            2. Compare to corresponding input Y_test[i]
            3. Compute cost
                -Cost should be low
        }
        """
        prediction = np.apply_along_axis(self._hypothesis, axis=1, arr=X_test)
        for i in range(Y_test.shape[0]):
            cost = self._computeCost(Y_test[i], prediction[i])
            if display:
                print('Predicted: {:.4f} Actual: {:.4f} Cost: {:.4f}'.format(prediction[i,0], Y_test[i,0], cost))


import numpy as np
import matplotlib.pyplot as plt

##### Sigmoid
sigmoid = lambda x: 1 / (1 + np.exp(-x))

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,100)

fig = plt.figure()
plt.plot(y,sigmoid(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('Sigmoid Function')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-2, -1, 0, 1, 2])

plt.ylim(-2, 2)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('sigmoid.png')

fig = plt.figure()

##### TanH
tanh = lambda x: 2*sigmoid(2*x)-1

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,100)

plt.plot(y,tanh(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('TanH Function')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])

plt.ylim(-4, 4)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('tanh.png')

fig = plt.figure()

##### ReLU
relu = lambda x: np.where(x>=0, x, 0)

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,1000)

plt.plot(y,relu(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('ReLU')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])

plt.ylim(-4, 4)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('relu.png')

fig = plt.figure()

##### Leaky ReLU
leakyrelu = lambda x: np.where(x>=0, x, 0.1*x)

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,1000)

plt.plot(y,leakyrelu(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('Leaky ReLU')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])

plt.ylim(-4, 4)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('lrelu.png')

fig = plt.figure()

##### Binary Step
bstep = lambda x: np.where(x>=0, 1, 0)

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,1000)

plt.plot(y,bstep(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('Step Function')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-2, -1, 0, 1, 2])

plt.ylim(-2, 2)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('step.png')

print('done')


import numpy as np

def fourier_series_coefficients(f, T, N, return_complex=True, sample_rate=1000):
    r"""Calculates the first 2*N+1 Fourier series coefficients of a periodic function.

    Given a periodic, function f(t) with period T, this function returns the
    complex coefficients {c0,c1,c2,...}
    such that:

    .. math:: f(t) ~= \sum_{k=-N}^N c_k \cdot e^{i2 \pi kt/T}

    where we define :math: `c_{-n}=\overline{c_n}`

    Refer to `wikipedia <http://en.wikipedia.org/wiki/Fourier_series>`_ for the relation between the real-valued and
    complex valued coeffs.

    Notes
    -----
    This function was copied from
    `stackoverflow <https://stackoverflow.com/questions/4258106/how-to-calculate-a-fourier-series-in-numpy>`_.

    Parameters
    ----------
    f : callable
        the periodic function, a callable like f(t)
    T : float
        the period of the function f, so that f(0)==f(T)
    N : int
        the function will return the first N+1 Fourier coeff.
    return_complex : bool, optional
        defaults to True
    sample_rate : int, optional
        used to tune fast Fourier transform (FFT) algorithm accuracy

    Returns
    -------
    ndarray
        numpy 1-dimensional complex-valued array of size N+1

    """
    # From Shanon theorem we must use a sampling freq. larger than the maximum
    # frequency you want to catch in the signal. (Minimum 1000*N required for sufficient accuracy.)
    f_sample = sample_rate * N

    t = np.linspace(-T/2, T/2, f_sample, endpoint=False)

    y = np.fft.rfft(f(t)) / t.size

    # multiply odd terms by -1 to match SageMath
    y[1::2] *= -1

    # only take the number of coefficients requested
    y = y[:N + 1]

    if return_complex:
        return y
    else:
        y *= 2
        return y[0].real, y[1:].real, -y[1:].imag


import numpy as np
import pickle

def sigmoid(x):
    """
    Applies the sigmoid transformation on a given 1d vector

    Parameters
    ----------
    x : ndarray
        1d Array to be sigmoided

    Returns
    -------
    ndarray
        1d Array of same shape as x containing sigmoided values of x

    Notes
    -----
    σ(x) = 1/(1 + e^-x) = (e^x)/(1 + e^x)
    where:
        σ = sigmoid function
        x = input
        e = Euler's number

    """
    return np.where(x >= 0, 1 / (1 + np.exp(-x)), np.exp(x) / (1 + np.exp(x)))

def softmax(x):
    """
    Applies the softmax transformation on a given 2d vector

    In order to make softmax more stable, x.max is subtracted from each value of x before performing softmax.

    Parameters
    ----------
    x : ndarray
        2d array to be softmaxed

    Returns
    -------
    ndarray
        2d Array of same shape as x containing softmaxed values of x

    Notes
    -----
    σ(x) = (e^x_i)/ Σ (e^x)
    where:
        σ = softmax function
        x_i = ith value of x
        x = input
        e = Euler's number

    """

    x = x - x.max(axis=1, keepdims=True)
    y = np.exp(x)
    return y / y.sum(axis=1, keepdims=True)

def one_hot(labels, categories):
    """
    One hot encodes the labels.

    Parameters
    ----------
    labels : ndarray
        1d array of ints containing labels.

    categories :  int
        the number of categories to encode the labels onto

    Returns
    -------
    ndarray
        2d Array of shape (len(x), categories) containing one hot encoded labels

    """
    y = np.zeros((labels.size, categories))
    y[np.arange(labels.size), labels] = 1
    return y

def batches(x, y, batch_size):
    """
    Yields successive batch_sized chunks of inputs and corresponding outputs from list.

    Parameters
    ----------
    x : list
        list of inputs
    y : list
        list of outputs
    batch_size :
        size of the batches to be yielded.

    Returns
    -------
    list
        batch of inputs of length (batch_size)
    list
        batch of outputs of length (batch_size)

    """

    for i in range(0, len(x), batch_size):
        yield x[i:i + batch_size], y[i:i + batch_size]

def save_model(model, filename='model'):
    """
    Saves an object to file

    Parameters
    ----------
    model : object
        any object to be stored on file
    filename : str
        name of the file the object needs to be stored on. Default: model

    """
    with open(filename, 'wb') as f:
        pickle.dump(model, f, protocol=-1)

def load_model(filename='model'):
    """
    Loads an object from file.

    Parameters
    ----------
    filename: str
        name of the file the object needs to be loaded from. Default: model

    Returns
    -------
        object that was stored on the file
    """
    with open(filename, 'rb') as f:
        return pickle.load(f)


import numpy as np

def makeMoffat(size , fwhm =3 , b = 2.5  , center = None):

    #x = np.arange(0, size, 1, float)
    #y = x[:,np.newaxis]
    x = np.linspace(0.5, size-.5 , size)
    y = x[:,np.newaxis]

    if center is None:
        x0 = y0 = size / 2.
    else:
        x0 = center[0]
        y0 = center[1]

    a = fwhm/(2.*(2**(1./b)-1)**.5)
    r = ((x-x0)**2./a**2. + (y-y0)**2./a**2.)**.5

    return ((b - 1.)*(1. + r**2.)**(-1.*b))/(np.pi*(a**2.))

def makeGaussian(size , FWHM  , e = 0 , center = None):

    f = FWHM/(2.35482)
    #f = FWHM/(2.*np.sqrt(2.*np.log(2.)))
    #x = np.arange(0, size, 1, float)
    #y = x[:,np.newaxis]
    x = np.linspace(0.5, size-.5 , size)
    y = x[:,np.newaxis]

    if center is None:
        x0 = y0 = size / 2.
    else:
        x0 = center[0]
        y0 = center[1]

    r = ((x-x0)**2. + ((y-y0)**2.)*(1. + np.abs(e))**2./(1. - np.abs(e))**2.)**.5
    factor = 1./(2.*np.pi*f**2.)
    return factor*np.exp((-1.*r**2.)/(2.*f**2.))

if __name__ == "__main__":
    print 'profile main'


import math
import numpy as np
from scipy import ndimage

def gauss2d(sigma, fsize):
  """
  Args:
    sigma: width of the Gaussian filter
    fsize: dimensions of the filter

  Returns:
    g: *normalized* Gaussian filter
  """

  g = np.zeros((fsize, 1))
  offset = fsize // 2
  for i in range(-offset, -offset + fsize):
     g[i + offset] = 1/(np.sqrt(2*np.pi) * sigma) * np.exp(-(i**2) / 2*sigma**2) \

  g = g / g.sum()

  return g

def createfilters():
  """
  Returns:
    fx, fy: filters as described in the problem assignment
  """

  g = gauss2d(0.9, 3)
  d_x = np.array([-1, 0, 1])
  d_x = d_x[np.newaxis, :]
  fx = g.dot(d_x)
  fy = d_x.T.dot(g.T)

  return fx, fy

def filterimage(I, fx, fy):
  """ Filter the image with the filters fx, fy.
  You may use the ndimage.convolve scipy-function.

  Args:
    I: a (H,W) numpy array storing image data
    fx, fy: filters

  Returns:
    Ix, Iy: images filtered by fx and fy respectively
  """

  Ix = ndimage.convolve(I, fx, mode='constant', cval=0.0)
  Iy = ndimage.convolve(I, fy, mode='constant', cval=0.0)

  return Ix, Iy

def detectedges(Ix, Iy, thr):
  """ Detects edges by applying a threshold on the image gradient magnitude.

  Args:
    Ix, Iy: filtered images
    thr: the threshold value

  Returns:
    edges: (H,W) array that contains the magnitude of the image gradient at edges and 0 otherwise
  """

  magitude = np.sqrt(Ix**2 + Iy**2)
  edges= np.where(magitude >= thr, magitude, 0)

  return edges

def nonmaxsupp(edges, Ix, Iy):
  """ Performs non-maximum suppression on an edge map.

  Args:
    edges: edge map containing the magnitude of the image gradient at edges and 0 otherwise
    Ix, Iy: filtered images

  Returns:
    edges2: edge map where non-maximum edges are suppressed
  """
  m, n = edges.shape
  edges2 = np.zeros((m, n))
  for i in range(1, m-1):
        for j in range(1, n-1):
            theta = np.arctan(Iy[i, j] / Ix[i, j]) * 180 / np.pi
  # handle top-to-bottom edges: theta in [-90, -67.5] or (67.5, 90]
            if (-90 <= theta <= -67.5) or (67.5 < theta <= 90):
                  q, r = edges[i-1, j], edges[i+1, j]
  # handle left-to-right edges: theta in (-22.5, 22.5]
            elif -22.5 < theta <= 22.5:
                  q, r = edges[i, j-1], edges[i, j+1]
  # handle bottomleft-to-topright edges: theta in (22.5, 67.5]
            elif 22.5 < theta <= 67.5:
                  q, r = edges[i+1, j-1], edges[i-1, j+1]
  # handle topleft-to-bottomright edges: theta in [-67.5, -22.5]
            elif -67.5 <= theta <= -22.5:
                  q, r = edges[i-1, j-1], edges[i+1, j+1]

            if (edges[i, j] >= q) and (edges[i, j] >= r):
                  edges2[i, j] = edges[i, j]
  return edges2


from numpy import array, zeros, diag, diagflat, dot
from sympy import Eq, Expr, Symbol

def jacobi(A, b, N=25, x=None):
    """Solves the equation Ax=b via the Jacobi iterative method."""
    # Create an initial guess if needed
    if x is None:
        x = zeros(len(A[0]))

    # Create a vector of the diagonal elements of A
    # and subtract them from A
    D = diag(A)
    R = A - diagflat(D)

    # Iterate for N times
    for i in range(N):
        x = (b - dot(R,x)) / D
    return x

def seidel(a, x, b):
    # Finding length of a(3)
    n = len(a)
    # for loop for 3 times as to calculate x, r , z
    for j in range(0, n):
        # temp variable d to store b[j]
        d = b[j]

        # to calculate respective xi, yi, zi
        for i in range(0, n):
            if (j != i):
                d -= a[j][i] * x[i]
        # updating the value of our solution
        x[j] = d / a[j][j]
    # returning our updated solution
    return x

def eulers_method(x0, xf, num, slope_equation: Expr, y_: Symbol, x_: Symbol, initial_slope: float):
    delta = (xf - x0) / num

    y_explicits = [slope_equation.subs({y_: initial_slope, x_: x0})]
    y_implicits = [slope_equation.subs({y_: initial_slope, x_: x0})]
    for i in range(num):
        slope_e = slope_equation.subs({
            y_: y_explicits[-1],
            x_: x0 + delta * i,
        })
        y_explicits.append(y_explicits[-1] + delta * slope_e)

    for i in range(num):
        y_implicits.append(y_implicits[-1] + delta * -1)

if __name__ == '__main__':
    # int(input())input as number of variable to be solved
    n = 3
    a = []
    b = []
    # initial solution depending on n(here n=3)
    x = [0, 0, 0]
    a = [[4, 1, 2], [3, 5, 1], [1, 1, 3]]
    b = [4, 7, 3]
    print(x)

    # loop run for m times depending on m the error value
    for i in range(0, 25):
        x = seidel(a, x, b)
        # print each time the updated solution
        print(x)

    A = array([[2.0, 1.0], [5.0, 7.0]])
    b = array([11.0, 13.0])
    guess = array([1.0, 1.0])

    sol = jacobi(A, b, N=25, x=guess)