import numpy as np
import mkl_random as rnd

__doc__ = """
Let's solve a classic problem of MC-estimating a probability that 3 segments of a unit stick randomly broken in 2 places can form a triangle.
Let $u_1$ and $u_2$ be standard uniform random variables, denoting positions where the stick has been broken.

Let $w_1 = \min(u_1, u_2)$ and $w_2 = \max(u_1, u_2)$. Then, length of segments are $x_1 = w_1$, $x_2 = w_2-w_1$, $x_3 = 1-w_2$.
These lengths must satisfy triangle inequality.

The closed form result is known to be $\frac{1}{4}$.

"""

def triangle_inequality(x1, x2, x3):
    """Efficiently finds `np.less(x1,x2+x3)*np.less(x2,x1+x3)*np.less(x3,x1+x2)`"""
    tmp_sum = x2 + x3
    res = np.less(x1, tmp_sum)   # x1 < x2 + x3
    np.add(x1, x3, out=tmp_sum)
    buf = np.less(x2, tmp_sum)   # x2 < x1 + x3
    np.logical_and(res, buf, out=res)
    np.add(x1, x2, out=tmp_sum)
    np.less(x3, tmp_sum, out=buf) # x3 < x1 + x2
    np.logical_and(res, buf, out=res)
    return res

def mc_dist(rs, n):
    """Monte Carlo estimate of probability on sample of size `n`, using given random state object `rs`"""
    ws = np.sort(rs.rand(2,n), axis=0)
    x2 = np.empty(n, dtype=np.double)
    x3 = np.empty(n, dtype=np.double)

    x1 = ws[0]
    np.subtract(ws[1], ws[0], out=x2)
    np.subtract(1, ws[1], out=x3)
    mc_prob = triangle_inequality(x1, x2, x3).sum() / n

    return mc_prob

def assign_worker_rs(w_rs):
    """Assign process local random state variable `rs` the given value"""
    assert not 'rs' in globals(), "Here comes trouble. Process is not expected to have global variable `rs`"

    global rs
    rs = w_rs
    # wait to ensure that the assignment takes place for each worker
    b.wait()

def worker_compute(w_id):
    return mc_dist(rs, batch_size)

if __name__ == '__main__':
    import multiprocessing as mp
    from itertools import repeat
    from timeit import default_timer as timer

    seed = 77777
    n_workers = 12
    batch_size = 1024 * 256
    batches = 10000

    t0 = timer()
    # Create instances of RandomState for each worker process from MT2203 family of generators
    rss = [ rnd.RandomState(seed, brng=('MT2203', idx)) for idx in range(n_workers) ]
    # use of Barrier ensures that every worker gets one
    b = mp.Barrier(n_workers)

    with mp.Pool(processes=n_workers) as pool:
        # map over every worker once to distribute RandomState instances
        pool.map(assign_worker_rs, rss, chunksize=1)
        # Perform computations on workers
        r = pool.map(worker_compute, range(batches), chunksize=1)

    # retrieve values of estimates into numpy array
    ps = np.fromiter(r, dtype=np.double)
    # compute sample estimator's mean and standard deviation
    p_est = ps.mean()
    pop_std = ps.std()
    t1 = timer()

    dig = 3 - int(np.log10(pop_std))
    frm_str = "{0:0." + str(dig) + "f}"
    print(("Monte-Carlo estimate of probability: " + frm_str).format(p_est))
    print(("Population estimate of the estimator's standard deviation: " + frm_str).format(pop_std))
    print(("Expected standard deviation of the estimator: " + frm_str).format(np.sqrt(p_est * (1-p_est)/batch_size)))
    print("Execution time: {0:0.3f} seconds".format(t1-t0))


#!/usr/bin/env python3

import numpy as np

# 1D Array
a = np.array([0, 1, 2, 3, 4])
b = np.array((0, 1, 2, 3, 4))
c = np.arange(5)
d = np.linspace(0, 2 * np.pi, 5)

print(a)  # >>>[0 1 2 3 4]
print(b)  # >>>[0 1 2 3 4]
print(c)  # >>>[0 1 2 3 4]
print(d)  # >>>[ 0.          1.57079633  3.14159265  4.71238898  6.28318531]
print(a[3])  # >>>3

# MD Array,
a = np.array([[11, 12, 13, 14, 15],
              [16, 17, 18, 19, 20],
              [21, 22, 23, 24, 25],
              [26, 27, 28, 29, 30],
              [31, 32, 33, 34, 35]])

print(a[2, 4])  # >>>25

# MD slicing
print(a[0, 1:4])  # >>>[12 13 14]
print(a[1:4, 0])  # >>>[16 21 26]
print(a[::2, ::2])  # >>>[[11 13 15]
#     [21 23 25]
#     [31 33 35]]
print(a[:, 1])  # >>>[12 17 22 27 32]

# Array properties
a = np.array([[11, 12, 13, 14, 15],
              [16, 17, 18, 19, 20],
              [21, 22, 23, 24, 25],
              [26, 27, 28, 29, 30],
              [31, 32, 33, 34, 35]])

print(type(a))  # >>><class 'numpy.ndarray'>
print(a.dtype)  # >>>int64
print(a.size)  # >>>25
print(a.shape)  # >>>(5, 5)
print(a.itemsize)  # >>>8
print(a.ndim)  # >>>2
print(a.nbytes)  # >>>200

# Basic Operators
a = np.arange(25)
a = a.reshape((5, 5))

b = np.array([10, 62, 1, 14, 2, 56, 79, 2, 1, 45,
              4, 92, 5, 55, 63, 43, 35, 6, 53, 24,
              56, 3, 56, 44, 78])
b = b.reshape((5, 5))

print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a ** 2)
print(a < b)
print(a > b)
print(a.dot(b))

# dot, sum, min, max, cumsum
a = np.arange(10)

print(a.sum())  # >>>45
print(a.min())  # >>>0
print(a.max())  # >>>9
print(a.cumsum())  # >>>[ 0  1  3  6 10 15 21 28 36 45]

# Fancy indexing
a = np.arange(0, 100, 10)
indices = [1, 5, -1]
b = a[indices]
print(a)  # >>>[ 0 10 20 30 40 50 60 70 80 90]
print(b)  # >>>[10 50 90]

# Incomplete Indexing
a = np.arange(0, 100, 10)
b = a[:5]
c = a[a >= 50]
print(b)  # >>>[ 0 10 20 30 40]
print(c)  # >>>[50 60 70 80 90]

# Where
a = np.arange(0, 100, 10)
b = np.where(a < 50)
c = np.where(a >= 50)[0]
print(b)  # >>>(array([0, 1, 2, 3, 4]),)
print(c)  # >>>[5 6 7 8 9]


import math
import numpy as np
import torch
import torch.nn.functional as F

import matplotlib.pyplot as plt

max_bound = 1.0
nsteps = 1000
dots = torch.linspace(-max_bound, max_bound, nsteps)
d = 16
m = int(d * math.log(d))  # 44
seqlen = 1024
n_hashes = 4  # This is L in the LSH notation
nbuckets = 23  # This is k in the LSH notation
# We set these so that bucket_size = 1024 / (2 * 23) = 22, so Reformer has 22 * 4 = 88 parameters
# Which is the same as Performer (2 * 44)

sm = torch.exp(dots)
performer_mse = 1 / m * torch.exp(2 * max_bound**2 + 2 * dots) * sm**2 * (1 - torch.exp(-2 * max_bound**2 - 2 * dots))
performer_mse_half = 1 / (m / 2) * torch.exp(2 * max_bound**2 + 2 * dots) * sm**2 * (1 - torch.exp(-2 * max_bound**2 - 2 * dots))
performer_hyp_mse = 1 / 2 * (1 - math.exp(-max_bound**2)) * performer_mse_half
tau = torch.sqrt(2 * max_bound**2 - 2 * dots)
lsh_prob = torch.exp(-tau**2 / (4 - tau**2) * math.log(d))
lsh_prob_power = 1 - (1 - lsh_prob**nbuckets)**n_hashes
reformer_mse = sm**2 * (1 - lsh_prob_power)
lsh_prob_power_scatterbrain = 1 - (1 - lsh_prob**(nbuckets))**(n_hashes)
scatterbrain_mse = (1 - lsh_prob_power_scatterbrain) * performer_mse
scatterbrain_hyp_mse = (1 - lsh_prob_power_scatterbrain) * performer_hyp_mse

import matplotlib
import matplotlib.pyplot as plt
matplotlib.style.use(['seaborn-colorblind'])
# import seaborn as sns
# sns.set_theme()
# sns.set_style('whitegrid')

plt.figure(figsize=(6, 4))
plt.plot(dots, reformer_mse, alpha=0.7, color='blue', label='Reformer')
plt.plot(dots, performer_mse, alpha=0.7, color='green', label='Performer')
plt.plot(dots, scatterbrain_mse, alpha=0.7, color='red', label='Scatterbrain')
plt.plot(dots, performer_hyp_mse, alpha=0.7, color='black', label='Performer hyp')
plt.plot(dots, scatterbrain_hyp_mse, alpha=0.7, color='brown', label='Scatterbrain hyp')
plt.xlabel(r'$q^\top k$', fontsize=14)
plt.ylabel('MSE', fontsize=14)
plt.xticks([-1.0, -0.5, 0.0, 0.5, 1.0])
plt.legend(fontsize=14)
plt.savefig('theory_mse_new.pdf', bbox_inches='tight')
plt.close()


import numpy as np
from simpson import simpson_integrate, simpson_error_bound
from trapezoid import trapezoid_integrate, trapezoid_error_bound

def f(x):
    return np.sin(x)

def main():
    points = np.linspace(0.0, np.pi/2, 11)

    print("Simpson integration result = "+str(simpson_integrate(f, 0.0, np.pi/2, 10, points)))
    print("Simpson integration error = "+str(abs(simpson_integrate(f, 0.0, np.pi/2, 10, points)-1.0)))
    print("Simpson theoretical error bound = "+str(simpson_error_bound(0.0, np.pi/2, 10, 1)))
    print()
    print("Trapezoid integration result = "+str(trapezoid_integrate(f, 0.0, np.pi/2, 10, points)))
    print("Trapezoid integration error = "+str(abs(trapezoid_integrate(f, 0.0, np.pi/2, 10, points)-1.0)))
    print("Trapezoid theoretical error bound = "+str(trapezoid_error_bound(0.0, np.pi/2, 10, 1)))

main()


import numpy as np
import astropy.constants as const
import astropy.units as u
from astropy.cosmology import FlatLambdaCDM
import warnings

def lensing_efficiency(zl,zs,cosmo):
    '''
    computes the lensing ratio for a defined LambdaCDM cosmology
    '''
    if zs == float('inf') or zs == 'inf': return 1.0
    dls = cosmo.angular_diameter_distance_z1z2(zl,zs).value
    ds = cosmo.angular_diameter_distance(zs).value
    return dls/ds

def dlsds(zl,zs,cosmo=FlatLambdaCDM(H0=70,Om0=0.3)):
    '''
    computes the lensing ratio for a defined cosmology
    '''
    if zs == float('inf') or zs == 'inf' or zs == np.inf: return 1.0
    dls = cosmo.angular_diameter_distance_z1z2(zl,zs).value
    ds = cosmo.angular_diameter_distance(zs).value
    return dls/ds

def critical_density(zl,zs,cosmo=FlatLambdaCDM(H0=70,Om0=0.3)):
    '''
    computes critical surface mass density for a defined LambdaCDM cosmology
    '''
    ratio = dlsds(zl,zs,cosmo=cosmo)
    dl = cosmo.angular_diameter_distance(zl)
    cd = (const.c**2)/(4*np.pi*const.G*ratio*dl)
    return cd.to('M_sun/kpc2').value

def jacobian_kg(kappa,gamma,ratio=1):
    '''
    computes the determanant of the Jacobian matrix from kappa and gamma matrices
    '''
    A = (1-kappa*ratio)**2-(gamma*ratio)**2
    return A

def jacobian_dxdy(deflectx,deflecty,ratio=1,dunit=1):
    '''
    computes the determanant of the Jacobian matrix from deflection matrices
    '''
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        dDXdy,dDXdx = np.gradient(deflectx*ratio/dunit)
        dDYdy,dDYdx = np.gradient(deflecty*ratio/dunit)
        A = (1-dDXdx)*(1-dDYdy)-dDXdy*dDYdx
        return A

def radial_eigenvalue_kg(kappa,gamma,ratio=1):
    '''
    computes the radial eigenvalue of the Jacobian matrix from kappa and gamma
    '''
    return (1-kappa*ratio+gamma*ratio)

def tangential_eigenvalue_kg(kappa,gamma,ratio=1):
    '''
    computes the tangential eigenvalue of the Jacobian matrix from kappa and gamma
    '''
    return (1-kappa*ratio-gamma*ratio)

def radial_eigenvalue_dxdy(deflectx,deflecty,ratio=1,dunit=1):
    '''
    computes the radial eigenvalue of the Jacobian matrix from the deflection matrices
    '''
    kappa,gamma = get_maps(deflectx,deflecty,ratio=ratio,dunit=dunit)
    return (1-kappa*ratio+gamma*ratio)

def tangential_eigenvalue_dxdy(deflectx,deflecty,ratio=1,dunit=1):
    '''
    computes the tangential eigenvalue of the Jacobian matrix from the deflection matrices
    '''
    kappa,gamma = get_maps(deflectx,deflecty,ratio=ratio,dunit=dunit)
    return (1-kappa*ratio-gamma*ratio)

def magnification_kg(kappa,gamma,ratio=1,absolute=True):
    '''
    computes the magnification from kappa and gamma matrices
    '''
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        A = (1-kappa*ratio)**2-(gamma*ratio)**2
        if absolute: A = np.abs(A)
        return 1.0/A

def magnification_dxdy(deflectx,deflecty,ratio=1,dunit=1,absolute=True):
    '''
    computes the magnification from deflection matrices (in units of pixels)
    '''
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        dDXdy,dDXdx = np.gradient(deflectx*ratio/dunit)
        dDYdy,dDYdx = np.gradient(deflecty*ratio/dunit)
        A = (1-dDXdx)*(1-dDYdy)-dDXdy*dDYdx
        if absolute: A = np.abs(A)
        return 1.0/A

def get_kappa(deflectx,deflecty,ratio=1,dunit=1):
    '''
    computes kappa from deflection matrices
    '''
    dDXdy,dDXdx = np.gradient(deflectx*ratio/dunit)
    dDYdy,dDYdx = np.gradient(deflecty*ratio/dunit)
    return 0.5*(dDXdx+dDYdy)

def get_gamma1(deflectx,deflecty,ratio=1,dunit=1):
    '''
    computes first component of gamma from deflection matrices
    '''
    dDXdy,dDXdx = np.gradient(deflectx*ratio/dunit)
    dDYdy,dDYdx = np.gradient(deflecty*ratio/dunit)
    return 0.5*(dDXdx-dDYdy)

def get_gamma2(deflectx,deflecty,ratio=1,dunit=1):
    '''
    computes second component of gamma from deflection matrices
    '''
    dDXdy,dDXdx = np.gradient(deflectx*ratio/dunit)
    return dDXdy

def get_gamma(deflectx,deflecty,ratio=1,dunit=1):
    '''
    computes gamma from deflection matrices
    '''
    dDXdy,dDXdx = np.gradient(deflectx*ratio/dunit)
    dDYdy,dDYdx = np.gradient(deflecty*ratio/dunit)
    gamma1 = 0.5*(dDXdx-dDYdy)
    gamma2 = dDXdy
    return np.sqrt(gamma1**2+gamma2**2)

def get_maps(deflectx,deflecty,ratio=1,dunit=1,return_all=False):
    '''
    computes kappa and gamma (and gamma1,gamma2) from deflection matrices
    '''
    dDXdy,dDXdx = np.gradient(deflectx*ratio/dunit)
    dDYdy,dDYdx = np.gradient(deflecty*ratio/dunit)
    kappa = 0.5*(dDXdx+dDYdy)
    gamma1 = 0.5*(dDXdx-dDYdy)
    gamma2 = dDXdy
    gamma = np.sqrt(gamma1**2+gamma2**2)
    if return_all:
        return kappa,gamma,gamma1,gamma2
    else:
        return kappa,gamma

def fermat_potential(sx,sy,deflectx,deflecty,psi,zl,zs,ratio=1,cosmo=FlatLambdaCDM(Om0=0.3,H0=70),dx=0,dy=0,ps=0.03*u.arcsec):
    '''
    computes the time delays in seconds in the image plane originating from a single source plane position
    '''

    thetaX,thetaY = np.meshgrid(np.arange(psi.shape[1])+1,np.arange(psi.shape[0])+1)
    sep = (np.sqrt((thetaX-sx)**2+(thetaY-sy)**2))*ps

    dls = cosmo.angular_diameter_distance_z1z2(zl,zs)
    ds = cosmo.angular_diameter_distance(zs)
    dl = cosmo.angular_diameter_distance(zl)

    fermat = ((1+zl)/const.c * (dl*ds)/dls * (0.5*sep**2 - psi*(u.arcsec)**2*ratio)).to('rad**2 s')

    return fermat

import numpy,import astropy,from astropy


from __future__ import print_function
import numpy as np
import sklearn.preprocessing as skpreprocess
from scipy import linalg
__author__ = 'Joseph Robinson'

"""
Utilities for feature encodings.
"""
def cov_matrix(data):
    mean_vec = np.mean(data, axis=0)
    return (data - mean_vec).T.dot((data - mean_vec)) / (data.shape[0] - 1)

def normalize(data):
    """
    # normalize and mean shift (i.e., (features - mu(features))/sigma(features))
    :param data:
    :return: normalized data
    """
    return skpreprocess.StandardScaler().fit_transform(data)

def substract_mean(data):
    """
    Substracts mean for every row in data, yeilding a mean equal to 0.

    """
    mean = np.mean(data, axis=0)
    return data - mean

def pca(data, dims_rescaled_data=200):
    """
    returns: data transformed in 2 dims/columns + regenerated original data
    pass in: data as 2D NumPy array
    """
    # m, n = data.shape
    # mean center the data
    data = substract_mean(data)
    # data -= data.mean(axis=0)
    # calculate the covariance matrix
    cov = np.cov(data, rowvar=False)
    # calculate eigenvectors & eigenvalues of the covariance matrix
    # use 'eigh' rather than 'eig' since R is symmetric,
    # the performance gain is substantial
    evals, evecs = linalg.eigh(cov)
    # sort eigenvalue in decreasing order
    idx = np.argsort(evals)[::-1]
    evecs = evecs[:,idx]
    # sort eigenvectors according to same index
    evals = evals[idx]
    # select the first n eigenvectors (n is desired dimension
    # of rescaled data array, or dims_rescaled_data)
    evecs = evecs[:, :dims_rescaled_data]
    # carry out the transformation on the data using eigenvectors
    # and return the re-scaled data, eigenvalues, and eigenvectors
    return np.dot(evecs.T, data.T).T, evals, evecs


import numpy as np
import scipy as sp
import pandas as pd

def calculate_kolmogorov_smirnov_distance(mu0, mu1, sigma0, sigma1):
    if np.isnan(mu0) or np.isnan(mu1) or np.isnan(sigma0) or np.isnan(sigma1):
        KS = np.nan

    else:
        a = 1/(2*sigma0**2) - 1/(2*sigma1**2)
        b = mu1/sigma1**2 - mu0/sigma0**2
        c = mu0**2/(2*sigma0**2) - mu1**2/(2*sigma1**2) - np.log(sigma1/sigma0)

        if a == 0:
            if b == 0:
                KS = 0
            else:
                x_sup = -c/b
                KS = abs(sp.stats.norm.cdf(x_sup, mu0, sigma0) - sp.stats.norm.cdf(x_sup, mu1, sigma1))
        else:
            x_1 = (-b + (b**2-4*a*c)**0.5) / (2*a)
            x_2 = (-b - (b**2-4*a*c)**0.5) / (2*a)

            KS_1 = abs(sp.stats.norm.cdf(x_1, mu0, sigma0) - sp.stats.norm.cdf(x_1, mu1, sigma1))
            KS_2 = abs(sp.stats.norm.cdf(x_2, mu0, sigma0) - sp.stats.norm.cdf(x_2, mu1, sigma1))

            KS = max(KS_1,KS_2)

    return KS

def sort_portfolios(returns, ranking_variable, n_portfolios, lags=1, return_assets=False):
    # align periods
    sorting_variable = ranking_variable.shift(lags)

    # set up parameters
    [t,n] = returns.shape
    include = returns.notna() & sorting_variable.notna()
    n_period = include.sum(axis=1)

    # sort assets
    returns_include = returns[include]
    sorting_variable[~include] = np.nan
    cutoff_ranks = np.dot(n_period.values.reshape(t,1)/n_portfolios,np.arange(n_portfolios+1).reshape(1,n_portfolios+1)).round()
    asset_ranks = sorting_variable.rank(axis=1)

    # set up output frames
    portfolio_returns = pd.DataFrame(index=returns.index,columns=range(1,n_portfolios+1))
    portfolio_assets = pd.DataFrame(index=returns.index,columns=range(1,n_portfolios+1))
    portfolio_mapping = pd.DataFrame(index=returns.index, columns=returns.columns)

    # calculate outputs
    for i_portfolio in range(0,n_portfolios):
        lower = cutoff_ranks[:,i_portfolio].reshape(t,1).repeat(n, axis=1)
        upper = cutoff_ranks[:,i_portfolio+1].reshape(t,1).repeat(n, axis=1)
        portfolio_returns[i_portfolio+1] = returns_include[(asset_ranks>lower) & (asset_ranks<=upper)].mean(axis=1)
        portfolio_assets[i_portfolio+1] = ((asset_ranks>lower) & (asset_ranks<=upper)).sum(axis=1)
        portfolio_mapping[(asset_ranks>lower) & (asset_ranks<=upper)] = i_portfolio

    # outputs
    if return_assets == False:
        return portfolio_returns
    else:
        return portfolio_returns, portfolio_assets, portfolio_mapping

def double_sort_portfolios(returns, ranking_variable_1, ranking_variable_2, n_portfolios_1, n_portfolios_2, lags_1=1, lags_2=1, return_assets=False):
    # identify missing values
    exclude = returns.isna() | ranking_variable_1.shift(lags_1).isna() | ranking_variable_2.shift(lags_2).isna()
    returns[exclude] = np.nan

    # first sort
    portfolio_mapping_1 = sort_portfolios(returns, ranking_variable_1, n_portfolios_1, lags_1, return_assets=True)[2]

    # second sorts
    portfolio_mapping_2 = pd.DataFrame(0, index=portfolio_mapping_1.index, columns=portfolio_mapping_1.columns)
    for i_portfolio_2 in range(0,n_portfolios_2):
        subportfolio_returns = returns[portfolio_mapping_1 == i_portfolio_2]
        portfolio_mapping_2 += (sort_portfolios(subportfolio_returns, ranking_variable_2, n_portfolios_2, lags_2, return_assets=True)[2]).fillna(0)
    portfolio_mapping_2[exclude] = np.nan

    # combined sort
    portfolio_mapping = portfolio_mapping_1*n_portfolios_1 + portfolio_mapping_2

    # set up output frames
    portfolio_returns = pd.DataFrame(index=returns.index,columns=[str(i_portfolio_1+1)+','+str(i_portfolio_2+1) for i_portfolio_1 in range(0,n_portfolios_1) for i_portfolio_2 in range(0,n_portfolios_2)])
    portfolio_assets = pd.DataFrame(index=returns.index,columns=[str(i_portfolio_1+1)+','+str(i_portfolio_2+1) for i_portfolio_1 in range(0,n_portfolios_1) for i_portfolio_2 in range(0,n_portfolios_2)])

    # calculate outputs
    for i_portfolio_all in range(0,n_portfolios_1*n_portfolios_2):
        portfolio_returns.iloc[:,i_portfolio_all] = returns[portfolio_mapping == i_portfolio_all].mean(axis=1)
        portfolio_assets.iloc[:,i_portfolio_all] = (portfolio_mapping == i_portfolio_all).sum(axis=1)

    # outputs
    if return_assets == False:
        return portfolio_returns
    else:
        return portfolio_returns, portfolio_assets, portfolio_mapping

def double_sort_portfolios_simultaneously(returns, ranking_variable_1, ranking_variable_2, n_portfolios_1, n_portfolios_2, lags_1=1, lags_2=1, return_assets=False):
    # identify missing values
    exclude = returns.isna() | ranking_variable_1.shift(lags_1).isna() | ranking_variable_2.shift(lags_2).isna()
    returns[exclude] = np.nan

    # first sort
    portfolio_mapping_1 = sort_portfolios(returns, ranking_variable_1, n_portfolios_1, lags_1, return_assets=True)[2]

    # second sorts
    portfolio_mapping_2 = sort_portfolios(returns, ranking_variable_2, n_portfolios_2, lags_2, return_assets=True)[2]

    # combined sort
    portfolio_mapping = portfolio_mapping_1*n_portfolios_1 + portfolio_mapping_2

    # set up output frames
    portfolio_returns = pd.DataFrame(index=returns.index,columns=[str(i_portfolio_1+1)+','+str(i_portfolio_2+1) for i_portfolio_1 in range(0,n_portfolios_1) for i_portfolio_2 in range(0,n_portfolios_2)])
    portfolio_assets = pd.DataFrame(index=returns.index,columns=[str(i_portfolio_1+1)+','+str(i_portfolio_2+1) for i_portfolio_1 in range(0,n_portfolios_1) for i_portfolio_2 in range(0,n_portfolios_2)])

    # calculate outputs
    for i_portfolio_all in range(0,n_portfolios_1*n_portfolios_2):
        portfolio_returns.iloc[:,i_portfolio_all] = returns[portfolio_mapping == i_portfolio_all].mean(axis=1)
        portfolio_assets.iloc[:,i_portfolio_all] = (portfolio_mapping == i_portfolio_all).sum(axis=1)

    # outputs
    if return_assets == False:
        return portfolio_returns
    else:
        return portfolio_returns, portfolio_assets, portfolio_mapping

def standardise_dataframe(df, ax=0):
    df = df.subtract(df.mean(axis=ax), axis=ax)
    df = df.divide(df.std(axis=ax), axis=ax)
    return df

def total_return_from_returns(returns): #returns total return of a return series
    return (returns + 1).prod(skipna=False) - 1

def fill_inside_na(df, meth = 'zero', lim = None): #fills missing values in the middle of a series (but not at beginning and end)
    nans = pd.DataFrame(df.bfill().isna() | df.ffill().isna())
    if meth is 'zero':
        df = df.fillna(0)
    else:
        df = df.fillna(method = meth, limit = lim)
    df[nans] = np.nan
    return df

def realised_volatility_from_returns(returns):
    return ((returns**2).mean(skipna=False))**0.5

def normal_central_moment(sigma, moment):

    '''Central moments of a normal distribution with any mean.
    Note that the first input is a standard deviation, not a variance.'''

    if moment % 2 == 1:
        #odd moments of a normal are zero
        normal_moment = 0
    else:
        #even moments are given by sigma^n times the double factorial
        normal_moment = sigma**moment * sp.special.factorialk(moment-1, 2)
    return normal_moment

def calculate_steady_state_probabilities(transition_matrix):
    dim = np.array(transition_matrix).shape[0]
    q = np.c_[(transition_matrix-np.eye(dim)),np.ones(dim)]
    QTQ = np.dot(q, q.T)
    steady_state_probabilities = np.linalg.solve(QTQ,np.ones(dim))
    return steady_state_probabilities

def iterate_markov_chain(state_probabilities, transition_matrix, steps):
    new_state = np.dot(state_probabilities, np.linalg.matrix_power(transition_matrix, steps))
    return new_state

def calculate_binary_entropy(probability):
    entropy = -1 * (probability*np.log2(probability) + (1-probability)*np.log2(1-probability))
    return abs(entropy)

def calculate_columnwise_autocorrelation(df, lag=1):
    autocorr = pd.Series(index=df.columns)
    for column in df.columns:
        autocorr[column] = df[column].astype('float64').autocorr(lag=lag)
    return autocorr

def export_df_to_latex(df, filename='file.tex', **kwargs):
    if filename[-4:] != '.tex':
        filename += '.tex'
    df.to_latex(buf=filename, multirow=False, multicolumn_format ='c', na_rep='', escape=False, **kwargs)

def calculate_shannon_entropy(vector):
    '''
    calculate the shannon entropy measure from a vector of probabilities
    '''

    n = len(vector)
    entropy = 0
    for v in vector:
        if v == 0:
            pass
        else:
            entropy += v*np.log(v)/np.log(n)
    return abs(entropy)

def shrink_outliers(Series, alpha=1.96, lamb=1):
    '''
    This function shrinks outliers in a series towards the threshold values.
    The parameter alpha defines the threshold values as a multiple of one sample standard deviation.
    The parameter lamb defines the degree of shrinkage of outliers towards the thresholds.

    The transformation is as follows:
    if the z score is inside the thresholds f(x)=x
    if it is above the upper threshold f(x)=1+1/lamb*ln(x+(1-lamb)/lamb)-1/lamb*ln(1/lamb)
    if it is below the lower threshold f(x)=-1-1/lamb*ln(-x+(1-lamb)/lamb)+1/lamb*ln(1/lamb)
    '''

    z_scores = (Series-Series.mean())/Series.std()
    adjusted_scores = z_scores/alpha
    adjusted_scores[adjusted_scores.values>1] = 1+1/lamb*np.log(adjusted_scores[adjusted_scores.values>1]+(1-lamb)/lamb)-1/lamb*np.log(1/lamb)
    adjusted_scores[adjusted_scores.values<-1] = -1-1/lamb*np.log((1-lamb)/lamb-adjusted_scores[adjusted_scores.values<-1])+1/lamb*np.log(1/lamb)
    new_z_scores = adjusted_scores*alpha
    new_series = new_z_scores*Series.std()+Series.mean()
    return new_series

def get_unique_values_from_list_of_lists(input_list):
    full_list = [j for i in input_list for j in i]
    short_list = list(dict.fromkeys(full_list))
    return short_list


from sympy import pi
from math import *
from cmath import *

# General expression for sinusoid:
# v(t) = Vm*sin(w*t + phi)
# where,
# phi = phase

# If comparing two sinusoids phase1 != phase2 then "out of phase".
# One sinusoid leads or lags by phase in radians or degrees.
# If phase difference = 0 then "in phase"

# Trigonometric identities:
# sin(A +/- B) = sinAcosB +/- cosAsinB
# cos(A +/- B) = cosAcosB -/+ sinAsinB

# With these identities, it is easy to show that:
# sin(wt +/- 180 degrees) = -sin(wt)
# cos(wt +/- 180 degrees) = -cos(wt)
# sin(wt +/- 90 degrees) = +/-cos(wt)
# cos(wt +/- 90 degrees) = -/+sin(wt)

# Adding
# A*cos(w*t) + B*sin(w*t) = C*cos(w*t-phase)
# C = (A**2+B**2)**(1/2)
# phase = tan-1(B/A)

# Example:
# 3*cos(wt) - 4*sin(w*t) = 5*cos(wt + 53.1)

def T(w):
    """
    Period
    w = angular frequency

    Theory:
    v(t + T) = v(t)
    """
    T = 2 * pi / w
    return T

def f(w):
    """Cyclic frequency"""
    return T(w)

def w(f):
    """Angular frequency"""
    return 2*pi*f

# A complex number can be written in rectangular form as
# z = x + jy            RECTANGULAR FORM
# z = r*angle(phase)    POLAR FORM
# z = r*exp(j*phase)    EXPONENTIAL FORM
# j = (-1)**.5
# r = (x**2 + y**2)**(1/2)
# phi = tan-1(y/x)
# x = r*cos(phi)
# y = r*cos(phi)

# z = x + jy = r*angle(phi) = r*(cos(phi) + j*sin(phi))
# Addition and subtraction are better performed in rectangular form
# Multiplication and division are better done in polar form

# Addition:
# z1 + z2 = (x1 + x2) + j(y1 + y2)

# Subtraction
# z1 - z2 = (x1 - x2) + j(y1 - y2)

# Multiplication:
# z1*z2 = r1*r2 ang(phi1 + phi2)

# Division:
# z1/z2 = r1/r2 ang(phi1-phi2)

# Reciprocal
# 1/z = 1/r ang(-phi)

# Square Root
# sqrt(z) = sqrt(r) ang(phi/2)

# Complex Conjugate
# z* = x-j*y = r ang(-phi) = r*exp(-j*phi)

# Note:
# 1/j = -j
# exp(+/-j*phi) = cos(phi) +/- j*sin(phi)
# cos(phi) = Re( exp(j*phi))
# sin(phi) = Im(exp(j*phi))
# v(t) = Re(Vm*cos(w*t+phi))
# V is used as the phasor representation of v(t)

class signal:
    """
    Types: "rect" "polar_rad" "polar_deg"
    https://docs.python.org/2/reference/datamodel.html#emulating-numeric-types
    """
    def __init__(self, val=complex(0,0), format="rect"):
        """
        Types: "rect" "polar_rad" "polar_deg" "wave_deg" "wave_rad"

        Input:
            rect: val = complex(0,0)
            polar_rad = (A,phi_rad)
            polar_deg = (A, phi_deg)
            wave = (A, "cos", w, phi) or (A, "sin", w, phi)

        """

        if format == "rect":
            self.rect = val
        elif format == "polar_rad":
            self.polar_rad = val
        elif format == "polar_deg":
            self.polar_deg = val
        elif "wave" in format:
            self.wave_original = val
            self.wave_original_type = format
            phasor = ()
            val = list(val)
            if "deg" in format:
                val[3] = radians(val[3])
            if val[1] == "cos":
                phasor = self.__cos2Phasor(val[0], val[3])
            elif val[1] == "sin":
                phasor = self.__sin2Phasor(val[0], val[3])
            else:
                raise 'Not a valid sinusoid. Format must be (A, "cos", w, phi) or (A, "cos", w, phi)'
            self.polar_rad = (phasor[0], phasor[1])
        else:
            raise 'type must be: "rect" "polar_rad" "polar_deg"'

    @property
    def rect(self):
        return self._rect
    @rect.setter
    def rect(self, val):
        self._rect = val
        self._polar_rad = self.__rect2polar(self._rect)
        self._polar_deg = self.__polar_deg_view()

    @property
    def polar_rad(self):
        return self._polar_rad
    @polar_rad.setter
    def polar_rad(self, val):
        self._polar_rad = val
        self._polar_deg = self.__polar_deg_view()
        self._rect = self.__polar2rect(self._polar_rad)

    def __polar_deg_view(self):
        """
        Does not return actual polar as actual polar needs to be in radians.
        For viewing ONLY.
        """
        polar = self._polar_rad
        polar_fix = list()
        polar_fix.append(polar[0])
        polar_fix.append(degrees(polar[1]))
        return polar_fix

    @property
    def polar_deg(self):
        return self._polar_deg
    @polar_deg.setter
    def polar_deg(self, val):
        self._polar_deg = val
        self._polar_rad = self.__polar_rad_view()
        self._rect = self.__polar2rect(self._polar_rad)

    @property
    def sinusoid(self):
        return self._sinusoid
    @sinusoid.setter
    def sinusoid(self, val):
        self._sinusoid = val

    def __polar2rect(self, r,phi=0):
        """
        Output: class <complex>
        """
        if isinstance(r,tuple) or isinstance(r,list):
            return rect(r[0],r[1])
        return rect(r,phi)

    def __rect2polar(self,z):
        """
        Polar cannot do math.
        Output: class <tuple>
        """
        return polar(z)

    def __polar_rad_view(self):
        """
        Does not return actual polar as actual polar needs to be in radians.
        For viewing ONLY.
        """
        polar = self._polar_deg
        polar_fix = list()
        polar_fix.append(polar[0])
        polar_fix.append(radians(polar[1]))
        return polar_fix

    def __cos2Phasor(self, A,phi):
        """
        Format: A*cos(wt+phi)
        Output: [A, phi] which represents polar form A angle(phi)
        """
        if A < 0:
            return self.__cos2Phasor(-A, phi+radians(180))
        else:
            return A, phi
    def __sin2Phasor(self, A, phi):
        if A < 0:
            return self.__sin2Phasor(-A, phi+radians(180))
        else:
            return A, phi-radians(90)

    def sin_wave(self, format="rad"):
        if format == "deg":
            return f"{self._polar_rad[0]} sin (wt * {degrees(self.polar_rad[1]-radians(90))})"
        return f"{self._polar_rad[0]} sin (wt * {self.polar_rad[1]-radians(90)})"

    def cos_wave(self, format="rad"):
        if format == "deg":
            return f"{self._polar_rad[0]} cos (wt * {degrees(self.polar_rad[1])})"
        return f"{self._polar_rad[0]} cos (wt * {self.polar_rad[1]})"

    def __add__(self,other):
        return signal(self._rect + other._rect)
    def __radd__(self, other):
        return self.__add__(other)

    def __sub__(self,other):
        return signal(self._rect - other._rect)
    def __rsub__(self, other):
        #Doesn't work?
        return other - self._rect

    def __mul__(self,other):
        return signal(self._rect * other._rect)
    def __rmul__(self, other):
        return self.__mul__(other)

    def __truediv__(self,other):
        return signal(self._rect / other._rect)
    def __rtruediv__(self,other):
        # return signal(other / self._rect)
        return other / self._rect

    def __pow__(self,other):
        # return signal(other / self._rect)
        return signal(self.rect**(other))

    def sqrt(self):
        return signal(self.rect**(1/2))

## NOTE:
# DERIVATIVE
# dv/dt      ==      jwV
# time domain    frquency domain

# INTEGRAL
# integrate(v,t)    ==    V/jw
# time domain          frquency domain

if __name__ == "__main__":
    z1 = signal((40,50), format="polar_rad")
    z2 = signal((20,-30), format="polar_deg")


# -*- coding: utf-8 -*-
"""A set of functions that make it easy to play with soft inforamtion"""

import numpy as np
from numpy import pi, exp, sqrt, log

def normal(x, mean, std):
    """
    Normal distribution

    Args:
        x: input value
        mean: the mean of the distribution
        std: standard deviation
    """
    return 1/(std*sqrt(2*pi))*exp(-(x-mean)**2/(2*std**2))

def dist(xa, ya, xb, yb):
    """Return the distance between a and b"""
    a = np.array((xa, ya))
    b = np.array((xb, yb))
    return np.linalg.norm(a-b)

def angle(xa, ya, xb, yb, xc, yc):
    """returns the angle of the points a, b and c"""
    vector1=np.array((xa-xb,ya-yb))
    vector2=np.array((xc-xb,yc-yb))
    unitvector1=vector1/np.linalg.norm(vector1)
    unitvector2=vector2/np.linalg.norm(vector2)
    prodscal=np.dot(unitvector1,unitvector2)
    if prodscal<-1:
        prodscal=-1
    elif prodscal>1:
        prodscal=1
    return np.arccos(prodscal)*180/pi

def log_normal(x, mean, std):
    """
    Natural logarithm of a normal distribution

    Args:
        x: input value
        mean: the mean of the distribution
        std: standard deviation
    """
    return -log(std) - (log(2) + log(pi))/2 - (x-mean)**2/(2*std**2)

def logprob_angle(xa, ya, xb, yb, xc, yc, measured_angle, std):
    """logprob that a, b and c are in (xa,ya),(xb,yb),(xc,yc) under the measured angle.

    Args:
        xa: abscissa of point a
        ya: ordinate of point a
        xb: abscissa of point b
        yb: ordinate of point b
        measured_dist: measured distance between a and b
        std: standard deviation of the measurement"""
    points_angle = angle(xa,ya,xb,yb,xc,yc)
    return log_normal(points_angle, measured_angle, std)

def logprob_distance(xa, ya, xb, yb, measured_dist, std):
    """
    Logprob that a and b are in (xa, ya), (xb, yb) under the measured distance.

    Args:
        xa: abscissa of point a
        ya: ordinate of point a
        xb: abscissa of point b
        yb: ordinate of point b
        measured_dist: measured distance between a and b
        std: standard deviation of the measurement
    """
    points_dist = dist(xa, ya, xb, yb)
    return log_normal(points_dist, measured_dist, std)

def make_logprob_angle(idx_a, idx_b, idx_c, measured_angle, std):
    """
    Make the function that return the logprob of positions under the measured distance

    Args:
        idx_a: index of point a
        idx_b: index of point b
        idx_c : index of point c
        measured_angle : measured angle between a b and c
        std: standard deviation of the measurement
    """
    def func(points):
        """
        Return the logprob of positions under the measured distance

        Args:
            points: estimated positions ([[x0, y0, h0], [x1, y1, h1], ...])
        """
        xa, ya = points[idx_a]
        xb, yb = points[idx_b]
        xc, yc = points[idx_c]
        return logprob_angle(xa, ya, xb, yb, xc, yc, measured_angle, std)

    return func

def make_logprob_distance(idx_a, idx_b, measured_dist, std):
    """
    Make the function that return the logprob of positions under the measured distance

    Args:
        idx_a: index of point a
        idx_b: index of point b
        measured_dist: measured distance between a and b
        std: standard deviation of the measurement
    """
    def func(points):
        """
        Return the logprob of positions under the measured distance

        Args:
            points: estimated positions ([[x0, y0, h0], [x1, y1, h1], ...])
        """
        xa, ya = points[idx_a]
        xb, yb = points[idx_b]
        return logprob_distance(xa, ya, xb, yb, measured_dist, std)

    return func

def make_logprob_position(idx, measured_x, measured_y, std):

    """
    Returns the soft position information function that can be applied to the estimated positions

    Args:
        idx: index of point
        measured_x: measured abscissa
        measured_y: measured ordinate
        std: standard deviation of the measurement

    Returns:
        (function): the logSI function that takes the estimated positions of the points as input.
    """
    def func(points):
        """
        Returns log-prob of the estimated positions under the measured position.

        Args:
            points: estimated positions ([[x0, y0, h0], [x1, y1, h1], ...])
        """
        estimated_x, estimated_y = points[idx]
        return logprob_distance(estimated_x, estimated_y, measured_x, measured_y, 0, std)

    return func


import numpy as np
from collections import namedtuple

def accuracy(y, yhat):
    return np.mean(y == yhat)

def toLabel(yhat):
    return yhat > 0.5

def mse(y, yhat):
    return 0.5 * np.mean((yhat - y) ** 2)

def yhat(X, w, b):
    return X.T.dot(w) + b

def gradient_w(X, y, w, b):
    return X.dot(yhat(X, w, b) - y) / X.shape[1]

def gradient_b(X, y, w, b):
    return np.mean(yhat(X, w, b) - y)

def error(X, y, w, b):
    return mse(y, yhat(X, w, b))

def smile_classifier(trainingFaces, trainingLabels):
    learning_rate = .005
    tolerance = .000001

    X = trainingFaces.T
    y = trainingLabels
    w = .01 * np.random.rand(X.shape[0])
    b = .01 * np.random.rand()

    prev_error = error(X, y, w, b)

    is_training = True

    while is_training:
        w = w - learning_rate * gradient_w(X, y, w, b)
        b = b - learning_rate * gradient_b(X, y, w, b)
        current_error = error(X, y, w, b)

        if abs(prev_error - current_error) < tolerance:
            is_training = False
        else:
            prev_error = current_error

    def predict(faces):
        return yhat(faces.T, w, b)

    Classifier =  namedtuple("Classifier", ["w", "b", "predict"])
    return Classifier(w, b, predict)

def detect_smile(trainingFaces, trainingLabels, testingFaces, testingLabels):

    print("Start training the model")
    clf = smile_classifier(trainingFaces, trainingLabels)

    print("Training MSE: %f" % mse(trainingLabels, clf.predict(trainingFaces)))
    print("Testing MSE: %f" % mse(testingLabels, clf.predict(testingFaces)))

    print()
    print("Training Accuracy: %f" % accuracy(trainingLabels, toLabel(clf.predict(trainingFaces))))
    print("Testing Accuracy: %f" % accuracy(testingLabels, toLabel(clf.predict(testingFaces))))

def main():
    trainingFaces = np.load("trainingFaces.npy")
    trainingLabels = np.load("trainingLabels.npy")
    testingFaces = np.load("testingFaces.npy")
    testingLabels = np.load("testingLabels.npy")
    detect_smile(trainingFaces, trainingLabels, testingFaces, testingLabels)

if __name__ == "__main__":
    main()


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

n1 = 20
n2 = 50

m1 = -2.6
m2 = 3.3

std1 = 2
std2 = 3

data = []

d1 = []
for i in range(n1):
    x1 = np.random.normal(m1, std1)

    d1.append([x1, 0])

d1.sort()
data += d1

d2 = []
for i in range(n2):
    x2 = np.random.normal(m2, std2)

    d2.append([x2, 1])

d2.sort()
data += d2

data = np.array(data)

# MLE Estimates
em1 = 0
em2 = 0

for x, c in data:
    if c == 0:
        em1 += x
    else:
        em2 += x

em1 /= n1
em2 /= n2

evar1 = 0
evar2 = 0

for x, c in data:
    if c == 0:
        evar1 += (x - em1) ** 2
    else:
        evar2 += (x - em2) ** 2

evar1 /= n1
evar2 /= n2

print("MLE mean1 = %s, real mean1 = %s" % (em1, m1))
print("MLE mean2 = %s, real mean2 = %s" % (em2, m2))

print("MLE var1 = %s, real var1 = %s" % (evar1, std1 ** 2))
print("MLE var2 = %s, real var2 = %s" % (evar2, std2 ** 2))

# Priors
pc1 = n1 / (n1 + n2)
pc2 = n2 / (n1 + n2)

points = [p for p in np.arange(-10, 10, 0.1)]

norm_density = lambda point, m, var: 1 / (math.sqrt(var) * np.sqrt(2 * np.pi)) * np.exp(-(point - m) ** 2 / (2 * math.sqrt(var) ** 2))

plt.plot(points, [norm_density(p, em1, evar1) for p in points], color='r')
plt.plot(points, [norm_density(p, em2, evar2) for p in points], color='b')

posterior = lambda point, likelihood, prior: (likelihood * prior) / (norm_density(point, em1, evar1) * pc1 + norm_density(point, em2, evar2) * pc2)

plt.plot(points, [posterior(p, norm_density(p, em1, evar1), pc1) for p in points], color='y')
plt.plot(points, [posterior(p, norm_density(p, em2, evar2), pc2) for p in points], color='g')

plt.show()


import numpy as np

from math import log

def ndcg(search_ranks, groundtruth_labels, top_k=10, ver='1', get_mean=True):

    calc_gain = (lambda rel: rel) if ver.startswith('1') else (lambda rel: (2**rel-1))

    ndcgs = []

    for q_id, groundtruth in groundtruth_labels.items():

        if groundtruth['idcg'] is None or len(groundtruth['idcg'])==0:
            continue

        if q_id not in search_ranks:
            ndcgs.append(np.zeros(top_k))
            continue

        gain = [calc_gain(groundtruth['docs'].get(doc_id, 0.)) for doc_id, doc_rel in search_ranks[q_id][:top_k]]

        dcg = [gain[0]]
        for i in range(1, top_k):
            dcg.append(gain[i]/(log(i+2, 2)) + dcg[i-1])

        ndcg = np.array(dcg) / np.array(groundtruth['idcg'])[:top_k]

        ndcgs.append(ndcg)

    ndcgs = np.array(ndcgs)

    return ndcgs.mean(axis=0) if get_mean else ndcgs


import numpy as np
import matplotlib.pyplot as plt

# define fitting function
def ChargeProcess(t, V0, t0, tau):
    return V0 * (1-np.exp(-(t-t0)/tau))

# define fitting function
def DischargeProcess(t, V0, t0, tau):
    return V0 * (np.exp(-(t-t0)/tau))

# read data from file
t, v = np.loadtxt('voltageLog.txt', delimiter=' ', unpack = True)

# plot of the experimental data
plt.plot(t, v, 'o', color='blue', markersize = 3)
plt.axhline(color = 'gray', zorder = -1)
plt.axvline(color = 'gray', zorder = -1)
plt.xlabel('time  $t$ (s)')
plt.ylabel('voltage $V_C$ (V)')
plt.text(27, 1.8, 'R = 10 k$\Omega$'+'\n'+'C = 100 $\mu$F')

# model curve for the charge process
t_fit_charge = np.linspace(0, 20, 128)
V0, t0, tau = 3.2915, 0, 1.1687
v_fit_charge = ChargeProcess(t_fit_charge, V0, t0, tau)
plt.plot(t_fit_charge, v_fit_charge, color='green')
#plt.draw()

# model curve for the discharge process
t_fit_discharge = np.linspace(20.01, 40, 128)
V0, t0, tau = 3.3, 20.0053, 1.2019
v_fit_discharge = DischargeProcess(t_fit_discharge, V0, t0, tau)
plt.plot(t_fit_discharge, v_fit_discharge, color='green')
#plt.draw()

plt.savefig('plot_voltage_and_fit.pdf')
#plt.draw()
plt.show()


import numpy as np
from scipy.spatial import distance_matrix

def distance2(A, B):

    M = A.shape[0]
    N = B.shape[0]

    A_dots = (A*A).sum(axis=1).reshape((M, 1))*np.ones(shape=(1, N))
    B_dots = (B*B).sum(axis=1)*np.ones(shape=(M, 1))
    D_squared = A_dots + B_dots - 2*A.dot(B.T)

    zero_mask = np.less(D_squared, 0.0)
    D_squared[zero_mask] = 0.0
    # return distance_matrix(A, B)
    return np.sqrt(D_squared)


from collections import OrderedDict
import numpy as np
from bokeh.plotting import figure
import pandas as pd

import pprint
pp = pprint.PrettyPrinter(indent=4)

def get_quantiles(data):
    # data is a list of lists
    final = []
    for i in data:
        q1 = np.percentile(i, 25)
        q2 = np.percentile(i, 50)
        q3 = np.percentile(i, 75)
        iqr = q3 - q1
        final.append((q1 - 1.5*iqr, q3 + 1.5*iqr,q1, q2,  q3,  [x for x in i if x < q1 - (1.5 * iqr) or x >q3 + (1.5 * iqr)  ]))
    return final

def make_box_raw(p, cats, upper, lower, q1, q2, q3, out):
    p.segment(cats, lower, cats, upper)
    p.vbar(cats, 0.7, q1, q2, fill_color="#E08E79", line_color="black")
    p.vbar(cats, 0.7, q2, q3, fill_color="#3B8686", line_color="black")
    p.rect(cats, lower, 0.2, 0.01, line_color="black")
    p.rect(cats, upper, 0.2, 0.01, line_color="black")
    for k, v in out.items():
        for point in v:
            p.circle([k], [point], size=6, color="#F38630", fill_alpha=0.6)
    return p

def zip_data(cats, data):
    zip_data = list(zip(*data))
    outs = OrderedDict()
    for counter, i in enumerate(cats):
        if len(zip_data[5][counter]) != 0:
            outs[i] = zip_data[5][counter]
    return zip_data, outs

def box_plot(data, cats = None, p = None, df_type = None):
    """
    Simple List
    ============
    cats: a list of labels
    data: a list of lists
    Example:
    cats = ['a', 'b']
    data = [[1, 2, 3], [4, 5, 6]]

    Data Frame
    ============
    data = df, df_type = 'index'
    OR
    data = df, df_type = 'columns'

    Pandas Series
    ==============
    data =  [df['s1'], df['s2'], df['s3']]

    Returns: p, the figure()
    """
    if isinstance(data, list) and isinstance(data[0], pd.core.series.Series ):
        cats = []
        for i in data:
            cats.append(i.name)
    elif isinstance(data, pd.core.frame.DataFrame):
        assert df_type != None
        if df_type == 'index':
            cats = data.index.to_list()
        elif df_type == 'columns':
            cats = data.columns.to_list()
        else:
            raise ValueError('df_type must be "columns" or "index')
        if df_type == 'index':
            _data = []
            for index, rows in data.iterrows():
                _data.append(rows.values)
            data = _data
        elif df_type == 'columns':
            _data = []
            names = list(data)
            for i in list(data):
                _data.append(data[i].values)
            data = _data
    else:
        assert len(cats) == len(data)
        assert isinstance(cats, list)
        assert isinstance(data, list)
    if not p:
        p = figure(tools="save", background_fill_color="#EFE8E2", x_range=cats)
    f, outs = zip_data(cats, get_quantiles(data))
    return make_box_raw(p, cats, f[0], f[1], f[2], f[3], f[4], outs)


"""
Cameron Knight
Code based on Intro and data section od the Machine learning series by Sentdex
https://www.youtube.com/channel/UCfzlCWGWYyIQ0aLC5w48gBQ
Description: regression on randomly generated data
"""

from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
import random

style.use('fivethirtyeight')

# Sample Data------------------------------------------------------------------------------
# xs = np.array([1,2,3,4,5,6], dtype = np.float64) # Data x values converted to num py array
# Explicitly set the data type to 64 bit float
# ys = np.array([5,2,5,3,7,6], dtype = np.float64) # data y values converted to num py array
# Explicitly set the data type to 64 bit float

def create_dataset(hm, variance, step=2, correlation=False):
    """
    Genorates a random set of data of lenth hm with the max variance of varance and a pos correlation corespondiong to
    the slope of step if correlation  == "pos" and negative if correlation = "neg" otherwise no correlation
    """

    val = 1
    ys = []
    for i in range(hm):
        y = val + random.randrange(-variance, variance)
        ys.append(y)
        if (correlation and correlation == 'pos'):
            val += step
        if (correlation and correlation == 'neg'):
            val -= step

    xs = [i for i in range(len(ys))]
    return np.array(xs, dtype=np.float64), np.array(ys, dtype=np.float64)

def best_fit_slope_and_intercept(xs, ys):
    """
    Calculates the slope of the best fit and intecept
    """
    m = (((mean(xs) * mean(ys)) - mean(xs * ys)) /
         ((mean(xs) ** 2) - mean(xs ** 2)))  # mean definition of linear regression

    b = mean(ys) - m * mean(xs)  # calculates the y intecept based on the slope

    return m, b

def squared_error(ys_orig, ys_line):
    return sum((ys_line - ys_orig) ** 2)

def coefficient_of_determ(ys_orig, ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]
    squared_error_reg = squared_error(ys_orig, ys_line)
    squared_error_mean = squared_error(ys_orig, y_mean_line)
    return 1 - (squared_error_reg / squared_error_mean)

xs, ys = create_dataset(40, 5, 2, correlation='pos')
m, b = best_fit_slope_and_intercept(xs, ys)

regression_Line = [m * x + b for x in xs]

predictPerc = .1  # Percent of the data out
predict_x = max(xs) * (1 + predictPerc)  # predict data predictPerc% out
predict_y = m * predict_x + b  # Predicts using regression line

r_sqr = coefficient_of_determ(ys, regression_Line)

print("r^2", r_sqr)

# Show Graph using matplotlib--------------------------------------------------------------
plt.scatter(xs, ys)
plt.scatter(predict_x, predict_y, s=50, color='g')
plt.plot(xs, regression_Line)
plt.show()


"""Compute the cosine similarity matrix of A.

https://stackoverflow.com/questions/17627219/whats-the-fastest-way-in-python-to-calculate-cosine-similarity-given-sparse-mat
"""
import numpy as np

def cos_matrix2(mat1: np.ndarray, mat2: np.ndarray = None) -> np.ndarray:
    """Compute the cosine similarity matrix of mat1, mat2: mat1 * mat2.T.

    Args:
        mat1: np.asarray
        mat2: [Optional], if not present mat2 = mat1
    Returns
        cosine similarity
    """
    if not isinstance(mat1, np.ndarray):
        mat1 = np.asarray(mat1, dtype=np.float32)

    if mat2 is None:
        mat2 = mat1.copy()

    if not isinstance(mat2, np.ndarray):
        mat2 = np.asarray(mat2, dtype=np.float32)

    if mat1.shape[1] != mat2.shape[1]:
        print("shape mismatch: %s, %s", mat1.shape, mat2.shape)
        raise SystemError(1)
    cosine = np.dot(mat1, mat2.T)

    norm1 = np.linalg.norm(mat1, axis=1)
    norm2 = np.linalg.norm(mat2, axis=1)

    # if not (norm1 and norm2): return 0

    size1 = norm1.size
    size2 = norm2.size
    norm_mat = np.dot(norm1.reshape(size1, 1), norm2.reshape(1, size2))

    for idx in range(size1):
        for jdx in range(size2):
            if norm_mat[idx, jdx] == 0:
                cosine[idx, jdx] = 0.0
            else:
                cosine[idx, jdx] = cosine[idx, jdx] / norm_mat[idx, jdx]
    return cosine


# -*- coding: utf-8 -*-
"""
Created on Wed Jul  7 10:01:26 2021

@author: Administrator
"""

import numpy as np
import main_algorithm as mydist
import gudhi as gd
import DI_distance as NS
import time

np.random.seed(1)
num_points = 32 # change this parameter to 64, 128, ... to get different number of points
low_end = 0.01 # change this parameter to 0.1, 1, ... to get different scale
high_end = 10 # change this parameter to 100, 1000, ... to get different scale

Xx = np.random.uniform(low = low_end, high = high_end, size = num_points)
Xy = Xx + np.random.uniform(low = low_end, high = high_end, size= num_points)
X = np.transpose(np.vstack((Xx, Xy)))

Yx = np.random.uniform(low = low_end, high = high_end, size = num_points)
Yy = Yx + np.random.uniform(low = low_end, high = high_end, size= num_points)
Y = np.transpose(np.vstack((Yx, Yy)))

ori = gd.bottleneck_distance(X, Y)

# DI distance
my_start = time.time()
shiftInv, bottleneckDistance, minBottleneckDistance = NS.myDistance(X, Y, 100)
my_end = time.time()
print('NS time is ', my_end-my_start)
# Don distance

X = [tuple(x) for x in X.tolist()]
Y = [tuple(x) for x in Y.tolist()]

don_start = time.time()
DShift = mydist.shifted_bottleneck_distance(X,Y, analysis = False)
don_end = time.time()

print('Don distance is ', DShift, '\nDon time is ', don_end-don_start)

DI distance and dissimilarity/Don_vs_NS_compare.py


import numpy as np
from sympy import cos, simplify, symbols

from einsteinpy.symbolic import GenericVector, MetricTensor

def euclidean_space_metric():
    symbolstr = "e1 e2"  # let angle between e1 & e2 be theta
    syms = symbols(symbolstr)
    th = symbols("theta")
    list2d = np.zeros((2, 2), dtype=int).tolist()
    # defining the metric tensor when axis are not orthogonal
    list2d[0][0] = list2d[1][1] = 1
    list2d[1][0] = list2d[0][1] = cos(th)
    metric = MetricTensor(list2d, syms, config="ll")
    return metric

def test_GenericVector_change_config_theoretical_test():
    # https://en.wikipedia.org/wiki/Covariance_and_contravariance_of_vectors#Definition
    a, b, th = symbols("a b theta")
    metric = euclidean_space_metric()
    # defining a contravariant vector
    cnvec = GenericVector([a, b], metric.syms, config="u", parent_metric=metric)
    covec = cnvec.change_config("l")  # get contravariant vector
    assert simplify(covec.tensor()[0] - (a + b * cos(th))) == 0
    assert simplify(covec.tensor()[1] - (b + a * cos(th))) == 0

def test_GenericVector_check_ValueErrors():
    a, b = symbols("a b")
    syms = symbols("e1 e2")
    # input a tensor with wring rank
    try:
        arr = [[a, b], [b, 1]]
        v1 = GenericVector(arr, syms, "l")
        boolstore = False
    except ValueError:
        boolstore = True
    assert boolstore
    # input a wrong length config
    try:
        arr = [a, b]
        v2 = GenericVector(arr, syms, "uu")
        boolstore = False
    except ValueError:
        boolstore = True
    assert boolstore


from numpy import *

def loadDataSet(fileName):
    dataMat = []
    with open(fileName) as fr:
        for line in fr.readlines():
            curLine = line.strip().split('\t')
            fltLine = list(map(float, curLine))
            dataMat.append(fltLine)
    return dataMat

def distEclud(vecA, vecB):
    return sqrt(sum(power(vecA - vecB, 2)))

def randCent(dataSet, k):
    n = shape(dataSet)[1]
    centroids = mat(zeros((k,n)))
    for j in range(n):
        minJ = min(dataSet[:, j])
        rangeJ = float(max(dataSet[:, j]) - minJ)
        centroids[:, j] = minJ + rangeJ * random.rand(k,1)
    return centroids

def kMeans(dataSet, k, distMeas = distEclud, createCent = randCent):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDist = inf
            minIndex = -1
            for j in range(k):
                distJI = distMeas(centroids[j,:], dataSet[i,:])
                if distJI < minDist:
                    minDist = distJI
                    minIndex = j
            if clusterAssment[i,0] != minIndex:
                clusterChanged = True
            clusterAssment[i,:] = minIndex, minDist**2
        print(centroids)
        for cent in range(k):
            ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]]
            centroids[cent,:] = mean(ptsInClust, axis=0)
    return centroids, clusterAssment

def biKmeans(dataSet, k, distMeas = distEclud):
    m = shape(dataSet)[0]
    clusterAssment = mat(zeros((m,2)))
    centroid0 = mean(dataSet, axis = 0).tolist()[0]
    centList = [centroid0]
    for j in range(m):
        clusterAssment[j, 1] = distMeas(mat(centroid0), dataSet[j, :]) ** 2
    while (len(centList) < k):
        lowestSSE = inf
        for i in range(len(centList)):
            ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A == i)[0],:]
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
            sseSplit = sum(splitClustAss[:,1])
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!= i)[0],1])
            print('sseSplit, and notSplit:', sseSplit, sseNotSplit)
            if (sseSplit + sseNotSplit) < lowestSSE:
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit + sseNotSplit
        bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)
        bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
        print('the bestCentToSplit is:', bestCentToSplit)
        print('the len of bestClustAss is:', len(bestClustAss))
        centList[bestCentToSplit] = bestNewCents[0, :]
        centList.append(bestNewCents[1,:])
        clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:] = bestClustAss
    return mat(centList), clusterAssment


import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
f = np.cos(x) * np.exp(x)

'''fig = plt.figure()
plt.plot(x, f)
plt.title("-2Pi to 2Pi Plot of cox(x) * exp(x)")
plt.xlabel("x")
plt.ylabel("cos(x) * exp(x)")
plt.show()'''

mean, stdD = 5.0, 2.0
n = np.random.normal(mean, stdD, 100000)

u = np.random.uniform(0, 10, 100000)

# HIstograms Plot Here.
num_bins = 10
fig = plt.figure(figsize= (12, 3))
plt.subplot(121)
n, bins, patches = plt.hist(n, num_bins, facecolor='blue', alpha=0.5)
plt.subplot(122)
n, bins, patches = plt.hist(u, num_bins, facecolor='red', alpha=0.5)
plt.show()

d = np.array([[2/3, 2/3, -1/3], [2/3, -1/3, 2/3], [-1/3, 2/3, 2/3]])
dt = d.transpose()
p = d.dot(dt)

e = np.array_equal(p, np.identity(3))

def check_orthogonal(M):
    # make sure the input is a matrix
    if len(np.shape(M)) != 2:
        print("error: input is not a matrix")
        return
    # make sure the input is a square matrix

    dim = np.shape(M)[0]
    if dim != np.shape(M)[1]:
        print("error: input is not a square matrix")
        return
    A = np.dot(M, M.T)
    if np.array_equal(A, np.identity(dim)):
        print("matrix is orthogonal")
    else:
        print("matrix is not orthogonal")

D = 1./3. * np.array([[2, 2, -1],
                        [2, -1, 2],
                        [-1, 2, 2]])

check_orthogonal(D)


if '__file__' in globals():
    import os, sys
    sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

import numpy as np
from dezero import Variable
import matplotlib.pyplot as plt

# y = x^4 - 2x^2 optimization with newton's method
def f(x):
    y = x**4 - 2 * x**2
    return y

def gx2(x):
    return 12 * x**2 - 4

if __name__ == '__main__':
    # gradient descent vs newton
    x = Variable(np.array(2.0))
    iters = 10
    newton_x = []
    newton_y = []
    for i in range(iters):
        if not isinstance(x.data, np.ndarray):
            newton_x.append(np.array(x.data))
        else:
            newton_x.append(x.data)
        y = f(x)
        newton_y.append(y.data)
        x.cleargrad()
        y.backward()

        x.data = x.data - x.grad / gx2(x.data)
        print(x.data, y.data)

    x = Variable(np.array(2.0))
    iters = 400
    lr = 0.001
    grad_descent_x = []
    grad_descent_y = []

    for i in range(iters):
        y = f(x)
        grad_descent_x.append(x.data)
        grad_descent_y.append(y.data)
        x.cleargrad()
        y.backward()

        x.data = x.data - lr * x.grad

    x = np.arange(-1.5, 2.2, 0.1)
    y = f(x)
    plt.scatter(grad_descent_x, grad_descent_y, label='Gradient Descent')
    plt.scatter(newton_x, newton_y, marker='*', color='violet', label='Newton\'s method')

    plt.plot(x, y, color='blue')
    plt.legend()
    title = r"f(x) =  $y=x^4-2x^2$"

    plt.title(title)
    plt.show()


import matplotlib.pyplot as plt
import numpy as np
from numpy import *
from pylab import *

'''https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil

   The NACA four-digit wing sections define the profile by
   For example, the NACA 2412 airfoil has a maximum camber of 2% located 40% (0.4 chords)
   from the leading edge with a maximum thickness of 12% of the chord.

   The NACA 0015 airfoil is symmetrical, the 00 indicating that it has no camber.
   The 15 indicates that the airfoil has a 15% thickness to chord length ratio:
   it is 15% as thick as it is long.

   m = maximum camber
   p = loaction of maimum camber
   t = thickness of the airfoil
   c = chrod length

   NACA m p cc
   '''
#defining the camber line
def camber_line( x, m, p, c ):
    return np.where((x>=0)&(x<=(c*p)),
                    m * (x / np.power(p,2)) * (2.0 * p - (x / c)),
                    m * ((c - x) / np.power(1-p,2)) * (1.0 + (x / c) - 2.0 * p ))

#derivative
def dyc_over_dx( x, m, p, c ):
    return np.where((x>=0)&(x<=(c*p)),
                    ((2.0 * m) / np.power(p,2)) * (p - x / c),
                    ((2.0 * m ) / np.power(1-p,2)) * (p - x / c ))

#defining the thickness
def thickness( x, t, c ):
    term1 =  0.2969 * (np.sqrt(x/c))
    term2 = -0.1260 * (x/c)
    term3 = -0.3516 * np.power(x/c,2)
    term4 =  0.2843 * np.power(x/c,3)
    term5 = -0.1015 * np.power(x/c,4)
    return 5 * t * c * (term1 + term2 + term3 + term4 + term5)

#defining the upper and lower coordinates of the airfoil
def naca_cambered(x, m, p, t, c=1):
    dyc_dx = dyc_over_dx(x, m, p, c)
    th = np.arctan(dyc_dx)
    yt = thickness(x, t, c)
    yc = camber_line(x, m, p, c)
    y1,y2,y3,y4= x - yt*np.sin(th), yc + yt*np.cos(th),  x + yt*np.sin(th), yc - yt*np.cos(th)
    data1=hstack((y1,y3[::-1]))     # X Coordinates starting from 1 to 0 and then back to 1
    data2=hstack((y2,y4[::-1]))     # Y Coordinates starting from x=1 to x=0 and then back to x=1
    data=vstack((data1,data2)).T
    savetxt('NACA'+'_'+str(int(m*100))+str(int(p*10))+str(int(t*100))+'.txt',data)
    return (y1,y2),(y3,y4)

def naca_symmetrical(x, t, c=1):
    yt = thickness(x, t, c)
    y1 = x
    y2 = yt
    y3 = x
    y4 = -yt
    data1=hstack((y1,y3[::-1]))     # X Coordinates starting from 1 to 0 and then back to 1
    data2=hstack((y2,y4[::-1]))     # Y Coordinates starting from x=1 to x=0 and then back to x=1
    data=vstack((data1,data2)).T
    savetxt('NACA'+'_'+'00'+str(int(t*100))+'.txt',data)
    return (y1,y2),(y3,y4)

'''
#naca0012
m = 0
p = 1
t = 0.12
c = 1.0
'''

##naca2412
#m = 0.02
#p = 0.4
#t = 0.12
#c = 1.0

def coordinates_symmetrical(x, t, c):
    x = np.linspace(1, 0, 200)
    for item in naca_symmetrical(x, t, c):
        (item[0], item[1], 'b')

def coordinates_cambered(x,m,p,t,c):
    x = np.linspace(1,0,200)
    for item in naca_cambered(x, m, p, t, c):
        (item[0], item[1], 'b')

##plt.plot(x, camber_line(x, m, p, c), 'r')
#plt.axis('equal')
#plt.xlim((-0.05, 1.05))
#savefig('airfoil.pdf')
#show()


import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as sct
import seaborn as sns
from statsmodels.distributions.empirical_distribution import ECDF
from IPython.core.pylabtools import figsize

#Configuracoes do matplot
figsize(12, 8)

sns.set()

np.random.seed(42)

#Criacao do data frame
dataframe = pd.DataFrame({"normal": sct.norm.rvs(20, 4, size=10000),
                          "binomial": sct.binom.rvs(100, 0.2, size=10000)})

#Plot dos dois graficos
sns.distplot(dataframe["normal"])
plt.show()

sns.boxplot(dataframe["binomial"])
plt.show()

#Respostas das questoes do desafio

"""
    Qual a diferença entre os quartis (Q1, Q2 e Q3) das variáveis normal e binomial de dataframe?
    Responda como uma tupla de três elementos arredondados para três casas decimais.
    Em outra palavras, sejam q1_norm, q2_norm e q3_norm os quantis da variável normal e q1_binom, q2_binom e q3_binom os quantis da variável binomial,
    qual a diferença (q1_norm - q1 binom, q2_norm - q2_binom, q3_norm - q3_binom)?
"""
def q1():
    q1_norm = np.quantile(dataframe["normal"], .25)
    q2_normal = np.quantile(dataframe["normal"], .50)
    q3_normal = np.quantile(dataframe["normal"], .75)

    q1_binom = np.quantile(dataframe["binomial"], .25)
    q2_binom = np.quantile(dataframe["binomial"], .50)
    q3_binom = np.quantile(dataframe["binomial"], .75)

    q1 = (q1_norm-q1_binom).round(3)
    q2 = (q2_normal-q2_binom).round(3)
    q3 = (q3_normal-q3_binom).round(3)

    return (q1, q2, q3)

print(q1())

"""
    Considere o intervalo  [𝑥¯−𝑠,𝑥¯+𝑠] , onde  𝑥¯ é a média amostral e  𝑠 s é o desvio padrão.
    Qual a probabilidade nesse intervalo, calculada pela função de distribuição acumulada empírica (CDF empírica) da variável normal?
    Responda como uma único escalar arredondado para três casas decimais.
"""

def q2():

    intervaloQuartilicoNormal = [dataframe["normal"].mean() - dataframe["normal"].std(), dataframe["normal"].mean() + dataframe["normal"].std()]
    probabilidadeMinima = sct.norm.cdf( intervaloQuartilicoNormal[0], 20, 4)
    probabilidadeMaxima = sct.norm.cdf( intervaloQuartilicoNormal[1], 20, 4)
    probabilidadeEntre = probabilidadeMaxima - probabilidadeMinima

    return probabilidadeEntre.round(3)

print(q2())

"""
    Qual é a diferença entre as médias e as variâncias das variáveis binomial e normal?
    Responda como uma tupla de dois elementos arredondados para três casas decimais.
    Em outras palavras, sejam m_binom e v_binom a média e a variância da variável binomial, e m_norm e v_norm a média e
    a variância da variável normal. Quais as diferenças (m_binom - m_norm, v_binom - v_norm)?
"""

def q3():
    m_binom = dataframe["binomial"].mean()
    v_binom = dataframe["binomial"].var()

    m_norm = dataframe["normal"].mean()
    v_norm = dataframe["normal"].var()

    media = m_binom - m_norm
    variancia = v_binom - v_norm

    return (media.round(3), variancia.round(3))

print(q3())

#Parte 2

#Configuracao do dataset
stars = pd.read_csv("pulsar_stars.csv")
stars.rename({old_name: new_name
              for (old_name, new_name)
              in zip(stars.columns,
                     ["mean_profile", "sd_profile", "kurt_profile", "skew_profile", "mean_curve", "sd_curve", "kurt_curve", "skew_curve", "target"])
             },
             axis=1, inplace=True)
stars.loc[:, "target"] = stars.target.astype(bool)
false_pulsar_mean_profile_standardized = []

"""
Considerando a variável mean_profile de stars:
Filtre apenas os valores de mean_profile onde target == 0 (ou seja, onde a estrela não é um pulsar).
Padronize a variável mean_profile filtrada anteriormente para ter média 0 e variância 1.
Chamaremos a variável resultante de false_pulsar_mean_profile_standardized.
Encontre os quantis teóricos para uma distribuição normal de média 0 e variância 1 para 0.80, 0.90 e 0.95 através da função norm.ppf() disponível em scipy.stats.
Quais as probabilidade associadas a esses quantis utilizando a CDF empírica da variável false_pulsar_mean_profile_standardized?
Responda como uma tupla de três elementos arredondados para três casas decimais.
"""

def q4():

    #Filtre apenas os valores de mean_profile onde target == 0
    mean_profile = []
    for i in range(stars.shape[0]):
        if stars["target"][i] == False:
            mean_profile.append(stars["mean_profile"][i])

    #Padronize a variável mean_profile filtrada anteriormente para ter média 0 e variância 1.
    media = np.mean(mean_profile)
    desvioPadrao = np.std(mean_profile)
    for i in range(len(mean_profile)):
        false_pulsar_mean_profile_standardized.append((mean_profile[i] - media) / desvioPadrao)

    #Encontre os quantis teóricos para uma distribuição normal de média 0 e variância 1 para 0.80, 0.90 e 0.95 através da função `norm.ppf()` disponível em `scipy.stats`.
    q80 = sct.norm.ppf(0.80, 0, 1)
    q90 = sct.norm.ppf(0.90, 0, 1)
    q95 = sct.norm.ppf(0.95, 0, 1)

    #Quais as probabilidade associadas a esses quantis utilizando a CDF empírica da variável `false_pulsar_mean_profile_standardized`?
    #Responda como uma tupla de três elementos arredondados para três casas decimais.
    probabilidadeq80  = sct.norm.cdf(q80, 0, 1)
    probabilidadeq90 = sct.norm.cdf(q90, 0, 1)
    probabilidadeq95 = sct.norm.cdf(q95, 0, 1)

    return  (probabilidadeq80.round(3), probabilidadeq90.round(3), probabilidadeq95.round(3))

print(q4())

"""
    Qual a diferença entre os quantis Q1, Q2 e Q3 de false_pulsar_mean_profile_standardized
    e os mesmos quantis teóricos de uma distribuição normal de média 0 e variância 1?
    Responda como uma tupla de três elementos arredondados para três casas decimais.
"""

def q5():

    #Quantis teoricos de distribuicao normal
    qNormal25 = sct.norm.ppf(0.25, 0, 1)
    qNormal50 = sct.norm.ppf(0.50, 0, 1)
    qNormal75 = sct.norm.ppf(0.75, 0, 1)

    #Quantis da variavel false_pulsar_mean_profile_standardized
    qStar25 = np.quantile(false_pulsar_mean_profile_standardized, .25)
    qStar50 = np.quantile(false_pulsar_mean_profile_standardized, .50)
    qStar75 = np.quantile(false_pulsar_mean_profile_standardized, .75)

    #Diferenca
    q25 = qStar25 - qNormal25
    q50 = qStar50 - qNormal50
    q75 = qStar75 - qNormal75

    return (q25.round(3), q50.round(3), q75.round(3))

print(q5())

import numpy,import scipy,from statsmodels


from __future__ import division
from sympy import *

init_printing()
x, y = symbols('x y') #define x e y como variáveis simbólicas.

def f(x):
  return (cos(x) - x)

def Secante(a, b):

  while((f(a)) * (f(b)) > 0):
    print(f'\nErro!\nCom os valores {a} e {b}, nao tem-se raizes para a funcao!')
    a = float(input('\nDigite outro valor para a: '))
    b = float(input('Digite outro valor para b: '))

  erro = 0.0001 #Margem de tolerancia
  i = 0

  while True:
    i += 1
    print(f'\n\nLoop = {i}')
    print(f'f(a) = {f(a):.4f}')
    print(f'f(b) = {f(b):.4f}')
    x_plus_one = b - ((f(b) * (b - a)) / (f(b) - f(a)))
    print(f'xn+1 = {x_plus_one:.4f}')
    modulo_x_plus_one = abs(f(x_plus_one))
    print(f'|f(xn+1)| = {modulo_x_plus_one:.4f}')
    if modulo_x_plus_one < erro:
      break
    a = b
    print(f'a < - xn - 1 = {a:.4f}')
    print(f'f(a) = {f(a):.4f}')
    b = x_plus_one
    print(f'b < - xn = {b:.4f}')
    print(f'f(b) = {f(b):.4f}')

  print(f'\n\nA raiz da funcao f(x) = cos(x) - x eh {x_plus_one:.4f} com tolerancia de {erro:.4f} em {i} iteracao(oes)')


import numpy as np  # do obliczeń trygonometrycznych oraz generowania argumentów funkcji do stworzenia wykresu
import sympy as sp  # do tworzenia wzorów funkcji z parametrem oraz obliczeń pochodnych
from horner import horner

def wartosc_funkcji(arg_x, wybor):
    """Zwracanie wartości wybranej funkcji dla argumentu x

            Parametry
            ----------
            arg_x : float
                wartosc argumentu x
            wybor  : String
                wybór funkcji A, B, C lub D
            Dane wyjsciowe
            -------
            wart_fun : float
                wartość funkcji dla argumentu x
    """
    if wybor == "A":    # A dla funkcji wielomianowej
        wart_fun = horner([5, 2, -1, 5], arg_x)  # obliczenie wartosci schematem hornera
    elif wybor == "B":  # B dla funkcji trygonometrycznej
        wart_fun = 5 * np.cos(arg_x) - 3 * np.sin(arg_x)
    elif wybor == "C":  # C dla funkcji wykładniczej
        wart_fun = 2**arg_x-5**arg_x
    elif wybor == "D":  # D dla funkcji złożonej
        wart_fun = -3*np.sin(arg_x)+2*arg_x**2-1
    else:
        print("Podano nieprawidlowa wartosc. Wybierz a, b, c lub d")
        wart_fun = None
    return wart_fun     # zwróc wartość funkcji dla konkretnego argumentu x

def wzor_funkcji(wybor):
    """Zwracanie wzoru funkcji z parametrem x

            Parametry
            ----------
            wybor  : String
                wybór funkcji A, B, C lub D
            Dane wyjsciowe
            -------
            wzor_fun : Symbol
                wzór funkcji zapisany ze zmienną x
    """
    arg_x = sp.Symbol('x')
    if wybor == "A":    # A dla funkcji wielomianowej
        wzor_fun = horner([5, 2, -1, 5], arg_x)     # przedstawienie funkcji w formie schematu Hornera
    elif wybor == "B":  # B dla funkcji trygonometrycznej
        wzor_fun = 5*sp.cos(arg_x)-3*sp.sin(arg_x)
    elif wybor == "C":  # C dla funkcji wykładniczej
        wzor_fun = 2**arg_x-5**arg_x
    elif wybor == "D":  # D dla funkcji złożonej
        wzor_fun = -3*sp.sin(arg_x)+2*arg_x**2-1
    else:
        print("Podano nieprawidlowa wartosc. Wybierz a, b, c lub d")
        wzor_fun = None
    return wzor_fun     # zwroc wzór funkcji


# -*- coding: utf-8 -*-
from numpy import *

#参数1:特征值向量
#参数2:比率
#返回值：k(符合指定比率的topK k值)
def setK(eigVals,rate = 0.9):
    eigValInd = argsort(eigVals)  #对特征值进行排序
    for i in range(1,eigVals.size+1):
        topK = eigValInd[:-(i + 1):-1]
        eigVal = eigVals[:, topK]
        a = eigVal.sum()
        b = eigVals.sum()
        #print a/b
        if a/b >= rate:
            break;
    return i

#给定一个矩阵，返回 经PCA算法降过维的 矩阵（降维的程度由rate决定）
#如果要指定k,可直接修改这条语句“ k = setK(eigVals,rate)”

def pca(dataMat, rate=0.9):
	meanVals = mean(dataMat, axis=0)
#	print "meanVals"
#	print meanVals
	meanRemoved = dataMat - meanVals #减去均值
#	print "meanRemoved"
#	print meanRemoved
#	print "std"
#	print dataMat.std(0)
#	stded = meanRemoved / dataMat.std(0) #用标准差归一化
	covMat = cov(meanRemoved, rowvar=0) #求协方差方阵
#	print "covMat"
#	print covMat
	eigVals, eigVects = linalg.eig(mat(covMat)) #求特征值和特征向量
#	print "eigVals"
#	print eigVals
#	print "eigVects"
#	print eigVects
	k = setK(eigVals,rate)   #get the topNfeat
	eigValInd = argsort(eigVals)  #对特征值进行排序
	eigValInd = eigValInd[:-(k + 1):-1]  #get topNfeat
	redEigVects = eigVects[:, eigValInd]       # 除去不需要的特征向量
	lowDDataMat = meanRemoved * redEigVects    #求新的数据矩阵
	#reconMat = (lowDDataMat * redEigVects.T) * std(dataMat) + meanVals  #对矩阵还原
	return lowDDataMat

fo = open("./Rules/result.txt", "r")
lines = fo.readlines()
count = len(lines)
M = ones((count, 11))
for i in range(count):
	for j in range(11):
		M[i][j] = lines[i].split('\n')[0].split(' ')[j]
#print M
fo.close()
#print pca(M, 0.9)
lowDDataMat = pca(M, 0.9)
#print lowDDataMat
'''
fo_rules = open("./Rules/rules1.txt", "w");
for i in range(count):
	for j in lowDDataMat[i]:
		fo_rules.write(str(j))
		fo_rules.write(" ")
	fo_rules.write("\n")
fo_rules.close()
'''

#lowDDataMat.dump("../bishe/Result/rules.txt")
#print load("../bishe/Result/rules.txt")
#print load("../bishe/Result/rules.txt")[1]
#a = array([[2.5,2.4],[0.5,0.7],[2.2,2.9],[1.9,2.2],[3.1,3.0],[2.3,2.7],[2,1.6],[1,1.1],[1.5,1.6],[1.1,0.9]])
#print "a:"
#print a
#print pca(a,0.9)

#eigVals = array([3.735,1.133,0.457,0.323,0.199,0.153])
#print setK(eigVals,0.6)


import numpy as np
from scipy.sparse.csgraph import connected_components

def _is_markov_matrix(matrix):
    shape = matrix.shape

    is_markov = len(shape) == 2 and \
        shape[0] == shape[1] and \
        np.allclose(matrix.sum(axis=1), 1)

    return is_markov

def _power_method(transition_matrix, increase_power=True):
    eigenvector = np.ones(len(transition_matrix))

    if len(eigenvector) == 1:
        return eigenvector

    transition = transition_matrix.transpose()

    while True:
        eigenvector_next = np.dot(transition, eigenvector)

        if np.allclose(eigenvector_next, eigenvector):
            return eigenvector_next

        eigenvector = eigenvector_next

        if increase_power:
            transition = np.dot(transition, transition)

def connected_nodes(matrix):
    _, labels = connected_components(matrix)

    groups = []

    for tag in np.unique(labels):
        group = np.where(labels == tag)[0]
        groups.append(group)

    return groups

def stationary_distribution(
    transition_matrix,
    increase_power=True,
    normalized=True,
    safe=True,
):
    if safe and not _is_markov_matrix(transition_matrix):
        msg = '\'transition_matrix\' should be a square matrix with' \
            'sum of each row equal to one'

        raise ValueError(msg)

    size = len(transition_matrix)
    distribution = np.zeros(size)

    grouped_indices = connected_nodes(transition_matrix)

    for group in grouped_indices:
        t_matrix = transition_matrix[np.ix_(group, group)]
        eigenvector = _power_method(t_matrix, increase_power=increase_power)
        distribution[group] = eigenvector

    if normalized:
        distribution /= size

    return distribution


from scipy import *
from matplotlib.pyplot import *
matplotlib.rcParams.update({'font.size': 20})

Nnodes=10000
power=-2;
maxdegree=1000;
mindegree=1;
ks = ((maxdegree**(power+1)-mindegree**(power+1) )*random.random(Nnodes)+mindegree**(power+1))**(1/(power + 1))

[counts,bins,patches]=hist(ks,bins=100)

figure()
subplot(2,1,1)
bar(bins[:-1],counts/float(sum(counts)),width=bins[1]-bins[0])
ylabel("fraction of nodes")

subplot(2,1,2)
bar(bins[:-1],counts/float(sum(counts)),width=bins[1]-bins[0],log=True)
#hist(ks,bins=arange(min(ks),max(ks)),normed=True,log=True)
xlabel("degree")
ylabel("fraction of nodes")

savefig("power_law_degree_distribution.png", transparent=True, dpi=60)

maxdegfound=int(ceil(max(bins)))
[counts,bins,patches]=hist(ks,bins=maxdegfound)

countsnozero=counts*1.
countsnozero[counts==0]=-Inf

figure()
scatter(bins[:-1],countsnozero/float(sum(counts)),s=60)
yscale('log')
xscale('log')
ylim(0.00008,1.1)
xlim(0.8,1100)
xlabel('degree')
ylabel("fraction of nodes")
subplots_adjust(bottom=0.15)

savefig("power_law_degree_distribution_scatter.png", transparent=True, dpi=60)


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

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

def sigmoid_derivative(x):
	return x * (1.0 - x)

class NeuralNetwork:
	def __init__ (self, x, y):
		self.input 	  = x
		self.weights1 = np.random.rand(self.input.shape[1], 4)
		self.weights2 = np.random.rand(4, 1)
		self.y 		  = y
		self.output   = np.zeros(self.y.shape)

	def feedforward(self):
		self.layer1   = sigmoid(np.dot(self.input, self.weights1))
		self.output	  = sigmoid(np.dot(self.layer1, self.weights2))

	def backprop(self):
		# chain rule to find derivative of loss function w.r.t. to weights1 and weights2
		d_weights2	= np.dot(self.layer1.T, 2 * (self.y - self.output) * sigmoid_derivative(self.output))
		d_weights1  = np.dot(self.input.T, (np.dot(2 * (self.y - self.output) * sigmoid_derivative(self.output), self.weights2.T) * sigmoid_derivative(self.layer1)))

		# update weights with derivate weights
		self.weights1 += d_weights1
		self.weights2 += d_weights2

if __name__ == "__main__":

	#==================================================== Chapter 1
	# X = np.array([[0,0,1], [0,1,1], [1,0,1], [1,1,1]])
	# y = np.array([[0], [1], [1], [0]])
	# nn = NeuralNetwork(X, y)

	# for i in range(1500):
	# 	nn.feedforward()
	# 	nn.backprop()

	# print(nn.output)

	#==================================================== Chapter 2
	URL = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
	df  = pd.read_csv(URL, names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class'])

	# print(df.info())
	# print(df.head(10))

	# df2 = df.loc[df['sepal_length'] > 5.0, ]
	# print(df2.head(10))

	# marker_shapes = ['.','^', '*']

	# ax = plt.axes()
	# for i, species in enumerate(df['class'].unique()):
	# 	species_data = df[df['class'] == species]
	# 	species_data.plot.scatter(x = 'sepal_length',
	# 		y = 'sepal_width',
	# 		marker = marker_shapes[i],
	# 		s = 100,
	# 		title = "Sepal Width vs Length by Species",
	# 		label = species,
	# 		figsize = (10, 7),
	# 		ax = ax)
	# plt.show()

	# df['petal_length'].plot.hist(title='Histogram of Petal Length')
	# plt.show()

	# df.plot.box(title='Boxplot of Sepal Length & Width, and Petal Length & Width')
	# plt.show()

	# Encoding
	# df2 = pd.DataFrame({'Day': ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']})
	# print(pd.get_dummies(df2))

	# Inputting Missing Values: Remove some values
	random_index = np.random.choice(df.index, replace=False, size=10)
	df.loc[random_index,'sepal_length'] = None
	print(df.isnull().any())

	# Drop NA method
	print("Number of rows before deleting: %d" % (df.shape[0]))
	df2 = df.dropna()
	print("Number of rows after deleting: %d" % (df2.shape[0]))

	# Replace missing with means
	df.sepal_length = df.sepal_length.fillna(df.sepal_length.mean())
	print(df.isnull().any())


import numpy as np
from .thermodynamics import kbt
from .utils import asKelvin

def dragSphere(radius=1000, viscosity=0.9e-9):
    """
    Calculates the simple Stokes' drag coefficient of a sphere in a Newtonian fluid
    at low Reynolds number. :math:`\\gamma = 6 \\pi \\eta r`

    Args:
        radius (float): radius of solid sphere (default: 1000 nm)
        viscosity (float): dynamic viscosity (default: 0.9e-9 pN s / nm²)

    Returns:
        :class:`float` Stokes drag coefficient in [pN/nm s]
    """

    dragCoefficient = 6 * np.pi * radius * viscosity

    return dragCoefficient

def diffusionCoefficient(radius=1000, temperature=25, viscosity=1e-9, verbose=False):
    """
    Calculates the diffusion coefficient for a sphere based on Stokes drag and the Stokes-Einstein relation.
    :math:`D = \\frac{kT}{\\gamma}`

    Args:
        radius (float): Radius of sphere in [nm] (Default: 1000 nm)
        temperature (float): Solvent temperature in °C (Default: 25)
        viscosity (float): Dynamic viscosity in [pN/nm^2 s] (Default: 0.9e-9 pN/nm^2 s)
        verbose (bool): Print parameters and results with units (Default: False)

    Returns:
        :class:`float` Diffusion constant in [nm^2 / s]
    """

    assert radius > 0
    assert temperature >= -273.15
    assert viscosity > 0

    kT = kbt(temperature)
    drag = dragSphere(radius=radius, viscosity=viscosity)

    diffusionConstant = kT / drag

    if verbose:
        print("In:")
        print("Radius: r = {} nm".format(radius))
        print("Temperature: T = {} °C".format(temperature))
        print("Dynamic viscosity: eta = {} pN/nm^2 s\n".format(viscosity))

        print("Out:")
        print("Diffusion constant: D = {} nm^2 / s".format(round(diffusionConstant, 12)))

    return diffusionConstant

def viscosityWater(T, absoluteT=False):
    """
    Calculate the viscosity of water at a given temperature from an `empiric equation`_ at Wikipedia in [pN s / nm^2].

    Args:
        T (float): Temperature
        absoluteT (bool): if `False`, temperature is given in [C], if `True` in [K]

    Returns:
        :class:`float` in [pN s / nm^2]

    .. _`empiric equation`:
        https://en.wikipedia.org/wiki/Temperature_dependence_of_liquid_viscosity#Viscosity_of_water
    """

    # formula empiric parameters
    A = 2.414E-5
    B = 247.8
    C = 140

    # get absolute temp
    if absoluteT:
        absT = T
    else:
        absT = asKelvin(T)

    # calculate viscosity and convert units
    n = A * 10**(B / (absT - C)) * 1E-6

    return n


import numpy as np

def moving_average(a, n=3) :
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:] / n


import numpy as np

def accuracy_score(y_true, y_predict):
    """计算 y_true 和 y_predict 之间的准确率"""
    assert y_true.shape[0] == y_predict.shape[0], \
        "the size of y_true must be equal to the size of y_predict"

    return sum(y_true == y_predict) / len(y_true)

def mean_squared_error(y_true, y_predict):
    '''计算 y_ture 和 y_predict 之间的 MSE'''
    assert len(y_true) == len(y_predict), \
        "the size of y_true must be equal to the size of y_predict"

    return np.sum((y_true - y_predict) ** 2) / len(y_true)

def root_mean_squared_error(y_true, y_predict):
    '''计算 y_true 和 y_predict 之间的 RMSE'''

    return np.sqrt(mean_squared_error(y_true, y_predict))

def mean_absolute_error(y_true, y_predict):
    '''计算 y_true 和 y_predict 之间的 MAE'''
    assert len(y_true) == len(y_predict), \
        "the size of y_true must be equal to the size of y_predict"

    return np.sum(np.absolute(y_true - y_predict)) / len(y_true)

def r2_score(y_true, y_predict):
    '''计算 y_true 和 y_predict 之间的 R Square'''

    return 1 - mean_squared_error(y_true, y_predict) / np.var(y_true)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 21 10:19:23 2021

@author: mathaes
"""
import numpy as np
from scipy.sparse import coo_matrix

class MatrixFactory:

    # make cycle adjacency matrix

    def makeCycleAdjM_dense(n: int):
        g = np.zeros((n, n), dtype=int)

        j = -1
        for i in range(0, n):
            g[i][j % n] = 1
            g[i][(j+2) % n] = 1
            j += 1
        return g

    def makeCycleAdjM_sparse_coo(n: int):
        rows = np.array()
        cols = np.array()
        values = np.array()

        j = -1
        for i in range(0, n):
            rows.append(i)
            cols.append(j%n)
            values.append(1)

            rows.append(i)
            cols.append((j+2) % n)
            values.append(1)

            j += 1

        return coo_matrix((values, (rows, cols)), shape=(n, n), dtype=int)

    def makeCycleAdjM_sparse_csr(n: int):
        return MatrixFactory.makeCycleAdjM_sparse_coo(n).tocsr()

    # make complete-graph adjacency matrix

    def makeCompleteAdjM_dense(n: int):
        g = np.ones((n, n), dtype=int)
        for i in range(0, n):
            g[i][i] = 0
        return g

    def makeCompleteAdjM_sparse_coo(n: int):
        # might as well start with dense for a complete graph
        return coo_matrix(MatrixFactory.makeCompleteAdjM_dense())

    def makeCompleteAdjM_sparse_csr(n: int):
        return MatrixFactory.makeCycleAdjM_sparse_coo().tocsr()

    # make path adjacency matrix

    def makePathAdjM_dense(n: int):
        g = np.zeros((n, n), dtype=int)

        g[0][1] = 1
        j = 0
        for i in range(1, n-1):
            g[i][j] = 1
            g[i][(j+2) % n] = 1
            j += 1
        g[n-1][n-2] = 1
        return g

    def makePathAdjM_sparse_coo(n: int):
        rows = np.array()
        cols = np.array()
        values = np.array()

        rows.append(0)
        cols.append(1)
        values.append(1)

        j = 0
        for i in range(1, n-1):
            rows.append(i)
            cols.append(j)
            values.append(1)

            rows.append(i)
            cols.append((j+2)%n)
            values.append(1)

            j += 1
        rows.append(n-1)
        cols.append(n-1)
        values.append(1)
        return coo_matrix((values, (rows, cols)), shape=(n, n), dtype=int)

    def makePathAdjM_sparse_csr(n: int):
        return MatrixFactory.makePathAdjM_sparse_coo(n).tocsr()

    # make regular adjacency matrix

    def makeRegularAdjM_dense(n: int, d: int):
        if d > n-1:
            raise Exception("Graph error: vertex degree cannot exceed n-1")

        if n % 2 ==1 and d % 2 == 1:
            raise Exception("Graph error: cannot construct an odd number of vertices with all odd degrees")

        raise Exception("makeRegularAdjM_dense not yet implemented")

    def makeRegularAdjM_sparse_coo(n: int, d: int):
        if d > n-1:
            raise Exception("Graph error: vertex degree cannot exceed n-1")

        if n % 2 ==1 and d % 2 == 1:
            raise Exception("Graph error: cannot construct an odd number of vertices with all odd degrees")

        raise Exception("makeRegularAdjM_dense not yet implemented")

    def makeRegularAdjM_sparse_csr(n: int, d: int):
        return MatrixFactory.makeRegularAdjM_sparse_coo(n).tocsr()

    # make bipartite complete adjacency matrix

    def makeBipartiteCompleteAdjM_dense(m: int, n: int):
        g = np.zeros((m+n, m+n), dtype=int)
        for i in range(0, m):
            for j in range(m, m+n):
                g[i][j] = 1
                g[j][i] = 1
        return g

    def makeBipartiteCompleteAdjM_sparse_coo(m: int, n: int):
        rows = np.array()
        cols = np.array()
        values = np.array()

        for i in range(0, m):
            for j in range(m, m+n):
                rows.append(i)
                cols.append(i)
                values.append(1)

        for i in range(m, m+n):
            for j in range(0, m):
                rows.append(i)
                cols.append(i)
                values.append(1)

        return coo_matrix((values, (rows, cols)), shape=(m+n, m+n), dtype=int)

    def makeBipartiteCompleteAdjM_sparse_coo(m: int, n: int):
        return MatrixFactory.makeBipartiteCompleteAdjM_sparse_coo(n).tocsr()

    # make petersen graph adjacency matrix

    def makePertersenAdjM_dense():
        array = np.zeros((10, 10))
        pass


import numpy as np
import pandas as pd

class LinearRegression(object):

    # setting learning rate and iteration times
    def __init__(self, alpha=0.0005, lamb=0.1, iters=100):
        self.iters = iters
        self.alpha = alpha
        self.lamb = lamb
        # add one line for intercept
        self.theta = np.array([0.0] * (X.shape[1] + 1))

    def __normalize(self, tensor):
        return (tensor - np.mean(tensor, axis=0)) / np.std(tensor, axis=0)

    def __addIntercept(self, tensor):
        intercept = np.ones((tensor.shape[0], 1))
        return np.append(intercept, tensor, axis=1)

    def __preprocess(self, tensor):
        # feature normalize
        tensor = self.__normalize(tensor)
        # add constant column to x(to accommodate the θ0 intercept term)
        tensor = self.__addIntercept(tensor)
        return tensor

    def __hypotheses(self, X, theta):
        return np.dot(X, theta)

    def computeCost(self, X, y, theta):
        inner_prodect = np.power(
            self.__hypotheses(X, theta) - y,
            2
        )
        return np.sum(inner_prodect) / (2 * X.shape[0]) + self.lamb * np.dot(theta[1:], theta[1:]) / 2 * X.shape[0]

    def __gradientDescent(self, X, y, theta):
        for i in range(self.iters):
            # use vectorization implementation to optimize performance

            # do not penalize θ0.
            theta[0:1] = theta[0:1] - (self.alpha / X.shape[0]) * (
                np.dot(X[:, 0:1].T, (self.__hypotheses(X[:, 0:1], theta[0:1]) - y)))
            theta[1:] = theta[1:] - (self.alpha / X.shape[0]) * (np.dot(
                X[:, 1:].T, (self.__hypotheses(X[:, 1:], theta[1:]) - y)) + self.lamb * theta[1:])

            print("the cost of iteration {} is {}".format(
                i, self.computeCost(X, y, theta)))

        return theta

    def fit(self, X, y):
        X = self.__preprocess(X)
        self.theta = self.__gradientDescent(X, y, self.theta)

    def predict(self, X):
        X = self.__preprocess(X)
        return self.__hypotheses(X, self.theta)

if __name__ == "__main__":
    import random
    # generate feature X
    X = np.arange(0, 20).reshape(10, 2)

    # generate sample response
    y = np.arange(
        0, 10) + np.array([random.random() for r in range(0, 10)])

    lr = LinearRegression()
    lr.fit(X, y)
    print(lr.predict(X))


import numpy as np
def read_square_matrix():
    d = [int(e) for e in input().split()]
    m = [d]
    for k in range(len(d)-1):
        m.append([int(e) for e in input().split()])
    return np.array(m)
def min_in_each_row(m): # หาวิธีเขียนแค่ค าสั่งเดียว
    return np.array([min(r) for r in m])
def max_in_each_column(m): # หาวิธีเขียนแค่ค าสั่งเดียว
    return np.array([max(r) for r in m.T])
def diff_of_sums_of_two_diags(m):# หาวิธีเขียนอย่างมากสองค าสั่ง
    diag1=sum([m[i][i] for i in range(len(m))])
    diag2=sum([m[len(m)-1-i][i] for i in range(len(m))])
    return abs(diag1 - diag2)
def halve(m): # หาวิธีเขียนอย่างมากสองค าสั่ง
    new=[]
    for i in range(m.shape[0]//2):
        subnew=[]
        for j in range(m.shape[1]//2):
            subnew.append(m[2*i][2*j] + m[2*i+1][2*j] + m[2*i][2*j+1] + m[2*i+1][2*j+1])
        new.append(subnew)
    return np.array(new)
exec(input().strip()) # do not remove this line


import numpy as np
# Math operations
import matplotlib.pyplot as plt
# Presentable graphs, optional
from mpl_toolkits.mplot3d import Axes3D
# Presentable 3D Graphs, optional

observations = 1000
#Here I declare a variable containing the size of the training set.

xs = np.random.uniform(low=-10, high=10, size=(observations,1))
zs = np.random.uniform(-10, 10, (observations,1))
# The size should be n x k, where n is number of observations and k is number of variables.

inputs = np.column_stack((xs,zs))
# 'np.column_stack' stacks the observations into a matrix, which results in a matrix with dimensions 1000 x 2.
# I did this because I want to multiply x with w, because the linear model (y=xw+b) relies on that operation.
print (inputs.shape)
# Checked n x k , where n should be the number of observations and k is the number of variables.

noise = np.random.uniform(-1, 1, (observations,1))
# I introduced the noise to randomize my data a bit.
# Real data always contains noise - it's never perfect - so introducing some noise makes the data a bit more random.

targets = 4*xs - 6*zs + 10 + noise
# A two-variable linear model, which is completely arbitrarily chosen.
# The Weights are 4 and -6 and the Bias is 10.
# My goal is that my ML algorithm to learn that this is the function.

print (targets.shape)
# Checked n x m, where m should be the number of output variables.

targets = targets.reshape(observations,)
xs = xs.reshape(observations,)
zs = zs.reshape(observations,)
# I reshaped the targets in order to use the 3D Plot.

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# A method to create the 3D Plot
ax.plot(xs, zs, targets)
ax.set_xlabel('xs')
ax.set_ylabel('zs')
ax.set_zlabel('Targets')
ax.view_init(azim=100)
plt.show()
# I wanted to see whether there is a strong trend that the model should learn to reproduce.

targets = targets.reshape(observations,1)
xs = xs.reshape(observations,1)
zs = zs.reshape(observations,1)
# Actually, Ididn't need to use the 3D Plot, so I reshaped the targets and the variables back.

init_range = 0.1
# Here I randomly selected some small initial weights.
# The init_range determines the interval where the initial weights and biases will be picked randomly. In this case, it is [-0.1, 0.1].
# Therefore, the initial random weights and biases will be picked from -0.1 to 0.1

weights = np.random.uniform(low=-init_range, high=init_range, size=(2, 1))
# Here I generated the weights randomly using this code.
# Weights are of size k x m, where k is the number of input variables and m is the number of output variables
# In this exercise, the weights matrix is 2x1 since there are 2 variables (x and z) and 1 output (y).
biases = np.random.uniform(low=-init_range, high=init_range, size=1)
# Here I generated the biases randomly using this code.
# In ML, there are as many biases as there are outputs.
# So in this exercise, there is only 1 bias as there is only 1 output.
# The bias is a scalar.

print (weights)
print (biases)

learning_rate = 0.02
# The learning rate determines the speed of the minimization of the Loss Function of the ML Algorithm.
# Gradient Descent depends on the learning rate.
# I chose this learning rate. It must not be too high that the GD iterates forever and too low that GD oscillates forever, never reaching the global minimum loss.

# The ML Algorithm
for i in range (100):

    outputs = np.dot(inputs,weights) + biases
    # This is the linear model: y = xw + b equation
    # I used Numpy to multiply matrices using the Dot Method.

    deltas = outputs - targets
    # Here, 'deltas' records the difference between outputs and targets, where deltas is 1000 x 1.
    # 'deltas' is a pythonic 1000x1 array.
    # This is the Error of the ML Algorithm.

    loss = np.sum(deltas ** 2) / 2 / observations
    # Calculates the loss function that compares the outputs to the targets.
    # I used the L2-norm loss because this is a Regression.
    # I divided it by 2 to get the elegant Update Rules from the Gradient Descent.
    # I divided it by the number of observations to get the Cost Function, which is the average loss per observation.
    # I adjusted the loss instead of the learning rate. This makes the learning of the ML independent of the number of observations.

    print (loss)
    # I decided to print the loss function at each step so I can analyze whether it is decreasing as desired and my ML algorithm is learning.

    deltas_scaled = deltas / observations
    # This is another way to make the ML algorithm more universal.

    weights = weights - learning_rate * np.dot(inputs.T,deltas_scaled)
    # The weights are 2x1, learning rate is 1x1 (scalar), inputs are 1000x2, and deltas_scaled are 1000x1
    # '.T' is a method to transform matrices and perform the desired operation
    biases = biases - learning_rate * np.sum(deltas_scaled)
    # I transformed the inputs for the operation to proceed
    # The weights are updated in a linear algebraic way (a matrix minus another matrix).
    # I updated the weights and the biases following the gradient descent methodology.

print (weights, biases)
# The bias improves as the number of iterations increases.

plt.plot(outputs, targets)
plt.xlabel('outputs')
plt.ylabel('targets')
plt.show()
# While this is not needed and may not be possible in other cases, I decided to plot the outputs and targets to check if they have a linear relationship.
# The closer the plot is to a 45 degree line, the closer target and output values are.

Building a Basic Neural Network with Numpy.py