# This function does the Gram-Schmidt process
# It transforms a matrix into a matrix of orthonormal vectors

import numpy as np
import numpy.linalg as la

# A => Matrix
def gsBasis(A):
  verySmallNumber = 1e-14
  B = np.array(A, dtype=np.float_)

  # loop over vectors in the matrix (column-wise)
  for i in range(B.shape[1]):
    # loop over all previous vectors before 'i'
    for j in range(i):
      # do the GS procedure
      B[:,i] = B[:,i] - (B[:,i] @ B[:,j]) * B[:,j]

    # if vector is linearly independent, normalize
    if la.norm(B[:,i]) > verySmallNumber:
      B[:, i] = B[:, i] / la.norm(B[:, i])
    else:
      B[:,i] = np.zeros_like(B[:,i])

  return B

# Some matrix definition
V = np.array([[1,0,2,6],
              [0,1,8,2],
              [2,8,3,1],
              [1,-6,2,3]], dtype=np.float_)

# A non-square matrix
U = np.array([[3,2,3],
              [2,5,-1],
              [2,4,8],
              [12, 2, 1]], dtype=np.float_)

print(gsBasis(V))
Normed = gsBasis(V)

# gsBasis on an orthonormal matrix returns itself
print(gsBasis(Normed))

def dimensions(A) :
    return np.sum(la.norm(gsBasis(A), axis=0))

print(dimensions(V))

# Now let's see what happens when we have one vector that is a linear combination of the others.
C = np.array([[1,0,2],
              [0,1,-3],
              [1,0,2]], dtype=np.float_)
gsBasis(C)


from scipy.stats import norm
from scipy.integrate import quad
import numpy as np

def d2(n=2, method="exact"):
    """
    Computes the d2 statistic used for measuring
    relative range standard deviation approximation.

    The d2 statistic is often found in statistical process
    control tables.

    That is, standard deviation can be approximated by
    R / d2 where R is the range of the data (max - min)
    and d2 is computed as below.

    See also:
    https://v8doc.sas.com/sashtml/qc/chapc/sect9.htm

    :param n:
        Number of distributions considered for computing
        d2. All are normal with mean=0 and std=1.
    :param method:
        Optional. Method to use. Default "exact".
            - "exact" computes the exact infinite integral.
            - "random" uses normal distributions generated
              on the fly to compute d2.
    :return:
        d2:
            the expectation value [that is, average here]
            of the ranges of the distributions
    """
    if method == "exact":
        def f(x, n):
            return 1 - (1 - norm.cdf(x)) ** n - (norm.cdf(x)) ** n

        d2 = quad(f, -np.inf, np.inf, args=(n))[0]
        return d2

    elif method == "random":
        x = {}
        # slots to fill with normally distributed samples
        for i in range(n):
            x[i] = norm.rvs(size=100000, loc=0, scale=1)

        x = np.vstack([x[i] for i in x])

        maxs = np.amax(x, axis=0)
        mins = np.amin(x, axis=0)

        r = maxs - mins

        d2 = np.average(r)
        return d2


import rotation
import numpy as np

def sphericalToCartesian(rho, lat, lon):
    phi = lat
    the = lon
    x = rho * np.cos(phi) * np.cos(the)
    y = rho * np.cos(phi) * np.sin(the)
    z = rho * np.sin(phi)
    return np.array([[x],
                    [y],
                    [z]])

def cartesianToSpherical(x,y,z):
    pho = np.sqrt(x**2 + y**2 + z**2)
    lon = np.arctan2(y,x)
    base = np.sqrt(x**2 + y**2)
    lat = np.arctan2(z, base)

    return (pho, (180/np.pi)*lat, (180/np.pi)*lon)

def localToWorldMatrix(lat, lon):
    lamda = lon * np.pi/180
    phi = lat * np.pi/180
    R = np.array([[-np.sin(lamda), -np.sin(phi) * np.cos(lamda), np.cos(phi) * np.cos(lamda)],
                [np.cos(lamda), -np.sin(phi) * np.sin(lamda), np.cos(phi) * np.sin(lamda)],
                [0, np.cos(phi), np.sin(phi)]])
    return R

def worldToLocalMatrix(lat, lon):
    # phi = lat
    # the = lon
    # R = rotation.RotX(180) * rotation.RotZ(90) * rotation.RotX(phi) * rotation.RotZ(the)
    R = localToWorldMatrix(lat, lon)
    return np.linalg.inv(R)

def pointWorldToLocal(p1_w, p0_w, R_wl):
    return R_wl * (p1_w - p0_w)

def pointLocalToWorld(p1_l, p0_w, R_lw):
    return R_lw * p1_l + p0_w

# if __name__ == "__main__":
#     R = localToWorldMatrix(30,60)
#     print(R)


# -*- coding: utf-8 -*-
"""
Created on Thu Dec  2 13:39:48 2021

@author: Oliver

"""

"""Roots of Equation Systems"""
import math

import numpy as np

"""Finding roots of multi-variable equations"""

x = 1.5
y = 3.5

dudx = lambda x, y: 2*x+y
dvdx = lambda y: 3*y**2
dudy = lambda x: x
dvdy = lambda x, y: 1+6*x*y

a = np.array([[dudx(x,y),dudy(x)], [dvdx(y),dvdy(x,y)]]) # Jacobian Matrix

initial_u_guess = lambda x, y: x**2 + x*y - 10
initial_v_guess = lambda x, y: y + 3*x*y**2 -57

x_new = x - ((initial_u_guess(x, y)*dvdy(x,y) - initial_v_guess(x, y)*x))/np.linalg.det(a)
y_new = y - (initial_v_guess(x, y)*dudx(x,y) - (initial_u_guess(x, y)*dvdx(y)))/np.linalg.det(a)
x_sol = 2
y_sol = 3
counter = 0
error = 0

while True:
    counter += 1
    x_new = x - ((initial_u_guess(x, y)*dvdy(x,y) - initial_v_guess(x, y)*x))/np.linalg.det(a)
    y_new = y - (initial_v_guess(x, y)*dudx(x,y) - (initial_u_guess(x, y)*dvdx(y)))/np.linalg.det(a)

    if x_sol - error<=x_new<=x_sol + error:
        print(f'The solution has been found after {counter} iterations and the values are x: {x_new} and y: {y_new}')
        break
    if counter == 3:
        print(f'The solution after {counter} iterations is: x: {x_new}, y: {y_new} with relative error in: x: {abs(x_new-x_sol)/x_sol} and y: {abs(y_new-y_sol)/y_sol}')
    else:
        x = x_new
        y = y_new

"""Roots of Polynomial Equations"""
# Factorization of Polynomials

f = [1,2,-24]
guess = 4
values =[f[0]]
z = f[0]
for i in range(0,len(f)-2):
    x = f[i+1] + z*guess
    print(x)
    z = x
    values.append(x)
print(values)

"""Polynomial Division"""

def expanded_synthetic_division(dividend, divisor):
    """Fast polynomial division by using Expanded Synthetic Division.
    Also works with non-monic polynomials.

    Dividend and divisor are both polynomials, which are here simply lists of coefficients.
    E.g.: x**2 + 3*x + 5 will be represented as [1, 3, 5]
    """
    out = list(dividend)  # Copy the dividend
    normalizer = divisor[0]
    for i in range(len(dividend) - len(divisor) + 1):
        # For general polynomial division (when polynomials are non-monic),
        # we need to normalize by dividing the coefficient with the divisor's first coefficient
        out[i] /= normalizer

        coef = out[i]
        if coef != 0:  # Useless to multiply if coef is 0
            # In synthetic division, we always skip the first coefficient of the divisor,
            # because it is only used to normalize the dividend coefficients
            for j in range(1, len(divisor)):
                out[i + j] += -divisor[j] * coef

    # The resulting out contains both the quotient and the remainder,
    # the remainder being the size of the divisor (the remainder
    # has necessarily the same degree as the divisor since it is
    # what we couldn't divide from the dividend), so we compute the index
    # where this separation is, and return the quotient and remainder.
    separator = 1 - len(divisor)
    return out[:separator], out[separator:]  # Return quotient, remainder.

if __name__=='__main__':
    print ("POLYNOMINAL SYNTHETIC DIVISION")
    N = [1, 2, -24]
    D = [1, -4]
    print (" %s /%s =" % (N,D),)
    print (" %s remainder %s" % expanded_synthetic_division(N, D))

"""Muller's Technique - Finding Roots"""

def f(x):
    return x**3 - 13*x - 12

def Muller(x_r, h, eps, maxit):
    iter = 0
    x2 = x_r
    x1 = x_r + h * x_r
    x0 = x_r - h * x_r
    while True:
        iter += 1
        h0 = x1 - x0
        h1 = x2 - x1
        d0 = (f(x1) - f(x0)) / h0
        d1 = (f(x2) - f(x1)) / h1
        a = (d1 - d0) / (h1 + h0)
        b = a * h1 + d1
        c = f(x2)
        rad = math.sqrt(abs(b**2 - 4*a*c))
        if abs(b + rad) > abs(b - rad):
            den = b + rad
        else:
            den = b - rad
        dx_r = -2 * c / den
        x_r = x2 + dx_r
        print(f'Iteration: {iter}, Value of root: {x_r}, Error: {abs((x2/x_r*100)-100)}%')
        if (abs(dx_r) < eps * x_r or iter >= maxit):
            break
        x0 = x1
        x1 = x2
        x2 = x_r
    return None
x_r = 20
h = 0.1
eps = 0.01
maxit = 10

solution = Muller(x_r, h, eps, maxit)
print(solution)


# Нахождение прямой, проходящей через набор точек
# Дмитрий Хизбуллин, 2021

import math
import numpy as np
from typing import Optional, Any
import matplotlib.pyplot as plt

def generate_points(axis: Optional[Any] = None) -> np.ndarray:
    """
    Функция, генерирующая массив точек, приблизительно лежащих на
    отрезке прямой.

    :param axis: Набор осей, на которых рисовать график
    :return: Numpy массив формы [N, 2] точек на прямой
    """

    # Давайте сгенерируем 10 точек, лежащих на прямой
    num_points = 10
    p_initial = np.array((-2, 3))
    speed = 1.0
    angle_degrees = -30
    angle_radians = angle_degrees / 180 * math.pi
    velocity = speed * np.array((math.cos(angle_radians),
                                 math.sin(angle_radians)))
    ideal_points = np.expand_dims(p_initial, 0) + \
                   np.outer(np.arange(0, num_points), velocity)
    # и добавим немного шума, чтобы промоделировать реальные измерения
    noise = 0.1 * np.random.randn(num_points, 2)
    points = ideal_points + noise

    if axis is not None:
        for ax in axis:
            ax.set_title("Входной набор точек")
            ax.plot(points[:, 0], points[:, 1], 'or')
            ax.grid(True, linestyle='--')
            ax.axis('equal')

    return points

def least_squares(points: np.ndarray, axis: Optional[Any] = None) \
        -> np.ndarray:
    """
    Функция для аппроксимации массива точек прямой, основанная на
    методе наименьших квадратов.

    :param points: Входной массив точек формы [N, 2]
    :param axis: Набор осей, на которых рисовать график
    :return: Numpy массив формы [N, 2] точек на прямой
    """

    x = points[:, 0]
    y = points[:, 1]
    # Для метода наименьших квадратов нам нужно, чтобы X был матрицей,
    # в которой первый столбец - единицы, а второй - x координаты точек
    X = np.vstack((np.ones(x.shape[0]), x)).T
    normal_matrix = np.dot(X.T, X)
    moment_matrix = np.dot(X.T, y)
    # beta_hat это вектор [перехват, наклон], рассчитываем его в
    # в соответствии с формулой.
    beta_hat = np.dot(np.linalg.inv(normal_matrix), moment_matrix)
    intercept = beta_hat[0]
    slope = beta_hat[1]
    # Теперь, когда мы знаем параметры прямой, мы можем
    # легко вычислить y координаты точек на прямой.
    y_hat = intercept + slope * x
    # Соберем x и y в единую матрицу, которую мы собираемся вернуть
    # в качестве результата.
    points_hat = np.vstack((x, y_hat)).T

    if axis is not None:
        for ax in axis:
            ax.set_title("Метод наименьших квадратов")
            ax.plot(x, y, 'or')
            ax.plot(x, y_hat, 'o-', mfc='none')
            ax.grid(True, linestyle='--')
            ax.axis('equal')

    return points_hat

def ransac(points: np.ndarray,
           min_inliers: int = 4,
           max_distance: float = 0.15,
           outliers_fraction: float = 0.5,
           probability_of_success: float = 0.99,
           axis: Optional[Any] = None) -> Optional[np.ndarray]:
    """
    RANdom SAmple Consensus метод нахождения наилучшей
    аппроксимирующей прямой.

    :param points: Входной массив точек формы [N, 2]
    :param min_inliers: Минимальное количество не-выбросов
    :param max_distance: максимальное расстояние до поддерживающей прямой,
                         чтобы точка считалась не-выбросом
    :param outliers_fraction: Ожидаемая доля выбросов
    :param probability_of_success: желаемая вероятность, что поддерживающая
                                   прямая не основана на точке-выбросе
    :param axis: Набор осей, на которых рисовать график
    :return: Numpy массив формы [N, 2] точек на прямой,
             None, если ответ не найден.
    """

    # Давайте вычислим необходимое количество итераций
    num_trials = int(math.log(1 - probability_of_success) /
                     math.log(1 - outliers_fraction**2))

    best_num_inliers = 0
    best_support = None
    for _ in range(num_trials):
        # В каждой итерации случайным образом выбираем две точки
        # из входного массива и называем их "суппорт"
        random_indices = np.random.choice(
            np.arange(0, len(points)), size=(2,), replace=False)
        assert random_indices[0] != random_indices[1]
        support = np.take(points, random_indices, axis=0)

        # Здесь мы считаем расстояния от всех точек до прямой
        # заданной суппортом. Для расчета расстояний от точки до
        # прямой подходит функция векторного произведения.
        # Особенность np.cross в том, что функция возвращает только
        # z координату векторного произведения, а она-то нам и нужна.
        cross_prod = np.cross(support[1, :] - support[0, :],
                              support[1, :] - points)
        support_length = np.linalg.norm(support[1, :] - support[0, :])
        # cross_prod содержит знаковое расстояние, поэтому нам нужно
        # взять модуль значений.
        distances = np.abs(cross_prod) / support_length

        # Не-выбросы - это все точки, которые ближе, чем max_distance
        # к нашей прямой-кандидату.
        num_inliers = np.sum(distances < max_distance)
        # Здесь мы обновляем лучший найденный суппорт
        if num_inliers >= min_inliers and num_inliers > best_num_inliers:
            best_num_inliers = num_inliers
            best_support = support

    # Если мы успешно нашли хотя бы один суппорт,
    # удовлетворяющий всем требованиям
    if best_support is not None:
        # Спроецируем точки из входного массива на найденную прямую
        support_start = best_support[0]
        support_vec = best_support[1] - best_support[0]
        # Для расчета проекций отлично подходит функция
        # скалярного произведения.
        offsets = np.dot(support_vec, (points - support_start).T)
        proj_vectors = np.outer(support_vec, offsets).T
        support_sq_len = np.inner(support_vec, support_vec)
        projected_vectors = proj_vectors / support_sq_len
        projected_points = support_start + projected_vectors

        if axis is not None:
            for ax in axis:
                ax.set_title("RANSAC")
                ax.scatter(best_support[:, 0], best_support[:, 1],
                            s=200, facecolors='none', edgecolors='k', marker='s')
                ax.plot(points[:, 0], points[:, 1], 'or')
                ax.plot(projected_points[:, 0], projected_points[:, 1],
                         'o-', mfc='none')
                ax.grid(True, linestyle='--')
                ax.axis('equal')
    else:
        projected_points = None

    return projected_points

def pca(points: np.ndarray, axis: Optional[Any] = None) -> np.ndarray:
    """
    Метод главных компонент (PCA) оценки направления
    максимальной дисперсии облака точек.

    :param points: Входной массив точек формы [N, 2]
    :param axis: Набор осей, на которых рисовать график
    :return: Numpy массив формы [N, 2] точек на прямой
    """

    # Найдем главные компоненты.
    # В первую очередь нужно центрировать облако точек, вычтя среднее
    mean = np.mean(points, axis=0)
    centered = points - mean
    # Функция вычисления собственных значений и векторов np.linalg.eig
    # требует ковариационную матрицу в качестве аргумента.
    cov = np.cov(centered.T)
    # Теперь мы можем посчитать главные компоненты, заданные
    # собственными значениями и собственными векторами.
    eigenval, eigenvec = np.linalg.eig(cov)
    # Мы хотим параметризовать целевую прямую в координатной системе,
    # заданной собственным вектором, собственное значение которого
    # наиболее велико (направление наибольшей вариативности).
    argmax_eigen = np.argmax(eigenval)
    # Нам понадобятся проекции входных точек на наибольший собственный
    # вектор.
    loc_pca = np.dot(centered, eigenvec)
    loc_maxeigen = loc_pca[:, argmax_eigen]
    max_eigenval = eigenval[argmax_eigen]
    max_eigenvec = eigenvec[:, argmax_eigen]
    # Ре-параметризуем прямую, взяв за начало отрезка проекции
    # первой и последней точки на прямую.
    loc_start = mean + max_eigenvec * loc_maxeigen[0]
    loc_final = mean + max_eigenvec * loc_maxeigen[-1]
    linspace = np.linspace(0, 1, num=len(points))
    # Получаем позиции точек, которые идут с одинаковым интервалом,
    # таким образом удаляя шум измерений и вдоль траектории движения.
    positions = loc_start + np.outer(linspace, loc_final - loc_start)

    if axis is not None:
        for ax in axis:
            ax.set_title("PCA")
            ax.plot(points[:, 0], points[:, 1], 'or')
            ax.plot(positions[:, 0], positions[:, 1], 'o-', mfc='none')
            ax.grid(True, linestyle='--')
            ax.axis('equal')

    return positions

def main():
    fig_all, axs = plt.subplots(2, 2)
    fig_all.set_size_inches(10.0, 6.0)

    fig, ax = plt.subplots(1, 1)
    fig.set_size_inches(5.0, 3.0)
    points = generate_points(axis=(axs[0, 0], ax))
    fig.savefig('1_points.png', dpi=300)

    fig, ax = plt.subplots(1, 1)
    fig.set_size_inches(5.0, 3.0)
    least_squares(points, axis=(axs[0, 1], ax))
    fig.savefig('2_leastsq.png', dpi=300)

    fig, ax = plt.subplots(1, 1)
    fig.set_size_inches(5.0, 3.0)
    ransac(points, axis=(axs[1, 0], ax))
    fig.savefig('3_ransac.png', dpi=300)

    fig, ax = plt.subplots(1, 1)
    fig.set_size_inches(5.0, 3.0)
    pca(points, axis=(axs[1, 1], ax))
    fig.savefig('4_pca.png', dpi=300)

    for ax in axs.flat:
        ax.set(xlabel='x', ylabel='y')

    for ax in axs.flat:
        ax.label_outer()

    fig_all.savefig('0_all.png', dpi=300)

    plt.show()

    print("Done!")

if __name__ == "__main__":
    main()


# -*- coding: utf-8 -*-
"""
Created on Wed Apr  8 16:49:15 2020

@author: Mehul
"""
from statistics import mean
import numpy as np
import random

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

#creating a dataset for testing
def create_dataset(hm,variance,step=2,correlation=False,which_type='pos'):
	val=1
	ys=[]
	for i in range(hm):
		y=val+random.randrange(-variance,variance)
		ys.append(y)
		if correlation==True:
			if which_type=='pos':
				val+=step
			elif which_type=='neg':
				val-=val
	xs=[i for i in range(len(ys))]
	return np.array(xs,dtype=np.float64),np.array(ys,dtype=np.float64)

#defining a function to get the best fit slope
#here we pass in the array of points in the function and get the slope anf the intercept
def best_fit_slope_and_intercept(xs,ys):
	m =(((mean(xs)*mean(ys))-mean(xs*ys))/((mean(xs)**2)-mean(xs*xs)))
	b=mean(ys)-m*mean(xs)
	return m,b

# defining function to predict the outcome from the model
def predict_outcome(x_predict,m,b):
	y=m*x_predict+b
	return y

#defining a function for measuring r-squared
def sum_of_squared_error(ys_original,ys_line):
	return sum((ys_original-ys_line)**2)

def r_squared_value(ys_original,ys_line):
	ys_mean=[mean(ys_original) for y in ys_original]
	squared_error_regression_line=sum_of_squared_error(ys_original,ys_line)
	squared_error_mean_line=sum_of_squared_error(ys_original,ys_mean)
	return (1-(squared_error_regression_line/squared_error_mean_line))

#getting dataset
xs,ys=create_dataset(40,10,5,True,'pos')

#getting the slope and the intercept
m,b=best_fit_slope_and_intercept(xs,ys)

#making the regression line list using list comprehension
regression_line=[(m*x+b) for x in xs]

#making prediction using the data
x_predict=8
y_predict=predict_outcome(x_predict,m,b)

# r-squared and checking whether the best fit line really fits our  data well or not
#We can have a best fit line for a poorly fit dataset also, the point is to have a metric
#can actually tell us whether th line represents the actual relationship and explains a significant
#percentage of the variation.
r_squared=r_squared_value(ys,regression_line)

#visualise the results
from matplotlib import style
import matplotlib.pyplot as plt
style.use('fivethirtyeight')
plt.scatter(xs,ys,color='red')
plt.plot(xs,regression_line)# this connects the points and gives us a line
plt.scatter(x_predict,y_predict,s=100,color='green')
plt.show()

Simple Linear Regression/Simple Linear Regression from scratch/linear_regression_from_scratch.py


# Python3 steven
#https://en.wikipedia.org/wiki/Mandelbrot_set
import sys
import numpy as np
import matplotlib.pyplot as plt
# MandelBrot eqaution： z:=z**2 + c

Z0 = 0 + 0j #start value

def mandelbrot(N=10):  #generate madenlbrot series
    sets=[]
    c = complex(.1, .2)
    a = c
    while N>0:
        print(abs(a),' ',end='')
        sets.append(abs(a))
        a = a**2 + c

        N -= 1
    return sets

def yieldMandelbrot(N):  #binay oper only yield >2.0
    xvalues = np.linspace(-2, 2, N)
    yvalues = np.linspace(-2, 2, N)
    for u, x in enumerate(xvalues):
        for v, y in enumerate(yvalues):
            z = Z0
            c = complex(x, y)
            for _ in range(100):
                z = z * z + c
                if abs(z) > 2.0:
                    yield v,u,abs(z)
                    break

def yieldMandelbrotAll(N):
    xvalues = np.linspace(-2, 2, N)
    yvalues = np.linspace(-2, 2, N)
    for u, x in enumerate(xvalues):
        for v, y in enumerate(yvalues):
            z = Z0
            c = complex(x, y)
            for _ in range(100):
                z = z * z + c
                if abs(z) > 2.0:
                    break
                yield v,u,abs(z)

def genMandelbrotColor(N=1000):  #color image,3 channels
    M = np.zeros([N, N,3], int) # + 255
    for v,u,z in yieldMandelbrotAll(N): #map z(0~2) to 0~255 pixsel value
        #M[v, u, :] = int(z*256/2)
        #M[v, u, 0] = int(z*256/2) #r channel
        M[v, u, 1] = int(z*256/2) #g channel
        #M[v, u, 2] = int(z*256/2) #b channel
    return M

def genMandelbrotGray(N=1000):  #gray image[0~255]
    M = np.zeros([N, N], int)
    for v,u,z in yieldMandelbrotAll(N): #map z(0~2) to 0~255 pixsel value
        M[v, u] = int(z*256/2) #M[v, u] = 1
    return M

def genMandelbrot(N=1000):  #white&black two value[0,1] image
    M = np.zeros([N, N], int)
    if 1:
        for v,u,_ in yieldMandelbrot(N):
            M[v, u] = 1
        return M
    else:#First version
        xvalues = np.linspace(-2, 2, N)
        yvalues = np.linspace(-2, 2, N)
        for u, x in enumerate(xvalues):
            for v, y in enumerate(yvalues):
                z = 0 + 0j
                c = complex(x, y)
                for _ in range(100):
                    z = z * z + c
                    if abs(z) > 2.0:
                        M[v,u] = 1
                        break
        return M

def main():
    N = 1000

    print('number of parameter:', len(sys.argv))
    print('parameters:', str(sys.argv))
    if len(sys.argv)>1:
        N = int(sys.argv[1])

    #mandelbrot()
    plt.imshow(genMandelbrot(N),cmap='gray')
    #plt.imshow(genMandelbrotGray(N),cmap='gray')
    #plt.imshow(genMandelbrotColor(N))
    plt.title('Mandelbrot,N ='+str(N))
    plt.show()

if __name__=='__main__':
    main()


"""
This module provides basic utilities related to Point type,
such as comparison function between two points.
"""
import numpy as np
from myConvexHull.dtype import Point

Y = 1
"A constant index to access the y value of a point."
X = 0
"A constant index to access the x value of a point."

def less_than(a, b) -> bool:
    # type: (Point, Point) -> bool
    """
    Compares two points and returns true if the first point's
    x value is less than the second point's x value or both
    points have the same x value but the first point's y value
    is less than the second point's y value.

    Args:

    `a`: the first `Point`

    `b`: the second `Point`

    Return:

    `True` if the true condition is met, `False` otherwise.
    """
    return a[X] < b[X] or (a[X] == b[X] and a[Y] < b[Y])

def greater_than(a, b) -> bool:
    # type: (Point, Point) -> bool
    """
    Compares two points and returns true if the first point's
    x value is more than the second point's x value or both
    points have the same x value but the first point's y value
    is more than the second point's y value.

    Args:

    `a`: the first `Point`

    `b`: the second `Point`

    Return:

    `True` if the true condition is met, `False` otherwise.
    """
    return a[X] > b[X] or (a[X] == b[X] and a[Y] > b[Y])

def distance(a, b) -> float:
    # type: (Point, Point) -> float
    """
    Calculates the distance between two points.

    Args:

    `a`: the first `Point`

    `b`: the second `Point`

    Return:

    The distance between the specified points.
    """
    return np.sqrt((a[X] - b[X]) ** 2 + (a[Y] - b[Y]) ** 2)


# -*- coding: utf-8 -*-
"""
Created on Thu Sep  9 09:22:42 2021

@author: Diana Sofía Carrillo
"""
from numpy import *

def gaussseidel(a, b, x):
    n = len(x)
    for i in range(n):
        s = 0
        for j in range (n):
            if i != j:
                s = s + a[i][j]*x[j]
        x[i] = (b[i] - s)/a[i][i]
    return x

def gaussseideliteraciones(a, b, x, e, m):
    n = len(x)
    t = x.copy()
    for k in range (m):
        x = gaussseidel(a, b, x)
        d = linalg.norm(array(x)-array(t), inf)
        if d < e:
            return [x, k]
        else:
            t = x.copy()
    return [[],m]

a = [[5, 2, 0],[0, -13, 5],[1, 1, 7]]
b = [45.34, -44.66, 16.2]
x = [1, 1, 1]
e = 10E-6
m = 20 #Num máximo de iteraciones
[x, k]=gaussseideliteraciones(a, b, x, e, m)
print("Solución:", x)
print("Iteraciones:", k)

def error_relativo(v_obtenido, a, b):
    v_real = linalg.solve(a, b)
    rta = linalg.norm(v_obtenido - v_real) / linalg.norm(v_real)
    return rta

print ("Error relativo:", error_relativo(x, a, b))


#Romberg Integration

"""
Name - Suryabrata Das

Sem: V

College_Roll_NO: 703

Paper-code: CMSA DSE-IB

Registration No: A01-1112-117-003-2018

Examination roll_no: 2021151264

Subject: Numerical Methods (DSE-I)

"""
import numpy as np
import matplotlib.pyplot as plt
def f1(x):
    f1 = np.sin(x)
    return f1;

# trapezoidal rule
def trapezoid(f,a,b,N):
    h = (b-a)/N
    xi = np.linspace(a,b,N+1)
    fi = f(xi)
    s = 0.0
    for i in range(1,N):
        s = s + fi[i]
    s = (h/2)*(fi[0] + fi[N]) + h*s
    return s

# romberg method starts from here

def romberg(f,a,b,eps,nmax):
# f ... function to be integrated
# [a,b] ... integration interval
# eps ... desired accuracy
# nmax ... maximal order of Romberg method
    Q = np.zeros((nmax,nmax),float)
    converged = 0
    for i in range(0,nmax):
        N = 2**i
        Q[i,0] = trapezoid(f,a,b,N)
        for k in range(0,i):
            n = k + 2
            Q[i,k+1] = 1.0/(4**(n-1)-1)*(4**(n-1)*Q[i,k] - Q[i-1,k])
        if (i > 0):
            if (abs(Q[i,k+1] - Q[i,k]) < eps):
                converged = 1

                break
            print("Integral Value: ",Q[i,k+1])
# main program
a = 0.0;b = 1.0 # integration interval [a,b]
romberg(f1,a,b,1.0e-12,10)


import numpy
import numpy.random

from numpy import bincount,any,sqrt
from numpy.random import randint

def birthday(num_people=88, num_trials=1000000, target_number=3):
    """
    Solve the generalized birthday problem using Monte-Carlo simulation.

    Given num_people in a room what is the probability that target_number
    or more of them share the same birthday.

    Assume years have 365 days

    Returns mean, standard deviation of the estimate
    """
    n=0
    for i in range(num_trials):
        m=bincount(randint(1,366,num_people))
        if any(m>=target_number):
            n=n+1
    # Mean of the results
    p=n/num_trials
    # std of the results
    # This works since all data is 0,1 then data**2 = 0,1 as well
    q=sqrt(n*(1-p)/(num_trials-1))
    return(p,q)

# Return P(less than 3 people with same birthday)
# Note this is 1-p(from above) and only works for 3 birthdays
def foo2(num_people=88, num_trials=1000000):
    n=0
    for i in range(num_trials):
        m=bincount(randint(1,366,num_people))
        if any(m>2):
            n=n+1
    p=1-n/num_trials
    return(p)

if __name__ == "__main__":
    # Set this true to run some example code
    if False:
        www=[]
        # Set random seed for reproducible results
        numpy.random.seed(42)
        for i in range(10):
            www.append(birthday(87,1000000,3))
            print("Completed trial",i,"of 0..9")
        www=numpy.array(www)


# coding:utf-8

# 输入训练样本的特征以及目标值，分别存储在变量X_train与y_train之中。
X_train = [[6], [8], [10], [14], [18]]
y_train = [[7], [9], [13], [17.5], [18]]

# 从sklearn.linear_model中导入LinearRegression。
from sklearn.linear_model import LinearRegression

# 使用默认配置初始化线性回归模型。
regressor = LinearRegression()
# 直接以披萨的直径作为特征训练模型。
regressor.fit(X_train, y_train)

# 导入numpy并且重命名为np。
import numpy as np

# 在x轴上从0至25均匀采样100个数据点。
xx = np.linspace(0, 26, 100)
xx = xx.reshape(xx.shape[0], 1)
# 以上述100个数据点作为基准，预测回归直线。
yy = regressor.predict(xx)

# 对回归预测到的直线进行作图。
import matplotlib.pyplot as plt

plt.scatter(X_train, y_train)
plt1, = plt.plot(xx, yy, label="Degree=1")
plt.axis([0, 25, 0, 25])
plt.xlabel('Diameter of Pizza')
plt.ylabel('Price of Pizza')
plt.legend(handles=[plt1])
plt.show()

# 输出线性回归模型在训练样本上的R-squared值。
print 'The R-squared value of Linear Regressor performing on the training data is', regressor.score(X_train, y_train)

# 从sklearn.preproessing中导入多项式特征产生器
from sklearn.preprocessing import PolynomialFeatures

# 使用PolynominalFeatures(degree=2)映射出2次多项式特征，存储在变量X_train_poly2中。
poly2 = PolynomialFeatures(degree=2)
X_train_poly2 = poly2.fit_transform(X_train)

# 以线性回归器为基础，初始化回归模型。尽管特征的维度有提升，但是模型基础仍然是线性模型。
regressor_poly2 = LinearRegression()

# 对2次多项式回归模型进行训练。
regressor_poly2.fit(X_train_poly2, y_train)

# 从新映射绘图用x轴采样数据。
xx_poly2 = poly2.transform(xx)

# 使用2次多项式回归模型对应x轴采样数据进行回归预测。
yy_poly2 = regressor_poly2.predict(xx_poly2)

# 分别对训练数据点、线性回归直线、2次多项式回归曲线进行作图。
plt.scatter(X_train, y_train)

plt1, = plt.plot(xx, yy, label='Degree=1')
plt2, = plt.plot(xx, yy_poly2, label='Degree=2')

plt.axis([0, 25, 0, 25])
plt.xlabel('Diameter of Pizza')
plt.ylabel('Price of Pizza')
plt.legend(handles=[plt1, plt2])
plt.show()

# 输出2次多项式回归模型在训练样本上的R-squared值。
print 'The R-squared value of Polynominal Regressor (Degree=2) performing on the training data is', regressor_poly2.score(
    X_train_poly2, y_train)

# 从sklearn.preprocessing导入多项式特征生成器。
from sklearn.preprocessing import PolynomialFeatures

# 初始化4次多项式特征生成器。
poly4 = PolynomialFeatures(degree=4)

X_train_poly4 = poly4.fit_transform(X_train)

# 使用默认配置初始化4次多项式回归器。
regressor_poly4 = LinearRegression()
# 对4次多项式回归模型进行训练。
regressor_poly4.fit(X_train_poly4, y_train)

# 从新映射绘图用x轴采样数据。
xx_poly4 = poly4.transform(xx)
# 使用4次多项式回归模型对应x轴采样数据进行回归预测。
yy_poly4 = regressor_poly4.predict(xx_poly4)

# 分别对训练数据点、线性回归直线、2次多项式以及4次多项式回归曲线进行作图。
plt.scatter(X_train, y_train)
plt1, = plt.plot(xx, yy, label='Degree=1')
plt2, = plt.plot(xx, yy_poly2, label='Degree=2')

plt4, = plt.plot(xx, yy_poly4, label='Degree=4')
plt.axis([0, 25, 0, 25])
plt.xlabel('Diameter of Pizza')
plt.ylabel('Price of Pizza')
plt.legend(handles=[plt1, plt2, plt4])
plt.show()

print 'The R-squared value of Polynominal Regressor (Degree=4) performing on the training data is', regressor_poly4.score(
    X_train_poly4, y_train)

# 准备测试数据。
X_test = [[6], [8], [11], [16]]
y_test = [[8], [12], [15], [18]]

# 使用测试数据对线性回归模型的性能进行评估。
regressor.score(X_test, y_test)

# 使用测试数据对2次多项式回归模型的性能进行评估。
X_test_poly2 = poly2.transform(X_test)
regressor_poly2.score(X_test_poly2, y_test)

# 使用测试数据对4次多项式回归模型的性能进行评估。
X_test_poly4 = poly4.transform(X_test)
regressor_poly4.score(X_test_poly4, y_test)

# 从sklearn.linear_model中导入Lasso。
from sklearn.linear_model import Lasso

# 从使用默认配置初始化Lasso。
lasso_poly4 = Lasso()
# 从使用Lasso对4次多项式特征进行拟合。
lasso_poly4.fit(X_train_poly4, y_train)

# 对Lasso模型在测试样本上的回归性能进行评估。
print lasso_poly4.score(X_test_poly4, y_test)

# 输出Lasso模型的参数列表。
print lasso_poly4.coef_

# 回顾普通4次多项式回归模型过拟合之后的性能。
print regressor_poly4.score(X_test_poly4, y_test)

# 回顾普通4次多项式回归模型的参数列表。
print regressor_poly4.coef_

# 输出普通4次多项式回归模型的参数列表。
print regressor_poly4.coef_

# 输出上述这些参数的平方和，验证参数之间的巨大差异。
print np.sum(regressor_poly4.coef_ ** 2)

# 从sklearn.linear_model导入Ridge。
from sklearn.linear_model import Ridge

# 使用默认配置初始化Riedge。
ridge_poly4 = Ridge()

# 使用Ridge模型对4次多项式特征进行拟合。
ridge_poly4.fit(X_train_poly4, y_train)

# 输出Ridge模型在测试样本上的回归性能。
print ridge_poly4.score(X_test_poly4, y_test)

# 输出Ridge模型的参数列表，观察参数差异。
print ridge_poly4.coef_

# 计算Ridge模型拟合后参数的平方和。
print np.sum(ridge_poly4.coef_ ** 2)


'''
We are trying to solve an Ordinary Differential Equation
using the Euler's method. For this example we are taking
the example of:

dy/dt = -ky : IVP => y = y0 for t = 0

We will solve this without using the scipy
odeint package
'''

# import the basic libraries
import math
import numpy as np                # used for numerical analysis
import matplotlib.pyplot as plt   # used for plotting Graphs

# initialize the problem
x0 = -np.pi/2
t =0
x = x0
#k ,t , y =  1, 0.0, 5.0
#y0 = y
tt, yy = [], []
tf = 2                         # final value for the time

dt = 0.1                         # step size
#exact_sol = []
# describing the equation in function

def f(t,x):
    return -math.sin(t+x)

# now thw Euler method
exact_sol = []
while t<= tf:
    tt.append(t)
    yy.append(x)
    x = x+dt*f(t,x)
    exact = -math.asin((1-t**2)/(1+t**2)) - t
    exact_sol.append(exact)
    #exact = -math.asin((1-t**2)/(1+t**2))
    #exact_sol.append(exact)

    t += dt

# finding the Exact Result

#time
exact = -math.asin((1-t**2)/(1+t**2)) - t

#print(exact)
# now the Plotting part
print(len(tt))

plt.plot(tt,yy,'o', label="dt=%.4f"%(dt))
plt.plot(tt, exact_sol,'k', label="Exact Solution")
plt.xlabel("time")
plt.ylabel('y')
plt.legend(loc='best')

# plotting the absolute error

plt.legend(loc='best')

plt.title("ODE by Euler Method")
plt.savefig('decay-euler1.png')


"""
A Monte Carlo simulation to compute size distortion in hypothesis testing in an OLS context
"""

import numpy as np
import matplotlib as plt

# generate artificial dataset (could use real data if available)
np.random.seed(0)
n = 4  # the higher is n, the less wrong we are about using +/-1.96 as critical values,
# the closer is the empirical size to 5%
sigma_u = 30
beta = 7
X = np.random.normal(0, 10, n).reshape((n, 1))
u = np.random.normal(0, sigma_u, n).reshape((n, 1))
y = X.dot(beta) + u

# monte carlo simulation
beta_zero = 5
XX_inv = np.linalg.inv(np.transpose(X).dot(X))
A = XX_inv.dot(np.transpose(X))
b = X.dot(beta_zero)
number_of_rejections = 0
reps = 100000
for m in range(1, reps + 1):
    u_m = np.random.normal(0, sigma_u, n).reshape((n, 1))
    y_m = b + u_m
    betahat_m = A.dot(y_m)
    uhat = y_m - X.dot(betahat_m)
    s2_m = np.transpose(uhat).dot(uhat) / (n - 1)
    t_m = (betahat_m - beta_zero) / np.sqrt(s2_m * XX_inv)
    number_of_rejections += abs(t_m) > 1.96
empirical_size = number_of_rejections / reps
print('The empirical size is %.10f' % empirical_size)
print('The size distortion is |theoretical-empirical|=|5%%-%.4f%%|=%.4f' % (empirical_size * 100,
                                                                            abs(5 - empirical_size * 100)))


import numpy as np
import matplotlib.pyplot as plt

def _marchenko_pastur(x,gamma,sigma=1.0):
    y=1/gamma
    largest_eigenval = np.power(sigma*(1 + np.sqrt(1/gamma)),2)
    smallest_eigenval= np.power(sigma*(1 - np.sqrt(1/gamma)),2)
    mp = (1/(2*np.pi*sigma*sigma*x*y))*np.sqrt((largest_eigenval - x)*(x - smallest_eigenval))*(0 if (x>largest_eigenval or x<smallest_eigenval) else 1)
    return mp

def marchenko_pastur(n,p,upper_bound=3,spacing=2000,sigma=1.0):
    x_mp_dist = np.linspace(0,upper_bound,spacing)
    y_mp_dist = [_marchenko_pastur(x,n/p,sigma=sigma) for x in x_mp_dist]

    return x_mp_dist,y_mp_dist

def eigenval_histogram(X,bins=100):
    p,n = X.shape
    S = (1./n) * np.dot(X,X.T)
    u,_ = np.linalg.eig(S)

    hist_heights, hist_bins = np.histogram(u,bins=bins,density=True)
    x = hist_bins[:-1]
    y = hist_heights
    col_widths = hist_bins[-1]/len(x)

    return x,y,col_widths

def transform_to_zero_mean_and_unit_std(X):
    m,n = X.shape
    means = np.mean(X,axis=1).reshape(m,1)
    mean_adjusted_X = np.subtract(X,means)
    stds = np.std(mean_adjusted_X,axis=1).reshape(m,1)
    mean_and_std_adjusted_X = np.divide(mean_adjusted_X,stds)
    return mean_and_std_adjusted_X

def marchenko_pastur_comparison(X,axis,histogram_bins=100,dist_upper_bound=3,dist_spacing=2000,sigma=1.0,transform=True):
    p,n = X.shape
    x_mp, y_mp = marchenko_pastur(n,p,upper_bound=dist_upper_bound,spacing=dist_spacing,sigma=sigma)

    if transform:
        X_ = transform_to_zero_mean_and_unit_std(X)
        x,y,w = eigenval_histogram(X_,bins=histogram_bins)
    else:
        x,y,w = eigenval_histogram(X,bins=histogram_bins)

    axis.bar(x,y,width=w,color='b')
    axis.plot(x_mp,y_mp,color='r')
    axis.set_xlabel(r'$\lambda$')
    axis.set_title('Marchenko-Pastur Distribution Comparison')


import numpy as np
import matplotlib.pyplot as plt
import methods as mt

#Comparaison à un pas donné des valeurs y calculées et des valeurs de la solution à ses points communs avec y. Retourne le maximum des écarts.

def step_error(y,a,b,sol):
	h=(b-a)/len(y)
	if type(y[0]) is np.ndarray:
		sol_values=np.zeros((len(y),y[0].shape[0]))
	else:
		sol_values=np.zeros(len(y))
	for i in range(len(y)):
		sol_values[i]=sol(a+i*h)
	if type(y[0]) is np.ndarray:
		diff_values=[np.linalg.norm(y[i]-sol_values[i]) for i in range(len(y))]
	else:
		diff_values=[abs(y[i]-sol_values[i]) for i in range(len(y))]
	diff=max(diff_values)
	return diff

#Tabulation des écarts maximums pour différents pas (en croissance logarithmique), sur un intervalle [a,b], construisant le tableau de valeurs avec la méthode meth. f et y0 sont nécessaires pour l'exécution de meth_n_step.

def errors_over_n(sol,f,meth,y0,a,b):
	space=[10,20,30,40,50,60,70,80,90,100,200,300,400,500,600,700,800,900,1000]
	diffs=np.zeros(len(space))
	for i in range(len(diffs)):
		h=(b-a)/space[i]
		y=mt.meth_n_step(y0,a,space[i],h,f,meth)
		diffs[i]=step_error(y,a,b,sol)
	return diffs

#Création de graphes d'erreurs pour les quatre méthodes sur le cas défini par sol, f, y0, et sur l'intervalle [a,b].

def plot_errors(sol,f,y0,a,b):
	diffs_euler=errors_over_n(sol,f,mt.step_euler,y0,a,b)
	diffs_middle=errors_over_n(sol,f,mt.step_middle,y0,a,b)
	diffs_heun=errors_over_n(sol,f,mt.step_heun,y0,a,b)
	diffs_rk4=errors_over_n(sol,f,mt.step_rk4,y0,a,b)

	plt.clf()
	X=[10,20,30,40,50,60,70,80,90,100,200,300,400,500,600,700,800,900,1000]
	plt.plot(X,diffs_euler,label="Méthode d'Euler")
	plt.plot(X,diffs_middle,label="Méthode du point milieu")
	plt.plot(X,diffs_heun,label="Méthode de Heun")
	plt.plot(X,diffs_rk4,label="Méthode de Runge-Kutta d'ordre 4")
	plt.legend()

	plt.xlabel("Pas")
	plt.ylabel("Erreur relative maximale")
	plt.title("Graphe de comparaison des erreurs entre les quatre méthodes")
	plt.xscale("log")
	plt.grid(True,which="both",linestyle='-.')
	plt.show()

if __name__=='__main__':
	f=lambda t,y: y/(1+t**2)
	sol=lambda t: np.exp(np.arctan(t))
	y0=1
	a=0
	b=5
	plot_errors(sol,f,y0,a,b)


"""Example implementation of the conjugate gradient descent algorithm using a hard coded objective function

    F(X1, X2) = 5X1**2 + X2**2 + 4X1X2 - 14X1 - 6X2 + 20

- At each step the value of X will be updated using
    X_k+1 = X_k + alpha * Pk

    Where pk is the conjugate direction and alpha is the step length
"""
import math
import logging
import numpy as np

logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger(__name__)

class ConjugateGradientDescent:
    def __init__(self, max_iterations=10, hessian_matrix=None, linear_terms=None):
        self.Hessian: np.ndarray = hessian_matrix or np.array([[10, 4], [4, 2]])
        self.LinearCoefficients = linear_terms or np.array([-14, -6])
        self.ConstantTerm = 20
        self.Minimiser = np.array([0, 0])
        self.CurrentIteration = 0
        self.MaxIterations = max_iterations
        self.Epsilon = math.pow(10, -3)  # Stop if magnitude of gradient is less than this value
        self._LastConjugate = None  # Track gradient in current iteration - 1 step

    @property
    def __current_gradient_value(self):
        gradient = self.del_f(self.Minimiser)
        return np.sqrt(gradient.dot(gradient))

    def f(self, xk: np.ndarray, decimal_places=3):
        """Returns the function value calcultaed from the equivalent quadratic form"""
        squared_terms = 0.5 * xk.dot(self.Hessian).dot(xk)
        linear_terms = self.LinearCoefficients.dot(xk)
        total_cost = squared_terms + linear_terms + self.ConstantTerm
        return round(total_cost, decimal_places)

    def del_f(self, xk: np.ndarray) -> np.ndarray:
        gradient_vector = np.matmul(self.Hessian, xk)
        if isinstance(self.LinearCoefficients, np.ndarray):
            gradient_vector = np.add(gradient_vector, self.LinearCoefficients)
        return gradient_vector

    def conjugate_direction(self, gk: np.ndarray):
        if self.CurrentIteration == 0:
            self._LastConjugate = -1 * gk
            return self._LastConjugate
        gk_previous: np.ndarray = self._LastConjugate
        bk = gk.dot(gk) / gk_previous.dot(gk_previous)
        pk = (-1 * gk) + bk*gk_previous
        self._LastConjugate = pk  # We will use this on next iteration as P_k-1
        return pk

    def exact_step_length(self, gk: np.ndarray, pk: np.ndarray, decimal_places=3) -> np.float64:
        """Get the step lengh by minimising f(x + ad_k) wrt to a

        alpha(or lambda) = -gk * pk / pk * A * pk
        """
        numerator = gk.dot(pk)
        denominator = pk.dot(self.Hessian).dot(pk)
        step_length = numerator/denominator
        return round(step_length, decimal_places) * -1

    def _find_next_candidate(self):
        """Get next potential minimum using X_k+1 = X_k + alpha * pk"""
        xk = self.Minimiser
        gk = self.del_f(xk)
        pk = self.conjugate_direction(gk)
        step_length = self.exact_step_length(gk, pk)
        x_k_plus1 = xk + (step_length * pk)
        log.debug(f"X**{self.CurrentIteration} = {xk} - {step_length} * {pk}")
        return x_k_plus1

    def execute(self):
        log.info('Conjugate gradient descent iteration started')
        for k in range(self.MaxIterations):
            log.debug(f"-----------Iteration {k}--------------")
            self.CurrentIteration = k
            self.Minimiser = self._find_next_candidate()
            log.debug(f"------Minimiser = {self.Minimiser}. Gradient = {self.__current_gradient_value}--------\n")
            if self.__current_gradient_value <= self.Epsilon:
                log.warning(f"Iteration stopped at k={k}. Stopping condition reached!")
                break
        log.info(f"------Minimiser = {self.Minimiser}. Gradient = {self.__current_gradient_value}--------")
        log.info('Conjugate gradient descent completed successfully')
        return self.Minimiser, self.f(self.Minimiser), round(self.__current_gradient_value, 3)


import numpy as np
import pandas as pd
from utils import regresiveSustitution, progressiveSustitution, swapRowsSpecial,regresiveSustitutions
from utils import rowOps
from utils import getMultipliers
from utils import swapRows
from utils import isSquared

def crout(A,b):

    pivots = []

    A = np.array(A).astype(float)
    b = np.array(b).astype(float)

    times = A[:, 0].size

    U = np.zeros((times, times))
    L = np.identity(times)
    cont = 0

    for d in range(0, times):
        U[d, d] = 1

    for d in range(0, times): #Etapas
        #Calculo L
        for j in range(d, times):
            sum0 = sum([L[j, s] * U[s, d] for s in range(0, j)])
            L[j, d] = A[j, d] - sum0
        #Calculo U
        for j in range(d+1, times):
            sum1 = sum([L[d, s] * U[s, j] for s in range(0, d)])
            U[d, j] = (A[d, j] - sum1) / L[d, d]
        cont = cont+1
        pivots.append({'status':'Step '+str(cont), 'matrixL': L.copy(), 'matrixU': U.copy()})

    LB = np.concatenate([L, b], axis=1)
    size = LB[:, 0].size

    pro = progressiveSustitution(LB, size)
    pro = np.array(pro).astype(float)

    UB = np.concatenate([U, pro.reshape((U.shape[0], 1))], axis=1)
    size2 = UB[:, 0].size
    reg = regresiveSustitutions(UB, size2 - 1)
    pivots.append({'status':'Results', 'reg': reg})

    return pivots

def showSteps(steps):
    for step in steps:
        try:
            print(pd.DataFrame(step).to_string(index=False, header=False)+"\n")
        except:
            print(step)

def showTable(table):
    result = ""
    if('status' in table[-1]):
        result = table[-1]
        table.pop()
    print(pd.DataFrame(table).to_string(index=False))
    print(result)

if __name__ == "__main__":
    A = [[4, -1, 0, 3],[1, 15.5, 3, 8],[0, -1.3, -4, 1.1],[14, 5, -2, 30]]
    B = [[1],[1],[1],[1]]

    results = crout(A,B)

    for r in range(0,len(results)-1):
        print(results[r]['status'])
        print("matrix L")
        showTable(results[r]['matrixL'])
        print("matrix U")
        showTable(results[r]['matrixU'])

    print("Results")
    print(results[len(results)-1]['reg'])


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Feb  7 18:59:29 2022

@author: Analabha Roy
"""

import numpy as np
from scipy.linalg import lu_factor, lu_solve
A = np.array([[25., 5., 1.],
              [64., 8., 1.],
              [144., 12., 1.]])

b = np.array([106.8,
              177.2,
              279.2])

A_fact, piv = lu_factor(A.copy())

print("Decomposed L Matrix:\n", np.tril(A_fact, k=0))
print("\n\nDecomposed U Matrix:\n", np.triu(A_fact, k=1))

x = lu_solve((A_fact.copy(), piv), b)

print("\nSolution is x =", x)
print("Solution is close?", np.allclose(A @ x, b))

id = np.eye(A.shape[0])
A_inv = np.zeros_like(A)

for i, row in enumerate(id):
    A_inv[:, i] = lu_solve((A_fact.copy(), piv), row)

print("\n\nInverse of matrix is:\n", A_inv)
print("Solution is close?", np.allclose(A @ A_inv, id))


import numpy as np

def gram_schmidt_factorization(matrix):
    matrix = np.array(matrix)
    m, n = matrix.shape
    q = np.zeros((m, n))
    r = np.zeros((n, n))
    matrix = np.transpose(matrix)
    r[0][0] = np.linalg.norm(matrix[0], 2)
    q[:, 0] = matrix[0]/r[0][0]
    for i in range(1, n):
        q[:, i] = matrix[i]
        for j in range(0, i):
            r[j][i] = np.matmul(q[:, j], q[:, i])
            q[:, i] = q[:, i] - (r[j][i] * q[:, j])
        r[i][i] = np.linalg.norm(q[:, i], 2)
        q[:, i] = q[:, i] / r[i][i]
    return q, r

def system_solver(matrix, vector):
    qr_decomposition = gram_schmidt_factorization(matrix)
    q_matrix = qr_decomposition[0]
    r_matrix = qr_decomposition[1]
    redacted_vector = np.matmul(q_matrix.transpose(), np.array(vector).transpose())
    r_matrix_inverse = np.linalg.inv(r_matrix)
    x_vector = np.matmul(r_matrix_inverse, redacted_vector)
    return x_vector

if __name__ == '__main__':
    coefficients_matrix = eval(input("enter A matrix like: [[1, 1, 1], [2, 2, 2], [3, 3, 3]\n"))
    right_hand_side_vector = eval(input("enter b vector like: [1, 1, 1]\n"))
    print(system_solver(coefficients_matrix, right_hand_side_vector))


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn import datasets as skdata

#We will use the iris dataset
iris_dataset = skdata.load_iris()
X = iris_dataset.data # (150, 4)
y = iris_dataset.target

#Compute the mean
mu = np.mean(X, axis=0)

#Center the data
B = X - mu

#Compute the covariance matrix
C = np.matmul(B.T, B)/(B.shape[0]) #(4, 150) x (150, 4) => (4, 4)

#Eigen decomposition
S, V = np.linalg.eig(C)

#Select the top 3 dimentions
order = np.argsort(S)[::-1]
W = V[:, order][:, 0:3] #Transformation for projecting X to subspace

#Project our data
Z = np.matmul(B, W) #(150, 3)

#Let's visualize our new feature space
data_split = (Z[np.where(y==0)[0], :], Z[np.where(y==1)[0], :], Z[np.where(y==2)[0], :])
colors = ('blue', 'red', 'green')
labels = ('Setosa', 'Versicolor', 'Virginica')
markers = ('o', '^', '+')

fig = plt.figure()
fig.suptitle('Projected Iris Data')
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.set_xlabel('PC1')
ax.set_ylabel('PC2')
ax.set_zlabel('PC3')

for z, c, l, m in zip(data_split, colors, labels, markers):
    ax.scatter(z[:, 0], z[:, 1], z[:, 2], c=c, label=l, marker=m)
    ax.legend(loc='upper right')

plt.show()

#Recover our data
X_hat = np.matmul(Z, W.T)+mu
mse = np.mean((X-X_hat)**2) #0.005919048088406607

#Seems like we recovered our data pretty well
#Let's instead choose only two dimentions
W_2 = V[:, 0:2] #Transformation for projecting X to subspace

#Project our data
Z_2 = np.matmul(B, W_2)

X_hat_2 = np.matmul(Z_2, W_2.T)+mu
mse = np.mean((X-X_hat_2)**2) # 0.02534107393239825

#As we reduce more dimentions, we lose more information


import data.warmUpExercise as a
import data.computeCost as cc
import data.gradientDescent as gd
import data.plotData as pd
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm

#============Part 1=================
print('Running warmUpExercise')
print('5x5 Identity Matrix')
print(a.warm())

#==========Part 2===================
print('Plotting Data')
data = np.loadtxt('ex1data1.txt',delimiter =",")
X = data[:,0]
y = data[:,1]
m = len(y)

pd.plotData(X,y)

#================part 3====================
print('Gradient Descent')
X_padded = np.column_stack((np.ones((m,1)),X))
theta = np.zeros((2,1))

iterations = 1500
alpha = 0.01

print('testing functions')
print(cc.computeCost(X_padded,y,theta))
theta = gd.gradientDescent(X_padded,y,theta,alpha,iterations)
print("{:f}, {:f}".format(theta[0,0], theta[1,0]))

#plot linear fit
plt.plot(X,X_padded.dot(theta),'-',label = 'Linear regression')
plt.legend(loc = 'lower right')
plt.draw()

#predicts values for population size of 35000 and 70000
predict1 = np.array([1,3.5]).dot(theta)
print("For population = 35000 we predict a profit of {:f}".format(float(predict1*10000)))
predict2 = np.array([1, 7]).dot(theta)
print('For population = 70,000, we predict a profit of {:f}'.format( float(predict2*10000) ))

#================ part 4============================
print("j(theta_0,theta1)")
theta0_vals = np.linspace(-10,10,100)
theta1_vals = np.linspace(-1,4,100)

J_vals = np.zeros((len(theta0_vals),len(theta1_vals)))
for i in range(len(theta0_vals)):
    for j in range(len(theta1_vals)):
        t = [[theta0_vals[i]],[theta1_vals[j]]]
        J_vals[i,j] = cc.computeCost(X_padded,y,t)

J_vals = np.transpose(J_vals)
fig = plt.figure()
ax = fig.gca(projection='3d')
theta0_vals, theta1_vals = np.meshgrid(theta0_vals, theta1_vals) # necessary for 3D graph
surf = ax.plot_surface(theta0_vals, theta1_vals, J_vals, cmap=cm.coolwarm, rstride=2, cstride=2)
fig.colorbar(surf)
plt.xlabel('theta_0')
plt.ylabel('theta_1')

fig = plt.figure()
ax = fig.add_subplot(111)

cset = plt.contour(theta0_vals, theta1_vals, J_vals, np.logspace(-2, 3, 20), cmap=cm.coolwarm)
fig.colorbar(cset)
plt.xlabel('theta_0')
plt.ylabel('theta_1')
plt.plot(theta[0,0], theta[1,0], 'rx', markersize=10, linewidth=2)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Wed Jun 28 7:33:03 2019

@author: Parikshith.H
"""

import numpy as np

a = np.array([[1, 2],
            [3, 4]])
b = np.array([[4, 3],
            [2, 1]])

# add arrays
print ("Array sum:\n", a + b)
# =============================================================================
# #output:
# Array sum:
#  [[5 5]
#  [5 5]]
# =============================================================================

# multiply arrays (elementwise multiplication)
print ("Array multiplication:\n", a*b)
# =============================================================================
# #output:
# Array multiplication:
#  [[4 6]
#  [6 4]]
# =============================================================================

# matrix multiplication
print ("Matrix multiplication:\n", a.dot(b))
# =============================================================================
# #output:
# Matrix multiplication:
#  [[ 8  5]
#  [20 13]]
# =============================================================================


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

# In[112]:

import numpy as np
import math

# In[ ]:

# # Criando Array

# In[6]:

#arrays podem ser mostrados como listas ou listas de listas e podem ser criados como listas. Quando criando um array nós
#passamos uma lista como um argumento numpy array

a = np.array(['1','2','3'])
print(a)
#pode imprimir o número de dimensões de uma lista usando o atributo "ndim"
print(a.ndim)

# In[4]:

#se inserirmos uma lista de listas, criamos então um array multi dimensional, por exemplo uma matrix
b = np.array([[1,2,3], [4,5,6]])
b
b.ndim

# In[5]:

#podemos imprimir o comprimento do array usando o atributo "shape", que retorna uma tupla, no caso = 2 linhas 3 colunas
b.shape

# In[11]:

#verificar o tipo de dados de um array "dtype"
print(a.dtype)
print(b.dtype)

# In[17]:

#floats também são aceitos em arrays numpy
c = np.array([1.1, 5, 2.8])
c.dtype.name
c
#ele converte automaticamente o 5 que era inteiro para um numero float sem perder precisão
#ele tenta manter o tipo do dado homogêneo

# In[18]:

#as vezes sabemos o formato do array que queremos criar mas não sabemos o que colocar nele, o numpy oferece
#funções para criar arrays com dados iniciais, tipo 0 e 1

#criando dois arrays com o mesmo formato potém com dados diferentes
d = np.zeros((2,3))
print(d)

e = np.ones((2,3))
print(e)

# In[21]:

#criando um array com números aleatórios
np.random.randint(2,3)

# In[24]:

#ainda da pra criar uma sequência de números com a função arange().
#o primeiro argumento é o número de partida, o segundo argumento é o número de parada e
#o terceiro argumento é a diferença entre os números, o "pulo"

#criando um array de números pares de 10 (incluso) até 50 (excluso)
f = np.arange(10, 50, 2) #começa no dez (incluso) e termina no 50 (sem contar o 50), pulando de 2 em 2
print(f)

# In[ ]:

#se quisermos criar uma sequencia de floats, podemos usar o linspace(). nessa função,
#o terceiro argumento não é a diferença entre um número e o outro (o pulo) e sim a quantidade de itens que queremos criar
np.linspace(0, 2, 15) #me gere 15 números entre 0 (incluso) e 2 (excluso)

# # Operações com arrays

# In[32]:

#da pra fazer manipulações matematicas (adição, subtração, divisão...) assim como usar arrays booleanos (true, false)
#da pra manipular matrizes como produto, transpor, inverter e tal
a = np.array([10, 20, 30, 40])
b = np.array([1, 2, 3, 4])

#a menos b
c = a - b
print(c)

#a vezes b
d = a * b
print(d)

# In[11]:

#com operações aritiméticas podemos converter o valor dos dados, tipo temperatura e distância

far = np.array([32, -16, 106, -30])

#formua de conversão
#((°F - 32) / 1.8)

cel = ((far - 32) / 1.8)
cel

# In[13]:

#usar booleano para checar se uma temperatura de um array é maior que 20°C
#vai retornar True se for verdadeiro ou False se for falso
cel > 20

# In[14]:

#usar módulo para checar se um número do array é par
cel % 2 == 0

# In[39]:

#numpy suporta manipulação de matrizes
#produto de matriz

A = np.array([[1,2], [3,4]])
B = np.array([[1,2], [3,4]])

#A = np.array([[a, b], [c, d]])
#B = np.array([[e, f], [g, h]])
# o processo é: a*e + b*g, a*f + b*h
#               c*e + d*g, c*f + d*h

soma1 = 1*1 + 2*3, 1*2 + 2*4

soma2 = 3*1 + 4*3, 3*2 + 4*4

print(A*B)
print('---')
print(A@B)
print('---')
print(soma1)
print('---')
print(soma2)

# In[40]:

#para ver o formato da matriz podemos fazer o .shape
A.shape
#(2, 2) --> duas linhas duas colunas

# In[45]:

#quando manipulando arrays de data types diferentes, ele vai colocar no final o resultado data type mais geral que existe,
#isso é chamado upcasting

#array integers
ar1 = np.array([[2, 4], [6, 8]])
#array float
ar2 = np.array([[2.5, 4.5], [6.5, 8.5]])
#somando os arrays
ar3 = ar1 + ar2
print(ar3)
print(ar3.dtype)

# In[46]:

#numpy tem funções como max, min, sum, mean
print(ar3.mean())
print(ar3.max())
print(ar3.min())
print(ar3.sum())

# In[48]:

#com arrays multidimensionais, podemos fazer a mesma coisa com cada linha e/ou coluna
#criando um array de 15 elementos de 1 a 15 com dimensão 3x5
quin = np.arange(1, 16, 1).reshape(3, 5)
quin

# # indexação, slicing e iteração

# In[49]:

#um array unidimensional funciona quase que como uma lista, para pegar um elemento usamos o [x]
ar = np.array([13,2,56,6,8,1,2])
ar[5]

# In[53]:

#já para um array multidimensional, usamos um index integer
ar2 = np.array([[2,3], [5,4], [1, 9], [2,5]])
ar2

# In[58]:

#para pegar o elemento, precisamos inserir o número da linha e o segundo o da coluna [x, y]
#lemrbando que em python começa no 0
ar2[3,0]

# In[75]:

#para pegar mais de um elemento podemos colocá-los direto numa lista usando a função doa rray
ar3 = np.array([ar2[0,0], ar2[1,1], ar2[2,0], ar2[3,1]])
print(ar3)
print('---')
#ou
print(ar2[[0,1,2,3], [0,1,0,1]]) #primeiro diz as linhas e depois quais colunas

# ## Boolean indexing

# In[78]:

#para achar elementos maiores que 3
print(ar2 > 3)
#retorna um array com verdadeiro ou falso

# ## Slicing

# In[81]:

#slicing é uma forma de criar sub-arrays com base num array original
#para um array unidimensional, ele funciona quase como uma lista
#para fazer o slice, usa-se o sinal de pois pontos : . por exemplo, se colocarmos o :5 no index, nós teriamos
# todos os elementos de 0 a 5 excluindo o 5
ar = np.array([1,2,3,4,5,6,7,8,9,10])
print(ar[:5])

# In[82]:

#daí se colocar dois números com os dois pontos, teríamos todos os elementos de x a y
#[3:8] todos os elementos de 3(incluso) a 8 (excluso)
print(ar[3:8])

# In[92]:

#agora para arrays multimensionais
ar2 = np.array([[1,2,3,4], [5,6,7,8], [9, 10, 11,12]])
print(ar2)

# In[87]:

#se adicionarmos [:2] teriamos todos os elemntos da coluna zero e da coluna um
print(ar2[:2])
#ou seja, ele tráz as linhas com todos os elementos e não os elementos individuais

# In[88]:

#se adicionar dois argumentos ar2[:2, 1:3] retorna as duas primeiras linhas e os elementos de 1 a 3 (excluso)
print(ar2[:2, 1:3])

# In[94]:

#assim, em arrays multidimensionais, o primeiro argumento é para selecionar colunas e o segundo argumento é para colunas
ar2

# In[99]:

#mudar um elemento num subarray muda ele também no array original, isso é chamado passado por referência
#então, modoificando um sub array, vai modificar o original

sub_ar2 = ar2[:2, 1:3]

print('sub_ar2 index [0,0] antes de mudar: ', sub_ar2[0,0])
sub_ar2[0,0] = 50
print('sub_ar2 [0,0] depios de mudar: ', sub_ar2[0,0])
print('array original ar2 [0,1] depois de mudar: ', ar2[0,1])
print('---')
print(ar2)

# In[105]:

sub2_ar2 = sub_ar2
sub2_ar2[0,1] = 5
print(sub2_ar2)
print('---')
print(ar2)

# # trabalhando com datasets e numpy

# # dataset de vinhos

# In[115]:

#carregabdo o arquivo csv, mas por algum acaso ele está com ponto e virgula ao invés de só a virgula separando
wines = np.genfromtxt('winequality-red.csv', delimiter = ';', skip_header=1)

# In[116]:

wines

# In[118]:

#para selecionar a coluna da acidez, a primeira coluna, nós podemos buscar inserindo o index da coluna no array
#lembrando que para arrays multidimensionais, o primeiro argumento é a linha e o segundo a coluna
# se dermos só uma argumento, ele retorna uma lista simples

#todas as linhas da primeira colun
print('um número para cortar: ',wines[:, 0]) #retorna a primeira coluna em forma de lista
#para retornarmos o valor da primeira coluna mas na forma de linhas, onde cada valor está na sua linha:
print('0 a 1 para cortar: \n', wines[:, 0:1]) #aqui retorna os valores na forma de uma única coluna

# In[121]:

#se for pra pegar o intervalo entre a primeira e a terceira coluna:
wines[:, 0:3]

# In[122]:

#e se for pra buscar um número de colunas não consecutivas, tipo 0, 2, 4: criamos um array e colocamos esse array como
#o segundo argumento da busca
wines[:,[0,2,4]]

# In[124]:

#para fazer um resumo do dataset. se quisermos saber o valor médio da qualidade do vinho vermelho,
#selecionamos a coluna de qualidade. o jeito mais apropriado de fazer isso é usando o valor -1 para buscar a última coluna
#já que números negativos quer dizer que estamos buscando pelo final da lista
wines[:, -1].mean()

# # dataset de admissão escolar

# In[153]:

graduate_admission = np.genfromtxt('Admission_Predict.csv', dtype=None, delimiter=",", skip_header=1,
                                   names=("Serial_No", 'GRE_Score', 'TOELF_Score', 'University_Rating', 'SOP',
                                         'LOR', 'CGPA', 'Research', 'Chance_of Admit'))
graduate_admission.shape

# In[154]:

#podemos retornar uma coluna do array usando apenas o nome que demos aqui em cima
graduate_admission['CGPA'][0:5]

# In[155]:

#para deixar o valor numa escala de 0 a 4, podemos dividir o valor por 10 e multiplocar por 40
graduate_admission['CGPA'] = graduate_admission['CGPA'] / 10*4

# In[156]:

graduate_admission['CGPA'][0:20]

# In[157]:

#usando boolean mask para descobrir quantos alunos tiveram experiência com pesquisa/research criando uma mascara booleana e
#passando ela para o operador index do array

len(graduate_admission[graduate_admission['Research'] == 1])

# In[159]:

#identificar quandos alunos tem maiores chances de admissão >80% dos que tem menos chance de admissão <40%
#primeiro usar a mascara booleana para pegar apenas aqueles alunos em que estamos interessados
#baseado na sua chance de admissão, daí pegamos seu CGPA Score e imprimimos o valor médio
print(graduate_admission[graduate_admission['Chance_of_Admit']> 0.8]['GRE_Score'].mean())
print(graduate_admission[graduate_admission['Chance_of_Admit']< 0.4]['GRE_Score'].mean())

# In[160]:

print(graduate_admission[graduate_admission['Chance_of_Admit']> 0.8]['CGPA'].mean())
print(graduate_admission[graduate_admission['Chance_of_Admit']< 0.4]['CGPA'].mean())


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
BE523 Biosystems Analysis & Design
HW2 - Problem 7. Bacteria growth, difference calculator
https://mathinsight.org/bacteria_growth_initial_model_exercises Exercise 5

Created on Thu Jan 21 12:11:46 2021
@author: eduardo
"""
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm  # allows linear regression without intercept

# Bacteria density data
# B = [0.028, 0.047, 0.082, 0.141, 0.240, 0.381]  # Exercise 5
B = np.array([0.022, 0.036, 0.060, 0.101, 0.169, 0.266])  # From problem 2

steps = len(B)  # Adjust the length of the vectors with the number of steps
dB = np.zeros(steps)
dt = 16  # time interval

t = np.linspace(0, (steps-1)*dt, steps)  # actual time vector

for i in range(1, steps):
    dB[i] = B[i] - B[i-1]  # compute the increment between time steps

# Perform a linear regression with B and dB, then plot (dB vs B)
model = sm.OLS(dB, B)  # No intercept by default, force through the origin
results = model.fit()
slope = results.params[0]  # grow rate of population
print("slope=", slope)

# Figure 1, plotting dB vs B
plt.figure(1)
plt.plot(B, dB, 'kx', B, slope*B, 'b-')
plt.legend(['data', 'linear regression $R^2$=%.3f' % results.rsquared], loc='best')
plt.xlabel('B')
plt.ylabel('dB')
plt.savefig('p7_bacteria_linear2.png', dpi=300, bbox_inches='tight')

# Generate an exponential equation ('exact solution')
tdouble = np.log(2)/np.log(1+slope)*dt
print('tdouble =', tdouble)
K = np.log(2)/tdouble
Bexp = B[0] * np.exp(K*t)

# Make 'predictions' using the analytical solution to the linear dynamical system,
# (also an exponential equation) in the form B(t) = B[0]*R^t with R>1
# we don't need to know the previous value, each calculation is only dependant of the time 't'
Bmodel = B[0]*pow(slope+1, t/dt)
print("The population after %d steps is: %.3f" % (steps, Bmodel[-1]))

# Figure 2, plotting P (from data and model) vs t
plt.figure(2)
plt.plot(t, B, 'bx', t, Bmodel, 'r-', t, Bexp, 'k+')
plt.legend(['data',
            'numerical B=%g$\cdot$(1+%.4f)$^t$' % (B[0], slope),
            'exact B=%g$\cdot$exp(%.4f$\cdot$t)' % (B[0], K)],
           loc='best')
plt.xlabel('Time (minutes)')
plt.ylabel('Bacteria population')
plt.savefig('p7_bacteria_%dsteps2.png' % steps, dpi=300, bbox_inches='tight')


#Code for creating halton sampling in low n-dimensions
# references : - https://gist.github.com/tupui/cea0a91cc127ea3890ac0f002f887bae
#              - https://www.w3resource.com/python-exercises/list/python-data-type-list-exercise-34.php

import numpy as np

def primes (n):
    #Defining prime numbers for base using sieve of erasthostenes
    not_prime = []
    prime = []
    for i in range(2, n+1):
        if i not in not_prime:
            prime.append(i)
            for j in range(i*i, n+1, i):
                not_prime.append(j)
    return prime

def vandercorput(n_sample,base=2):
    #generate sample using van der corput sequence per dimension
    sequence=[]
    for i in range(0,n_sample):
        f=1. ;   r=0.
        while i > 0:
            i, remainder = divmod(i, base)
            f = f/base
            r = r+f*remainder
        sequence.append(r)
    return sequence

def halton (dimension,n_sample):
    # halton sequence general form of van der corput sequence in n-dimensions
    big_number = 1000       # just an input for base, as long as dim <= len(base) the program won't error
    base = primes(big_number)[:dimension]
    #print("base = ",base)             # for debugging
    sample = [vandercorput(n_sample + 1, dim) for dim in base] # looping van der corput for each dimension
    sample = np.stack(sample, axis= -1)[1:]     #arrange the array
    sample[1:n_sample,:] = sample[0:n_sample - 1,:]
    sample[0,:] = 0
    return sample


import numpy as np
import matplotlib.pyplot as plt

def squareDistance(u, v):
    diff = u - v
    return diff.dot(diff)

# We want to minimize intra-cluster variance (squard error)
def objective(X, R, M):
    cost = 0
    for k in range(len(M)):
        for n in range(len(X)):
            cost += R[n,k] * squareDistance(M[k], X[n])

    return cost

def expDistance(p1, p2, beta):
    return np.exp(-beta * squareDistance(p1, p2))

def plot_k_means(X, K, max_iter=20, beta=1.0):
    N, D = X.shape

    M = np.array(
        X[np.random.choice(N, K, replace=False), :]
    )

    R = np.zeros((N, K))

    costs = np.zeros(max_iter)
    for i in range(max_iter):
        # calculate responsibility for each point for each cluster
        for k in range(K):
            for n in range(N):
                sumMeanDistances = np.array([
                    expDistance(M[j], X[n], beta) for j in range(K)
                ]).sum()

                meanDistance = expDistance(M[k], X[n], beta)

                softMax = meanDistance / sumMeanDistances

                R[n,k] = softMax

        # calculate the means for each cluster
        for k in range(K):
            M[k] = R[:,k].dot(X) / R[:,k].sum()

        # calculate cost and break if it hasn't changed much
        costs[i] = objective(X, R, M)
        if i > 0 and np.abs(costs[i] - costs[i - 1]) < 0.1:
            break

    # pick K random RGB values to represent clusters
    random_colors = np.random.random((K, 3))
    # weight the color of a point based on its responsibility to each cluster.
    colors = R.dot(random_colors)

    plt.scatter(X[:,0], X[:,1], c=colors)
    plt.show()

def generate_samples():
    D = 2
    s = 4 # distance between means

    mean1 = np.array([0, 0])
    mean2 = np.array([s, s])
    mean3 = np.array([0, s])

    samples = 900
    data = np.zeros((samples, D))

    # generate the dataset
    data[:300, :] = np.random.randn(300, D) + mean1
    data[300:600, :] = np.random.randn(300, D) + mean2
    data[600:, :] = np.random.randn(300, D) + mean3

    return data

def main():
    data = generate_samples()

    clusters = 3
    plot_k_means(data, clusters)

    # clusters = 5
    # plot_k_means(data, clusters, max_iter=30)

    # clusters = 5
    # plot_k_means(data, clusters, max_iter=30, beta=0.3)

if __name__ == '__main__':
    main()


# reference: Liu, Y. and Durlofsky, L.J., 2020. 3D CNN-PCA: A Deep-Learning-Based. Parameterization for Complex Geomodels. arXiv preprint arXiv:2007.08478
import numpy as np
import matplotlib.pyplot as plt

class PCA(object):
    def __init__(self, nc=1, nr=1, l=1):
        self.l = l
        self.nc = nc
        self.nr = nr
        self.xm = np.zeros((nc, 1))
        self.usig = np.zeros((nc, l))
        self.data_matrix = None
        self.sig = None
        self.u = None

    def construct_pca(self, x):
        assert x.shape == (self.nc, self.nr)
        self.data_matrix = x
        self.xm = np.mean(x, axis=1)[:, None]
        y = 1. / (np.sqrt(float(self.nr - 1.))) * (x - self.xm)
        self.u, self.sig, _ = np.linalg.svd(y, full_matrices=False)
        self.u = self.u[:, :self.l]
        self.sig = self.sig[:self.l, None]
        self.usig = np.dot(self.u, np.diag(self.sig[:, 0]))

    def generate_pca_realization(self, xi, dim=None):
        if dim is None:
            assert xi.shape[0] == self.l
            if xi.shape == (self.l, ):
                xi = xi[:, None]
            return self.usig.dot(xi) + self.xm
        else:
            assert xi.shape[0] == dim
            if xi.shape == (dim, ):
                xi = xi[:, None]
            return self.usig[:, :dim].dot(xi) + self.xm

    def get_xi(self, m, dim=None):
        assert self.u is not None, "Input or calculate U matrix to obtain reconstructed xi"
        assert m.shape[0] == self.nc

        if m.shape == (self.nc, ):
            m = m[:, None]
        if dim is None:
            xi = self.u.T.dot(m - self.xm) / self.sig
        else:
            xi = self.u[:, :dim].T.dot(m - self.xm) / self.sig[:dim]
        return xi

    def energy_plot(self, rel_energy, truncate_point):
        plt.figure(figsize=(8, 4))
        plt.subplot(1, 2, 1)
        plt.plot(rel_energy)
        plt.ylabel('Relative Energy', fontsize=12)
        plt.xlabel('Number of principal components', fontsize=12)
        plt.subplot(1, 2, 2)
        plt.plot(rel_energy[:truncate_point])
        plt.ylabel('Relative Energy', fontsize=12)
        plt.xlabel('Number of principal components', fontsize=12)
        plt.tight_layout()
        plt.show()

    def princ_component(self, tol=0.9):
        cum_energy = np.cumsum(self.sig**2)
        rel_energy = cum_energy / cum_energy[-1]
        truncate_point = np.argmin(np.abs(rel_energy - tol)) + 1
        print('Principle components: ', truncate_point)
        return truncate_point, rel_energy