"""
In the previous examples, we solved a constrained Rosenbrock problem and the sphere problem.

While both of these problems are nonlinear, they're really just a mix of simple polynomials. This means that we can
express it with overloaded Python operators: [+, -, *, /, **, ...] and so on.

But what if we want to use a function that's not part of simple arithmetic - something like cos(x), for example?
That's what we'll explore here!

-----

One of the coolest things about the `Opti` stack is that it's fast - really, **really** fast. You can solve
nonlinear, nonconvex optimization problems with thousands of variables in mere seconds on a laptop, thanks to
automatic differentiation (AD) provided by CasADi and modern optimization methods via IPOPT.

In order for AD to work, we need to be able to make a list (more precisely, a directed graph) of each mathematical
operation (think +, -, *, /, **, log(), fabs(), etc.) that's applied throughout our optimization formulation (some
call this list a "trace" in the literature). This means we can't just use NumPy out of the box like we'd like to,
because some of its functions break our trace.

Instead, we need to use a custom math library, which sounds scary at first. However, the AeroSandbox development team
has tried to make this as seamless to you as possible - by writing our own NumPy with identical syntax! Here's how
this works:

	* `aerosandbox.numpy` imports the entirety of NumPy.

	* For NumPy functions that break our AD trace (e.g. `np.sum()`), we've rewritten our own versions of them. This
	means:

		* If you pass normal NumPy arrays to these functions, they'll work 100% exactly the same as they would in
		original NumPy - same result and same speed.

		* If you pass optimization variables to these functions, they'll intelligently switch over to a version of the
		function that allows us to preserve the AD trace.

	* **So what does this mean for you, dear user?** It means that when working with AeroSandbox, all you need to do
	is replace `import numpy as np` with `import aerosandbox.numpy as np`, and you're good to go!

	* Caveat: Not all NumPy functions that should be overwritten have been overwritten - we've done our best,
	but there are *sooo* many obscure NumPy functions! If you get an error on a function you want to use,
	raise an issue ticket!

You'll notice that in our last example, we imported `aerosandbox.numpy` in order to use the `sum()` function.

Here, let's do an example with some other functions.
"""
import aerosandbox as asb
import aerosandbox.numpy as np

opti = asb.Opti()

x = opti.variable(init_guess=3)

f = np.exp(  # You can use normal operations from NumPy like this!
    np.cos(  # These functions are intelligently overloading in the background...
        x
    )
)

opti.minimize(f)

opti.subject_to([
    x >= 0,
    x <= np.pi / 2
])
"""
Note another feature we just introduced here: you can give `opti.subject_to()` a list of constraints, not just a
single constraint like we did before! Often, this makes for cleaner, more readable code.

Also, note that you can declare variables, constraints, and objectives in any order. As long as they're all set in
place by the time you call `sol = opti.solve()`, you're good. Speaking of, let's solve!
"""
sol = opti.solve()

x_opt = sol.value(x)

print(f"x = {x_opt}")
"""

Nice, it solved! The value of x at the optimum turns out to be equal to pi / 2.

Note that there are tons and tons of nonlinear functions you can use - everything from logarithms to vector norms to
linear solves to eigenvalue decompositions. The list is quite extensive and can be viewed at:
`aerosandbox/numpy/test_numpy/test_all_operations_run.py`, where many of the valid operations are listed.

This would not be possible without tons of hard work by the CasADi team!

"""

tutorial/1 - Optimization and Math/4 - (More) Nonlinear functions.py


import torch
import math
from math import log10, sqrt
import numpy as np
import torch.nn.functional as F
import cv2

def PSNR(deblurIm, sharpIm):
    """
    Calculates the PSNR of the deblurred image
    using the formula:
    PSNR = 20* log10(maxI/sqrt(mean(delburred image- target image)**2))
    """
    deblurIm = deblurIm.astype(np.float64)
    sharpIm = sharpIm.astype(np.float64)
    mse = np.mean((deblurIm - sharpIm) ** 2)
    # MSE is zero means no noise is present in the signal .
    # Therefore PSNR have no importance.
    if(mse == 0):
        return float('inf')
    max_pixel = 255.0
    psnr = 20 * math.log10(max_pixel / math.sqrt(mse))

    return psnr

def SSIM(deblurIm, sharpIm):
    """
    Calculates the SSIM (The structural similarity index measure) between
    the target image and the deblurred image with:

    SSIM = mean((2*mu_d*mu_s + C1)(2*sig_d_sig_s + C2)/(mu_d^2 + mu_s^2 + C1)(sig_d + sig_s + C2))
    """
    C1 = 0.01 ** 2
    C2 = 0.03 ** 2

    deblurIm = deblurIm.astype(np.float64)
    sharpIm = sharpIm.astype(np.float64)

    filt_ = cv2.getGaussianKernel(11, 1.5)
    filt = np.outer(filt_, filt_.transpose())

    mu_d = cv2.filter2D(deblurIm, -1, filt)[5:-5, 5:-5]
    mu_s = cv2.filter2D(sharpIm, -1, filt)[5:-5, 5:-5]

    mu_d_2 = mu_d**2
    mu_s_2 = mu_s**2
    mu_d_mu_s = mu_d * mu_s

    sig_d = cv2.filter2D(deblurIm**2, -1, filt)[5:-5, 5:-5] - mu_d_2
    sig_s = cv2.filter2D(sharpIm**2, -1, filt)[5:-5, 5:-5] - mu_s_2
    sig_d_sig_s = cv2.filter2D(deblurIm * sharpIm, -1, filt)[5:-5, 5:-5] - mu_d_mu_s

    ssim_ = ((2 * mu_d_mu_s + C1) * (2 * sig_d_sig_s + C2)) / ((mu_d_2 + mu_s_2 + C1) * (sig_d + sig_s + C2))

    ssim = ssim_.mean()

    return ssim


"""Defines vector rotation (lower case) and frame tranformations (upper case).
   Both transformations can be concatenated to be performed in right-to-left
   upon a given vector operator. Note that the *dot()* method must be used,
   since these functions return a 2d *numpy.array* object.
"""

import numpy
from math import sin, cos, atan2

def x(tht_rad):
    """Returns a 3x3 numpy array (matrix) that, when used to multiply a vector
       (using the *dot* method), results in that vector rotated about the x axis
       by the given angle (in radians).
    """
    return numpy.array([
        [1, 0, 0],
        [0, cos(tht_rad), -sin(tht_rad)],
        [0, sin(tht_rad), cos(tht_rad)]])

def y(tht_rad):
    """Returns a 3x3 numpy array (matrix) that, when used to multiply a vector
       (using the *dot* method), results in that vector rotated about the y axis
       by the given angle (in radians).
    """
    return numpy.array([
        [cos(tht_rad), 0, sin(tht_rad)],
        [0, 1, 0],
        [-sin(tht_rad), 0, cos(tht_rad)]])

def z(tht_rad):
    """Returns a 3x3 numpy array (matrix) that, when used to multiply a vector
       (using the *dot* method), results in that vector rotated about the z axis
       by the given angle (in radians).
    """
    return numpy.array([
        [cos(tht_rad), -sin(tht_rad), 0],
        [sin(tht_rad), cos(tht_rad), 0],
        [0, 0, 1]])

def X(tht_rad):
    """Returns a 3x3 numpy array (matrix) that, when used to multiply a vector
       (using the *dot* method), results in that vector evaluated in a new frame
       defined by a rotation about the x axis by the given angle (in radians).
    """
    return numpy.array([
        [1, 0, 0],
        [0, cos(tht_rad), sin(tht_rad)],
        [0, -sin(tht_rad), cos(tht_rad)]])

def Y(tht_rad):
    """Returns a 3x3 numpy array (matrix) that, when used to multiply a vector
       (using the *dot* method), results in that vector evaluated in a new frame
       defined by a rotation about the y axis by the given angle (in radians).
    """
    return numpy.array([
        [cos(tht_rad), 0, -sin(tht_rad)],
        [0, 1, 0],
        [sin(tht_rad), 0, cos(tht_rad)]])

def Z(tht_rad):
    """Returns a 3x3 numpy array (matrix) that, when used to multiply a vector
       (using the *dot* method), results in that vector evaluated in a new frame
       defined by a rotation about the z axis by the given angle (in radians).
    """
    return numpy.array([
        [cos(tht_rad), sin(tht_rad), 0],
        [-sin(tht_rad), cos(tht_rad), 0],
        [0, 0, 1]])

def xyz2sph(xyz):
    """Transforms cartesian coordinates into a spherical coordinate system (with
       polar singularities at +/- z). Returns a 3-component numpy array with
       +z rotation (radians), -y rotation (radians), and radius values.
    """
    xy = (xyz[0]**2 + xyz[1]**2)**0.5
    phi_rad = atan2(xyz[1], xyz[0])
    tht_rad = atan2(xyz[2], xy)
    r = (xyz[0]**2 + xyz[1]**2 + xyz[2]**2)**0.5
    return numpy.array([phi_rad, tht_rad, r])

def sph2xyz(sph):
    """Transforms spherical coordinates into a cartesian coordinates system
       (assuming polar singularities at +/- z). Accepts a 3-component array/list
       with +z rotation (radians), -y rotation (radians), and radius values.
    """
    x = cos(sph[0]) * cos(sph[1])
    y = sin(sph[0]) * cos(sph[1])
    z = sin(sph[1])
    return sph[2] * numpy.array([x, y, z])


#!/usr/local/bin/python3

import math
import numpy as np

# get prime factors unduplicatedly, optimized
def getPrimes( num ):
    pset = set()
    if 0 == num % 2:
        pset.add(2)
    for i in range(3, num+1, 2):
        if 0 == num % i:
            isprime = True
            for j in range(3, int(math.sqrt(i))+1, 2):
                if 0 == i % j and i != j:
                    isprime = False
                    break
            if isprime:
                pset.add(i)
    if len(pset) == 0:
        pset.add(num)
    return pset

# get prime factor lists, optimized: sorted by set length
def getPrimesLists( start, end ):
    plist = list()
    for i in range(start, end+1):
        plist.append(getPrimes(i))
    plist.sort(key=lambda ps:len(ps))
    return plist

# find frequent itemsets, to be optimized: implemented in multi-round map-reduce
def findFrequentItemsets( buckets, candset, cursize, thrd ):
    # print(len(buckets), len(candset), cursize, thrd)
    filist = list()
    newcandset = list()
    # count frequent item sets in current loop
    for itemset in buckets:
        if len(itemset) == cursize:
            maybe = False
            if len(candset) == 0:
                maybe = True
            else:
                for cand in candset:
                    if set(cand).issubset(set(itemset)):
                        maybe = True
            if maybe:
                count = 0
                for bucket in buckets:
                    if set(itemset).issubset(set(bucket)):
                        count += 1
                        if count >= thrd:
                            existed = False
                            for check in filist:
                                if itemset == check:
                                    existed = True
                                    break
                            if not existed:
                                filist.append(itemset)
                            break
    # construct candidate item sets for next loop
    # print(filist)
    for i in range(len(filist)-1):
        for j in range(i+1, len(filist)):
            cand = list(set(filist[i]).union(set(filist[j])))
            if len(cand) == cursize+1:
                existed = False
                for check in newcandset:
                    if cand == check:
                        existed = True
                        break
                if not existed:
                    newcandset.append(cand)
    if len(newcandset) == 0:
        return filist
    # next loop
    filist.extend(findFrequentItemsets( buckets, newcandset, cursize+1, thrd ))
    # return current result
    return filist

# sort frequent itemsets list & output
def sortFISandOutput( filist, outputfile ):
    outlist = list()
    dtype = list()
    order = list()
    for i in filist:
        outlist.append(tuple(sorted(list(i))))
    print(outlist)
    maxfield = len(outlist[len(outlist)-1])
    for i in range(1, maxfield+1):
        dtype.append((str(i), int))
        order.append(str(i))
    # print(dtype, order)
    outlist = np.array(outlist, dtype = dtype)
    outlist.sort(order = order)
    with open(outputfile, 'w') as f:
        for out in outlist:
            # print(out)
            for i in out:
                f.write("%2d\t" % i)
            f.write("\n")
    return 0

if __name__ == "__main__":
    start = 2
    end = 10000
    dimstart = 3
    threshold = 50
    outputfile = './B.txt'
    buckets = getPrimesLists(start, end)
    sortFISandOutput( findFrequentItemsets(buckets, [], dimstart, threshold), outputfile)
    # print( findFrequentItemsets(buckets, set([]), dimstart, threshold))


import numpy as np
import h5py

# Normalized between 0-1
def rescale(rmat):
    min_x = np.min(rmat)
    max_min_x = np.max(rmat) - min_x
    norm_rmat = (rmat - min_x)/ max_min_x
    return norm_rmat

# Shifting to non-negative by adding the minimum number
def shifting_min(rmat):
    min_x = np.min(rmat)*-1
    norm_rmat = rmat + min_x
    return norm_rmat

# Z-score
def z_score(rmat):
    mean_x = np.mean(rmat)
    std_x = np.std(rmat)
    norm_rmat = (rmat - mean_x)/std_x
    return norm_rmat

# Variance
def variance_neuron_rank(input):
    rank = []
    for i in range(input.shape[0]):
        slice_neuron = input[i]
        unfold_slice_neuron = slice_neuron.flatten()
        var = np.var(unfold_slice_neuron)
        mean = np.average(unfold_slice_neuron)
        rank.append((i, var, mean))
    rank = np.array(sorted(rank, reverse=True, key=lambda pair: pair[1]))
    return rank

# Coefficient of variation
def std_mean_neuron_rank(input):
    rank = []
    for i in range(input.shape[0]):
        slice_neuron = input[i]
        unfold_slice_neuron = slice_neuron.flatten()
        std = np.std(unfold_slice_neuron)
        mean = np.average(unfold_slice_neuron)
        rank.append((i, std/mean))
    rank = np.array(sorted(rank, reverse=True, key=lambda pair: pair[1]))
    return rank

# Signal to noise ratio
def signal_to_noise_ratio(rmat, marking):
    snr_list = []
    for i in range(rmat.shape[0]):
        signals = []
        for j in range(4):  # 4 condition stimuli
            marks = np.where(marking == j+1)[0]
            values = rmat[i,:,marks[0]:marks[-1]+1] # At ith neuron, all timesample, at marking trial
            timesample_mean = np.mean(values, axis=1) # Compute mean of each timesample (17,)
            signals.append(timesample_mean)
        signals_var = np.var(signals)
        total_var = np.var(rmat[i])
        snr = signals_var/total_var # signal/noise
        snr_list.append([i, snr])
    rank = np.array(sorted(snr_list, reverse=True, key=lambda pair: pair[1]))
    return rank

# Limit variance with IQR
def IQR(rank):
    Q1 = np.percentile(rank[:,1], 25)
    Q3 = np.percentile(rank[:,1], 75)
    IQR = Q3 - Q1
    min_iqr = Q1 - (1.5 * IQR)
    max_iqr = Q3 + (1.5 * IQR)
    print(f'Q1 = {Q1} Q3={Q3}, min_iqr = {min_iqr}, max_iqr = {max_iqr}, iqr = {IQR}')

    outlier_mask = np.where((rank[:,1] > max_iqr) | (rank[:,1] < min_iqr), True, False)
    outlier_mask_lower = np.where(rank[:,1] < min_iqr, True, False)
    outlier_mask_upper = np.where(rank[:,1] > max_iqr, True, False)

    outlier_neuron_no = rank[outlier_mask]
    outlier_per = f'Overall outlier percentage: {(np.sum(outlier_mask)/len(rank))*100:.3f}% Upper IQR outlier: {(np.sum(outlier_mask_upper)/len(rank))*100:.3f}% Lower IQR outlier: {(np.sum(outlier_mask_lower)/len(rank))*100:.3f}%'
    print('Outlier percentage:', outlier_per)
    return outlier_neuron_no[:,0]

def lower_boundary_threshold(rank, percentage=0.2):
    rank_upper, rank_lower = np.split(rank,[int((1 - (percentage/100)) * rank.shape[0])])
    return rank_lower[:,0]

def order_by_trial(rmat_act, olfactory_path, visual_path, masked_trial_no):
    f_olfactory = h5py.File(olfactory_path, 'r') # olfactory trial sequences
    f_visual = h5py.File(visual_path, 'r') # visual trial sequences

    olfactory = f_olfactory[list(f_olfactory.keys())[-1]]
    ref = [olfactory[0,0], olfactory[1,0]]
    olfactory_vertical = np.array(f_olfactory[ref[0]]).squeeze() # matlab file conversion swap col/row
    olfactory_angled = np.array(f_olfactory[ref[1]]).squeeze()

    visual = f_visual[list(f_visual.keys())[-1]]
    ref = [visual[0,0], visual[1,0]]
    visual_vertical = np.array(f_visual[ref[0]]).squeeze()
    visual_angled = np.array(f_visual[ref[1]]).squeeze()

    stimuli_mark = np.concatenate((np.ones(len(visual_vertical)), np.ones(len(olfactory_vertical))*2, np.ones(len(visual_angled))*3, np.ones(len(olfactory_angled))*4))
    seq_trial = np.concatenate((visual_vertical, olfactory_vertical, visual_angled, olfactory_angled))
    # mask out the nan
    seq_trial = seq_trial[masked_trial_no]
    stimuli_mark = stimuli_mark[masked_trial_no]
    # init index
    index_seq_trial = np.arange(len(seq_trial))
    # Sort by seq w/o nan
    sorted_index_seq_trial = np.array([x for y, x in sorted(zip(seq_trial, index_seq_trial),key=lambda pair: pair[0])])
    sorted_stimuli_mark = np.array([x for y, x in sorted(zip(seq_trial, stimuli_mark), key=lambda pair: pair[0])])

    return sorted_index_seq_trial, sorted_stimuli_mark, stimuli_mark

# Loop to find which trial include nan and return the trial no. with nan
def find_nan_trial(rmat_act):
    counter = []
    trial_no = []
    for i in range(rmat_act.shape[2]):
        if np.isnan(np.min(rmat_act[:,:,i])):
            counter.append((i,np.count_nonzero(np.isnan(rmat_act[:,:,i]))))
            trial_no.append(i)
    return counter, trial_no # return the trial no. with nan


import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

def anal_sol(H_plot, radius=10, velocity=20e3, density=3000, strength=10e5, angle=45,
               init_altitude=100e3, radians=False):
    '''
    Solves analytical solution for meteroid impact

    Parameters
    ----------

    radius : float
        The radius of the asteroid in meters

    velocity : float
        The entery speed of the asteroid in meters/second

    density : float
        The density of the asteroid in kg/m^3

    strength : float
        The strength of the asteroid (i.e., the ram pressure above which
        fragmentation and spreading occurs) in N/m^2 (Pa)

    angle : float
            The initial trajectory angle of the asteroid to the horizontal
            By default, input is in degrees. If 'radians' is set to True, the
            input should be in radians

    init_altitude : float, optional
        Initial altitude in m

    radians : logical, optional
        Whether angles should be given in degrees or radians. Default=False
        Angles returned in the DataFrame will have the same units as the
        input

    Returns
    -------
    Result : DataFrame
            pandas dataFrame with collumns:
            altitude, velocity, dedz

    '''
    # define constants
    Cd = 1 # drag coefficient
    H = 8000 # atomspheric consatnt
    rho = 1.2 # air density at the ground

    # define initial conditions

    m = 4/3 * np.pi * radius**3 * density # mass, asteroid to be assumed as spheric shape
    A = np.pi * radius**2 # cross-sectional area

    if radians is False: # converts degrees to radians
        angle = angle * (np.pi)/180

    # constant in analytical solution
    c = velocity/(np.exp((-Cd * A * rho * H / (2 * m * np.sin(angle))) * np.exp(-init_altitude/H)))

    def v_h(h):
        return c * np.exp((-Cd * A * rho * H / (2 * m * np.sin(angle))) * np.exp(-h/H))

    C2 = -Cd * A * rho * H / (2 * m * np.sin(angle))

    def dEdz(z):
        return c * np.exp(C2 * np.exp(-z/H)) * C2 * np.exp(-z/H) * (-1/H) * m * v_h(z)

    #H_plot = np.linspace(100000, 0, 200)
    v_plot = v_h(H_plot)

    dedz = np.zeros((len(v_plot),)) # create array to store dedz results
    dedz[0] = 0 # initial dedz
    for i in range(1,len(v_plot)): # loop through all rows of result
            energy = ((1/2 * m * v_plot[i]**2) - (1/2 * m * v_plot[i-1]**2))/4.184e12
            alt = (H_plot[i] - H_plot[i-1])/1e3
            dedz[i] = energy / alt
    #dEdz_plot = dedz(H_plot)

    result = pd.DataFrame({'altitude':H_plot, 'velocity':v_plot, 'dedz':dedz})
    #result = result.sort_values(by='altitude', ascending=False)

    return result


import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn')

def u(tau, V, t):
	"""
	membrane potential as a function of time
	"""

	return V*(1-np.exp(-t/tau))

def T(tau, theta, V):
	"""
	the time between consecutive spikes
	"""

	return tau*np.log(V/(V-theta))

def adjusted_u(tau, theta, V, t):
	"""
	membrane potential adjusted for threshold
	"""

	if V > theta:
		period = T(tau, theta, V)
		adjusted_t = t - (t/period).astype(int)*period
		return u(tau, V, adjusted_t)
	else:
		return u(tau, V, t)

Theta = [15, 20, 25]		# mV
Tau = [8, 10]				# ms
V = [30, 35]				# mV

Theta, Tau, V = np.meshgrid(Theta, Tau, V)
Theta = Theta.flatten()
Tau = Tau.flatten()
V = V.flatten()

plt.figure(figsize = (24,15))
plt.suptitle('(b) Membrane potential as a function of time')

for i in range(12):

	theta, tau, v = Theta[i], Tau[i], V[i]

	Time = np.arange(0, 3.5*T(tau, theta, v), .05)

	# plot setup
	plt.subplot(3, 4, i+1)
	plt.title(f'$\\theta$ = {theta} mV, $\\tau_m$ = {tau} ms, $V_i$ = {v} mV')
	plt.xlabel('Time $t$ (ms)')
	plt.ylabel('Membrane potential $u$ (mV)')

	# plot
	plt.plot(Time, adjusted_u(tau, theta, v, Time), color = 'g')

plt.savefig('P2b.jpg')


import numpy as np

def get_orbit(initial_pt,a=0.1,alpha=3.1,iters=100):
    '''
    Gets the orbit of a given point fixing alpha and a of the function
    f(a,alpha,x) = x - a*sin(2pi*x)+alpha
    Inputs:
    initial_pt: A float of the initial point
    a: A float fixing the value of a
    alpha: A float fixing the value of alpha
    iters: An integer counting the number of times we want to iterate this map

    Returns:
    orbit_list: A list of floats each float coressponding to an iterate of the map
    '''
    orbit_lst = [initial_pt]
    new_pt = initial_pt
    for i in range(iters):
        new_pt = new_pt-a*np.sin(2*np.pi*new_pt)+alpha
        new_pt = new_pt%1
        orbit_lst.append(new_pt)
    return np.array(orbit_lst)

def fixed_noise_otbit(initial_pt,a=0.1,alpha=None):
    '''
    Gets the orbit of two points under some fixed vector of noise realisations alpha
    with the same function as the get_orbit function except alpha changes at each iterate
    Inputs:
    initial_pt: : A float of the initial point
    a: A float fixing the value of a
    alpha: A nx1 numpy array of noise realisations
    Returns:
    orbit_list: A list of floats each float coressponding to an iterate of the map
    '''
    orbit_lst = [initial_pt]
    new_pt = initial_pt
    iters = len(alpha)
    for i in range(iters):
        new_pt = new_pt-a*np.sin(2*np.pi*new_pt)+alpha[i]
        new_pt = new_pt%1
        orbit_lst.append(new_pt)
    return np.array(orbit_lst)


import numpy as np
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt

class LinearRegression:
    W = None
    Ws = []
    training_errors = []

    def init_weights(self, n_features):
        limit = 1 / np.sqrt(n_features)
        self.W = np.random.uniform(-limit, limit, (n_features, ))

    def fit(self, X, y, n_iterations=100, learning_rate=0.01):
        X = np.insert(X, 0, 1, axis=1)
        self.training_errors = []

        self.init_weights(X.shape[1])

        # do gradient descent for n_iterations
        for i in range(n_iterations):
            y_pred = X.dot(self.W)

            # calculate l2 loss
            mse = np.mean(0.5 * (y - y_pred)**2)
            self.training_errors.append(mse)
            self.Ws.append(self.W.tolist())

            # gradient of l2 loss wrt w
            grad_W = -(y - y_pred).dot(X)

            # update weights
            self.W -= learning_rate * grad_W

    def predict(self, X):
        X = np.insert(X, 0, 1, axis=1)
        y_pred = X.dot(self.W)
        return y_pred

def loss_fn(W, X, y):
    return np.mean(0.5 * (y - X.dot(W))**2)

bias = 10
X, y, coef = make_regression(n_samples=100, n_features=1, noise=30, bias=bias, coef=True)

lc = LinearRegression()
lc.fit(X, y)

print(f'true linear model: {bias} + {coef}*x')
print(f'learned linear model: {lc.W[0]} + {lc.W[1]}*x')

# plot loss surface
# https://jakevdp.github.io/PythonDataScienceHandbook/04.12-three-dimensional-plotting.html
sz = max(np.abs(bias), np.abs(coef))
w1 = np.linspace(bias-2*sz, bias+2*sz, 30)
w2 = np.linspace(coef-2*sz, coef+2*sz, 30)
W1, W2 = np.meshgrid(w1, w2)
WW = np.meshgrid(w1, w2)
XX = np.insert(X, 0, 1, axis=1)

z = np.mean((y[:,np.newaxis] - XX.dot(np.stack(WW).reshape(2,30*30)))**2, axis=0).reshape(30,30)

fig = plt.figure(figsize=(8,8))
ax = plt.axes(projection="3d")
ax.plot_surface(W1, W2, z, rstride=1, cstride=1, cmap='terrain', edgecolor=None, alpha=0.7)
ax.plot(np.array(lc.Ws)[:,0], np.array(lc.Ws)[:,1], np.zeros_like(np.array(lc.Ws)).shape[0])
plt.show()

# plot loss surface and proxy loss surface
XX2 = XX[:2]
y2 = y[:2]
z2 = np.mean((y2[:,np.newaxis] - XX2.dot(np.stack(WW).reshape(2,30*30)))**2, axis=0).reshape(30,30)

fig = plt.figure(figsize=(8,8))
ax = plt.axes(projection="3d")
ax.plot_surface(W1, W2, z, rstride=1, cstride=1, cmap='terrain', edgecolor=None, alpha=0.7)
ax.plot_surface(W1, W2, z2, rstride=1, cstride=1, cmap='viridis', edgecolor=None, alpha=0.7)
plt.show()

print('min of true function',np.unravel_index(z.argmin(), z.shape))
print('min of proxy function', np.unravel_index(z2.argmin(), z2.shape))


import numpy as np
import networkx as nx

def whits(G, normalized=True, weight=None):
    """Returns HITS hubs and authorities values for nodes.

    The HITS algorithm computes two numbers for a node.
    Authorities estimates the node value based on the incoming links.
    Hubs estimates the node value based on outgoing links.

    Parameters
    ----------
    G : graph
      A NetworkX graph

    normalized : bool (default=True)
       Normalize results by the sum of all of the values.

    Returns
    -------
    (hubs,authorities) : two-tuple of dictionaries
       Two dictionaries keyed by node containing the hub and authority
       values.

    Examples
    --------
    >>> G = nx.path_graph(4)

    The `hubs` and `authorities` are given by the eigenvectors corresponding to the
    maximum eigenvalues of the hubs_matrix and the authority_matrix, respectively.

    The ``hubs`` and ``authority`` matrices are computed from the adjancency
    matrix:

    >>> adj_ary = nx.to_numpy_array(G)
    >>> hubs_matrix = adj_ary @ adj_ary.T
    >>> authority_matrix = adj_ary.T @ adj_ary

    `whits` maps the eigenvector corresponding to the maximum eigenvalue
    of the respective matrices to the nodes in `G`:

    >>> hubs, authority = whits(G)

    Notes
    -----
    The eigenvector calculation uses NumPy's interface to LAPACK.

    The HITS algorithm was designed for directed graphs but this
    algorithm does not check if the input graph is directed and will
    execute on undirected graphs.

    References
    ----------
    .. [1] A. Langville and C. Meyer,
       "A survey of eigenvector methods of web information retrieval."
       http://citeseer.ist.psu.edu/713792.html
    .. [2] Jon Kleinberg,
       Authoritative sources in a hyperlinked environment
       Journal of the ACM 46 (5): 604-32, 1999.
       doi:10.1145/324133.324140.
       http://www.cs.cornell.edu/home/kleinber/auth.pdf.
    """

    if len(G) == 0:
        return {}, {}
    adj_ary = nx.to_numpy_array(G, weight=weight)
    # Hub matrix
    H = adj_ary @ adj_ary.T
    e, ev = np.linalg.eig(H)
    # eigenvector corresponding to the maximum eigenvalue
    h = ev[:, np.argmax(e)]
    # Authority matrix
    A = adj_ary.T @ adj_ary
    e, ev = np.linalg.eig(A)
    # eigenvector corresponding to the maximum eigenvalue
    a = ev[:, np.argmax(e)]
    if normalized:
        h = h / h.sum()
        a = a / a.sum()
    else:
        h = h / h.max()
        a = a / a.max()
    hubs = dict(zip(G, map(float, h)))
    authorities = dict(zip(G, map(float, a)))
    return hubs, authorities


"""
Ankit Khandelwal
15863
Exercise 8
"""

import matplotlib.pyplot as plt
import numpy as np

x_values = np.linspace(0, 1, 101)
y = []

for i in x_values:
    y.append(924 * i ** 6 - 2772 * i ** 5 + 3150 * i ** 4 - 1680 * i ** 3 + 420 * i ** 2 - 42 * i + 1)
plt.plot(x_values, y)
plt.grid()

A = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]

def der(x):
    h = 0.001
    return (fx(x + h) - fx(x)) / h

def fx(x):
    return 924 * x ** 6 - 2772 * x ** 5 + 3150 * x ** 4 - 1680 * x ** 3 + 420 * x ** 2 - 42 * x + 1

X = []

for guess in A:
    while abs(fx(guess)) > 10 ** -11:
        guess = guess - fx(guess) / der(guess)
    X.append(guess)
print('The solutions are: ', X)

for i in X:
    plt.scatter(i, 0)
plt.xlabel('X')
plt.ylabel('f(x)')
plt.show()

Ankit Khandelwal_HW3/Exercise 8/exercise 8.py


# -*- coding: utf-8 -*-
"""
Ejercicios 3 y 4: Cálculo del polinomio de aproximación (caso continuo).
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
np.set_printoptions(precision = 2)   # solo dos decimales
np.set_printoptions(suppress = True) # no usar notación exponencial

#-----------------------------------------------------------------------------
# Ejercicio 3
#-----------------------------------------------------------------------------
f = lambda x : np.cos(x)

# Construimos la matriz A del sistema y el vector b de términos independientes
g = 2                               # grado del polinomio
A = np.zeros((g + 1,g + 1)) # matriz de coeficientes
b = np.zeros(g + 1)             # término independiente
for i in range(g + 1):          # recorremos las filas de A
    for j in range(g + 1):      # recorremos las columnas de A
        integrando_A = lambda x : x**(i+j) # tenemos que integrar x**(i+j)
        # integrate.quad devuelve la integral y una cota del error (dos coordenadas)
        A[i,j] = integrate.quad(integrando_A,-1,1)[0] # tomamos sólo el valor de la integral (coordenada 0)
    # Para el término independiente b
    integrando_b = lambda x : (x**i)*f(x) # tenemos que integrar x*i * f(x)
    b[i] = integrate.quad(integrando_b,-1,1)[0]  # tomamos sólo el valor de la integral (coordenada 0)

print('Matriz del sistema =\n', A)    # mostramos la matriz A
print('Término independiente =', b)   # mostramos el vector b

# Resolvemos el sistema
p = np.linalg.solve(A, b)
# Reordenamos los coeficientes del polinomio p
# para que parezcan de mayor a menor potencia de x
p = p[::-1]
print('Coeficientes del polinomio p =', p)

# Para hacer la representación gráfica del polinomio
plt.close('all')

xx = np.linspace(-1,1)   # para hacer la gráfica del polinomio p en [-1,1]
yy = np.polyval(p,xx)   # evaluamos p en xx

plt.figure()
plt.plot(xx,f(xx),'red',label = 'exacta')  # dibujamos la función cos x (en rojo)
plt.plot(xx,yy, label = 'aproximada')  # dibujamos el polinomio
plt.grid()
plt.legend()
plt.show()

# Calculamos el error relativo
error_rel = np.linalg.norm(f(xx) - yy)/np.linalg.norm(f(xx))
print('Error relativo = ', error_rel)

#-------------------------------------------------------------------------------------
# Ejercicio 4 (se resuelve como en el caso anterior pero con los datos del ejercicio 4)
#-------------------------------------------------------------------------------------

f = lambda x : np.cos(np.arctan(x)) - np.exp(x**2)*np.log(x+2)

# Construimos la matriz A del sistema y el vector b de términos independientes
g = 4             # grado del polinomio
A = np.zeros((g + 1,g + 1))             # matriz de coeficientes
b = np.zeros(g + 1)            # término independiente

for i in range(g + 1):          # recorremos las filas de A
    for j in range(g + 1):      # recorremos las columnas de A
        integrando_A = lambda x : x**(i+j) # tenemos que integrar x**(i+j)
        # integrate.quad devuelve la integral y una cota del error (dos coordenadas)
        A[i,j] = integrate.quad(integrando_A,-1,1)[0] # tomamos sólo el valor de la integral (coordenada 0)
    # Para el término independiente b
    integrando_b = lambda x : (x**i)*f(x) # tenemos que integrar x*i * f(x)
    b[i] = integrate.quad(integrando_b,-1,1)[0]  # tomamos sólo el valor de la integral (coordenada 0)

print('Matriz del sistema =\n', A)    # mostramos la matriz A
print('Término independiente =', b)   # mostramos el vector b

# Resolvemos el sistema
p = np.linalg.solve(A, b)
# Reordenamos los coeficientes del polinomio p
# para que parezcan de mayor a menor potencia de x
p = p[::-1]
print('Coeficientes del polinomio p =', p)

# Para hacer la representación gráfica del polinomio
plt.close('all')

xx = np.linspace(-1,1)   # para hacer la gráfica del polinomio p en [-1,1]
yy = np.polyval(p,xx)   # evaluamos p en xx

plt.figure()
plt.plot(xx,f(xx),'red',label = 'exacta')  # dibujamos la función cos x (en rojo)
plt.plot(xx,yy, label = 'aproximada')  # dibujamos el polinomio
plt.grid()
plt.legend()
plt.show()

# Calculamos el error relativo
error_rel = np.linalg.norm(f(xx) - yy)/np.linalg.norm(f(xx))
print('Error relativo = ', error_rel)


import numpy as np

class BaseRegression:

    def __init__(self, dataset):
        self.dataset = dataset

        # Use last column as a output. Others are input.
        self.inputs = self.dataset[..., :-1]
        self.outputs = self.dataset[..., -1:]

        # Store len of training sets and count of features.
        self.len_dataset, self.features_count = self.inputs.shape

        # Create randomized weight vector.
        self.weights = np.random.uniform(0, 1, self.features_count + 1)

        # Epoch of training.
        self.epoch = 1

        # Some prepared matrix/vectors for future usage:
        # - Matrix of input data with 1 as a first value: Use in prediction of training inputs and gradient.
        self.input_vectors = np.concatenate([np.ones((self.len_dataset, 1)), self.inputs], axis=1)
        # - Tranposed input vectors: used in gradient and normal equation
        self.trans_input = np.transpose(self.input_vectors)
        # - Flatten output: use in error.
        self.output_vector = self.outputs.flatten()
        # - Clone output vector to matrix with len(weights) rows
        self.cloned_output = np.tile(self.output_vector, (len(self.weights), 1))

    def hypothesis(self, vector):
        raise NotImplemented

    def calculate(self, vector):
        # proxy method for hypothesis.
        return self.hypothesis(vector)

    @property
    def error(self):
        raise NotImplemented

    def normal_equation(self):
        raise NotImplemented

    @property
    def accuracy(self):
        # Calculate accuracy based on error function.
        return round(100.00 - self.error * 100.00, 4)

    @property
    def predicted(self):
        # Calculate training set with weight vector.
        return np.array([self.hypothesis(x) for x in self.inputs])

    @property
    def cloned_predicted(self):
        # Clone predicted vector to matrix with len(weights) rows
        return np.tile(self.predicted, (len(self.weights), 1))

    @property
    def regularization(self, delta=0.1):
        return (delta / self.len_dataset) * np.power(self.weights, 2).sum()

    def gradient(self, speed):
        # Gradient Descent iteration function.
        permit = speed / self.len_dataset
        self.weights -= ((self.cloned_predicted - self.cloned_output) * self.trans_input).sum(axis=1) * permit
        self.epoch += 1

    def train_gradient(self, timeout=3000):
        # Gradient Descent method with automation decreasing speed rate.
        speed = 0.1
        iteration = 0
        latest_error = 1
        while iteration <= timeout:
            error = self.error
            if -0.0001 < error < 0.0001:
                break
            if error > latest_error:
                speed = speed * 0.5
            self.gradient(speed)

            latest_error = error
            iteration += 1

    def show_info(self, show_predicted_vs_output=False):
        predicted_vs_putput = ""
        if show_predicted_vs_output:
            predicted_vs_putput = [f"{p} - {o}\n" for p, o in zip(self.predicted, self.output_vector)]

        print(f"========== {self.__class__.__name__} ========= \n"
              f"Count of Features: {self.features_count} | Len of Dataset: {self.len_dataset} | Epoch: {self.epoch} \n"
              f"Weights: {self.weights}\n"
              f"Accuracy: {self.accuracy}% | Error: {self.error}\n"
              f"{''.join(predicted_vs_putput)}"
              f"=====================================\n"
              )

class LinearRegression(BaseRegression):

    @property
    def error(self):
        # Calculate squared error of training set. Scalar value.
        return (1 / (self.len_dataset * 2)) * np.power(self.predicted - self.output_vector, 2).sum()

    def hypothesis(self, vector):
        # Calculate value of input vector based on current weight
        return np.dot(self.weights, np.concatenate([np.ones(1), vector])).sum()

    def normal_equation(self):
        # Normal Equation method
        theta = np.linalg.inv(self.trans_input.dot(self.input_vectors)).dot(self.trans_input).dot(self.outputs)
        self.weights = theta.flatten()

class LogisticRegression(BaseRegression):

    def hypothesis(self, vector):
        # Sigmoid activation of calculated values of input vector based on current weight.
        doted = np.dot(self.weights, np.concatenate([np.ones(1), vector]))
        return 1.0 / (1 + np.exp(-doted))

    @property
    def error(self):
        # Calculate error of training set. Scalar value.
        cost = -self.output_vector * np.log(self.predicted) - (1 - self.output_vector) * np.log(1 - self.predicted)
        return cost.sum() / self.len_dataset

class SVM(LogisticRegression):

    def __init__(self, dataset):
        super().__init__(dataset)
        self.weights = np.zeros(self.features_count)
        self.weights = np.random.uniform(0, 1, self.features_count)

    def hypothesis(self, vector):
        return np.dot(self.weights, vector)

    def train_gradient(self, timeout=30000):
        # Gradient Descent method with automation decreasing speed rate.
        speed = 1
        for i in range(1, timeout):
            for x, y in zip(self.inputs, self.outputs):
                if y * self.hypothesis(x) < 1:
                    self.weights = self.weights + speed * ((y * x) - (2 * (1 / timeout) * self.weights))
                else:
                    self.weights = self.weights + speed * (-2 * (1 / timeout) * self.weights)


import numpy as np
from math import sqrt
import matplotlib.pyplot as plt
from matplotlib import style
import warnings
from collections import Counter
style.use('ggplot')

#euclidean_distance = sqrt( ((plot1[0] - plot2[0])**2) + ((plot1[1] - plot2[1])**2) )

dataset = {'k': [[1,2], [2,3], [3,1]], 'r' : [[6,5], [7,7], [8,6]]}

new_features1 = [5,7]

new_features2 = [1,1]

def k_nearest_neighbors(data, predict, k=3):
	if(len(data) >= k):
		warnings.warn('K is lesser than total data group')

	distances = []

	for group in data:
		for features in data[group]:
			#euclidean_distance = np.sqrt( ((features[0] - predict[0])**2) + ((features[1] - predict[1])**2) )
			euclidean_distance = np.linalg.norm(np.array(features) - np.array(predict) )
			distances.append([euclidean_distance, group])

	votes = [i[1] for i in sorted(distances)[:k]]
	#print(Counter(votes).most_common(1))
	vote_results = Counter(votes).most_common(1)[0][0]

	return vote_results

result = k_nearest_neighbors(dataset, new_features1, k=3)

print(result)

[[plt.scatter(ii[0], ii[1], s=100, color=i) for ii in dataset[i]] for i in dataset]
plt.scatter(new_features1[0], new_features1[1], s=200, color = result)
plt.scatter(new_features2[0], new_features2[1], s=200, color = result)
result = k_nearest_neighbors(dataset, new_features2, k=3)

print(result)

plt.scatter(new_features2[0], new_features2[1], s=200, color = result)
plt.show()


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def read_data(file_path):
    data = pd.read_csv(file_path, header=None, names=["feature"])
    data = np.array(data)
    return data

def scatter(x, y):
    pos = np.where(y == 1)[0].tolist()
    neg = np.where(y == 0)[0].tolist()
    plt.plot(x[pos, 0], x[pos, 1], '+')
    plt.plot(x[neg, 0], x[neg, 1], 'o')
    plt.show()

def sigmoid(x, w, b):
    t = x.dot(w) + b
    t = 1 + np.exp(-t)
    t = 1 / t
    return t

def compute_acc(x, y, w, b):
    pre = sigmoid(x, w, b)
    pre = np.where(pre>0.5, 1, 0)
    res = pre == y
    acc = np.sum(res == True)
    return acc/len(x)

def GD(x, y, w, b, l_rate, epoches):
    loss = []
    for epoch in range(epoches):
        dl_db = (y - sigmoid(x, w, b))
        dl_dw = dl_db * x
        dl_db = np.mean(dl_db)
        dl_dw = np.mean(dl_dw, axis=0)
        dl_dw = np.expand_dims(dl_dw, axis=1)
        w = w + l_rate * dl_dw
        b = b + l_rate * dl_db
        loss.append(np.sum((y - sigmoid(x, w, b))**2))
        if epoch % 10 == 0:
            print("epoch {}: w={} , b={}, loss={}".format(epoch, w, b, loss[epoch]))
    return w, b, loss

def main():
    # 数据准备
    x = read_data("./ex4x.dat")
    y = read_data("./ex4y.dat")
    t = []
    for i in range(len(x)):
        temp = x[i, 0].split()
        temp[0] = float(temp[0])
        temp[1] = float(temp[1])
        t.append(temp)
    x = np.array(t)
    y = y.astype("int16")
    # -----------------------
    w, b, loss = GD(x, y, np.random.rand(2, 1), np.random.rand(), 0.0001, 1000)

    pos = np.where(y == 1)[0].tolist()
    neg = np.where(y == 0)[0].tolist()
    plt.plot(x[pos, 0], x[pos, 1], '+')
    plt.plot(x[neg, 0], x[neg, 1], 'o')
    plt.plot(x, (-b-w[0, 0]*x[:, 0])/w[1, 0], 'r')
    plt.show()
    print(compute_acc(x, y, w, b))

if __name__ == '__main__':
    main()


import numpy as np

def cov2d(std: np.ndarray, corr: np.ndarray):
    num_classes, dim_features = std.shape
    assert dim_features == 2
    assert corr.shape == (num_classes,)

    cov = np.zeros((num_classes, dim_features, dim_features))
    for i in range(num_classes):
        cov[i] = np.diag(std[i]) @ np.array([[1, corr[i]],
                                             [corr[i], 1]]) @ np.diag(std[i])
    return cov

def gaussian_mixture(mean: np.ndarray, cov: np.ndarray, n: int, p: np.ndarray = None):
    num_classes, dim_features = mean.shape
    if p is None:
        p = np.ones(num_classes) / num_classes
    assert cov.shape == (num_classes, dim_features, dim_features)
    assert p.shape == (num_classes,)

    # components [num_points]
    z = np.random.choice(num_classes, size=n, p=p)
    # features [num_points, dim_features]
    x = mean[z] + (np.linalg.cholesky(cov)[z] @ np.random.randn(n, dim_features, 1)).squeeze()
    return x, z


import numpy as np
import scipy.stats as st
import scipy
import scipy.optimize
import warnings
import bebi103
import pandas as pd

def log_like_gamma(params, t):

    """Log likelihood for a gamma distribution with
    input being alpha and beta parameters as well as counts.

    Parameters
    ----------
    params : array of the parameters alpha and beta.
    t : array array of counts.

    Returns
    -------
    output : float
        Log-likelihood.
    """

    alpha, beta = params

    if alpha <= 0 or beta <= 0:
        return -np.inf

    return st.gamma.logpdf(t, alpha, loc=0, scale=1/beta).sum()

def gamma_mle(t):

    """
    Perform maximum likelihood estimates for parameters for
    gamma measurements, parametrized by alpha and beta.

    Parameters
    ----------
    t : array array of counts.

    Returns
    -------
    output : float
         maximum likelihood estimates for alpha and beta.
    """

    # Initial guess
    t_bar = np.mean(t)
    beta_guess = t_bar / np.var(t)
    alpha_guess = t_bar * beta_guess

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")

        res = scipy.optimize.minimize(
            lambda params, t: -log_like_gamma(params, t),
            (alpha_guess, beta_guess),
            args=(t,),
            method="powell",
        )

    if res.success:
        return res.x
    else:
        raise RuntimeError('Convergence failed with message', res.message)

def gen_gamma(params, size, rg):
	"""
	Generate samples from the gamma distribution.

		Parameters
		----------
		params: parameterized alpha and beta
		size:s number of samples that are drawn.
		rg: numpy.random.Generator instance

		Returns
		-------
		output : numpy array
			Randon samples from the parameterized gamma distribution.
	"""
	alpha, beta = params
	return rg.gamma(alpha, 1 / beta, size=size)

def gamma_bs_MLE(conc_data):

	"""
	Generate parametric bootstrap replicates of maximum likelihood estimator
	from gamma distribution using bebi103.draw_bs_reps_mle.

		Parameters
		----------
		conc_data: MLEs data used for generating parametric bootstrap replicates

		Returns
		-------
		output : numpy array
		Bootstrap replicates of MLEs.

	"""

	bs_reps = bebi103.draw_bs_reps_mle(mle_fun=gamma_mle, gen_fun=gen_gamma, data=conc_data, size=5000, n_jobs=3)

	alpha_vals = bs_reps[:,0]
	beta_vals = bs_reps[:,1]
	return [np.mean(alpha_vals), np.mean(beta_vals)], np.percentile(bs_reps, [2.5, 97.5], axis=0)

def get_gamma_conf_intervals(concentrations, dataframe, conc_column, time_column):

	"""
	generate bootstrap replicates for gamma MLE estimation of catastrophe
	data and return a dataframe with all relevant information, along
	with arrays containing confidence intervals and MLE averages.

		Parameters
		----------
		concentrations: list conaining all tubulin concentrations of
		interest
		dataframe: dataframe with catastrophe times under colunns labeled
		with tubulin concentratiosn
		conc_column: the label used for the column with tubulin conc.
		in the dataframe
		time_column: the label used for the column with catastrophe
		times in the dataframe

		Returns
		-------
		output : dataframe, array, array
		dataframe with all relevant info
		array with confidence interavls
		array with MLE estimates

	"""

	#Make lists for both the MLE values and the confidence intervals
	MLE_vals = []
	conf_ints = []

	#Loop through the different tubulin concentrations
	for conc in concentrations:

		#Grab the relevant data for each different concentration
		data = dataframe[dataframe[conc_column] == conc][time_column]

		#Convert this data into an array for ease of analysis
		array = np.array(data)

		#Use our Gamma MLE funtion to pull out both MLE values and confidence intervals for our data
		mle_val, conf_int = gamma_bs_MLE(array)

		#Add the MLE values and confidence intervals to respective lists
		MLE_vals.append(mle_val)
		conf_ints.append(conf_int)

	#Convert our MLE values to an array for ease of use
	MLE_array = np.array(MLE_vals)

	#Make a dataframe containing these values and the relevant concentrations of tubulin that correspond to the measurements
	MLE_df = pd.DataFrame(MLE_array, columns=['alpha MLE', 'beta MLE'])
	MLE_df['Tubulin Concentration (uM)'] = concentrations

	alpha_low, alpha_high, beta_low, beta_high = [], [], [], []

	#Examine each confidence interval separately
	for piece in conf_ints:

		#Grab the alphas
		alphas = piece[:,0]

		#grab the beta values
		betas = piece[:,1]

		#append the high and lower bounds into separate lists
		alpha_low.append(alphas[0])
		alpha_high.append(alphas[1])
		beta_low.append(betas[0])
		beta_high.append(betas[1])

	MLE_df['low alpha'] = alpha_low
	MLE_df['high alpha'] = alpha_high
	MLE_df['low beta'] = beta_low
	MLE_df['high beta'] = beta_high

	return MLE_df, conf_ints, MLE_array


## import packages
import numpy as np
from PIL import Image
from scipy.signal import convolve2d as conv2
import matplotlib.pyplot as plt

import gauss_module

def rgb2gray(rgb):

    r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
    gray = 0.2989 * r + 0.5870 * g + 0.1140 * b

    return gray

## function gauss (Question 1.a)

sigma = 4.0
[Gx, x] = gauss_module.gauss(sigma)

plt.figure(1)
plt.plot(x, Gx, '.-')
plt.show()

## function gaussianfilter (Question 1.b)

img = rgb2gray(np.array(Image.open('graf.png')))
smooth_img = gauss_module.gaussianfilter(img, sigma)

plt.figure(2)
ax1 = plt.subplot(1,2,1)
ax2 = plt.subplot(1,2,2)
plt.sca(ax1)
plt.imshow(img, cmap='gray', vmin=0, vmax=255)
plt.sca(ax2)
plt.imshow(smooth_img, cmap='gray', vmin=0, vmax=255)
plt.show()

## function gaussdx (Question 1.c)

sigma = 4.0
[Gx, x] = gauss_module.gauss(sigma)
[Dx, x] = gauss_module.gaussdx(sigma)

plt.figure(5)
plt.plot(x, Gx, 'b.-')
plt.plot(x, Dx, 'r-')
plt.legend( ('gauss', 'gaussdx'))
plt.show()

## function gaussdx (Question 1.d)

img_imp = np.zeros([27,27])
img_imp[13, 13] = 1.0
plt.figure(6), plt.imshow(img_imp, cmap='gray')

sigma = 7.0
[Gx, x] = gauss_module.gauss(sigma)
[Dx, x] = gauss_module.gaussdx(sigma)

Gx = Gx.reshape(1, Gx.size)
Dx = Dx.reshape(1, Dx.size)

plt.figure(7)
plt.subplot(2,3,1)
plt.imshow(conv2(conv2(img_imp, Gx, 'same'), Gx.T, 'same') , cmap='gray')
plt.subplot(2,3,2)
plt.imshow(conv2(conv2(img_imp, Gx, 'same'), Dx.T, 'same') , cmap='gray')
plt.subplot(2,3,3)
plt.imshow(conv2(conv2(img_imp, Dx.T, 'same'), Gx, 'same') , cmap='gray')
plt.subplot(2,3,4)
plt.imshow(conv2(conv2(img_imp, Dx, 'same'), Dx.T, 'same') , cmap='gray')
plt.subplot(2,3,5)
plt.imshow(conv2(conv2(img_imp, Dx, 'same'), Gx.T, 'same') , cmap='gray')
plt.subplot(2,3,6)
plt.imshow(conv2(conv2(img_imp, Gx.T, 'same'), Dx, 'same') , cmap='gray')
plt.show()

## function gaussderiv (Question 1.e)

img_c = np.array(Image.open('graf.png')).astype('double')
img = rgb2gray(img_c)
[imgDx, imgDy] = gauss_module.gaussderiv(img, 7.0)

plt.figure(8)
ax1 = plt.subplot(1,3,1)
ax2 = plt.subplot(1,3,2)
ax3 = plt.subplot(1,3,3)
plt.sca(ax1)
plt.imshow(imgDx, cmap='gray')
plt.sca(ax2)
plt.imshow(imgDy, cmap='gray')
plt.sca(ax3)
imgmag = np.sqrt(imgDx**2 + imgDy**2)
plt.imshow(imgmag, cmap='gray')
plt.show()


# -*- coding: utf-8 -*-
"""
SEIR_model.py
Created on 2020/4/8

@author: yiqing
"""

import scipy.integrate
import numpy as np

class SEIR:
    def model(data, t, N, beta, gamma, sigma):
        """
        :param data:
        :param t:
        :param N:
        :param beta:
        :param gamma:
        :param sigma:
        :return:
        """

        S, E, I, R = data

        dS_t = - (beta * S * I)/N
        dE_t = beta * S * I/N - sigma * E
        dI_t = sigma * E - gamma * I
        dR_t = gamma * I

        return [dS_t, dE_t, dI_t, dR_t]

    def solve(init_value, t, N, beta, gamma, sigma):
        """
        :param model: SEIR model
        :param init_value: S,E,I,R = population - E0, E0, 0, 0
        :param t:
        :param beta: The parameter controlling how often a susceptible-infected contact results in a new infection.
        :param gamma: The rate an infected recovers and moves into the resistant phase.
        :param sigma: The rate at which an exposed person becomes infective.
        :return:
        """

        result = scipy.integrate.odeint(SEIR.model, init_value, t, args=(N, beta, gamma, sigma))
        result = np.array(result)
        return result


import numpy as np
from scipy import linalg
from scipy.stats import norm, t, levy_stable, logser
from scipy.special import gamma, comb

def SimuGaussian(n, m, corrMatrix):
    """
    # Gaussian Copula simulations with a given correlation matrix
    Requires:
        n = number of variables
        m = sample size
        corrMatrix = correlation matrix
    """

    # Generate n independent standard Gaussian random variables V = (v1 ,..., vn):
    v = [np.random.normal(0,1,m) for i in range(0,n)]

    # Compute the lower triangular Cholesky factorization of the correlation matrix:
    L = linalg.cholesky(corrMatrix, lower=True)
    y = np.dot(L, v)
    u = norm.cdf(y, 0, 1 )

    return u

def SimuStudent(n, m, corrMatrix, k):
    """
    # Student Copula with k degrees of freedom and a given correlation matrix
    Requires:
        n = number of variables
        m = sample size
        corrMatrix = correlation matrix
        k = degree of freedom
    """

    # Generate n independent standard Gaussian random variables  V = (v1 ,..., vn):
    v = [np.random.normal(0,1,m) for i in range(0,n)]

    # Compute the lower triangular Cholesky factorization of rho:
    L = linalg.cholesky(corrMatrix, lower=True)
    z = np.dot(L, v)

    # generate a random variable r, following a chi2-distribution with nu degrees of freedom
    r = np.random.chisquare(df=k,size=m)

    y = np.sqrt(k/ r)*z
    u = t.cdf(y, df=k, loc=0, scale=1)

    return u

def SimuClayton(n, m, theta):
    """
    # Clayton copula
    Requires:
        n = number of variables
        m = sample size
        theta = Clayton copula parameter
    """

    # Generate n independent standard uniform random variables  V = (v1 ,..., vn):
    v = [np.random.uniform(0,1,m) for i in range(0,n)]

    # generate a random variable x following the gamma distribution gamma(theta**(-1), 1)
    X = np.array([np.random.gamma(theta**(-1), scale=1.0) for i in range(0,m)])

    phi_t = lambda t:  (1+t)**(-1/theta)

    u = [phi_t(-np.log(v[i])/X) for i in range(0,n)]

    return u

def SimuGumbel(n, m, theta):
    """
    # Gumbel copula
    Requires:
        n = number of variables to generate
        m = sample size
        theta = Gumbel copula parameter
    """

    v = [np.random.uniform(0,1,m) for i in range(0,n)]
    X = levy_stable.rvs(alpha=1/theta, beta=1,scale=(np.cos(np.pi/(2*theta)))**theta,loc=0, size=m)

    phi_t = lambda t:  np.exp(-t**(1/theta))

    u = [phi_t(-np.log(v[i])/X) for i in range(0,n)]
    return u

def SimuFrank(n, m, theta):
    """
    # Frank copula
    Requires:
        n = number of variables to generate
        m = sample size
        theta = Frank copula parameter
    """

    v = [np.random.uniform(0,1,m) for i in range(0,n)]
    p = 1-np.exp(-theta)
    X = logser.rvs(p, loc=0, size=m, random_state=None)

    phi_t = lambda t:  -np.log(1-np.exp(-t)*(1-np.exp(-theta)))/theta
    u = [phi_t(-np.log(v[i])/X) for i in range(0,n)]
    return u

def SimuSibuya(alpha, m):
    """
        Sibuya distribution Sibuya(α)
        Used for sampling F=Sibuya(α) for Joe copula
        The algorithm in given in Proposition 3.2 in Hofert (2011) "Efficiently sampling nested Archimedean copulas"
    """

    G_1 = lambda y: ((1-y)*gamma(1-alpha) )**(-1/alpha)
    F = lambda n: 1- ((-1)**n)*comb(n,alpha-1)

    X = np.random.uniform(0,1,m)

    for i in range(0,len(X)):
        if X[i] <= alpha:
            X[i] = 1

        if F(np.floor(G_1(X[i]))) < X[i]:
            X[i] = np.ceil(G_1(X[i]))
        else:
            X[i] = np.floor(G_1(X[i]))
    return X

def SimuJoe(n, m, theta):
    """
    # Joe copula
    Requires:
        n = number of variables to generate
        m = sample size
        theta = Joe copula parameter
    """

    alpha = 1/theta
    X = SimuSibuya(alpha, m)

    v = [np.random.uniform(0,1,m) for i in range(0,n)]

    phi_t = lambda t: (1-(1-np.exp(-t))**(1/theta))

    u = [phi_t(-np.log(v[i])/X) for i in range(0,n)]

    return u

def SimuAMH(n, m, theta):
    """
    # Ali-Mikhail-Haq copula
    Requires:
        n = number of variables
        m = sample size
        theta = Ali-Mikhail-Haq copula parameter
    """

    v = [np.random.uniform(0,1,m) for i in range(0,n)]

    X = np.random.geometric(p=1-theta, size=m)

    phi_t = lambda t:  (1-theta)/(np.exp(t)-theta)

    u = [phi_t(-np.log(v[i])/X) for i in range(0,n)]
    return u

from scipy.stats import bernoulli

combination = [
    {"type": "clayton", "weight": 0.33, "theta": 3},
    {"type": "student", "weight": 0.33, "corrMatrix": 1, "k": 2},
    {"type": "gumbel", "weight": 0.33, "theta": 3}
]

def SimuMx(n, m, combination):
    v = [np.random.uniform(0,1,m) for i in range(0,n)]

    weights = [comb["weight"] for comb in combination]

    #Generate m random sample of category labels
    y = np.array([np.where(ls==1)[0][0] for ls in np.random.multinomial(n=1, pvals=weights, size=m)])

    for i in range(0,len(combination)):

        combinationsize = len(v[0][y==i])
        if combination[i]["type"] == "clayton":
            theta = combination[i]["theta"]
            X = np.array([np.random.gamma(theta**(-1), scale=1.0) for i in range(0,combinationsize)])
            phi_t = lambda t:  (1+t)**(-1/theta)
            for j in range(0,len(v)):
                v[j][y==i] = phi_t(-np.log(v[j][y==i])/X)

        elif combination[i]["type"] == "gumbel":
            theta = combination[i]["theta"]
            X = levy_stable.rvs(alpha=1/theta, beta=1,scale=(np.cos(np.pi/(2*theta)))**theta,loc=0, size=combinationsize)
            phi_t = lambda t:  np.exp(-t**(1/theta))
            for j in range(0,len(v)):
                v[j][y==i] = phi_t(-np.log(v[j][y==i])/X)

        elif combination[i]["type"] == "gaussian":
            corrMatrix = combination[i]["corrMatrix"]

            v2 = np.random.uniform(0,1,combinationsize)

            L = linalg.cholesky(corrMatrix, lower=True)

            for j in range(0,len(v)):
                v[j][y==i] = np.sqrt(-2*np.log(v[j][y==i]))*np.cos(2*np.pi*v2)

            y = np.dot(L, [vk[y==i] for vk in v])
            Y = norm.cdf(y, loc=0, scale=1)

            for j in range(0,len(v)):
                v[j][y==i] = np.array(Y[j])

        elif combination[i]["type"] == "student":
            corrMatrix = combination[i]["corrMatrix"]
            k = combination[i]["k"]

            v2 = np.random.uniform(0,1,combinationsize)
            L = linalg.cholesky(corrMatrix, lower=True)
            r = np.random.chisquare(df=k,size=combinationsize)

            for j in range(0,len(v)):
                v[j][y==i] = np.sqrt(-2*np.log(v[j][y==i]))*np.cos(2*np.pi*v2)

            z = np.dot(L, [vk[y==i] for vk in v])
            Y = t.cdf(np.sqrt(k/ r)*z, df=k, loc=0, scale=1)

            for j in range(0,len(v)):
                v[j][y==i] = np.array(Y[j])

    return v


'''
Import libraries and functions
'''
import math
import numpy as np
import statistics
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
import matplotlib.pyplot as plt

sqrt = math.sqrt
mean = statistics.mean

def get_mean_bias_error(x, y):
    '''
    It calculates the mean bias error function (MBE).

    Input:
        * x -> list or array of measured/actual data. For example,
        in terms of et0, it would be FAO56-PM et0.

        * y -> list or array of predicted values. For example, in
        terms of et0, it would be the predicted et0 by a neural
        network model, etc.

    Output:
        * mbe -> a float with the mean bias error value
    '''
    # check the lengths are the same
    assert len(x)==len(y)
    # convert the inputs in numpy array
    x = np.array(x)
    y = np.array(y)
    # mbe calculus
    delta = y - x
    mbe = float(sum(delta)*1.0/len(x))
    return(mbe)

def get_root_mean_square_error(x, y):
    '''
    It calculates de root mean square error (RMSE).

    Input:
        * x -> list or array of measured/actual data. For example,
        in terms of et0, it would be FAO56-PM et0.

        * y -> list or array of predicted values. For example, in
        terms of et0, it would be the predicted et0 by a neural
        network model, etc.

    Output:
        * rmse -> a float with the root mean squared error value
    '''
    # check the lengths are the same
    assert len(x)==len(y)
    # convert the inputs in numpy array
    x = np.array(x)
    y = np.array(y)
    # rmse calculus
    delta = (y - x)**2
    rmse = sqrt(sum(delta)*1.0/len(x))
    return rmse

def get_relative_error(x, y):
    '''
    It calculates de relative error RE.

    Input:
        * x -> list or array of measured/actual data. For example,
        in terms of et0, it would be FAO56-PM et0.

        * y -> list or array of predicted values. For example, in
        terms of et0, it would be the predicted et0 by a neural
        network model, etc.

    Output:
        * re -> a float with the relative error value. The value given
        is a function of per-unit, instead of per-cent.
    '''
    # check the lengths are the same
    assert len(x)==len(y)
    # convert the inputs in numpy array
    x = np.array(x)
    y = np.array(y)
    # re calculus
    mbe = get_mean_bias_error(x, y)
    x_mean = mean(x)
    re = mbe/x_mean
    return re

def get_ratio_error(x, y):
    '''
    It calculates the ratio between both average estimated and measured

    Input:
        * x -> list or array of measured/actual data. For example,
        in terms of et0, it would be FAO56-PM et0.

        * y -> list or array of predicted values. For example, in
        terms of et0, it would be the predicted et0 by a neural
        network model, etc.

    Output:
        * re -> a float with the relative error value. The value given
        is a function of per-unit, instead of per-cent.
    '''
    # check the lengths are the same
    assert len(x)==len(y)
    # convert the inputs in numpy array
    x = np.array(x)
    y = np.array(y)
    # ratio error calculus
    return mean(y)/mean(x)

def get_coefficient_of_determination(x, y):
    '''
    It calculates the coefficient of determination (R**2)

    Input:
        * x -> list or array of measured/actual data. For example,
        in terms of et0, it would be FAO56-PM et0.

        * y -> list or array of predicted values. For example, in
        terms of et0, it would be the predicted et0 by a neural
        network model, etc.
    Output:
        * R2 -> a float with the coefficient of determination in
        per unit, instead of percentage.
    '''
    # check the lengths are the same
    assert len(x)==len(y)
    # convert the inputs in numpy array
    x = np.array(x)
    y = np.array(y)

    # determines the means of the values
    x_mean = mean(x)
    y_mean = mean(y)
    delta_measured = x - x_mean
    delta_measured_2 = (x - x_mean)**2
    delta_prediction = y - y_mean
    delta_prediction_2 = (y - y_mean)**2
    R2 = (sum(delta_measured*delta_prediction)/sqrt(sum(delta_measured_2)*sum(delta_prediction_2)))**2
    return R2

def get_nash_suteliffe_efficiency(x, y):
    '''
    It calculates NSE. It is calculated by sklearn.metrics.r2_score

    Input:
        * x -> list or array of measured/actual data. For example,
        in terms of et0, it would be FAO56-PM et0.

        * y -> list or array of predicted values. For example, in
        terms of et0, it would be the predicted et0 by a neural
        network model, etc.
    Output:
        * nse -> a float with the coefficient of determination in
        per unit, instead of percentage.
    '''
    # check the lengths are the same
    assert len(x)==len(y)
    # convert the inputs in numpy array
    x = np.array(x)
    y = np.array(y)

    return r2_score(x, y)


import numpy as np

def wrap_closed_open_interval(values, lower, upper):
    return (np.asarray(values) - lower) % (upper - lower) + lower

def wrap_open_closed_interval(values, lower, upper):
    return -((lower - np.asarray(values)) % (upper - lower)) - lower

def spherical2interaural(azimuth, elevation, radius, angles_in_degrees=True):
    if angles_in_degrees:
        azimuth = np.deg2rad(azimuth)
        elevation = np.deg2rad(elevation)
    lateral = np.arcsin(np.sin(azimuth) * np.cos(elevation))
    vertical = np.arctan2(np.tan(elevation), np.cos(azimuth))
    if angles_in_degrees:
        return np.rad2deg(lateral), np.rad2deg(vertical), radius
    else:
        return lateral, vertical, radius

def spherical2cartesian(azimuth, elevation, radius, angles_in_degrees=True):
    if angles_in_degrees:
        azimuth = np.deg2rad(azimuth)
        elevation = np.deg2rad(elevation)
    hoz_proj = radius * np.cos(elevation)
    x = hoz_proj * np.cos(azimuth)
    y = hoz_proj * np.sin(azimuth)
    z = radius * np.sin(elevation)
    return x, y, z

def cartesian2spherical(x, y, z, angles_in_degrees=True):
    x = np.asarray(x)
    y = np.asarray(y)
    z = np.asarray(z)
    azimuth = np.arctan2(y, x)
    elevation = np.arctan2(z, np.sqrt(x**2 + y**2))
    radius = np.sqrt(x**2 + y**2 + z**2)
    if angles_in_degrees:
        return np.rad2deg(azimuth), np.rad2deg(elevation), radius
    else:
        return azimuth, elevation, radius

def cartesian2interaural(x, y, z, angles_in_degrees=True):
    x = np.asarray(x)
    y = np.asarray(y)
    z = np.asarray(z)
    lateral = np.arctan2(y, np.sqrt(x**2 + z**2))
    vertical = np.arctan2(z, x)
    radius = np.sqrt(x**2 + y**2 + z**2)
    if angles_in_degrees:
        return np.rad2deg(lateral), np.rad2deg(vertical), radius
    else:
        return lateral, vertical, radius

def interaural2spherical(lateral, vertical, radius, angles_in_degrees=True):
    if angles_in_degrees:
        lateral = np.deg2rad(lateral)
        vertical = np.deg2rad(vertical)
    azimuth = np.arctan2(np.tan(lateral), np.cos(vertical))
    elevation = np.arcsin(np.cos(lateral) * np.sin(vertical))
    if angles_in_degrees:
        return np.rad2deg(azimuth), np.rad2deg(elevation), radius
    else:
        return azimuth, elevation, radius

def interaural2cartesian(lateral, vertical, radius, angles_in_degrees=True):
    if angles_in_degrees:
        lateral = np.deg2rad(lateral)
        vertical = np.deg2rad(vertical)
    med_proj = radius * np.cos(lateral)
    x = med_proj * np.cos(vertical)
    y = radius * np.sin(lateral)
    z = med_proj * np.sin(vertical)
    return x, y, z


# coding=utf-8

# Author: Rodolfo J. O. Soares <rodolfoj.soares@gmail.com>

import numpy as np

def softmax(vector, theta=1.0):
    """Takes an vector w of S N-element and returns a vectors where each column
    of the vector sums to 1, with elements exponentially proportional to the
    respective elements in N.

    Parameters
    ----------
    vector : array of shape = [N,  M]

    theta : float (default = 1.0)
            used as a multiplier  prior to exponentiation

    Returns
    -------
    dist : array of shape = [N, M]
           Which the sum of each row sums to 1 and the elements are exponentially
           proportional to the respective elements in N
    """

    w = np.atleast_2d(vector)
    e = np.exp(np.array(w) / theta)
    dist = e / np.sum(e, axis=1).reshape(-1, 1)
    return dist

def softmin(vector):
    """Takes a vector w of S N-element and return a softmax-based activation function
    that is defined as f(x)=softmax(−x).

    Parameters
    ----------
    vector : array of shape = [N,  M]

    Returns
    -------
    dist : array of shape = [N, M]
           Which the sum of each row sums to 1 and the elements are exponentially
           proportional to the respective elements in N
    """

    return softmax((-1 * vector))


# Plot 3-dim dirichlet on 2d simplex as heatmap and samples

# Author: Thomas Boggs
# https://gist.github.com/tboggs/8778945
# http://blog.bogatron.net/blog/2014/02/02/visualizing-dirichlet-distributions/

import superimport

from functools import reduce

import numpy as np
import matplotlib.pyplot as plt
import pyprobml_utils as pml

import matplotlib.tri as tri

_corners = np.array([[0, 0], [1, 0], [0.5, 0.75**0.5]])
_triangle = tri.Triangulation(_corners[:, 0], _corners[:, 1])
_midpoints = [(_corners[(i + 1) % 3] + _corners[(i + 2) % 3]) / 2.0 \
              for i in range(3)]

def xy2bc(xy, tol=1.e-3):
    '''Converts 2D Cartesian coordinates to barycentric.
    Arguments:
        `xy`: A length-2 sequence containing the x and y value.
    '''
    s = [(_corners[i] - _midpoints[i]).dot(xy - _midpoints[i]) / 0.75 \
         for i in range(3)]
    return np.clip(s, tol, 1.0 - tol)

class Dirichlet(object):
    def __init__(self, alpha):
        '''Creates Dirichlet distribution with parameter `alpha`.'''
        from math import gamma
        from operator import mul
        self._alpha = np.array(alpha)
        self._coef = gamma(np.sum(self._alpha)) / \
                     reduce(mul, [gamma(a) for a in self._alpha])
    def pdf(self, x):
        '''Returns pdf value for `x`.'''
        from operator import mul
        return self._coef * reduce(mul, [xx ** (aa - 1)
                                         for (xx, aa)in zip(x, self._alpha)])
    def sample(self, N):
        '''Generates a random sample of size `N`.'''
        return np.random.dirichlet(self._alpha, N)

def draw_pdf_contours(dist, border=False, nlevels=200, subdiv=8, **kwargs):
    '''Draws pdf contours over an equilateral triangle (2-simplex).
    Arguments:
        `dist`: A distribution instance with a `pdf` method.
        `border` (bool): If True, the simplex border is drawn.
        `nlevels` (int): Number of contours to draw.
        `subdiv` (int): Number of recursive mesh subdivisions to create.
        kwargs: Keyword args passed on to `plt.triplot`.
    '''
    refiner = tri.UniformTriRefiner(_triangle)
    trimesh = refiner.refine_triangulation(subdiv=subdiv)
    pvals = [dist.pdf(xy2bc(xy)) for xy in zip(trimesh.x, trimesh.y)]
    from matplotlib import cm
    plt.tricontourf(trimesh, pvals, nlevels, cmap=cm.jet, **kwargs)
    plt.axis('equal')
    plt.xlim(0, 1)
    plt.ylim(0, 0.75**0.5)
    plt.axis('off')
    if border is True:
        #plt.hold(1)
        plt.triplot(_triangle, linewidth=1)

def plot_points(X, barycentric=True, border=True, **kwargs):
    '''Plots a set of points in the simplex.
    Arguments:
        `X` (ndarray): A 2xN array (if in Cartesian coords) or 3xN array
                       (if in barycentric coords) of points to plot.
        `barycentric` (bool): Indicates if `X` is in barycentric coords.
        `border` (bool): If True, the simplex border is drawn.
        kwargs: Keyword args passed on to `plt.plot`.
    '''
    if barycentric is True:
        X = X.dot(_corners)
    plt.plot(X[:, 0], X[:, 1], 'k.', ms=1, **kwargs)
    plt.axis('equal')
    plt.xlim(0, 1)
    plt.ylim(0, 0.75**0.5)
    plt.axis('off')
    if border is True:
        #plt.hold(1)
        plt.triplot(_triangle, linewidth=1)

f = plt.figure(figsize=(8, 6))
alphas = [[0.1] * 3,
          [1] * 3,
          [5] * 3,
          [50] * 3,
          [2, 5, 15]]
for (i, alpha) in enumerate(alphas):
    plt.figure(figsize=(8,8))
    dist = Dirichlet(alpha)
    draw_pdf_contours(dist)
    title = r'$\alpha$ = (%.3f, %.3f, %.3f)' % tuple(alpha)
    plt.title(title, fontdict={'fontsize': 12})
    a0 = int(np.round(alpha[0]*10))
    pml.savefig('dirichletHeatmap{}.jpg'.format(a0))
    plt.show()

    plt.figure(figsize=(8,8))
    plot_points(dist.sample(5000))
    pml.savefig('dirichletSample{}.jpg'.format(a0))
    plt.show()


'''
------------------------------------
Assignment 4 - EE2703 (Jan-May 2020)
Done by Akilesh Kannan (EE18B122)
Created on 09/02/20
Last Modified on 13/02/20
------------------------------------
'''

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

def coscosxFunc(x):
    return np.cos(np.cos(x))
def expFunc(x):
    return np.exp(x)

def pi_tick(value, tick_number):
    # find number of multiples of pi/2
    N = int(round(2 * value / PI))
    if N == 0:
        return "0"
    elif N == 1:
        return r"$\pi/2$"
    elif N == -1:
        return r"$-\pi/2$"
    elif N == 2:
        return r"$\pi$"
    elif N == -2:
        return r"$-\pi$"
    elif N % 2 != 0:
        return r"${0}\pi/2$".format(N)
    else:
        return r"${0}\pi$".format(N//2)

PI = np.pi
plotsDir = 'plots/'
x = np.linspace(-2*PI, 4*PI, 1201)
x = x[:-1]

plt.figure('Figure 1')
ax1 = plt.axes()
ax1.xaxis.set_major_formatter(plt.FuncFormatter(pi_tick))
plt.plot(x, coscosxFunc(x), 'k', label='True Function')
plt.plot(x, coscosxFunc(x%(2*PI)), '--', label='Fourier Series expansion')
plt.axis([-6, 10, 0.5, 1.05])
plt.legend(loc='upper right')
plt.title('$cos(cos(x))$')
plt.grid()
plt.savefig(plotsDir+'Figure 1.png')

plt.figure('Figure 2')
ax2 = plt.axes()
ax2.xaxis.set_major_formatter(plt.FuncFormatter(pi_tick))
plt.semilogy(x, expFunc(x), 'k', label='True Function')
plt.semilogy(x, expFunc(x%(2*PI)), '--', label='Fourier Series expansion')
plt.legend(loc='upper left')
plt.title('$e^x$')
plt.grid()
plt.savefig(plotsDir+'Figure 2.png')

def cosCoeff(x, k, f):
    return f(x)*np.cos(k*x)
def sinCoeff(x, k, f):
    return f(x)*np.sin(k*x)

def calc51FourierCoeffs (f):
    aCoeff = np.zeros(26)
    bCoeff = np.zeros(26)
    aCoeff[0] = quad(cosCoeff, 0, 2*PI, args=(0, f))[0]/(2*PI)
    for i in range(1, 26):
        aCoeff[i] = quad(cosCoeff, 0, 2*PI, args=(i, f))[0]/(PI)
        bCoeff[i] = quad(sinCoeff, 0, 2*PI, args=(i, f))[0]/(PI)
    coeffs = np.zeros(51)
    coeffs[0] = aCoeff[0]
    coeffs[1::2] = aCoeff[1:]
    coeffs[2::2] = bCoeff[1:]
    return coeffs

coeffCosCos = calc51FourierCoeffs(coscosxFunc)
coeffExp = calc51FourierCoeffs(expFunc)

xTicksForCoeffsSemilog = ['$a_0$']
for i in range(1, 26):
    xTicksForCoeffsSemilog.append('$a_{'+str(i)+'}$')
    xTicksForCoeffsSemilog.append('$b_{'+str(i)+'}$')

plt.figure('Figure 3')
plt.xticks(np.arange(51), xTicksForCoeffsSemilog, rotation=60)
plt.tick_params(axis='x', labelsize=7)
plt.semilogy(abs(coeffCosCos[1::2]), 'bo', label='$a_n$')
plt.semilogy(abs(coeffCosCos[2::2]), 'yo', label='$b_n$')
plt.legend()
plt.title('$cos(cos(x))$ semilog plot')
plt.grid()
plt.savefig(plotsDir+'Figure 3.png')

plt.figure('Figure 4')
plt.xticks(np.arange(51), xTicksForCoeffsSemilog, rotation=60)
plt.tick_params(axis='x', labelsize=7)
plt.semilogy(abs(coeffExp[1::2]), 'bo', label='$a_n$')
plt.semilogy(abs(coeffExp[2::2]), 'yo', label='$b_n$')
plt.legend()
plt.title('$e^x$ semilog plot')
plt.grid()
plt.savefig(plotsDir+'Figure 4.png')

plt.figure('Figure 5')
plt.loglog(abs(coeffCosCos[1::2]), 'bo', label='$a_n$')
plt.loglog(abs(coeffCosCos[2::2]), 'yo', label='$b_n$')
plt.legend()
plt.title('$cos(cos(x))$ loglog plot')
plt.grid()
plt.savefig(plotsDir+'Figure 5.png')

plt.figure('Figure 6')
plt.loglog(abs(coeffExp[1::2]), 'bo', label='$a_n$')
plt.loglog(abs(coeffExp[2::2]), 'yo', label='$b_n$')
plt.legend()
plt.title('$e^x$ loglog plot')
plt.grid()
plt.savefig(plotsDir+'Figure 6.png')

def findLSTSQCoeff(f):
    x = np.linspace(0, 2*PI, 401)
    x = x[:-1]
    b = f(x)
    M = np.zeros((400, 51))
    M[:,0] = 1
    for k in range(1,26):
        M[:,(2*k)-1]=np.cos(k*x)
        M[:,2*k]=np.sin(k*x)
    return np.linalg.lstsq(M, b, rcond=None)[0]

lstsqCosCos = findLSTSQCoeff(coscosxFunc)
lstsqExp = findLSTSQCoeff(expFunc)

plt.figure('Figure 3.1')
plt.semilogy(abs(coeffCosCos[1::2]), 'ro', label='$a_n$ by Integration')
plt.semilogy(abs(coeffCosCos[2::2]), 'bo', label='$b_n$ by Integration')
plt.semilogy(abs(lstsqCosCos[1::2]), 'go', label='$a_n$ by lstsq')
plt.semilogy(abs(lstsqCosCos[2::2]), 'yo', label='$b_n$ by lstsq')
plt.title('Comparing $cos(cos(x))$ FS coefficients - semilogy plot')
plt.legend()
plt.grid()
plt.savefig(plotsDir+'Figure 3.1.png')

plt.figure('Figure 4.1')
plt.semilogy(abs(coeffExp[1::2]), 'ro', label='$a_n$ by Integration')
plt.semilogy(abs(coeffExp[2::2]), 'bo', label='$b_n$ by Integration')
plt.semilogy(abs(lstsqExp[1::2]), 'go', label='$a_n$ by lstsq')
plt.semilogy(abs(lstsqExp[2::2]), 'yo', label='$b_n$ by lstsq')
plt.title('Comparing $e^x$ FS coefficients - semilogy plot')
plt.legend()
plt.grid()
plt.savefig(plotsDir+'Figure 4.1.png')

plt.figure('Figure 5.1')
plt.loglog(abs(coeffCosCos[1::2]), 'ro', label='$a_n$ by Integration')
plt.loglog(abs(coeffCosCos[2::2]), 'bo', label='$b_n$ by Integration')
plt.loglog(abs(lstsqCosCos[1::2]), 'go', label='$a_n$ by lstsq')
plt.loglog(abs(lstsqCosCos[2::2]), 'yo', label='$b_n$ by lstsq')
plt.title('Comparing $cos(cos(x))$ FS coefficients - loglog plot')
plt.legend()
plt.grid()
plt.savefig(plotsDir+'Figure 5.1.png')

plt.figure('Figure 6.1')
plt.loglog(abs(coeffExp[1::2]), 'ro', label='$a_n$ by Integration')
plt.loglog(abs(coeffExp[2::2]), 'bo', label='$b_n$ by Integration')
plt.loglog(abs(lstsqExp[1::2]), 'go', label='$a_n$ by lstsq')
plt.loglog(abs(lstsqExp[2::2]), 'yo', label='$b_n$ by lstsq')
plt.title('Comparing $e^x$ FS coefficients - loglog plot')
plt.legend()
plt.grid()
plt.savefig(plotsDir+'Figure 6.1.png')

absErrorsCosCos = [abs(coeffCosCos[i]-lstsqCosCos[i]) for i in range(len(coeffCosCos))]
absErrorsExp = [abs(coeffExp[i]-lstsqExp[i]) for i in range(len(coeffExp))]

print('Max. deviation for cos(cos(x)): '+str(max(absErrorsCosCos)))
print('Max. deviation for exp(x): '+str(max(absErrorsExp)))

xNew = np.linspace(-2*PI, 4*PI, 1201)
xNew = xNew[:-1]

matrixA = np.zeros((1200,51))
matrixA[:,0] = 1
for k in range(1,26):
    matrixA[:,(2*k)-1]=np.cos(k*xNew)
    matrixA[:,2*k]=np.sin(k*xNew)

plt.figure('Figure 7')
ax3 = plt.axes()
ax3.xaxis.set_major_formatter(plt.FuncFormatter(pi_tick))
plt.plot(xNew, matrixA@coeffCosCos, 'go', label='Integration')
plt.plot(xNew, matrixA@lstsqCosCos, 'r', label='lstsq')
plt.title('Reconstruction of $cos(cos(x))$')
plt.legend()
plt.grid()
plt.savefig(plotsDir+'Figure 7.png')

plt.figure('Figure 8')
ax4 = plt.axes()
ax4.xaxis.set_major_formatter(plt.FuncFormatter(pi_tick))
plt.plot(xNew, matrixA@coeffExp, 'go', label='Integration')
plt.plot(xNew, matrixA@lstsqExp, 'r', label='lstsq')
plt.title('Reconstruction of $e^x$')
plt.legend()
plt.grid()
plt.savefig(plotsDir+'Figure 8.png')


# pylint: disable=missing-module-docstring
#
# Copyright (C) 2022 by YadavGulshan@Github, < https://github.com/YadavGulshan >.
#
# This file is part of < https://github.com/Yadavgulshan/diagonalizable > project,
# and is released under the "MIT License".
# Please see < https://github.com/YadavGulshan/diagonalizable/blob/master/LICENCE >
#
# All rights reserved.

import numpy as np
import os

matrix = np.array(
    [
        [4,2,2],
        [2,4,2],
        [2,2,4],
    ]
)

class Diagonalizable:
    def __init__(self, matrix):
        self.matrix = matrix
        self.size = len(matrix)

    def check_diagonalizable_square(self):
        # Checking if the matrix is square
        print("Making required checks!!")
        print("please wait...")
        if all(i==self.matrix.shape[0] for i in self.matrix.shape):
            os.system('clear')
            return True
        else:
            return False

    def getEigenStuff(self):
        # First check if the matrix is square
        if self.check_diagonalizable_square():
            # Getting the eigenvalues and eigenvectors
            eigenValue, eigenVector = np.linalg.eig(self.matrix)
            return eigenValue, eigenVector
        else:
            print("The matrix is not square")
            exit()

    def check_diagonalizable(self):
        # Checking if the matrix is diagonalizable
        eigenValue, eigenVector = self.getEigenStuff()
        print("Eigen Value: ", eigenValue)
        print("Eigen Vector: \n",eigenVector)
        print(set(eigenValue), '\n')

        # Check if all the eigenvalues are unique and real
        if len(eigenValue) == len(set(eigenValue)) and np.all(np.isreal(eigenValue)):
            return "Diagonalizable"
        else:
            return "Not Diagonalizable"

diagonalizable = Diagonalizable(matrix)
print(diagonalizable.check_diagonalizable())


'''
This module contains functions to generate 2D rotation matrices.
'''
import math
import numpy as np

def rotation_from_angle(angle):
    '''
    generates a 2x2 rotation matrix from a given angle following
    the definition here:
    https://en.wikipedia.org/wiki/Rotation_matrix.

    :param angle: the angle by which to rotate
    :type angle: float

    :returns: the rotation matrix
    :rtype: a 2x2 :any:`numpy.ndarray`
    '''
    c = math.cos(angle)
    s = math.sin(angle)

    return np.array([[c, -s],[s, c]])


import sys
import numpy as np

from matplotlib import pyplot as plt
from math import pow

np.set_printoptions(threshold=sys.maxsize)

RHO = 1500.0
LAMBDA = 1.0
N = 51
DELTA = 1/(N-1) # 0.02 m = 2 cm = 1 / (N-1)
K = 0.1
C = 1000.0
k = LAMBDA / (C*RHO)
DELTA_T = K * pow(DELTA,2) / k

T = np.zeros((N, N))
x = np.linspace(-0.5, 0.5, num=N)
y = np.linspace(1, 0, num=N)

def step_time_2d(T, K):
    output = T + K * ( np.roll(T,-1,1) - 2 * T + np.roll(T,1,1) + np.roll(T,-1,0) - 2 * T + np.roll(T,1,0) )
    output[0,:] = -10
    output[-1,:] = 0
    output[:,0] = output[:,1]
    output[:,-1] = output[:,-2]
    output[15,25] += 250000 / (C * RHO) * DELTA_T
    return output

time_steps = 0
while True:
    new_T = step_time_2d(T, K)
    time_steps += 1
    if abs(np.mean(new_T - T)) < pow(10, -9):
        break
    T = new_T

q = (-LAMBDA) * np.array(np.gradient(T, DELTA))

c = plt.contourf(x, y, T, levels=list(range(-10,int(np.max(T)), 8)))
plt.streamplot(x, y, q[1], -q[0], color="white", density=0.7, linewidth=1.4)
plt.axis((-0.5, 0.5, 0, 1))
ax = plt.gca()
ax.set_xlabel("y position (m)")
ax.set_ylabel("x position (m)")
cbar = plt.colorbar(c, ax=ax, ticks=list(range(-10, int(np.max(T)), 10)))
cbar.set_label("Temperatur (°C)")

print(f"Time step size: {DELTA_T}")
print(f"Tolarence cleared after {time_steps} iterations. ({time_steps * DELTA_T / 60 / 60} hours)")
print(f"Total temperature flow through the top is {np.sum(T[2,:]-T[1,:]) * LAMBDA}")
print(f"Total temperature flow through the bottom is {np.sum(T[-2,:]-T[-1,:]) * LAMBDA}")

plt.show()


import numpy as np

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

def cross_entropy(prob, t):
    if prob.ndim == 1:
        t = t.reshape(1, t.size)
        prob = prob.reshape(1, prob.size)
    return -np.mean(np.log(prob[np.arange(prob.shape[0]), t] + 1e-7))

def softmax_cross_entropy(y, t):
    return cross_entropy(softmax(y), t)


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

DISTANCE = 10
ANGLE = np.pi / 4

AXIS_ROT_ANGLE = -np.pi / 2
AXIS_TRANS = np.array([10, 0])

MEAN_SENSOR = np.array([DISTANCE, ANGLE])
COVARIANCE_SENSOR = np.array([[0.75 * 0.75, 0], [0, (np.pi / 90) * (np.pi / 90)]])

if __name__ == "__main__":
    samples_dist = np.random.multivariate_normal(MEAN_SENSOR, COVARIANCE_SENSOR, 100)

    # plt.boxplot([180 * s[1] / np.pi for s in samples_dist])

    samples_x = [d * math.cos(theta) for [d, theta] in samples_dist]
    samples_y = [d * math.sin(theta) for [d, theta] in samples_dist]

    samples_coord = [np.array(list(coords)) for coords in zip(samples_x, samples_y)]

    rotation_matrix = np.array(
        [
            [math.cos(AXIS_ROT_ANGLE), math.sin(AXIS_ROT_ANGLE)],
            [-math.sin(AXIS_ROT_ANGLE), math.cos(AXIS_ROT_ANGLE)],
        ]
    )
    trans_new_coord = np.matmul(AXIS_TRANS, rotation_matrix)

    mean_sensor_x = DISTANCE * math.cos(ANGLE)
    mean_sensor_y = DISTANCE * math.sin(ANGLE)

    mean_sensor_rotated = np.matmul(
        np.array([mean_sensor_x, mean_sensor_y]), rotation_matrix
    )
    mean_sensor_transformed = np.array(
        [
            mean_sensor_rotated[0] - trans_new_coord[0],
            mean_sensor_rotated[1] - trans_new_coord[1],
        ]
    )
    mean_sensor_transformed_dist = np.array(
        [
            np.linalg.norm(mean_sensor_transformed),
            math.atan2(mean_sensor_transformed[1], mean_sensor_transformed[0]),
        ]
    )

    samples_transformed_dist = np.random.multivariate_normal(
        mean_sensor_transformed_dist, COVARIANCE_SENSOR, 100
    )
    plt.boxplot([180 * s[1] / np.pi for s in samples_transformed_dist])
    plt.show()

    # samples_rotated = [np.matmul(s, rotation_matrix) for s in samples_coord]
    # samples_transformed_dist = [np.array([s[0] - trans_new_coord[0], s[1] - trans_new_coord[1]]) for s in samples_rotated]

    # samples_transformed_x = [s[0] for s in samples_transformed_dist]
    # samples_transformed_y = [s[1] for s in samples_transformed_dist]
    #
    # samples_transformed_dist = [np.array( [np.linalg.norm(s), math.atan2(s[1], s[0])] ) for s in samples_transformed_dist]
    # plt.boxplot([180 * s[1] / np.pi for s in samples_transformed_dist])

    # fig1, ax1 = plt.subplots()
    # ax1.set_title("Points in original axis")
    # ax1.scatter(samples_x, samples_y)
    #
    # fig2, ax2 = plt.subplots()
    # ax2.set_title("Points in transformed axis")
    # ax2.scatter(samples_transformed_x, samples_transformed_y)

    fieldnames = ["distance", "angle"]

    with open("data/sensor1.csv", mode="w") as csv_file:
        writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
        writer.writeheader()

        for s in samples_dist:
            writer.writerow({"distance": round(s[0], 4), "angle": round(s[1], 4)})

    with open("data/sensor2.csv", mode="w") as csv_file:
        writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
        writer.writeheader()

        for s in samples_transformed_dist:
            writer.writerow({"distance": round(s[0], 4), "angle": round(s[1], 4)})

    test_distances = np.random.uniform(low=2.5, high=10.5, size=(10,))
    test_angles = np.random.uniform(low=np.pi / 30, high=2 * np.pi / 3, size=(10,))

    test_points = [np.array([e[0], e[1]]) for e in zip(test_distances, test_angles)]
    test_points_coords = [
        np.array([e[0] * math.cos(e[1]), e[0] * math.sin(e[1])]) for e in test_points
    ]

    test_points_rotated = [np.matmul(s, rotation_matrix) for s in test_points_coords]
    test_points_transformed = [
        np.array([s[0] - trans_new_coord[0], s[1] - trans_new_coord[1]])
        for s in test_points_rotated
    ]

    # plt.scatter([s[0] for s in test_points_coords], [s[1] for s in test_points_coords])
    # plt.scatter([s[0] for s in test_points_transformed], [s[1] for s in test_points_transformed])

    test_points_transformed_dist = [
        np.array([np.linalg.norm(s), math.atan2(s[1], s[0])])
        for s in test_points_transformed
    ]

    with open("data/test_sensor1.csv", mode="w") as csv_file:
        writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
        writer.writeheader()

        for s in test_points:
            writer.writerow({"distance": round(s[0], 4), "angle": round(s[1], 4)})

    with open("data/test_sensor2.csv", mode="w") as csv_file:
        writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
        writer.writeheader()

        for s in test_points_transformed_dist:
            writer.writerow({"distance": round(s[0], 4), "angle": round(s[1], 4)})


from Statistics.Mean import mean
from Calculator.Multiplication import multiplication
from Calculator.Subtraction import subtraction
from Calculator.Square import square
from Calculator.Division import division
from Calculator.SquareRoot import square_root
import numpy as np

def sample_correlation(data, data1):
    try:

        mean1 = mean(data)
        mean2 = mean(data1)
        List1 = []
        List2 = []

        for num in data:
            a = subtraction(int(round(mean1, 0)), num)
            List1.append(a)

        for num in data1:
            b = subtraction(mean2, num)
            List2.append(b)
        c = np.multiply(List1, List2)
        cc = 0
        for num in c:
            cc = cc + num

        d = 0
        e = 0
        # pprint(List1)
        # pprint(List2)
        for num in List1:
            d = d + square(num)
        for num in List2:
            e = e + square(num)

        f = multiplication(int(d), e)
        g = square_root(int(f))
        h = division(int(g), cc)
        # pprint(float(cc))
        # pprint(e)
        # pprint(f)
        # pprint(float(g))
        # pprint(str(round(h,9)))
        nCorrelation = round(h, 9)
        # pprint(nCorrelation)
        return nCorrelation
    except ZeroDivisionError:
        print("Error - Cannot divide by 0")
    except ValueError:
        print("Error - Invalid data inputs")

# https://www.statisticshowto.datasciencecentral.com/probability-and-statistics/correlation-coefficient-formula/

# https://www.geeksforgeeks.org/numpy-subtract-in-python/


import math as m
import numpy as np
from matplotlib import pyplot as plt

a=1; b=-62.1; c=1
x1=(-b+m.sqrt(b**2-4*a*c))/(2*a)
x2=(-b-m.sqrt(b**2-4*a*c))/(2*a)
print('x1=',x1,', x2=',x2)
print('p(x1)=',a*x1**2+b*x1+c)
print('p(x2)=',a*x2**2+b*x2+c)

x = np.linspace(-2, 2, 1000)
y = a*x**2+b*x+c

plt.plot(x, y)

def raizCuad(a,b,c):
  D=b**2-4*a*c
  if D>0:
    if b>=0:
      x1=-2*c/(b+m.sqrt(D))
      x2=(-b-m.sqrt(D))/(2*a)
    else:
      x1=(-b-m.sqrt(D))/(2*a)
      x2=-2*c/(b-m.sqrt(D))
    return [x1,x2]
  elif D==0:
    x=-b/(2*a)
    return x
  else:
    return "no existen raíces reales"

a=1; b=62.1; c=1
xnuevo=raizCuad(a,b,c)
print(xnuevo)
print('p(x1)=',a*xnuevo[0]**2+b*xnuevo[0]+c)
print('p(x2)=',a*xnuevo[1]**2+b*xnuevo[1]+c)


import numpy, scipy.special, scipy.integrate

def HIMF(logMHI):
    '''
    ALFALFA 100% HI mass function Schechter model.

    Inputs:

    logMHI = log(M_HI/M_sol)

    Outputs:

    n = Number density at log(M_HI/M_sol)
    '''

    alpha = -1.25
    phi_s = 4.5E-3
    M_s = 10.**9.94

    MHI = 10.**logMHI

    return numpy.log(10.)*phi_s*((MHI/M_s)**(alpha+1))*numpy.exp(-MHI/M_s)

#Cosmology
#WMAP9
H0 = 69.7 #km/s/Mpc
O_m = 0.2814
O_L = 1.-O_m
c = 299792.458 #km/s
O_HI = 3.9E-4
G = 6.67E-11

rho_c_0 = 3.*((H0/3.086E19)**2.)/(8.*numpy.pi*G)
rho_HI_0 = O_HI*rho_c_0*(((3.086E22)**3.)/2.E30) #M_sol/Mpc^3

def E(z):

    return numpy.sqrt((O_m*((1.+z)**3.)) + O_L)

def co_dist(z):

    d_H = c/H0

    return d_H * scipy.integrate.quad(lambda x: 1./E(x),0.,z)[0]

def phys_size(theta,z):
    '''
    Returns the physical size of the beam in Mpc at a given redshift.

    Inputs:

    theta = Angular size (radians) at z=0
    z = Redshift

    Outputs:

    l = Physical size (Mpc)
    '''

    theta_eff = theta*(1.+z)

    return co_dist(z)*theta_eff/(1.+z)

def m_av_mod(y,x):
    '''
    Integral over the 2PCF in velocity space (y-direction) and angular space (x-direction).
    Uses the fit made in Jones et al. 2016. Both x and y should be in Mpc.

    Inputs:

    y = Integration limit in velocity space (Mpc)
    x = Integration limit in the angular direcction (Mpc)

    Output:

    m = Effective volume (Mpc^3) due to clustering in 2PCF.
    '''

    A, alpha, a = 12.104434  ,   -1.13210593,   0.64058959
    b = 1./a

    r = A**(-1./alpha)

    return a*((2.*numpy.pi)/((alpha+2.)*(alpha+3.)*r**alpha)) * ( (y/b)*((x/a)**2.)*((r)**alpha)*(alpha+2.)*(alpha+3.) + 2.*(y/b)*((x/a)**(2.+alpha))*(alpha+3.)*scipy.special.hyp2f1(0.5,-1.-(alpha/2.),1.5,-(y*a/(x*b))**2.) - 2.*(y/b)**(alpha+3.) )


import numpy as np

from math import log, exp

To = 12000
Tw = 2000
m = 5

#First part
def solveIntegralNT(a, b, p):

    sum = .0
    N = 30
    step = (b - a) / (1.0 * N)

    for i in range(1, N):
        sum += NT(a + i * step, p)

    sum += (NT(a, p) + NT(b, p)) / 2
    sum *= step

    return sum

def Fp_funct(p):
    const_part = 7243.0 * (0.5 / 300.0)
    res = const_part - 2.0 * solveIntegralNT(0.0, 1.0, p)
    return res

def calculeateP(eps):
    p1 = 3.0
    p2 = 28.0

    p = (p1 + p2) / 2
    while abs((p2 - p1) / p) >= eps:
        p = (p1 + p2) / 2

        if Fp_funct(p)*Fp_funct(p1) < 0:
            p2 = p
        else:
            p1 = p
    return p

def TZ(z):
    zm = 1
    for i in range(1, m+1):
        zm *= z
    return To + (Tw - To)*zm

def NT(z, p):
    res = 1.0
    Tz = TZ(z)
    if Tz != 0:
        res = 7243.0 * (p / Tz) * z
    return res

p = calculeateP(1e-4)
print(p)
print("\n")

Second course/4th semester/Computational Algorithms/Lab5 - idk_lab_name/1st part.py


"""
PolynomialGenerator class definition
"""
from math import comb, factorial
import numpy as np

class PolynomialGenerator():
    """
    PolynomialGenerator generates polynomial trajectories from constraints
    """

    def __init__(self):
        pass

    def generate_coefficients(self, q_init, q_final, t_f, t_0=0.0):
        """
        Calculates polynomial trajectory coefficients from constraints

        Args:
            q_init (list): list of constraints: [q_init, dq_init, ddq_init]
            q_final (list): list of constraints: [q_final, dq_final, ddq_final]
            t_f (float): Finish time
            t_0 (float, optional): Start time

        Returns:
            np.array: Resultant polynomial coefficients
        """
        A_0 = self._get_constraint_submatrix(t_0)
        A_f = self._get_constraint_submatrix(t_f)

        A = np.vstack((A_0, A_f))
        q = np.hstack((np.array(q_init), np.array(q_final)))

        return np.linalg.inv(A).dot(q)

    def _get_constraint_submatrix(self, t):
        """
        Generates time constraint submatrix for time t

        Args:
            t (float): Time constraint

        Returns:
            np.ndarray: Time constraint submatrix
        """
        A = np.array([[1, t, t**2, t**3, t**4, t**5],
                      [0, 1, 2 * t, 3 * t**2, 4 * t**3, 5 * t**4],
                      [0, 0, 2, 6 * t, 12 * t**2, 20 * t**3]])
        return A

    def polynomial_from_coefs(self, coefs, from_t, to_t, n):
        """
        Generates discrete polynomial of len(coefs) - 1 degree of size n

        Args:
            coefs (array-like): Coefficients of the polynomial to generate
            from_t (float): Start point
            to_t (float): End point
            n (int): Number of points

        Returns:
            np.ndarray: Array of n points from the polynomial
        """
        ts = np.linspace(from_t, to_t, n)
        poly = 0

        for i, coef in enumerate(coefs):
            poly += coef * ts**i

        return poly

    def dpolynomial_from_coefs(self, d, coefs, from_t, to_t, n):
        """
        Generates dth derivative of a polynomial with coefficients coefs

        Args:
            d (TYPE): Order of the derivative
            coefs (array-like): Coefficients of the polynomial to generate
            from_t (float): Start point
            to_t (float): End point
            n (int): Number of points

        Returns:
            np.array: Array of n points from the polynomial's dth derivative
        """
        ts = np.linspace(from_t, to_t, n)
        poly = 0

        if d >= len(coefs):
            return np.zeros(n)

        for i, coef in enumerate(coefs[d:], d):
            poly += coef * factorial(d) * comb(i, d) * ts**(i - d)

        return poly


import numpy as np
from scipy.stats import norm

# Underlying informations
S0 = 100.0
sigma = 0.2

# European option informations
T = 1.0
K = 100.0
r = 0.05

# Binomial tree parameters
nbr_steps = 1000
dt = T/nbr_steps
u = np.exp(sigma*np.sqrt(dt))
d = 1/u
p = (np.exp(r*dt)-d)/(u-d)

# parameters for greek calculation
d_sigma = sigma/100

# European call price and greeks according to Black-Scholes

def d1():
    return (np.log(S0/K)+(r+0.5*sigma**2)*T)/(sigma*np.sqrt(T))

def d2():
    return d1() - sigma*np.sqrt(T)

def price_BS(S0):
    return S0*norm.cdf(d1())-K*np.exp(-r*T)*norm.cdf(d2())

def delta_BS():
    return norm.cdf(d1())

def gamma_BS():
    return norm.pdf(d1())/(S0*sigma*np.sqrt(T))

def theta_BS():
    return -S0*norm.pdf(d1())*sigma/(2*np.sqrt(T))-r*K*np.exp(-r*T)*norm.cdf(d2())

def vega_BS():
    return S0*np.sqrt(T)*norm.pdf(d1())

# European call price and greeks according to the binomial tree

def binom_tree(sigma):
    u = np.exp(sigma*np.sqrt(dt))
    d = 1/u
    p = (np.exp(r*dt)-d)/(u-d)

    steps = []
    steps.append([[S0, 0.0]])

    # Generating prices
    for i in range(2, nbr_steps+2):
        step = []
        for j in reversed(range(i)):
            step.append([S0*(u**(j+1))*d**(i-j), 0.0])
        steps.append(step)

    # Reverse induction payoff
    for j in range(nbr_steps+1):
        steps[nbr_steps][j][1] = max(0.0, steps[nbr_steps][j][0]-K)

    for i in reversed(range(nbr_steps)):
        for j in range(i+1):
            steps[i][j][1] = np.exp(-r*dt)*(p*steps[i+1][j][1]
                                            + (1-p)*steps[i+1][j+1][1])

    return steps

def price_binom():
    return binom_tree(sigma)[0][0][1]

def delta_binom():
    tree = binom_tree(sigma)
    return (tree[1][0][1]-tree[1][1][1])/(S0*(u-d))

def gamma_binom():
    tree = binom_tree(sigma)
    return ((tree[2][0][1] - tree[2][1][1])/(S0*(u**2-1))
            - (tree[2][1][1] - tree[2][2][1])/(S0*(1-d**2)))/(0.5*S0*(u**2-d**2))

def theta_binom():
    tree = binom_tree(sigma)
    return (tree[2][1][1] - tree[0][0][1])/(2*dt)

def vega_binom(d_sigma):
    tree = binom_tree(sigma)
    tree_d_sigma = binom_tree(sigma+d_sigma)
    return (tree_d_sigma[0][0][1] - tree[0][0][1])/d_sigma

# Testing
delta_bs, delta_mc = delta_BS(), delta_binom()
print('Delta : \nTheorical value : {} ; Binomial tree value : {} ; Error : {} %'
    .format(delta_bs, delta_mc, 100*np.round(np.abs((delta_mc - delta_bs)/delta_bs), 5)))
gamma_bs, gamma_mc = gamma_BS(), gamma_binom()
print('Gamma : \nTheorical value : {} ; Binomial tree value : {} ; Error : {} %'
    .format(gamma_bs, gamma_mc, 100*np.round(np.abs((gamma_mc - gamma_bs)/gamma_bs), 5)))
theta_bs, theta_mc = theta_BS(), theta_binom()
print('Theta : \nTheorical value : {} ; Binomial tree value : {} ; Error : {} %'
    .format(theta_bs, theta_mc, 100*np.round(np.abs((theta_mc - theta_bs)/theta_bs), 5)))
vega_bs, vega_mc = vega_BS(), vega_binom(d_sigma)
print('Vega : \nTheorical value : {} ; Binomial tree value : {} ; Error : {} %'
    .format(vega_bs, vega_mc, 100*np.round(np.abs((vega_mc - vega_bs)/vega_bs), 5)))

input('Press enter to continue...')


import numpy as np
from scipy.integrate import simps
from scipy.optimize import root_scalar, fsolve

from ..constants import speed_of_light, omega_matter, omega_lamda, H0
cc = speed_of_light*1e-3 # Km/s

def distance_from_redshift(z):
    """
    Returns the luminosity distance [Mpc] from the redshift.

    Uses the Planck 2018 cosmological parameters from Tab. 1 in https://arxiv.org/abs/1807.06209.

    :param float z: Redshift.

    :rtype: float
    """
    integrand = lambda x: 1/np.sqrt(omega_matter*(1+x)**3+omega_lamda)
    dz = 1e-5
    X = np.arange(0,z+dz,dz)
    Y = integrand(X)
    d_L = cc/H0*(1+z)*simps(Y,X)
    return d_L

def redshift_from_distance(d_L):
    """
    Returns the redshift from the luminosity distance [Mpc].

    Uses the Planck 2018 cosmological parameters from Tab. 1 in https://arxiv.org/abs/1807.06209.

    :param float d_L: Luminosity distance [Mpc]. Must be no less than 1.

    :rtype: float
    """
    if d_L<1:
        raise ValueError('d_L must be no less than 1 Mpc!')
    f = lambda x: d_L - distance_from_redshift(x)
    z = fsolve(f,10).item()
    return z