import matplotlib.pyplot as plt
import numpy as np

def Re_Z(f,R1,R2,C2):
    """
    Return the real part of the complex impedance of a circuit formed by a series of a resistance (R1), a capacitor (C1) and a parallel of a resistance (R2) and a capacitor (C2).
    ---------------------------------------------------------------------
    INPUT:
        f: array, N dimensional. frequency array
        R1, C1, R2, C2: real, scalar. Resistnces and capacitors.
    OUTPUT:
        Real part of the impedance
    """
    w = 2*np.pi*f
    return R1 + R2/(1+(w*C2*R2)**2)

def Im_Z(f,R1,C1,R2,C2):
    """
    Returns the imaginary part of the complex impedance of a circuit formed by a series of a resistance (R1), a capacitor (C1) and a parallel of a resistance (R2) and a capacitor (C2).
    ---------------------------------------------------------------------
    INPUT:
        f: array, N dimensional. frequency array
        R1, C1, R2, C2: real, scalar. Resistnces and capacitors.
    OUTPUT:
        Real part of the impedance
    """
    w = 2*np.pi*f
    return -1.0/(w*C1)-w*C2*R2**2/(1+(w*C2*R2)**2)

R1 = 90 # Ohm
C1 = 20e-6 # F
R2 = 7e3 # Ohm
C2 = 0.3e-6 # F

# Frequency extremes
f_min = 1 #Hz
f_max = 1e6 #Hz

# sampling frequency
sf = 1 #Hzu
# sampling points
sp = (f_max-f_min)*sf

# Frequency partition
f_part = np.geomspace(f_min,f_max,int(sp))

# Calculatin real and imaginary part of the impedance
Z_real = Re_Z(f_part,R1,R2,C2)
Z_imag = Im_Z(f_part,R1,C1,R2,C2)

# Calculating modulus and phase of the impedance
Z_modulus = np.sqrt(Z_real**2 + Z_imag**2)
Z_phase = np.arctan2(Z_imag,Z_real)

# Creating visualisation
fig, ax = plt.subplots(2, sharex = True)
ax[0].plot(f_part,20*np.log10(Z_modulus))
ax[0].set_ylabel("|Z| [dB]")
ax[1].plot(f_part,180/(np.pi)*Z_phase)
ax[1].set_ylabel("Phase [°]")
ax[1].set_xscale("log")
ax[1].set_xlabel("Frequency [Hz]")
#---------------------------------------------------------

#Johnson-Nyquist Noise formula
k_b = 1.38064852e-23 #m2 kg s-2 K-1
T = 300
S = 4*T*k_b*Z_real

# Creating visualization
fig1,ax1 = plt.subplots()
ax1.semilogx(f_part,10*np.log10(S))
ax1.set_ylabel(r"$S_{\nu}$ [dB]")
ax1.set_xlabel("Frequency [Hz]")
plt.show()


import math
import matplotlib.pyplot as plt
import numpy as np
import data_class as dc
import json

ROOT = 0.176923

def algebraic_equasion_function(x):
    """
    This function makes a calculation for an Algebraic equasion
    It calculates f(x) with the given equasion and x as a parameter
    """
    formula = x**2 + 6*x + 9
    return formula

def algebraic_equasion_defunction(x):
    """
    This function makes a calculation for an Algebraic equasion
    It calculates f'(x) with the given equasion and x as a parameter
    """
    formula = 2*x + 6
    return formula

def algebraic_u(x):
    """
    This function makes a calculation for an Algebraic equasion
    It calculates u(x) = f(x)/f'(x) with the given equasion and x as a parameter
    """
    f = algebraic_equasion_function(x)
    de_f = algebraic_equasion_defunction(x)
    u = f / de_f
    return u

def function_F_of_X(x):  # function - f(x)
    """
    This function makes a calculation for Non-Algebraic equasion
    It calculates f(x) with the given equasion and x as a parameter
    """
    solution = x ** 3 + x + 2 * math.sqrt(math.e ** x)
    return solution

def DE_function_F_of_X(x):  # function - f'(x)
    """
    This function makes a calculation for Non-Algebraic equasion
    It calculates f'(x) with the given equasion and x as a parameter
    """
    solution = 3 * x ** 2 + 1 + math.sqrt(math.e ** x)
    return solution

def solve_U_function_of_X(x):  # function - u(x)
    """
    This function makes a calculation for Non-Algebraic equasion
    It calculates u(x) = f(x)/f'(x) with the given equasion and x as a parameter
    """
    f_of_x = function_F_of_X(x)
    de_f_of_x = DE_function_F_of_X(x)
    variable_u = f_of_x / de_f_of_x
    return variable_u

json_data = []

first = True

def save_data(the_data):
    json_data.append([{"VALUES": i.VALUES, "F_X": i.F_X, "DEF_X": i.DEF_X, "P1": i.P1, "P3": i.P3, "ERROR": i.ERROR,
                       "counter": i.counter} for i in the_data])

def save_data_to_json(filename_str: str):
    with open(filename_str, "w") as file:
        json.dump(json_data, file)
    json_data.clear()

def read_data_from_json(filename_str: str):
    with open(filename_str, "r") as file:
        data = json.load(file)
    return data

def print_data(filename_str: str, title: str):
    data = read_data_from_json(filename_str)
    list_Lables = []
    NR_non_al = []
    MP_non_al = []

    for d in data[:(int(len(data) / 2))]:
        print(f"Value: {d[0]['VALUES']} Counter: {d[-1]['counter']}")
        list_Lables.append(d[0]['VALUES'])
        NR_non_al.append(d[-1]['counter'])
    print()

    for d in data[(int(len(data) / 2)):]:
        print(f"Value: {d[0]['VALUES']} Counter: {d[-1]['counter']}")
        MP_non_al.append(d[-1]['counter'])

    make_a_plot(list_Lables, NR_non_al, MP_non_al, title)

def solve_until_zero(x, counter, temp_data):  # function calculate f(x) till f(x) == 0
    the_data = temp_data
    while True:
        f_of_x = round(function_F_of_X(x), 4)  # Our f'(x) for the table!
        if first:
            new_x = x - solve_U_function_of_X(x)  # Our X new for the table!
        else:
            new_x = x - 1 / 12 * (5 * solve_U_function_of_X(x) + 7 * f_of_x / DE_function_F_of_X(
                x - 6 * solve_U_function_of_X(x) / 7))  # Our X new for the table!

        p1 = calculate_p(x, new_x, 1)  # p1
        p3 = calculate_p(x, new_x, 3)  # p3
        #  DEBUG print(f"VALUE: {x}, F_OF_X:{f_of_x}, DE_F_OF_X: {de_f_of_x}, NEW_VALUE: {new_x}")
        if counter == -1:
            counter += 1
            the_data.append(dc.data_container(x, f_of_x, DE_function_F_of_X(x), "", "", calculate_error(x), counter))
        else:
            counter += 1
            if round(f_of_x, 4) != 0:
                the_data.append(
                    dc.data_container(new_x, round(function_F_of_X(new_x), 4), DE_function_F_of_X(new_x), p1, p3,
                                      calculate_error(new_x), counter))
                # for i in range(0, the_data.counter_DC()):
                #     the_data.print(i)
                return solve_until_zero(new_x, counter, the_data)
            else:
                break

    save_data(the_data)

def calculate_error(x: float):  # function to calculate an error ERROR
    error = abs(x - ROOT)
    return error

def calculate_p(old_x, new_x: float, power: int):  # function to calculate an error P1 (Power of 1)
    p = abs(new_x - ROOT) / abs(old_x - ROOT) ** power
    return p

def call_NA(list: list):
    global first
    counter = -1
    first = True
    for element in list:
        temp_data = []
        solve_until_zero(element, counter, temp_data)
        # print('\n')
    first = not first
    for element in list:
        temp_data = []
        solve_until_zero(element, counter, temp_data)
        # print('\n')

def solve_until_zeroA(x, counter, temp_data):  # function calculate f(x) till f(x) == 0
    the_data_algebraic = temp_data
    while True:
        f_of_x = round(function_F_of_X(x), 4)  # Our f'(x) for the table!
        if first:
            new_x = x - algebraic_u(x)  # Our X new for the table!
        else:
            new_x = x - 1 / 12 * (5 * algebraic_u(x) + 7 * round(algebraic_equasion_function(x),
                                                                 4) / algebraic_equasion_defunction(
                x - 6 * algebraic_u(x) / 7))

        p1 = calculate_p(x, new_x, 1)  # p1
        p3 = calculate_p(x, new_x, 3)  # p3
        if counter == -1:
            counter += 1
            the_data_algebraic.append(
                dc.data_container(x, f_of_x, DE_function_F_of_X(x), "", "", calculate_error(x), counter))
        else:
            counter += 1
            if round(f_of_x, 4) != 0:
                the_data_algebraic.append(
                    dc.data_container(new_x, round(function_F_of_X(new_x), 4), DE_function_F_of_X(new_x), p1, p3,
                                      calculate_error(new_x), counter))
                return solve_until_zero(new_x, counter, the_data_algebraic)
            else:
                break

    save_data(the_data_algebraic)

def call_A(list: list):
    global first
    counter = -1
    for element in list:
        temp_data = []
        solve_until_zeroA(element, counter, temp_data)
        # print('\n')
    first = not first
    for element in list:
        temp_data = []
        solve_until_zeroA(element, counter, temp_data)
        # print('\n')

def make_a_plot(labels_parameter: list, NewtonMethod: list, MultiPoint: list, title: str):
    """
    This function makes a plot of the given data for comparison
    """
    x = np.arange(len(labels_parameter))
    width = 0.35

    fig, ax = plt.subplots()
    rects1 = ax.bar(x - width / 2, MultiPoint, width, label='Multi-Point Method')
    rects2 = ax.bar(x + width / 2, NewtonMethod, width, label='Newtons Method')

    ax.set_ylabel('Number of Repeats')
    ax.set_title(title)
    ax.set_xticks(x)
    ax.set_xticklabels(labels_parameter)
    ax.legend()
    ax.bar_label(rects1, padding=3)
    ax.bar_label(rects2, padding=3)
    fig.tight_layout()
    plt.show()

if __name__ == "__main__":
    labels = [-35, -25, -15, -10, -7.5, -5, 0, 5, 15, 25, 35, 50]
    print("NON ALGEBRAIC:")
    call_NA(labels)
    #print(json_data)
    save_data_to_json("data1.json")
    read_data_from_json("data1.json")
    print_data("data1.json", 'Non-Algebraic')
    print("ALGEBRAIC:")
    call_A(labels)
    #print(json_data)
    save_data_to_json("data2.json")
    read_data_from_json("data2.json")
    print_data("data2.json", 'Algebraic')


import numpy as np
import matplotlib.pyplot as plt
import random
import itertools
from scipy.io import loadmat

def initialize_centroids(X, k):
    """X: dataset, k: number of centroids"""

    number_of_samples = X.shape[0]
    sample_points_ids = random.sample(range(0, number_of_samples), k)

    centroids = [tuple(X[id]) for id in sample_points_ids]
    unique_centroids = list(set(centroids))

    number_of_unique_centroids = len(unique_centroids)

    while number_of_unique_centroids < k:
        new_sample_points_ids = random.sample(range(0, number_of_samples), k - number_of_unique_centroids)
        new_centroids = [tuple(X[id]) for id in new_sample_points_ids]
        unique_centroids = list(set(unique_centroids + new_centroids))

        number_of_unique_centroids = len(unique_centroids)

    return np.array(unique_centroids)

def euclidean_distance(x,y):
    """Computes euclidean distance between matrices x and y."""

    x_sq = np.reshape(np.sum(x * x, axis=1), (x.shape[0], 1))
    y_sq = np.reshape(np.sum(y * y, axis=1), (y.shape[0]), 1)
    xy = x @ y.T

    dist = x_sq + y_sq - 2 * xy

    return np.sqrt(dist)

def assign_clusters(X, centroids):
    """ assigns the points to one of the centroids"""

    k = centroids.shape[0]

    clusters = {}

    distance_matrix = euclidean_distance(X, centroids)

    closest_cluster_ids = np.argmin(distance_matrix, axis=1)

    for i in range(k):
        clusters[i] = []

    for i, cluster_id in enumerate(closest_cluster_ids):
        clusters[cluster_id].append(X[i])

    return clusters

def stop_iteration(old_centroids, new_centroids, threshold):
    ''' stopping condition'''
    dist = euclidean_distance(old_centroids, new_centroids)
    centroids_stop= np.max(dist.diagonal()) <= threshold

    return centroids_stop

def kmeans(X, k,threshold=0):
    """Performs k-means and find k cluster centroid"""

    new_centroids = initialize_centroids(X=X, k=k)

    centroids_covered = False

    while not centroids_covered:
        previous_centroids = new_centroids
        clusters = assign_clusters(X, previous_centroids)

        new_centroids = np.array([np.mean(clusters[key], axis=0, dtype=X.dtype) for key in sorted(clusters.keys())])

        centroids_covered = stop_iteration(previous_centroids, new_centroids, threshold)

    return new_centroids

k = 8
x1 = loadmat('clustering_data2.mat')
data = x1['X']
X = data.T

centroids = kmeans(X, k, threshold=1/100)

clusters = assign_clusters(X, centroids)

marker = itertools.cycle(('+','o','^','s','*','x','p','H'))

plt.figure()
for centroid, points in clusters.items():
    points = np.array(points)
    centroid = np.mean(points, axis=0)

    plt.scatter(points[:, 0], points[:, 1], marker=next(marker), s = 50)
    plt.grid()
    plt.scatter(centroid[0], centroid[1], marker='x', color="black")

plt.show()


# Reference: https://www.sfu.ca/~ssurjano/branin.html

import pygpbo
import numpy as np
def F(p1,p2,noise=0):
  return -(np.power((p2-5.1/(4*np.power(3.14,2))*np.power(p1,2)+5/3.14*p1-6),2)+10*(1-1/(8*3.14))*np.cos(p1)+10) + noise*np.random.randn()

bounds = {'p1': (-5, 10), 'p2': (0, 15)}

optimizer = pygpbo.BayesOpt(F, bounds)
optimizer.add_custom_points([{'p1': -4, 'p2': 1}, {'p1': -3, 'p2': 5},{'p1': 9, 'p2': 10},{'p1': 4, 'p2': 14}])

optimizer.maximize(n_iter=30)
print("Optimum point and value: ", optimizer.max_sample)

# Output seen:
"""
Optimum point and value:  ({'p1': 9.474934424992359, 'p2': 2.373669396349481}, array([-0.43209275]))
"""


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

###
# Name: Alley and Royal
# Student ID: 2293544
# Email: busick@chapman.edu
# Course: PHYS220/MATH220/CPSC220 Fall 2018
# Assignment: CW07
###

import numpy as np

def s(T, t, n):

    #k values
    array = np.arange(1, n+1)

    def function(k):
        return (((1)/(2*k-1))*np.sin((2*(2*k-1)*np.pi*t)/(T)))
    summation = np.vectorize(function)
    value = (4/np.pi)*np.sum(summation(array))
    return value

def f(T, t):
    if 0 < t < (T/2):
        return 1
    elif t == 0:
        return 0
    elif -(T/2) < t < 0:
        return -1
    else:
        print("Out of range")
        return None


import numpy as np
import os

def compute_euclidean_distance(point, centroid):
    return np.sqrt(np.sum((point - centroid) ** 2))

def assign_label_cluster(distance, data_point, centroids):
    index_of_minimum = min(distance, key=distance.get)
    return [index_of_minimum, data_point, centroids[index_of_minimum]]

def compute_new_centroids(cluster_label, centroids):
    return np.array(cluster_label + centroids) / 2

def iterate_k_means(data_points, centroids, total_iteration):
    label = []
    cluster_label = []
    total_points = len(data_points)
    k = len(centroids)

    for iteration in range(0, total_iteration):
        for index_point in range(0, total_points):
            distance = {}
            for index_centroid in range(0, k):
                distance[index_centroid] = compute_euclidean_distance(data_points[index_point],
                                                                      centroids[index_centroid])
            label = assign_label_cluster(distance, data_points[index_point], centroids)
            centroids[label[0]] = compute_new_centroids(label[1], centroids[label[0]])

            if iteration == (total_iteration - 1):
                cluster_label.append(label)

    return [cluster_label, centroids]

def print_label_data(result):
    print("Result of k-Means Clustering: \n")
    for data in result[0]:
        print("data point: {}".format(data[1]))
        print("cluster number: {} \n".format(data[0]))
    print("Last centroids position: \n {}".format(result[1]))

def create_centroids():
    centroids = []

    centroids.append([-4.282 ,2.269])
    centroids.append([-4.334 ,- 2.43])
    centroids.append(  [0.044 ,- 5.791])
    return np.array(centroids)

filename = os.path.dirname(__file__) + "\data.csv"
data_points = np.genfromtxt(filename, delimiter=",")
centroids = create_centroids()
total_iteration = 130

[cluster_label, new_centroids] = iterate_k_means(data_points, centroids, total_iteration)
print_label_data([cluster_label, new_centroids])
print()


from random import *
from numpy import *

darts=input("How many darts? ")

x=random.random(darts)
y=random.random(darts)
one=ones(darts)

dist=sqrt(x*x+y*y)
print "dist ",dist
hit=(less_equal(dist,one)==True)
print "hit ",hit
hits=sort(hit)
print "hits ",hits
hitsnum=hits.searchsorted(True, side="right") - hits.searchsorted(True, side="left")
print "hitsnum ",hitsnum

pic=4.0*hitsnum/darts
print "Pi calculated as ",pic
error=100*(pic-3.1415926535897932)/(3.1415926535897932)
print "Error is ", error, "%"


"""
implement bayesian analysis of proportion/fraction/percent type parameter
like bias of coin, % of mutations etc
"""
from math import sqrt, exp, log
import numpy as np
import matplotlib.pyplot as plt
from SciInf_utilities import *
import sys
#--------------------------------------
#
print("\n implement bayesian analysis of proportion/fraction/percent type parameter")
print(" like bias of coin, % of mutations etc")
print(" work with log p \n")
if(len(sys.argv) == 3):
  n_pos = int(sys.argv[1])
  n_neg = int(sys.argv[2])
else:
  n_pos = int(input('Enter # of positive events/samples> '))
  n_neg = int(input('Enter # of negative events/samples> '))
print('\n # of positive, negative events: ',n_pos,n_neg,'\n')
# main
#
#generate pdf and cdf
#
df = 1./(NPOINT + 1)
f_axis = np.zeros(NPOINT)
log_f_pdf = np.zeros(NPOINT)
for i in range(NPOINT):
  f_axis[i] = (i+1)*df
  ff = f_axis[i]
  ffm1 = 1. - ff
  # f_pdf[i] = (ff**n_pos)*(ffm1**n_neg)
  log_f_pdf[i] = n_pos*log(ff) + n_neg*log(ffm1)
pdf_max = max(log_f_pdf)
log_f_pdf = log_f_pdf - pdf_max
f_pdf = np.exp(log_f_pdf)
f_cdf = pdf_to_cdf(f_axis,f_pdf)
write_pdf_cdf(f_axis,f_pdf,f_cdf,title='fraction pdf cdf',filename='fraction_pdf_cdf.dat')
summarize(f_axis,f_pdf,f_cdf,title='fraction parameter')
#
# plot
#
if(MAKEPLOT):
  plt.figure()
  plt.plot(f_axis,f_pdf,'g-')
  plt.plot(f_axis,f_cdf,'r-')
  plt.xlabel(' fraction (r)')
  plt.ylabel(' prob(r)')
  plt.title(' posterior pdf, cdf for fraction')
  plt.ylim((0.,1.2))
  plt.grid(True)
  plt.show()


import numpy as np

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

# print function
def identity_function(x):
    return x

# 1st -> 2nd layer
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])

A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
print(Z1)

# 2nd -> 3rd layer
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])

A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
print(Z2)

# 3rd -> return layer
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])

A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)
print(Y)

# multi layer neural network
def init_network():
    network = {}

    network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
    network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
    network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])

    network['B1'] = np.array([0.1, 0.2, 0.3])
    network['B2'] = np.array([0.1, 0.2])
    network['B3'] = np.array([0.1, 0.2])

    return network

def foward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    B1, B2, B3 = network['B1'], network['B2'], network['B3']

    A1 = np.dot(x, W1) + B1
    Z1 = sigmoid(A1)
    A2 = np.dot(Z1, W2) + B2
    Z2 = sigmoid(A2)
    A3 = np.dot(Z2, W3) + B3
    Y = identity_function(A3)

    return Y

network = init_network()
x = np.array([1.0, 0.5])
y = foward(network, x)
print(y)


# # Quaternion class implementation for python.
#
# Offers native integration of operators between quaternions generated and
# numpy data structures.
#
# Toby James 2018

import numpy as np
class Quaternion():
    """
    Quaternion class implemented to allow manipulation of quaternions with
    each other, matrices (including, where appropriate, vectors) and scalars.

    Declare a quaternion of the form q = w + xi + yj +zk by

    >> q = Quaternion(w,x,y,z)

    Addition, subtraction, multiplication and division are all supported natively
    between quaternions and where appropriate with other data types.

    Transformation of quaternions is also supported:

    Normalised unit quaternion:     >> q_u = q.unit()
    Conjugate of q:                 >> q_c = q.conjugate()
    Reciprocal or inverse of q:     >> q_r = q.reciprocal() or >> q_i = q.inverse(). They are equivalent.

    Tolerance can be applied to the unit quaternion function to determine the
    unit quaternion to a desired accuracy.

    >> q_u = q.unit(tolerance=0.0001)

    """

    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z
        self.magnitude = np.sqrt(self.w**2 + self.x**2 + self.y**2 + self.z**2)

    def __repr__(self):             #Appropriate representation
        return "Quaternion(%r + %r i + %r j + %r k)" % (self.w, self.x, self.y, self.z)

    def unit(self, tolerance=0):#Creates the equivalent unit quaternion
    #By default, produces unit quaternion with an arbitrary tolerance ie magnitude will not perfectly be 1 - but should be sufficiently close
    #if tolerance is given, recursively divides by magnitude until the new magnitude is sufficiently close to 1 as desired
        if not tolerance:
            return Quaternion(self.w/self.magnitude,
                              self.x/self.magnitude,
                              self.y/self.magnitude,
                              self.z/self.magnitude)
        else:
            new_q = self.unit()     #scarily close to recursion
            while abs(1 - new_q.magnitude) > tolerance:
                new_q = new_q.unit()
            return new_q

    def conjugate(self):            #Create the quaternion conjugate
        return Quaternion(self.w,
                          -self.x,
                          -self.y,
                          -self.z)

    def reciprocal(self):           #Create the reciprocal
        return (self.conjugate()/(self.magnitude**2))

    def inverse(self):              #The same
        return self.reciprocal()

    def __add__(self,other):        #Addition of quaternions
        if isinstance(other, Quaternion):
            return Quaternion(self.w+other.w,self.x+other.x, self.y+other.y, self.z+other.z)
        else:
            raise TypeError("Unable to broadcast together types Quaternion and %r." %type(other))

    def __sub__(self,other):        #Subtraction of quaternions
        if isinstance(other, Quaternion):
            return Quaternion(self.w-other.w,self.x-other.x, self.y-other.y, self.z-other.z)
        else:
            raise TypeError("Unable to broadcast together types Quaternion and %r." %type(other))

    # For multiplication, python 3.5+ supports @ as the matrix multiplication operator.
    # Improvements in readability may be gained by replacing .dot functions with @.

    # It is not (currently) in __future__, so from __future__ import does not allow for
    # use with python versions < 3.5.

    # No non-stylistic improvements, it breaks backwards compatibility and more than 2
    # matrices are never multiplied here therefore it is probably not currently worth
    # implementing.

    def __mul__(self, other):
        # Allow for right multiplication by scalars, matrices and quaternions
        if isinstance(other, Quaternion):
            x = self.x * other.w + self.y * other.z - self.z * other.y + self.w * other.x
            y = -self.x *other.z + self.y * other.w + self.z * other.x + self.w * other.y
            z = self.x * other.y - self.y * other.x + self.z * other.w + self.w * other.z
            w = -self.x *other.x - self.y * other.y - self.z * other.z + self.w * other.w
            return Quaternion(w,x,y,z)
        elif isinstance(other, np.ndarray):
            if other.shape[0] == 4:
                return np.array([self.w,self.x,self.y,self.z]).dot(other)
            else:
                raise ValueError("Operand with shape (%r,%r) could not be broadcast with a quaternion." % other.shape)
        elif isinstance(other, (int, float)):
            return Quaternion(self.w*other,
                              self.x*other,
                              self.y*other,
                              self.z*other)
        else:
            raise TypeError("Multiplication of quaternion with %r is not supported." % type(other))

    def __rmul__(self,other):
        # Allow for left multiplication by matrices and scalars (quaternion right multiplication handled above)
        if isinstance(other, np.ndarray):
            if other.shape[1] == 4:
                return other.dot(np.array([self.w,self.x,self.y,self.z]))
            else:
                raise ValueError("Operand with shape (%r,%r) could not be broadcast with a quaternion." % other.shape)
        elif isinstance(other, (int, float)):
            return Quaternion(self.w*other,
                              self.x*other,
                              self.y*other,
                              self.z*other)
        else:
            raise TypeError("Multiplication of quaternion with %r is not supported." %type(other))

    def __truediv__(self, other):
        # Allow for division by scalars and quaternions: division by other types is undefined
        if isinstance(other, Quaternion):
            mag_2 = other.magnitude**2
            w = (other.w*self.w + other.x*self.x + other.y*self.y + other.z*self.z)/mag_2
            x = (other.w*self.x - other.x*self.w - other.y*self.z + other.z*self.y)/mag_2
            y = (other.w*self.y + other.x*self.z - other.y*self.w - other.z*self.x)/mag_2
            z = (other.w*self.z - other.x*self.y + other.y*self.x - other.z*self.w)/mag_2
            return Quaternion(w,x,y,z)
        elif isinstance(other, (int, float)):
            return Quaternion(self.w/other,
                              self.x/other,
                              self.y/other,
                              self.z/other)
        else:

            raise TypeError("Division of quaternion by given types is not supported.")

    def to_vector(self):
        return np.array([self.x, self.y, self.z])

    def basis(self):
        a_11 = 1 - 2*((self.y)**2 + (self.z)**2)
        a_12 = 2*(self.x*self.y + self.z*self.w)
        a_13 = 2*(self.x*self.z-self.y*self.w)
        a_21 = 2*(self.x*self.y - self.z*self.w)
        a_22 = 1 - 2*((self.x)**2 + (self.z)**2)
        a_23 = 2*(self.y*self.z + self.x*self.w)
        a_31 = 2*(self.x*self.z + self.y*self.w)
        a_32 = 2*(self.y*self.z - self.x*self.w)
        a_33 =1 - 2*((self.x)**2 + (self.y)**2)

        A = np.array([a_11, a_12, a_13, a_21, a_22, a_23, a_31, a_32, a_33])

        return A.reshape(3,3)

    __array_priority__ = 10000  # big number so numpy respects left matrix multiplication with quaternions


import numpy as np

def EaseInSine(t):
    return 1-np.sin((1-t)*np.pi/2)

def EaseOutSine(t):
    return np.sin(t*np.pi/2)

def EaseInOutSine(t):
    return 0.5-0.5*np.sin((1-t*2)*np.pi/2)

def EaseInQuad(t):
    return t**2

def EaseOutQuad(t):
    return 1-EaseInQuad(1-t)

def EaseInOutQuad(t):
    return (t<0.5) * 0.5 * EaseInQuad(t * 2) + (t>=0.5) * 0.5 * (EaseOutQuad((t-0.5)*2) + 1)

def EaseInCubic(t):
    return t**3

def EaseOutCubic(t):
    return 1-EaseInCubic(1-t)

def EaseInOutCubic(t):
    return (t<0.5) * 0.5 * EaseInCubic(t * 2) + (t>=0.5) * 0.5 * (EaseOutCubic((t-0.5)*2) + 1)

def EaseInQuart(t):
    return t**4

def EaseOutQuart(t):
    return 1-EaseInQuart(1-t)

def EaseInOutQuart(t):
    return (t<0.5) * 0.5 * EaseInQuart(t * 2) + (t>=0.5) * 0.5 * (EaseOutQuart((t-0.5)*2) + 1)

def EaseInQuint(t):
    return t**5

def EaseOutQuint(t):
    return 1-EaseInQuint(1-t)

def EaseInOutQuint(t):
    return (t<0.5) * 0.5 * EaseInQuint(t * 2) + (t>=0.5) * 0.5 * (EaseOutQuint((t-0.5)*2) + 1)

def EaseInExpo(t):
    return (2**(t*10)-1)/1023

def EaseOutExpo(t):
    return 1-EaseInExpo(1-t)

def EaseInOutExpo(t):
    return (t<0.5) * 0.5 * EaseInExpo(t * 2) + (t>=0.5) * 0.5 * (EaseOutExpo((t-0.5)*2) + 1)

def EaseInCirc(t):
    return 1-np.sqrt(1-t**2)

def EaseOutCirc(t):
    return 1-EaseInCirc(1-t)

def EaseInOutCirc(t):
    return (t<0.5) * 0.5 * EaseInCirc((t<0.5) * t * 2) + (t>=0.5) * 0.5 * (EaseOutCirc((t>=0.5) * (t-0.5)*2) + 1)

def EaseInBack(t):
    return t**2 * (np.e*t-np.e+1)

def EaseOutBack(t):
    return 1-EaseInBack(1-t)

def EaseInOutBack(t):
    return (t<0.5) * 0.5 * EaseInBack(t * 2) + (t>=0.5) * 0.5 * (EaseOutBack((t-0.5)*2) + 1)

def EaseInElastic(t):
    return EaseInExpo(t) * np.sin(t * np.pi * 6.5) / np.sin(np.pi * 6.5)

def EaseOutElastic(t):
    return 1-EaseInElastic(1-t)

def EaseInOutElastic(t):
    v1 = (t<0.46503) * 0.5 * EaseInElastic(t * 2)
    v2 = (t>0.53497) * 0.5 * (EaseOutElastic((t-0.5)*2) + 1)
    # 确保一阶导数连续
    v3 = ((0.46503<=t) & (t<=0.53497)) * (13.047404544869156 * t - 6.023702272434578)
    return v1+v2+v3

def EaseInBounce(t):
    def _poly(_t):
        return 1-((_t-0.5)*2)**2
    v1 = (t<0.125) * _poly(t/0.125) * 0.015625
    v2 = ((0.125<=t) & (t<0.375)) * _poly((t-0.125)/0.25) * 0.0625
    v3 = ((0.375<=t) & (t<0.750)) * _poly((t-0.375)/0.375) * 0.25
    v4 = (0.750<=t) * _poly((t-0.75)/0.5) * 1
    return v1+v2+v3+v4

def EaseOutBounce(t):
    return 1-EaseInBounce(1-t)

def EaseInOutBounce(t):
    return (t<0.5) * 0.5 * EaseInBounce(t * 2) + (t>=0.5) * 0.5 * (EaseOutBounce((t-0.5)*2) + 1)


import numpy as np

'''
Dynamic Time Warping

A dynamic programming approach to measuring the distance between two
temporal sequences. Ideal for sequences that may vary in speed or in phase.
'''

def distance(x, y):
    ''' Generic euclidean distance function '''
    return np.sqrt(np.sum((x - y)**2))

def dtw(a, b):
    '''
    Returns the DTW distance between sequences `a` and `b`.

    Parameters:
    -----------
    a : numpy array
    b : numpy array

    Returns:
    --------
    dist : float
    The distance between sequences `a` and `b`
    '''
    # Ensure inputs are np arrays
    a = np.asarray(a)
    b = np.asarray(b)

    # dtw_table[i,j] is the dtw distance up to and including a[i-1] and b[j-1]
    dtw_table = np.empty((a.size+1, b.size+1))
    # Initialize first row and column
    dtw_table[:, 0] = np.inf
    dtw_table[0, :] = np.inf
    dtw_table[0, 0] = 0.

    for i in xrange(1, a.size+1):
        for j in xrange(1, b.size+1):
            cost = distance(a[i-1], b[j-1])
            dtw_table[i, j] = cost + min(dtw_table[i-1, j],
                                         dtw_table[i, j-1],
                                         dtw_table[i-1, j-1])

    return dtw_table[a.size, b.size], dtw_table

def display_grid(grid):
    import matplotlib.pyplot as plt
    fig = plt.figure()
    ax = fig.add_subplot(111)
    im = ax.imshow(grid, interpolation='None')
    for (j, i), label in np.ndenumerate(grid):
        ax.text(i, j, label, ha='center', va='center')
    plt.show()

def main():
    np.set_printoptions(suppress=True, precision=1)
    a = np.array([0, 5, 8, 9, 3, 0])
    b = np.array([5, 8, 9, 3])

    distance, table = dtw(a, b)
    print distance
    display_grid(table)

if __name__ == '__main__':
    main()


import numpy as np
from copy import deepcopy
import pandas as pd

def MP(ft, D, tol=1e-4):
    """
    This is an implementation of matching pursuit (MP) algorithm
    Input: Signal: f(t), dictionary D with normalized columns g_i
    Output: List if coefficients a_n and indices for corresponding atoms gamma_n
    Initialization:
        R1 = f(t)
        n = 1
    Repeat:
        Find g_{gamma_n} with maximum inner product <R_n, g_{gamma_n}>
        a_n = <R_n, g_{gamma_n}>
        R_{n + 1} = R_n - a_n * g_{gamma_n}
        n = n + 1
    Util stop condition (e.g., ||R_n|| < threshold)
    return
    """
    R = deepcopy(ft)
    a = []
    gamma = []
    D = D / np.linalg.norm(D, axis=0)

    while np.dot(R, R) > tol:
        inner_prod = np.dot(R, D)
        gammai = np.argmax(np.abs(inner_prod))
        ai = inner_prod[gammai]
        R -= ai * D[:, gammai]
        a.append(ai)
        gamma.append(gammai)

    a = np.asarray(a)
    gamma = np.asarray(gamma)

    return a, gamma

if __name__ == "__main__":
    v = np.random.random(8)
    print('The original: ', v)
    # D = np.random.random((8, 12))
    D = np.random.randn(8, 12)
    a, gamma = MP(v, D)

    # print(a, gamma)
    df = pd.DataFrame([a, gamma]).T
    res = df.groupby(1).sum()
    D /= np.linalg.norm(D, axis=0)
    vn = np.sum(a * D[:, gamma], axis=1)
    print('The approximation: ', vn)
    # vnn = np.sum(res.values.T * D[:, res.index.values.astype(int)], axis=1)
    # print('The approximation: ', vnn)


import numpy as np
from scipy.special import wofz

def gaussian(x, x0, sigma):
    return np.exp(-np.power(x - x0, 2.) / (2 * np.power(sigma, 2.)))

def lorentzian(x, x0, gamma):
    return 1 / (1 + np.power((x - x0) / gamma, 2))

def voigt(x, x0, sigma, gamma):
    if sigma == 0:
        return lorentzian(x, x0, gamma)
    if gamma == 0:
        return gaussian(x, x0, sigma)
    return np.real(wofz(((x - x0) + 1j * gamma) / sigma / np.sqrt(2)))

def voigt_fwhm(g_width, l_width):
    """
    Accurate to 0.0003% for a pure Lorentzian profile, precise for a pure Gaussian.

    Source: https://en.wikipedia.org/wiki/Voigt_profile.
    """
    return 0.5346 * l_width + np.sqrt(0.2166 * l_width**2 + g_width**2)


import numpy as np
import matplotlib

matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

np.random.seed(1)

def generate_data(sample_size):
    a = np.linspace(0, 4 * np.pi, num=sample_size // 2)
    x = np.concatenate(
        [np.stack([a * np.cos(a), a * np.sin(a)], axis=1),
         np.stack([(a + np.pi) * np.cos(a), (a + np.pi) * np.sin(a)], axis=1)])
    x += np.random.random(size=x.shape)
    y = np.concatenate([np.ones(sample_size // 2), -np.ones(sample_size // 2)])
    return x, y

def build_design_mat(x1, x2, bandwidth):
    return np.exp(
        -np.sum((x1[:, None] - x2[None]) ** 2, axis=-1) / (2 * bandwidth ** 2))

def optimize_param(design_mat, y, regularizer, lr): #lr: learning rate
# implement here
    theta = np.random.rand(len(y))
    while(1):
        f = design_mat.dot(theta)
        delf = np.array(np.zeros(len(y)))
        for i in range(len(f)) :
            delfi = np.array([])
            fi = f[i]
            yi = y[i]
            for j in range(len(f)) :
                if(1 - yi*fi > 0) :
                    delfi = np.append(delfi,- yi * design_mat[i][j])
                else :
                    delfi = np.append(delfi, 0)
            delf = delf + delfi
        if (np.linalg.norm(delfi, ord=2) < 0.00001) :
            break
        print("a")
        theta = theta - lr * (regularizer * delf + 2 * design_mat.dot(theta))
    return theta

def visualize(theta, x, y, grid_size=100, x_min=-16, x_max=16):
    grid = np.linspace(x_min, x_max, grid_size)
    X, Y = np.meshgrid(grid, grid)
    mesh_grid = np.stack([np.ravel(X), np.ravel(Y)], axis=1)
    design_mat = build_design_mat(x, mesh_grid, bandwidth=1.)
    plt.clf()
    plt.figure(figsize=(6, 6))
    plt.xlim(x_min, x_max)
    plt.ylim(x_min, x_max)
    plt.contourf(X, Y, np.reshape(np.sign(design_mat.T.dot(theta)),
                                  (grid_size, grid_size)),
                 alpha=.4, cmap=plt.cm.coolwarm)
    plt.scatter(x[y == 1][:, 0], x[y == 1][:, 1], marker='$O$', c='red')
    plt.scatter(x[y == -1][:, 0], x[y == -1][:, 1], marker='x', c='blue')
    plt.savefig('ML1-homework2.png')

#x, y = generate_data(sample_size=200)
x, y = generate_data(sample_size=200)
design_mat = build_design_mat(x, x, bandwidth=1.)
theta = optimize_param(design_mat, y, regularizer=1, lr=0.0001)
#theta = optimize_param(design_mat, y, regularizer=1, lr=0.01)

visualize(theta, x, y)


from app.utils.methods import BaseMethod
import numpy as np

class SimpleLU(BaseMethod):
    def __init__(self, A, b, n):
        self.A = np.array(A).reshape(n, n)
        self.b = np.array(b).reshape(len(b), 1)
        self.n = n

    def proggresive_subst(self, M):
        n = len(M)
        x = np.zeros([n, 1])

        x[0] = M[0, n] / M[0, 0]
        array = [[1]]
        for i in range(1, n):
            aux = np.concatenate((array, np.transpose(x[0:i])), axis=1)
            array_aux = [M[i - 1, n]]
            aux_ = np.concatenate((array_aux, -M[i, 0:i]), axis=0)
            x[i] = np.dot(aux, aux_) / M[i, i]
        return x

    def back_subst(self, M):
        n = len(M)
        x = np.ones([n, 1])
        for i in range(n - 1, -1, -1):
            value = 0
            for j in range(i + 1, n):
                value += M[i, j] * x[j]
            x[i] = (M[i, n] - value) / M[i, i]
        return x

    def run(self):
        det = np.linalg.det(self.A)
        if det == 0:
            return {"Error": "Matrix must be nonsingular."}
        self.n = np.size(self.A, 1)
        L = np.eye(self.n)
        U = np.zeros((self.n, self.n))
        M = self.A
        result = {"iterations": [{"stage": 0, "M": list(map(lambda x: list(x), M))}]}
        for i in range(0, self.n - 1):
            for j in range(i + 1, self.n):
                if M[j, i] != 0:
                    L[j, i] = np.divide(M[j, i], M[i, i])
                    M[j, i:self.n] = np.subtract(M[j, i:self.n], (np.divide(M[j, i], M[i, i])) * M[i, i:self.n])
                U[i, i:self.n] = M[i, i:self.n]
                U[i + 1, i + 1:self.n] = M[i + 1, i + 1:self.n]
                result['iterations'].append({
                    "stage": i + 1,
                    "M": list(map(lambda x: list(x), M)),
                    "L": list(map(lambda x: list(x), L)),
                    "U": list(map(lambda x: list(x), U))
                })
        MLB = np.concatenate((L, self.b), axis=1)
        z = self.proggresive_subst(MLB)
        MUZ = np.concatenate((U, z), axis=1)
        result['result'] = list(map(lambda x: x[0], self.back_subst(MUZ)))
        return result


import numpy as np

x = np.array([[1,2],[3,4]], dtype=np.float64)
y = np.array([[5,6],[7,8]], dtype=np.float64)

# [[ 6.0  8.0]
#  [10.0 12.0]]
print x + y

# [[-4.0 -4.0]
#  [-4.0 -4.0]]
print x - y

# [[ 5.0 12.0]
#  [21.0 32.0]]
print x * y

# [[ 0.2         0.33333333]
#  [ 0.42857143  0.5       ]]
print x / y

# [[ 1.          1.41421356]
#  [ 1.73205081  2.        ]]
print np.sqrt(x)


"""
Simple Metropolis Hastings sampler taken from Hogg and Foreman-MacKey(2018):

  From Problem 2:
  Sample in a single parameter x and give the sampler
  as its density function p(x) a Gaussian density with
  mean 2 and variance 2. Make the proposal distribution
  q (x'∣x ) a Gaussian pdf for x′ with mean x and
  variance 1. Initialize the sampler with x = 0 and run
  the sampler for more than 10000 steps. Plot the results
  as a histogram with the true density overplotted sensibly.

"""

import probayes as pb
import numpy as np
import scipy.stats
from pylab import *; ion()

n_steps = 12288

def q(**kwds):
  x, xprime = kwds['x'], kwds["x'"]
  return scipy.stats.norm.pdf(xprime, loc=x, scale=1.)

x = pb.RV('x', vtype=float, vset=(-np.inf, np.inf))
process = pb.SP(x)
process.set_prob(scipy.stats.norm.pdf, loc=2, scale=np.sqrt(2),
                 order={'x': 0})
process.set_tran(q)
lambda_delta = lambda : process.Delta(x=scipy.stats.norm.rvs(loc=0., scale=1.))
process.set_delta(lambda_delta)
process.set_scores('hastings')
process.set_update('metropolis')
sampler = process.sampler({'x': 0.}, stop=n_steps)
samples = [sample for sample in sampler]
summary = process(samples)
xvals = summary.v['x']

figure()
xbins = np.linspace(np.min(xvals), np.max(xvals), 128)
xhist, _ = np.histogram(xvals, xbins)
xprop = xhist / (n_steps * (xbins[1]-xbins[0]))
step(xbins[:-1], xprop, 'b')
norm_x = scipy.stats.norm.pdf(xbins, loc=2, scale=np.sqrt(2))
plot(xbins, norm_x, 'r')


#taken from https://machinelearningmastery.com/statistical-hypothesis-tests-in-python-cheat-sheet/
#This section lists statistical tests that you can use to compare data samples.
import numpy as np

from scipy.stats import ttest_ind
def student(data1, data2):
    """Tests whether the means of two independent samples are significantly different.
    Assumptions
    -----------
    * Observations in each sample are independent and identically distributed (iid).
    * Observations in each sample are normally distributed.
    * Observations in each sample have the same variance.
    Interpretation
    --------------
    * H0: the means of the samples are equal.
    * H1: the means of the samples are unequal.

    Parameters
    ----------
    data1, data2: array_like
        The arrays must have the same shape.

    Returns
    -------
    stat : float or array
        The calculated t-statistic.
    p : float or array
        The two-tailed p-value.
    """
    stat, p = ttest_ind(data1, data2)
    return stat, p

from scipy.stats import ttest_rel
def paired_student(data1, data2):
    """Tests whether the means of two paired samples are significantly different.
    Assumptions
    -----------
    * Observations in each sample are independent and identically distributed (iid).
    * Observations in each sample are normally distributed.
    * Observations in each sample have the same variance.
    * Observations across each sample are paired.
    Interpretation
    --------------
    * H0: the means of the samples are equal.
    * H1: the means of the samples are unequal.

    Parameters
    ----------
    data1, data2: array_like
        The arrays must have the same shape.

    Returns
    -------
    stat : float or array
        t-statistic
    p : float or array
        two-tailed p-value
    """
    stat, p = ttest_rel(data1, data2)
    return stat, p

from scipy.stats import f_oneway
def ANOVA(data1, data2):
    """Analysis of Variance Test (ANOVA)
    Tests whether the means of two or more independent samples are significantly different.
    Assumptions
    -----------
    * Observations in each sample are independent and identically distributed (iid).
    * Observations in each sample are normally distributed.
    * Observations in each sample have the same variance.
    Interpretation
    --------------
    * H0: the means of the samples are equal.
    * H1: one or more of the means of the samples are unequal.

    Parameters
    ----------
    data1, data2: array_like
        The sample measurements for each group (must have the same shape).

    Returns
    -------
    stat : float
        The computed F-value of the test.
    p : float
        The associated p-value from the F-distribution.
    """
    stat, p = f_oneway(data1, data2)#, ...)
    return stat, p


import numpy as np

class LinearRegression:

    def __init__(self , learning_rate = 0.01 , n_itres = 1000 , norm = False ):
        self.learning_rate = learning_rate
        self.n_itres       = n_itres
        self.weights       = None
        self.bias          = None
        self.norm          = norm

    def fit(self , X , y):
        if self.norm == True:
            self.NormalEquation(X , y)
        else:
            self.GradinetDescent(X , y)

    def NormalEquation(self , X , y):
        n        = len(X)
        X        = np.concatenate((np.ones((n , 1)) , X) , axis = 1)
        XT       = X.transpose()
        XTX      = np.dot(XT , X)
        XTXinv   = np.linalg.inv(XTX)
        XTXinvXT = np.dot(XTXinv , XT)
        self.B   = np.dot(XTXinvXT , y)

    def GradinetDescent(self , X , y):
        self.weights = np.zeros(n_feats)
        self.bias    = 0

        for _ in range(self.n_itres):
            y_predicted = np.dot(X , self.weights) + self.bias
            dw = (1 / n_samples) * np.dot(X.T , (y_predicted - y))
            db = (1 / n_samples) * np.sum(y_predicted - y)
            self.weights = self.weights - self.learning_rate * dw
            self.bias    = self.bias - self.learning_rate * db

    def _predictNorm(self , x_test):
        x_test = np.concatenate(([1] , x_test ))
        return np.dot(x_test , self.B)

    def _predictGD(self , X):
        y_predicted = np.dot(X , self.weights) + self.bias
        return y_predicted

    def predict(self , x_test):
        if self.norm == True:
            _predictNorm(x_test)
        else:
            _predictGD(x_test)


"""
This implements a heuristic for the TSP, rather than an exact algorithm, and as
a result will be able to handle much larger problem sizes.  Here is a data file
describing a TSP instance
(original source: http://www.math.uwaterloo.ca/tsp/world/bm33708.tsp).

The first line indicates the number of cities. Each city is a point in the
plane, and each subsequent line indicates the x- and y-coordinates of a single
city.

The distance between two cities is defined as the Euclidean distance --- that
is, two cities at locations (x,y)(x,y) and (z,w)(z,w) have distance
sqrt[(x-z)^2 + (y-w)^2] between them.

1.  Start the tour at the first city.
2.  Repeatedly visit the closest city that the tour hasn't visited yet.
    In case of a tie, go to the closest city with the lowest index.
    For example, if both the third and fifth cities have the same distance from
    the first city (and are closer than any other city), then the tour should
    begin by going from the first city to the third city.
3.  Once every city has been visited exactly once, return to the first city to
    complete the tour.
"""

import numpy as np

def greedy_tsp(coords):
    """This function is an implementation of greedy algorithm for travelling
    sales man problem

    Examples
    >>> coords = np.array([
    >>>    [1, 1, 2],
    >>>    [2, 5, 8],
    >>>    [3, 1, 3],
    >>>    [4, 1, 4],
    >>>    [5, 2, 4]
    >>> ])
    >>> mindist, path = greedy_tsp(coords)
    >>> print(mindist)
    15.21110255092798
    >>> print(path)
    [1, 3, 4, 5, 2, 1]

    """
    mindist = 0
    origin_coord = coords[0, 1:]
    start = 0
    path = [coords[0, 0]]
    while coords.shape[0] > 1:
        start_coord = coords[start, 1:]
        coords = np.delete(coords, start, 0)
        squared_dists = ((coords[:, 1:] - start_coord) ** 2).sum(axis=1)
        start = np.argmin(squared_dists)
        mindist += np.sqrt(squared_dists[start])
        path.append(coords[start, 0])
    end_coord = coords[start, 1:]
    mindist += np.sqrt(((end_coord - origin_coord) ** 2).sum())
    path.append(path[0])
    return mindist, path

if __name__ == "__main__":
    coords = np.loadtxt("nn.txt", skiprows=1)
    mindist, _ = greedy_tsp(coords)
    print(mindist)


import numpy as np
import math

# Create random input and output data
x = np.linspace(-math.pi, math.pi, 2000)
y = np.sin(x)

# Randomly initialize weights
a = np.random.randn()
b = np.random.randn()
c = np.random.randn()
d = np.random.randn()

learning_rate = 1e-6
for t in range(5000):
    # Forward pass, compute predicted y
    # y = a + b * x + c * x^2 + d * x^3
    y_pred = a + b * x + c * (x**2) + d * (x**3)

    # Compute and print loss
    loss = np.square(y_pred - y).sum()
    if t % 100 == 99:
        print("Epoch: {} Loss: {}.".format(t, loss))

    # Backprop to compute gradients of a, b, c, d with respect to loss
    grad_y_pred = 2.0 * (y_pred - y)
    grad_a = grad_y_pred.sum()
    grad_b = (grad_y_pred * x).sum()
    grad_c = (grad_y_pred * x**2).sum()
    grad_d = (grad_y_pred * x**3).sum()

    # Update weights
    a -= learning_rate * grad_a
    b -= learning_rate * grad_b
    c -= learning_rate * grad_c
    d -= learning_rate * grad_d

print("Result: f(x) = {} + {} * x + {} * x**2 + {} * x**3".format(a, b, c, d))

Learning PyTorch/Learning PyTorch with Example/polynomial_numpy.py


#!/usr/bin/env python

#   mp_fft.py
#   2018-08-25  Markku-Juhani O. Saarinen <mjos@iki.fi>

#   high-precision convolutions with FFT. Original adopted via apfft from
#   https://jakevdp.github.io/blog/2013/08/28/understanding-the-fft/

import numpy as np
import mpmath as mp

mp_real = np.vectorize(mp.re)

# FFT and inverse FFT using mpmath

def mp_fft(v, inv_fft=False):

    n = v.shape[0]
    if n == 1:
        return v

    if np.log2(n) % 1 > 0:
        raise ValueError("size of b must be a power of 2")

    # inverse op changes this sign, and scaling at the end
    s_pi = mp.pi * mp.mpc(1j)
    if not inv_fft:
        s_pi = -s_pi

    m = np.array([ [mp.mpc(1), mp.mpc(1)], [mp.mpc(1), mp.mpc(-1)] ])
    m = m.dot(v.reshape((2, -1)))

    # build-up each level of the recursive calculation all at once
    while m.shape[0] < n:
        m_even = m[:, :m.shape[1] / 2]
        m_odd = m[:, m.shape[1] / 2:]
        l = m.shape[0]
        w = mp.exp(s_pi / mp.mpf(l));
        a = np.empty(l, dtype=mp.mpc)
        x = mp.mpc(1)
        for i in range(l - 1):
            a[i] = x;
            x *= w
        a[l - 1] = x
        a = a[:, None]
        m = np.vstack([m_even + a * m_odd,
                       m_even - a * m_odd])

    # scale the result if inverse
    if inv_fft:
        m /= mp.mpf(n)

    return m.ravel()

# convolution on arrays with FFT

def mp_conv(x, y):
    xy = mp_fft(x, False) * mp_fft(y, False)
    xy = mp_fft(xy, True)
    return mp_real(xy)

# -- test code --

# equivalent exhaustive method for testing

def mp_conv2(x, y):
    l = x.shape[0]
    r = np.array([mp.mpf(0)] * l)
    for i in range(l):
        for j in range(l):
            r[(i + j) % l] += x[i] * y[j]
    return mp_real(r)

# a vector of random numbers, scaled to sum up to 1

def mp_rand_dist(l):
    v = np.array([mp.rand() for i in range(l)])
    return v / v.sum()

# returns a very small number if fft works

def test_fft():
    n = 2**10
    a = mp_rand_dist(n)
    b = mp_rand_dist(n)
    c = mp_conv(a, b)
    d = mp_conv2(a, b)
    eps = 0
    for i in range(n):
        eps = max(eps, mp.fabs(c[i] - d[i]))
    return eps

# run the test
#mp.mp.dps = 100
#print test_fft()


# 使用sympy.diff求导定义函数  ，求解其导数
from sympy import *

init_printing(use_unicode=True)
x = symbols("x")
f = log(x)
# 一阶导数
pprint(diff(f, x))
# 二阶导数可以传入第三个参数，表示阶数
pprint(diff(f, x, 2))

print("--------------------------------")

f2 = tan(x)
pprint(diff(f2, x))


import numpy as np
from metrics import *

class SimpleLogisticRegression:

    def __init__(self):
        self.betas = None

    def predict_proba(self, X, intercept=True):
        X = np.c_[np.ones(X.shape[0]), X] if intercept else X

        try:
            z = np.dot(X, self.betas)
        except TypeError as e:
            raise Exception('Model is not fitted. Call fit before predict').with_traceback(e.__traceback__)
        return 1 / (1 + np.exp(-z))

    def predict(self, X, threshold = 0.5):
        return np.array([1 if e > threshold else 0 for e in self.predict_proba(X)])

    def __gradient_descent(self, X, y, learning_rate, steps, seed, verbosity, cost_func='cross_entropy', **kwargs):
        np.random.seed(seed)

        minimize = {
            'cross_entropy': cross_entropy
        }

        self.betas = np.random.rand(X.shape[1])

        for i in range(steps):
            try:
                predicted = self.predict_proba(X, intercept=False)
            except TypeError as e:
                raise Exception('Model is not fitted. Call fit before predict').with_traceback(e.__traceback__)

            try:
                error = minimize.get(cost_func, lambda: 'Invalid')(predicted, y)
            except ValueError as e:
                raise Exception('X and y have different lenght')

            if verbosity != 0 and i % verbosity == 0 :
                print('Step: {} --- Error: {}'.format(i, error))

            error = predicted - y
            gradient = np.dot(X.T, error)/(len(X))
            self.betas -= learning_rate*gradient

        self.is_fitted = True

    def fit(self, X, y, mode='gradient_descent', learning_rate=0.01, steps=10000, seed=0, verbosity=0, cost_func='cross_entropy'):
        modes = {
            'gradient_descent': self.__gradient_descent
        }

        try:
            X = np.c_[np.ones(X.shape[0]), X]
        except AttributeError as e:
            raise Exception('X must a be a numpy array').with_traceback(e.__traceback__)

        try:
            modes.get(mode, lambda: 'Invalid')(X=X,
                                                y=y,
                                                learning_rate=learning_rate,
                                                steps=steps,
                                                seed=seed,
                                                verbosity=verbosity,
                                                cost_func=cost_func)
        except TypeError as e:
            Exception('Invalid parameter type').with_traceback(e.__traceback__)
        except Exception as e:
            raise e


# Задача № 11
'''Для вычисления несобственного интеграла модернизуруем метод Симпсона'''

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

def function_graph(function):
    '''Функция, которая рисует графики'''
    fig = plt.subplots()
    x = np.linspace(1, 2)
    plt.plot(x, function(x))
    plt.show()

def simpson(f, x1, x2, n, e):
    '''Функция вычисления интеграла,
    где x1,x2 - границы интегрирования,
    f - целевая функция, n - разбиение,
    e - шаг, на который отходим от границ интеграирования '''
    a=x1+e
    b=x2
    h=(b-a)/n
    k=0.0
    x=a + h
    for i in range(1,(n//2) + 1):
        k += 4*f(x)
        x += 2*h
    x = a + 2*h
    for i in range(1,n//2):
        k += 2*f(x)
        x += 2*h
    return (h/3)*(f(a)+f(b)+k)

def function(x):
    return math.log(x)/(1+x**2)

print(simpson(function, 0.0, 1.0, 10000000, 0.000000001))

#print(function(10))
#function_graph(function)


import numpy as np
import matplotlib.pyplot as plt

from O5 import prepare_data_wine

# 1. Standardize data

X_train, X_test, y_train, y_test = prepare_data_wine(standardize=True,
                                                     split=True)

# 2. Compute mean vector for each class

np.set_printoptions(precision=4)
mean_vectors = []
for label in np.sort(np.unique(y_train)):
    mean_vectors.append(np.mean(X_train[y_train == label], axis=0))
    print('MV %s: %s\n' % (label, mean_vectors[label - 1]))

# 3.1 Create within-class scatter matrix

d = 13  # number of features
S_W = np.zeros((d, d))
for label, mv in zip(range(1, 4), mean_vectors):
    class_scatter = np.zeros((d, d))

    for row in X_train[y_train == label]:
        row, mv = row.reshape(d, 1), mv.reshape(d, 1)
        class_scatter += (row - mv).dot((row - mv).T)
        S_W += class_scatter

print(f"Within-class scatter matrix: {S_W.shape[0]} x {S_W.shape[1]}")

# The assumption that we are making when we are computing the scatter matrices
# is that the class labels in the training dataset are uniformly distributed.
# However, if we print the number of class labels, we see that this assumption
# is violated:
print(f'Class label distribution: {np.bincount(y_train)[1:]}')

# Thus, we want to scale the individual scatter matrices, S(i), before we sum
# them up as scatter matrix S(W). When we divide the scatter matrices by the
# number of class-examples, nn ii , we can see that computing the scatter
# matrix is in fact the same as
# computing the covariance matrix, Σ(i) —the covariance matrix is a normalized
# version of the scatter matrix:
# Σ(i) = 1/n(i) * S(i) =1/n(i) * ∑(x−m(i))(x-m(i))^T

# Compute scaled within-class scatter matrix:

d = 13
S_W = np.zeros((d, d))
for label, mv in zip(range(1, 4), mean_vectors):
    class_scatter = np.cov(X_train[y_train == label].T)
    S_W += class_scatter
print(f"Within-class scatter matrix: {S_W.shape[0]} x {S_W.shape[1]}")

# 3.2 Compute between-class scatter matrix:

mean_overall = np.mean(X_train, axis=0)
d = 13  # Number of features
S_B = np.zeros((d, d))

for i, mean_vector in enumerate(mean_vectors):
    n = X_train[y_train == i + 1, :].shape[0]
    mean_vector = mean_vector.reshape(d, 1)  # Make column vector
    mean_overall = mean_overall.reshape(d, 1)
    S_B += n * (mean_vector - mean_overall).dot((mean_vector - mean_overall).T)

print(f'Between-class scatter matrix: {S_B.shape[0]}x{S_B.shape[1]}')

# 4 Compute the eigenvectors and corresponding eigenvalues of the matrx,
# S(w, -1) * S(b)
eigen_values, eigen_vectors = np.linalg.eig(np.linalg.inv(S_W).dot(S_B))

# 5 Sort the eigenvalues in descending order
eigen_pairs = [(np.abs(eigen_values[i]), eigen_vectors[:, i])
               for i in range(len(eigen_values))]
eigen_pairs = sorted(eigen_pairs, key=lambda k: k[0], reverse=True)
print(f'Eigenvalues in descending order: \n')
for eigen_value in eigen_pairs:
    print(eigen_value[0])

# In LDA, the number of linear discriminants is at most c−1, where c is the
# number of class labels, since the in-between scatter matrix, S(B), is the
# sum of c matrices with rank one or less. We can indeed see that we only have
# two nonzero eigenvalues (the eigenvalues 3-13 are not exactly zero, but this
# is due to the floating-point arithmetic in NumPy).

tot = sum(eigen_values.real)
discr = [(i / tot) for i in sorted(eigen_values.real, reverse=True)]
cum_discr = np.cumsum(discr)

plt.bar(range(1, 14), discr, alpha=0.5, align='center',
        label='Individual "discriminability"')
plt.step(range(1, 14), cum_discr, where='mid',
         label='Cumulative "discriminablity"')
plt.ylabel('"Discriminability" ratio')
plt.xlabel('Linear Discriminants')
plt.ylim([-0.1, 1.1])
plt.legend(loc='best')
plt.tight_layout()
plt.show()

# 6 Choose the k eigenvectors that correspond to the k largest eigenvalues
# to construct a d * k - dimensional transformation matrix, W; the
# eigenvectors are the columns of this matrix.

w = np.hstack((eigen_pairs[0][1][:, np.newaxis].real,
               eigen_pairs[1][1][:, np.newaxis].real))
print('Matrix W: \n', w)

# 7 Project the examples onto the new feature space using the transformation
# matrix, W.

X_train_lda = X_train.dot(w)
colors = ['r', 'b', 'g']
markers = ['s', 'x', 'o']

for l, c, m in zip(np.unique(y_train), colors, markers):
    plt.scatter(X_train_lda[y_train == l, 0],
                X_train_lda[y_train == l, 1] * (-1),
                c=c, label=l, marker=m)

plt.xlabel('LD 1')
plt.ylabel('LD 2')
plt.legend(loc='lower right')
plt.tight_layout()
plt.show()


import scipy.stats as spss
import pandas as pd

# Data - continuous distributions
df_uniform = spss.uniform.rvs(size=10000,loc=10,scale=20)
df_normal = spss.norm.rvs(size=10000,loc=10,scale=100)
df_std_norm = spss.norm.rvs(size=10000,loc=0,scale=1)
df_gamma = spss.gamma.rvs(a=5,size=10000)
df_exponential = spss.expon.rvs(scale=1,loc=0,size=10000)
df_beta = spss.beta.rvs(5, 2, loc=0, scale=1, size=10000) #, random_state=None)
# discrete distributions
df_binomial = spss.binom.rvs(n=10,p=0.8,size=10000)
df_poisson = spss.poisson.rvs(mu=3,size=10000)
df_bernoulli = spss.bernoulli.rvs(size=10000,p=0.6)

# Plot
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(rows=3, cols=3, # shared_xaxes=True,
     vertical_spacing=0.2, horizontal_spacing=0.1,
    specs=[[{"type": "xy"}, {"type": "xy"}, {"type": "xy"}],
           [{"type": "xy"}, {"type": "xy"}, {"type": "xy"}],
           [{"type": "xy"}, {"type": "xy"}, {"type": "xy"}]])
    #subplot_titles=("Plot 1", "Plot 2"))

fig.update_layout(template="plotly_dark")

fig.add_trace(go.Histogram(x=df_uniform, showlegend=False),
              row=1, col=1,)
fig.add_trace(go.Histogram(x=df_normal, showlegend=False),
              row=1, col=2,)
fig.add_trace(go.Histogram(x=df_std_norm, showlegend=False),
              row=1, col=3,)
fig.add_trace(go.Histogram(x=df_gamma, showlegend=False),
              row=2, col=1,)
fig.add_trace(go.Histogram(x=df_exponential, showlegend=False),
              row=2, col=2,)
fig.add_trace(go.Histogram(x=df_beta, showlegend=False),
              row=2, col=3,)
fig.add_trace(go.Histogram(x=df_binomial, showlegend=False),
              row=3, col=1,)
fig.add_trace(go.Histogram(x=df_poisson, showlegend=False),
              row=3, col=2,)
fig.add_trace(go.Histogram(x=df_bernoulli, showlegend=False),
              row=3, col=3,)

# Title
offset_top=0.12
fig.add_annotation(dict(xref='paper',yref='paper',x=0.5,y=offset_top+1.22,xanchor='center',yanchor='top',
  font=dict(family='Arial',size=24,color='grey'),showarrow=False,
  text="Statistical Distributions"))
# Subtitle
fig.add_annotation(dict(xref='paper',yref='paper',x=0.5,y=offset_top+1.095,xanchor='center',yanchor='top',
  font=dict(family='Arial',size=16,color='grey'),showarrow=False,
  text="Arrangement of values of a variable showing their frequency of occurrence"))
# Footer
offset_bottom=0.05
fig.add_annotation(dict(xref='paper',yref='paper',x=0.5,y=-(offset_bottom+0.02),xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='#30DayChartChallenge - statistics - 2021/04/09'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.5,y=-(offset_bottom+0.075),xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Datasets created using scipy.stats'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.5,y=-(offset_bottom+0.15),xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='twitter.com/vivekparasharr | github.com/vivekparasharr | vivekparasharr.medium.com'))

# Annotations
offset_chart_titles = 1.12
fig.add_annotation(dict(xref='paper',yref='paper',x=0.13,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Uniform Distribution'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.48,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Normal Distribution'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.84,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Standard Normal Distribution'))
offset_chart_titles = 0.72
fig.add_annotation(dict(xref='paper',yref='paper',x=0.13,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Gamma Distribution'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.48,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Exponential Distribution'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.84,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Beta Distribution'))
offset_chart_titles = 0.32
fig.add_annotation(dict(xref='paper',yref='paper',x=0.13,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Binomial Distribution'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.48,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Poisson Distribution'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.84,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=12, color='grey'),showarrow=False,
  text='Bernoulli Normal Distribution'))

# Annotations for formulas
offset_chart_titles = 1.07
fig.add_annotation(dict(xref='paper',yref='paper',x=0.13,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='uniform.rvs(size=10000,loc=10,scale=20)'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.48,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='norm.rvs(size=10000,loc=10,scale=100)'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.84,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='norm.rvs(size=10000,loc=0,scale=1)'))
offset_chart_titles = 0.67
fig.add_annotation(dict(xref='paper',yref='paper',x=0.13,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='gamma.rvs(a=5,size=10000)'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.48,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='expon.rvs(scale=1,loc=0,size=10000)'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.84,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='beta.rvs(5, 2, loc=0, scale=1, size=10000)'))
offset_chart_titles = 0.27
fig.add_annotation(dict(xref='paper',yref='paper',x=0.13,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='binom.rvs(n=10,p=0.8,size=10000)'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.48,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='poisson.rvs(mu=3,size=10000)'))
fig.add_annotation(dict(xref='paper',yref='paper',x=0.84,y=offset_chart_titles,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=10, color='grey'),showarrow=False,
  text='bernoulli.rvs(size=10000,p=0.6)'))

# Annotations for type of distribution
fig.add_annotation(dict(xref='paper',yref='paper',x=-0.09,y=1.06,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=16, color='grey'),showarrow=False,
  text='Continuous', textangle=270))
fig.add_annotation(dict(xref='paper',yref='paper',x=-0.09,y=0.68,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=16, color='grey'),showarrow=False,
  text='Continuous', textangle=270))
fig.add_annotation(dict(xref='paper',yref='paper',x=-0.09,y=0.25,xanchor='center',yanchor='top',
  font=dict(family='Arial', size=16, color='grey'),showarrow=False,
  text='Discrete', textangle=270))

fig.show()


import numpy as np
from scipy.stats import gaussian_kde
import matplotlib
import matplotlib.pyplot as plt

# class defining the posterior distribution for the coin flip example
class target_coin_flip():
    def __init__(self, NN, NH):
        self.NN = NN # number of tosses
        self.NH = NH # number of heads
    def evaluate(self, H):
        # samples from the posterior
        pH=1 if (H>=0 and H<=1) else 0.0
        return np.power(H,self.NH)*np.power(1-H,self.NN-self.NH)*pH

#class defining the logarithmic representation of the posterior
class target_log_coin_flip():
    # TODO : use the "target_coin_flip" class as a starting point
    #        and edit appropriately to convert to logarithmic scale
    def __init__(self, num_tosses, num_heads):
        self.num_heads = num_heads
        self.num_tails = num_tosses - num_heads

    def evaluate(self, H):
        if 0.0 < H < 1.0:
            return self.num_heads * np.log(H) + self.num_tails * np.log(1 - H)
        else:
            return -np.inf

def MCMC(target, starting_sample, num_iters=1e6, burnin=1e4):
    print("Running MCMC")
    # Markov Chain Monte Carlo (MCMC) - Metropolis
    current_sample = starting_sample
    # proposal distribution is a Gaussian with mean 0.0 and std 0.1
    proposal_mean = 0.0
    proposal_sigma = 0.1
    samples = []
    samples.append(starting_sample)
    for iter_ in range(int(num_iters)):
        # GENERATION BASED ON PROPOSAL
        next_sample_candidate = current_sample + proposal_mean + np.random.randn()*proposal_sigma
        # ACCEPTANCE PROBABILITY - Metropolis
        acceptance_prob = np.min([1.0, target.evaluate(next_sample_candidate)/(target.evaluate(current_sample))])
        # ACCEPT OR REJECT with uniform probability
        temp = np.random.rand()
        if temp <= acceptance_prob:
            # ACCEPTING THE CANDIDATE SAMPLE
            samples.append(next_sample_candidate)
            current_sample = next_sample_candidate
        else:
            samples.append(current_sample)
    if len(samples)>burnin:
        # keep only samples after burn-in iterations
        samples=samples[int(burnin):]
    else:
        raise ValueError("Number of samples {:} smaller than burnin period {:}.".format(len(samples), burnin))
    return samples

def MCMCLOG(logtarget, starting_sample, num_iters=1e6, burnin=1e4):
    print("Running MCMCMHLOG")
    # Markov Chain Monte Carlo (MCMC) - Metropolis Hastings
    # TODO: start from the "MCMC" function above and edit whenever
    #       necessary to convert to logarithmic scale
    current_sample = starting_sample
    # proposal distribution is a Gaussian with mean 0.0 and std 0.1
    proposal_mean = 0.0
    proposal_sigma = 0.1
    samples = [starting_sample]
    for _ in range(int(num_iters)):
        # GENERATION BASED ON PROPOSAL
        next_sample_candidate = current_sample + proposal_mean + np.random.randn()*proposal_sigma
        # ACCEPTANCE PROBABILITY - Metropolis
        acceptance_prob = np.min([0.0, logtarget.evaluate(next_sample_candidate) - logtarget.evaluate(current_sample)])
        # ACCEPT OR REJECT with uniform probability
        temp = np.log(np.random.rand())
        if temp <= acceptance_prob:
            # ACCEPTING THE CANDIDATE SAMPLE
            samples.append(next_sample_candidate)
            current_sample = next_sample_candidate
        else:
            samples.append(current_sample)
    if len(samples)>burnin:
        # keep only samples after burn-in iterations
        samples=samples[int(burnin):]
    else:
        raise ValueError("Number of samples {:} smaller than burnin period {:}.".format(len(samples), burnin))
    return samples

if __name__ == "__main__":

    #IN NN TOSSES, NH TIMES HEAD (NN>=NH)
    NN = 3000 # NN = 300 / 3000 tosses
    NH = 1500 # NH = 150 / 1500 heads
    burnin = 1e2 # number of burn-in iterations
    num_iters = 1e5 # number of total MCMC iterations
    starting_sample = 0.5 # starting point for MCMC algorithm

    target = target_coin_flip(NN,NH)
    samples_mcmc = MCMC(target, starting_sample, num_iters, burnin)
    # plot histogram with samples drawn from posterior distribution with the MCMC algorithm
    plt.hist(samples_mcmc, density=True, facecolor='g', alpha=0.7, bins=10, label="MCMC") # if you're using python>3, use density=True instead of normed=True
    plt.xlim([0,1])
    plt.legend()

    target = target_log_coin_flip(NN,NH)
    samples_mcmclog = MCMCLOG(target, starting_sample, num_iters, burnin)
    plt.hist(samples_mcmclog, density=True, facecolor='b', alpha=0.4, bins=10, label="MCMC-LOG")
    plt.xlim([0,1])
    plt.legend()
    plt.show()


import numpy as np
from itertools import accumulate

def time_series_sim(k,ga,diff,delta_t,N,G):
    """ returns a time series that is the solution of a Langevin equation describing a Brownian particle in a harmonic potential
    :param k: Spring constant
    :param ga: friction coefficient
    :param diff: Diffusion coefficient
    :param delta_t: time step
    :param N: number of samples that are returned
    :param G: ratio of simulated data points and returned samples (100 means that only every 100th point is used)
    :return:
    """
    # differential equation x_i = x_(i-1) - k/gamma*x_(i-1) + sqrt(2*D*delta_t)*w_i
    def next_point(x, y):
        amplitude = np.sqrt(2 * diff * delta_t / float(G))
        return x - k / ga * x * delta_t / float(G) + amplitude * y

    # random force
    w=np.random.normal(0,1,N*G)
    xx = np.fromiter(accumulate(w, next_point), np.float)

    return xx[::G]  # only use every G point

def time_series(A,D,delta_t,N):
    """ returns a time series that is the solution of a Langevin equation describing a Brownian particle in a harmonic potential
    :param A: mean square amplitude - oscillator strength
    :param D: Diffusion coefficient
    :param delta_t: time step
    :param N: number of samples that are returned
    :return:
    """
    # using Smolukowski solution for simulation

    #first point
    x=[np.random.normal(0,np.sqrt(A))]
    stddev = np.sqrt(A * (1.0 - np.exp(-2.0 * D / A * delta_t)))

    for i in range(N-1):
        x.append(np.random.normal(x[-1]*np.exp(-D/A*delta_t),stddev))

    return np.array(x)

def time_series2(A,gamma,delta_t,N):
    """ returns a time series that is the solution of a Langevin equation describing a Brownian particle in a harmonic potential
    :param A: mean square amplitude - oscillator strength
    :param gamma: friction coefficient
    :param delta_t: time step
    :param N: number of samples that are returned
    :return:
    """
    # using Smolukowski solution for simulation

    #first point
    x=[np.random.normal(0,np.sqrt(A))]
    stddev = np.sqrt(A * (1.0 - np.exp(-2.0 * gamma * delta_t)))

    for i in range(N-1):
        x.append(np.random.normal(x[-1]*np.exp(-gamma*delta_t),stddev))

    return np.array(x)

# differential equation x_i = x_(i-1) - k/gamma*x_(i-1) + sqrt(2*D*delta_t)*w_i
# using 4th-order Runge-Kutta
def next_point_RK4(x,y):
    k0=-a*x
    k1=-a*(x+k0/2.0)
    k2=-a*(x+k1/2.0)
    k3=-a*(x+k2)
    return x + (k0+2*k1+2*k2+k3)/6 + ampl*y


#
# Description: example of a bootstrapping and forward curve generation
# script, this can be used to build a set of curves for different currencies
# TODO: include some spline smoothing to the zero curve, from first principles!
#

from sympy.solvers import solve
from sympy import Symbol, abs, Real
x = Symbol('x', real=True)

import pylab as pylab

def g(yieldCurve, zeroRates,n, verbose):
    '''
        generates recursively the zero curve
        expressions eval('(0.06/1.05)+(1.06/(1+x)**2)-1')
        solves these expressions to get the new rate
        for that period

    '''
    if len(zeroRates) >= len(yieldCurve):
        print("\n\n\t+zero curve boot strapped [%d iterations]" % (n))
        return
    else:
        legn = ''
        for i in range(0,len(zeroRates),1):
            if i == 0:
                legn = '%2.6f/(1+%2.6f)**%d'%(yieldCurve[n], zeroRates[i],i+1)
            else:
                legn = legn + ' +%2.6f/(1+%2.6f)**%d'%(yieldCurve[n], zeroRates[i],i+1)
        legn = legn + '+ (1+%2.6f)/(1+x)**%d-1'%(yieldCurve[n], n+1)
        # solve the expression for this iteration
        if verbose:
            print("-[%d] %s" % (n, legn.strip()))
        rate1 = solve(eval(legn), x)
        # Abs here since some solutions can be complex
        rate1 = min([Real(abs(r)) for r in rate1])
        if verbose:
            print("-[%d] solution %2.6f" % (n, float(rate1)))
        # stuff the new rate in the results, will be
        # used by the next iteration
        zeroRates.append(rate1)
        g(yieldCurve, zeroRates,n+1, verbose)

verbose = True
tenors = [.1,.25,0.5,1,2,3,5,7,10,20,30]
#
# money market, futures, swap rates
#
yieldCurve = [0.07,	0.09,	0.15,	0.21,	0.37,	0.57,	1.13,	1.70,	2.31,	3.08	,3.41]

#yieldCurve = [0.05, 0.06, 0.07, 0.08 ,0.085 ,0.0857 ,0.0901,0.0915,0.0925,0.0926,0.0934,0.0937]
zeroRates = [yieldCurve[0]] # TODO: check that this is the correct rate

print("\n\n\tAlexander Baker, March 2012\n\tYield Curve Bootstrapper\n\tAlexander Baker\n\n")

# kick off the recursive code
g(yieldCurve, zeroRates, 1, verbose)
print("\tZeroRate Array",zeroRates)

pylab.plot(tenors,yieldCurve)
pylab.plot(tenors,zeroRates)
pylab.show()


from aerosandbox.numpy import sin, cos, linalg
from aerosandbox.numpy.array import array
import numpy as _onp
from typing import Union, List

def rotation_matrix_2D(
        angle,
        as_array: bool = True,
):
    """
    Gives the 2D rotation matrix associated with a counterclockwise rotation about an angle.
    Args:
        angle: Angle by which to rotate. Given in radians.
        as_array: Determines whether to return an array-like or just a simple list of lists.

    Returns: The 2D rotation matrix

    """
    s = sin(angle)
    c = cos(angle)
    rot = [
        [c, -s],
        [s, c]
    ]
    if as_array:
        return array(rot)
    else:
        return rot

def rotation_matrix_3D(
        angle: Union[float, _onp.ndarray],
        axis: Union[_onp.ndarray, List, str],
        as_array: bool = True,
        axis_already_normalized: bool = False
):
    """
    Yields the rotation matrix that corresponds to a rotation by a specified amount about a given axis.

    An implementation of https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle

    Args:

        angle: The angle to rotate by. [radians]
        Direction of rotation corresponds to the right-hand rule.
        Can be vectorized.

        axis: The axis to rotate about. [ndarray]
        Can be vectorized; be sure axis[0] yields all the x-components, etc.

        as_array: boolean, returns a 3x3 array-like if True, and a list-of-lists otherwise.

            If you are intending to use this function vectorized, it is recommended you flag this False. (Or test before
            proceeding.)

        axis_already_normalized: boolean, skips axis normalization for speed if you flag this true.

    Returns:
        The rotation matrix, with type according to the parameter `as_array`.
    """
    s = sin(angle)
    c = cos(angle)

    if isinstance(axis, str):
        if axis.lower() == "x":
            rot = [
                [1, 0, 0],
                [0, c, -s],
                [0, s, c]
            ]
        elif axis.lower() == "y":
            rot = [
                [c, 0, s],
                [0, 1, 0],
                [-s, 0, c]
            ]
        elif axis.lower() == "z":
            rot = [
                [c, -s, 0],
                [s, c, 0],
                [0, 0, 1]
            ]
        else:
            raise ValueError("If `axis` is a string, it must be `x`, `y`, or `z`.")
    else:
        ux = axis[0]
        uy = axis[1]
        uz = axis[2]

        if not axis_already_normalized:
            norm = (ux ** 2 + uy ** 2 + uz ** 2) ** 0.5
            ux = ux / norm
            uy = uy / norm
            uz = uz / norm

        rot = [
            [c + ux ** 2 * (1 - c), ux * uy * (1 - c) - uz * s, ux * uz * (1 - c) + uy * s],
            [uy * ux * (1 - c) + uz * s, c + uy ** 2 * (1 - c), uy * uz * (1 - c) - ux * s],
            [uz * ux * (1 - c) - uy * s, uz * uy * (1 - c) + ux * s, c + uz ** 2 * (1 - c)]
        ]

    if as_array:
        return array(rot)
    else:
        return rot

def rotation_matrix_from_euler_angles(
        roll_angle: Union[float, _onp.ndarray] = 0,
        pitch_angle: Union[float, _onp.ndarray] = 0,
        yaw_angle: Union[float, _onp.ndarray] = 0,
        as_array: bool = True
):
    """
    Yields the rotation matrix that corresponds to a given Euler angle rotation.

    Note: This uses the standard (yaw, pitch, roll) Euler angle rotation, where:
    * First, a rotation about x is applied (roll)
    * Second, a rotation about y is applied (pitch)
    * Third, a rotation about z is applied (yaw)

    In other words: R = R_z(yaw) @ R_y(pitch) @ R_x(roll).

    Note: To use this, pre-multiply your vector to go from body axes to earth axes.
        Example:
            >>> vector_earth = rotation_matrix_from_euler_angles(np.pi / 4, np.pi / 4, np.pi / 4) @ vector_body

    See notes:
    http://planning.cs.uiuc.edu/node102.html

    Args:
        roll_angle: The roll angle, which is a rotation about the x-axis. [radians]
        pitch_angle: The pitch angle, which is a rotation about the y-axis. [radians]
        yaw_angle: The yaw angle, which is a rotation about the z-axis. [radians]
        as_array:

    Returns:

    """
    sa = sin(yaw_angle)
    ca = cos(yaw_angle)
    sb = sin(pitch_angle)
    cb = cos(pitch_angle)
    sc = sin(roll_angle)
    cc = cos(roll_angle)

    rot = [
        [ca * cb, ca * sb * sc - sa * cc, ca * sb * cc + sa * sc],
        [sa * cb, sa * sb * sc + ca * cc, sa * sb * cc - ca * sc],
        [-sb, cb * sc, cb * cc]
    ]

    if as_array:
        return array(rot)
    else:
        return rot

def is_valid_rotation_matrix(
        a: _onp.ndarray,
        tol=1e-9
) -> bool:
    """
    Returns a boolean of whether the given matrix satisfies the properties of a rotation matrix.

    Specifically, tests for:
        * Volume-preserving
        * Handedness of output reference frame
        * Orthogonality of output reference frame

    Args:
        a: The array-like to be tested
        tol: A tolerance to use for truthiness; accounts for floating-point error.

    Returns: A boolean of whether the array-like is a valid rotation matrix.

    """

    def approx_equal(x, y):
        return (x > y - tol) and (x < y + tol)

    det = linalg.det(a)
    is_volume_preserving_and_right_handed = approx_equal(det, 1)

    eye_approx = a.T @ a
    eye = _onp.eye(a.shape[0])
    is_orthogonality_preserving = True
    for i in range(eye.shape[0]):
        for j in range(eye.shape[1]):
            if not approx_equal(eye_approx[i, j], eye[i, j]):
                is_orthogonality_preserving = False

    return (
            is_volume_preserving_and_right_handed and
            is_orthogonality_preserving
    )


import numpy as np
import matplotlib.pyplot as plt
import csv
import math
from pprint import pprint

# Read data from csv file
data = []
with open('UsaFuelPassenger.csv') as f:
    data = [list(map(float, i)) for i in csv.reader(f)]
    # random.shuffle(data)

# Make train sets
x_train = np.array(list(map(lambda x: x[0], data[:])), dtype='float')
y_train = np.array(list(map(lambda y: y[1], data[:])), dtype='float')

# Make validation sets
x_val = np.array(list(map(lambda x: x[0], data[8:])), dtype='float')
y_val = np.array(list(map(lambda y: y[1], data[8:])), dtype='float')

plt.scatter(x_train, y_train, c='blue')
plt.scatter(x_val, y_val, c='red')
data.sort()  # uncommend it , to see graph with connected points.
plt.plot(list(map(lambda x: x[0], data)), list(map(lambda x: x[1], data)), c='black')
plt.show()

# In[82]:

# Actual polynomial output value
def polynomial_output(x, a):
    return np.sum([param[0] * x ** i for i, param in enumerate(a)], axis=0)

# Design matrix
def dm(x_train, M):
    return np.array([[x ** j for j in range(M + 1)] for x in x_train], dtype='float')

# Mean squared error
def MSE(x, y, a):
    return np.mean(np.square(y - polynomial_output(x, a)))

# Draw model
def plot_model(a, x_train, y_train, x_val, y_val, M=None, error=None, lambda_=None):
    x = np.linspace(0, max(x_train) + 1, 100)
    y = polynomial_output(x, a)

    plt.scatter(x_train, y_train, c='blue', label='Training data')
    plt.scatter(x_val, y_val, c='red', label='Validation data')
    plt.plot(x, y, c='red', label=f'λ: {lambda_}')

    plt.xlim(right=max(x_train) + 1, left=0)
    plt.ylim(top=max(y_train) + 20, bottom=0)

    if error:
        plt.title(f'Error = {error}')
    plt.suptitle(f'M = {M}')
    plt.legend()
    plt.show()

# Model selection
M_range = range(8)
lambdas = [0.005, 0.03, 0.4]

# First we need to find the power of polynomial
# by choosing the best model with the least training error.
y = y_train[:, None]
models = []

for M in M_range:
    X = dm(x_train, M)
    f = X.transpose() @ X
    s = np.linalg.inv(f)
    th = s @ X.transpose()
    a = th @ y  # https://en.wikipedia.org/wiki/Polynomial_regression#Matrix_form_and_calculation_of_estimates
    # a = np.linalg.inv(X.transpose() @ X) @ X.transpose() @ y  # https://en.wikipedia.org/wiki/Polynomial_regression#Matrix_form_and_calculation_of_estimates
    val_error = MSE(x_val, y_val, a)
    models.append([a, val_error, M])
    plot_model(a, x_train, y_train, x_val, y_val, M=M, error=val_error)

model = min(models, key=lambda x: x[1])

print('Best fitted model to training data:')
plot_model(model[0], x_train, y_train, x_val, y_val, M=model[2], error=model[1])

# Then we can avoid overfitting with regularization.
regs = []
for regularization_lambda in lambdas:
    X = dm(x_train, model[2])
    eps = regularization_lambda * np.eye(model[2] + 1)
    a = np.linalg.inv(X.transpose() @ X + eps) @ X.transpose() @ y
    regs.append([a, MSE(x_train, y_train, a), MSE(x_val, y_val, a), regularization_lambda, model[2]])
    plt.show()

# Plot best model
print('Best model:')
lambda_ = min(regs, key=lambda x: x[2])
plot_model(lambda_[0], x_train, y_train, x_val, y_val, M=lambda_[4], error=lambda_[2], lambda_=lambda_[3])
print(lambda_[0])

# In[85]:

def get_prediction(x, a):
    y = polynomial_output(x, a)
    if y < 0:
        y = 0
    return y

x = 2
print(get_prediction(x, lambda_[0]))


"""
Implementation of the bigClAM algorithm.

Throughout the code, we will use tho following variables

  * F refers to the membership preference matrix. It's in [NUM_PERSONS, NUM_COMMUNITIES]
   so index (p,c) indicates the preference of person p for community c.
  * A refers to the adjency matrix, also named friend matrix or edge set. It's in [NUM_PERSONS, NUM_PERSONS]
    so index (i,j) indicates is 1 when person i and person j are friends.
"""

import numpy as np
import pickle
from util.generate_data import Datagen, gen_json,gen_json2
from util.pre_data import preData
import json

def sigm(x):
    return np.divide(np.exp(-1.*x),1.-np.exp(-1.*x))

def log_likelihood(F, A):
    """implements equation 2 of
    https://cs.stanford.edu/people/jure/pubs/bigclam-wsdm13.pdf"""
    A_soft = F.dot(F.T)

    # Next two lines are multiplied with the adjacency matrix, A
    # A is a {0,1} matrix, so we zero out all elements not contributing to the sum
    FIRST_PART = A*np.log(1.-np.exp(-1.*A_soft))
    sum_edges = np.sum(FIRST_PART)
    SECOND_PART = (1-A)*A_soft
    sum_nedges = np.sum(SECOND_PART)

    log_likeli = sum_edges - sum_nedges
    return log_likeli

def gradient(F, A, i):
    """Implements equation 3 of
    https://cs.stanford.edu/people/jure/pubs/bigclam-wsdm13.pdf

      * i indicates the row under consideration

    The many forloops in this function can be optimized, but for
    educational purposes we write them out clearly
    """
    N, C = F.shape

    neighbours = np.where(A[i])
    nneighbours = np.where(1-A[i])

    sum_neigh = np.zeros((C,))
    for nb in neighbours[0]:
        dotproduct = F[nb].dot(F[i])
        sum_neigh += F[nb]*sigm(dotproduct)

    sum_nneigh = np.zeros((C,))
    #Speed up this computation using eq.4
    for nnb in nneighbours[0]:
        sum_nneigh += F[nnb]

    grad = sum_neigh - sum_nneigh
    return grad

def train(A, C, iterations = 100):
    # initialize an F
    N = A.shape[0]
    F = np.random.rand(N,C)

    for n in range(iterations):
        for person in range(N):
            grad = gradient(F, A, person)

            F[person] += 0.005*grad

            F[person] = np.maximum(0.001, F[person]) # F should be nonnegative
        ll = log_likelihood(F, A)
        print('At step %5i/%5i ll is %5.3f'%(n, iterations, ll))
    return F

if __name__ == "__main__":

    # p2c = pickle.load(open('data/p2c.pl','rb'))
    ##generate data
    # datagen = Datagen(100, [.3, .3, .2, .2],[.2, .3, .3, .2] , .1).gen_assignments().gen_adjacency()
    # p2c = datagen.person2comm
    # adj = datagen.adj
    adj = preData()
    F = train(adj, 37)
    F_argmax = np.argmax(F,1)
    # data = gen_json(adj, p2c, F_argmax)
    data=gen_json2(adj,F_argmax)
    # with open('../data/data.json','w') as f:
    with open('ui/assets/data.json','w') as f:
        json.dump(data,f, indent=4)

    for i,row in enumerate(F):
        print(row)
        # print(p2c[i])


from collections import Counter
from typing import Dict, Sequence, List, Tuple, Hashable, Optional

from sympy import laguerre, assoc_laguerre, prod, factorial, Rational, Poly
from sympy.abc import x

from ccc.errors import ConstraintNotImplementedError

class PermutationCounter:
    """
    Count constrained permutations of a sequence.

    """

    def __init__(
        self,
        sequence: Sequence[Hashable],
        constraints: Optional[List[Tuple]] = None,
        same_distinct: bool = False,
    ) -> None:

        self.sequence: Sequence[Hashable] = sequence
        self.length: int = len(sequence)
        self.frequencies: Counter = Counter(sequence)

        self.constraints: List[Tuple] = constraints
        self.polynomials: Dict[Hashable, Poly] = {}

        self.same_distinct: bool = same_distinct
        self._correction_factor: int = 1

        if not constraints:
            return

        if len(constraints) == 1:
            op, *args = constraints[0]
            getattr(self, "impose_constraint_" + op)(*args)

        else:
            raise ConstraintNotImplementedError(
                "Imposing multiple constraints is not supported for permuations"
            )

    def impose_constraint_derangement(self) -> None:
        """
        Permutation has no items in their original position.

        Uses the Laguerre polynomial approach detailed in
        "Derangements and Laguerre polynomials", S. Even & J. Gillis,
        Mathematical Proceedings of the Cambridge Philosophical Society,
        Volume 79, Issue 1 January 1976 , pp. 135-143.

        """
        for item, count in self.frequencies.items():
            self.polynomials[item] = laguerre(count, x)

        if self.same_distinct:
            self._correction_factor = prod(factorial(freq) for freq in self.frequencies.values())

    def impose_constraint_no_adjacent(self) -> None:
        """
        Permutation has no consecutive items equal to each other.

        Uses the generalised Laguerre polynomial approach detailed in
        "Counting words with Laguerre series", J. Taylor, The Electronic
        Journal of Combinatorics (E-JC), Volume 21, Issue 2 (2014).

        (https://www.combinatorics.org/ojs/index.php/eljc/article/view/v21i2p1)

        """
        for item, count in self.frequencies.items():
            self.polynomials[item] = assoc_laguerre(count, -1, x)

        if self.same_distinct:
            self._correction_factor = prod(factorial(freq) for freq in self.frequencies.values())

    def count_unconstrained_permutations(self) -> int:
        """
        Number of permutations where no constraint is specified.

        If a, b, ..., n are the counts of each item in the sequence,
        this is equal to:

            len(sequence)! / a! / b! / ... /n!

        If instances of the same symbol are considered distinct, then
        the number of permutations is equal to:

            len(sequence)!

        """
        if self.same_distinct:
            return factorial(self.length)

        return factorial(self.length) / prod(factorial(freq) for freq in self.frequencies.values())

    def probability(self) -> Rational:
        """
        Probability that a permutation of the sequence meets the
        specified contraints.
        """
        return self.count() / self.count_unconstrained_permutations()

    def count(self) -> int:
        """
        Number of permutations of the sequence that meets the
        specified contraints.
        """
        if not self.constraints:
            return self.count_unconstrained_permutations()

        terms = prod(self.polynomials.values()).apart().as_ordered_terms()
        return abs(sum(eval_gamma(t) for t in terms)) * self._correction_factor

def eval_gamma(term):
    """
    Evaluate the term k*x**n as k*n!
    """
    coeff, exp = term.as_coeff_exponent(x)
    return coeff * factorial(exp)


import numpy as np
from scipy.spatial.distance import squareform
from scipy.cluster.hierarchy import linkage

def seriation(Z,N,cur_index):
    '''
        input:
            - Z is a hierarchical tree (dendrogram)
            - N is the number of points given to the clustering process
            - cur_index is the position in the tree for the recursive traversal
        output:
            - order implied by the hierarchical tree Z

        seriation computes the order implied by a hierarchical tree (dendrogram)
    '''
    if cur_index < N:
        return [cur_index]
    else:
        left = int(Z[cur_index-N,0])
        right = int(Z[cur_index-N,1])
        return (seriation(Z,N,left) + seriation(Z,N,right))

def compute_serial_matrix(dist_mat,method="ward"):
    '''
        input:
            - dist_mat is a distance matrix
            - method = ["ward","single","average","complete"]
        output:
            - seriated_dist is the input dist_mat,
              but with re-ordered rows and columns
              according to the seriation, i.e. the
              order implied by the hierarchical tree
            - res_order is the order implied by
              the hierarhical tree
            - res_linkage is the hierarhical tree (dendrogram)

        compute_serial_matrix transforms a distance matrix into
        a sorted distance matrix according to the order implied
        by the hierarchical tree (dendrogram)
    '''
    N = len(dist_mat)
    flat_dist_mat = squareform(dist_mat)
    res_linkage = linkage(flat_dist_mat, method=method, optimal_ordering=True)
    res_order = seriation(res_linkage, N, N + N-2)
    seriated_dist = np.zeros((N,N))
    a,b = np.triu_indices(N,k=1)
    seriated_dist[a,b] = dist_mat[ [res_order[i] for i in a], [res_order[j] for j in b]]
    seriated_dist[b,a] = seriated_dist[a,b]

    return seriated_dist, res_order, res_linkage


# -*- coding: utf-8 -*-
"""
Created on Mon Feb 14 11:40:09 2022

@author: pendl
"""

import cv2
import numpy as np
import matplotlib.pyplot as plt

def computeSVD(mat):

    A=np.array(mat)
    m , n =A.shape
    #print(A.shape)
    AAt=np.dot(A,A.T)
    AtA=np.dot(A.T,A)

    w, U = np.linalg.eig(AAt)
    idx = np.flip(np.argsort(w))
    eval_AAt = w[idx]
    U = U[:, idx]

    p = min(m,n)
    sigma=np.zeros((m,n))
    for i in range(p):
        sigma[i,i]= np.abs(np.sqrt(eval_AAt[i]))

    w_2, V = np.linalg.eig(AtA)
    idx = np.flip(np.argsort(w_2))
    eval_AtA = w_2[idx]
    V= V[:,idx]
    return U,sigma,V

def computeHomography(ps1, ps2):

    if (len(ps1) < 4) or (len(ps2) < 4):
        print("Need atleast four points to compute SVD.")
        return 0

    x = ps1[:, 0]
    y = ps1[:, 1]
    xp = ps2[:, 0]
    yp = ps2[:,1]

    nrows= 8

    A = []
    for i in range(int(nrows/2)):
        row1 = np.array([-x[i], -y[i], -1, 0, 0, 0, x[i]*xp[i], y[i]*xp[i], xp[i]])
        A.append(row1)
        row2 = np.array([0, 0, 0, -x[i], -y[i], -1, x[i]*yp[i], y[i]*yp[i], yp[i]])
        A.append(row2)

    A = np.array(A)
    print("Computing Homography matrix for ")
    print(A)
    U, E, V = computeSVD(A)
    H_vertical = V[:, V.shape[1] - 1]
    H = H_vertical.reshape([3,3])
    H = H / H[2,2]
    return H

pointset1 = np.array([[5, 5], [150, 5], [150, 150], [5, 150]])
pointset2 = np.array([[100, 100], [200, 80], [220, 80], [100, 200]])

computeHomography(pointset1, pointset2)


# pylint: disable=missing-function-docstring, missing-module-docstring/
from pyccel.decorators import types

#==============================================================================

@types( int )
def sum_natural_numbers( n ):
    x = 0
    for i in range( 1, n+1 ):
        x += i
    return x

# ...
@types( int )
def factorial( n ):
    x = 1
    for i in range( 2, n+1 ):
        x *= i
    return x

# ...
@types( int )
def fibonacci( n ):
    x = 0
    y = 1
    for i in range( n ): # pylint: disable=unused-variable
        z = x+y
        x = y
        y = z
    return x

# ...
@types( int )
def double_loop( n ):
    x = 0
    for i in range( 3, 10 ): # pylint: disable=unused-variable
        x += 1
        y  = n*x
        for j in range( 4, 15 ): # pylint: disable=unused-variable
            z = x-y
    return z

# ...
@types( 'int[:,:](order=C)' )
def double_loop_on_2d_array_C( z ):

    from numpy import shape

    m, n = shape( z )

    for i in range( m ):
        for j in range( n ):
            z[i,j] = i-j

# ...
@types( 'int[:,:](order=F)' )
def double_loop_on_2d_array_F( z ):

    from numpy import shape

    m, n = shape( z )

    for i in range( m ):
        for j in range( n ):
            z[i,j] = i-j

# ...
@types( 'int[:,:](order=C)' )
def product_loop_on_2d_array_C( z ):

    from numpy     import shape
    from itertools import product

    m, n = shape( z )

    x = [i for i in range(m)]
    y = [j for j in range(n)]

    for i,j in product( x, y ):
        z[i,j] = i-j

# ...
@types( 'int[:,:](order=F)' )
def product_loop_on_2d_array_F( z ):

    from numpy     import shape
    from itertools import product

    m, n = shape( z )

    x = [i for i in range(m)]
    y = [j for j in range(n)]

    for i,j in product( x, y ):
        z[i,j] = i-j

# ...
@types( 'int[:]' )
def map_on_1d_array( z ):

    @types( int )
    def f( x ):
        return x+5

    res = 0
    for v in map( f, z ):
        res *= v

    return res

# ...
@types( 'int[:]' )
def enumerate_on_1d_array( z ):

    res = 0
    for i,v in enumerate( z ):
        res += v*i

    return res

# ...
@types( int )
def zip_prod( m ):

    x = [  i for i in range(m)]
    y = [2*j for j in range(m)]

    res = 0
    for i1,i2 in zip( x, y ):
        res += i1*i2

    return res

# ...
@types( 'real[:], real[:]' )
def product_loop_on_real_array( z, out ):

    from numpy     import shape

    n, = shape( z )

    for i in range(n):
        out[i] = z[i]**2

# ...
@types('int,int,int')
def fizzbuzz_search_with_breaks( fizz, buzz, max_val ):
    for i in range(1,max_val+1):
        if i%fizz == 0 and i%buzz == 0:
            break
    return i

# ...
@types('int,int,int')
def fizzbuzz_sum_with_continue( fizz, buzz, max_val ):
    fizzbuzz_sum = 0
    for i in range(1,max_val+1):
        if i%fizz != 0:
            continue
        if i%buzz != 0:
            continue
        fizzbuzz_sum += i
    return fizzbuzz_sum

# ...
@types(int)
def fibonacci_while(n):
    x = 0
    y = 1
    i = 1
    while i <= n:
        z = x+y
        x = y
        y = z
        i = i + 1
    return x

# ...
@types(int)
def sum_nat_numbers_while(n):
    x = 0
    i = 0
    while i <= n:
        x += i
        i = i + 1
    return x

# ...
@types(int,int)
def double_while_sum(n, m):
    x = 0
    y = 0
    i = 0
    while x <= n:
        while y <= m:
            i += y
            y = y + 1
        i += x
        x = x + 1
    return i

# ...
@types( int )
def factorial_while( n ):
    x = 1
    i = 1
    while i <= n:
        x = i * x
        i = i + 1
    return x

@types( int )
def while_not_0( n ):
    while n:
        n -= 1
    return n