import numpy as np
import csv
import sys

#Avoid log0=-inf
def c_log(x):
	return np.log10(x + 0.0000001)

def read_network(filepath):
	nt = []
	with open(filepath) as csvfile:
		csvreader = csv.reader(csvfile, delimiter = ",")

		for row in csvreader:
			row_int = [int(s) for s in row]
			nt.append(row_int)

	nt = np.array(nt, dtype = int)

	return nt

#Initialize chromosome population
def initialize_pop(N, S, k):
	pop = np.random.randint(low = 0, high = k, size = (N, S))
	return pop

#Calculate AIC for a particular grouping (ch)
#p_ij = l_ij/S_iS_j
def calc_AIC(nt, ch, k, S):
	k_dict = {i:[] for i in range(k)}
	for i in range(S):
		k_dict[ch[i]].append(i)

	loglikehood = 0.
	for i in range(k):
		for j in range(k):
			nodes_i, nodes_j = k_dict[i], k_dict[j]
			s_i, s_j = len(nodes_i), len(nodes_j)
			l_ij = sum([nt[x, y] for x in nodes_i for y in nodes_j])
			p_ij = l_ij / (s_i * s_j + 0.0000001)
			loglikehood += (l_ij * c_log(p_ij) + (s_i * s_j - l_ij) * (c_log(1. - p_ij)))

	AIC = 2. * k * k + 2. * S - (2. * loglikehood)

	return AIC

def locally_optimize(pop, nt, k, S, N):
	#Iterate to locally optimize each chromosome
	for i in range(N):
		ch = pop[i]
		best_score = calc_AIC(nt, ch, k, S)
		best_neighbor = ch.copy()
		cont = True
		cycles = 0

		#Hill climbing: search for the best neighbor
		while cont:
			cycles += 1
			starter_best_score = best_score
			starter_best_neighbor = best_neighbor
			for position in range(S):
				for group in range(k):
					#try every possible neighbor differing by this position
					if group != ch[position]:
						temp_ch = starter_best_neighbor.copy()
						temp_ch[position] = group
						temp_score = calc_AIC(nt, temp_ch, k, S)
						if temp_score < best_score:
							best_score = temp_score
							best_neighbor = temp_ch.copy()
			if starter_best_score == best_score or cycles > 1000:
				cont = False

		pop[i] = best_neighbor.copy()

	return pop

def simulate_pop_one_gen(pop, nt, N, S, M, k):
	#Hill climbing
	pop = locally_optimize(pop, nt, k, S, N)

	#Reproduction
	AICs = np.array([calc_AIC(nt, ch, k, S) for ch in pop])

	fitnesses = 1. / AICs
	wbar = np.mean(fitnesses)
	freqs = (1. / N) * (fitnesses / wbar)
	new_nums = np.random.multinomial(N, pvals = freqs)

	#Mutation
	new_pop = np.array([pop[i] for i in range(N) for j in range(new_nums[i])])
	mutations = np.random.binomial(n = 1, p = M, size = (N, S))
	mutations_index = np.where(mutations == 1)
	targets = np.random.randint(low = 0, high = k, size = len(mutations_index[0]))

	for i in range(len(targets)):
		new_pop[mutations_index[0][i], mutations_index[1][i]] = targets[i]

	return new_pop

def simulate_pop(pop, nt, N, S, M, k, MAX_GEN):
	cont = True
	gens = 0

	while cont:
		gens += 1
		pop = simulate_pop_one_gen(pop, nt, N, S, M, k)
		print(gens, flush = True)
		neighbor_equal = [np.array_equal(pop[i], pop[i + 1]) for i in range(N - 1)]
		if all(neighbor_equal) or gens > MAX_GEN:
			cont = False
			print(gens, flush = True)

	score = calc_AIC(nt, pop[0], k, S)
	grouping = pop[0]

	return score, grouping

def main():
	SEED = 1024
	MAX_GEN = 1000
	N = 1000 		#size of the chromosome's population
	M = 3. 	        #expected number of mutations in each generation
	MIN_K = 1       #minimum number of groups
	MAX_K = 6       #maximum number of groups
	S = 0 			#initialize num of nodes in the network

	if len(sys.argv) > 1:
		NETWORK_PATH = sys.argv[1]
	else:
		NETWORK_PATH = "tests/adj_m_small.csv"
	BEST_SCORES = np.ones(MAX_K) * np.inf
	BEST_GROUPING = []

	######################SCRATCH TESTS######################
	TEST = True
	if TEST:
		N = 20
		MIN_K = 5
		MAX_K = 15
		MAX_GEN = 100
		M = 3.

	#########################################################

	print("N = {}".format(N), flush = True)
	np.random.seed(SEED)

	#Read network
	nt = read_network(NETWORK_PATH) #adj matrix; np arr
	S = len(nt)
	MAX_K = min(MAX_K, S) #cannot have more groups than nodes
	assert(S > 0)
	M = M / (N * S)

	#Grouping
	for k in range(MIN_K, MAX_K + 1):
		pop = initialize_pop(N, S, k)
		best_score, best_group = simulate_pop(pop, nt, N, S, M, k, MAX_GEN)
		print("k =", k, "AIC =", best_score, "grouping:", best_group, flush = True)

if __name__ == "__main__":
	main()


""".. include:: ../_notebooks/01-shannon.rst"""

from typing import Optional
import numpy as np

########################################################################
class Shannon:
    """Shannon entropy"""

    # ----------------------------------------------------------------------
    def __new__(self, data: np.ndarray, bins: Optional[int] = 16, base: Optional[int] = 2, conditional: Optional[int] = None) -> float:
        """Calculate the shannon entropy for `n` variables.

        If `n` is higher than 1, then the joint entropy is calculated.
        """
        # Data must be nxm
        if len(data.shape) == 1:
            data = data.reshape(1, -1)

        dist = self.dist(data, bins)

        if not conditional is None:
            return np.sum(- dist * np.log(dist) / np.log(base)) - (self.__new__(self, data[conditional], bins, base))
        else:
            return np.sum(- dist * np.log(dist) / np.log(base))

    # ----------------------------------------------------------------------
    @classmethod
    def dist(cls, x: np.ndarray, bins: int) -> np.ndarray:
        """Calculate the joint probability of `x`."""

        jointProbs, edges = np.histogramdd(x.T, bins=bins, normed=True)
        jointProbs /= jointProbs.sum()
        dist = jointProbs[jointProbs != 0]

        return dist


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

def get_random_coef(size=10):
    return np.random.normal(size=size)*np.random.binomial(n=1, p=0.3, size=size)

def make_random_fractal(imgsize=1000, max_iter=200, verbose=False):
    coef = get_random_coef()
    return coef, make_fractal(coef, imgsize=imgsize, max_iter=max_iter, verbose=verbose)

"""
This is a macro that will print out strings you need for f(z) and df(z)
if you want to change the power of the polynomial. You will also want to change the size variable in
get_random_coef
"""
def print_f_df(power):
    f = ""
    df = ""
    for i in range(power):
        f += "coef[{}]".format(i)+"*z"*i + "+"
        if i>0:
            df += "{}*coef[{}]".format(i, i)+"*z"*(i-1) + "+"
    print(f[:-1])
    print(df[:-1])

def make_fractal(coef, imgsize=1000, max_iter=200, verbose=False):
    if verbose:
        print("Creating fractal for ", coef)
    def f(z):
        return coef[0]+coef[1]*z+coef[2]*z*z+coef[3]*z*z*z+coef[4]*z*z*z*z+coef[5]*z*z*z*z*z+coef[6]*z*z*z*z*z*z+coef[7]*z*z*z*z*z*z*z+coef[8]*z*z*z*z*z*z*z*z+coef[9]*z*z*z*z*z*z*z*z*z
    def df(z):
        return 1*coef[1]+2*coef[2]*z+3*coef[3]*z*z+4*coef[4]*z*z*z+5*coef[5]*z*z*z*z+6*coef[6]*z*z*z*z*z+7*coef[7]*z*z*z*z*z*z+8*coef[8]*z*z*z*z*z*z*z+9*coef[9]*z*z*z*z*z*z*z*z

    y, x = np.ogrid[1: -1: imgsize*2j, -1: 1: imgsize*2j]
    z = x +y*1j

    img = np.zeros(z.shape)
    it = range(max_iter)
    if verbose:
        it = tqdm(range(max_iter))
    for i in it:
        z = z - f(z)/df(z)
        ind = np.logical_and(np.abs(f(z)) < 1e-4, img==0)
        img[ind] = i

    return img

def save(img, outfile, cmap="gist_yarg", imgsize=1000):
    fig = plt.figure(figsize=(imgsize/100.0, imgsize/100.0), dpi=100)
    ax = fig.add_axes([0, 0, 1, 1], aspect=1)
    ax.axis('off')
    ax.imshow(img, cmap=cmap)
    fig.savefig(outfile)
    plt.close(fig)


# Starting code for HW 2. Add to this!
import numpy as np
import scipy.linalg as linalg

tol = 1e-12 # tolerance

# Function definitions:
f1 = lambda x: np.cos(x[0] + x[1]) - x[1]**2
f2 = lambda x: np.sin(x[0] - x[1]) - x[0]*x[1]
f = lambda x: (f1(x), f2(x))

f1d1 = lambda x : -np.sin(x[0] + x[1])
f1d2 = lambda x : -np.sin(x[0] + x[1]) - 2*x[1]
f2d1 = lambda x:  np.cos(x[0] - x[1]) - x[1]
f2d2 = lambda x: -np.cos(x[0] - x[1]) - x[0]

xk = np.array([1,1]) # Initial guess x_k

k = [0]
residuals = [np.abs(f(xk))]

itnum = 0 # iteration counter
success = True # Assume success unless we fail!

while linalg.norm(f(xk))>tol:
    A = np.array([[f1d1(xk), f1d2(xk)],[f2d1(xk), f2d2(xk)]])
    xk = xk - np.matmul(linalg.inv(A), f(xk))

    print(f"x_{itnum} = {xk}")
    itnum += 1
    k.append(itnum)
    residuals.append(linalg.norm(f(xk)))

    if itnum>10000:
        print('Something is wrong, too many iterations, let\'s break out of this loop...!')
        success = False
        break

if success:
    # Plotting using matplotlib, imported at the top of the code.
    print()
    print(f"Done in {itnum} iterations")
    print(f"x1 = {xk[0]}")
    print(f"x2 = {xk[1]}")


#!/usr/bin/python
# -*- coding:utf-8 -*-
# @author  : east
# @time    : 2019/10/7 0:23
# @file    : gauss1d.py
# @project : fem
# software : PyCharm

import numpy as np

"""
Data from Wikipedia [Gaussian quadrature](https://en.wikipedia.org/wiki/Gaussian_quadrature).
"""

n = [1, 2, 3, 4, 5]
name = ['gauss1', 'gauss2', 'gauss3', 'gauss4', 'gauss5']

gauss1 = np.array([[0], [2]])

gauss2 = np.array([[-1/np.sqrt(3), 1/np.sqrt(3)], [1, 1]], dtype=np.float64)

gauss3 = np.array([[-np.sqrt(3/5), 0, np.sqrt(3/5)],
                   [5 / 9, 8 / 9, 5 / 9]], dtype=np.float64)

gauss4 = np.array([[-np.sqrt(3/7+2/7*np.sqrt(6/5)), -np.sqrt(3/7-2/7*np.sqrt(6/5)),
                    np.sqrt(3/7-2/7*np.sqrt(6/5)), np.sqrt(3/7-2/7*np.sqrt(6/5))],
                   [(18-np.sqrt(30))/36, (18+np.sqrt(30))/36,
                    (18+np.sqrt(30))/36, (18-np.sqrt(30))/36]], dtype=np.float64)

gauss5 = np.array([[-np.sqrt(5+2*np.sqrt(10/7))/3, -np.sqrt(5-2*np.sqrt(10/7))/3,
                    0, np.sqrt(5-2*np.sqrt(10/7))/3, np.sqrt(5+2*np.sqrt(10/7))/3],
                   [(322-13*np.sqrt(70))/900, (322+13*np.sqrt(70))/900, 128/225,
                    (322+13*np.sqrt(70))/900, (322-13*np.sqrt(70))/900]], dtype=np.float64)

for k, v in enumerate([gauss1, gauss2, gauss3, gauss4, gauss5]):
    np.save(name[k], v)


#!/usr/bin/env python
# Wenchang Yang (wenchang@princeton.edu)
# Tue Jun 15 22:10:57 EDT 2021
if __name__ == '__main__':
    from misc.timer import Timer
    tt = Timer(f'start {__file__}')
#import sys, os.path, os, glob
import xarray as xr, numpy as np, pandas as pd
from numba import njit,guvectorize
from scipy.stats import t as stu
#import matplotlib.pyplot as plt
#more imports
#
if __name__ == '__main__':
    tt.check('end import')
#
#start from here
@njit
def mycorr(x, y):
    xm = np.mean(x)
    ym = np.mean(y)
    xxm = np.mean(x*x)
    yym = np.mean(y*y)
    xym = np.mean(x*y)
    return (xym - xm*ym)/np.sqrt( (xxm - xm*xm)*(yym - ym*ym) )

@guvectorize(["f8[:],f8[:], b1[:],f8[:],f8[:],f8[:],i8[:],f8[:],f8[:],f8[:]"],
    "(n),(n),()->(),(),(),(),(),(),()", nopython=True, target='parallel')
def linregress_core(y, x, ess_on, slope, intercept, r, dof, tvalue, slope_stderr, intercept_stderr):#, predict_stderr):
    """calculate parameters associated with linear relationship between x and y.
    see https://en.wikipedia.org/wiki/Simple_linear_regression"""
    xm = np.mean(x)
    ym = np.mean(y)
    xxm = np.mean(x*x)
    yym = np.mean(y*y)
    xym = np.mean(x*y)
    s2x = xxm - xm*xm
    s2y = yym - ym*ym
    sxy = xym - xm*ym
    #slope
    slope[0] = sxy/s2x
    #intercept
    intercept[0] = ym - slope[0]*xm
    e = np.zeros(x.shape)
    for ii in range(x.size):
        e[ii] = y[ii] - intercept[0] - slope[0]*x[ii]
    eem = np.mean(e*e)
    #correlation
    r[0] = slope[0]*np.sqrt(s2x/s2y)
    N = x.size
    #effective sample size considered
    if ess_on[0]:
        s = 1.0
        for tao in range(1, N//2+1):
            #lag-1 correlation coefficient; here we use np.corrcoef
            #r1x = np.corrcoef(x[:-tao], x[tao:])[0,1]
            #r1y = np.corrcoef(y[:-tao], y[tao:])[0,1]
            #use mycorr is faster than np.corrcoef
            r1x = mycorr(x[:-tao], x[tao:])
            r1y = mycorr(y[:-tao], y[tao:])
            if r1x < 0 or r1y <0:
                break
            s = s + 2*(1 - tao/N)*r1x*r1y
        Ne = int(N/s)
        dof[0] = Ne - 2#degree of freedom in t-test
    else:
        dof[0] = N - 2
    #tvalue
    tvalue[0] = r[0]*np.sqrt( dof[0]/(1 - r[0]*r[0]) )
    #standard error of slope
    slope_stderr[0] = np.sqrt( eem/s2x/dof[0] )
    #standard error of intercept
    intercept_stderr[0] = slope_stderr[0] * np.sqrt( xxm )
    #standard error of the prediction
    #for ii in range(x.size):
    #    predict_stderr[ii] = np.sqrt( eem/dof[0]*( 1.0 + (x[ii] - xm)**2/s2x ) )

    #return slope, intercept, r, dof, tvalue, slope_stderr, intercept_stderr, predict_stderr

def linregress_np(y, x, ess_on=False, alpha=0.05):
    """wrap around linregress_core and use scipy.stats.t.cdf to calculate pvalue and t_alpha"""
    x_ = x.astype('float64')
    y_ = y.astype('float64')
    #slope, intercept, r, dof, tvalue, slope_stderr, intercept_stderr, predict_stderr = linregress_core(x_, y_, ess_on)
    slope, intercept, r, dof, tvalue, slope_stderr, intercept_stderr = linregress_core(y_, x_, ess_on)
    pvalue = 2*stu.cdf(-np.abs(tvalue), dof)
    t_alpha = stu.ppf(1-alpha/2, dof)

    return slope, intercept, r, dof, tvalue, slope_stderr, intercept_stderr, pvalue, t_alpha

def linregress(da_y, da_x, dim=None, ess_on=False, alpha=0.05, predict_stderr_on=None, predicted_on=None):
    """xarray-version linregress_np (accept xr.DataArray as input)"""
    if dim is None:
        dim = [d for d in da_y.dims if d in da_x.dims][0]

    #automatically control whether to output predicted values; output when input arrays have lower dimensions
    if predicted_on is None:
        if da_y.ndim > 2 or da_x.ndim > 2:
            predicted_on = False
        else:
            predicted_on = True
    #automatically control whether to output predict_stderr; output when input arrays have lower dimensions
    if predict_stderr_on is None:
        if da_y.ndim > 2 or da_x.ndim > 2:
            predict_stderr_on = False
        else:
            predict_stderr_on = True

    #use xr.apply_ufunc to accept dataarray as input
    slope, intercept, r, dof, tvalue, slope_stderr, intercept_stderr, pvalue, t_alpha = xr.apply_ufunc(
        linregress_np,
        da_y, da_x,
        input_core_dims=[[dim], [dim]],
        output_core_dims=[[], [], [], [], [], [], [], [], []],
        dask='allowed', kwargs={'ess_on': ess_on, 'alpha': alpha})

    #add some attrs
    slope.attrs['long_name'] = 'slope of the linear regression'
    intercept.attrs['long_name'] = 'intercept of the linear regression'
    r.attrs['long_name'] = 'correlation coefficient'
    dof.attrs['long_name'] = 'degree of freedom of the t distribution'
    tvalue.attrs['long_name'] = 't-value in the t-test'
    slope_stderr.attrs['long_name'] = 'standard error of the estimated slope'
    intercept_stderr.attrs['long_name'] = 'standard error of the estimated intercept'
    pvalue.attrs['long_name'] = 'p-value'
    t_alpha.attrs['long_name'] = 't value corresponding to the significance level alpha'

    #result as a dataset
    ds = xr.Dataset(dict(
        slope=slope, intercept=intercept, r=r,
        dof=dof, tvalue=tvalue,
        slope_stderr=slope_stderr, intercetp_stderr=intercept_stderr,
        pvalue=pvalue, t_alpha=t_alpha))

    if predicted_on:
        predicted = da_x * slope + intercept
        predicted.attrs['long_name'] = 'predicted values by the linear regression model'
        ds['predicted'] = predicted
    if predict_stderr_on:
        e = da_y - slope*da_x - intercept
        eem = (e*e).mean(dim)
        xm = da_x.mean(dim)
        s2x = (da_x*da_x).mean(dim)
        predict_stderr = np.sqrt( eem/dof*( 1.0 + (da_x - xm)**2/s2x ) )
        predict_stderr.attrs['long_name'] = 'standard error of the predicted value'
        ds['predict_stderr'] = predict_stderr

    return ds

if __name__ == '__main__':
    #test
    from wyconfig import * #my plot settings
    from xaddon.xlib.linregress import linregress as linregress_da
    import xarray as xr
    from xdata import ds
    x = ds.nino34.values
    y = ds.iod.values

    slope, intercept, r, dof, tvalue, slope_stderr, intercept_stderr, pvalue, t_alpha = linregress_np(y, x)
    ds_ = linregress(ds.iod, ds.nino34)
    print(f'{slope = }; {intercept = }; {r = }; {dof = }; {tvalue = }; {slope_stderr = }; {intercept_stderr = }; {pvalue = }; {t_alpha = }')

    #print(linregress_da(xr.DataArray(y), xr.DataArray(x)))
    print('\n scipy.stats.linregress result')
    print(linregress_da(ds.iod, ds.nino34))
    print('\n linregress result with ess_on=False')
    print(linregress(ds.iod, ds.nino34, ess_on=False))
    print('\n linregress result with ess_on=True')
    print(linregress(ds.iod, ds.nino34, ess_on=True))

    da_y = ds.sst.pipe(lambda x: x.groupby('time.month') - x.groupby('time.month').mean('time') )
    da_x = ds.nino34
    tt.check('start linregress')
    ds_x = linregress(da_y, da_x, predict_stderr_on=True)
    tt.check('start linregress_da')
    ds_da = linregress_da(da_y, da_x)
    tt.check('end linregress_da')

    plt.scatter(x, y, color='none', edgecolor='C0')
    x_ = np.sort(x)
    iis = np.argsort(x)
    predict = x_*slope + intercept
    spread = ds_.predict_stderr[iis]*t_alpha
    plt.fill_between(x_, predict-spread, predict + spread, alpha=0.5, color='C0')
    plt.plot(x_, predict, color='C0', ls='--')

    tt.check(f'**Done**')
    plt.show()


import numpy as np

def compute_q10_correction(q10, T1, T2):
    """Compute the Q10 temperature coefficient.

    As explained in [1]_, the time course of voltage clamp recordings are
    strongly affected by temperature: the rates of activation and inactivation
    increase with increasing temperature. The :math:`Q_{10}` temperature
    coefficient, a measure of the increase in rate for a  10 :math:`^{\circ}C`
    temperature change, is a correction factor used in HH-style models to
    quantify this temperature dependence.

    In HH-style models, the adjustment due to temperature can be achieved by
    decreasing the time constants by a factor :math:`Q_{10}^{(T_2 - T_1)/10}`,
    where the temperatures :math:`T_1 < T_2`. The temperature unit must be
    either the Celsius or the Kelvin. Note that :math:`T_1` and :math:`T_2`
    must have the same unit, and do not need to be exactly 10 degrees apart.

    Parameters
    ----------
    q10 : :obj:`float`
        The :math:`Q_{10}` temperature coefficient.
    T1 : :obj:`float`
        Temperature at which the first rate is recorded.
    T2 : :obj:`float`
        Temperature at which the second rate is recorded.

    Returns
    -------
    correction : :obj:`float`
        Correction factor due to temperature.

    References
    ----------
    .. [1] D. Sterratt, B. Graham, A. Gillies, D. Willshaw,
           "Principles of Computational Modelling in Neuroscience",
           Cambridge University Press, 2011.
    """

    # that the test below allows T1 = T2 is intentional; the function should
    # accomendate for no correction, i.e. a correction factor equal to 1.
    if T1 > T2:
        msg = ("T2 must be greater than or equal to T1")
        raise ValueError(msg)
    return q10**((T2 - T1) / 10)

def vtrap(x, y):
    """Traps for zero in denominator of rate eqns.

    HH-style model rate equations often contain expressions that are
    equivalent to

    .. math::
        x / (\exp(u) - 1),

    where :math:`u = x / y`. From Taylor series approximation, one can find that
    the above expression is approximated by

    .. math::
        y (1 - u / 2)

    if :math:`u << 1`.

    This approximation ensures that indeterminate cases are handled properly.

    Parameters
    ----------
    x : :obj:`float` or :term:`ndarray`
        Numerator in the exponential function argument.
    y : :obj:`float` or :term:`ndarray`
        Denominator in the exponential function argument.

    Returns
    -------
    vtrap : :obj:`float` or :term:`ndarray`
        The result of the evaluation.

    References
    ----------
    Inspired by the ``vtrap`` function in the NEURON simulator;
    github.com/neuronsimulator/nrn/blob/master/src/nrnoc/hh.mod
    """

    u = x / y
    return np.where(u < 1e-6, y * (1 - u / 2), x / (np.exp(u) - 1))


# -*- coding: utf-8 -*-
"""
Euler - Cromer algorithm for solving 2nd order ODEs like y''(x) = f(x,t,y(x))
And 1D oscillator modelling as the demo
@author: ssklykov
"""
# %% Import section
import numpy as np
import matplotlib.pyplot as plt
from DynamicSystem import dynamicSys

# %% Algorithm implementation (in general form - using 1st, 2nd order derivatives)
def eulerCromer(f: float, y1stDerivPrevious: float, yPrevious: float, h: float) -> tuple:
    """Implementation is kept general - it consumes only the values calculated outside of it"""
    y2ndDeriv = f  # y''(x..) = f(*args); a  = f/m - this is the 2nd derivative!!! (acceleration)
    y1stDeriv = y1stDerivPrevious + h*y2ndDeriv  # y'(n) = y'(n-1) + h*y''; this is speed or velocity!
    y = yPrevious + h*y1stDeriv
    return (y1stDeriv, y)

# %% Testing values and their probing
gamma = 0.015; radius = 5; mass = 1; amplitude = 8.8
xStart = -2*radius; xFinish = 2*radius
initSpeed = 1.6
nTimePoints = int(40000); hTime = 0.0025
sys1 = dynamicSys("1st example"); sysForce1 = sys1.getForce

# %% Getting force profile
nX = 1000; hX = (xFinish - xStart)/nX
nX += 1
f = np.zeros(nX, dtype=float); x = np.zeros(nX, dtype=float)
for i in range(nX):
    x[i] = xStart + i*hX
    gammaPureExternalForce = 0.0
    f[i] = sysForce1(amplitude, x[i], radius, mass, gammaPureExternalForce, initSpeed)

plt.figure(); plt.plot(x,f,linewidth=2); plt.xlabel("x coordinates"); plt.title("External force profile f(x)")

# %% Testing algorithm for solving 1D oscillatory form
xCoord = np.zeros(nTimePoints+1, dtype='float'); speeds = np.zeros(nTimePoints+1, dtype='float')
timePoints = np.zeros(nTimePoints+1, dtype='float')
xCoord[0] = xStart; speeds[0] = initSpeed; timePoints[0] = 0
# Get modelled values - x coordinates, speed along x
for i in range(1, nTimePoints+1):
    forceCalibrated = sysForce1(amplitude, xCoord[i-1], radius, mass, gamma, speeds[i-1])
    (speeds[i], xCoord[i]) = eulerCromer(forceCalibrated, speeds[i-1], xCoord[i-1], hTime)
    timePoints[i] = timePoints[i-1] + hTime
# Plotting dynamic pictures for x and v
plt.figure(); plt.plot(timePoints,xCoord,'r',linewidth=2); plt.xlabel("time"); plt.ylabel("x"); plt.title("x(t)")
plt.figure(); plt.plot(timePoints,speeds,'g',linewidth=2); plt.xlabel("time"); plt.ylabel("v"); plt.title("v(t)")
plt.figure(); plt.plot(xCoord,speeds,'m',linewidth=1); plt.xlabel("x"); plt.ylabel("v")
plt.title("speed versus x coordinate")


'''
POLYNOMIAL CALCULUS

INPUT N                     {Degree of P(x)}
INPUT A(0), A(1)....A(N)    {Coefficients of P(x)}
INPUT C                     {Constant of integration}
INPUT X                     {Indepedent Variable}
INPUT x                     {List of coefficients of the equation}
'''

import numpy
import copy
#   x:@list coefficients of x  such that x2-x-2 would be [1,-1,-2] and x2-1 would be [1,0,-1]
#   note: it is not p of x as in p(4)
def p(x):
    p = numpy.poly1d(x)
    return p

'''
Evaluating p(x)
'''
def eval_polynomial(x, X):
    equation = p(x)
    A = equation.c      #list of coefficients
    B = copy.copy(A)
    index = len(A)
    counter = len(A) - 1

    B[counter] = A[0] # B(N):=A(N)
    print('b({0}) = {1}'.format(counter, A[0]))
    counter = counter - 1

    for N in range(1,index):
        B[counter] = A[N] + B[counter + 1] * X
        print('b({0}) = {1} +({2})*{3} = {4}'.format(counter, A[N], B[counter + 1], X, B[counter]))
        counter = counter - 1

'''
Evaluating p(x) derivate
'''
def eval_polynomial_derivative(x, X):
    equation = p(x)
    A = equation.c #list of coefficients
    D = copy.copy(A)
    index = len(A) - 1 # -1 to ensure loop stops when K=1
    counter = len(A) - 1

    D[counter - 1] = counter * A[0]  # D(N-1) := N * A(N)
    print("d({0}) = {1} * {2} = {3}".format(counter - 1, counter, A[0], D[counter - 1]));
    counter = counter - 1

    for N in range(1,index): # For K = N-1  DOWNTO 1
        D[counter - 1] = (counter) * A[N] + D[counter] * X # DO D(K-1):= K * A(K) + D(K) * X
        print("d({0}) = {1} * {2} + {3} * {4} = {5}".format(counter - 1, counter, A[N], D[counter], X, D[counter - 1]));

        counter = counter - 1

'''
Algorithm to evaluate I(x)

'''
def eval_polynomial_integral(x,X):
    equation = p(x)
    A = equation.c
    I = copy.copy(x)
    index = len(A)
    counter = len(A) - 1

    I.insert((counter + 1),(A[0] /(counter + 1))) # I(N+1):= A(N)/(N+1)
    print("I({0}) = {1}/{2} = {3}".format(counter + 1, A[0], counter + 1, I[counter + 1]))

    for N in range(1, index): #For K=N DOWNTO 1
        I[counter] =( A[N] / counter ) + I[counter + 1] * X # DO I(K):= A(K-1)/K + I(K+1)*X
        print("I({0}) = {1}/{2} + {3} * {4} = {5}".format(counter, A[N], counter, I[counter + 1], X, I[counter]))
        counter = counter - 1

    #   I(0):= C + I(1)*X
    I[0] = 0 + I[1] * X
    print("I(0) = 0 + {0} * {1} = {2}".format(I[1], X, I[0]))
    print('\n')

    return I[0];

'''
Definite Integral of p(x) taken over two points
upper       {Upper limit}
lower       {Lower limit}
'''
def eval_polynomial_integral_limits(x,upper, lower):
    print('I({0}) - I({1}) = {2}'.format(upper, lower, eval_polynomial_integral(x, upper) - eval_polynomial_integral(x,lower)))

print(eval_polynomial([-0.02, 0.2, -0.4,1.28], 4))
print(eval_polynomial_derivative([-0.02, 0.2, -0.4,1.28], 4))
print(eval_polynomial_integral([-0.02, 0.2, -0.4,1.28], 4))
(eval_polynomial_integral_limits([-0.02, 0.2, -0.4,1.28], 4, 1))


import numpy as np
import scipy
import sympy
from numpy import linalg as lg
from numpy.linalg import solve
from numpy.linalg import eig
from scipy.integrate import quad

# Question 1

# The Eigenvalue is 6

# Question 2

# v is not an eigenvector of A

# Question 3

'''
A.
The basis is:
[ 0]
[-4]

B.
The basis is:
[4]
[4]
'''

# Question 4
'''
a.
False. The condition that Axequalslambdax for some scalar lambda is not sufficient to determine if x is an eigenvector of A. The vector x must be nonzero.

b.
False. There may be linearly independent eigenvectors that both correspond to the same eigenvalue.

c.
True. A steady-state vector for a stochastic matrix is actually an eigenvector because it satisfies the equation Axequalsx.

d.
False. If the matrix is a triangular matrix, the values on the main diagonal are eigenvalues. Otherwise, the main diagonal may or may not contain eigenvalues.

e.
True. An eigenspace of A corresponding to the eigenvalue lambda is the null space of the matrix (AminuslambdaI).
'''

# Question 5
'''
Attempt 1
AD =
FOIL:
(-3 - lambda)(-1 - lambda)
lambda ^2 + 4lambda + 3

AD - BC:
Characteristic polynomial:
lambda ^2 + 4lambda + 1

Set the characteristic polynomial equal to 0 and solve to find the eigenvalues:
lambda ^2 + 4lambda + 1 = 0

Attempt 2
Foil AD =
(5 - lambda)(-1 - lambda)
lambda ^2 - 4 lambda - 5

AD - BC:
lambda ^2 - 4 lambda - 3

Use the quadratic equation to find the roots:
2 + sqrt(7), 2 - sqrt(7)
'''

# Question 6
'''
D1 = (1 - lambda)(2 - lambda)(-lambda)
D2 = 0
D3 = -21

U1 = 0
U2 = -14(1 - lambda)
U3 = 0

D1 + D2 + D3 - U1 - U2 - U3
(1 - lambda)(2 - lambda)(-lambda) + 0 +(-21) - 0 - (-14(1 - lambda)) - 0

(1 - lambda)(2 - lambda)(-lambda) - 21 + 14 - 14lambda
(2 - lambda - 2lambda + lambda^2)(-lambda) - 14lambda - 7
-lambda^3 +3lambda^2- 16lambda -7 # Correct
'''

# Question 7
# The real eigenvalues are just the coefficients of the diagonal
# 0, 2, 2, 9, 9

# Question 8
# Attempt 1
'''
pinv = np.array([[-5, 3],[2, -1]])
print(pinv)

p = np.linalg.inv(pinv)
print(p)

d = np.array([[1,0],[0,3]])
d4 = d ** 4
print(d4)

#Multiply p and D^4
pd4 = np.array([[1,3],[2,405]])
print(pd4)

# Multiply by pinv
final = pd4 * pinv
print(final)
'''

# Attempt 2
p = np.array([[1,2],[3,5]])
print(p)

pinv = np.linalg.inv(p)
print(pinv)

d = np.array([[1,0],[0,2]])
print(d)

d4 = d ** 4
print(d4)

mult = np.dot(p, d4)
print(mult)

a4 = np.dot(mult, pinv)
print(a4) # Correct

# Question 9
'''
p = np.array([[7,0],[3,1]])
'''

# Question 10
# The matrix is just the coefficients

# Question 11
# Attempt 1
# First find the eigenvalues
# Foil:
'''
(-1 - lambda)(6 - lambda) - (-12)
lambda^2 - 5lambda - 18

# Attempt 2
(6 - lambda) (1 - lambda) - (-6)
lambda^2 - 7lambda + 12

Factor:
(lambda - 3)(lambda -4)

# The second vector is the inverse of the bd diagonal
B = np.array([[1, -1], [-3, 2]])

# This is the answer from factoring the polynomial
TB = np.array([[3, 0], [0,4]])

import numpy,from numpy,import scipy,from scipy,import sympy


#numpy solution

import numpy
def primesfrom2to(n):
    """ Input n>=6, Returns a array of primes, 2 <= p < n """
    sieve = numpy.ones(n/3 + (n%6==2), dtype=numpy.bool)
    for i in xrange(1,int(n**0.5)/3+1):
        if sieve[i]:
            k=3*i+1|1
            sieve[       k*k/3     ::2*k] = False
            sieve[k*(k-2*(i&1)+4)/3::2*k] = False
    return numpy.r_[2,3,((3*numpy.nonzero(sieve)[0][1:]+1)|1)]

#pure-python solution

def primes1(n):
    """ Returns  a list of primes < n """
    sieve = [True] * (n/2)
    for i in xrange(3,int(n**0.5)+1,2):
        if sieve[i/2]:
            sieve[i*i/2::i] = [False] * ((n-i*i-1)/(2*i)+1)
    return [2] + [2*i+1 for i in xrange(1,n/2) if sieve[i]]

def primes2(n):
    """ Input n>=6, Returns a list of primes, 2 <= p < n """
    n, correction = n-n%6+6, 2-(n%6>1)
    sieve = [True] * (n/3)
    for i in xrange(1,int(n**0.5)/3+1):
      if sieve[i]:
        k=3*i+1|1
        sieve[      k*k/3      ::2*k] = [False] * ((n/6-k*k/6-1)/k+1)
        sieve[k*(k-2*(i&1)+4)/3::2*k] = [False] * ((n/6-k*(k-2*(i&1)+4)/6-1)/k+1)
    return [2,3] + [3*i+1|1 for i in xrange(1,n/3-correction) if sieve[i]]


from scipy.stats import norm
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
# style.use('fivethirtyeight')
np.random.seed(0)

# from https://www.python-course.eu/expectation_maximization_and_gaussian_mixture_models.php
# also see https://towardsdatascience.com/implement-expectation-maximization-em-algorithm-in-python-from-scratch-f1278d1b9137

datenpunkte_pro_cluster = 20
clusterbereich = np.linspace(-5, 5, num=datenpunkte_pro_cluster)

number_clusters_to_generate = 3
clusters = []
for i in range(number_clusters_to_generate):
    # Datenpunkte, jeweils auf der x-Achse verschoben
    cluster = clusterbereich * np.random.rand(datenpunkte_pro_cluster) + (i - 1) * 18
    clusters.append(cluster)
# Die Beispieldaten X bestehen aus der Summe aler Cluster
X = np.stack(clusters).flatten()

def plot_setup(X):
    number_examples = len(X)

    # Eine Figure zum darstellen der Daten erstellen
    fig = plt.figure(figsize=(10, 10))
    ax0 = fig.add_subplot(111)
    ax0.set_xlim([-33, 33])
    ax0.set_ylim([-0.02, 0.4])

    # Die Datenpunkte plotten
    rgb_color = np.array([0.0, 0.0, 1.0])
    for i in range(number_examples):
        ax0.scatter(X[i], 0.0, color=rgb_color, s=100)

    ax0.set_xlabel("x")
    ax0.set_yticks([])
    ax0.set_yticks([], minor=True)
    ax0.xaxis.label.set_size(30)
    ax0.tick_params(axis='both', which='both', labelsize=20)

    plt.savefig("em1d_0.png")
    plt.close()

def plot_data_and_gaussians(p, mu, var, X, iter):
    number_gaussians = len(mu)
    number_examples = len(p)

    # Eine Figure zum darstellen der Daten erstellen
    fig = plt.figure(figsize=(10, 10))
    ax0 = fig.add_subplot(111)
    ax0.set_ylim([-0.02, 0.4])

    # Die Datenpunkte plotten
    rgb_color = np.zeros(shape=(3))
    for i in range(number_examples):
        # Die Farbe der Datenpunkte entspricht der Verteilung der Wahrscheinlichkeiten p(x | mu, sigma)
        # (wie wahrscheinlich welcher Gauss den Datenpunkt erzeugt und nur für die ersten drei).
        for j in range(min(number_gaussians, 3)):
            rgb_color[j] = p[i][j]

        # Normieren der Farben ist notwendig für die Darstellung
        rgb_color = rgb_color / np.linalg.norm(rgb_color, ord=1)

        # Plotten der Datenpunkte
        ax0.scatter(X[i], 0.0, color=rgb_color, s=100)

    # Ploten der Gaussverteilungen
    plotting_grid = np.linspace(-30, 30, num=300)
    colors = ['r', 'g', 'b', 'm']
    color = colors[0]
    for i in range(number_gaussians):
        gaussian = norm(loc=mu[i], scale=var[i])
        gaussian_pdf = gaussian.pdf(plotting_grid).flatten()

        if i < len(colors):
            color = colors[i]
        ax0.plot(plotting_grid, gaussian_pdf, c=color, linewidth=3.0)

    ax0.set_xlabel("x")
    ax0.set_yticks([])
    ax0.set_yticks([], minor=True)
    ax0.xaxis.label.set_size(30)
    ax0.tick_params(axis='both', which='both', labelsize=20)

    plt.savefig("em1d_" + str(iter + 1) + ".png")
    plt.close()

# y(z) = pi N(x, mu, Sigma) / sum(N(x, mu, Sigma))
# y = f(pi, x, mu, Sigma)
# y = f(mu, var, pi, x)
# y = p
def e_step(mu, var, pi, X):
    """ Calculates probabilities p for every datapoint how likely it has been generated by every gaussian.

    Args:
    mu: Means of the Gaussian distributions.
    var: Variances of the Gaussian distributions.
    pi: Scaling factors ("height") for the Gaussian distributions.
    X: The 1D data in the format X[index_of_example], with every x = X[i] being a scalar

    Returns:
    The probabilities p
    """

    number_gaussians = len(mu)
    number_examples = len(X)
    p = np.zeros((number_examples, number_gaussians))

    # Probability for each datapoint x_i to belong to gaussian g
    gaussians = []
    for i in range(number_gaussians):
        gaussians.append(norm(loc=mu[i], scale=np.sqrt(var[i])))

    for i, gaussian, this_pi in zip(range(number_gaussians), gaussians, pi):
        p[:, i] = this_pi * gaussian.pdf(X)  # Write the probability that x belongs to gaussian i in column i.

    # Normalize the probabilities such that each row of r sums to 1 and weight it by mu_c == the fraction of points belonging to cluster c
    for i in range(len(p)):
        p[i] = p[i] / (np.sum(pi) * np.sum(p, axis=1)[i])

    return p

# N_k = total_p_of_gaussians[i]
def m_step(p, pi, mu, X):
    """M-Step"""
    number_gaussians = len(mu)
    number_examples = len(X)
    x = X.reshape(number_examples, 1)

    total_p = np.sum(p)

    """calculate "contribution weights" of gaussians"""
    total_p_of_gaussians = []
    for c in range(number_gaussians):
        p_this_gaussian_for_all_data = np.sum(p[:, c])
        total_p_of_gaussians.append(p_this_gaussian_for_all_data)

    """update the fraction of contributions (pi) from each cluster"""
    for i in range(number_gaussians):
        pi[i] = (total_p_of_gaussians[i] / total_p)

    """update means mu for each cluster """
    mu = np.sum(p * x, axis=0) / total_p_of_gaussians

    """update variances var"""
    var_c = []
    for i in range(number_gaussians):
        ps_this_gaussian = np.array(p[:, i])
        ps_this_gaussian = ps_this_gaussian.reshape(ps_this_gaussian.shape[0], 1)

        distances_examples_to_cluster = x - mu[i]
        distances_squared = distances_examples_to_cluster * distances_examples_to_cluster

        sigma_this_cluster = (1.0 / total_p_of_gaussians[i]) * np.sum(ps_this_gaussian * distances_squared)

        var_c.append(sigma_this_cluster)

    return pi, mu, var_c

def em(X, iterations: int = 10):
    mu = [-8, 8, 5]
    pi = [1/3, 1/3, 1/3]
    var = [5, 3, 1]

    plot_setup(X=X)
    for iter in range(iterations):
        print("Iteration ", iter, " of ", iterations)
        p = e_step(mu=mu, var=var, pi=pi, X=X)
        plot_data_and_gaussians(p=p, mu=mu, var=var, X=X, iter=iter)
        pi, mu, var = m_step(p=p, pi=pi, mu=mu, X=X)

em(X=X, iterations=14)


# -*- coding: utf-8 -*-
"""
Created on Fri May  8 10:45:02 2020

@author: perip
"""

import numpy as np

#Dadas as seguintes séries temporais
s1 = np.array((168, 398, 451, 337, 186, 232, 262, 349, 189, 204, 220, 220, 207, 239, 259, 258, 242, 331, 251, 323, 106, 1055, 170))
s2 = np.array((168, 400, 451, 300, 186, 200, 262, 349, 189, 204, 220, 220, 207, 239, 259, 258, 242, 331, 251, 180, 106, 1055, 200))

#calcular a distância euclidiana entre as séries
dist = np.linalg.norm(s1-s2)
print("Distância euclidiana entre S1 e S2 : ", dist)

#calcular a série temporal com os valores médios entre s1 e s2
medi = ((s1 + s2) / 2)
print("Vals. Média: ", medi)

#calcular a série temporal com os valores máximos de cada instante entre s1 e s2
maxi = np.maximum(s1, s2)
print("Vals. Máx: ", maxi)

#calcular a série temporal com os valores mínimos de cada instante entre s1 e s2
mini = np.minimum(s1, s2)
print("Vals. Min: ", mini)


import numpy as np

def booleanViewings(ratings):
    """
    Converts the information on which coefficients are non-zero into a boolean matrix
    :param ratings: Sparse matrix of the ratings, with non-zero coefficients when a rating is available
    :return: "Boolean" matrix with a 1 at position (f, u) if the rating (f, u) is available in the ratings matrix ; a 0
              otherwise
    """
    nF, nU = ratings.shape
    viewings = np.zeros((nF, nU))
    nnz_rows, nnz_cols = ratings.nonzero()
    for f, u in list(zip(nnz_rows, nnz_cols)):
        viewings[f, u] = 1
    return viewings

def sparseToDense(S):
    """
    Converts a scipy.sparse sparse matrix into a numpy dense array
    :param S: Input sparse matrix
    :return: Corresponding numpy array
    """
    return np.asarray(S.toarray())

def normalizeRatings(denseRatings, boolViewings):
    """
    Normalizes the ratings on a per-user basis
    :param denseRatings: The ratings matrix, given as a numpy array
    :param boolViewings: A boolean (0 or 1) matrix indicating the disponibility of the ratings (result of
           booleanViewings())
    :return: A 2D numpy array containing the normalized ratings, along with two 1D arrays, containing respectively the
             mean rating and the standard deviation of the ratings of each user
    The normalization process consists in replacing the rating r of a (f, u) film-user couple by (r - m)/s, where m, s
    are the mean/standard deviation of the ratings of u
    """
    user_means = np.sum(denseRatings, axis=0) / np.sum(boolViewings, axis=0)
    user_stdDevs = (np.sum(denseRatings * denseRatings, axis=0) / np.sum(boolViewings,
                                                                         axis=0)) - user_means * user_means
    normalized_ratings = boolViewings * (denseRatings - user_means) / user_stdDevs
    return normalized_ratings, user_means, user_stdDevs

def buildUsersGraph(normRatings, boolViewings, film_weights=[], verbose=True):
    """
    Builds a graph structure between the users, measuring the similarity of ratings of users who have seen at least one
    common film
    :param normRatings: The normalized ratings, result of normalizeRatings()
    :param boolViewings: A boolean (0 or 1) matrix indicating the disponibility of the ratings
                         (result of booleanViewings())
    :param film_weights: Optional weights, one for each film, applied to the terms concerning this film
                         in the similarity sum
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: A similarity matrix between users, and a common viewings matrix indicating for users (u, v) the number of
             common films graded by both u and v
    The two output matrices describe the structure of a graph, whose vertices are the users, and an edge connects two
    users if their number of common viewings is > 0
    The similarity matrix provides weights for these edges, corresponding to a measure of how similar are the gradings
    of the connected users
    The similarity between two users is computed as the sum, over the films f they have both seen, of the product of
    their two grades of f, multiplied optionally by the weight of f
    This corresponds to a non-normalized correlation measure. Possible film weights are for instance the standard
    deviation of the gradings of each film, to give less weight to the films on which all users agree (this has not
    proved useful as of now)
    """
    nF, nU = normRatings.shape
    if len(film_weights) != nF:
        film_weights = np.ones(nF)
    similarities = np.zeros((nU, nU))
    user_commonViewings = np.zeros((nU, nU))
    for f in range(nF):
        if verbose and f % 100 == 0:
            print(f)
        users = np.where(boolViewings[f, :] == 1)[0]
        nRatings = len(users)
        for i in range(nRatings):
            for j in range(i + 1, nRatings):
                similarities[users[i], users[j]] += normRatings[f, users[i]] * normRatings[f, users[j]] * film_weights[
                    f]
                similarities[users[j], users[i]] = similarities[users[i], users[j]]
                user_commonViewings[users[i], users[j]] += 1
                user_commonViewings[users[j], users[i]] += 1
    return similarities, user_commonViewings

def buildFilmsGraph(normRatings, boolViewings, user_weights=[], verbose=True):
    """
    Builds a graph structure between the films, measuring the similarity of ratings of films who have been viewed by
    at least a common user
    :param normRatings: The normalized ratings, result of normalizeRatings()
    :param boolViewings: A boolean (0 or 1) matrix indicating the disponibility of the ratings
    (result of booleanViewings())
    :param user_weights: Optional weights, one for each user, applied to the terms concerning this user in the
                         similarity sum
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: A similarity matrix between films, and a common viewers matrix indicating for films (f, g) the number of
    common users having graded both f and g
    The return arguments contain the structure of a graph, whose vertices are the films, and an edge connects two films
    if their number of common viewers is > 0
    The similarity matrix provides weights for these edges, corresponding to a measure of how similar are the gradings
    of the connected films
    The similarity between two films is computed as the sum, over the users u that have both seen them, of the product
    of his two grades, multiplied optionally by the weight of u
    This corresponds to a non-normalized correlation measure. Using user weights has not been tested as of now.
    """
    return buildUsersGraph(normRatings.transpose(), boolViewings.transpose(), user_weights, verbose)

def buildNeighbors(user_commonViewings):
    """
    Extracts the list of neighbors of each user from the commonViewings matrix
    :param user_commonViewings: Matrix indicating the number of common viewed films for each two users, as returned by
                                buildUsersGraph()
    :return: A list of lists, indicating for each user his neighbors in the graph, ie the users which have at least one
    film in common with him
    Note : can be used in the same way for the films point of view based model
    """
    nU = user_commonViewings.shape[0]
    neighbors = []
    for ui in range(nU):
        n = []
        for uj in range(nU):
            if user_commonViewings[ui, uj] > 0:
                n.append(uj)
        neighbors.append(n)
    return neighbors

def sortNeighbors(neighbors, similarities, useTuples=True, verbose=True):
    """
    Sorts each list of neighbors in decreasing order according to similarities between users
    :param neighbors: List of lists indicating the neighbors of each user, as returned by buildNeighbors()
    :param similarities: Similarity matrix between users, as returned by buildUsersGraph()
    :param useTuples: Boolean parameter, indicating if the function should return a list of list of tuples.
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: A list of lists of tuples, with for each user its neighbors in decreasing order of similarity, in the form
    of (neighbor index, similarity) tuples.
    Only neighbors with positive similarity are keeped. If useTuples is set to False, the output is a simple list of
    lists, containing only neighbor indices instead of tuples to save memory
    Note : can be used in the same way for the films point of view based model
    """
    sorted_neighbors = []
    nU = similarities.shape[0]
    for ui in range(nU):
        if verbose and ui % 100 == 0:
            print(ui)
        n = [(uj, similarities[ui, uj]) for uj in neighbors[ui] if similarities[ui, uj] > 0]
        n = sorted(n, key=lambda x: x[1], reverse=True)
        if not useTuples:
            n1 = []
            for i, j in n:
                n1.append(i)
            sorted_neighbors.append(n1)  # To save memory
        else:
            sorted_neighbors.append(n)
    return sorted_neighbors

def buildUsersModel(ratings, verbose=True):
    """
    Does the whole model building process from the users point of view given the initial sparse ratings matrix
    :param ratings: The initial sparse ratings matrix
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: The built model, as a dictionary containing the following entries:
        - 'nF' : The number of films (ie number of rows in the ratings matrix)
        - 'nU' : The number of users (ie number of columns in the ratings matrix)
        - 'boolViewings' : The boolean (0 or 1) matrix indicating which user has seen which film
        - 'denseRatings' : The ratings matrix in a dense form (numpy array)
        - 'normRatings' : A dense matrix containing the normalized (per user) ratings
        - 'userMeans' : The vector of mean grades given by users
        - 'userStdDevs' : The vector of standard deviations of the grades given by users
        - 'sortedNeighbors' : The list of lists of the neighbors of each user, along with similarities, in decreasing
                              order of similarity
    """
    nF, nU = ratings.shape
    boolViewings = booleanViewings(ratings)
    denseRatings = sparseToDense(ratings)
    normRatings, userMeans, userStdDevs = normalizeRatings(denseRatings, boolViewings)
    similarities, user_commonViewings = buildUsersGraph(normRatings, boolViewings, verbose=verbose)
    neighbors = buildNeighbors(user_commonViewings)
    sorted_neighbors = sortNeighbors(neighbors, similarities, useTuples=True, verbose=verbose)
    model = {'nF': nF, 'nU': nU, 'boolViewings': boolViewings, 'denseRatings': denseRatings, 'normRatings': normRatings,
             'userMeans': userMeans, 'userStdDevs': userStdDevs, 'sortedNeighbors': sorted_neighbors}
    return model

def buildFilmsModel(ratings, verbose=True):
    """
    Does the whole model building process from the films point of view given the initial sparse ratings matrix
    :param ratings: The initial sparse ratings matrix
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: The built model, as a dictionary containing the following entries:
        - 'nF' : The number of films (ie number of rows in the ratings matrix)
        - 'nU' : The number of users (ie number of columns in the ratings matrix)
        - 'boolViewings' : The boolean (0 or 1) matrix indicating which user has seen which film
        - 'denseRatings' : The ratings matrix in a dense form (numpy array)
        - 'normRatings' : A dense matrix containing the normalized (per user) ratings
        - 'userMeans' : The vector of mean grades given by users
        - 'userStdDevs' : The vector of standard deviations of the grades given by users
        - 'similarities' : The matrix of similarities between films
        - 'sortedNeighbors' : The list of lists of the neighbors of each film, in decreasing order of similarity
    """
    nF, nU = ratings.shape
    if verbose:
        print('Preprocessing data')
    boolViewings = booleanViewings(ratings)
    denseRatings = sparseToDense(ratings)
    normRatings, userMeans, userStdDevs = normalizeRatings(denseRatings, boolViewings)
    if verbose:
        print('Building graph')
    similarities, film_commonViewings = buildFilmsGraph(normRatings, boolViewings, verbose=verbose)
    if verbose:
        print('Extracting neighbors')
    neighbors = buildNeighbors(film_commonViewings)
    if verbose:
        print('Sorting neighbors')
    sorted_neighbors = sortNeighbors(neighbors, similarities, useTuples=False, verbose=verbose)
    model = {'nF': nF, 'nU': nU, 'boolViewings': boolViewings, 'denseRatings': denseRatings, 'normRatings': normRatings,
             'userMeans': userMeans, 'userStdDevs': userStdDevs, 'similarities': similarities,
             'sortedNeighbors': sorted_neighbors}
    if verbose:
        print('Model built')
    return model

def usersPrediction(f, u, k, model, verbose=True):
    """
    Predicts the grade of a user for one film given the (users point of view based) model and the parameter k
    :param f: The film index
    :param u: The user index
    :param k: The parameter k of the k nearest neighbors algorithm
    :param model: The prediction model, as built by buildUsersModel()
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: An estimation as a floating point number of the grade the user would give to the film
    The prediction is made by taking as many neighbors as possible of u, but at most k, who have seen the film f, chosen
    by decreasing order of similarity with u.
    The average of their normalized ratings, weighted by their similarities with u, is then computed as a prediction of
    the normalized grade of u for f.
    The grade is then multiplied by u's standard deviation, added to u's mean grade, and culled if necessary between 1
    and 5, to give the final estimate.
    If no neighbors of u with a positive similarity with u have seen the film f, the prediction is simply the average
    grade given by u.
    """
    count = 0
    ref_neighbors = []
    n_neighbors = len(model['sortedNeighbors'][u])
    while len(ref_neighbors) < k and count < n_neighbors:
        n, sim = model['sortedNeighbors'][u][count]
        if model['boolViewings'][f, n] == 1 and sim > 0:
            ref_neighbors.append((n, sim))
        count += 1
    if len(ref_neighbors) == 0:
        if verbose:
            print("No (correlated) neighbors have seen the film, returning average grade")
        return model['userMeans'][u]
    meanGrade = 0
    totWeights = 0
    for n, sim in ref_neighbors:
        meanGrade += (model['userMeans'][u] + model['userStdDevs'][u] * model['normRatings'][f, n]) * sim
        totWeights += sim
    meanGrade /= totWeights
    meanGrade = min(meanGrade, 5)
    meanGrade = max(meanGrade, 1)
    return meanGrade

def filmsPrediction(f, u, k, model, verbose=True):
    """
    Predicts the grade of a user for one film given the (films point of view based) model and the parameter k
    :param f: The film index
    :param u: The user index
    :param k: The parameter k of the k nearest neighbors algorithm
    :param model: The prediction model, as built by buildFilmsModel()
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: An estimation as a floating point number of the grade the user would give to the film
    The prediction is made by taking as many neighbors as possible of f, but at most k, who have been graded by u,
    chosen by decreasing order of similarity with f.
    The average of their normalized grades by u, weighted by their similarities with f, is then computed as a prediction
    of the normalized grade of u for f.
    The grade is then multiplied by u's standard deviation, added to u's mean grade, and culled if necessary between 1
    and 5, to give the final estimate.
    If no neighbors of f with a positive similarity with f have been graded by u, the prediction is simply the average
    grade given by u.
    """
    count = 0
    ref_neighbors = []
    n_neighbors = len(model['sortedNeighbors'][f])
    while len(ref_neighbors) < k and count < n_neighbors:
        n = model['sortedNeighbors'][f][count]
        sim = model['similarities'][f, n]
        if model['boolViewings'][n, u] == 1 and sim > 0:
            ref_neighbors.append((n, sim))
        count += 1
    if len(ref_neighbors) == 0:
        if verbose:
            print("No (correlated) neighbors have been seen by the user")
        return model['userMeans'][u]
    meanGrade = 0
    totWeights = 0
    for n, sim in ref_neighbors:
        meanGrade += model['denseRatings'][n, u] * sim
        totWeights += sim
    meanGrade /= totWeights
    meanGrade = min(meanGrade, 5)
    meanGrade = max(meanGrade, 1)
    return meanGrade

def usersModel_predictionErrorsOverk(k_list, model, testSet=[], verbose=True):
    """
    Computes the prediction train error, and optionally test error, for different values of the parameter k using a user
    point of view based model.
    :param k_list: List of  values of k to use
    :param model: Users based model for making predictions
    :param testSet: Optional testSet, given as a sparse matrix whose non zero values are used as ground truth
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: The list of train errors over k, and the (possibly empty) list of test errors over k
    """
    tr_errors = []
    te_errors = []

    for k in k_list:

        if verbose:
            print('k={}:'.format(k))

        count = 0
        mse = 0
        for u in range(model['nU']):
            if verbose and u % 100 == 0:
                print('User #{}'.format(u + 1))
            for f in range(model['nF']):
                if model['boolViewings'][f, u] == 1:
                    mse += (model['denseRatings'][f, u] - usersPrediction(f, u, k, model, verbose=verbose)) ** 2
                    count += 1
        tr_rmse = np.sqrt(mse / count)
        tr_errors.append(tr_rmse)
        if verbose:
            print("Train RMSE : {}".format(tr_rmse))

        if len(testSet) > 0:
            count = 0
            mse = 0
            nnz_rows, nnz_cols = testSet.nonzero()
            for f, u in list(zip(nnz_rows, nnz_cols)):
                mse += (testSet[f, u] - usersPrediction(f, u, k, model, verbose=verbose)) ** 2
                count += 1
            te_rmse = np.sqrt(mse / count)
            te_errors.append(te_rmse)
            if verbose:
                ("Test RMSE : {}".format(te_rmse))

    return tr_errors, te_errors

def filmsModel_predictionErrorsOverk(k_list, model, testSet=[], verbose=False):
    """
    Computes the prediction train error, and optionnaly test error, for different values of the parameter k using a
    film point of view based model.
    :param k_list: List of  values of k to use
    :param model: Films based model for making predictions
    :param testSet: Optional testSet, given as a sparse matrix whose non zero values are used as ground truth
    :param verbose: If set to True, status prints are made during the execution of the code
    :return: The list of train errors over k, and the (possibly empty) list of test errors over k
    """
    tr_errors = []
    te_errors = []

    for k in k_list:

        if verbose:
            print('k={}:'.format(k))

        count = 0
        mse = 0
        for u in range(model['nU']):
            if verbose and u % 100 == 0:
                print('User #{}'.format(u + 1))
            for f in range(model['nF']):
                if model['boolViewings'][f, u] == 1:
                    mse += (model['denseRatings'][f, u] - filmsPrediction(f, u, k, model, verbose=verbose)) ** 2
                    count += 1
        tr_rmse = np.sqrt(mse / count)
        tr_errors.append(tr_rmse)
        if verbose:
            print("Train RMSE : {}".format(tr_rmse))

        if len(testSet) > 0:
            count = 0
            mse = 0
            nnz_rows, nnz_cols = testSet.nonzero()
            for f, u in list(zip(nnz_rows, nnz_cols)):
                mse += (testSet[f, u] - filmsPrediction(f, u, k, model, verbose=verbose)) ** 2
                count += 1
            te_rmse = np.sqrt(mse / count)
            te_errors.append(te_rmse)
            if verbose:
                ("Test RMSE : {}".format(te_rmse))

    return tr_errors, te_errors

def create_prediction_file_usersModel(outputPath, testSet, model, k, verbose=True):
    """
    Creates a CSV output file containing predictions on the test set made by a user based model
    :param outputPath: Path of the file to create
    :param testSet: Sparse matrix, whose non zero entries indicate the predictions to make
    :param model: User based model to use for making the predictions
    :param k: Parameter k of the k nearest neighbors algorithm for making predictions
    :param verbose: If set to True, status prints are made during the execution of the code
    """
    with open(outputPath, 'w') as output:
        output.write('Id,Prediction\n')
        nnz_row, nnz_col = testSet.nonzero()
        for f, u in list(zip(nnz_row, nnz_col)):
            pred = usersPrediction(f, u, k, model, verbose=verbose)
            output.write('r{}_c{},{}\n'.format(f + 1, u + 1, pred))

def create_prediction_file_filmsModel(outputPath, testSet, model, k, verbose=False):
    """
    Creates a CSV output file containing predictions on the test set made by a film based model
    :param outputPath: Path of the file to create
    :param testSet: Sparse matrix, whose non zero entries indicate the predictions to make
    :param model: Film based model to use for making the predictions
    :param k: Parameter k of the k nearest neighbors algorithm for making predictions
    :param verbose: If set to True, status prints are made during the execution of the code
    """
    with open(outputPath, 'w') as output:
        output.write('Id,Prediction\n')
        nnz_row, nnz_col = testSet.nonzero()
        for f, u in list(zip(nnz_row, nnz_col)):
            pred = filmsPrediction(f, u, k, model, verbose=verbose)
            output.write('r{}_c{},{}\n'.format(f + 1, u + 1, pred))

def writeCSV(outputPath, ratings):
    """
    Creates a CSV output file from a ratings sparse matrix
    :param outputPath: Path of the file to create
    :param ratings: Sparse matrix, whose non zero entries indicate the entries to write into the CSV file
    """
    with open(outputPath, 'w') as output:
        output.write('Id,Prediction\n')
        nnz_row, nnz_col = ratings.nonzero()
        for f, u in list(zip(nnz_row, nnz_col)):
            pred = ratings[f, u]
            output.write('r{}_c{},{}\n'.format(f + 1, u + 1, pred))

def RMSE(ratings, groundTruth):
    """
    Computes the Root Mean Squared Error of a set of ratings given the ground truth:
    :param ratings: The under evaluation ratings sparse matrix
    :param groundTruth: The ground truth ratings sparse matrix
    :return: The RMSE of the ratings as compared to ground truth
    """
    nnz_row, nnz_col = ratings.nonzero()
    mse = 0
    count = 0
    for f, u in list(zip(nnz_row, nnz_col)):
        mse += (ratings[f, u] - groundTruth[f, u]) ** 2
        count += 1
    rmse = np.sqrt(mse / count)
    return rmse


import numpy as np
import matplotlib.pyplot as plt

def condition_points(points):
    """ Conditioning: Normalization of coordinates for numeric stability
    by substracting the mean and dividing by half of the component-wise
    maximum absolute value.
    Args:
        points: (l, 3) numpy array containing unnormalized homogeneous coordinates.

    Returns:
        ps: (l, 3) numpy array containing normalized points in homogeneous coordinates.
        T: (3, 3) numpy array, transformation matrix for conditioning
    """
    t = np.mean(points, axis=0)[:-1]
    s = 0.5 * np.max(np.abs(points), axis=0)[:-1]
    T = np.eye(3)
    T[0:2,2] = -t
    T[0:2, 0:3] = T[0:2, 0:3] / np.expand_dims(s, axis=1)
    ps = points @ T.T
    return ps, T

def enforce_rank2(A):
    """ Enforces rank 2 to a given 3 x 3 matrix by setting the smallest
    eigenvalue to zero.
    Args:
        A: (3, 3) numpy array, input matrix

    Returns:
        A_hat: (3, 3) numpy array, matrix with rank at most 2
    """

    U, d, V = np.linalg.svd(A)
    d[-1] = 0
    A_hat = U @ np.diag(d) @ V
    return A_hat

def compute_fundamental(p1, p2):
    """ Computes the fundamental matrix from conditioned coordinates.
    Args:
        p1: (n, 3) numpy array containing the conditioned coordinates in the left image
        p2: (n, 3) numpy array containing the conditioned coordinates in the right image

    Returns:
        F: (3, 3) numpy array, fundamental matrix
    """

    #
    # You code here
    #

    x1 = p1[:, 0:1]
    y1 = p1[:, 1:2]
    x2 = p2[:, 0:1]
    y2 = p2[:, 1:2]
    I = np.ones((x1.shape[0], 1))
    # Vorlesung l8-stereo Seite 21
    A = np.concatenate([x1*x2, y2*x1, x1, x2*y1, y1*y2, y1, x2, y2, I], axis=1)
    _, _, Vt = np.linalg.svd(A)
    F = Vt.T[:,-1].reshape((3,3))
    F = enforce_rank2(F)
    return F

def eight_point(p1, p2):
    """ Computes the fundamental matrix from unconditioned coordinates.
    Conditions coordinates first.
    Args:
        p1: (n, 3) numpy array containing the unconditioned homogeneous coordinates in the left image
        p2: (n, 3) numpy array containing the unconditioned homogeneous coordinates in the right image

    Returns:
        F: (3, 3) numpy array, fundamental matrix with respect to the unconditioned coordinates
    """

    u1, T1 = condition_points(p1)
    u2, T2 = condition_points(p2)
    F = compute_fundamental(u1, u2)
    # Vorlesung l8-stereo Seite 22
    F = (T1.T).dot(F.dot(T2))
    return F

def draw_epipolars(F, p1, img):
    """ Computes the coordinates of the n epipolar lines (X1, Y1) on the left image border and (X2, Y2)
    on the right image border.
    Args:
        F: (3, 3) numpy array, fundamental matrix
        p1: (n, 2) numpy array, cartesian coordinates of the point correspondences in the image
        img: (H, W, 3) numpy array, image data

    Returns:
        X1, X2, Y1, Y2: (n, ) numpy arrays containing the coordinates of the n epipolar lines
            at the image borders
    """

    n = p1.shape[0]
    P = np.concatenate([p1, np.ones((p1.shape[0], 1))], axis=1)
    l = P @ F.T
    # line coefficients: x^T * l = 0, with l = F*points = (a, b, c)^T
    # Vorlesung l8-stereo Seite 19
    a = l[:, 0]
    b = l[:, 1]
    c = l[:, 2]
    # first point of the line
    X1 = np.zeros(n)
    Y1 = (-c + a * X1) / b
    # last point of the line
    X2 = X1 + img.shape[1] - 1
    Y2 = -(c + a * X2) / b
    return X1, X2, Y1, Y2

def compute_residuals(p1, p2, F):
    """
    Computes the maximum and average absolute residual value of the epipolar constraint equation.
    Args:
        p1: (n, 3) numpy array containing the homogeneous correspondence coordinates from image 1
        p2: (n, 3) numpy array containing the homogeneous correspondence coordinates from image 2
        F:  (3, 3) numpy array, fundamental matrix

    Returns:
        max_residual: maximum absolute residual value
        avg_residual: average absolute residual value
    """

    n = p1.shape[0]
    residuals = []
    for i in range(n):
        residuals.append(np.abs(p1[i,:] @ F @ p2[i,:]))
    max_residual = np.max(residuals)
    avg_residual = np.mean(residuals)
    return max_residual, avg_residual

def compute_epipoles(F):
    """ Computes the cartesian coordinates of the epipoles e1 and e2 in image 1 and 2 respectively.
    Args:
        F: (3, 3) numpy array, fundamental matrix

    Returns:
        e1: (2, ) numpy array, cartesian coordinates of the epipole in image 1
        e2: (2, ) numpy array, cartesian coordinates of the epipole in image 2
    """

    # Vorlesung l8-stereo Seite 19
    V1, d, V2 = np.linalg.svd(F)
    e1 = V1[0:2, 2] / V1[2, 2]
    e2 = V2.T[0:2, 2] / V2[2, 2]
    return e1, e2


import numpy as np

def initializeRandom(dataArray, number_of_clusters, dim, dNo):
    # return k_initial_seeds
    initialseed = []
    for i in range(number_of_clusters):
        initialseed.append([])
        mx = np.max(dataArray, axis=0)
        mn = np.min(dataArray, axis=0)

        for j in range(len(mx)):
            initialseed[i].append(np.random.randint(mn[j], mx[j] + 1))
    reps = np.array(initialseed)
    return reps

def euclideanDistance(rep, data):
    # print(type(rep))
    # print(type(data))
    # return np.sum(np.sqrt(np.abs(rep**2-data**2)))
    # print(np.linalg.norm(rep - data))
    return np.linalg.norm(rep - data)

def manhattanDistance(rep, data):
    return np.sum(np.abs(rep - data), axis=0)

def clustering_k_means(datapointarray, k, reps, dNo):

    clusters = []

    for i in range(dNo):
        data = datapointarray[i]
        dist = []
        for j in range(int(k)):
            clusters.append([])
            # print(reps[j])
            dis = euclideanDistance(reps[j], data)
            dist.append(dis)
        # print("distance = ", dist)
        cNo = dist.index(min(dist))
        # print("Cluster assigned = ", cNo)
        clusters[cNo].append(data)

    return clusters

    # calculate distance from representative [call distance function]
    # assign to cluster
    # find cluster mean [call mean function]
    # set new cluster representative

def clustering_k_medians(datapointarray, k, reps, dNo):

    clusters = []

    for i in range(dNo):
        data = datapointarray[i]
        dist = []
        for j in range(int(k)):
            clusters.append([])
            dis = manhattanDistance(reps[j], data)
            dist.append(dis)

        cNo = dist.index(min(dist))
        clusters[cNo].append(data)

    return clusters

def findmeanofcluster(cluster):
    # cluster = np.delete(cluster, [0], axis=0)
    return np.mean(cluster, axis=0)

def findmedianofcluster(cluster):
    # return median
    return np.median(cluster, axis=0)


#count the inversions in an array of integers
#based on merge sort algorithm
import numpy as np
def merge(list1, list2):
    i = 0
    j = 0
    count = 0
    out = []
    for k in range(len(list1 + list2)):
        if list1[i] <= list2[j]:
            out.append(list1[i])
            i += 1
            if i == len(list1):
                out.extend(list2[j:])
                return out, count
        else:
            out.append(list2[j])
            j += 1
            count += len(list1) - i
            if j == len(list2):
                out.extend(list1[i:])
                return out, count
    return out, count

def divide(l):
    n = len(l)
    return l[0:n//2], l[n//2:]

def inv_count(l):
    if len(l) == 1:
        return l, 0
    else:
        list1, list2 = divide(l)
        #print(list1, list2)
        list1_sorted, count1 = inv_count(list1)
        list2_sorted, count2 = inv_count(list2)
        list_sorted, count_cross = merge(list1_sorted, list2_sorted)
        return list_sorted, count1 + count2 + count_cross

if __name__ == "__main__":
    data = np.loadtxt("./course1/week2/IntegerArray.txt")
    l = data.tolist()
    s, count = inv_count(l)
    print(count)


import numpy as np
import matplotlib.pyplot as plt

def minmax(t, u):
    """
    Compute all local minima and maxima of the function u(t),
    represented by discrete points in the arrays u and t.
    Return lists minima and maxima of (t[i],u[i]) extreme points.
    """
    minima = []; maxima = []
    for n in range(1, len(u)-1, 1):
        if u[n-1] > u[n] < u[n+1]:
            minima.append((t[n], u[n]))
        if u[n-1] < u[n] > u[n+1]:
            maxima.append((t[n], u[n]))
    return minima, maxima

def periods(extrema):
    """
    Given a list of (t,u) points of the maxima or minima,
    return an array of the corresponding local periods.
    """
    p = [extrema[n][0] - extrema[n-1][0]
         for n in range(1, len(extrema))]
    return np.array(p)

def amplitudes(minima, maxima):
    """
    Given a list of (t,u) points of the minima and maxima of
    u, return an array of the corresponding local amplitudes.
    """
    # Compare first maxima with first minima and so on
    a = [(abs(maxima[n][1] - minima[n][1]))/2.0
         for n in range(min(len(minima),len(maxima)))]
    return np.array(a)

def test_empirical_analysis():
    t = np.linspace(0, 6*np.pi, 1181)
    # Modulated amplitude and period
    u = np.exp(-(t-3*np.pi)**2/12.0)*np.cos(np.pi*(t + 0.6*np.sin(0.25*np.pi*t)))
    plt.plot(t, u, label='signal')
    plt.hold('on')
    minima, maxima = minmax(t, u)
    t_min = [ti for ti, ui in minima]
    t_max = [ti for ti, ui in maxima]
    u_min = [ui for ui, ui in minima]
    u_max = [ui for ui, ui in maxima]
    plt.plot(t_min, u_min, 'bo', label='minima')
    plt.plot(t_max, u_max, 'ro', label='maxima')
    plt.legend()

    plt.figure()
    p = periods(maxima)
    a = amplitudes(minima, maxima)
    plt.plot(range(len(p)), p, 'g--', label='periods')
    plt.hold('on')
    plt.plot(range(len(a)), a, 'y-', label='amplitudes')
    plt.legend()

    p_ref = np.array([
        1.48560059,  2.73158819,  2.30028479,  1.42170379,  1.45365219,
        2.39612999,  2.63574299,  1.45365219,  1.42170379])
    a_ref = np.array([
        0.00123696,  0.01207413,  0.19769443,  0.59800044,  0.90044961,
        0.96007725,  0.42076411,  0.08626735,  0.0203696 ,  0.00312785])
    p_diff = np.abs(p - p_ref).max()
    a_diff = np.abs(a - a_ref).max()
    tol = 1E-7
    assert p_diff < tol
    assert a_diff < tol

if __name__ == '__main__':
    test_empirical_analysis()
    plt.show()


import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

# Open the image
img = np.array(Image.open('house.jpg')).astype(np.uint8)

# Sobel Operator
h, w, d = img.shape

# define filters
horizontal = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])  # s2
vertical = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])  # s1

# define images with 0s
newgradientImage = np.zeros((h, w, d))

# offset by 1
for channel in range(d):
    for i in range(1, h - 1):
        for j in range(1, w - 1):
            horizontalGrad = (horizontal[0, 0] * img[i - 1, j - 1, channel]) + \
                             (horizontal[0, 1] * img[i - 1, j, channel]) + \
                             (horizontal[0, 2] * img[i - 1, j + 1, channel]) + \
                             (horizontal[1, 0] * img[i, j - 1, channel]) + \
                             (horizontal[1, 1] * img[i, j, channel]) + \
                             (horizontal[1, 2] * img[i, j + 1, channel]) + \
                             (horizontal[2, 0] * img[i + 1, j - 1, channel]) + \
                             (horizontal[2, 1] * img[i + 1, j, channel]) + \
                             (horizontal[2, 2] * img[i + 1, j + 1, channel])

            verticalGrad = (vertical[0, 0] * img[i - 1, j - 1, channel]) + \
                           (vertical[0, 1] * img[i - 1, j, channel]) + \
                           (vertical[0, 2] * img[i - 1, j + 1, channel]) + \
                           (vertical[1, 0] * img[i, j - 1, channel]) + \
                           (vertical[1, 1] * img[i, j, channel]) + \
                           (vertical[1, 2] * img[i, j + 1, channel]) + \
                           (vertical[2, 0] * img[i + 1, j - 1, channel]) + \
                           (vertical[2, 1] * img[i + 1, j, channel]) + \
                           (vertical[2, 2] * img[i + 1, j + 1, channel])

            # Edge Magnitude
            mag = np.sqrt(pow(horizontalGrad, 2.0) + pow(verticalGrad, 2.0))
            # Avoid underflow: clip result
            newgradientImage[i - 1, j - 1, channel] = mag

# now add the images r g and b
rgb_edge = newgradientImage[:,:,0] + newgradientImage[:,:,1] + newgradientImage[:,:,2]

plt.figure()
plt.title('RGBSobelEdge')
plt.imsave('RGBSobel_house.jpg', rgb_edge, cmap='gray')
plt.imshow(rgb_edge, cmap='gray')
plt.show()


import numpy as np

from TCalc.age_eye import age_to_eye_diameter

# This first set of functions are dependent ONLY on the telescope
# and don't require any information about the eyepiece
def focal_ratio(f_o,D_o):
    """Calculates the Focal Ratio for the telescope
    Args:
        f_o(float): Focal Length of Objective/Aperture (mm)
        D_o(float): Diameter of Objective/Aperture (mm)
    Returns:
        float: Focal Ratio/f-number (:math: `f_R`) of the telescope
    """
    return f_o/D_o

def dawes_lim(D_o):
    """Calulates the Reosultion of the telescope according to the Dawes Limit

    Args:
         D_o (float): Diameter of Objective/Aperture (mm)
    Returns:
        float: Resolving power of the telescope arroding to Dawes Limit (arc-sec)

    Note:
        This function is generally useful when you are doing filter-less observations. For operation with filters, check Resolution Power function.
    """
    return 115.8/D_o

def resolving_power(W,D_o):
    """Calulates the Reosultion of the telescope

    Args:
        W (float): Wavelength of light recieved (nm)
        D_o (float): Diameter of Objective/Aperture (mm)
    Returns:
        float: Resolving power of the telescope (:math: `P_R`) (arc-sec)

    Note:
        This function is generally useful when you are using filters. For filter-less observations, check Dawes Limit function.
    """
    return 0.2516616*(W/D_o)

def Min_magnification(D_o,D_eye=7,age=None):
    """Calulates the minimum usable magnification of the telescope
    Args:
         D_o (float): Diameter of Objective/Aperture (mm)
         D_eye (float): Diameter of Eyepiece (mm); default is 7 mm
    Returns:
        float: Minimum Usable Magnification (:math: `M_min`) of the telescope

    """
    if age is None:
        D_eye = D_eye
    else:
        D_eye = age_to_eye_diameter(age)

    return D_o/D_eye

def Max_magnification(D_o):
    """Calulates the maximum usable magnification of the telescope
    Args:
         D_o (float): Diameter of Objective/Aperture (mm)
    Returns:
        float: Maximum Usable Magnification (:math: `M_max`) of the telescope

    Note:
        The maximum usable magnification also depends upon seeing.
    """
    return 2*D_o

def Min_eyepiece(f_o,M_max):
    """Calulates the minimum focal length of eyepiece you can use on the telescope
    Args:
         f_o (float): Focal length of Objective/Aperture (mm)
         M_max (float): Maximum usable magnification
    Returns:
        float: The minimum focal length of eyepiece (:math: `f_{e-min}`) you can use with your telescope

    """
    return f_o/M_max

def Max_eyepiece(f_R, D_eye=7, age=None):
    """Calulates the maximum focal length of eyepiece you can use on the telescope
    Args:
         D_eye (float): Diameter of Eyepiece (mm); default is 7 mm
         f_R (float): Focal Ratio/f-number of telescope
    Returns:
        float: The maximum focal length of eyepiece (:math: `f_{e-max}`) you can use with your telescope

    """
    if age is None:
        D_eye = D_eye
    else:
        D_eye = age_to_eye_diameter(age)

    return f_R*D_eye

def Lmag_limit(D_o):
    """Calculates the magnitude of the faintest star which can be seen from the telescope

    Args:
         D_o (float): Diameter of Objective/Aperture (mm)

    Returns:
        float: Limiting Magnitude (LM) of telescope
    """
    return 2 + 5*np.log10(D_o)

# These next functions depend on the properties of the telescope
# and the eyepiece, and no other information
def magnification(f_o,f_e):
    """Calculates the Angular Magnification for the telescope
    Args:
        f_o(float): Focal Length of Objective/Aperture (mm)
        f_e(float): Focal Length of Eyepiece (mm)
    Returns:
        float: Visual Magnification (M) of the telescope
    """
    return f_o/f_e

def true_fov(M, fov_e=50):
    """Calulates the True Field of View (FOV) of the telescope & eyepiece pair
    Args:
        fov_e (float): FOV of eyepiece; default 50 deg
        M (float): Magnification of Telescope
    Returns:
        float: True Field of View (deg)

    """
    return fov_e/M

def exit_pupil(f_e,f_R):
    """Calulates the Exit Pupil of the telescope & eyepiece pair
    Args:
        f_e (float): Focal Length of eyepiece (mm)
        f_R (float): Focal Ratio of telescope
    Returns:
        float: Diameter of Exit Pupil(:math: `D_EP`) (mm)

    Note:
        For optimal usage, :math: `D_EP \approx 2-3 mm` is the optimum range for maximizing the resolving power of the eye
    """
    return f_e/f_R

def surface_brightness(D_EP):
    """Calculates the surface brightness of the object as seen from the telescope

    Args:
         D_o (float): Diameter of Exit Pupil (mm)

    Returns:
        percentage: Surface Brightness (SB)
    """
    return 2*(D_EP**2)


import numpy as np
import pymc as pm
from matplotlib import pyplot as plt

# The parameters to be inferred. We only know them here because we are synthesising the data.
true_alpha = 10
true_beta = 50

num_flashes = 5000

# Generate the angles
true_thetas = np.random.uniform(low=-0.5*np.pi, high=0.5*np.pi, size=num_flashes)

# Generate the x coordinates of the flashes along the coastline
data = true_alpha + true_beta * np.tan(true_thetas)

alpha = pm.Normal("alpha", 0, 1.0/50**2)
beta = pm.Exponential("beta", 1.0/100)

# @pm.stochastic(name="obs", dtype=np.float64, observed=True)
# def obs(value=data, alpha=alpha, beta=beta):
#     return np.sum(np.log(beta) - np.log(np.pi) - np.log(beta**2 + (alpha-value)**2))

@pm.observed(name="obs", dtype=np.float64)
def obs(value=data, alpha=alpha, beta=beta):
    return np.sum(np.log(beta) - np.log(np.pi) - np.log(beta**2 + (alpha-value)**2))

model = pm.Model([alpha, beta, obs])

mcmc = pm.MCMC(model)
mcmc.sample(40000, 10000, 1)

alpha_samples = mcmc.trace("alpha")[:]
beta_samples = mcmc.trace("beta")[:]

# histogram of the samples:

plt.hist(alpha_samples, normed = True)
plt.show()

plt.hist(beta_samples, normed = True)
plt.show()


# An example from Wikipedia
# https://en.wikipedia.org/wiki/Gradient_descent

import time
import math;
import numpy as np

def G(x):

  x1 = x[0]
  x2 = x[1]
  x3 = x[2]
  return np.array([
    3*x1 - math.cos(x2*x3) - 3/2,
    4*x1**2 - 625*x2**2 + 2*x2 -1,
    math.exp(-x1*x2) + 20*x3 + (10*math.pi - 3)/3
  ])

def J(x):
  x1 = x[0]
  x2 = x[1]
  x3 = x[2]
  return np.array([
    [3, math.sin(x2*x3)*x3,  math.sin(x2*x3)*x2],
    [8*x1, -1250*x2 + 2, 0],
    [-x2*math.exp(-x1*x2), -x1*math.exp(-x1*x2), 20]
  ])

def error(x):
  return 0.5 * ((x**2 - 1)**2)

def solve():
  x = np.array([5, 5, 5])

  for i in range(100):
    g = G(x)
    j = J(x)

    delta = np.linalg.solve(j, -g)
    x = x + delta

  g = G(x)
  print(x)
  print(np.dot(g, g))

if __name__ == '__main__':

  start_time = time.time()

  solve()
  print(f'{(time.time() - start_time) * 1000} ms')


import numpy as np

# data = "3,4,3,1,2"
data = "1,4,3,3,1,3,1,1,1,2,1,1,1,4,4,1,5,5,3,1,3,5,2,1,5,2,4,1,4,5,4,1,5,1,5,5,1,1,1,4,1,5,1,1,1,1,1,4,1,2,5,1,4,1,2,1,1,5,1,1,1,1,4,1,5,1,1,2,1,4,5,1,2,1,2,2,1,1,1,1,1,5,5,3,1,1,1,1,1,4,2,4,1,2,1,4,2,3,1,4,5,3,3,2,1,1,5,4,1,1,1,2,1,1,5,4,5,1,3,1,1,1,1,1,1,2,1,3,1,2,1,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,4,5,1,3,1,4,4,2,3,4,1,1,1,5,1,1,1,4,1,5,4,3,1,5,1,1,1,1,1,5,4,1,1,1,4,3,1,3,3,1,3,2,1,1,3,1,1,4,5,1,1,1,1,1,3,1,4,1,3,1,5,4,5,1,1,5,1,1,4,1,1,1,3,1,1,4,2,3,1,1,1,1,2,4,1,1,1,1,1,2,3,1,5,5,1,4,1,1,1,1,3,3,1,4,1,2,1,3,1,1,1,3,2,2,1,5,1,1,3,2,1,1,5,1,1,1,1,1,1,1,1,1,1,2,5,1,1,1,1,3,1,1,1,1,1,1,1,1,5,5,1"

fish = np.zeros(shape=(9, 1), dtype=np.int64)
for i in np.array(data.split(",")).astype(int):
    fish[i] += 1

numdays = 256
for i in range(0, numdays):
    newfish = np.copy(fish)
    newfish[0:8] = fish[1:9]
    newfish[8] = fish[0]
    newfish[6] += fish[0]
    fish = newfish
    print(f"afer day {i+1} {np.sum(fish)}")


import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import inv
import csv

def read_csv(filename):
    with open(filename, 'rt') as csv_file:
        points = []
        buffer = csv.reader(csv_file)
        for row in buffer:
            points.append([float(x) for x in row])
    return points

def compute_polynoms(points):
    a = points[1]
    n = a.size

    h = np.zeros(n - 1)
    for i in range(n - 1):
        h[i] = points[0][i + 1] - points[0][i]

    u = np.zeros((n, n))
    u[0][0] = 1
    u[n - 1][n - 1] = 1
    for i in range(1, n - 1):
        u[i][i - 1] = h[i - 1]
        u[i][i] = 2 * (h[i - 1] + h[i])
        u[i][i + 1] = h[i]

    v = np.zeros(n)
    for i in range(1, n - 1):
        v[i] = (3 * (a[i + 1] - a[i]) / h[i]) - \
            (3 * (a[i] - a[i - 1]) / h[i - 1])

    c = inv(u).dot(v)

    d = np.zeros(n - 1)
    b = np.zeros(n - 1)

    for i in range(n - 1):
        d[i] = (c[i + 1] - c[i]) / (3 * h[i])
        b[i] = (a[i + 1] - a[i]) / h[i] - ((2 * c[i] + c[i + 1]) * h[i]) / 3

    return a, b, c, d

def spline_interpolation(points, x):
    a, b, c, d = compute_polynoms(points)
    k = -1
    for i in range(points[0].size - 1):
        if points[0][i] <= x and x <= points[0][i + 1]:
            k = i

    return a[k] + (b[k] * (x - points[0][k])) + (c[k] * (x - points[0][k]) ** 2) + (d[k] * (x - points[0][k]) ** 3)

if(__name__ == "__main__"):
    points = np.array(read_csv("points.csv"))
    interval = np.linspace(min(points[0]), max(points[0]), 5000)
    interpolation = [spline_interpolation(points, x) for x in interval]
    plt.plot(interval, interpolation)
    plt.plot(points[0], points[1], 'o')
    plt.show()


import math
import numpy as np

from .utils import pad_image

def G(x, sigma):
    """
Calculates a gaussian kernel"""
    return ((1 / (2 * math.pi * pow(sigma, 2))) *
            math.exp(-(pow(x, 2) / (2 * pow(sigma, 2)))))

def spatial_component(n, sigma):
    """
Calculates the spatial gaussian kernel
Arguments:
    n     -- filter size
    sigma -- variance"""
    center = int((n - 1) / 2) # index
    def dist(x, y):
        return math.sqrt(pow(x - center, 2) + pow(y - center, 2))
    matrix = np.zeros([n, n])
    for i in range(n):
        for j in range(n):
            matrix[i, j] = G(dist(i, j), sigma)
    return matrix

def range_component(region, n, sigma):
    """
Calculates the range gaussian kernel of a region of the image
Arguments:
    region -- region of the image to use
    n      -- filter size
    sigma  -- variance"""
    center = int((n - 1) / 2)
    pixel = region[center, center]
    matrix = np.zeros([n, n])
    for i in range(n):
        for j in range(n):
            Ii = region[i, j]
            matrix[i, j] = G(Ii - pixel, sigma)
    return matrix

def filter(image, n, sigma_s, sigma_r):
    """
Bilateral Filter
Arguments:
    image -- input image
    n     -- size of the filter
    σs    -- space variance
    σr    -- range variance"""

    gs = spatial_component(n, sigma_s)

    padding_needed = math.floor(n / 2)

    result = np.empty_like(image)
    working_area = pad_image(image, padding_needed)

    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            region = working_area[i:i + n, j:j + n]
            gr = range_component(region, n, sigma_r)
            w = gr * gs
            Wp = np.sum(w)
            If = np.sum(w * region)
            result[i, j] = If / Wp

    return result


from sympy import *

# https://docs.sympy.org/latest/install.html
# conda install sympy
# conda install mpmath

# Sobre matrices: https://docs.sympy.org/latest/tutorial/matrices.html

def eval_mai(G1, G2, C1, C2):
  return Matrix([[s*C1+G1, -G1,        -s*C1,    0   ], \
                 [-G1,     G1+G2+s*C2, -G2,  -   s*C2], \
                 [-s*C1,   -G2,         s*C1+G2, 0   ], \
                 [0,       -s*C2,       0,       s*C2]])

def eval_f_in_s(f, attributes, ww):
  f_eval = []
  for w in ww:
    attributes['s'] = w*I
    f_eval.append(f.evalf(subs=attributes))
  return f_eval

# Para mejorar la impresion
init_printing()

# Defino simbolos
G1 = Symbol('G1')
G2 = Symbol('G2')
C1 = Symbol('C1')
C2 = Symbol('C2')
s = Symbol('s')

# Cargo la MAI
print('Matriz admitancia indefinida para el T puenteado.')
print(eval_mai(G1, G2, C1, C2))

# Computamos la transferencia de tension.
# Recordamos la expresion:
# $ V_{14}^{34} = (-1)^{-1-4-3-4} \frac{Y_{14}^{34}}{Y_{14}^{14}} $

# Cargo la matriz definida del numerador.
Y_14_34 = eval_mai(G1, G2, C1, C2)
Y_14_34.row_del(3)
Y_14_34.row_del(2)
Y_14_34.col_del(3)
Y_14_34.col_del(0)

# Cargo la matriz definida del denominador.
Y_14_14 = eval_mai(G1, G2, C1, C2)
Y_14_14.row_del(3)
Y_14_14.row_del(0)
Y_14_14.col_del(3)
Y_14_14.col_del(0)

# Computo la transferencia
V_14_34 = (-1.0)**(-1.0-4.0-3.0-4.0) * Y_14_34.det() / Y_14_14.det()
V_14_34 = cancel(V_14_34)
print('La transferencia de tension es:')
print(V_14_34)


import numpy as np

class LinearRegression(object):
    def __init__(self, learn_rate):
        self.weights = np.array([])
        self.learn_rate = learn_rate

    def _compute_gradient(self, X, Y):
        return (X.T.dot(X.dot(self.weights) - Y)) * 2 / len(Y)

    def fit(self, X, Y):
        intercept = np.ones((len(X), 1))
        X = np.append(intercept, X, 1)

        self.weights = np.zeros((len(X.T)))
        grad = np.ones((len(X.T), 1))

        while np.linalg.norm(grad) > 0.001:
            grad = self._compute_gradient(X, Y)
            self.weights -= grad * self.learn_rate

    def predict(self, X):
        intercept = np.ones((len(X), 1))
        X = np.append(intercept, X, 1)

        return X.dot(self.weights)

class Ridge(LinearRegression):
    def __init__(self, learn_rate, alpha):
        super().__init__(learn_rate)
        self.alpha = alpha

    def _compute_gradient(self, X, Y):
        return (((X.T.dot(X.dot(self.weights) - Y)) * 2
                + self.weights * self.alpha * 2)
                / len(Y))


# +++++++++++++++
# PROBLEM DISCRIPTION
# +++++++++++++++
#A googol (10^100) is a massive number: one followed by one-hundred zeros;
# 100^100 is almost unimaginably large: one followed by two-hundred zeros.
# Despite their size, the sum of the digits in each number is only 1.
#
# Considering natural numbers of the form, a^b, where a, b < 100, what is the maximum digital sum?
#
# +++++++++++++++
import numpy as np
import math

def digitalSum(n):
    nDigits = [int(i) for i in str(n)]
    sum = 0
    for digit in nDigits:
        sum += digit
    return sum

maxDigitalSum = 0

for a in range(1,100):
    for b in range(1,100):
        num = a ** b
        sum = digitalSum(num)
        if sum > maxDigitalSum:
            maxDigitalSum = sum
            print a, "^" , b, " with digital sum of ", maxDigitalSum, " is the new max"

digitalSum(num)
print "Fin"


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

style.use('fivethirtyeight')

#xs = np.array([1,2,3,4,5,6], dtype=np.float64)
#ys = np.array([5,4,6,5,6,7], dtype=np.float64)

def create_dataset(hm, variance, step=2, correlation=False):
    val = 1
    ys = []
    for i in range(hm):
        y = val + random.randrange(-variance, variance)
        ys.append(y)
        if correlation and correlation == 'pos':
            val +=step
        elif 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):
    m = ( ((mean(xs) * mean(ys)) - mean(xs*ys)) /
        ((mean(xs)*mean(xs)) - mean(xs*xs)))

    b = mean(ys) - m*mean(xs)

    return m, b

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

def coefficient_of_determination(ys_orig,ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]
    squared_error_regr = squared_error(ys_orig, ys_line)
    squared_error_y_mean = squared_error(ys_orig, y_mean_line)
    return 1 - (squared_error_regr/ squared_error_y_mean)

xs, ys = create_dataset(40, 10, 2, correlation='pos')

m ,b= best_fit_slope_and_intercept(xs,ys)

regression_line = [(m*x)+b for x in xs]

predict_x = 8
predict_y = (m*predict_x)+b

r_squared = coefficient_of_determination(ys, regression_line)
print(r_squared)

plt.scatter(xs,ys)
plt.plot(xs, regression_line)
plt.scatter(predict_x,predict_y, s=100, color='g')

plt.show()


# coding: utf-8
'''
激活函数
用来处理一些非线性的东西
'''

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-7, 7, 180)  # 生成-7~7的等间距180个点

# 激活函数的原始实现(直接套用数学公式定义)
def sigmoid(input):
    '''
    sigmoid: S型函数
    形状如y = 1/e^x
  '''
    y = [1 / float(1 + np.exp(-x)) for x in input]
    return y

def relu(input):
    '''
    当x<=0时 y=0,
    当x>0时 y=x的运算
  '''
    y = [x * (x > 0) for x in input]
    return y

def tanh(input):
    '''tan 函数'''
    y = []
    for x in input:
        y1 = np.exp(x) - np.exp(-x)
        y2 = float(np.exp(x) + np.exp(-x))
        y.append(y1 / y2)
    return y

def softPlus(input):
    y = [np.log(1 + np.exp(x)) for x in input]
    return y

# 使用tf定义
tf_sigmoid = tf.nn.sigmoid(x)
tf_relu = tf.nn.relu(x)
tf_tanh = tf.nn.tanh(x)
tf_softPlus = tf.nn.softplus(x)
# 运算每个的结果
sess = tf.Session()
y_sigmoid, y_relu, y_tanh, y_softPlus = sess.run(
    [tf_sigmoid, tf_relu, tf_tanh, tf_softPlus])
sess.close()

# 绘制图像
plt.subplot(221)
plt.plot(x, y_sigmoid, label="sigmoid")

plt.subplot(222)
plt.plot(x, y_relu, label="relu")

plt.subplot(223)
plt.plot(x, y_tanh, label="tanh")

plt.subplot(224)
plt.plot(x, y_softPlus, label="softPlus")

plt.show()


import numpy as np

class KMeans(object):
	def __init__(self, inputs, clusters):
		self.inputs = np.array(inputs)
		if self.inputs.ndim == 1:
			self.inputs = self.inputs.reshape(self.inputs.shape[0], 1)
		self.shuffle()
		self.test_cases = self.inputs.shape[0]
		self.num_clusters = clusters
		self.clusters = self.inputs[np.random.choice(self.test_cases, clusters, replace=False), :]
		self.cluster_dist = np.zeros((self.test_cases, self.num_clusters))

	def shuffle(self):
		np.random.shuffle(self.inputs)

	def calculate_cluster_distances(self):
		for i in range(self.num_clusters):
			self.cluster_dist[:, i] = np.sqrt(np.sum((self.inputs - self.clusters[i])**2, axis=1))

	def assign_data_points(self):
		assigned_cluster = np.where(self.cluster_dist.argsort(axis=1) == 0)[1]
		for i in range(self.num_clusters):
			points = self.inputs[assigned_cluster == i]
			self.clusters[i] = points.sum(axis=0)/points.shape[0]

	def train(self):
		while True:
			old_clusters_norm = np.linalg.norm(self.clusters, axis=1)
			self.shuffle()
			self.calculate_cluster_distances()
			self.assign_data_points()
			new_clusters_norm = np.linalg.norm(self.clusters, axis=1)

			if np.isnan(self.clusters).any() == True:
				# If one of the clusters is stuck at nan then start whole
				# process again from begining.
				self.clusters = self.inputs[np.random.choice(
					self.test_cases, self.num_clusters, replace=False), :]
			elif (old_clusters_norm == new_clusters_norm).all() == True:
				break

	def assign_clusters(self, inputs):
		cluster_dist = np.zeros((inputs.shape[0], self.num_clusters))
		for i in range(self.num_clusters):
			cluster_dist[:, i] = np.sqrt(np.sum((inputs - self.clusters[i])**2, axis=1))
		assigned_clusters = np.where(cluster_dist.argsort(axis=1) == 0)[1]
		return assigned_clusters, cluster_dist


import numpy as np

def copy(x):
    if isinstance(x,np.ndarray):
        return x
    if isinstance(x, int) or isinstance(x, float):
        return x
    cp = []
    for i in x:
        cp.append(copy(i))
    return cp

def hierarchical_cluster(inp, beta=0.0, min_c=None, max_c=None):
    """
    clusters the data with hierarchical clustering
    :param inp: data to cluster
    :param beta: a parameter indicating the significance of the size of two joined clusters. a larger beta would mean
    that clusters with a large size are not likely to be joined with other clusters with great size
    default to 0: cluster size is insignificant
    :param min_c: minimum amount of clusters
    :param max_c: maximum amount of clusters
    :return: an array of integers, each indicating the cluster of the input parameter with whom they share an index
    """
    prev_score = 0
    clusters = [[i] for i in inp]
    steps = [{"clusters": clusters}]

    if min_c is None:
        min_c = 1

    def min_distance(x, y):
        m = np.Inf
        for i in x:
            for j in y:
                d = np.sum((i-j)**2)
                if d < m:
                    m = d
        return np.sqrt(m)

    def stamp_mark(x, y):
        return min_distance(x, y) + (beta*min(len(x), len(y)))

    while len(clusters) > min_c:
        clusters = copy(clusters)
        join1, join2 = 0, 0
        min_score = np.Inf
        for i in xrange(len(clusters)):
            for j in xrange(i+1, len(clusters)):
                score = stamp_mark(clusters[i], clusters[j])
                if score < min_score:
                    min_score = score
                    join1 = i
                    join2 = j
        clusters[join1] += clusters[join2]
        del clusters[join2]
        steps.append({"clusters": clusters})
        steps[-2]['stamp_mark'] = min_score-prev_score
        prev_score = min_score

    steps = steps[:-1]

    if max_c is not None:
        steps = steps[max_c:]

    max_score = 0
    step = steps[0]['clusters']
    for i in steps:
        if i['stamp_mark'] > max_score:
            max_score = i['stamp_mark']
            step = i['clusters']

    def cluster_of(x):
        for i in xrange(len(step)):
            for j in step[i]:
                if (j == x).all():
                    return i

    return [cluster_of(x) for x in inp]

def main():
    data = np.array([[136,345],[109,362],[139,373],[129,70],[105,84],[121,84],[394,246],[386,257],[400,256]])
    clusters = hierarchical_cluster(data)
    print clusters

if __name__ == '__main__':
    main()

My Machine Learning Kit/unsupervised/HierarchicalCluster.py


# -*- coding: utf-8 -*-
import numpy as np

def average_difference(data: np.ndarray) -> float:
    """计算平均偏差（不常用）

    :param data: 要计算的数据
    :return: 返回平均偏差

    Example:

    >>> average_difference(np.array([0.2,0.3,0.2,0.1]))
    0.05000000000000001
    """
    x_ = data.mean()
    return np.abs(data - x_).mean()

def std(data: np.ndarray, ddof: int = 1) -> float:
    """ 计算修正的样本标准差

    :param data: 要求标准差的数据
    :param ddof: 修正值，除法分母为n - ddof，默认为1
    :return: 标准差

    >>> std(np.array([0.2,0.2,0.2,0.2]))
    0.0
    """
    return data.std(ddof=ddof)

def variation_coefficient(data: np.ndarray, ddof: int = 1) -> float:
    """计算样本的变异系数

    :param data:
    :param ddof:
    :return:

    >>> variation_coefficient(np.array([41.24,41.27,41.23,41.26]))
    0.00044260408687289926
    """
    return std(data,ddof) / data.mean()


#!/usr/bin/env python
import os
import sys
import argparse
import string
import warnings
import shutil

import math, random
import numpy as np

#from jodl gayatin
#Imagine four points randomly selected on a surface of a sphere. These points form the vertices of an imaginary tetrahedron. What is the probability that the tetrahedron contains the center of the sphere? Imagine four points randomly selected on a surface of a sphere. These points form the vertices of an imaginary tetrahedron. What is the probability that the tetrahedron contains the center of the sphere?

#from https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere

def fibonacci_sphere(samples=1,randomize=True):
    rnd = 1.
    if randomize:
        rnd = random.random() * samples

    points = []
    offset = 2./samples
    increment = math.pi * (3. - math.sqrt(5.));

    for i in range(samples):
        y = ((i * offset) - 1) + (offset / 2);
        r = math.sqrt(1 - pow(y,2))

        phi = ((i + rnd) % samples) * increment

        x = math.cos(phi) * r
        z = math.sin(phi) * r

        points.append(np.array([x,y,z]))

    return points

#from https://stackoverflow.com/questions/25179693/how-to-check-whether-the-point-is-in-the-tetrahedron-or-not
def sameside(v1,v2,v3,v4,p):
    normal = np.cross(v2-v1, v3-v1)
    result =  np.dot(normal, v4-v1) * np.dot(normal, p-v1) > 0
    return result

def tetraCoord(A,B,C,D):
    v1 = B-A ; v2 = C-A ; v3 = D-A
    # mat defines an affine transform from the tetrahedron to the orthogonal system
    mat = np.concatenate((np.array((v1,v2,v3,A)).T, np.array([[0,0,0,1]])))
    # The inverse matrix does the opposite (from orthogonal to tetrahedron)
    M1 = np.linalg.inv(mat)
    return(M1)

def pointInsideT(v1,v2,v3,v4,p):
    # Find the transform matrix from orthogonal to tetrahedron system
    try:
        M1=tetraCoord(v1,v2,v3,v4)
        # apply the transform to P
        p1 = np.append(p,1)
        newp = M1.dot(p1)
        # perform test
        return(np.all(newp>=0) and np.all(newp <=1) and sameside(v2,v3,v4,v1,p))
    except np.linalg.linalg.LinAlgError:
        return False

def parseArgs():
    """ Simply parse the arguments """
    parser = argparse.ArgumentParser(description = 'Check if apk size is out of cellular download limit.')
    args = parser.parse_args()
    return args

def main():
    # parse arguments
    args = parseArgs()
    #checkAndroidApkSize(args)
    numPointsOnSphere = 50
    points = fibonacci_sphere(numPointsOnSphere, False)
    numInside = 0;
    numTests = 0;
    origin = np.array([0,0,0])
    for p1 in points:
        for p2 in points:
            for p3 in points:
                for p4 in points:
                    numTests += 1
                    if pointInsideT( p1, p2, p3, p4, origin):
                        numInside += 1;

    print "numPointsOnSphere = ", numPointsOnSphere
    print "NumTests = ", numTests
    print "NumInside = ", numInside
    print "Probabiliy=", float(numInside) / float(numTests)

if __name__ == "__main__":
    main()


import numpy as np

# Random Number Generator - Uniform Distribution
def U():
        return np.random.rand()

# Integrated Function
def f(x):
    return np.exp(x)

if __name__ == "__main__":
    # Number of Random Numbers (Preferably use big number since this method converges rather slowly)
    N = 100000

    # Limits of Integration
    a, b = 0, 10

    # It is not actually mandatory to be the exact max{f(x)}
    f_max = f(b)

    # "Volume" of Integration
    V = (b-a) * f_max

    Hits = 0

    for i in range(N):
        x = (b-a) * U() + a
        y = f_max * U()

        if y <= f(x):
            Hits += 1

    # Probalitiy to Hit
    p = Hits/N

    # Integral Computed with Hit-or-Miss Method
    I = V * p

    # Integral Error
    δI = V/np.sqrt(N) * np.sqrt(p-p**2)

    print(f'Ι ± δI = {I:.2f} ± {δI:.2f}')