'''
Code given from professor Michael Plexousakis, instructor of the course.
http://users.tem.uoc.gr/~plex/mem253-Fall2017/
'''
import numpy as np
import gaussQuad as gq
import matplotlib.pyplot as plt

# Finite element method to solve the two-point boundary value problem
#
#    -u'' + q u = f,  in [l, r]
#
# with zero Dirichlet boundary condtions.

# The functions q and f

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

# Endpoints and partition of [l,r]

l = 0.0;  r = 1.0;  N = 99;  h = (r - l) / (N+1);  x = np.linspace(l, r, N+2)

# The system matrix is tridiagonal symmetric positive definite. Compute the
# main diagonal and the first diagonal above. Use a Gauss quadrature rule
# exact for polynomials of degree <= 4

nq, p, w = gq.gaussQuad(4)

d = np.zeros(N)
c = np.zeros(N)

for j in range(N+1):
	xj = x[j];  s1 = 0.0;  s2 = 0.0;  s3 = 0.0
	for k in range(nq):
		qk = q(xj + h*p[k])
		s1 += w[k] * qk * (1 - p[k])**2
		s2 += w[k] * qk * (1 - p[k])*p[k]
		s3 += w[k] * qk *      p[k] *p[k]

	if j > 0:           d[j-1] += h * s1
	if j > 0 and j < N: c[j-1] += h * s2
	if j < N:           d[j  ] += h * s3

for j in range(N):
	d[j] += 2 / h
	c[j] -= 1 / h

# Use a Gauss quadrature rule exact for polynomials of degree <= 5

b = np.zeros(N)
nq, p, w = gq.gaussQuad(5)

for j in range(N+1):
	xj = x[j];  s1 = 0.0;  s2 = 0.0
	for k in range(nq):
		fk = f(xj + h*p[k])
		s1 += w[k] * fk * (1 - p[k])
		s2 += w[k] * fk *      p[k]

	if j > 0: b[j-1] += h * s1
	if j < N: b[j  ] += h * s2

# Solve the system

for k in range(1,N):
	t = c[k-1] / d[k-1]
	d[k] -= t * c[k-1]
	b[k] -= t * b[k-1]

b[N-1] = b[N-1] / d[N-1]
for k in range(N-2, -1, -1):
	b[k] = (b[k] - c[k] * b[k+1]) / d[k]

# Plot the solution

u = np.lib.pad(b, (1,1), 'constant', constant_values=(0, 0))
plt.plot(x, u, 'k-')
plt.legend(('u'), loc='upper right', shadow=True)
plt.show()

Finite Elements Method (given code)/fem.py


import numpy as np

def rsolve(A, b):
    """
    Solve system x*A = b for x. Can be used to efficiently calculate x=b*inv(A).

    Parameters
    ----------
    A: np.array, system matrix
    b: np.array, right hand side

    Returns
    -------
    solution of x*A = b
    """
    return np.linalg.solve(A.T, b.T).T

def normal_log_pdf(x, mu, covmat):
    """
    Log-likelihood of a multivariate normal distribution.

    Parameters
    ----------
    x: np.array, input vector
    mu: np.array, mean vector
    covmat: np.array, covariance matrix

    Returns
    -------
    likelihood value as scalar
    """

    data_misfit = 0.5*np.sum((x-mu)*np.linalg.solve(covmat, x-mu))
    norm_constant = np.log(np.linalg.det(2 * np.pi * covmat))

    return data_misfit + norm_constant


import numpy as np
import matplotlib.pyplot as plt

def newton_method(f, df, x0, eps, steps):
    x = x0
    for i in range(steps):
        x_next = x - f(x) / df(x)
        if abs(x_next - x) <= eps:
            return x_next
        x = x_next
    return x

def plot_newton_fractal(f, df, top_left=-2+2.0j, botton_right=2-2.0j, eps=1e-10, steps=100, rows=200, columns=200, cmap="jet"):

    xs = np.linspace(top_left.imag, botton_right.imag, rows)
    ys = np.linspace(top_left.real, botton_right.real, columns)

    fractal = np.array([[newton_method(f, df, x + y*1j, eps, steps) for x in xs] for y in ys])

    plt.subplot(2,2,1)
    plt.title("Real part")
    plt.ylabel("Im")
    plt.pcolormesh(xs, ys, fractal.real, cmap=cmap)
    plt.colorbar()

    plt.subplot(2,2,2)
    plt.title("Imaginary part")
    plt.pcolormesh(xs, ys, fractal.imag, cmap=cmap)
    plt.colorbar()

    plt.subplot(2,2,3)
    plt.title("Absolute value")
    plt.xlabel("Re")
    plt.ylabel("Im")
    plt.pcolormesh(xs, ys, np.abs(fractal), cmap=cmap)
    plt.colorbar()

    plt.subplot(2,2,4)
    plt.xlabel("Re")
    plt.title("Angle")
    plt.pcolormesh(xs, ys, np.angle(fractal), cmap=cmap)
    plt.colorbar()

    plt.tight_layout()

if __name__ == "__main__":
    plot_newton_fractal(lambda z: z**3 - 1, lambda z: 3*z**2)
    plt.show()


import numpy as np

# --------------------------------------------------------------
#                Rotation Functions
# --------------------------------------------------------------

def rotxM(theta):
    """Return x rotation matrix"""
    theta = theta * np.pi / 180
    M = [[1, 0, 0], [0, np.cos(theta), -np.sin(theta)], [0, np.sin(theta), np.cos(theta)]]
    return M

def rotyM(theta):
    """Return y rotation matrix"""
    theta = theta * np.pi / 180
    M = [[np.cos(theta), 0, np.sin(theta)], [0, 1, 0], [-np.sin(theta), 0, np.cos(theta)]]
    return M

def rotzM(theta):
    """Return z rotation matrix"""
    theta = theta * np.pi / 180
    M = [[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]]
    return M

def rotxV(x, theta):
    """Rotate a coordinate in the local x frame"""
    M = [[1, 0, 0], [0, np.cos(theta), -np.sin(theta)], [0, np.sin(theta), np.cos(theta)]]
    return np.dot(M, x)

def rotyV(x, theta):
    """Rotate a coordinate in the local y frame"""
    M = [[np.cos(theta), 0, np.sin(theta)], [0, 1, 0], [-np.sin(theta), 0, np.cos(theta)]]
    return np.dot(M, x)

def rotzV(x, theta):
    """Roate a coordinate in the local z frame"""
    M = [[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]]
    return np.dot(M, x)

def rotVbyW(V, W, theta):
    """Rotate a vector V, about an axis W by angle theta"""
    ux = W[0]
    uy = W[1]
    uz = W[2]

    c = np.cos(theta)
    s = np.sin(theta)
    if (
        np.array(theta).dtype == np.dtype("D")
        or np.array(W).dtype == np.dtype("D")
        or np.array(V).dtype == np.dtype("D")
    ):
        dtype = "D"
    else:
        dtype = "d"

    R = np.zeros((3, 3), dtype)

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

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

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

    return np.dot(R, V)

# --------------------------------------------------------------
#                Array Rotation and Flipping Functions
# --------------------------------------------------------------

def rotateCCW(inArray):
    """Rotate the inArray array 90 degrees CCW"""
    rows = inArray.shape[0]
    cols = inArray.shape[1]
    output = np.empty([cols, rows], inArray.dtype)

    for row in range(rows):
        for col in range(cols):
            output[cols - col - 1][row] = inArray[row][col]

    return output

def rotateCW(inArray):
    """Rotate the inArray array 90 degrees CW"""
    rows = inArray.shape[0]
    cols = inArray.shape[1]
    output = np.empty([cols, rows], inArray.dtype)

    for row in range(rows):
        for col in range(cols):
            output[col][rows - row - 1] = inArray[row][col]

    return output

def reverseRows(inArray):
    """Flip Rows (horizontally)"""
    rows = inArray.shape[0]
    cols = inArray.shape[1]
    output = np.empty([rows, cols], inArray.dtype)
    for row in range(rows):
        output[row] = inArray[row][::-1].copy()

    return output

def reverseCols(inArray):
    """Flip Cols (vertically)"""
    rows = inArray.shape[0]
    cols = inArray.shape[1]
    output = np.empty([rows, cols], inArray.dtype)
    for col in range(cols):
        output[:, col] = inArray[:, col][::-1].copy()

    return output

def orientArray(index, inArray):
    """Take an input array inArray, and rotate/flip according to the index
    output from quadOrientation (in orientation.py)"""

    if index == 0:
        outArray = inArray.copy()
    elif index == 1:
        outArray = rotateCCW(inArray)
        outArray = rotateCCW(outArray)
        outArray = reverseRows(outArray)
    elif index == 2:
        outArray = reverseRows(inArray)
    elif index == 3:
        outArray = rotateCCW(inArray)  # Verified working
        outArray = rotateCCW(outArray)
    elif index == 4:
        outArray = rotateCW(inArray)
        outArray = reverseRows(outArray)
    elif index == 5:
        outArray = rotateCCW(inArray)
    elif index == 6:
        outArray = rotateCW(inArray)
    elif index == 7:
        outArray = rotateCCW(inArray)
        outArray = reverseRows(outArray)

    return outArray


# Program to add two matrices using list comprehension

X = [[12, 7, 3],
     [4, 5, 6],
     [7, 8, 9]]

Y = [[5, 8, 1],
     [6, 7, 3],
     [4, 5, 9]]

result = [[X[i][j] + Y[i][j]  for j in range(len(X[0]))] for i in range(len(X))]

for r in result:
    print(r)

# Using NumPy
import numpy as np

A = np.array([[2, 4], [5, -6]])
B = np.array([[9, -3], [3, 6]])
C = A + B      # element wise addition
print(C)

# Program to transpose a matrix using list comprehension

X = [[12, 7],
     [4, 5],
     [3, 8]]

result = [[X[j][i] for j in range(len(X))] for i in range(len(X[0]))]

for r in result:
    print(r)

# Using NumPy
A = np.array([[1, 1], [2, 1], [3, -3]])
print(A.transpose())

# Program to multiply two matrices using list comprehension

# 3x3 matrix
X = [[12, 7, 3],
     [4, 5, 6],
     [7, 8, 9]]

# 3x4 matrix
Y = [[5, 8, 1, 2],
     [6, 7, 3, 0],
     [4, 5, 9, 1]]

# result is 3x4
result = [[sum(a*b for a,b in zip(X_row,Y_col)) for Y_col in zip(*Y)] for X_row in X]

for r in result:
    print(r)

# Using NumPy
A = np.array([[3, 6, 7], [5, -3, 0]])
B = np.array([[1, 1], [2, 1], [3, -3]])
C = A.dot(B)
print(C)


# -*- coding: utf-8 -*-
"""
Created on Fri Jan 19 10:05:43 2018

@author: philt
"""

import numpy as np
import matplotlib.pyplot as plt

# load the data
X = []
Y = []
for line in open('data_poly.csv'):
    x, y = line.split(',')
    x = float(x)

    X.append([1, x, x*x]) # add the bias term
    Y.append(float(y))

# let's turn X and Y into numpy arrays since that will be useful later
X = np.array(X)
Y = np.array(Y)

# let's plot the data to see what it looks like
plt.scatter(X[:,1], Y)
plt.show()

# apply the equations we learned to calculate a and b
# numpy has a special method for solving Ax = b
# so we don't use x = inv(A)*b
# note: the * operator does element-by-element multiplication in numpy
#       np.dot() does what we expect for matrix multiplication
w = np.linalg.solve(np.dot(X.T, X), np.dot(X.T, Y))
Yhat = np.dot(X, w)

# Plot the results
plt.scatter(X[:,1], Yhat)
plt.plot(sorted(X[:,1]), sorted(Yhat))
plt.show()

# determine how good the model is by computing the r-squared
d1 = Y - Yhat
d2 = Y - Y.mean()
r2 = 1 - d1.dot(d1) / d2.dot(d2)
print("the r-squared is:", r2)


import pandas as pd
import numpy as np
import math
from decimal import Decimal

def jaccard_similarity(dataFrame, row):
    intersection_cardinality = len(set.intersection(*[set(dataFrame), set(row)]))
    union_cardinality = len(set.union(*[set(dataFrame), set(row)]))
    return 1 - (intersection_cardinality/float(union_cardinality))

def nth_root(value, n_root):
    root_value = 1/float(n_root)
    return round(Decimal(value) ** Decimal(root_value), 3)

def minkowski_distance(x, y, p_value):
    return nth_root(sum(pow(abs(a-b), p_value) for a, b in zip(x, y)), p_value)

def manhattan_distance(dataFrame, row):
    return sum(abs(dataFrame - row) for dataFrame, row in zip(dataFrame, row))

def euclidian_distance(dataFrame, row):
    return np.linalg.norm(dataFrame.iloc[:, :-1].sub(row[:-1]), axis=1)

def cosine_similarity(dataFrame, row):
    return 1 - np.dot(dataFrame, row) / (np.sqrt(np.dot(dataFrame, dataFrame)) * np.sqrt(np.dot(row, row)))

def lowest_distance_rows(index, dataFrame,  euc_prediction, defined_row=None,  num_of_rows=1, multiple_lines=False):
    best_matching_species = []

    if not defined_row:
        defined_row = dataFrame.iloc[index].tolist()

    if euc_prediction == True:
        dataFrame['distance'] = euclidian_distance(dataFrame, defined_row)
    else:
        dataFrame['distance'] = dataFrame.iloc[:, :-1].apply(cosine_similarity, row=(defined_row[:-1]), axis=1)

    best_matching_species = dataFrame.sort_values(['distance']).iloc[0, -2]

    if multiple_lines:
        best_matching_species = dataFrame.sort_values(
            ['distance']).iloc[1:num_of_rows+1]

    dataFrame.drop('distance', axis=1, inplace=True)

    return best_matching_species

def randomize_data(dataFrame):
    dataFrame = dataFrame.sample(frac=1).reset_index(drop=True)
    return dataFrame[:100], dataFrame[100:].reset_index(drop=True)

def check_right_prediction(possible_predictions, answer):
    if answer in possible_predictions:
        return True
    return False

def count_predictions(test_data, train_data, num_of_rows, euclidian_prediction=True):
    right_predictions = 0
    for i in range(len(test_data.index)):
        row_of_test = test_data.iloc[i].tolist()
        if check_right_prediction(lowest_distance_rows(i, train_data, euclidian_prediction, row_of_test, num_of_rows), row_of_test[-1]):
            right_predictions += 1
    return right_predictions

# opening iris data
pd.options.mode.chained_assignment = None

dataFrame = pd.read_csv("iris.csv")
list_of_ranges = [1, 3, 5]

# mean, max and min of each column (a, b)
print(dataFrame.mean(), "\n")
print(dataFrame.iloc[:, :-1].max(), "\n")
print(dataFrame.iloc[:, :-1].min(), "\n")

# 10 rows with lower sum (c)
print(dataFrame.sum(axis=1, numeric_only=True).sort_values(
    ascending=True).head(10), '\n')

# closest line(s) of nth line (d, e ,f)
for show_range in list_of_ranges:
    print(lowest_distance_rows(11, dataFrame, False, None, show_range, True), '\n')

# 1st Task - Mix Data
dataFrame = dataFrame.sample(frac=1).reset_index(drop=True)

# 2nd Task - Separe Training and Test Data (2/1)
train_data, test_data = dataFrame[:100], dataFrame[100:].reset_index(drop=True)

# 3rd Task - Make Predictions Based on Euc. Distance & Cosine Similarity

num_of_right_predictions = []

for show_range in list_of_ranges:
    print('Actual Range:',show_range)
    # print("Euclidian Distance - Number of right predictions: ", count_predictions(test_data, train_data, show_range))
    print('Right Predictions - Euclidian Distance:', count_predictions(test_data, train_data, show_range) / 50 * 100,'%')
    train_data, test_data = randomize_data(dataFrame)
    # print("Cosine Similarity - Number of right predictions:",count_predictions(test_data, train_data, show_range, False))
    print('Right Predictions - Cosine Similarity:', count_predictions(test_data, train_data, show_range, False) / 50 * 100,'%')
    print()


import tensorflow as tf
import numpy as np
from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()
m,n=housing.data.shape                          # m : number of records, n : features
print("housing data : ({}, {})".format(m, n))

housing_data_plus_bias = np.c_[np.ones((m,1)), housing.data]
print housing_data_plus_bias.shape

n_epochs = 1000
learning_rate = 0.01

X = tf.constant(housing_data_plus_bias, dtype=tf.float32, name="X")

y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name="y")

theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name="theta")

y_pred = tf.matmul(X, theta, name="predictions")

error = y_pred - y

mse = tf.reduce_mean(tf.square(error), name="mse")

#
#opt 1
#
## partial derivatives fo the cost function
gradients = 2/m * tf.matmul(tf.transpose(X), error)   # default: manually  (work!)
training_op = tf.assign(theta, theta - learning_rate * gradients)

##
##opt 2
##
#gradients =  tf.gradients(mse, [theta])[0]  # audodiff: this did not work! ???
#training_op = tf.assign(theta, theta - learning_rate * gradients)

##
##opt 3
##
#optimizer = tf.train.GradientDescentOptimizer(learning_rate = learning_rate)
#training_op = optimizer.minimize(mse)

init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)

    for epoch in range(n_epochs):
        if epoch % 100 == 0:
            print("Epoch", epoch, "MSE = ", mse.eval())
        sess.run(training_op)

    best_theta = theta.eval()

print best_theta


import numpy as np
from . import wavelet

# Imported from I3D

def L0(x, mu):
    """ Proximal operator of the L0 norm
    """
    return x * (abs(x) > mu)

def L1(x, mu):
    """ Proximal operator of the L1 norm
    """
    return np.sign(x) * np.maximum(0, abs(x) - mu)

def L1_wavelet(x, mu):
    """ Compute the proximal operator evaluated at x of the function
        mu*||Wx||_1
        where W is the orthogonal wavelet transform and mu > 0
    """
    w = wavelet.transform(x)
    w = L1(w, mu)  # the prox.L1
    p = wavelet.inverse_transform(w)
    return p

def L2_sq(x, mu):
    """ Proximal operator of the L2 squared norm: 0.5*norm(.,2)**2 """
    return x / (1+mu)

def KL(x, mu, y):
    """ Proximal operator of the Kullback-Liebler divergence: KL(y,.)
            KL(y;x) = \sum_i y_i*ln(y_i/x_i) + x_i - y_i
    """
    return 0.5 * (x - mu + np.sqrt( (x-mu)**2 + 4*mu*y) )

def simplex(x, z=1):
    # Projection sur le simplexe https://gist.github.com/mblondel/6f3b7aaad90606b98f71
    dimension = x.shape
    v = x.flatten()
    u = np.sort(v)[::-1]
    cssv = np.cumsum(u) - z
    ind = np.arange(u.shape[0]) + 1
    cond = u - cssv / ind > 0
    rho = ind[cond][-1]
    theta = cssv[cond][-1] / float(rho)
    w = np.maximum(v - theta, 0)
    return w.reshape(dimension)

def L1_ball(x, s=1):
    # Projection sur la boule L1 https://gist.github.com/daien/1272551/edd95a6154106f8e28209a1c7964623ef8397246
    dimension = x.shape
    v = x.flatten()
    u = np.abs(v) # compute the vector of absolute values
    if u.sum() <= s: # check if v is already a solution
        return x
    w = simplex(u, s) # project *u* on the simplex
    w *= np.sign(v) # compute the solution to the original problem on v
    return w.reshape(dimension)

def L1_wavelet_ball(x, mu=1):
    """ Compute the projection of x onto the set
        ||Wx||_1 <= mu
        where W is the orthogonal wavelet transform and mu > 0
    """
    return wavelet.inverse_transform(L1_ball(wavelet.transform(x), mu))


import numpy as np
import matplotlib.pyplot as plt

n=20

def f_lin(x):
    return 8*x-7

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

def f_patr(x):
    return 5*x**2 - 9*x + 4

def regLiniara(X, Y):
    """

    :param X: Vectorul de valori x
    :param Y: Vectorul de valori random y
    :return: a si b astfel incat y = ax + b sa fie cat mai apropiata de a cuprinde penctele (xi, yi)
    """
    A = np.zeros((2, 2))
    w = np.zeros((2, 1))
    for i in range(n):
        A[0][0] += X[i]**2
        A[0][1] += X[i]
        A[1][0] += X[i]
        w[0][0] += X[i]*Y[i]
        w[1][0] += Y[i]
    A[1][1] = n
    sol = gaussPp(A, w, 10**-16)
    a = sol[0][0]
    b = sol[1][0]
    return a, b

def regPol(X, Y):
    """

    :param X: Vectorul de valori x
    :param Y: Vectorul de valori random y
    :return: a, b si c astfel incat y = ax^2 + bx + c sa fie cat mai apropiata de a cuprinde penctele (xi, yi)
    """
    A = np.zeros((3,3))
    w = np.zeros((3, 1))
    for i in range(n):
        A[0][0] += X[i]**4
        A[0][1] += X[i]**3
        A[0][2] += X[i]**2
        A[1][0] += X[i] ** 3
        A[1][1] += X[i] ** 2
        A[1][2] += X[i]
        A[2][0] += X[i] ** 2
        A[2][1] += X[i]
        w[0][0] += Y[i] * X[i]**2
        w[1][0] += Y[i] * X[i]
        w[2][0] += Y[i]
    A[2][2] = n
    sol = gaussPp(A, w, 10**-16)
    a = sol[0][0]
    b = sol[1][0]
    c = sol[2][0]
    return a, b, c

def regExp(X, Y):
    """

    :param X: Vectorul de valori x
    :param Y: Vectorul de valori random y
    :return: a si b astfel incat y = b*e^(ax) sa fie cat mai apropiata de a cuprinde penctele (xi, yi)
    """
    A = np.zeros((2,2))
    w = np.zeros((2,1))
    for i in range(n):
        A[0][0] += X[i]**2
        A[0][1] += X[i]
        A[1][0] += X[i]
        w[0][0] += np.log(Y[i])*X[i]
        w[1][0] += np.log(Y[i])
    A[1][1] = n
    sol = gaussPp(A, w, 10**-16)
    a = sol[0][0]
    b_prim = sol[1][0]
    b = np.exp(b_prim)
    return a, b

def metSubsDesc(A, b, tol):
    """
    param A: matrice patratica, superior triunghiulara, cu toate elem pe diag principala nenule
    param b: vectorul term liberi
    param tol: val numerica ft mica in rap cu care vom compara numerele apropiate de 0
    return: sol sistem
    """

    m, n = np.shape(A)
    if m != n:
        print("Matricea nu este patratica. Introduceti o alta matrice.")
        x = None
        return x

    # Verificam daca matricea este superior triunghiulara
    for i in range(m):
        for j in range(i):
            if abs(A[i][j]) > tol:
                print("Matricea nu este superior triunghiulara")
                x = None
                return x

    # Verificam daca elementele de pe diagonala principala sunt nule (sist comp det)
    for i in range(n):
        if A[i][i] == 0:
            print("Sistemul nu este compatibil determinat")
            x = None
            return x
    x = np.zeros((n, 1))
    x[n - 1] = 1 / A[n - 1][n - 1] * b[n - 1]
    k = n - 2
    while k >= 0:
        sum = 0
        for i in range(k + 1, n):
            sum += A[k][i] * x[i]
        x[k] = 1 / A[k][k] * (b[k] - sum)
        k = k - 1
    return x

def gaussPp(A, b, tol):
    """
    param A: matricea asoc sistemului, patratica
    param b: vectorul term liberi
    param tol: val cu care comparam nr nenule
    return x: solutia sistemului, numarul de interschimbari de linii si lista de pivoti
    """
    m, n = np.shape(A)
    if m != n:
        print("Matricea nu este patratica. Introduceti o alta matrice.")
        x = None
        return x

    A_extins = np.concatenate((A, b), axis=1)  # axis=0 l-ar pune pe b o noua linie, 1 il pune drept coloana
    for k in range(n - 1):
        max = A_extins[k][k]
        p = k
        for j in range(k + 1, n):
            if abs(A_extins[j][k]) > abs(max):
                max = A_extins[j][k]
                p = j
        if abs(max) <= tol:
            print("Sistemul nu admite solutie unica")
            x = None
            return x

        if (p != k):
            A_extins[[p, k]] = A_extins[[k, p]]  # swap linia p cu linia k
        for j in range(k + 1, n):
            A_extins[j] = A_extins[j] - (A_extins[j][k] / A_extins[k][k]) * A_extins[k]

    if abs(A_extins[n - 1][n - 1]) <= tol:
        print("Sistemul nu admite solutie unica")
        x = None
        return x

    x = metSubsDesc(A_extins[:, 0:n], A_extins[:, n], tol)
    return x

X = np.linspace(1, n, n)

Y = np.random.rand(n)

Y1 = f_lin(X) + Y

print("Valorile random obtinute: "+str(Y))
a, b = regLiniara(X, Y1)
p1 = a*X[0] + b
p2 = a*X[n-1] + b

plt.plot(X, Y1, 'o', color='red', markersize=10)
plt.plot([X[0], X[n-1]], [p1, p2], '-', color='blue')
plt.show()

Y2 = f_patr(X) + Y
a, b, c = regPol(X, Y2)

Y_aux = []
for i in range(n):
    Y_aux.append(a*X[i]**2+b*X[i]+c)
plt.plot(X, Y2, 'o', color='red', markersize=10)
plt.plot(X, Y_aux, '-', color='blue')
plt.show()

Y3 = f_exp(X) + Y
a, b = regExp(X, Y3)

Y_aux = []
for i in range(n):
    Y_aux.append(b*np.exp(a*X[i]))

plt.plot(X, Y3, 'o', color='red', markersize=10)
plt.plot(X, Y_aux, '-', color='blue')
plt.show()


# coding: utf-8

# # Function dftmatrix
#
# ## Synopse
#
# Kernel matrix for the 1-D Discrete Fourier Transform DFT.
#
# - **A = dftmatrix(N)**
#
#   - **A**: Output image, square N x N, complex
#
#
#   - **N**: Integer, number of points of the DFT

# In[1]:

import numpy as np

def dftmatrix(N):
    x = np.arange(N).reshape(N,1)
    u = x
    Wn = np.exp(-1j*2*np.pi/N)
    A = (1./np.sqrt(N)) * (Wn ** u.dot(x.T))
    return A

# ## Examples

# In[1]:

testing = (__name__ == "__main__")

if testing:
    get_ipython().system(' jupyter nbconvert --to python dftmatrix.ipynb')
    import numpy as np
    import sys,os
    import matplotlib.image as mpimg
    ea979path = os.path.abspath('../../')
    if ea979path not in sys.path:
        sys.path.append(ea979path)
    import ea979.src as ia

# ### Example 1

# In[2]:

if testing:
    A = ia.dftmatrix(128)
    ia.adshow(ia.normalize(A.real),'A.real')
    ia.adshow(ia.normalize(A.imag),'A.imag')

# Example 2
# ---------

# In[3]:

if testing:
    A = ia.dftmatrix(4)
    print('A=\n', A.round(1))
    print('A-A.T=\n', A - A.T)
    print((np.abs(np.linalg.inv(A)-np.conjugate(A))).max() < 10E-15)

# ### Example 3
#
# Showing the product $\mathbf{x}\mathbf{u}^T$:

# In[4]:

if testing:
    u = x = np.arange(10).reshape(10,1)
    print('u xT=\n', u.dot(x.T))

# ## Equation
#
#
# $$ \begin{matrix}
#     W_N &=& \exp{\frac{-j2\pi}{N}} \\ A_N &=& \frac{1}{\sqrt{N}} (W_N)^{\mathbf{u} \mathbf{x}^T} \\ \mathbf{u} &=& \mathbf{x} = [0, 1, 2, \ldots, N-1]^T
# \end{matrix} $$

# $$ \begin{matrix}
#         A_N       &=& A_N^T \ \mbox{symmetric} \\
#        (A_N)^{-1} &=& (A_N)^*\ \mbox{column orthogonality, unitary matrix}
# \end{matrix} $$

# ## See Also
#
# - `dft` - Discrete Fourier Transform
# - `dftmatrixexamples` - Visualization of the DFT matrix
#
# ## References
#
# - http://en.wikipedia.org/wiki/DFT_matrix

# In[6]:

if testing:
    print('testing dftmatrix')
    print(repr(np.floor(0.5 + 10E4*ia.dftmatrix(4).real) / 10E4) == repr(np.array(
          [[ 0.5,  0.5,  0.5,  0.5],
           [ 0.5,  0. , -0.5,  0. ],
           [ 0.5, -0.5,  0.5, -0.5],
           [ 0.5,  0. , -0.5,  0. ]])))
    print(repr(np.floor(0.5 + 10E4*ia.dftmatrix(4).imag) / 10E4) == repr(np.array(
          [[ 0. ,  0. ,  0. ,  0. ],
           [ 0. , -0.5,  0. ,  0.5],
           [ 0. ,  0. ,  0. ,  0. ],
           [ 0. ,  0.5,  0. , -0.5]])))

# In[ ]:


# -*- coding: utf-8 -*-
"""

This file contains the Kernel class. An object that
returns a kernel function

"""

import numpy as np

class Kernel():
    def __init__(self, choice, param1=None, param2=None):
        self.kernel = set_kernel_by_choice(choice, param1, param2)
        self.choice = choice

    def get_kernel(self):
        return self.kernel

    def get_type(self):
        typ = self.choice
        if typ == 'linear':
            return 'linear'
        if typ == 'polynomial':
            return 'poly'
        if typ == 'gaussian':
            return 'rbf'
        if typ == 'sigmoid':
            return 'sigmoid'
        else:
            return 'other'

def set_kernel_by_choice(choice, param1, param2):
    if choice == 'linear':
        return lambda x1, x2: np.inner(x1, x2)
    elif choice == 'sigmoid':
        return lambda x1, x2: np.tanh(param1 * np.dot(x1, x2) + param2)
    elif choice == 'gaussian':
        return lambda x1, x2: np.exp(-1*param1*np.linalg.norm(np.subtract(x1, x2)))
    elif choice == 'polynomial':
        return lambda x1, x2: (param1 + np.inner(x1, x2)) ** param2


import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
from scipy import special
from scipy.stats import norm

def get_data():
  true_dis = np.random.uniform(0,1,size=800)
  pr_dis = []
  for num in true_dis:
    if num <= 0.25:
      pr = np.random.normal(0,1)
    elif num <= 0.5:
      pr = np.random.normal(3,1)
    elif num <= 0.75:
      pr = np.random.normal(6,1)
    else:
      pr = np.random.normal(9,1)

    pr_dis.append(pr)
  return pr_dis

def get_prazen(data,h,pr_dis):
  n = len(pr_dis)
  y = []
  # print (n)
  for num in data:
    # summ = 0.0
    diff = [1 if np.abs(num-pr) <= h/2.0 else 0 for pr in pr_dis ]
    summ = sum(diff)
    # print ("SUM - ",summ)
    summ/=(n*h*1.0)
    y.append(summ)

  # print ("Prazen " - y)
  return y

#Q-7-a
# getting alphas
X = np.arange(-5,10,0.1)
alpha = X
pr_dis = get_data()
# True distribution
Y0 = 0.25*(norm.pdf(alpha,0,1) + norm.pdf(alpha,3,1) + norm.pdf(alpha,6,1) + norm.pdf(alpha,9,1))
# get kernel density estimator
Y = get_prazen(alpha,0.1,pr_dis)
Y2 = get_prazen(alpha,1,pr_dis)
Y3 = get_prazen(alpha,7,pr_dis)

# plotting the graphs
plt.figure('Kernel Density Estimation',figsize=(15,10))
plt.plot(X, Y0 ,label='True pdf')
plt.plot(X, Y ,label='h = 0.1')
plt.plot(X, Y2 ,label='h = 1')
plt.plot(X, Y3 ,label='h = 7')

plt.xlabel('x', fontsize=18)
plt.ylabel('Pr[X<=x]', fontsize=18)
plt.legend(loc="upper left", prop={'size': 16})
plt.grid()
plt.show()
plt.savefig("PDF.png")

def get_bias(kd_matrix, true_matrix):
  # pr = np.mean(diff, axis=0)
  avg_kd_matrix = np.mean(kd_matrix,axis=0)
  pr = np.subtract(avg_kd_matrix, true_matrix)
  return pr

def get_var(kde_matrix, estimation):
  diff = np.subtract(kd_matrix, estimation)
  print (diff.shape)
  sqr = np.power(diff,2)
  pr = np.mean(sqr, axis=0)
  return pr

#Q-7-b
h_s = [0.01,0.1,0.3,0.6,1,3,7]
mse_list = []
var_list = []
bias_list = []
alpha = np.arange(-5,10,0.1)
y_0 = 0.25*(norm.pdf(alpha,0,1) + norm.pdf(alpha,3,1) + norm.pdf(alpha,6,1) + norm.pdf(alpha,9,1))

# generate 150 samples of 800 data points
raw_matrix = []
for i in range(150):
    b = get_data()
    raw_matrix.append(b)

for h in h_s:
  print ("h - ",h)
  kd_matrix = []
  true_matrix = []

  for i in range(150):
    b = raw_matrix[i]
    kde_0 = get_prazen(alpha,h,b)
    kd_matrix.append(kde_0)

  kd_matrix = np.array(kd_matrix)
  estimation = kd_matrix.mean(axis=0)

  bias = get_bias(kd_matrix, y_0)

  total_bias = [i**2 for i in bias]
  total_bias_2 = np.mean(total_bias)

  var = get_var(kd_matrix, estimation)
  var_total = np.mean(var)

  mse = var + bias**2
  mse_total = total_bias_2 + var_total

  mse_list.append(mse_total)
  bias_list.append(total_bias_2)
  var_list.append(var_total)

print ("Minimum value of MSE = {} at h = {}".format(min(mse_list),h_s[np.argmin(mse_list)]))

plt.figure('Bias Square vs h',figsize=(10,8))
plt.plot(h_s, bias_list)
plt.xlabel('h',fontsize=16)
plt.ylabel('bias-square',fontsize=18)
plt.grid()
plt.show()
plt.savefig("bias-square.png")

plt.figure('Variance vs h', figsize=(10,8))
plt.plot(h_s, var_list)
plt.xlabel('h',fontsize=16)
plt.ylabel('Variance',fontsize=16)
plt.grid()
plt.show()
plt.savefig("variance.png")


import numpy as np

with open('src/day7/input.txt') as f:
    positions = [int(i) for i in f.read().strip().split(',')]

def calc_total_loss(loss_fxn, data, parameter):
    loss = sum([loss_fxn(d, parameter) for d in positions])
    return loss

def l1_loss_fxn(point, parameter):
    return abs(point-parameter)

def part2_loss_fxn(point, parameter):
    # identity 1+2+..+n = n(n+1)/2
    n = abs(point - parameter)
    return n * (n + 1) / 2

def minimize_loss(loss_fxn, data, parameter_space):
    min_loss = float('inf')
    p_star = None  # Parameter that minimizes loss function
    for p in parameter_space:
        loss = calc_total_loss(loss_fxn, data, p)
        if loss < min_loss:
            min_loss = loss
            p_star = p
    return min_loss, p_star

# Brute Force search entire parameter space
parameter_space = range(min(positions), max(positions)+1)
part1_sol = minimize_loss(l1_loss_fxn, positions, parameter_space)

# Can prove median minimizes L1 loss
med = np.median(positions)
if type(med) is int:
    med_loss = calc_total_loss(l1_loss_fxn, positions, med)
else:
    med_loss = minimize_loss(l1_loss_fxn, positions, [np.floor(med), np.ceil(med)])

assert med_loss == part1_sol

part2_sol = minimize_loss(part2_loss_fxn, positions, parameter_space)
print("Part 1 Solution:", part1_sol[0])
print("Part 2 Solution:", part2_sol[0])


"""
From scratch implementation of K means clustering which is a unsupervised
clustering  method that works by iteratively computing new centroids and
moving centroids to the center of the new formed clusters.

Programmed by Aladdin Persson <aladdin.persson at hotmail dot com>
*    2020-05-28 Initial coding

"""

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

class KMeansClustering:
    def __init__(self, X, num_clusters):
        self.K = num_clusters
        self.max_iterations = 100
        self.plot_figure = True
        self.num_examples = X.shape[0]
        self.num_features = X.shape[1]

    def initialize_random_centroids(self, X):
        centroids = np.zeros((self.K, self.num_features))

        for k in range(self.K):
            centroid = X[np.random.choice(range(self.num_examples))]
            centroids[k] = centroid

        return centroids

    def create_clusters(self, X, centroids):
        # Will contain a list of the points that are associated with that specific cluster
        clusters = [[] for _ in range(self.K)]

        # Loop through each point and check which is the closest cluster
        for point_idx, point in enumerate(X):
            closest_centroid = np.argmin(
                np.sqrt(np.sum((point - centroids) ** 2, axis=1))
            )
            clusters[closest_centroid].append(point_idx)

        return clusters

    def calculate_new_centroids(self, clusters, X):
        centroids = np.zeros((self.K, self.num_features))
        for idx, cluster in enumerate(clusters):
            new_centroid = np.mean(X[cluster], axis=0)
            centroids[idx] = new_centroid

        return centroids

    def predict_cluster(self, clusters, X):
        y_pred = np.zeros(self.num_examples)

        for cluster_idx, cluster in enumerate(clusters):
            for sample_idx in cluster:
                y_pred[sample_idx] = cluster_idx

        return y_pred

    def plot_fig(self, X, y):
        plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
        plt.show()

    def fit(self, X):
        centroids = self.initialize_random_centroids(X)

        for it in range(self.max_iterations):
            clusters = self.create_clusters(X, centroids)

            previous_centroids = centroids
            centroids = self.calculate_new_centroids(clusters, X)

            diff = centroids - previous_centroids

            if not diff.any():
                print("Termination criterion satisfied")
                break

        # Get label predictions
        y_pred = self.predict_cluster(clusters, X)

        if self.plot_figure:
            self.plot_fig(X, y_pred)

        return y_pred

if __name__ == "__main__":
    np.random.seed(10)
    num_clusters = 3
    X, _ = make_blobs(n_samples=1000, n_features=2, centers=num_clusters)

    Kmeans = KMeansClustering(X, num_clusters)
    y_pred = Kmeans.fit(X)


from IPython.display import display, Latex, Math

print("We can get rid of all parameters by setting: ")
display(Latex('$U=C/C_{max}$'))
print("and")
display(Latex('$T=t*\lambda$'))
print("both of which are unit-less")
print("Then the ODE becomes:")
print("")
display(Latex('$dU/dT=-U*\ln U,$'))
print("")
print("As we can see, there is no parameter, so we can use a natural scale for the time step (i.e., neither delta t = 0.00000001 nor delta t  = 1000000)")
print("")
print("Sequence of instructions for the forward Euler method:")
print("- delta_t=0.001")
print("- tmax=10")
print("- N=10000")
print("- U=array_of_zeros(1 row, 10001 columns ")
print("- U(1)=U0, initial value of the renormalized variable U")
print("- T=array_of_zeros(1 row, 10001 columns ")
print("- FOR i=1 to N+1 do T(i)=(i-1)*delta_t  end FOR loop   : we define the time grid")
print("- for i=1 to N do:")
print("      U(i+1)=U(i)-delta_t*U(i)*ln(U(i))")
print("end FOR loop")
print("plot({T(i),U(i)}")

print("")
print("Sequence of instructions for the RK4 method:")
print("- delta_t=0.001")
print("- tmax=10")
print("- N=10000")
print("- U=array_of_zeros(1 row, 10001 columns ")
print("- U(1)=U0, initial value of the renormalized variable U")
print("- T=array_of_zeros(1 row, 10001 columns ")
print("- FOR i=1 to N+1 do T(i)=(i-1)*delta_t  end FOR loop   : we define the time grid")
print("- for i=1 to N do:")
print("      k1=-U(i)*ln(U(i)) ")
print("      k2=-[U(i)+k1*delta_t/2]*ln([U(i)+k1*delta_t/2]) ")
print("      k3=-[U(i)+k2*delta_t/2]*ln([U(i)+k2*delta_t/2]) ")
print("      k4=-[U(i)+k3*delta_t]*ln([U(i)+k3*delta_t]) ")
print("      U(i+1)=U(i)+delta_t*(k1+2*k2+2*k3+k4)/6")
print("end FOR loop")
print("plot({T(i),U(i)}")

print("")
print("We note that the Euler method is equivalent to a Runge Kutta 4 scheme with k1=k2=k3=k4")

print("")
print("Python code for the Runge Kutta method:")

import numpy as np
import matplotlib.pyplot as plt

delta_t=0.001
tmax=10
N=10000
U = np.zeros(N+1)
T = np.zeros(N+1)
U[0]=0.5
for i in range(0,N):  # Python starts indexing rows and columns at 0, not 1 as in the algorithm above
        T[i] = i*delta_t
for i in range(0,N):
        k1=-U[i]*np.log(U[i])
        k2=-(U[i]+0.5*k1*delta_t)*np.log((U[i]+0.5*k1*delta_t))
        k3=-(U[i]+0.5*k2*delta_t)*np.log((U[i]+0.5*k2*delta_t))
        k4=-(U[i]+k3*delta_t)*np.log((U[i]+k3*delta_t))
        U[i+1]=U[i]+delta_t*(k1+2*k2+2*k3+k4)/6

# plot the time variations of all the node values
plt.figure()
plt.plot(U,'-')
plt.show()


import numpy as np

def OrdenDeConvergencia(xNmenos2, xNmenos1, xN, xNmas1):
    """
    Calcula el orden de convergencia para determinado punto aplicando la division que se realiza en la definicion.
    Si algun denominador resultase nulo, no se devolvera una constante, se devuelve None.
    """
    a = xNmas1 - xN
    if a == 0:
        return None
    b = xN - xNmenos1
    if b == 0:
        return None

    numerador = np.log(abs(a / b))

    c = xN - xNmenos1
    if c == 0:
        return None

    d = xNmenos1 - xNmenos2
    if d == 0:
        return None

    denominador = np.log(abs(c / d))
    if denominador == 0:
        return None
    return numerador / denominador

#funcion original que habiamos hecho para calcular la constante alfa
def CalcularHistoriaDeOrden2(historia):
    """
    Calcula la constante alfa, necesita de la historia de la busqueda de la raiz.
    Devuelve la historia del alfa y el alfa final conseguido
    """
    tope = len(historia)
    if tope < 5:
        return 0, np.array([])
    historiaDeOrden = np.zeros((tope - 4, 2))

    j = 0
    for i in range(2, tope - 2):
        ordenActual = OrdenDeConvergencia(historia[i - 2][1], historia[i - 1][1], historia[i][1], historia[i + 1][1])
        if not (ordenActual is None):
            historiaDeOrden[j][1] = ordenActual
            historiaDeOrden[j][0] = i
            j = j + 1
    historiaDeOrden = historiaDeOrden[:j]
    return historiaDeOrden[j - 1][1], historiaDeOrden

def CalcularHistoriaDeOrden(historiaRaices):
    """
    Calcula la constante alfa, necesita de la historia de la busqueda de la raiz.
    Devuelve la historia del alfa y el alfa final conseguido
    """
    nIteraciones = len(historiaRaices) - 1
    if len(historiaRaices) < 5:
        return 0, np.array([])
    alfa = np.zeros((nIteraciones - 1, 2))

    for n in range(3 - 1, nIteraciones - 1):
        e_n_mas_1 = historiaRaices[n + 1][1] - historiaRaices[n][1]
        e_n = historiaRaices[n][1] - historiaRaices[n - 1][1]
        e_n_menos_1 = historiaRaices[n - 1][1] - historiaRaices[n - 2][1]

        # Tira un warning de division por 0 debido a que varios numeros estan cercanos al mismo, se agrego para evitarlo,
        # si se saltean estas divisiones los graficos quedan peor.
        with np.errstate(divide='ignore'):
            alfa[n] = n, np.log10(np.abs(e_n_mas_1 / e_n)) / np.log10(np.abs(e_n / e_n_menos_1))

    return alfa[nIteraciones - 2][1], alfa

def ConstanteAsintotica(xN, xNmas1, alfa, raiz):
    """
    Realiza el calculo de la constante asintotica para una iteracion. Los valores recibidos son validos.
    Si resulta que el denominador es 0, se devuelve None.
    """
    numerador = abs((xNmas1 - raiz))
    denominador = abs(xN - raiz) ** alfa
    if denominador == 0:
        return None
    return numerador / denominador

def CalcularHistoriaConstanteAsintotica(historia, alfa):
    """
    Calcula la constante lambda, necesita de la historia de la busqueda de la raiz y del alfa obtenido previamente
    Devuelve la historia del lambda y el lambda final conseguido
    """
    if alfa <= 0:
        return 0, np.array([])
    tope = len(historia)
    raiz = historia[tope - 1][1]

    historiaConstanteAsintotica = np.zeros((tope - 2, 2))
    j = 0
    for i in range(0, tope - 2):
        constanteActual = ConstanteAsintotica(historia[i][1], historia[i + 1][1], alfa, raiz)
        if not (constanteActual is None):
            historiaConstanteAsintotica[j][1] = constanteActual
            historiaConstanteAsintotica[j][0] = i
            j = j + 1

    historiaConstanteAsintotica = historiaConstanteAsintotica[:j]
    return historiaConstanteAsintotica[j - 1][1], historiaConstanteAsintotica


import numpy as np

def minor(A,i,j): #Calculo del determinante del menor
    m=[]
    rows=len(A)
    cols=rows
    for r in range(rows):
      l=[]
      for c in range(cols):
         if c != j:
            l.append(A[r][c])
      if r !=i:
        m.append(l)
    return np.linalg.det(m) #El menor de un elemento aij se denota Mij es el determinante de la matriz resultante al borrar el renglon i y la columna j de  A

def comatrix(A):  #Calculo de matriz de cofactores de A
    rows=len(A)
    cols=rows
    c=[]
    for i in range(rows):
        l=[]
        for j in range(cols):
          l.append((-1)**(i+j)*minor(A,i,j)) #Para calcular los cofactres de una matriz se necesita la menor de la cada elemento de la matriz A
                                             #la Formula es Aij = (-1)^(i+j)|Mij|
        c.append(l)
    return (c)

def adjunct(A): #Calculo de matriz adjunta, adjA= transpuesta de matriz de cofactores de A
    return np.transpose(comatrix(A))

def inverse(A): # Calculo de la Matriz inversa mediante la adjunta. A^-1 = [(A*)^T] / |A|
    '''
    Se pide que el usuario introduzca una matriz de nxn
	ya que para calacular la inversa esta debe ser una matriz cuadrada

	Consta de una serie de funciones para implementar la formula de la matrizinversa

			A^-1 = [(A*)^T] / |A|

		La matriz adjunta [(A*)^T] => tambien se conoce como la transpuesta de la matriz de cofactores
		Determinamte de A =>|A|

	Cada funcion se encarga de genrar una partep para la ecucion.
	Se calcula inicialmente la determinante de la matriz menor de A.
	Posteriormente se calcula la matriz de cofactores usando la siguiente fórmula

			Aij = (-1)^(i+j)|Mij|

	Una vez calculada la matríz de cofactores generamos la matriz adjunta
	simplmente se transpone la matriz de cofactores
	Finalmente, se halla 1/|A| y se multiplica por la matriz adjunta.

    '''
    return (1/np.linalg.det(A)*adjunct(A))

rows = int(input("ingrese el tamaño de la matriz: "))
matrix = []
print("ingrese la matriz %s x %s: "% (rows, rows))
for i in range(rows):
    matrix.append(list(map(int, input().rstrip().split())))

Aarry=np.array(matrix)

myAinv=inverse(Aarry)
print("my A inverse:\n",myAinv,"\n")

Ainv=np.linalg.inv(Aarry)
# Tambien se genera una matríz inversa utilizando unicamente la libreria de Numpy
print("numpy A inverse:\n",Ainv)


#Author
#Felice De Luca
#https://github.com/felicedeluca

##
# EU corresponds to the normalized standard deviation of the edge length.
##

import networkx as nx
import math

def avg_edge_length(G):

    '''
    Computes the average edge length of the given graph layout <tt>G</tt>
    '''

    sum_edge_length = 0.0

    pos_dict = nx.get_node_attributes(G, 'pos')

    for edge in nx.edges(G):

        (s,t) = edge

        x_source = float(pos_dict[s].split(",")[0])
        x_target = float(pos_dict[t].split(",")[0])

        y_source = float(pos_dict[s].split(",")[1])
        y_target = float(pos_dict[t].split(",")[1])

        curr_length = math.sqrt((x_source - x_target)**2 + (y_source - y_target)**2)

        sum_edge_length += curr_length

    edges_count = len(nx.edges(G))
    avg_edge_len = sum_edge_length/edges_count

    return avg_edge_len

def uniformity_edge_length(G):
    '''
    The Edge length uniformity corresponds to the normalized standard deviation of the edge length.
    '''

    edges = nx.edges(G)
    edge_count = len(edges)
    avgEdgeLength = avg_edge_length(G)
    tot_sum = 0.0

    pos_dict = nx.get_node_attributes(G, 'pos')

    for edge in edges:

        (s,t) = edge

        x_source = float(pos_dict[s].split(",")[0])
        x_target = float(pos_dict[t].split(",")[0])

        y_source = float(pos_dict[s].split(",")[1])
        y_target = float(pos_dict[t].split(",")[1])

        curr_length = math.sqrt((x_source - x_target)**2 + (y_source - y_target)**2)

        num = (curr_length-avgEdgeLength)**2
        den = edge_count*(avgEdgeLength**2)

        currValue = num/den
        tot_sum += currValue

    uniformity_e_len = math.sqrt(tot_sum)

    result = round(uniformity_e_len, 3)

    return result


# Simulate an inhomogeneous Poisson point process on a rectangle
# This is done by simulating a homogeneous Poisson process, which is then
# thinned according to a (spatially *dependent*) p-thinning.
# The intensity function is lambda(x,y)=exp(-(x^2+y^2)/s^2), where s>0.
# Author: H. Paul Keeler, 2019.
# Website: hpaulkeeler.com
# Repository: github.com/hpaulkeeler/posts
# For more details, see the post:
# hpaulkeeler.com/simulating-an-inhomogeneous-poisson-point-process/

import numpy as np;  # NumPy package for arrays, random number generation, etc
import matplotlib.pyplot as plt  # For plotting
from scipy.optimize import minimize  # For optimizing
from scipy import integrate  # For integrating

plt.close('all');  # close all figures

# Simulation window parameters
xMin = -1;
xMax = 1;
yMin = -1;
yMax = 1;
xDelta = xMax - xMin;
yDelta = yMax - yMin;  # rectangle dimensions
areaTotal = xDelta * yDelta;

numbSim = 10 ** 3;  # number of simulations

s = 0.5;  # scale parameter

# Point process parameters
def fun_lambda(x, y):
    return 100 * np.exp(-(x ** 2 + y ** 2) / s ** 2);  # intensity function
#fun_lambda = lambda x,y: 100 * np.exp(-(x ** 2 + y ** 2) / s ** 2);

###START -- find maximum lambda -- START ###
# For an intensity function lambda, given by function fun_lambda,
# finds the maximum of lambda in a rectangular region given by
# [xMin,xMax,yMin,yMax].
def fun_Neg(x):
    return -fun_lambda(x[0], x[1]);  # negative of lambda
#fun_Neg = lambda x: -fun_lambda(x[0], x[1]);  # negative of lambda

xy0 = [(xMin + xMax) / 2, (yMin + yMax) / 2];  # initial value(ie centre)
# Find largest lambda value
resultsOpt = minimize(fun_Neg, xy0, bounds=((xMin, xMax), (yMin, yMax)));
lambdaNegMin = resultsOpt.fun;  # retrieve minimum value found by minimize
lambdaMax = -lambdaNegMin;

###END -- find maximum lambda -- END ###

# define thinning probability function
def fun_p(x, y):
    return fun_lambda(x, y) / lambdaMax;
#fun_p = lambda x, y: fun_lambda(x, y) / lambdaMax;

# for collecting statistics -- set numbSim=1 for one simulation
numbPointsRetained = np.zeros(numbSim);  # vector to record number of points
for ii in range(numbSim):
    # Simulate a Poisson point process
    numbPoints = np.random.poisson(areaTotal * lambdaMax);  # Poisson number of points
    xx = np.random.uniform(0, xDelta, ((numbPoints, 1))) + xMin;  # x coordinates of Poisson points
    yy = np.random.uniform(0, yDelta, ((numbPoints, 1))) + yMin;  # y coordinates of Poisson points

    # calculate spatially-dependent thinning probabilities
    p = fun_p(xx, yy);

    # Generate Bernoulli variables (ie coin flips) for thinning
    booleRetained = np.random.uniform(0, 1, ((numbPoints, 1))) < p;  # points to be retained

    # x/y locations of retained points
    xxRetained = xx[booleRetained];
    yyRetained = yy[booleRetained];
    numbPointsRetained[ii] = xxRetained.size;

# Plotting
plt.scatter(xxRetained, yyRetained, edgecolor='b', facecolor='none', alpha=0.5);
plt.xlabel('x');
plt.ylabel('y');

# run empirical test on number of points generated
if numbSim >= 10:
    # total mean measure (average number of points)
    LambdaNumerical = integrate.dblquad(fun_lambda, xMin, xMax, lambda x: yMin, lambda y: yMax)[0];
    # Test: as numbSim increases, numbPointsMean converges to LambdaNumerical
    numbPointsMean = np.mean(numbPointsRetained);
    # Test: as numbSim increases, numbPointsVar converges to LambdaNumerical
    numbPointsVar = np.var(numbPointsRetained);


def five_num_summary(items):

    """The function takes a list as input and return a dict with keys 'max', 'median', 'min', 'q1', and 'q3'
    corresponding to the maximum, median, minimum, first quartile and third quartile, respectively.

    Example
    -------

           Input: gauteng = [39660.0,36024.0, 32127.0, 39488.0, 18422.0, 23532.0, 8842.0, 37416.0, 16156.0, 18730.0, 19261.0, 25275.0]

           Output: five_num_summ(gauteng) == {'max': 39660.0, 'median': 24403.5, 'min': 8842.0, 'q1': 18422.5, 'q3': 36024.5}

    """

    #Importing the module
    import numpy as np
    items = np.array(items)

    #Assigning the required data to 'num_summary'
    num_summary = {'max': round(np.max(items),2), 'median': round(np.median(items),2), 'min': round(np.min(items),2), 'q1': round(np.quantile(items,0.25),2), 'q3': round(np.quantile(items,0.75),2)}

    return num_summary


# A toy making company has at least
#
#	300 squares of felt,
#	700 oz of stuffing, and
#	230 ft of trim to make dogs and dinosaurs.

# A dog uses ( $1.36 )
#	1 square of felt,
#	4 oz of stuffing, and
#	1 ft of trim.
#
# A dinosaur uses ( $1.77 )
#	2 squares of felt,
#	3 oz of stuffing, and
#	1 ft of trim.

# It costs the company $1.25 to make each dog and $1.89 for each dinosaur.
# What is the company's minimum cost?

import numpy as np
from numpy import matrix, transpose, dot

dog_cost, dinosaur_cost = 1.36, 1.77

n_felt = 300
n_stuff = 700
n_trim = 230

felt = [ 1, 2 ]
stuff = [ 4, 3 ]
trim = [ 1, 1 ]

# Set up the constraints and obtain the corner points
a1 = np.array( [ felt, stuff ] )
b1 = np.array( [ n_felt, n_stuff ] )

# solve the two sets of equations at a time to get points of intersection of the lines
c1 = np.linalg.solve( a1, b1 )
print( c1 )

# Second pair of corner points
a2 = np.array( [ felt, trim ] )
b2 = np.array( [ n_felt, n_trim ] )
c2 = np.linalg.solve( a2, b2 )

print( c2 )

# Third pair of corner points
a3 = np.array( [ stuff, trim ] )
b3 = np.array( [ n_stuff, n_trim ] )
c3 = np.linalg.solve( a3, b3 )

print( c3 )

# Use the corner points and compute dot product to determine the minimum value
x= [ c1[ 0 ], c2[ 0 ], c3[ 0 ] ]
y= [ c1[ 1 ], c2[ 1 ], c3[ 1 ] ]

obj = matrix( [ dog_cost, dinosaur_cost ] )
obj = transpose( obj )
corners = matrix( [x,y] )
corners = transpose( corners )
result = dot( corners,obj )

print( "Value of Objective Function at Each Corner Point", result )
print( "Minimum cost of the company is ", result.min() )


import numpy as np
import numpy.linalg as la
import numpy.random as npr
import scipy.linalg as sla
from functools import reduce
import matplotlib.pyplot as plt

def mdot(*args):
    """Multiple dot product."""
    return reduce(np.dot, args)

def sympart(A):
    """Return the symmetric part of matrix A."""
    return 0.5*(A+A.T)

def gain(P, A, B, R):
    Hux = mdot(B.T, P, A)
    Huu = R+mdot(B.T, P, B)
    return -la.solve(Huu, Hux)

def lqr(A, B, Q, R, max_iters=None, threshold=1e-6):
    """
    Solve a infinite-horizon linear-quadratic regulation problem
    minimize sum(x[k].T, @ Q @ x[k] + u[k].T @ R @ u[k])
    subject to x[k+1] = A @ x[k] + B @ u[k]
    where
    A, B are dynamics matrices
    Q, R are positive semidefinite cost matrices
    """

    P = np.copy(Q)
    t = 0
    timeout = False
    converged = False
    while not converged or timeout:
        P_prev = np.copy(P)
        Hxx = Q+mdot(A.T, P, A)
        Hux = mdot(B.T, P, A)
        Huu = R+mdot(B.T, P, B)
        K = -la.solve(Huu, Hux)
        P = sympart(Hxx+np.dot(Hux.T, K))  # Take symmetric part to avoid numerical divergence issues
        converged = la.norm(P-P_prev, ord=2) < threshold
        if max_iters is not None:
            timeout = t > max_iters
        t -= 1
    return P, K

def rollout(x0, A, B, K):
    x_hist = np.zeros([T, n])
    u_hist = np.zeros([T, m])
    w_hist = np.zeros([T, n])
    x_hist[0] = np.copy(x0)
    for t in range(T-1):
        x = x_hist[t]
        u = K.dot(x)
        w = npr.multivariate_normal(wm, W)
        x_hist[t+1] = A.dot(x) + B.dot(u) + w
        u_hist[t] = u
        w_hist[t] = w
    return x_hist, u_hist, w_hist

def plot_hist(x_hist_all, u_hist_all, w_hist_all):
    fig, ax = plt.subplots(nrows=3)
    ylabels = ['State', 'Input', 'Disturbance']
    dims = [n, m, n]
    for i, (hist, ylabel, d) in enumerate(zip([x_hist_all, u_hist_all, w_hist_all], ylabels, dims)):
        for k in range(d):
            ax[i].plot(hist[:, :, k].T, color='C%d' % k, alpha=0.8)
        ax[i].set_ylabel(ylabel)
    ax[-1].set_xlabel('Time')
    fig.tight_layout()
    return fig, ax

if __name__ == "__main__":
    # Random seed
    npr.seed(4)

    # Problem data
    n = 5
    m = 2
    T = 30

    x0 = 5*npr.randn(n)

    A = npr.randn(n, n)
    # Normalize A to fix the spectral radius to r
    r = 2
    A = A*r/(max(abs(la.eig(A)[0])))
    B = npr.randn(n, m)

    Q = 1*np.eye(n)
    R = 1*np.eye(m)
    W = 0.1*np.eye(n)
    wm = np.zeros(n)

    # Solve DARE manually
    P, K = lqr(A, B, Q, R)

    # Solve DARE via SciPy
    Pscipy = sla.solve_discrete_are(A, B, Q, R)
    Kscipy = gain(Pscipy, A, B, R)

    # Compare DARE solutions
    print('Spectral norm of difference between P and Pscipy = %f' % la.norm(P-Pscipy, ord=2))
    print('Spectral norm of difference between K and Kscipy = %f' % la.norm(K-Kscipy, ord=2))

    # Simulate
    N = 10
    x_hist_all, u_hist_all, w_hist_all = np.zeros([N, T, n]), np.zeros([N, T, m]), np.zeros([N, T, n])
    for i in range(N):
        x_hist_all[i], u_hist_all[i], w_hist_all[i] = rollout(x0, A, B, K)

    # Plot
    plt.close('all')
    plt.style.use('../conlab.mplstyle')
    plot_hist(x_hist_all, u_hist_all, w_hist_all)


''' mbinary
#########################################################################
# File : least_square.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 Name: least-square.py
    > Author: mbinary
    > Mail: zhuheqin1@gmail.com
    > Created Time: Sat 07 Apr 2018 09:55:25 PM DST
    > Blog: https://mbinary.xyz
    > Description: 最小二乘法解线性方程组, 解矛盾方程组
 ************************************************************************'''

import re
import numpy as np

def solveConflitEqualtion(A,y):
    '''solve equation like this: Av = y,
        A:m*n  v:n*1  y:m*1
        return vector v
    '''
    A = np.matrix(A)
    y = np.array(y)
    ata = A.T*A
    print('AtA')
    print(ata)
    return np.linalg.solve(ata,A.T*y) # note that is numpy.linalg.solve

def solveLinear(point,index):
    y = [[i[1]] for i in point]
    x = [[i[0]] for i in point]
    A = []
    for i in x:
        A.append([i[0]**j for j in index])
    res =  solveConflitEqualtion(A,y)
    print('the solution is : \n',res)
    print('namely: ')
    items = ['{:.4f}x^{}'.format(res[i,0],j)   for i, j in enumerate(index)]
    print('phi(x) = ',' + '.join(items))

def handleInput(s=None,y=None):
    # numPt = re.compile (r'\d*\.{0,1}\d+')
    if not s: s = input('input matrix A:m*n  //m>=n\n')
    s = s.replace(' ','')
    li = re.findall(r'(\[(\d+)(,(\d+))+\])',s)
    li = [parseLst(i[0]) for i in li]
    if not y:y = input('input a vector y:n*1\n')
    y = parseLst(y)
    print('Equation: Av = y:')

    print('y is as follows: ')
    print(y)
    print('A is as follows: ')
    for i in li:
        for j in i:
            print('{}'.format(j).rjust(5),end='')
        print('')

    print('result v is as follows: ')
    res = solveConflitEqualtion(li,y)
    print(res)
def parseLst(s):
    s = s.strip('[]')
    li = s.split(',')
    li = [float(j) for j in li]
    return li

if __name__ == '__main__':
    '''
    li = '[[23,2],[2,5],[2,6]]'
    y = '[1,3]'
    while True:
        handleInput(li,y)
        s = input('input y to continue, n for exit')
        if s!='y':break
    '''
    point = [(-3,14.3),(-2,8.3),(-1,4.7),(2,-8.3),(4,-22.7)]
    lst = [0,3]
    solveLinear(point,lst)

    point= [(-3,14.3),(-2,8.3),(-1,4.7),(2,8.3),(4,22.7)]
    lst = [0,2]
    solveLinear(point,lst)

    A = [[1,2],[2,1],[1,1]]
    y  = [[5],[6],[4]]
    res = solveConflitEqualtion(A,y)

    print(res)
    A = [[1,-2],[1,5],[2,1],[1,1]]
    y = [[1],[13.1],[7.9],[5.1]]
    print(solveConflitEqualtion(A,y))


# coding: utf-8
#
# Copyright (C) 2020 wsn-toolkit
#
# This program was written by Edielson P. Frigieri <edielsonpf@gmail.com>

"""Path loss models."""

from abc import ABCMeta, abstractmethod
import numpy as np
import math

DEFAULT_C = 2.998e8
MIN_LOSS = 0

class BasePropagationModel(metaclass=ABCMeta):
	"""Base class for propagation loss models."""

	def __init__(self):
		pass

	def _friis_loss(self, distance, frequency):
		"""
		Calculate the path loss based on Friss propagation loss model.

		Formula:
				PL0 = 10*log10(Gr*Gt*((4*pi*d*f)/c)^2)

		Parameters
		----------
		distance : double
		   The distance (m).

		frequency: double
			The frequency of operation (Hz).

		Returns
		-------
		double
			The Friss loss in decibels [dBm] evaluated for `distance` and `frequency`.
		"""
		Gr=1
		Gt=1

		return 10*math.log10(Gr*Gt*((4*math.pi*distance*frequency)/DEFAULT_C)**2)

	@abstractmethod
	def loss(self, distance, frequency):
		"""Calculate the path loss."""
		raise NotImplementedError

class FreeSpace(BasePropagationModel):
	"""Class for Log-nomal propagation models."""

	def __init__(self):
		super(FreeSpace).__init__()

	def loss(self, distance, frequency):
		"""
		Calculate the link loss.
		For distance and frequency in meters and hertz, respectively.

		Calculates the loss based on the free-space Friis equation

		Parameters
		----------
		distance : double
		   The distance between two nodes (m).

		frequency: double
			The frequency of operation (Hz).

		Returns
		-------
		double
			The loss evaluated for `distance` and `frequency`.
		"""
		if distance > 0.1:
			L = self._friis_loss(distance, frequency)
		else:
			L = MIN_LOSS

		return L

class LogDistance(BasePropagationModel):
	"""Class for Log-nomal propagation models."""

	def __init__(self, d0 = 1.0, sigma = 0.0, n0 = 2.0):
		self.d0 = d0
		self.sigma = sigma
		self.n0 = n0
		super(LogDistance).__init__()

	def loss(self, distance, frequency):
		"""
		Calculate the link loss.
		For distance and frequency in meters and hertz, respectively.

		The log-normal path-loss model may be considered as a generalization of the free-space Friis equation
		where a random variable is added in order to account for shadowing (large–scale fading) effects.

		See:    https://www.sciencedirect.com/topics/computer-science/path-loss-model
				https://en.wikipedia.org/wiki/Log-distance_path_loss_model

				Building Type	            Frequency of Transmission	n0 	sigma [dB]
				Vacuum, infinite space		                            2.0	    0
				Retail store	            914 MHz	                    2.2	    8.7
				Grocery store	            914 MHz	                    1.8	    5.2
				Office with hard partition	1.5 GHz	                    3.0	    7
				Office with soft partition	900 MHz	                    2.4	    9.6
				Office with soft partition	1.9 GHz	                    2.6	    14.1
				Textile or chemical	        1.3 GHz	                    2.0	    3.0
				Textile or chemical	        4 GHz	                    2.1	    7.0, 9.7
				Office	                    60 GHz	                    2.2	    3.92
				Commercial	                60 GHz	                    1.7	    7.9

		Formula:
				PL = PL0 + 10*n0*log10(d/d0) + X_sigma

		Parameters
		----------
		distance : double
		   The distance between two nodes (m).

		frequency: double
			The frequency of operation (Hz).

		Returns
		-------
		double
			The loss evaluated for `distance` and `frequency`.
		"""
		if distance < self.d0:
			L = self._friis_loss(distance, frequency) + np.random.normal(0, self.sigma)
		else:
			L = self._friis_loss(self.d0, frequency) + self.n0*10*math.log10(distance/self.d0) + np.random.normal(0, self.sigma)
		return L

class TwoSlope(BasePropagationModel):
	"""Class for Log-nomal propagation models."""

	def __init__(self, d0 = 1.0, d1 = 10.0, sigma = 0.0, n0 = 2.0, n1 = 3.0):
		self.d0 = d0
		self.d1 = d1
		self.sigma = sigma
		self.n0 = n0
		self.n1 = n1
		super(TwoSlope).__init__()

	def loss(self, distance, frequency):
		"""
		Calculate the link loss.
		For distance and frequency in meters and hertz, respectively.

		The two-slope path-loss model implements a log distance path loss propagation model with two distance fields.
		This model is the same as the LogDistance model except that it has two distance fields: near, far with different exponents.

		Within each field the reception power is calculated using the log-distance propagation equation:

			PL = PL0 + 10*n0*log10(d/d0)

		where each field begins where the previous ends and all together form a continuous function.

		A random variable is added in order to account for shadowing (large–scale fading) effects.

		Formula:

				PL = PL0  + X_sigma 															, for d < d0
				PL = PL0 + 10*n0*log10(d/d0) + X_sigma 											, for d0<= d < d1
				PL = PL0 + 10*n0*log10(d1/d0) + 10*n1*log10(d/d1)  + X_sigma					, for d1 <= d

		Parameters
		----------
		distance : double
		   The distance between two nodes (m).

		frequency: double
			The frequency of operation (Hz).

		Returns
		-------
		double
			The loss evaluated for `distance` and `frequency`.
		"""
		if distance < self.d0:
			L = self._friis_loss(distance, frequency) + np.random.normal(0, self.sigma)
		elif self.d0 <= distance < self.d1:
			L = self._friis_loss(self.d0, frequency) + self.n0*10*math.log10(distance/self.d0) + np.random.normal(0, self.sigma)
		else:
			L = self._friis_loss(self.d0, frequency) + self.n0*10*math.log10(self.d1/self.d0) + self.n1*10*math.log10(distance/self.d1) + np.random.normal(0, self.sigma)

		return L


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

# Task 1
# ======

# given data
m1_m=24.2   # population 1 mean
m1_s=6.1    # population 1 standard deviation
m2_m=25.9   # population 2 mean
m2_s=5.5    # population 2 standard deviation
t2m=24.5
n=30

# calculations
m1_error=m1_s/(float(n))**0.5   # standard error if we sampled from pop 1
m2_error=m2_s/(float(n))**0.5   # standard error if we sampled from pop 2
m1_z=(t2m-m1_m)/m1_error        # standard score if sampled from pop 1
m2_z=(t2m-m2_m)/m2_error        # standard score if sampled from pop 2

print ("P(T>24.5 | M1) :",1-(norm.cdf(m1_z, 0, 1)))
print ("P(T>24.5 | M2) :",1-(norm.cdf(m2_z, 0, 1)))

# plotting
x1 = np.linspace(m1_m - 3*m1_s, m1_m + 3*m1_s, 100)   # create data from population parameters
x2 = np.linspace(m2_m - 3*m2_s, m2_m + 3*m2_s, 100)   # create data from population parameters
plt.plot(x1, norm.pdf(x1, m1_m, m1_s))                # create plot
plt.plot(x2, norm.pdf(x2, m2_m, m2_s))                # create plot

x1 = np.linspace(m1_m - 3*m1_error, m1_m + 3*m1_error, 100)   # create data from distribution
x2 = np.linspace(m2_m - 3*m2_error, m2_m + 3*m2_error, 100)   # create data from distribution
plt.plot(x1, norm.pdf(x1, m1_m, m1_error))                # create plot
plt.plot(x2, norm.pdf(x2, m2_m, m2_error))                # create plot

plt.show()

# Task 2
# ======

s=1.1 # estimate for population standard deviation
# Find distance in standard deviations for a confidence interval of 95. Note that this function in python takes the left integral. To get the distance from the mean, integrate to 1-0.25 (because you leave out the 0.25 tails on each side). The following give you the upper and lower bound.
z1=norm.ppf(.975)    # percent point function
z2=norm.ppf(.025)    # percent point function
# Since the distribution is symmetrical, we can only use one of the z's from now on. The question wants to know the sample number required to make sure that the 95% confidence interval is the true mean +/- 0.5. We can therefore say abs(z2) = z1 = 0.5. We have an estimate for the population standard deviation and can estimate our sampling distribution standard deviation from by: 0.5/z1. Therefore, we can calculate the samples required for this interval as:
ns=(1.1/(0.5/z1))**2
print ('min. no. of samples required: ', ns)


import numpy as np

def premier(n):   # entrée : entier n / sortie : booléen (True si premier)
                  # teste si un nombre n est premier

    if n == 1 or n == 2:    # 1 et 2 sont premiers

        return True

    if n%2 == 0:     # si n est un nombre pair

        return False

    r = n**0.5     # racine carrée de n

    if r == int(r):     # si la racine carrée de n est un nombre entier, n n'est pas premier

        return False

    for x in range(3, int(r), 2):     # intervalle [3 ; n-1] avec un pas de 2 (nombres impairs)

        if n % x == 0:     # si n est divisible par x, il n'est pas premier

            return False

    return True      # true si n est premier

def cut(k, long):     # entrée : k = chaîne de caractères / sortie : long = liste de blocs de chaînes de caractères
                      # découpe k en blocs de longueur long

    d = 0
    f = long

    l = []

    while f <= len(k):      # tant que la longueur à découper est inférieure à la longueur de la chaîne de caractères

        l.append(k[d:f])     # découpe un bloc de longeuur f dans la chaîne de caractères à partir de d (initialisé à 0) jusqu'à f

        d = f
        f = f + long    # on passe au bloc suivant

    m = len(k)%long

    if m != 0:     # si k n'est pas vide (nombre de caractères restant < f)

        l.append(k[len(k)-m:])     # ajoute les caractères restant à l

    return l     # renvoie k transformée en liste de blocs de chaînes de caractères de longueur l

def pgcde(a, b):  # entrée : entiers a et b / sortie : r PGCD de a et b, u et v coefficients de bézout tels que au + bv = r

    r = a
    u = 1
    v = 0
    rp = b
    up = 0
    vp = 1

    while rp != 0:   # algorithme d'Euclide étendu (pour trouver les coefficients)
        q = r//rp
        rs, us, vs = r, u, v
        r, u, v = rp, up, vp
        rp, up, vp = (rs - q*rp), (us - q*up), (vs - q*vp)
        assert u*a + v*b == r

    return (r, u, v)

def key():    # sortie : tuple de deux entiers
              # génère une paire de clés (publique / privée) sous forme de tuple

    p = np.random.choice(1000,1)    # nombre choisi aléatoirement entre 0 et 999 avec un pas de 1
    q = np.random.choice(1000,1)    # nombre choisi aléatoirement entre 0 et 999 avec un pas de 1

    while not premier(p):
        p = np.random.choice(1000,1)  # tant que p n'est pas premier, choisit un nouveau nombre aléatoire entre 0 et 999 avec pas de 1

    while not premier(q):
        q = np.random.choice(1000,1)    # tant que p n'est pas premier, choisit un nouveau nombre aléatoire entre 0 et 999 avec pas de 1

    n = p*q   # multiplication des deux nombres premiers (module de chiffrement)
    m = (p-1)*(q-1)   # valeur de l'indicatrice d'Euler en n

    r = 10
    d = 0
    while r != 1 or d <= 2 or d >= m:
        c = np.random.choice(1000,1)    # exposant de chiffrement premier avec m et strictement inférieur à n
        r, d, v = pgcde(c,m)      # bézout car c et m premiers entre eux

    n, c, d = int(n), int(c), int(d)      # nombres entiers

    return {"priv":(n,c), "pub":(n,d)}  # clé privée et clé publique

def chiffre(n, c, msg):     # entrée : clé privée c (int) et message à chiffrer (str) / sortie : liste de chaînes de caractères

    asc = [str(ord(j)) for j in msg]      # conversion du message en codes ascii

    for i, k in enumerate(asc):     # ajout de 0 pour que chaque code ascii soit de longueur 4
        if len(k) < 3:

            while len(k) < 3:

                k = '0' + k

            asc[i] = k

    ascg = ''.join(asc)     # chaîne de caractères qui contient tous les codes ascii concaténés

    f = 4   # découper ascg en blocs de longueur 4

    while len(ascg)%f != 0:   # on rajoute des 0 tant que len(ascg) n'est pas divisible par 4

        ascg = ascg + '0'

    l = cut(ascg, f)    #liste composée d'ascg découpée en blocs de longueur f

    crypt = [str((int(i)**c)%n) for i in l]  # exponentiation modulaire : message à la puissance c modulo n

    return crypt

def dechiffre(n, d, *crypt):    #  entrée : clé publique (int) et message à déchiffrer (liste) / sortie : chaîne de caractères correspondant au message déchiffré

    resultat = [str((int(i)**d)%n) for i in crypt]   # exponentiation modulaire

    for i, s in enumerate(resultat):   # ajout de 0 pour que chaque code ascii soit de longueur 5

        if len(s) < 4:

            while len(s) < 4:

                s = '0' + s

            resultat[i] = s

    g = ''.join(resultat)   # chaîne de caractères qui contient tous les codes ascii concaténés

    asci = ''

    d , f = 0 , 3

    while f < len(g):

        asci = asci + chr(int(g[d:f]))   # convertit en caractère correspondant au code de caractère Unicode (de d à f)

        d , f = f , f + 3

    return asci


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

# Suppose that the cost function for a product is given by
x = symbols('x', positive = True ) # 0 <= x
C = 0.002 * x**3 + 7*x + 7626

avgC = C / x

g_xlim = [ 1, 500 ]

lam_c = lambdify( x, avgC, np )

x_vals = np.linspace( g_xlim[0], g_xlim[1], 10000, endpoint=True )
y_vals = lam_c( x_vals )

plt.plot( x_vals, y_vals, label = '' )

daC = diff( avgC, x, 1 )

# The production level that produces the minimum average cost per unit is
min = solve( daC, x )[ 0 ]
min

# (Round to the nearest whole number as needed.)
round( min )

plt.show()


"""
REGRESSÃO LINEAR SIMPLES => Existe apenas 1 atributo previsor
REGRESSÃO LINEAR MÚLTIPLA -> Mais de 1 atributo previsor

Variável x são as variáveis independentes (explantatórias)
Variável y é a variável independente (a qual buscamos prever)

A correlação vai de -1 a 1, onde valores -1 e 1 são correlações perfeitas
que significa que existe total correação. Se o for 1 significa que uma variável
aumenta e a outra também, correlação positiva.
Se o valor for -1 significa que a correlação é negativa, onde uma variável
aumenta e a outra diminui.
O valor 0 para correlação significa que não há correlação.

# NOTA:
É muito importante a análise da correlação, pois se tivermos valores muito
baixos (próximos de 0) provavel que seu modelo de regressão linear não vai se
adaptar muito bem a sua base de dados.

Neste exemplo como temos uma correlação de 0.93, é uma correlação forte,
portanto, é um primeiro indicativo que o modelo de regressão linear pode ser
usado.
"""

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from yellowbrick.regressor import ResidualsPlot

base = pd.read_csv("plano-saude.csv")

X = base.iloc[:, 0].values
y = base.iloc[:, 1].values

# Iremos verificar se existe algum tipo de correlação entre as variáveis x e y
# A palavra correlação vem da estatística e indica quanto uma variável está
# próxima de outra, ou seja, se uma variável está ou não correlacionada.
correlacao = np.corrcoef(X, y)

# Veja o resultado da correlação é uma matriz 2 por 2 onde, a intersecção do 0 com 0
# o resultado é 1, ou seja, a correlação da variável idade com ela mesma é 1, e
# a correlação da variável 0 (idade) com a 1 (custo) é de 0.9390 (93%)
# Quanto maior o valor, melhor, ou seja, maior correlação, por isso na diagonal
# principal temos os valores de 1, que são as correlações das variáveis com
# elas mesmas, que é totalmente correlacionada (100%)

# Iremos alterar a forma (shape) das variáveis para matriz, pois os algoritmos
# do scikit-learn precisam que os parâmetros sejam matrizes
# Usamos o -1 para não mexer nas linhas e o 1 para adicionar uma coluna
X = X.reshape(-1, 1)

# Criação do regressor
regressor = LinearRegression()

# treinamento do regressor
# O treinamento do algoritmo de regressão linear é para encontrar os melhores
# parâmetros do b0 e b1 que são a constante e o coeficiente, respectivamente
regressor.fit(X, y)

# Exibe o valor de b0 (constante)
print(regressor.intercept_)

# Exibe o valor de b1 (coeficiente)
# Temos um coeficiente para cada uma das variáveis previsores
# Como temos apenas 1 variável previsora neste exemplo, será apenas 1 valor
print(regressor.coef_)

# Utiliza a lib matplot para exibir um gráfico com os valores (Reais) de X e y
plt.scatter(X, y)

# Criando linha da regressão linear após encontrar o b0 e b1 (Valores previstos)
# Será adicionado a linha da regressão linear de acordo com a previsão
plt.plot(X, regressor.predict(X), color='red')

# Adicionando título ao gráfico
plt.title("Regressão Linear Simples")
# adicionando nome para os eixos
plt.xlabel("Idade")
plt.ylabel("Custo")

# Realizando a previsão de um novo registro
# previsão pessoa com 40 anos
previsao1 = regressor.predict(np.array(40).reshape(1, -1))
# Podemos reescrever a previsão acima utilizando os parâmetros b0 + b1 * valor X
previsao2 = regressor.intercept_ + regressor.coef_ * 40

# O valor previsto para o custo do plano de saúde foi de 1.915 reais

# Verificando como a correlação está se comportando
# O valor retornado é diferente do valor de coeficiente de correlação encontrado
# anteriormente, o valor do score é o valor de correlação do seu modelo de regressão
# Quanto valores maiores melhor
# Mede quão bom é o seu modelo
score = regressor.score(X, y)

# Valores de residuais são as distâncias que existem entre os pontos (reais) e a linha reta (prevista)
# Passamos como parâmetro o regressor que já foi treinado
visualizador = ResidualsPlot(regressor)
visualizador.fit(X, y)
visualizador.poof()


import numpy as np
from test_utils import test

def basic_sigmoid_test(target):
    x = 1
    expected_output = 0.7310585786300049
    test_cases = [
        {
            "name": "datatype_check",
            "input": [x],
            "expected": float,
            "error": "Datatype mismatch."
        },
        {
            "name": "equation_output_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong output."
        }
    ]

    test(test_cases, target)

def sigmoid_test(target):
    x = np.array([1, 2, 3])
    expected_output = np.array([0.73105858,
                                0.88079708,
                                0.95257413])
    test_cases = [
        {
            "name":"datatype_check",
            "input": [x],
            "expected": np.ndarray,
            "error":"Datatype mismatch."
        },
        {
            "name": "shape_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong shape."
        },
        {
            "name": "equation_output_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong output."
        }
    ]

    test(test_cases, target)

def sigmoid_derivative_test(target):
    x = np.array([1, 2, 3])
    expected_output = np.array([0.19661193,
                                0.10499359,
                                0.04517666])
    test_cases = [
        {
            "name":"datatype_check",
            "input": [x],
            "expected": np.ndarray,
            "error":"The function should return a numpy array."
        },
        {
            "name": "shape_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong shape."
        },
        {
            "name": "equation_output_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong output."
        }
    ]

    test(test_cases, target)

def image2vector_test(target):
    image = np.array([[[ 0.67826139,  0.29380381],
                      [ 0.90714982,  0.52835647],
                      [ 0.4215251 ,  0.45017551]],

                     [[ 0.92814219,  0.96677647],
                      [ 0.85304703,  0.52351845],
                      [ 0.19981397,  0.27417313]],

                     [[ 0.60659855,  0.00533165],
                      [ 0.10820313,  0.49978937],
                      [ 0.34144279,  0.94630077]]])

    expected_output = np.array([[ 0.67826139],
                                [ 0.29380381],
                                [ 0.90714982],
                                [ 0.52835647],
                                [ 0.4215251 ],
                                [ 0.45017551],
                                [ 0.92814219],
                                [ 0.96677647],
                                [ 0.85304703],
                                [ 0.52351845],
                                [ 0.19981397],
                                [ 0.27417313],
                                [ 0.60659855],
                                [ 0.00533165],
                                [ 0.10820313],
                                [ 0.49978937],
                                [ 0.34144279],
                                [ 0.94630077]])
    test_cases = [
        {
            "name":"datatype_check",
            "input": [image],
            "expected": np.ndarray,
            "error":"The function should return a numpy array."
        },
        {
            "name": "shape_check",
            "input": [image],
            "expected": expected_output,
            "error": "Wrong shape"
        },
        {
            "name": "equation_output_check",
            "input": [image],
            "expected": expected_output,
            "error": "Wrong output"
        }
    ]

    test(test_cases, target)

def normalizeRows_test(target):
    x = np.array([[0, 3, 4],
                  [1, 6, 4]])
    expected_output = np.array([[ 0., 0.6, 0.8 ],
                                [ 0.13736056, 0.82416338, 0.54944226]])

    test_cases = [
        {
            "name":"datatype_check",
            "input": [x],
            "expected": np.ndarray,
            "error":"The function should return a numpy array."
        },
        {
            "name": "shape_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong shape"
        },
        {
            "name": "equation_output_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong output"
        }
    ]

    test(test_cases, target)

def softmax_test(target):
    x = np.array([[9, 2, 5, 0, 0],
                  [7, 5, 0, 0 ,0]])
    expected_output = np.array([[ 9.80897665e-01, 8.94462891e-04,
                                 1.79657674e-02, 1.21052389e-04,
                                 1.21052389e-04],

                                [ 8.78679856e-01, 1.18916387e-01,
                                 8.01252314e-04, 8.01252314e-04,
                                 8.01252314e-04]])
    test_cases = [
        {
            "name":"datatype_check",
            "input": [x],
            "expected": np.ndarray,
            "error":"The function should return a numpy array."
        },
        {
            "name": "shape_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong shape"
        },
        {
            "name": "equation_output_check",
            "input": [x],
            "expected": expected_output,
            "error": "Wrong output"
        }
    ]

    test(test_cases, target)

def L1_test(target):
    yhat = np.array([.9, 0.2, 0.1, .4, .9])
    y = np.array([1, 0, 0, 1, 1])
    expected_output = 1.1
    test_cases = [
        {
            "name":"datatype_check",
            "input": [yhat, y],
            "expected": float,
            "error":"The function should return a float."
        },
        {
            "name": "equation_output_check",
            "input": [yhat, y],
            "expected": expected_output,
            "error": "Wrong output"
        }
    ]

    test(test_cases, target)

def L2_test(target):
    yhat = np.array([.9, 0.2, 0.1, .4, .9])
    y = np.array([1, 0, 0, 1, 1])
    expected_output = 0.43

    test_cases = [
        {
            "name":"datatype_check",
            "input": [yhat, y],
            "expected": float,
            "error":"The function should return a float."
        },
        {
            "name": "equation_output_check",
            "input": [yhat, y],
            "expected": expected_output,
            "error": "Wrong output"
        }
    ]

    test(test_cases, target)


#!\usr\bin\python
"""
File: Euler.py

Copyright (c) 2016 Michael Seaman

License: MIT

As part of HW6, this implementation aids with solving the ODE's
given as the problems
"""

import numpy as np

def Euler(dydx, y_a, delta_x, a, b):
    """
    Takes the function dydx and computes an approximation of
    the function it models using Euler's method and a stepsize
    delta_x from a to b
    """
    x = np.arange(a, b, delta_x, dtype = np.float64)
    y = np.zeros(len(x)) + y_a
    for i in xrange(1,len(y)):
        y[i] = y[i-1] + delta_x * dydx(x[i-1], y[i-1])
    return y

def linear_derivative(x, y):
    return -.25

def linear_function(x, y):
    return -.25 * x + 20

def test_Euler():
    """
    Implemented around the fact that Euler's method will approximate
    a linear curve perfectly.
    """
    a = -20
    b = 30
    d_x = .001
    y_a = linear_function(a,6)
    xList = np.arange(a, b, d_x)
    lf_vec = np.vectorize(linear_function)
    exact = lf_vec(xList, xList)
    approx = Euler(linear_derivative, y_a, d_x, a, b)
    assert np.allclose(exact, approx)


# LINEAR REGRESSION
#
# y = b1 * x + b0
#
# To find b0 and b1 there are 2 ways:
#
# 1. Karl's correlation coeff
# 2. Least square method
#
# Steps (using Karl's Correlation Coeff Method):
# 1. Find summation of X (x_sum)
# 2. Find summation of Y (y_sum)
# 3. Find x * y and its summation
# 4. Find (x) ** 2 and its summation
# 5. Find b1 using formula
# 6. Find b0 using formula
# 7. Find y using regression formula

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression

kcoeff = []

df = pd.DataFrame({
    "X" : [0, 1, 2, 3, 4],
    "Y" : [2, 3, 5, 4, 6]
})
def calculateXY():
    df["(X) * (Y)"] = df["X"] * df["Y"]

def calculateXSquare():
    df["(X) ^ 2"] = df["X"] ** 2

def calculateKcoeff(x_val):
    xy_sum = df["(X) * (Y)"].sum()
    x_sum = df["X"].sum()
    y_sum = df["Y"].sum()
    xsquare_sum = df["(X) ^ 2"].sum()
    n = df["X"].count()

    X_mean = df["X"].mean()
    Y_mean = df["Y"].mean()

    b1 = round((n*xy_sum - (x_sum*y_sum)) / (n*xsquare_sum-pow(x_sum,2)), 2)
    b0 = round(Y_mean - b1 * X_mean, 2)
    kcoeff.append(b0)
    kcoeff.append(b1)
    print(kcoeff)
    print(x_val)
    y = round(kcoeff[0] + kcoeff[1] * x_val, 2)
    return y

def scikitlearn_linear_reg(x_val):
    X = df["X"].values
    X = np.reshape(X, (-1, 1))
    Y = df["Y"].values
    Y = np.reshape(Y, (-1, 1))
    reg = LinearRegression().fit(X, Y)
    y = round(reg.predict(np.array([[x_val]])).flatten()[0], 2)
    return y

if __name__ == "__main__":
    x = int(input("Enter x value: "))
    calculateXY()
    calculateXSquare()
    y_custom = calculateKcoeff(x)
    y_scikit = scikitlearn_linear_reg(x)
    print("FINAL DATA:")
    print(df.head(6))
    print(f"For x = {x}")
    print(f"By custom linear regression model: y = {y_custom}")
    print(f"By scikit-learn linear regression model: y = {y_scikit}")

########### OUTPUT ###########

# Enter x value: 10
# [b0, b1] = [2.2, 0.9]
# x = 10
# FINAL DATA:
#    X  Y  (X) * (Y)  (X) ^ 2
# 0  0  2          0        0
# 1  1  3          3        1
# 2  2  5         10        4
# 3  3  4         12        9
# 4  4  6         24       16
# For x = 10
# By custom linear regression model: y = 11.2
# By scikit-learn linear regression model: y = 11.2


import numpy as np

# loss functions and derivative
# mean squared error ou encore moyenne de l'erreur au carré
def mse(y_true, y_pred):
    return np.mean(np.power(y_true-y_pred, 2))

def mse_prime(y_true, y_pred):
    return 2 * (y_pred - y_true) / len(y_true)

# getting loss when using logistic regression ( softmax function )

def get_loss(y_true, y_pred):
  return -1 * np.sum(y_true * np.log(y_pred))

def get_loss_numerically_stable(y_true, y_pred):
   return -1 * np.sum(y_true * (y_pred + (-y_pred.max() - np.log(np.sum(np.exp(y_pred-y_pred.max()))))))


import numpy as np

class PiecewiseQuadratic(object):
    """Interpolate by piecewise quadratic polynomials data from a univariate function.

    This class represents a piecewise quadratic polynomial that passes through a series
    of (x,y) data points. The resulting continous function will have a continous derivative
    with prescribed value of first derivative at the inital data point (x_0,y_0).

    Args:
        times (:obj:`numpy array`): Time points at which the data was collected, `shape=(N,)`.
        data (:obj:`numpy array`): The scalar data values collected at the timepoints, `shape=(N,)`.
        bc_indx (:obj:`float`): Index of data point at which to enforce derivative value ```bc_deriv```. Defaults to zero.
        bc_deriv (:obj:`float`): Value of first derivative to enforce at times[```bc_indx```]. Defaults to zero.

    """

    def __init__(self, times, data, bc_indx=0, bc_deriv=0):
        assert np.allclose( np.sort(times), times ), "Input times needs to be sorted"
        self._times = times
        self._data  = data
        self._Mi = np.array([ [-1,  1,  -1],
                             [ 0,  0,   1],
                             [ 1,  0,   0]] )
        self._point_derivatives = self._get_point_derivatives( times, data, bc_indx, bc_deriv )

    def _get_point_derivatives(self, times, data, bc_indx, bc_deriv):
        """Find the first derivatives at the given timepoints to speed up later interpolation evaluation.

        The idea is to solve for the values as a chain starting with setting the first derivative to `bc_start`,
        fitting the first quadratic. The derivative of this interval at the endpoint is then passed along
        to fit the next quadratic and so on. Only the derivatives are saved rather than the polynomial
        coefficents to save memory. The resulting point derivatives are expressed in the local basis, i.e
        each polynomial exist on a [0,1] intervall mapping.

        Args:
            times (:obj:`numpy array`): Time points at which the data was collected, `shape=(N,)`.
            data (:obj:`numpy array`): The scalar data values collected at the timepoints, `shape=(N,)`.

        Returns:
            point_derivatives (:obj:`numpy array`): Derivatives of the piecewise polynomial at input times, `shape=(N,)`.

        """

        point_derivatives = np.zeros((len(times),))

        point_derivatives[bc_indx] = bc_deriv * (times[bc_indx+1] - times[bc_indx]) # local basis scale
        # NOTE: The point derivatives are in the local basis scale in a forward sense. i.e point_derivatives[i]
        # is in the scale of region i, which has lenght dt = times[i+1] - times[i].

        base = 2*( data[1:] - data[:-1] )
        for region in range(bc_indx, len(times)-2, 1): # forward marching +
            # Here we may first solve, and then convert to the local scale of derivative
            point_derivatives[region+1] = (base[region] - point_derivatives[region]) * ( (times[region+2] - times[region+1]) / (times[region+1] - times[region]) )

        for region in range(bc_indx, 0, -1 ): # backwards marching -
            # Here we must first convert to local derivative scale and then solve.
            point_derivatives[region-1] = base[region-1] - point_derivatives[region] * ( (times[region] - times[region-1]) / (times[region+1] - times[region]) )

        return point_derivatives

    def _time_to_intervall(self, time):
        """Find integer index of polynomial interval containing input time.
        """
        indx = np.argsort(np.abs(self._times-time))[0]
        if self._times[0] < time <= self._times[indx]:
            indx = indx - 1
        return indx

    def __call__(self, t, derivative=0):
        """Evaluate the piecewise polynomial or any of its derivatives at input points.

        Args:
            t (:obj:`iterable`): Time points at which to evaluate the piecewise polynomial.
            derivative (:obj:`int`): Order of desired derivative to evaluate. Default is 0.

        Returns:
            (:obj:`numpy array`) the polynomial values at input times.

        """
        out = np.zeros((len(t),))
        for i in range(len(t)):

            indx = self._time_to_intervall(t[i])
            c = self._Mi.dot( np.array([self._data[indx], self._data[indx+1], self._point_derivatives[indx]]) )
            xpar = (t[i] - self._times[indx]) / (self._times[indx+1] - self._times[indx])

            assert xpar>=0.0 and xpar<=1.0, "xpar="+str(xpar)+" _times[indx+2]="+str(self._times[indx+2])+" _times[indx+1]="+str(self._times[indx+1])+" _times[indx]="+str(self._times[indx])+" t[i]="+str(t[i])

            if derivative==0:
                out[i] = c[0]*xpar**2 + c[1]*xpar + c[2]
            elif derivative==1:
                out[i] = (2*c[0]*xpar + c[1]) / (self._times[indx+1] - self._times[indx])
            elif derivative==2:
                out[i] = 2*c[0] / (self._times[indx+1] - self._times[indx])**2
            else:
                out[i] = 0

        return out


import numpy as np

def sigmoid(t):
    """
    Applies the sigmoid function elementwise to the input data.

    Parameters
    ----------
    t : array, arbitrary shape
        Input data.

    Returns
    -------
    t_sigmoid : array, arbitrary shape.
        Data after applying the sigmoid function.
    """
    return 1/(1+np.exp(-t))

def negative_log_likelihood(X, y, w):
    """
    Negative Log Likelihood of the Logistic Regression.

    Parameters
    ----------
    X : array, shape [N, D]
        (Augmented) feature matrix.
    y : array, shape [N]
        Classification targets.
    w : array, shape [D]
        Regression coefficients (w[0] is the bias term).

    Returns
    -------
    nll : float
        The negative log likelihood.
    """
    return np.sum(-y*(X@w)+np.log(1+np.exp(X@w)))

def compute_loss(X, y, w, lmbda):
    """
    Negative Log Likelihood of the Logistic Regression.

    Parameters
    ----------
    X : array, shape [N, D]
        (Augmented) feature matrix.
    y : array, shape [N]
        Classification targets.
    w : array, shape [D]
        Regression coefficients (w[0] is the bias term).
    lmbda : float
        L2 regularization strength.

    Returns
    -------
    loss : float
        Loss of the regularized logistic regression model.
    """
    # The bias term w[0] is not regularized by convention
    return negative_log_likelihood(
        X, y, w) / len(y) + lmbda * 0.5 * np.linalg.norm(w[1:]) ** 2

def get_gradient(X, y, w, mini_batch_indices, lmbda):
    """
    Calculates the gradient (full or mini-batch) of the negative log likelilhood w.r.t. w.

    Parameters
    ----------
    X : array, shape [N, D]
        (Augmented) feature matrix.
    y : array, shape [N]
        Classification targets.
    w : array, shape [D]
        Regression coefficients (w[0] is the bias term).
    mini_batch_indices: array, shape [mini_batch_size]
        The indices of the data points to be included in the (stochastic) calculation of the gradient.
        This includes the full batch gradient as well, if mini_batch_indices = np.arange(n_train).
    lmbda: float
        Regularization strentgh. lmbda = 0 means having no regularization.

    Returns
    -------
    dw : array, shape [D]
        Gradient w.r.t. w.
    """
    sample_size = y.shape[0]
    batch_size = mini_batch_indices.shape[0]
    X = X[mini_batch_indices]
    y = y[mini_batch_indices]
    return -sample_size / batch_size * (X.T @ (y - 1 / (1 + np.exp(-X @ w))))

def predict(X, w):
    """
    Parameters
    ----------
    X : array, shape [N_test, D]
        (Augmented) feature matrix.
    w : array, shape [D]
        Regression coefficients (w[0] is the bias term).

    Returns
    -------
    y_pred : array, shape [N_test]
        A binary array of predictions.
    """
    y_pred = sigmoid(X @ w)
    y_pred[y_pred <= 0.5] = 0
    y_pred[y_pred > 0.5] = 1
    return y_pred


import numpy as np

def decimal_to_fixed_p (num, width, int_bit, frac_bit):
    '''
    given number, width, integer bit and fractinal bit,
    returns the fixed point representation.
    example: decimal_to_fixed_p (11.125,8,5,3) returns 01011001 = 2^3+2^1+2^0+2^(-3)
    precondition: There is no overflow
    (integer part of the number should be representable with int_bit number of bits )
    '''
    # seperate to int and fractional parts
    intg , frac = str(num).split(".")
    int_b = np.binary_repr(int(intg), width = int_bit)
    frac = "0."+frac
    # multiply fractional part with 2**frac_bit to turn into integer
    # frac= int(float(frac) * float(2**frac_bit))
    frac = float(frac) * float(2**frac_bit)
    _, f = str(frac).split(".")
    # raises Exception when the number can't be represented as fixed numbers
    if f != "0":
        raise Exception("number can't be represented as fixedpoint numbers")
    frac = int(frac)
    frac_b = np.binary_repr(frac, width = frac_bit)
    r = int_b + frac_b
    return r

def fixed_p_to_decimal (fp, width, int_bit, frac_bit):
    '''
    given fixedpoint representation, width, integer bit and fractinal bit,
    returns the number.
    example: fixed_p_to_decimal ('01011001',8,5,3) returns 11.125
    '''
    int_b = fp[:int_bit]
    frac_b = fp[int_bit:width]
    int_num = int(int_b, 2)
    frac = int(frac_b, 2)
    frac_num = float(frac / 2**(frac_bit))
    num = float(int_num +frac_num)
    return num


'''
Created on 2017. 4. 6.

@author: Byoungho Kang
'''

import numpy as np
import matplotlib.pyplot as plt

'''
    계단함수 구현
    입력파라미터 numpy.ndarray
    출력파라미터 numpy.ndarray
'''
def stepFunction(x):
    y = [] # Empty List
    for value in x:
        if value > 0:
            y.append(1)
        else:
            y.append(0)

    return np.array(y)

'''
    시그모이드함수 구현
    입력파라미터 numpy.ndarray
    출력파라미터 numpy.ndarray
    np.exp(-x) : x가 numpy.ndarray type이라도 broadcast 되어 연산됨.
'''
def sigmoidFunction(x):
    return 1/(1+np.exp(-x))

'''
    Rectified Linear Unit 함수 구현
    입력파라미터 numpy.ndarray
    출력파라미터 numpy.ndarray
'''
def reluFunction(x):
    return np.maximum(0, x)

'''
    Softmax함수 구현
    입력파라미터 numpy.ndarray
    출력파라미터 numpy.ndarray
'''
def softmaxFunction(x):
    expX = np.exp(x - np.max(x))    # Overflow 대비를 위해 원소 중 최대값을 각 입력 신호에서 빼서 예방한다.
    sumExpX = np.sum(expX)          # 지수 함수 결과의 합
    return expX / sumExpX

x = np.arange(-5, 5, 0.01)
y1 = stepFunction(x)
y2 = sigmoidFunction(x)
y3 = reluFunction(x)

a = np.array([2.3, -0.9, 3.6])
y4 = softmaxFunction(a)
print(y4, np.sum(y4))

a = np.array([900, 1000, 1000])
y4 = softmaxFunction(a)
print(y4, np.sum(y4))

plt.plot(x, y1, label="Step Function"   ,linestyle="-")
plt.plot(x, y2, label="Sigmoid Function",linestyle="--")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Step & Sigmoid")
plt.legend()
plt.show()

plt.plot(x, y3, label="ReLU Function"   ,linestyle="-.")
plt.xlabel("x")
plt.ylabel("y")
plt.title("ReLU Function")
plt.legend()
plt.show()


import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

class LogReg():
      """
      Logistic Regression (Gradient Descent) implemented in Python

      Χ: input data
      y: target value
      y_hat: prediction
      w: weights
      b: bias
      m: training examples
      n:number of features
      """

    def __init__(self, X, y, iters=1000, learning_rate=10):
        self.X = X
        self.y = y
        self.m, self.n = self.X.shape
        self.learning_rate = learning_rate
        self.iters = iters

    def sigmoid(self, x):
        z = 1/(1+np.exp(-x))
        return z

    def cost_function(self, y, y_hat):
        cost = -np.mean((y*np.log(y_hat))-((1-y)*np.log(1-y_hat)))
        return cost

    def grads(self, X, y, y_hat):

        # Gradient of weights
        dw = (1/self.m)*np.dot(X.T, (y_hat - y))

        # Gradient of bias
        db = (1/self.m)*np.sum((y_hat - y))

        return dw, db

    def fit(self):

        #initialize weights and bias with zero
        self.w = np.zeros((self.n,1))
        self.b = 0

        #reshape y
        self.y = self.y.reshape(self.m,1)

        #normalize X
        self.X = self.X / np.linalg.norm(self.X)

        ovr_cost = []

        for iteration in range(self.iters):

            #calculate predictions
            y_hat = self.sigmoid(np.dot(self.X,self.w) + self.b)

            #calculate gradients
            dw,db = self.grads(self.X, self.y, y_hat)

            #update parameters
            self.w -= self.learning_rate * dw
            self.b -= self.learning_rate * db

            #calculate cost
            cost = self.cost_function(self.y, self.sigmoid(np.dot(self.X, self.w) + self.b))

            ovr_cost.append(round(cost,5))

        return ovr_cost

    def predict(self,X):

        #normalize X
        X = X / np.linalg.norm(X)

        preds = self.sigmoid(np.dot(X,self.w) + self.b)

        return [1 if pred>0.5 else 0 for pred in preds]

if __name__ == '__main__':

    X, y = make_classification(n_samples=1000,n_features=5, n_redundant=1,
                           n_informative=2, random_state=7)

    X_train, X_test, y_train, y_test = train_test_split(
             X, y, test_size = 0.2, random_state=7)

    LR = LogReg(X_train, y_train)
    LR.fit()
    y_pred = LR.predict(X_test)
    accuracy = np.mean(y_test==y_pred)

    print("Accuracy: {:.2%}".format(accuracy))