import numpy as np
print(f'numpy version = {np.__version__}')

w = np.array([70,48, 52, 60, 52])
print(f'w => {w}')
print(f'w.size => {w.size}')
print(f'np.min(w) => {np.min(w)}')
print(f'np.max(w) => {np.max(w)}')
print(f'np.sum(w) => {np.sum(w)}')
print(f'np.mean(w) => {np.mean(w)}')
print(f'np.median(w) => {np.median(w)}')
print(f'np.sort(w) => {np.sort(w)}')

from scipy import stats

m = stats.mode(w)
print(f'stats.mode(w) => {m}')
print(f'm[0] => {m[0]}')
print(f'm[0][0] => {m[0][0]}')

print(f'm[1] => {m[1]}')
print(f'm[1][0] => {m[1][0]}')
print(f'np.sort(w)[::-1] => {np.sort(w)[::-1]}')
w.sort()
print(f'w.sort() => {w} #inplace')

a = np.reshape(np.arange(1,21), (4,5))
print(f'np.reshape(np.arange(1,21), (4,5)) => {a}')
print(f'np.mean(a) => {np.mean(a)}')
print(f'a.size => {a.size}')
print(f'a.shape => {a.shape}')
print(f'np.mean(a[0]) => {np.mean(a[0])}')
print(f'np.mean(a[:,1]) => {np.mean(a[:, 1])}')
print(f'np.mean(a, axis=0) => {np.mean(a, axis=0)}')
print(f'np.sum(a, axis=0) => {np.sum(a, axis=0)}')
print(f'np.sum(a, axis=1) => {np.sum(a, axis=1)}')
print(f'np.std(a) => {np.std(a)}')
print(f'np.var(a) => {np.var(a)}')
print(f'np.ptp(a) => {np.ptp(a)} # peak to peak  np.max(a) - np.min(a) => {np.max(a) - np.min(a)} ')


from scipy.special import comb
import math
import numpy as np
import matplotlib.pyplot as plt

def ensemble_error(n_classifier, error):
    k_start = int(math.ceil(n_classifier / 2.))
    probs = [comb(n_classifier, k) * error**k * (1-error)**(n_classifier - k)
             for k in range(k_start, n_classifier + 1)]
    return sum(probs)

ensemble_error(n_classifier=11, error=0.25)

error_range = np.arange(0.0, 1.01, 0.01)
ens_errors = [ensemble_error(n_classifier=11, error=error)
              for error in error_range]

plt.plot(error_range,
         ens_errors,
         label='Ensemble error',
         linewidth=2)

plt.plot(error_range,
         error_range,
         linestyle='--',
         label='Base error',
         linewidth=2)

plt.xlabel('Base error')
plt.ylabel('Base/Ensemble error')
plt.legend(loc='upper left')
plt.grid(alpha=0.5)
plt.show()

Chapter 07/Learning with ensembles/program1.py


"""
Solving system of equations:
x[0]**3+x[1]**3-9 = 0
x[0]-0.5*x[1] = 0
cos(x[2])+x[0]-1.5 = 0
with some constraints:
150 <= x[2] <= 158
and possible non-linear constraint:
(x[2] - 150.8)**2 <= 1.5

Note:
1. Using Ax <= b constraints is also allowed
2. You can try using equality constraints (h(x)=0, Aeq x = beq) as well.
3. Required function tolerance is p.ftol, constraints tolerance is p.contol,
and hence using h(x)=0 constraints is not 100% same
to some additional f coords
"""

from openopt import SNLE
from numpy import *

# you can define f in several ways:
f = lambda x: (x[0]**3+x[1]**3-9, x[0]-0.5*x[1], cos(x[2])+x[0]-1.5)
#f = (lambda x: x[0]**3+x[1]**3-9, lambda x: x[0]-0.5*x[1], lambda x: cos(x[2])+x[0]-1.5)
# Python list, numpy.array are allowed as well:
#f = lambda x: [x[0]**3+x[1]**3-9, x[0]-0.5*x[1], cos(x[2])+x[0]-1.5]
#or f = lambda x: asfarray((x[0]**3+x[1]**3-9, x[0]-0.5*x[1], cos(x[2])+x[0]-1.5))

#optional: gradient
def df(x):
    df = zeros((3,3))
    df[0,0] = 3*x[0]**2
    df[0,1] = 3*x[1]**2
    df[1,0] = 1
    df[1,1] = -0.5
    df[2,0] = 1
    df[2,2] = -sin(x[2])
    return df

x0 = [8,15, 80]

#w/o gradient:
#p = SNLE(f, x0)

p = SNLE(f, x0, df = df, maxFunEvals = 1e5, iprint = 10, plot=1, ftol = 1e-8, contol=1e-15)

#optional: user-supplied gradient check:
#p.checkdf()

#optional: graphical output, requires matplotlib installed
#p.plot = 1

#set some constraints
p.lb, p.ub = [-inf]*3, [inf]*3
p.lb[2], p.ub[2] = 145, 150

# you could try also comment/uncomment nonlinear constraints:
p.c = lambda x: (x[2] - 146)**2-1.5
# optional: gradient
p.dc = lambda x: asfarray((0, 0, 2*(x[2]-146)))
# also you could set it via p=SNLE(f, x0, ..., c = c, dc = dc)

#optional: user-supplied dc check:
#p.checkdc()

#r = p.solve('nssolve', debug=0, maxIter=1e9)
# using converter to nlp, try to minimize sum(f_i(x)^2):
r = p.solve('nlp:ralg', xlabel='iter', iprint=10, plot=1)

print('solution: %s' % r.xf)
print('max residual: %e' % r.ff)


# -*- coding: utf-8 -*-
from random import choice
from numpy import array,dot,random

unit_step = lambda x:0 if x < 0 else 1

def train(training_data,errors,eta,n,weight):
    for i in range(n):
        x,expect = choice(training_data)
        output = dot(x,weight)
        error = expect - unit_step(output)
        errors.append(error)
        weight += eta * error * x
    return weight,errors

def show(training_data,weight):
    for x, _ in training_data:
      results = dot(x,weight)
      print("{}:{} -> {}".format(x[:2],results,unit_step(results)))

def perceptron(training_data):
    # constant variable declare
    eta = 0.1
    n = 100
    weight = random.rand(3)
    errors = []
    weight ,errors = train(training_data,errors,eta,n,weight)
    show(training_data,weight)

if __name__ == '__main__':
    training_data_or = [
        (array([0,0,1]), 0),
        (array([0,1,1]), 1),
        (array([1,0,1]), 1),
        (array([1,1,1]), 1),
    ]

    training_data_and = [
        (array([0,0,1]), 0),
        (array([0,1,1]), 0),
        (array([1,0,1]), 0),
        (array([1,1,1]), 1),
    ]

    training_data_not = [
        (array([0,0,1]), 1),
        (array([0,1,1]), 0),
        (array([1,0,1]), 0),
        (array([1,1,1]), 1),
    ]
    # data_list = [training_data_not,training_data_and,training_data_or]
    data_list = [training_data_and,training_data_or]
    map(perceptron,data_list)


import numpy as np

def _createWeights(arr):
    '''
        Given a list of edges in a graph, returns the labels of distinct nodes
        and a dictionary of their weights.
    '''
    labels, counts = np.unique(arr[:, 0], return_counts=True)   # Finds the unique entries in first column, returns their values
                                                                # and their counts to calculate weights
    weight_dict = dict()
    for index, label in enumerate(labels):                      # Creates a dictionary that holds the normalization factor,
        weight_dict[label] = 1 / counts[index]                  # 1/(number of outgoing links) for each node

    return labels, weight_dict

def _createTransitionMatrix(arr, labels, weight_dict):
    '''
        Create a transition matrix based on the transitions in arr of shape (n, 2).
        Labels is the list of distinct nodes in the original graph.
        Weight_dict is a dictionary where the weight of a link for each node in labels are kept.
    '''
    transitionMatrix = np.zeros((labels.size, labels.size))

    for i, label in enumerate(labels):
        links_from_label = arr[np.nonzero(label == arr[:, 0]), 1][0]
    #    if links_from_label.shape == (0,):                              # For dangling nodes, nodes that have no outgoing links
    #        weight_dict[label] = 1 / labels.size                        # Not necessary while using with a damping factor
    #        links_to_label = labels
        transitionMatrix[np.searchsorted(labels, links_from_label), i] = weight_dict[label]

    return transitionMatrix

def _checkStochastic(matrix, eps=1e-10, verbose=False):
    '''
        Checks if a given matrix is stochastic, within an error of eps.
        Returns the number of columns that violates the condition.
        If verbose is set to True, prints the results.
    '''
    if verbose:
        print(f"Sums of columns that do not add to 1 in a reasonable error margin {eps} will be shown.")
    nonstochastic_columns = 0
    for i in matrix.sum(0):
        if not (i - 1 < eps):
            nonstochastic_columns += 1
            if verbose: print(i)
    if verbose:
        print(f"Calculation finished, average value of sums of all columns is {np.mean(matrix.sum(0))}.")
    return nonstochastic_columns

def _checkSparsity(matrix, verbose=False):
    '''
        Checks the sparsity of a given matrix. Returns the sparcity rate.
        Set verbose to true to print the results.
    '''
    zeros = np.count_nonzero(matrix==0)
    elements = matrix.size
    sparse_rate = np.divide(zeros,elements)
    if verbose:
        print(f"There are {elements} elements in total, {zeros} of them are zero.")
        print(f"Sparsity rate of this matrix is {sparse_rate*100}%")
    return sparse_rate

def addDamping(transitionMatrix, labels, p = 0.15):
    '''
        Adds damping vector to the given matrix. Damping vector simulates
        the random surfing between two nodes, even if they are not linked.
    '''
    if(p < 0 or p > 1):
        throw ValueError("Please try again with a damping factor in interval [0,1].")
    rankMatrix = (1 - p) * transitionMatrix + p * ((1/labels.size) * np.ones((labels.size,labels.size)))
    return rankMatrix

def solveRank(rankMatrix, tol=1e-7, verbose=False):
    '''
    Given the stochastic transition matrix of a graph, returns the PageRank vector of the graph,
    using the power method until the average distance between two vectors are less than 'tol'.

    Set verbose to True in order the see the iteration number and the difference of last two vectors.
    '''
    v0 = np.ones(rankMatrix.shape[0]) / rankMatrix.shape[0]

    iteration_counter = 1
    while True:
        v = np.dot(rankMatrix, v0)
        v = v / np.linalg.norm(v)
        if (np.mean(np.abs(np.subtract(v,v0))) < 2 * tol):
            break
        iteration_counter += 1
        v0 = v
        if verbose:
            print(f"Iteration: {iteration_counter}, Error: {np.mean(np.abs(np.subtract(v,v0)))}")

    if verbose:
        print(f"Appropriate vector found in {iteration_counter} iterations, final difference between two iteration result vectors was less than {tol}.")
    return v

def calculateRankVector(data, damping=0):
    '''
        Calculates the PageRank ranking vector on a (n, 2) array, which
        represents the edges of a directed graphs. Each row represents an edge
        as a tuple (node_from, node_to).
        Set damping to a positive value < 1 to apply damping on the final matrix.
    '''
    labels, weights = createWeights(data)
    rankMatrix = createTransitionMatrix(data, labels, weights)
    if damping = 0:
        rankMatrix = addDamping(rankMatrix, labels, damping)
    resultVector = solveRank(rankMatrix)
    return resultVector

def rankItemsByRankings(labels, final):
    '''
        Ranks the items in 'labels' using the values in the 'final' vector, in descending order.
    '''
    return labels[final.argsort()][::-1]


# -*- coding: utf-8 -*-
"""
Created on Sun Jun 04 00:01:19 2017

Implement a neuron that learns from two dimensional input data using Oja's Hebb rule

@author: Niranjan Thakurdesai
"""

import numpy as np
import matplotlib.pyplot as plt
from numpy import linalg as LA
import pickle
with open('../data/c10p1.pickle', 'rb') as f:
    data = pickle.load(f)
u_mat = data['c10p1']

# Zero-mean centering
# wi+1=wi+Δtη(vu−αv^2w)
mu = np.mean(u_mat, axis=0)
num_points = u_mat.shape[0]
u_centered = u_mat - np.tile(mu, (num_points, 1))

plt.scatter(u_centered[:,0], u_centered[:,1])
plt.show()

# Implement learning rule
n = 1   # n = 1/time constant
alpha = 1
timestep = 0.01
w0 = np.random.rand(2)    # initial weight vector
max_iters = 100000
w = w0
iters = 0
for i in range(max_iters):
    u = u_centered[np.remainder(i,num_points),:]    # current input vector
    v = np.dot(u,w)    # current output
    w = w + timestep*n*(v*u - alpha*v*v*w)    # update

C = np.dot(u_centered.T, u_centered)/num_points   # input covariance matrix
ev, e = LA.eig(C)
"""
The correlation matrix has only one principal eigenvector, but there are
two vectors of length 1/√α that are parallel to this eigenvector.
w can converge to either of these two vectors.
"""

"""
What happens when the data is not zero-mean centered before the learning process?
"""
# Add a constant offset to input data
offset = [5,10]
u_mat1 = u_mat + np.tile(offset, (num_points, 1))
mu1 = mu + offset
w1 = w0
iters = 0
for i in range(max_iters):
    u = u_mat1[np.remainder(i,num_points),:]    # current input vector
    v = np.dot(u,w1)    # current output
    w1 = w1 + timestep*n*(v*u - alpha*v*v*w1)    # update
"""
The two vectors that w converges to in different runs of the algorithm are parallel to the vector that points roughly towards the mean of the data.
"""

"""
What happens when the pure Hebb rule is used instead of Oja's rule?
"""
w2 = w0
iters = 0
for i in range(max_iters):
    u = u_centered[np.remainder(i,num_points),:]    # current input vector
    v = np.dot(u,w2)    # current output
    w2 = w2 + timestep*n*(v*u)    # update
"""
The vectors found by the learning rule have the same direction as those found by Oja's rule, but the length grows without bound as a function of the number of iterations.
"""


from sympy import Symbol, pprint, summation

def add_series(expr, num, values):
    """Print addition of series given by user, if values of the terms provided show the result as well"""
    n = Symbol("n")
    series = summation(expr, (n, 1, num))

    if values:
        listo = []

        values = values.split(",")
        for i in values:
            listo.append(tuple(i.split("=")))

        listo = dict(listo)
        series_value = series.subs(listo)
        print("Addition of the series: {}, result: {}".format(series, series_value))

    else:
        pprint(series)

if __name__ == '__main__':
    # expr = a+(n-1)*d
    # num = 3
    # values = a=5,d=4
    expr = input('Enter the nth term of series: ')
    num = int(input('Enter the number of terms you want in the series: '))
    values = input("Enter the variable values(a=3,b=4... etc, leave blank if not desired):")
    add_series(expr, num, values)


import numpy as np

class KMeans():
    def __init__(self, n_clusters):
        """
        This class implements the traditional KMeans algorithm with hard assignments:

        https://en.wikipedia.org/wiki/K-means_clustering

        The KMeans algorithm has two steps:

        1. Update assignments
        2. Update the means

        While you only have to implement the fit and predict functions to pass the
        test cases, we recommend that you use an update_assignments function and an
        update_means function internally for the class.

        Use only numpy to implement this algorithm.

        Args:
            n_clusters (int): Number of clusters to cluster the given data into.

        """
        self.n_clusters = n_clusters
        self.means = None

    def fit(self, features):
        """
        Fit KMeans to the given data using `self.n_clusters` number of clusters.
        Features can have greater than 2 dimensions.

        Args:
            features (np.ndarray): array containing inputs of size
                (n_samples, n_features).
        Returns:
            None (saves model - means - internally)
        """
        # initialize means
        self.means = features[np.random.randint(features.shape[0], size=self.n_clusters), :]
        old_means = np.zeros((self.n_clusters, features.shape[1]))
        labels = np.zeros(features.shape[0])

        while not np.array_equal(self.means, old_means):
            old_means = self.means.copy()
            self.update_assignments(features, labels)
            self.update_means(features, labels)

    def update_assignments(self, features, labels):
        for row in range(features.shape[0]):
            dists = np.linalg.norm(self.means - features[row,:], axis=1)
            labels[row] = np.argmin(dists)

    def update_means(self, features, labels):
        for cluster in range(self.means.shape[0]):
            bool_ind = labels == cluster
            self.means[cluster] = np.mean(features[bool_ind,:], axis=0)

    def predict(self, features):
        """
        Given features, an np.ndarray of size (n_samples, n_features), predict cluster
        membership labels.

        Args:
            features (np.ndarray): array containing inputs of size
                (n_samples, n_features).
        Returns:
            predictions (np.ndarray): predicted cluster membership for each features,
                of size (n_samples,). Each element of the array is the index of the
                cluster the sample belongs to.
        """
        predictions = np.zeros(features.shape[0])
        self.update_assignments(features, predictions)
        return predictions


"""
hilbert matrix using numpy, contains:
    - hilb(n, m): generate Hilbert Matrix of size (n, m)

"""

import numpy as np
from math import factorial

def binomial(n, k):
    """ binomial(n, k): Generate Binomial Coefficient for nCk """
    if k < 0 or k > n:
        return 0
    if k == n or k == 0:
        return 1
    return factorial(n) // (factorial(k) * factorial(n - k))

def hilb(n, m=0):
    """
    hilb Hilbert Matrix.
    :param n: number of rows
    :param m: number of cols

    hilb(n,m) is the n-by-m matrix with elements 1/(i + j - 1)
    """
    if n < 1 or m < 0:
        raise ValueError("Matrix size must be one or greater")
    elif n == 1 and (m == 0 or m == 1):
        return np.array([[1]])
    elif m == 0:
        m = n
    # np.arange(0,m) is [0, 1, 2, ..., m - 1]
    # np.arange(0,m)[:,np.newaxis] is [[0] [1]]
    # [1, 2, 3] + [[1] [2] [3]] = [[2, 3, 4] [3, 4, 5], [4, 5, 6]]
    v = np.arange(1, n + 1) + np.arange(0, m)[:, np.newaxis]
    return 1. / v


import numpy

def normal_equation(x_data, y_data):

    x_transpose = numpy.transpose(x_data)
    term1 = numpy.dot(x_transpose, y_data)
    term2 = numpy.dot(x_transpose, x_data)
    term3 = numpy.linalg.inv(term2)
    theta = numpy.dot(term3, term1)
    return theta


import numpy as np

def waysToArrange(N, K, k):
    C = np.zeros((N + 1, N + 1))
    for i in range(N + 1):
        for j in range(i + 1):
            if (j == 0 or j == i):
                C[i][j] = 1
            else:
                C[i][j] = (C[i - 1][j - 1] +
                           C[i - 1][j])
    dp = np.zeros((K + 1))
    count = 0
    dp[0] = 1
    for i in range(K):
        dp[i + 1] = (dp[i] * C[count + k[i] - 1][k[i] - 1])
        count += k[i]
    return dp[K]

if __name__ == "__main__":
    N = 4
    k = [2, 2]

    K = len(k)
    print(int(waysToArrange(N, K, k)))


import numpy as np

class DHs:
    def __init__(self, **kwargs):
        self.alpha = kwargs['alpha']
        self.theta = kwargs['theta']
        self.d = kwargs['d']
        self.a = kwargs['a']

    def DH(self):

        wholeDT = []

        #  T = np.where((T < 0) & (T > -0.01) , 0, T) <= it is not inportant
        for i in range(0,len(self.theta)): # you can use others it doesnt matter
            aco = 0 if ((np.cos(np.deg2rad(self.alpha[i] % 360)) < 1e-10) & (np.cos(np.deg2rad(self.alpha[i] % 360)) > -1e-10)) else np.cos(np.deg2rad(self.alpha[i] % 360))
            asi = 0 if ((np.sin(np.deg2rad(self.alpha[i] % 360)) < 1e-10) & (np.sin(np.deg2rad(self.alpha[i] % 360)) > -1e-10)) else np.sin(np.deg2rad(self.alpha[i] % 360))
            tco = 0 if ((np.cos(np.deg2rad(self.theta[i] % 360)) < 1e-10) & (np.cos(np.deg2rad(self.theta[i] % 360)) > -1e-10)) else np.cos(np.deg2rad(self.theta[i] % 360))
            tsi = 0 if ((np.sin(np.deg2rad(self.theta[i] % 360)) < 1e-10) & (np.sin(np.deg2rad(self.theta[i] % 360)) > -1e-10)) else np.sin(np.deg2rad(self.theta[i] % 360))

            DT = [
                [tco, -tsi*aco, tsi*asi, self.a[i]*tco],
                [tsi, tco*aco, -tco*asi, self.a[i]*tsi],
                [0, asi, aco, self.d[i]],
                [0, 0, 0, 1]
                ]

            wholeDT.append(DT)
        return np.reshape(wholeDT, [len(self.theta), 4, 4]), len(self.theta)

    def multDT(self):

        multipliedDTs = np.matmul(
            self.DH()[0][self.DH()[1] - 2],
            self.DH()[0][self.DH()[1] - 1]
            )

        if self.DH()[1] > 2:
            for i in range(self.DH()[1] - 3, -1, -1):
                multipliedDTs = np.matmul(
                    self.DH()[0][i],
                    multipliedDTs
                    )
        return multipliedDTs

    def pointInPlane(self, nokta, **axis):
        if axis['axis'] == 'global':
            return np.matmul(self.multDT(), np.reshape(np.append(nokta, [1]), [4, 1]))
        else:
            return np.matmul(np.linalg.inv(self.multDT()), np.reshape(np.append(nokta, [1]), [4, 1]))


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

def set_initial_state(N=1000, I_0=1, R_0=0, beta=0.01, gamma=0.01, t=np.linspace(0, 200, 200)):
    S_0 = N - I_0 - R_0
    return N, S_0, I_0, R_0, beta, gamma, t

def solve_diffential_equations(y, beta, gamma, t, N):
    S, I, R = y
    dSdt = - beta * S * I / N
    dIdt = beta * S * I/ N - gamma * I
    dRdt = gamma * I
    return dSdt, dIdt, dRdt

def main():
    N, S_0, I_0, R_0, beta, gamma, t = set_initial_state()
    y_0 = S_0, I_0, R_0
    result = odeint(solve_diffential_equations, y_0, t, args=(beta, gamma, N))
    S, I, R = result.T

    fig = plt.figure(facecolor="w")
    ax = fig.add_subplot(111, axisbelow=True)
    ax.plot(t, S, 'b', alpha=0.5, lw=2, label='Susceptible')
    ax.plot(t, I, 'r', alpha=0.5, lw=2, label='Infected')
    ax.plot(t, R, 'g', alpha=0.5, lw=2, label='Recovered')
    ax.set_xlabel("time")
    ax.set_ylabel("Users")
    ax.grid(b=True, which='major', c='w', lw=2, ls='-')
    legend = ax.legend(loc='best')
    plt.show()

if __name__ == "__main__":
    main()


import pylab
import numpy
import string

def chi_sq(fit,y,err):
    '''
    Returns chi_sq estimate for the fit.
    '''
    X_sq=numpy.sum(((y-fit)/err)**2)
    nu=len(y)-2.0
    sigma=numpy.sum(y-fit)/nu
    return X_sq

def fit(x,y,err,which):
    '''
    Performs fit to input data.  Computes function (gauss or lorentz) with
    a spread of function parameters, then returns the parameters for the
    function with the smalles chi squared value.
    Returns the fit to optimal parameters.
    '''
    max_index=numpy.argmax(y)
    x_0=x[numpy.argmax(y)]
    chi_all=[]
    alpha_all=[]
    x_0_all=[]
    if which=='gaussian':
        print 'Gaussian'
        for x_0 in numpy.linspace(x[max_index-4],x[max_index+4],40): #iterate from four points below and four points above the peak in 40 steps.
            for alpha in numpy.linspace(1,30,40):
                fit=(1.0/alpha)*numpy.sqrt(numpy.log(2)/numpy.pi)*numpy.exp((-1*numpy.log(2)*(x-x_0)**2)/alpha**2)
                chi_all.append(chi_sq(fit,y,err))
                alpha_all.append(alpha)
                x_0_all.append(x_0)
        alpha=alpha_all[numpy.argmin(chi_all)]   #find index of minimum in array
        x_0=x_0_all[numpy.argmin(chi_all)]       #find index of minimum in array
        print 'alpha,x_0=',alpha,x_0
        fit=(1.0/alpha)*numpy.sqrt(numpy.log(2)/numpy.pi)*numpy.exp((-1*numpy.log(2)*(x-x_0)**2)/alpha**2)
    if which=='lorentzian':
        print 'Lorentzian'
        for x_0 in numpy.linspace(x[max_index-4],x[max_index+4],40): #iterate from four points below and four points above the peak in 40 steps.
            for alpha in numpy.linspace(1,30,40):
                fit=(1.0/numpy.pi)*(alpha)/((x-x_0)**2+alpha**2)
                chi_all.append(chi_sq(fit,y,err))
                alpha_all.append(alpha)
                x_0_all.append(x_0)
        alpha=alpha_all[numpy.argmin(chi_all)]   #find index of minimum in array
        x_0=x_0_all[numpy.argmin(chi_all)]       #find index of minimum in array
        print 'alpha,x_0=',alpha,x_0
        fit=(1.0/numpy.pi)*(alpha)/((x-x_0)**2+alpha**2)  #refit with parameters of minimum chi_sq
    fit=numpy.array(fit)
    nu=len(y)-2.0
    sigma=float(numpy.sum(y-fit))/nu  #estimate error
    print 'Error=',sigma
    print
    return fit

###############
##Begin main
###############
freq=[]
strn=[]
err=[]

fin=open('ps3.dat')  #open input data
for line in fin.readlines(): #iterate of lines in the file
    line=line.split() #split line at whitespaces
    freq.append(float(line[0])) #split freq,strength,and error to different lists
    strn.append(float(line[1]))
    err.append(float(line[2]))

###convert lists to arrays
freq=numpy.array(freq)
strn=numpy.array(strn)
err=numpy.array(err)

#calls fitting function in the plotting call
pylab.ioff()
pylab.errorbar(freq,strn,yerr=err,fmt='bo-',label='Data')
pylab.plot(freq,fit(freq,strn,err,'lorentzian'),'ro-',label='Lorentzian')
pylab.plot(freq,fit(freq,strn,err,'gaussian'),'go-',label='Gaussian')
pylab.legend(numpoints=1,shadow=True)
pylab.xlabel('Frequency')
pylab.ylabel('Line Strength')
#pylab.show()
pylab.savefig('fit.pdf')
pylab.close()


'''
    Topic: Gradient Descent (Practical) using only NumPy and Python.
    Author: Yash Sharma (github.com/Yashs744)
'''

# Library
import numpy as np

# Class GradientDescent
class GradientDescent:
    def __init__(self, lr):
        '''
            Initialize 'm' and 'c' (from y = mx + c) and Learning Rate.

            Arguments:
                lr {float} -- Learning Rate
        '''

        self.m = 0
        self.c = 0

        self.lr = lr

    def predict(self, x):
        '''
            Arguments:
                x {float | int} -- Value of independent variable to produce output.

            Returns:
                float -- Value of yHat i.e value of 'mx + c'
        '''

        return (x * self.m + self.c)

    def cost(self, x, y):
        '''
            Calculate Cost i.e error between real value of y and yHat.
            Error Function: Sum of Squares.

            Arguments:
                x {float | int} -- Independent Variable
                y {float | int} -- Dependent Variable

            Returns:
                float -- Cost (or error)
        '''

        cost = 0

        for i in range(len(x)):
            cost += ((y[i] - self.predict(x[i])) ** 2)

        return (cost / float(len(x)))

    def generate_batches(self, x, y, batch_size):
        '''
            Generate Batches of Data to fit model.

            Arguments:
                x {float | int} -- Values (array) of Independent Variable
                y {float | int} -- Values (array) of Dependent Variable
                batch_size {int} -- Size of each batch on Data.
        '''

        num_points = len(x)

        idx = np.random.permutation(num_points)
        batches = range(0, num_points - batch_size + 1, batch_size)

        for batch in batches:
            X_mini = x[idx[batch:batch + batch_size]]
            y_mini = y[idx[batch:batch + batch_size]]

            yield (X_mini, y_mini)

    def derivative(self, x, y):
        '''
            Calculate Derivative of Cost (or Error) w.r.t m and c variable.

            m_derivative = -2x(y - yhat)
            c_derivate = -2(y - yhat)

            Arguments:
                x {float | int} -- Value of Independent Variable
                y {float | int} -- Value of Dependent Variable

            Returns:
                Tuple -- Tuple of Derivatives.
        '''

        y_hat = self.predict(x)

        dJ_m = -2 * (x * (y - y_hat))
        dJ_c = -2 * (y - y_hat)

        return (dJ_m, dJ_c)

    def update_parameters(self, x, y):
        '''
            Update the Parameters (i.r Variable m and c).
            Parameter are updates as follow:
                m = m - ((m_derivative / N) * lr)
                c = c - ((c_derivative / N) * lr)

            Arguments:
                x {float | int} -- Values (array) of Independent Variable
                y {float | int} -- Values (array) of Dependent Variable
        '''

        m_derv = 0
        c_derv = 0

        for i in range(len(x)):
            dJ = self.derivative(x[i], y[i])

            m_derv += dJ[0]
            c_derv += dJ[1]

        self.m -= ((m_derv / float(len(x))) * self.lr)
        self.c -= ((c_derv / float(len(x))) * self.lr)

    def fit(self, X, y, epochs = 5, batch_size = 10):
        '''
            Fit the data.

            Arguments:
                X {float | int} -- Values (array) of Independent Variable
                y {float | int} -- Values (array) of Dependent Variable

            Keyword Arguments:
                epochs {int} -- Number of Training Epochs (default: {5})
                batch_size {int} -- Size of the Batch (default: {10})
        '''

        loss = 0

        for e in range(epochs):
            loss = 0
            for (batch_x, batch_y) in self.generate_batches(X, y, batch_size):
                loss = self.cost(batch_x, batch_y)

                self.update_parameters(batch_x, batch_y)

            print (f"Epoch: {e+1} - Loss: {loss}")

if __name__ == "__main__":
    X = np.linspace(start = 1, stop = 100 , num = 100)
    y = 12 * X

    grad = GradientDescent(lr = 0.0001)
    grad.fit(X, y, epochs = 10)

    print (f"Weight: {grad.m} & Bias: {grad.c}")


# The NumPy module also comes with a number of built-in routines for linear algebra calculations.
# These can be found in the sub-module linalg.

# linalg.det
# The linalg.det tool computes the determinant of an array.
# print numpy.linalg.det([[1 , 2], [2, 1]])       #Output : -3.0

# linalg.eig
# The linalg.eig computes the eigenvalues and right eigenvectors of a square array.
# vals, vecs = numpy.linalg.eig([[1 , 2], [2, 1]])
# print vals                                      #Output : [ 3. -1.]
# print vecs                                      #Output : [[ 0.70710678 -0.70710678]
#                                                 #          [ 0.70710678  0.70710678]]

# linalg.inv
# The linalg.inv tool computes the (multiplicative) inverse of a matrix.
# print numpy.linalg.inv([[1 , 2], [2, 1]])       #Output : [[-0.33333333  0.66666667]
#                                                 #          [ 0.66666667 -0.33333333]]

# Task :
# You are given a square matrix with dimensions X. Your task is to find the determinant.
# Note: Round the answer to 2 places after the decimal.

import numpy as np

N = int(input())
l = []
for i in range(N):
    l.append(list(map(float, input().split())))
l = np.array(l).reshape(N, N)
print(round(np.linalg.det(l), 2), end = '')

# Sample Input
# 2
# 1.1 1.1
# 1.1 1.1

# Sample Output
# 0.0


import os
import sys
import json
import re
import random
import copy
import math
import collections

import numpy as np
import scipy.stats
import scipy.spatial

def perform_t_test(points1, points2, two_sided=True):
    n1 = float(len(points1))
    n2 = float(len(points2))
    x1_bar = float(np.mean(points1))
    x2_bar = float(np.mean(points2))
    s1_2 = float(np.var(points1))
    s2_2 = float(np.var(points2))
    s = math.sqrt(s1_2 / n1 + s2_2 / n2)
    t = (x1_bar - x2_bar) / s
    df_numerator = pow(s1_2 / n1 + s2_2 / n2, 2.0)
    df_denominator = pow(s1_2 / n1, 2.0) / (n1 - 1.0) + pow(s2_2 / n2, 2.0) / (n2 - 1.0)
    df = df_numerator / df_denominator

    probability = scipy.stats.t.cdf(t, df)
    if two_sided:
        if probability > 0.5:
            p_value = 1.0 - probability
        else:
            p_value = probability
        probability = 1.0 - 2 * p_value

    p_value = 1.0 - probability
    return p_value

def calculate_confidence_interval(points, confidence_threshold=0.95):
    n = float(len(points))
    v = n - 1.0
    x_bar = np.mean(points)
    s = math.sqrt(np.var(points))
    A = scipy.stats.t.ppf(confidence_threshold, v)
    lower_bound = x_bar - A * s / math.sqrt(n)
    upper_bound = x_bar + A * s / math.sqrt(n)
    return (lower_bound, upper_bound)


from sympy import sympify, lambdify, integrate
from sympy.abc import p, q
from scipy.integrate import dblquad

class Phi:
    def __init__(self, name, phi_pq=None, phi_p=None, phi_q=None, eps=1e-7):
        self.name = name
        self.phi_pq_S = phi_pq
        self.phi_p_S = phi_p
        self.phi_q_S = phi_q
        self.eps = eps
        self.__gen_phi()
        if self.__check() is False:
            raise Exception('input phi does not integrate to one')

    def __gen_phi(self):
        if self.phi_p_S is not None and self.phi_q_S is not None:
            self.phi_p_S = sympify(self.phi_p_S)
            self.phi_q_S = sympify(self.phi_q_S)
            self.phi_pq_S = self.phi_p_S * self.phi_q_S
            self.phi_qcp_S = self.phi_q_S
        elif self.phi_pq_S is not None:
            self.phi_pq_S = sympify(self.phi_pq_S)
            self.phi_p_S = integrate(self.phi_pq_S, (q, 0 , 1))
            self.phi_q_S = integrate(self.phi_pq_S, (p, 0 , 1))
            self.phi_qcp_S = self.phi_pq_S / self.phi_p_S
        else:
            raise Exception('please provide both phi_p and phi_q, or the joint pdf phi_pq.')

        self.phi_p_N = lambdify(p, self.phi_p_S)
        self.phi_q_N = lambdify(q, self.phi_q_S)
        self.phi_pq_N = lambdify((q, p), self.phi_pq_S)
        self.phi_qcp_N = lambdify((q, p), self.phi_qcp_S)

    def __check(self):
        phi_tot = dblquad(self.phi_pq_N, 0, 1, lambda p: 0, lambda p: 1)[0]
        if abs(phi_tot - 1) < self.eps:
            phi_one = True
        else:
            phi_one = False
        return phi_one


import numpy as np

def qubic_spline_coeff(x_nodes, y_nodes):
    """Here underscored variables are related to the matrix equation,
    whereas normal ones stand for the spline coefficients
    """
    polynomials_num = len(x_nodes) - 1
    coeffs = np.zeros((polynomials_num, 3))
    hs = (x_nodes - np.roll(x_nodes, 1))[1:]
    ys = (y_nodes - np.roll(y_nodes, 1))[1:]
    # Build A
    upper_diag = hs.copy()
    upper_diag[0] = 0
    lower_diag = hs.copy()
    lower_diag[-1] = 0
    diag = np.r_[[1], 2 * (upper_diag[1:] + lower_diag[:-1]), [1]]
    A_ = np.diag(upper_diag, 1) + np.diag(diag) + np.diag(lower_diag, -1)
    # Build b
    b_ = np.r_[[0], 3 / hs[1:] * ys[1:] - 3 / hs[:-1] * ys[:-1], [0]]
    c = np.dot(np.linalg.inv(A_), b_)
    a = y_nodes[:-1]
    b = 1 / hs * ys - hs / 3 * (2 * c[:-1] + c[1:])
    d = 1 / (3 * hs) * (c[1:] - c[:-1])
    return np.c_[a, b, c[:-1], d]

def qubic_spline(x, x_nodes, qs_coeff):
    for i in range(len(x_nodes)):
        if x < x_nodes[i] or i == len(x_nodes) - 1:
            dx = x - x_nodes[i - 1]
            return np.dot(qs_coeff[i - 1], np.array([1., dx, dx**2, dx**3]))
    return 0

def make_qubic_spline(x_nodes, y_nodes):
    coeff = qubic_spline_coeff(x_nodes, y_nodes)
    spline_vectorized = np.vectorize(qubic_spline, excluded=set((1, 2)))
    def _qubic_spline(x):
        return spline_vectorized(x, x_nodes, coeff)
    return _qubic_spline


import math

import numpy as np
from sympy.ntheory.primetest import (
    is_square
)
from sympy import (
    Matrix
)

from ..utils import (
    get_letters_alphabetically
)

class HillError(Exception):
    pass

def transform(
        text: str,
        key: str,
        alphabet: str,
        mode: str = "encrypt"
) -> str:
    """Hill Cipher. Encryption/decryption function.

    Args:
        text: text to be encrypted/decrypted.
        key: a set of letters of the same alphabet.
        alphabet: alphabet compiled by the user.
        mode: encryption or decryption (default "encrypt").

    Returns:
        Encrypted or decrypted string.
    """
    if not text:
        raise HillError("Input text is empty!")

    if not key:
        raise HillError("The key is missing!")

    if not alphabet:
        raise HillError("Alphabet is empty!")

    if not is_square(len(key)):
        raise HillError("Key length must be a square!")

    if not set(key.lower()).issubset(alphabet):
        raise HillError("The key must be alphabetic characters!")

    if len(alphabet) != len(set(alphabet)):
        raise HillError("The alphabet must be composed of unique characters!")

    n = math.isqrt(len(key))
    matrix_key = np.array(list(map(lambda x: alphabet.index(x.lower()), key))).reshape((n, n))

    matrix_key_det = int(np.linalg.det(matrix_key))
    if matrix_key_det == 0:
        raise HillError("Matrix determinant is zero! The matrix is degenerate!")

    if math.gcd(matrix_key_det, len(alphabet)) != 1:
        raise HillError("Matrix determinant and key length must be coprime!")

    match mode:
        case "encrypt":
            pass

        case "decrypt":
            matrix_key = np.array(Matrix(matrix_key).inv_mod(len(alphabet)))

        case _:
            raise HillError(f"Invalid processing type! -> {mode}")

    letters, indices = get_letters_alphabetically(text, alphabet)

    new_letters = ""
    for i in range(0, len(letters), n):
        vct = list(map(lambda x: alphabet.index(x.lower()), letters[i:i + n]))
        vct += [0 for _ in range(len(vct), n)]

        new_vct = np.matmul(matrix_key, vct) % len(alphabet)
        new_letters += "".join(map(lambda x: alphabet[x], new_vct))

    text_list = list(text + new_letters[len(letters)::])
    for i, index in enumerate(indices):
        old_letter = text_list[index]
        new_letter = new_letters[i]

        if old_letter.isupper():
            new_letter = new_letter.upper()

        text_list[index] = new_letter

    return "".join(text_list)

def encrypt(
        text: str,
        key: str,
        alphabet: str
):
    """Hill Cipher. Interface for calling encryption functions.

    Args:
        text: text to be encrypted.
        key: a set of letters of the same alphabet.
        alphabet: alphabet compiled by the user.

    Returns:
        Encrypted string.
    """
    return transform(text, key, alphabet, "encrypt")

def decrypt(
        text: str,
        key: str,
        alphabet: str
):
    """Hill Cipher. Interface for calling decryption functions.

    Args:
        text: text to be decrypted.
        key: a set of letters of the same alphabet.
        alphabet: alphabet compiled by the user.

    Returns:
        Decrypted string.
    """
    return transform(text, key, alphabet, "decrypt")

def make(
        text: str,
        key: str,
        alphabet: str,
        mode: str = "encrypt"
):
    """Hill Cipher. Interface for calling encryption/decryption functions.

    Args:
        text: text to be encrypted/decrypted.
        key: a set of letters of the same alphabet.
        alphabet: alphabet compiled by the user.
        mode: encryption or decryption (default "encrypt").

    Returns:
        Encrypted or decrypted string.
    """
    match mode:
        case "encrypt":
            return encrypt(text, key, alphabet)

        case "decrypt":
            return decrypt(text, key, alphabet)

        case _:
            raise HillError(f"Invalid processing type! -> {mode}")


import numpy as np
import scipy
import matplotlib.pyplot as plt
import sys

def compute_r_squared(data, predictions):
    '''
    In exercise 5, we calculated the R^2 value for you. But why don't you try and
    and calculate the R^2 value yourself.

    Given a list of original data points, and also a list of predicted data points,
    write a function that will compute and return the coefficient of determination (R^2)
    for this data.  numpy.mean() and numpy.sum() might both be useful here, but
    not necessary.

    Documentation about numpy.mean() and numpy.sum() below:
    http://docs.scipy.org/doc/numpy/reference/generated/numpy.mean.html
    http://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html
    '''

    # your code here
    SST = ((data-np.mean(data))**2).sum()
    SSReg = ((predictions-data)**2).sum()
    r_squared = 1 - SSReg / SST
    return r_squared


import numpy as np

def rotation_matrix(a):
    return np.array([[np.cos(a), -np.sin(a)],
                     [np.sin(a), np.cos(a)]])

def margin(angle, size, v1, v2):
    vv = v2 - v1
    vv = vv / np.sqrt((vv**2).sum())
    edgelen = size / np.sin(angle)
    v3 = v1 + edgelen * np.dot(rotation_matrix(angle), vv)
    v4 = v2 - edgelen * np.dot(rotation_matrix(-angle), vv)
    return np.c_[v2, v4, v3, v1].T

def regular_polygon(n, v1, v2):
    ang = 2 * np.pi / n
    mat = rotation_matrix(ang)
    vs = np.c_[v1, v2]
    vv = v2 - v1
    vprev = v2
    for i in range(n - 2):
        v = np.dot(mat, vv) + vprev
        vs = np.c_[vs, v]
        vv = v - vprev
        vprev = v
    return vs.T


# This Program is distributed under the terms of the MIT license.
# Author: Rui Sun
# Date: 2021-12-12
# Copyright: Rui Sun from SAA, SJTU
# Description: Task 2 - Numerical-Integration
# Environment: Python 3.9.5 64-bit
#              Numpy 1.20.3
#              Matplotlib 3.4.2

import numpy as np
import matplotlib.pyplot as plt
from numpy.lib.polynomial import roots

EPS = 1e-7
MAXSIZE = 200

# Romberg Integration
# \int_a^b{f(x)}\mathrm{d}x
def romberg(f, a, b):
    h = b - a
    R = np.zeros(MAXSIZE)
    R[0] = (h / 2) * (f(a) + f(b))
    for k in range(1, MAXSIZE):
        h /= 2
        R[k] = R[k - 1] / 2
        for j in range(1, 2**(k - 1) + 1):
            R[k] += h * f(a + (2 * j - 1) * h)
        if abs(R[k] - R[k - 1]) < EPS:
            return R[k]
    print("Error: Romberg Integration failed.")
    return 0

# Gauss-Legendre Integration
# \int_a^b{f(x)}\mathrm{d}x
def legendre(n, x):
    if n == 0:
        return 1.0
    elif n == 1:
        return x
    else:
        return ((2.0 * n - 1.0) * x * legendre(n - 1, x) - (n - 1.0) * legendre(n - 2, x)) / n

def dlegendre(n, x):
    if n == 0:
        return 0.0
    elif n == 1:
        return 1.0
    else:
        return (n / (x**2 - 1.0)) * (x * legendre(n, x) - legendre(n - 1, x))

def legendre_roots(polyorder):
    if polyorder < 2:
        print("Error: Polynomial order must be greater than 1.")
        return None
    else:
        roots = []
        for i in range(1, int(polyorder / 2) + 1):
            x = np.cos(np.pi * (i - 0.25) / (polyorder + 0.5))
            error = 10 * EPS**2
            while error > EPS**2:
                dx = -legendre(polyorder, x) / dlegendre(polyorder, x)
                x += dx
                error = abs(dx)
            roots.append(x)
        roots = np.array(roots)
        if polyorder % 2 == 0:
            roots = np.concatenate((-roots, roots[::-1]))
        else:
            roots = np.concatenate((-roots, [0], roots[::-1]))
        return roots

def gauss_legendre_weights(polyorder):
    w = []
    xs = legendre_roots(polyorder)
    w = 2.0 / ((1 - xs**2) * dlegendre(polyorder, xs)**2)
    return w, xs

def gauss_legendre(f, a, b):
    delta = 99999
    I = 0
    n = 1
    while delta > EPS:
        n += 1
        w, xs = gauss_legendre_weights(n)
        I_old = I
        I = (b - a) / 2 * np.sum(w * f((b - a) / 2 * xs + (b + a) / 2))
        delta = abs(I - I_old)
        if n > 1000:
            print("Error: Gauss-Legendre Integration failed.")
            return 0
    return I

def fa(x):
    return (x**2) * np.exp(x)

def fb(x):
    return np.exp(x) * np.sin(x)

def fc(x):
    return 4 / (1 + x**2)

def fd(x):
    return 1 / (1 + x)

def f(x):
    return np.exp(-x**2)

# main
print('Romberg Integration')
print('f(x) = x^2 e^x')
print('a = 0, b = 1')
print('I = ', romberg(fa, 0, 1))
print('\n')
print('f(x) = e^x sin(x)')
print('a = 1, b = 3')
print('I = ', romberg(fb, 1, 3))
print('\n')
print('f(x) = 4/(1+x^2)')
print('a = 0, b = 1')
print('I = ', romberg(fc, 0, 1))
print('\n')
print('f(x) = 1/(1+x)')
print('a = 0, b = 1')
print('I = ', romberg(fd, 0, 1))
print('\n')

print('Gauss-Legendre Integration')
print('f(x) = x^2 e^x')
print('a = 0, b = 1')
print('I = ', gauss_legendre(fa, 0, 1))
print('\n')
print('f(x) = e^x sin(x)')
print('a = 1, b = 3')
print('I = ', gauss_legendre(fb, 1, 3))
print('\n')
print('f(x) = 4/(1+x^2)')
print('a = 0, b = 1')
print('I = ', gauss_legendre(fc, 0, 1))
print('\n')
print('f(x) = 1/(1+x)')
print('a = 0, b = 1')
print('I = ', gauss_legendre(fd, 0, 1))
print('\n')

x = np.linspace(-5, 5, 1000)
y = f(x)
plt.title(r'f(x)=e^(-x^2)')
plt.grid('on')
plt.plot(x, y)

print('Romberg Integration')
print(f'I = {romberg(f, -5, 5)}')
print('Gauss-Legendre Integration')
print(f'I = {gauss_legendre(f, -5, 5)}')


"""
The minimal path sum in the 5 by 5 matrix below, by starting in any cell in the
left column and finishing in any cell in the right column, and only moving up,
down, and right, is indicated in red and bold; the sum is equal to 994.

 A = np.array([  [131, 201, 630, 537, 805],
                 [673,  96, 803, 699, 732],
                 [234, 342, 746, 497, 524],
                 [103, 965, 422, 121,  37],
                 [18, 150, 111, 956, 331]  ]).T

Find the minimal path sum, in matrix.txt (right click and "Save Link/Target
As..."), a 31K text file containing a 80 by 80 matrix, from the left column to
the right column.

Solution comment: Solve column by column, each time only considering a (N x 2) matrix.
Best path either straight right, or any number of steps up or down followed by a right step.
If at any point the column sum going up/down exceeds the best known path, we can stop going
up/down.

Time complexity is O(N x N x (N/2 + N/2) = O(N^3) worst case, but
the up/down search will terminate much quicker than (N/2 + N/2) on average. Runs in ~0.23s.
"""

import numpy as np

A = np.loadtxt('p082_matrix.txt', delimiter=',')
B = np.zeros(A.shape[0])  # Buffer for the column being solved.

for j in reversed(range(A.shape[1]-1)):
    for i in range(A.shape[0]):
        c_best = A[i,j] + A[i,j+1]
        # Down search
        col_sum = A[i,j]
        for k in range(i+1, A.shape[0]):
            col_sum += A[k,j]
            if col_sum >= c_best:
                break
            c_best = min(c_best, col_sum + A[k,j+1])

        # Up search
        col_sum = A[i,j]
        for k in reversed(range(i)):
            col_sum += A[k,j]
            if col_sum >= c_best:
                break
            c_best = min(c_best, col_sum + A[k,j+1])

        B[i] = c_best
    A[:,j] = B

print(np.min(A[:,0]))


import numpy as np

'''
Function:  Normalization
Description: Normalize input data. For vector x, the normalization process is given by
            normalization(x) = (x - min(x))/(max(x) - min(x))
Input:  data        dataType: ndarray   description: input data
Output: normdata    dataType: ndarray   description: output data after normalization
'''

def Normalization(data):
    # get the max and min value of each column
    minValue = data.min(axis=0)
    maxValue = data.max(axis=0)
    diff = maxValue - minValue
    # normalization
    mindata = np.tile(minValue, (data.shape[0], 1))
    normdata = (data - mindata) / np.tile(diff, (data.shape[0], 1))
    return normdata

'''
Function:  Standardization
Description: Standardize input data. For vector x, the normalization process is given by
             Standardization(x) = x - mean(x)/std(x)
Input:  data            dataType: ndarray   description: input data
Output: standarddata    dataType: ndarray   description: output data after standardization
'''

def Standardization(data):
    # get the mean and the variance of each column
    meanValue = data.mean(axis=0)
    varValue = data.std(axis=0)
    standarddata = (data - np.tile(meanValue, (data.shape[0], 1))) / np.tile(varValue, (data.shape[0], 1))
    return standarddata


import numpy as np
from models.k_means import KMeans

class GMM:
    """Implementes Gaussian Mixture Model fitted with EM algorithm. Similar to
    K-means, iteratively adjusts the clusters to match better the points
    belonging to them and then recompute which points belong to what cluster.
    In this case the clusters are soft, each point has a likelihood for each
    possible clusters. Clusters are modeled ase multivariate gaussians. The
    model is fitted using the EM algorithm.

    Parameters
    ----------
    k : `int`, optional
        Number of clusters. Defaults to 2.

    max_iter : `int`, optional
        Max iterations to perform before the algorithm stops training if
        convergence is not achieved.

    th : `float`, optional
        Minimum change in the posterior probabilities `h` to continue training.
        When the change is smaller the training is stopped as the algorithm is
        considered to have converged. Defaylts to 1e-7.
    """

    def __init__(self, k=2, max_iter=100, th=1e-7):
        self.k = k
        self.max_iter = max_iter
        self.weights = None
        self.th = 1e-7

    def fit(self, X, re_init=True, verbose=False):
        """Fits `self.k` multivariate gaussians to `X` using EM algorithm.

        Parameters
        ----------
        X : `numpy.ndarray` (n_examples, n_features)
            Training data.

        re_init : `boolean`, optional
            Whether to reinitialize centroids. Defaults to True. If this is the
            first call to fit, centroids will be initialized eitherway.
        """

        # Initialize gaussians
        if re_init or (self.weights is None):
            self._init_gaussians(X)

        prev_h = None
        for i in range(self.max_iter):
            # Expectation
            h = self._expectation_step(X)

            # Maximization
            self._maximization_step(X, h)
            if (prev_h is not None
                and np.linalg.norm(h-prev_h) < self.th):
                # Converged
                break
            prev_h = h

    def predict(self, X):
        return self._expectation_step(X)

    def _expectation_step(self, X):
        (n_samples, n_features) = X.shape
        # Computes likelihood for each sample of each gaussian
        h = np.zeros((n_samples, self.k))
        for k in range(self.k):
            h[:, k] = self._multinomial_gaussian(
                    X,
                    self.means[k],
                    self.covariances[k])

            h[:, k] *= self.weights[k]

        h = h/np.sum(h, axis=1, keepdims=True)

        return h

    def _maximization_step(self, X, h):
        (n_samples, n_features) = X.shape
        # Update weights
        self.weights = (1./n_samples)*np.sum(h, axis=0)

        # Update gaussians
        for k in range(self.k):
            h_sum = np.sum(h[:, k])
            self.means[k] = np.sum(h[:, k, None]*X, axis=0)/h_sum

            diff = (X-self.means[k])
            self.covariances[k] = np.matmul(diff.T, h[:, k, None]*diff)/h_sum

    def _multinomial_gaussian(self, x, mean, variance):
        factor = 1./np.sqrt(np.linalg.det(variance) * (2*np.pi) ** self.k)
        diff = x - mean
        inv_var = np.linalg.pinv(variance)
        exp = np.exp(-0.5 * np.sum((diff @ inv_var) * diff, axis=1))

        return factor * exp

    def _init_gaussians(self, X):

        # Init gaussians using the result of running kmeans
        self.means, self.covariances = KMeans(k=self.k)._init_gmm(X)

        self.weights = [1./self.k]*self.k


## module midpoint
''' yStop = integrate (F,x,y,xStop,tol=1.0e-6)
    Modified midpoint method for solving the
    initial value problem y' = F(x,y}.
    x,y   = initial conditions
    xStop = terminal value of x
    yStop = y(xStop)
    F     = user-supplied function that returns the
            array F(x,y) = {y'[0],y'[1],...,y'[n-1]}.
'''
import numpy as np
import math
def integrate(F,x,y,xStop,tol):

    def midpoint(F,x,y,xStop,nSteps):

  # Midpoint formulas
        h = (xStop - x)/nSteps
        y0 = y
        y1 = y0 + h*F(x,y0)
        for i in range(nSteps-1):
            x = x + h
            y2 = y0 + 2.0*h*F(x,y1)
            y0 = y1
            y1 = y2
        return 0.5*(y1 + y0 + h*F(x,y2))

    def richardson(r,k):
  # Richardson's extrapolation
        for j in range(k-1,0,-1):
            const = (k/(k - 1.0))**(2.0*(k-j))
            r[j] = (const*r[j+1] - r[j])/(const - 1.0)
        return

    kMax = 51
    n = len(y)
    r = np.zeros((kMax,n))
  # Start with two integration steps
    nSteps = 2
    r[1] = midpoint(F,x,y,xStop,nSteps)
    r_old = r[1].copy()
  # Increase the number of integration points by 2
  # and refine result by Richardson extrapolation
    for k in range(2,kMax):
        nSteps = 2*k
        r[k] = midpoint(F,x,y,xStop,nSteps)
        richardson(r,k)
      # Compute RMS change in solution
        e = math.sqrt(np.sum((r[1] - r_old)**2)/n)
      # Check for convergence
        if e < tol: return r[1]
        r_old = r[1].copy()
    print("Midpoint method did not converge")


# -*- coding: utf-8 -*-
"""
Created on Wed Sep 25 15:15:18 2019

@author: Sturla
"""
import numpy as np
import matplotlib.pyplot as plt
#from matplotlib import rc

T = 1000 #The total population
gamma = 0.20 #Probability og becomming immune

def beta(I : int) -> float:
    '''Calculates the new probability for each iteration.'''
    return 0.5*I/T

def increment_time(S : int, I : int, R : int):
    '''Runs one iteration of the process.'''
    #Pick the number of new infected and immune by a binomial distubution.
    new_infected = np.random.binomial(S, beta(I))
    new_immune = np.random.binomial(I, gamma)

    S -= new_infected
    R += new_immune
    I += (new_infected - new_immune)
    return S, I, R

def simulation() -> list:
    '''Simulates the markovchain using the given probabilities'''
    time_steps = []
    time_steps.append([950, 50, 0])
    for i in range(0, 200):
        S, I, R = increment_time(time_steps[i][0], time_steps[i][1], time_steps[i][2])
        time_steps.append([S, I, R])
    return time_steps

def simulation_max_infected(initial_conditions : list):
    '''
    Simulates the markov chain, and keeps track of the max number of
    infected, and the number uf iterations untill max is reached
    '''
    time_steps = []
    time_steps.append(initial_conditions)

    max_infected = initial_conditions[1]
    index = 0
    for i in range(0, 200):
        S, I, R = increment_time(time_steps[i][0], time_steps[i][1], time_steps[i][2])
        if I > max_infected:
            max_infected = I
            index = i
        elif I == 0:
           break
        time_steps.append([S, I, R])
    return max_infected, index

def calc_avrage_max():
    '''
    Simulates the process 1000 times, and returns the avarge max, and the
    avarge iterations untill max is reached.
    '''
    max_list = []
    time_list = []
    for i in range(1000):
        max_infected, time = simulation_max_infected([950, 50, 0])
        max_list.append(max_infected)
        time_list.append(time)
    return np.average(max_list), np.average(time_list)

max_infected, time_of_max = calc_avrage_max()

print("e)")
graph = np.array(simulation())
# use LaTeX fonts in the plot
#plt.rc('text', usetex=True)
#plt.rc('font', family='serif')

# plot
plt.plot(graph)
# set labels (LaTeX can be used)
#plt.title(r'\textbf{Mutual Information Feature Selection}', fontsize=11)
#plt.xlabel(r'\textbf{Time (days)}', fontsize=11)
#plt.ylabel(r'\textbf{People}', fontsize=11)
plt.ylabel('People')
plt.xlabel('Time (days)')
plt.title("The number of people in each state", fontdict={'fontname': 'Times New Roman', 'fontsize': 21}, y=1.03)
plt.show()
print("f)")
print("Max number of infected: " , max_infected)
print("Time untill max: " ,time_of_max)

Project 1/project1 problem 1 task d and e.py


from numpy import pi, arange, arctan2, sqrt, meshgrid, linspace, sin, cos, array, log, ones
from scipy import ndimage

def ConvertToCylindrical(array_in, x_min, x_max, y_min, y_max, theta_offset = 0.0, oversample_th = 2.0, oversample_r = 2.0):
    """Take an array in rectangular coordinates and transform into cylindrical
    (r,theta) coordinates.  Must specify axis limits (min and max) in real units
    of both axes.  As a side effect, this allows calculation of the center element ->
    (x0, y0) = (-x_min/x_stepsize, -y_min/y_stepsize)

    the spacing of the input mesh in the transformed variables is approximately:
    dr = (1/r) * (x dx + y dy)
    dtheta = 1/r^2 * sqrt(x^2 dy + y^2 dx)

    the output mesh is has a spacing of dr_min/oversample_r, and
    dtheta_min/oversample_th
    so that the output grid is bigger (larger array) than the input array, when
    oversample_r and oversample_th are greater than 1.

    At each pixel of the output array, a lookup pair of x, y is created
    that correspond to the r, th at that pixel.  The value of the input array
    at x, y is inserted into the output array (at r, th) via the scipy.ndimage function.

    Lookup pairs that are outside the x,y boundaries of the input array result in a zero
    being written at r,th (this occurs every time, as a rectangular r,th space maps onto
    a non-rectangular x,y space)

    A mask with the same dimensions as the output array is also generated, which has a
    value 1 everywhere the mapping was successful, and 0 where unsuccessful.

    Returns: data array in r,th space; mask; and boundaries of new r,th space
    """

    x_axis = linspace(x_min, x_max, array_in.shape[0])
    y_axis = linspace(y_min, y_max, array_in.shape[1])

    x_stepsize = float(x_max - x_min) / array_in.shape[0]
    y_stepsize = float(y_max - y_min) / array_in.shape[1]

    x,y = meshgrid(x_axis, y_axis)
    # meshgrid makes two new arrays with same dimensions as array_in,
    # but filled with x and y coordinates of each point instead of data

    r_in = sqrt(x**2 + y**2)
    theta_in = arctan2(y, x)
    # these are two more arrays with same dimensions as array_in,
    # but filled with the r, theta coordinates of each point of array_in

    dtheta_min = (1.0/r_in[r_in>0]**2 * sqrt(x[r_in>0]**2 * y_stepsize**2 + y[r_in>0]**2 * x_stepsize)).min() * 180.0/pi
    # dtheta is approximately 1/r^2 * sqrt(x^2 dy + y^2 dx)
    th_step = dtheta_min / oversample_th

    dr_min = (1.0/r_in[r_in>0] * ( abs(x[r_in>0] * x_stepsize) + abs(y[r_in>0] * y_stepsize) )).min()
    # dr is (1/r) * (x dx + y dy)
    r_step = dr_min / oversample_r

    th_out_axis = arange(theta_offset, theta_offset + 360.0 + th_step, th_step )
    r_out_axis = arange(r_in.min(), r_in.max(), r_step)

    th_out, r_out = meshgrid(th_out_axis, r_out_axis)

    x_lookup = ( r_out * cos(th_out*pi/180.0) - x_min ) / x_stepsize  # coordinates for looking up data in array_in
    y_lookup = ( r_out * sin(th_out*pi/180.0) - y_min ) / y_stepsize
    #return x_lookup, y_lookup, r_out, th_out

    data_mask = ones(r_out.shape, dtype=int)
    data_mask[x_lookup >= array_in.shape[0]] = 0
    data_mask[x_lookup < 0] = 0
    data_mask[y_lookup >= array_in.shape[1]] = 0
    data_mask[y_lookup < 0] = 0

    #x_lookup.clip(0)
    #y_lookup.clip(0)

    array_out = ndimage.map_coordinates(array_in, array([x_lookup, y_lookup]), order = 1)
    # ndimage.map_coordinates takes a source image, and a set of two arrays: the arrays hold lookup coordinates.
    # Each pixel of a new array (with dimensions equal to the lookup arrays) is set by looking at the pixel
    # in the source image with the coordinate pair specified.

    extent = [th_out.min(), th_out.max(), r_in.min(), r_in.max()]
    return array_out, data_mask, extent
    #self.qxpp.clip(0)
    #self.qzpp.clip(0)

if __name__ == '__main__':
    from pylab import imshow, show, xlabel, ylabel, colorbar, figure, title
    x, y = meshgrid(arange(-50., 150., 1), arange(-20., 30., 1))
    r = sqrt(x**2 + y**2)
    th = arctan2(y, x)
    d = sin(r*pi/25.0)**2
    #d = cos(th)**2 * 1.0/(r + 1.0)**2
    array_out, data_mask, extent = ConvertToCylindrical(d.T, -50, 150, -20, 30)
    # transposing the meshgrid because meshgrid(x,y) has shape (len(y), len(x))

    figure()
    imshow(array_out, aspect = 'auto', origin='lower', extent = extent)
    xlabel(r'$\theta ({}^{\circ})$', size='large')
    ylabel(r'$r$', size='large')
    title(r'$\sin^2(r)$ in cylindrical coords', size='large')
    colorbar()

    figure()
    imshow(d, origin='lower', extent=[-50,150,-20,30])
    xlabel(r'$x$', size='large')
    ylabel(r'$y$', size='large')
    title(r'$\sin^2(r)$ in rectangular coords', size='large')
    colorbar()

    a2, m2, e2 = ConvertToCylindrical(d.T, -50, 150, -20, 30, theta_offset=90.0)
    figure()
    imshow(a2, aspect = 'auto', origin='lower', extent=e2)
    xlabel(r'$\theta ({}^{\circ})$', size='large')
    ylabel(r'$r$', size='large')
    title(r'$\sin^2(r)$ in cylindrical coords with 90.0 deg offset', size='large')
    colorbar()
    show()


# find eigenvalue S, and corresponding eigenvector X, using Reighley quotient R(A,B)= x'Ax/x'Bx = X' S X / X'X
# Example:
# A = (x1 - x2)' * ( x1 - x2); % L2 normal, x's dimension is nxp, n = number of observations, p = dimensionality.
# B = cov([x1; x2]);
# [S,X] = ReighleyQuotient(A,B,'min',1)
#
# function [S,X]=ReighleyQuotient(A,B,ORDER,ORTH)
# %Reighley quotient R(A,B)= x'Ax/x'Bx = X' S X / X'X
# %find eigenvalue S, and corresponding eigenvector X
# %ORDER='min', minimim first, ascend order of S, ORDER='max', descend order of S
# %ORTH=1, orthogonalize eigenvectors.
# % by Jing Wang, jingwang.physics@gmail.com, 05/23/3017
#
# if ishermitian(A) && ishermitian(B)
#     [~,Sb,Vb]=svd(B);
#     %C=Vb*sqrt(Sb);
#     sbinv=1./sqrt(diag(Sb));
#     Sbinv=diag(sbinv);
#     D=Sbinv* Vb'*A*Vb*Sbinv;
#     [~,S,V]=svd(D);
#     X_=Vb*Sbinv*V;
#     IND=[];
#     for i=1:size(X_,2)
#         x=X_(:,i);
#         X_(:,i)=x./norm(x);
#         if norm(x)<1e6
#             IND=[IND;i];
#         end
#     end
#
#     X=X_(:,IND); % remove bad column, if it's rank deficient
#
#     if strcmpi(ORDER, 'min')
#         X=X(:,end:-1:1);
#     end
#
#     if ORTH
#         [X,~]=qr(X);
#     end
#
# else
#     disp('Error!A or B is not Hermitian.')
# end
#
# end

from numpy.linalg import svd, norm, qr
def ReighleyQuotient(A, B, ascending=True, orthogonalize=True, tol=1e6):
    """
    find eigenvalue S, and corresponding eigenvector X, that solves the
    Reighley equation R(A,B) = x'Ax/x'Bx = X' S X / X'X

    Parameters
    ----------
    A : matrix

    B : matrix

    ascending : bools
        Whether to order the eigenpairs by eigenvalue size with minimum first

    orthogonalize : bool, default True
        Whether to QR the matrix and return it orthogonalized.

    tol : float, default 1e6
        The highest value accepted for the norm of a given column.

    Returns
    -------
    S : singular values

    References
    ----------
    From the following article:

    Adapted from the article author's matlab code:
    https://www.mathworks.com/matlabcentral/fileexchange/66547-reighleyquotient-a-b-order-orth-
    """
    assert A == A.H and B == B.H # both are hermitian
    _, s, V = svd(B)
    C = V * np.sqrt(s)
    sinv = (1./np.sqrt(s.diag()))
    D = sinv * V.T * A * V * sinv

    new_s, new_V = svd(D)
    X_ = V*sinv*new_V
    IND = []
    for i in range(X_.shape[1]):
        x = X_[:,i]
        X_[:,i] = x/norm(x)
        if norm(x) < tol:
            IND.append(i)

    X = X_[:,IND]

    if ascending:
        X = X[:,::-1]
    if orthogonalize:
        X, _ = qr(X)

    return new_s, X


import numpy as np

# A Python3 program to find if 2 given line segments intersect or not
# This code is contributed by Ansh Riyal
# SOURCE: https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
# class Point:
#     def __init__(self, x, y):
#         self.x = x
#         self.y = y

def is_on_segment(p, q, r):
    # very very jank indexing
    if (
        (q[0] <= max(p[0], r[0]))
        and (q[0] >= min(p[0], r[0]))
        and (q[1] <= max(p[1], r[1]))
        and (q[1] >= min(p[1], r[1]))
    ):
        return True
    return False

def orientation(p, q, r):
    # to find the orientation of an ordered triplet (p,q,r)
    # function returns the following values:
    # 0 : Collinear points
    # 1 : Clockwise points
    # 2 : Counterclockwise
    # See https://www.geeksforgeeks.org/orientation-3-ordered-points/amp/
    # for details of below formula.
    val = (float(q[1] - p[1]) * (r[0] - q[0])) - (float(q[0] - p[0]) * (r[1] - q[1]))
    if val > 0:
        return 1  # Clockwise orientation
    elif val < 0:
        return 2  # Counterclockwise orientation
    else:
        return 0  # Collinear orientation

# The main function that returns true if
# the line segment 'p1q1' and 'p2q2' intersect.
def check_intersect(p1, q1, p2, q2):
    # Find the 4 orientations required for
    # the general and special cases
    o1 = orientation(p1, q1, p2)
    o2 = orientation(p1, q1, q2)
    o3 = orientation(p2, q2, p1)
    o4 = orientation(p2, q2, q1)

    # General case
    if (o1 != o2) and (o3 != o4):
        return True

    # Special Cases
    # p1 , q1 and p2 are collinear and p2 lies on segment p1q1
    if (o1 == 0) and is_on_segment(p1, p2, q1):
        return True
    # p1 , q1 and q2 are collinear and q2 lies on segment p1q1
    if (o2 == 0) and is_on_segment(p1, q2, q1):
        return True
    # p2 , q2 and p1 are collinear and p1 lies on segment p2q2
    if (o3 == 0) and is_on_segment(p2, p1, q2):
        return True
    # p2 , q2 and q1 are collinear and q1 lies on segment p2q2
    if (o4 == 0) and is_on_segment(p2, q1, q2):
        return True
    # If none of the cases
    return False

# SOURCE: https://stackoverflow.com/questions/20677795/how-do-i-compute-the-intersection-point-of-two-lines
def line_intersection(line1, line2):
    xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
    ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])

    # define local function
    def det(a, b):
        return a[0] * b[1] - a[1] * b[0]

    div = det(xdiff, ydiff)
    x, y = None, None

    if div != 0:
        d = (det(*line1), det(*line2))
        x = det(d, xdiff) / div
        y = det(d, ydiff) / div
    return x, y

# SOURCE: https://stackoverflow.com/questions/3252194/numpy-and-line-intersections
def get_intersect(p1, q1, p2, q2):
    """
    Returns the point of intersection of the lines passing through p1,q1 and p2,q2.
    p1: [x, y] a point on the FIRST LINE
    q1: [x, y] another point on the FIRST LINE
    p2: [x, y] a point on the SECOND LINE
    q2: [x, y] another point on the SECOND LINE
    """

    is_intersecting = check_intersect(p1, q1, p2, q2)
    intersect_pt = None
    if is_intersecting:
        # using this shaves off ~0.5s per 100iterations for the LiDAR code
        x, y = line_intersection((p1, q1), (p2, q2))
        intersect_pt = [x, y]

    # operations below are slow
    # if is_intersecting:
    #     s = np.vstack([p1, q1, p2, q2])  # s for stacked
    #     h = np.hstack((s, np.ones((4, 1))))  # h for homogeneous
    #     l1 = np.cross(h[0], h[1])  # get first line
    #     l2 = np.cross(h[2], h[3])  # get second line
    #     x, y, z = np.cross(l1, l2)  # point of intersection
    #     if z != 0:
    #         intersect_pt = [x / z, y / z]
    return intersect_pt

if __name__ == "__main__":
    # Driver program to test above functions:
    p1 = [1, 1]
    q1 = [10, 1]
    p2 = [1, 2]
    q2 = [10, 2]
    print(get_intersect(p1, q1, p2, q2))

    p1 = [10, 0]
    q1 = [0, 10]
    p2 = [0, 0]
    q2 = [10, 10]
    print(get_intersect(p1, q1, p2, q2))

    p1 = [-5, -5]
    q1 = [0, 0]
    p2 = [1, 1]
    q2 = [10, 10]
    print(get_intersect(p1, q1, p2, q2))


''' mbinary
#########################################################################
# File : interplotion.py
# Author: mbinary
# Mail: zhuheqin1@gmail.com
# Blog: https://mbinary.xyz
# Github: https://github.com/mbinary
# Created Time: 2018-10-02  21:14
# Description:
#########################################################################
'''

#########################################################################
# File : interplotion.py
# Author: mbinary
# Mail: zhuheqin1@gmail.com
# Blog: https://mbinary.xyz
# Github: https://github.com/mbinary
# Created Time: 2018-05-18  09:29
# Description: 插值计算,有牛顿插值,拉格朗日插值,以及通过插值得到的多项式估计新的函数值
#########################################################################

import sympy
from collections import namedtuple
from functools import reduce
from operator import mul

X = sympy.Symbol ('x')
point = namedtuple('point',['x','y'])

class interplotion:
    def __init__(self,points):
        self.points  = [point(x,y) for x,y in points]
        self.xs= [i for i,j in points]
        self.poly,self.rem = self.newton(self.points,0,len(self.points)-1)

    def newton(self,li,a,b):
        '''li:[(x,f(x))...]'''

        qs = [li[0].y]

        def quoDiff(begin,end):
            if begin == end:return li[begin].y
            q = (quoDiff(begin+1,end)-quoDiff(begin,end-1))/(li[end].x-li[begin].x)
            if begin == a:qs.append(q)
            return q

        quoDiff(a,b)
        poly ,base = 0, 1
        for i,q  in enumerate(qs):
            poly += q*base
            base*= X-li[i].x
        return poly, base*qs[-1]
    def lagrange(self,points=None):
        xs = None
        if points is None:
            xs = self.xs
            points = self.points
        else: xs =[x for x,y in points]
        product = reduce(mul,[X-x  for x in xs],1)
        poly = 0
        for x,y in points:
            tmp  = product/(X-x)
            coef = y/(tmp.subs(X,x))
            poly+= coef *tmp
        return poly

    def predict(self,val,poly = None):
        if poly is None:poly = self.poly
        return poly.subs(X,val) #  note the func  subs

if __name__ == '__main__':
    f = interplotion([(81,9),(100,10),(121,11)])
    p = f.lagrange()
    print(p.subs(X,105))
    print(p)

    intor = interplotion([(0,11),(0.02,9),(0.04,7),(0.06,10)])
    p = intor.lagrange()
    print(p)
    res = intor.predict(0.08)
    print(res)


#!/usr/bin/env python
# coding: utf-8

# # Método de punto fijo
# J.J
# ---
# Dado que se busca resolver $f(x) = 0$, la función $f$ puede ser reescrita como $f(x) = g(x) - x = 0$. El método de punto fijo está dado por las iteraciones:
#
# \begin{equation}
# x_{k+1} = g(x_{k}).
# \end{equation}
#
# El método converge a una raíz si los valores de $x_{k}$ en cada iteración cumplen $|g'(x)| \leq 1$.

# Ejemplo: $f(x) = 2x^{3} - 9x^{2} + 7x + 6 = 0$
# \begin{equation}
# x = \frac{1}{7} \{ -2x^{3} + 9x^{2} - 6 \}
# \end{equation}

# In[1]:

import numpy as np
from sympy import *
from sympy.utilities.lambdify import lambdify
import matplotlib.pyplot as plt
init_printing(use_unicode=True)

# In[2]:

#calcular la derivada de f
x = symbols('x')
funcion = 2*x**3 - 9*x**2 + 7*x + 6
gfuncion = (-2*x**3 + 9*x**2 - 6)/7 #escribir la función aqui
dgfuncion = diff(gfuncion, x)
print(str(dgfuncion))

# In[3]:

f = lambdify(x, funcion)
g = lambdify(x, gfuncion)
dg = lambdify(x, dgfuncion)

# In[4]:

X = np.linspace(-1, 4, 100)
plt.plot(X,dg(X), label = 'g´(x)')
plt.ylim(-1,1)
plt.legend()
plt.show()

# La desigualdad se cumple aproximadamente entre en los intervalos $[-0.44, 0.46]$ y $[2.5, 3.34]$, aun que en realidad, en el primer intervalo no converge.

# In[5]:

e = 0.0001 #error
maxit = 100 #iteraciones máximas

# In[6]:

def PuntoFijo(x0, func = g, error = e, iterations = maxit):
    it = 0
    while (abs(f(x0)) > e) and (it < maxit):
        it += 1
        xk = g(x0)
        x0 = xk
    return x0

# In[7]:

sol = PuntoFijo(2.6)
print(sol)

# In[8]:

plt.plot(X, f(X), label='f(x)')
plt.plot(sol,f(sol),'ro')
plt.legend()
plt.show()

# In[ ]:


# Based on
# https://www.teachme.codes/numpy-docs/Meshgrid-SimulationExample/code/
# https://scicomp.stackexchange.com/questions/33325/can-i-solve-a-model-in-gekko-with-black-box-simulated-annealing-or-ga-solvers

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

# define objective function
def f(x):
    x1 = x[0]
    x2 = x[1]
    obj = 0.2 + x1**2 + x2**2 - 0.1*math.cos(6.0*3.1415*x1) - 0.1*math.cos(6.0*3.1415*x2)
    return obj

# Start location
x_start = [0.8, -0.5]

# Design variables at mesh points
'''
i1 = np.arange(-1.0, 1.0, 0.01)
i2 = np.arange(-1.0, 1.0, 0.01)
x1m, x2m = np.meshgrid(i1, i2)
fm = np.zeros(x1m.shape)
for i in range(x1m.shape[0]):
    for j in range(x1m.shape[1]):
        fm[i][j] = 0.2 + x1m[i][j]**2 + x2m[i][j]**2 \
             - 0.1*math.cos(6.0*3.1415*x1m[i][j]) \
             - 0.1*math.cos(6.0*3.1415*x2m[i][j])
'''

# Design variables at mesh points
ns = 100
xs = np.arange(-1.0, 1.0, 1/ns)
ys = np.arange(-1.0, 1.0, 1/ns)
XG,YG = np.meshgrid(xs, ys)
ZG =  0.2 + xs**2 + ys**2\
       - 0.1*np.cos(6.0*np.pi*XG)\
       - 0.1*np.cos(6.0*np.pi*YG)
fm = ZG;

##################################################
# Simulated Annealing
##################################################
# Number of cycles
n = 50
# Number of trials per cycle
m = 50
# Number of accepted solutions
na = 0.0
# Probability of accepting worse solution at the start
p1 = 0.7
# Probability of accepting worse solution at the end
p50 = 0.001
# Initial temperature
t1 = -1.0/math.log(p1)
# Final temperature
t50 = -1.0/math.log(p50)
# Fractional reduction every cycle
frac = (t50/t1)**(1.0/(n-1.0))
# Initialize x
x = np.zeros((n+1,2))
x[0] = x_start
xi = np.zeros(2)
xi = x_start
na = na + 1.0
# Current best results so far
xc = np.zeros(2)
xc = x[0]
fc = f(xi)
fs = np.zeros(n+1)
fs[0] = fc
# Current temperature
t = t1
# DeltaE Average
DeltaE_avg = 0.0
np.random.seed(0)
for i in range(n):
    print('Cycle: ' + str(i) + ' with Temperature: ' + str(t))
    for j in range(m):
        # Generate new trial points
        xi[0] = xc[0] + random.random() - 0.5
        xi[1] = xc[1] + random.random() - 0.5
        # Clip to upper and lower bounds
        xi[0] = max(min(xi[0],1.0),-1.0)
        xi[1] = max(min(xi[1],1.0),-1.0)
        DeltaE = abs(f(xi)-fc)
        if (f(xi)>fc):
            # Initialize DeltaE_avg if a worse solution was found
            #   on the first iteration
            if (i==0 and j==0): DeltaE_avg = DeltaE
            # objective function is worse
            # generate probability of acceptance
            p = math.exp(-DeltaE/(DeltaE_avg * t))
            # determine whether to accept worse point
            if (random.random()<p):
                # accept the worse solution
                accept = True
            else:
                # don't accept the worse solution
                accept = False
        else:
            # objective function is lower, automatically accept
            accept = True
        if (accept==True):
            # update currently accepted solution
            xc[0] = xi[0]
            xc[1] = xi[1]
            fc = f(xc)
            # increment number of accepted solutions
            na = na + 1.0
            # update DeltaE_avg
            DeltaE_avg = (DeltaE_avg * (na-1.0) +  DeltaE) / na
    # Record the best x values at the end of every cycle
    x[i+1][0] = xc[0]
    x[i+1][1] = xc[1]
    fs[i+1] = fc
    # Lower the temperature for next cycle
    t = frac * t

# print solution
print('Best solution: ' + str(xc))
print('Best objective: ' + str(fc))

# Create a contour plot
plt.figure()
#CS = plt.contour(x1m, x2m, fm)
CS = plt.contour(XG, YG, fm)
# Label contours
plt.clabel(CS, inline=1, fontsize=10)
# Add some text to the plot
plt.title('Non-Convex Function')
plt.xlabel('x1')
plt.ylabel('x2')

plt.plot(x_start[0],x_start[1],'r-o', markersize=10) # initial point
plt.plot(x[:,0],x[:,1],'y-o')
pml.savefig('sim_anneal_contour.png')

fig = plt.figure()
ax1 = fig.add_subplot(211)
ax1.plot(fs,'r.-')
ax1.legend(['Objective'])
ax2 = fig.add_subplot(212)
ax2.plot(x[:,0],'b.-')
ax2.plot(x[:,1],'g--')
ax2.legend(['x1','x2'])
pml.savefig('sim_anneal_iterations.png')

plt.show()


# kurtosis python
# https://www.google.com/search?q=kurtosis+python&oq=Kurtosis+python&aqs=chrome.0.0l4j0i22i30l6.1736j0j7&sourceid=chrome&ie=UTF-8
# https://en.wikipedia.org/wiki/Kurtosis

''' Statistical functions:
Kurtosis is the fourth central moment
divided by the square of the variance.
If Fisher’s definition is used, then 3.0
is subtracted from the result to give 0.0
for a normal distribution.

If bias is False then the kurtosis is
calculated using k statistics to eliminate
bias coming from biased moment estimators.
'''
# -----------------------------------
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.kurtosis.html
# scipy part -----------------------------------

# from scipy.stats import norm, kurtosis
from scipy import stats
import matplotlib.pyplot as plt
import matplotlib.pylab as p
import numpy as np
import pandas as pd

'''
# # Discrete vs continuous distribution dataset
# data = stats.norm.rvs(size=1000, random_state=3)
# # reference point
# print(stats.kurtosis(data))
# # print(kurtosis(data, fisher=False))

x = np.linspace(-5, 5, 100)
ax = plt.subplot()
distnames = ['laplace', 'norm', 'uniform', 'cauchy', 'maxwell', 'logistic']
for distname in distnames:
    if distname == 'uniform':
        dist = getattr(stats, distname)(loc=-2, scale=4)
    else:
        dist = getattr(stats, distname)
    data = dist.rvs(size=1000)
    kur = stats.kurtosis(data, fisher=True)
    y = dist.pdf(x)
    ax.plot(x, y, label=f'{distname}, {round(kur, 3)}')
    ax.legend()
plt.show()
'''

# geeksforgeeks part -----------------------------------
x1 = np.linspace(-5, 5, 10000)
y1 = 1./(np.sqrt(2.*np.pi)) * np.exp(-.5*(x1)**2)  # normal distribution
# p.plot(x1, y1, '*')
# print(stats.kurtosis(y1))
# print(stats.kurtosis(y1, fisher=False))
# p.show()

# stackoverflow part -----------------------------------
x_ = np.random.normal(0, 2, 10000)
f, (ax1, ax2) = plt.subplots(1, 2)

# print(f'excess kurtosis of random norm dist(should be 0):\
# {stats.kurtosis(x_)}')
# print(f'skewness of random norm dist (should be 0): {stats.skew(x_)}')

# print(f'excess kurtosis of norm dist (should be 0): {stats.kurtosis(x1)}')
# print(f'skewness of norm dist (should be 0): {stats.skew(x1)}')

# ax1.hist(x_, bins='auto')
# ax1.set_title('probability density (random)')
# ax2.hist(y1, bins='auto')
# ax2.set_title('your dataset')

# plt.tight_layout()
# plt.show()

# pandas part -----------------------------------
# df = pd.read_csv('Data/nba.csv')
# df = pd.read_csv('Data/XAUNZD_Daily.csv')
# print(df.tail())

# # skewness along the index axis
# print(df.kurt(axis=0, skipna=True))

# # skewness of the data over the column axis
# print(df.kurt(axis=1, skipna=True))

# BONUS part -----------------------------------
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.kstat.html
# https://mathworld.wolfram.com/Cumulant.html
# https://mathworld.wolfram.com/k-Statistic.html
'''
sample size increases, n-th moment and n-th k-statistic converge
In the case of the normal distribution, they converge to zero
'''
rndm = np.random.RandomState(1234)
# print(rndm)
for n in [2, 3, 4, 5, 6, 7]:
    # sample size increases
    x = rndm.normal(size=10**n)
    m, k = stats.moment(x, 3), stats.kstat(x, 3)
    print(f'{m}, {k}, {m-k}')


#!/usr/bin/python
# -*- coding: utf-8 -*-
# Pierre Haessig — July 2016
""" Matrix helper functions
"""

from __future__ import division, print_function, unicode_literals
import numpy as np

def block_toeplitz(c, r=None):
    '''
    Construct a block Toeplitz matrix, with blocks having the same shape

    Signature is compatible with ``scipy.linalg.toeplitz``

    Parameters
    ----------
    c : list of 2d arrays
        First column of the matrix.
        Each item of the list should have same shape (mb,nb)
    r : list of 2d arrays
        First row of the matrix. If None, ``r = conjugate(c)`` is assumed;
        in this case, if c[0] is real, the result is a Hermitian matrix.
        r[0] is ignored; the first row of the returned matrix is
        made of blocks ``[c[0], r[1:]]``.

    c and r can also be lists of scalars; if so they will be broadcasted
    to the fill the blocks

    Returns
    -------
    A : (len(c)*mb, len(r)*nb) ndarray
        The block Toeplitz matrix.

    Examples
    --------
    Compatible with ``scipy.linalg.toeplitz`` (but less optimized):
    >>> block_toeplitz([1,2,3], [1,4,5,6])
    array([[1, 4, 5, 6],
           [2, 1, 4, 5],
           [3, 2, 1, 4]])

    Regular usage:
    >>> I2 = np.eye(2)
    >>> block_toeplitz([1*I2,2*I2,3*I2], [1*I2,4*I2,5*I2,6*I2])
    array([[1, 0, 4, 0, 5, 0, 6, 0],
           [0, 1, 0, 4, 0, 5, 0, 6],
           [2, 0, 1, 0, 4, 0, 5, 0],
           [0, 2, 0, 1, 0, 4, 0, 5],
           [3, 0, 2, 0, 1, 0, 4, 0],
           [0, 3, 0, 2, 0, 1, 0, 4]])

    Usage with broadcasting of scalar blocks:
    >>> block_toeplitz([1*I2,2*I2,3*I2], [1,4,5,6])
    array([[1, 0, 4, 4, 5, 5, 6, 6],
           [0, 1, 4, 4, 5, 5, 6, 6],
           [2, 0, 1, 0, 4, 4, 5, 5],
           [0, 2, 0, 1, 4, 4, 5, 5],
           [3, 0, 2, 0, 1, 0, 4, 4],
           [0, 3, 0, 2, 0, 1, 4, 4]])
    '''
    c = [np.atleast_2d(ci) for ci in c]
    if r is None:
        r = [np.conj(ci) for ci in c]
    else:
        r = [np.atleast_2d(rj) for rj in r]

    mb,nb = c[0].shape
    dtype = (c[0]+r[0]).dtype
    m = len(c)
    n = len(r)

    A = np.zeros((m*mb, n*nb), dtype=dtype)

    for i in range(m):
        for j in range(n):
            # 1. select the Aij block from c or r:
            d = i-j
            if d>=0:
                Aij = c[d]
            else:
                Aij = r[-d]
            # 2. paste the block
            A[i*mb:(i+1)*mb, j*mb:(j+1)*mb] = Aij

    return A


import torch
import dgl
import numpy as np
import networkx as nx
import scipy.sparse as sp
import matplotlib.pyplot as plt

INF = 20211120 #一个较大的数，graph_mat[i][j]=INF表示i不可以到达j

class FloydWarshall:
    def __init__(self, graph_mat) -> None:
        self.graph_mat = graph_mat # 图的邻接矩阵

    def shortest_path(self):
        m, n = len(self.graph_mat), len(self.graph_mat[0])
        distance_mat = self.graph_mat.copy()
        path = [[None] * n for _ in range(m)] # 记录路径
        for k in range(m):
            for i in range(m):
                for j in range(n):
                    if distance_mat[i][k]+distance_mat[k][j] < distance_mat[i][j]:
                        distance_mat[i][j] = distance_mat[i][k]+distance_mat[k][j]
                        path[i][j] = k
        return distance_mat, path
# 递归
# def get_nodes_edges_recursion(paths, i, j):
#         if i == j:
#             return [i], []
#         else:
#             if paths[i][j] == None:
#                 return [j], [(i, j)]
#             else:
#                 left_nodes, left_edges = get_nodes_edges(paths, i, paths[i][j])
#                 right_nodes, right_edges = get_nodes_edges(paths, paths[i][j], j)
#         return left_nodes+right_nodes, left_edges+right_edges

def get_nodes_edges(paths, i, j):
    pass_nodes = [i, j]
    pass_edges = []
    while paths[i][j] != None:
        j = paths[i][j]
        pass_nodes.insert(1, j)
        pass_edges.append((j, pass_nodes[2]))
    pass_edges.append((i, j))
    return pass_nodes, pass_edges[::-1]

def draw(sp_mat, color_nodes, color_edges):
    '''画一张图，并给指定的节点和边着色
    Parameters
    ----------
    sp_mat : scipy.sparse.spmatrix
    color_nodes : list, e.g. [0, 1, 2, ...]
        指定着色的节点
    color_edges : list, e.g. [(0, 1), (2, 5), ...]
        指定着色的边

    Returns
    -------
    None
    '''

    G = dgl.from_scipy(sp_mat, eweight_name='w') # 注意：dgl.from_scipy()默认为有向多重图
    # print(type(G))
    nx_multi_G = dgl.to_networkx(G, edge_attrs='w').to_undirected() # 变为无向简单图
    nx_G = nx.Graph(nx_multi_G)
    # print(nx_G.edges(data=True))
    edge_labels = nx.get_edge_attributes(nx_G, 'w')
    # tensor.item() tensor(0.)->0(tensor->int)
    edge_labels = { (key[0],key[1]): edge_labels[key].item() for key in edge_labels }
    edges = list(nx_G.edges)
    num_nodes = nx_G.number_of_nodes()
    num_edges = nx_G.number_of_edges()
    node_color = ['b'] * num_nodes
    edge_color = ['b'] * num_edges

    for i in color_nodes:
        node_color[i] = 'r'

    for i in range(num_edges):
        u, v = edges[i][0], edges[i][1]
        if (u, v) in set(color_edges) or (v, u) in set(color_edges):
            edge_color[i] = 'r'
    pos = nx.circular_layout(nx_G)
    plt.title('Undigraph: Shortest Path')
    nx.draw(nx_G, pos, with_labels=True, node_color=node_color, edge_color=edge_color)
    nx.draw_networkx_edge_labels(nx_G, pos, edge_labels=edge_labels)
    plt.savefig('floyd_warshall.png', format='PNG')
    plt.show()

def main():
    row = np.array([0, 0, 1, 1, 2, 2, 3, 3, 4])
    col = np.array([1, 2, 2, 3, 4, 3, 4, 5, 5])
    data = np.array([1, 12, 9, 3, 5, 4, 13, 15, 4])

    count_node = 6
    sp_mat = sp.coo_matrix((data, (row, col)), shape=(count_node, count_node))
    graph_mat = sp_mat.toarray()
    # 无向图
    for i in range(len(graph_mat)):
        for j in range(i):
            if graph_mat[i][j] == 0 and graph_mat[j][i] == 0:
                graph_mat[i][j] = graph_mat[j][i] = INF
            else:
                graph_mat[i][j] += graph_mat[j][i]
                graph_mat[j][i] = graph_mat[i][j]

    distance_mat, paths = FloydWarshall(graph_mat).shortest_path()
    print(distance_mat)
    print(paths)

    for i in range(count_node):
        for j in range(i+1, count_node):
            print('{}->{}: {} | {}'.format(i, j, get_nodes_edges(paths, i, j), distance_mat[i][j]))

    start_node, end_node = 0, 5 # 开始节点，结束节点
    pass_nodes, pass_edges = get_nodes_edges(paths, start_node, end_node)
    '''使用get_nodes_edges_recursion注意添加开始节点
    '''
    # pass_nodes, pass_edges = get_nodes_edges_recursion(paths, start_node, end_node)
    # pass_nodes.insert(0, start_node)
    draw(sp_mat, pass_nodes, pass_edges)
if __name__ == '__main__':
    main()

import numpy,import scipy,import networkx


"""
Provides convenient, but non-vital utilities.
"""

import numpy as np

def N_to_C(N):
    """
    Computes the number of connections for a network with N regions.

    Arguments
    ---------
    N : 2 <= int
        Number of regions.

    Returns
    -------
    C : 1 <= int
        Number of connections.
    """
    return N * (N - 1) / 2

def C_to_N(C):
    """
    Computes the number of regions for a network with C connections.

    Arguments
    ---------
    C : 1 <= int
        Number of connections.

    Returns
    -------
    N : 1 <= int
        Number of regions.
    """
    N = (np.sqrt(8 * C + 1) - 1) / 2 + 1
    return N

def nm_to_c(n, m):
    """
    Converts a pair of region indices (n, m) to a connection index.

    Arguments
    ---------
    n : 0 <= int
        First region index.
    m : n < int
        Second region index.

    Returns
    -------
    c : 0 <= int < N * (N - 1) / 2
        Connection index.

    Notes
    -----
    N is the total number of regions.
    """
    return N_to_C(n) + m

def c_to_nm(c):
    """
    Converts a connection index to a pair of region indices (n, m).

    Arguments
    ---------
    c : 0 <= int < N * (N - 1) / 2
        Connection index.

    Returns
    -------
    n : 0 <= int
        First region index.
    m : n < int
        Second region index.

    Notes
    -----
    N is the total number of regions.
    """
    n = np.floor((np.sqrt(8 * c + 1) - 1) / 2) + 1
    m = c - N_to_C(n)
    return (n, m)


from time import perf_counter
import numpy as np
from matplotlib import pyplot as plt

#returns the minimum of a unidimensional unimodal function using Newton Rapson method
def Newton_Rapson(f,fp, fpp, a, e = 0.1, plot=False):
    t = perf_counter()
    A = []
    k = a
    while abs(fp(k)) > e :
        k = k - fp(k)/fpp(k)
        if plot :
            A.append(k)
    t = perf_counter() - t
    print("time execution: ", t)
    if plot :
        X = np.linspace(a,2*k-a,1000)
        plt.plot(X,f(X))
        plt.plot(k,f(k),"s",label="optimum")
        for u in A:
            plt.plot(u,f(u),"o")
        plt.legend()
        plt.show()
    return k

#returns the minimum of a unidimensional unimodal function using Quasi Newton method
def Quasi_Newton(f, a, s = 0.01, e = 0.1, plot=False):
    t = perf_counter()
    A = []
    k = a
    dev = abs((f(k + s) - f(k - s))/(2 * s))
    while  dev > e:
        if plot :
            A.append(k)
        x = f(k + s)
        y = f(k - s)
        z = f(k)
        k = k - ((s * (x - y))/(2 * (x - 2 * z + y)))
        dev = abs((f(k + s) - f(k - s))/(2 * s))
    t = perf_counter() - t
    print("time execution: ", t)
    if plot :
        X = np.linspace(a,2*k-a,1000)
        plt.plot(X,f(X))
        for u in A:
            plt.plot(u,f(u),"o")
        plt.plot(k,f(k),"s",label="optimum")
        plt.legend()
        plt.show()
    return k

#returns the minimum of a unidimensional function usingthe Secant method
def Secant_Method(f, fp, lamda, s = 0.01, e = 0.01, plot=False):
    A = []
    t = perf_counter()
    a = lamda
    b = a + s
    while fp(s) < 0:
        a = s
        s *= 2
    b = s
    k = a - ((fp(a) * (b - a)) / (fp(b) - fp(a)))
    while abs(fp(k)) > e:
        if plot :
            A.append(k)
        if fp(k) >= 0 :
            b = k
        else : a = k
        k = a - ((fp(a) * (b - a)) / (fp(b) - fp(a)))
    t = perf_counter() - t
    print("time execution: ", t)
    if plot :
        X = np.linspace(lamda,2*k-lamda,1000)
        plt.plot(X,f(X))
        for u in A:
            plt.plot(u,f(u),"o")
        plt.plot(k,f(k),"s",label="optimum")
        plt.legend()
        plt.show()
    return k


'''
Aims:
To perform a random search for a given problem, to compute a quality measure for the
solution candidates, to make some statistical analysis to a sample set of possible solutions.
Task:
Develop an application in python that has the following functionalities:
1. Randomly creates possible solutions for your assigned problem (Random Candidate
Solution Generator) (25p).
2. Check if a candidate to the solution is indeed a viable solution (25p).
3. Assigns a measure of quality (a positive value) to a candidate solution - where zero
marks a correct one and, as the candidate is more and more farther from the correct
solution the number grows (Fitness Function) (25p).
4. For a sample set (of size n) of random generated solutions, the mean and the standard
deviation of their quality measures is computed (25p)

11. Caravan
A caravan of n camels is traveling through desert in a single line. To break the monotony
of the long traveling days, each other day the camels are settled so it doesn't see the
same camel in front of her before. Generate possibilities to arrange the camels, knowing
how were place in the first day

Solution:
    - initially, suppose the camles were arranged
        0, 1, 2, .. n - 1 (so it's easier)

    - A candidate solution is any possible permutation of
        - the camels (no need for check if a solution is valid)

    - A fitness function can be:
        - nr of camels - nr of camels that see another camel in from - 1
'''

import numpy
import random
import matplotlib.pyplot as pyplot

def generate_camels(n):
    x = range(n)
    random.shuffle(x)
    return x

def fitness_function(p):
    cnt = len(p) - 1
    for i in range(len(p) - 1):
        if p[i] + 1 != p[i + 1]:
            cnt -= 1
    return cnt

def generate_samples(n, m):
    samples = [generate_camels(n) for _ in range(m)]
    fitness = [fitness_function(x) for x in samples]
    print("\n".join([str(samples[i]) + " fitness = " + str(fitness[i]) for i in range(len(samples))]))
    pyplot.hist(fitness)
    pyplot.draw()
    print("mean: %.6f" % numpy.mean(fitness))
    print("std dev: %.6f" % numpy.std(fitness))
    pyplot.show()

def main():
    n = int(input("number of camles: = "))
    m = int(input("sample size: = "))
    generate_samples(n, m)

main()


"""

https://lpsa.swarthmore.edu/Root_Locus/RootLocusWhy.html

"""

import numpy as np
import matplotlib.pyplot as plt
from sympy.abc import *
from sympy.integrals import inverse_laplace_transform
from sympy.solvers import solve
from sympy import *

def eval_transfer_function(G, R, K0, t0, t1):
    G = G.subs(K, K0)
    R = R.subs(K, K0)
    C = inverse_laplace_transform(G*R, s, t)
    x = np.linspace(t0, t1, 100)
    y = np.array([C.subs(t, ts).evalf().subs(0, 1) for ts in x])
    y = np.array([re(ys) for ys in y])
    return (x, y)

"""

Assume transfer function is
G(s) = 1/(s*(s+3))

In a closed-loop feedback system with gain K
we have the following expression
H(s) = C(s)/R(s) = K*G(s) / (1 + K*G(s))

where C(s) is the output and R(s) is the input

Evaluating H(s) gives us
H(s) = K/(s**2 + 3*s + K)

We can control the gain K so we can vary it and look at the output

The input we will feed in this case is a step response (laplace transform is 1/s)

Since it is time consuming to try various K to see what works best, we can find K
another way

Solve for the roots of the poles
For the function

s**2 + 3*s + K gives us the characteristic roots
s = -3/2 +- sqrt(9 - 4K)/2
9 > 4K is overdamped (real roots)
9 < 4K is underdamped (complex roots)
9 = 4K is critically damped
Say we want critical damping, we solve for K to be 4.5

"""
D = s**2 + 3*s + K
G1 = K/D
R1 = 1/s
x1, y1 = eval_transfer_function(G1, R1, 1, 0, 10)
x2, y2 = eval_transfer_function(G1, R1, 10, 0, 10)
x3, y3 = eval_transfer_function(G1, R1, 100, 0, 10)
x4, y4 = eval_transfer_function(G1, R1, 4.5, 0, 10)

print("Roots", solve(D, s))

plt.xlabel('Time')
plt.ylabel('Amplitude')
plt.figure(1)
plt.plot(x1, y1, label='K=1')
plt.plot(x2, y2, label='K=10')
plt.plot(x3, y3, label='K=100')
plt.plot(x4, y4, label='K=4.5')
plt.legend(loc='best')
plt.savefig('simple-gain.png')
plt.show()


#!/usr/bin/env python

import math
import numpy as np

def unitized(r):
    v = np.copy(r)
    nrm = np.linalg.norm(v)
    if math.isclose(nrm, 0.0, rel_tol=1e-14):
        return v
    else:
        return v/np.linalg.norm(v)

def unitize(r):
    r /= np.linalg.norm(r)
    return r

def is_unitized(r):
    return math.isclose(np.linalg.norm(r), 1.0, rel_tol=1e-12)

def perm_tensor():
    '''
    This function returns the permutation tensor.
    '''
    epsilon = np.zeros((3,3,3))
    epsilon[1,2,0] =  1.0
    epsilon[2,1,0] = -1.0
    epsilon[0,2,1] = -1.0
    epsilon[2,0,1] =  1.0
    epsilon[0,1,2] =  1.0
    epsilon[1,0,2] = -1.0
    return epsilon

def cross_mat(r):
    mat = np.zeros((3,3))
    mat[0,1] = -r[2]
    mat[0,2] = r[1]
    mat[1,0] = r[2]
    mat[1,2] = -r[0]
    mat[2,0] = -r[1]
    mat[2,1] = r[0]
    return mat

def decomp_LTL(A, overwrite=False, zero_upper=True, out=None):
    m, n = A.shape
    assert m == n
    if overwrite:
        L = A
    else:
        if out is not None:
            assert n,n == out.shape
            out[:,:] = 0.0
            L = out
        else:
            L = np.zeros((n,n))

    for i in range(n-1,-1,-1):
        L[i,i] = math.sqrt(A[i,i] - np.dot(L[i+1:,i],L[i+1:,i]))
        for j in range(i):
            L[i,j] = (A[i,j] - np.dot(L[i+1:,i], L[i+1:,j]))/L[i,i]

    if overwrite and zero_upper:
        for i in range(n):
            for j in range(i+1,n):
                L[i,j] = 0.0
    return L


# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

def f(x):
    return np.power(x, 2)

def d_f_1(x):
    return 2.0*x

def d_f_2(f, x, delta=1e-4):
    return (f(x+delta) - f(x-delta)) / (2*delta)

def gradient_descent():
    """梯度下降求最小值
    """

    data = np.arange(-10, 11)
    plt.plot(data, f(data))
    plt.show()

    learning_rate = 0.1
    epochs = 30

    x_init = 10.0
    x = x_init
    for i in range(epochs):
        d_f_x = d_f_1(x)
        # d_f_x = d_f_2(f, x)
        x = x - learning_rate * d_f_x
        print(x)

    print('initial x =', x_init)
    print('arg min f(x) of x =', x)
    print('f(x) =', f(x))

if __name__ == '__main__':
    gradient_descent()


import numpy as np
import numpy.linalg as la
import numpy.polynomial.polynomial as poly
import matplotlib.pyplot as plt

# read data
data = np.loadtxt('data/whData.dat', dtype=np.object)

# Reading and storing columns 0 and 1 and finally converted to numpy.float array
data = data[:, 0:2].astype(np.float)

# Removing outliers
data = data[data[:, 0] > 0]

# create weight vector for train data
wgt = np.copy(data[:, 0])

# create height vector for train data
hgt = np.copy(data[:, 1])

xmin = hgt.min() - 15
xmax = hgt.max() + 15
ymin = wgt.min() - 15
ymax = wgt.max() + 15

def plot_data_and_fit(h, w, x, y):
    plt.plot(h, w, 'ko', x, y, 'r-')
    plt.xlim(xmin, xmax)
    plt.ylim(ymin, ymax)
    plt.show()

def trsf(x):
    return x / 100.

n = 10
x = np.linspace(xmin, xmax, 100)

# method1:
# regression using ployfit
c = poly.polyfit(hgt, wgt, n)
y = poly.polyval(x, c)
plot_data_and_fit(hgt, wgt, x, y)

# method2:
# regression using the Vandermonde matrix and pinv
X = poly.polyvander(hgt, n)
c = np.dot(la.pinv(X), wgt)
y = np.dot(poly.polyvander(x, n), c)
plot_data_and_fit(hgt, wgt, x, y)

# method3:
# regression using the Vandermonde matrix and lstsq
X = poly.polyvander(hgt, n)
c = la.lstsq(X, wgt)[0]
y = np.dot(poly.polyvander(x, n), c)
plot_data_and_fit(hgt, wgt, x, y)

# method4:
# regression on transformed data using the Vandermonde
# matrix and either pinv or lstsq
X = poly.polyvander(trsf(hgt), n)
c = np.dot(la.pinv(X), wgt)
y = np.dot(poly.polyvander(trsf(x), n), c)
plot_data_and_fit(hgt, wgt, x, y)


import numpy as np

class Loss(object):

    def loss(self, y, h_x):
        return 0

    def loss_gradient(self, y, h_x):
        return 0

class LMS(Loss):

    def loss(self, y, h_x):
        return (y - h_x) ** 2

    def loss_gradient(self, y, h_x):
        return h_x - y

class SVM(Loss):

    def loss(self, y, h_x):
        return max(0, 1 - y * h_x)

    def loss_gradient(self, y, h_x):
        if abs(h_x) < 1:
            return -y
        return 0


import matplotlib.pyplot as plt
import numpy as np

N = 8
plt.figure(1, figsize=(9.5, 6))
for k in range(N):
	s = np.exp(-1j*2*np.pi*k/N*np.arange(N))
	plt.subplot(N/2, 2, k+1)
	plt.plot(np.real(s), 'b', lw=1.5)
	plt.axis([0,N-1,-1.5,1.5])
	plt.title(r"$s^{*}_{%s}$"%(k), fontsize=18)
	plt.subplot(N/2, 2, k+1)
	plt.plot(np.imag(s), 'g', lw=1.5)
	plt.axis([0,N-1,-1.5,1.5])

plt.tight_layout()
plt.savefig('complex-sinewaves.png')
plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jul  8 15:33:55 2021

@author: alessandro
"""

import numpy as np
import numpy.linalg as npl

def matrice(a): return np.array([1, -1, 2, -1, 6, 1, 2, 1, a]).reshape((3, 3))

AS = [matrice(a) for a in range(6, 11)]

# a)
normeinf = [npl.norm(A, np.inf) for A in AS]
print(normeinf)

# b)
massimo = max(normeinf)
indice = 6 + normeinf.index(massimo)
print(f"Norma massima = {massimo} in indice {indice}")

# c)
normeuno = [npl.norm(A, 1) for A in AS]
print(normeuno)
minimo = min(normeuno)
indice = 6 + normeuno.index(minimo)
print(f"Norma minima = {minimo} in indice {indice}")

# ---------------