import pandas as pd
import numpy as np
import math
import matplotlib.pyplot as plt
from .metrics import mse_score, rmse_score, r2_score, mae_score
from ..features.build_features import StandardScaler, MinMaxScaler

class LinearRegressor():
    """Linear regressor"""

    def __init__(self, method='normal_equation', normalize=False, lr=0.01, epochs=1000, add_intercept=False):
        assert method in ['normal_equation', 'gradient_descent'], "Method not supported. Supported methods are 'normal_equation' and 'gradient_descent'"
        self.method = method
        self.normalize = normalize
        self.add_intercept = add_intercept
        self._weights = None

        if self.method == 'gradient_descent':
            self.lr = lr
            self.epochs = epochs

        if self.normalize:
            self._feature_scaler = MinMaxScaler()
            self._target_scaler = MinMaxScaler()

    def fit(self, X, y):
        """Fit the model to the data"""

        if self.normalize:
            X = self._feature_scaler.fit_transform(X)
            y = self._target_scaler.fit_transform(y)

        X = X.to_numpy()
        if self.add_intercept:
            X = np.hstack((np.ones((X.shape[0], 1)), X))
        y = y.to_numpy()

        if self.method == 'normal_equation':
            self._weights = np.dot(np.dot(np.linalg.inv(np.dot(X.T, X)), X.T), y)

        else:
            # mse_new = np.inf
            self._weights = np.zeros(X.shape[1])
            self.cost_history = [0] * self.epochs

            for i in range(self.epochs):
                grad = np.dot(X.T, np.dot(X, self._weights) - y) / y.shape[0]
                self._weights = self._weights - self.lr * grad
                self.cost_history[i] = mse_score(y, np.dot(X, self._weights))

            #     if (rmse_new > rmse_old):
            #         print("Stopped at iteration {}".format(i))
            #         break
            plt.scatter(range(self.epochs), self.cost_history)
            plt.xlabel('epoch')
            plt.ylabel('mse')

    def predict(self, X):
        """Use the fitted model to predict on data"""

        assert self._weights is not None, "Model needs to be fitted first. Use the fit method"

        if self.normalize:
            X = self._feature_scaler.transform(X)

        X = X.to_numpy()
        if self.add_intercept:
            X = np.hstack((np.ones((X.shape[0], 1)), X))

        y_pred = np.dot(X, self._weights)
        if self.normalize:
            y_pred = self._target_scaler.inverse_transform(y_pred)

        return np.round(y_pred, 2)

    def get_weights(self):
        """Get weights from the fitted model"""

        assert self._weights is not None, "Model needs to be fitted first. Use the fit method"
        return self._weights

    def score(self, X, y, metric='r2'):
        """Score the model"""

        assert metric in ['r2', 'rmse', 'mae'], "Metric not supported. Supported metrics are 'r2', 'rmse' and 'mae'"

        y_pred = self.predict(X)

        if metric == 'r2':
            score = r2_score(y, y_pred)
        elif metric == 'rmse':
            score = rmse_score(y, y_pred)
        elif metric == 'mae':
            score = mae_score(y, y_pred)

        return score


import functools
import operator
import random
import math
import sympy
from crypy.utils.maths import int_root

class RSA:
    def __init__(self, n, e, d=None, factors=None):
        self.n = n
        self.e = e
        self.d = d
        self.factors = factors

        if factors is not None:
            assert n == functools.reduce(operator.mul, factors)

    @property
    def phi(self):
        return functools.reduce(operator.mul, map(lambda x: x-1, self.factors))

    @staticmethod
    def _convert(data):
        if type(data) == int:
            pass
        elif type(data) == str:
            data = int.from_bytes(data.encode(), 'big')
        elif type(data) == bytes:
            data = int.from_bytes(data, 'big')
        return data

    def encrypt(self, data):
        int_data = self._convert(data)

        return pow(int_data, self.e, self.n)

    def decrypt(self, data):
        int_data = self._convert(data)

        return pow(int_data, self.d, self.n)

    def try_find_pq(self):
        d = self.d
        e = self.e
        n = self.n

        if d is None:
            return None

        k = d*e - 1
        if k % 2 == 1:
            return None

        r = k
        t = 0
        while True:
            t += 1
            r = r//2
            if r % 2 == 1:
                break
        for i in range(100):
            g = random.randint(0, n)
            y = pow(g, r, n)
            if y != 1 and y != n-1:
                for j in range(1, t):
                    x = pow(y, 2, n)
                    if x == 1:
                        break
                    if x == n-1:
                        continue
                    y = x
                x = pow(y, 2, n)
                if x == 1:
                    break
        else:
            return None
        p = math.gcd(y - 1, n)
        q = n//p
        self.factors = [p, q]

        return p, q

    def no_modulo_attack(self, data):
        int_data = self._convert(data)
        return int_root(self.e, int_data)

    def close_primes_attack(self, quantity=2):
        middle = int_root(quantity, self.n)

        if sympy.isprime(middle):
            primes_base = [middle]
            for i in range(quantity):
                primes = primes_base[:]
                for j in range(i):  # previous
                    primes = [sympy.prevprime(primes[0])] + primes
                for j in range(quantity-i-1):  # next
                    primes = primes + [sympy.nextprime(primes[-1])]
                n = functools.reduce(operator.mul, primes)
                if n == self.n:
                    return primes
        else:
            primes_base = [sympy.prevprime(middle), sympy.nextprime(middle)]
            for i in range(1, quantity):
                primes = primes_base[:]
                for j in range(i-1):  # previous
                    primes = [sympy.prevprime(primes[0])] + primes
                for j in range(quantity-i-1):  # next
                    primes = primes + [sympy.nextprime(primes[-1])]
                n = functools.reduce(operator.mul, primes)
                if n == self.n:
                    return primes

        return None


"""
Calculus functionality (differentiation and integration) for polynomials expressed in the Bernstein basis (see
:mod:`~polynomials_on_simplices.polynomial.polynomials_unit_simplex_bernstein_basis`).
"""

import numbers

import numpy as np

from polynomials_on_simplices.geometry.primitives.simplex import affine_transformation_from_unit, dimension
from polynomials_on_simplices.polynomial.polynomials_base import get_dimension

def integrate_bernstein_basis_fn_unit_simplex(r, n):
    r"""
    Integrate a degree r Bernstein base polynomial over the n-dimensional unit simplex.

    .. math::

        \int_{\Delta_c^n} b_{\nu, r}(x) \, dx = \frac{1}{\prod_{i = 1}^n (r + i)}.

    Note that this is independent of nu, i.e. each basis polynomial has the same integral over the unit simplex.

    :param int r: Degree of the basis polynomial.
    :param int n: Dimension of the unit simplex.
    :return: Evaluation of the integral of the basis polynomial over the unit simplex.
    :rtype: float

    .. rubric:: Examples

    :math:`\int_{\Delta_c^2} b_{\nu, 2}(x) \, dx = \frac{1}{\prod_{i = 1}^2 (2 + i)} = \frac{1}{3 \cdot 4}`.

    >>> integrate_bernstein_basis_fn_unit_simplex(2, 2) == 1 / 12
    True
    """
    denom = 1
    for i in range(1, n + 1):
        denom *= (r + i)
    return 1 / denom

def integrate_bernstein_polynomial_unit_simplex(r, a, n):
    r"""
    Integrate a degree r Bernstein polynomial over the n-dimensional unit simplex.

    .. math::

        \int_{\Delta_c^n} \sum_{i = 0}^{\dim(\mathcal{P}_r(\mathbb{R}^n)) - 1} a_{\nu_i} b_{\nu_i, r}(x) \, dx
        = \frac{1}{\prod_{i = 1}^n (r + i)} \sum_{i = 0}^{\dim(\mathcal{P}_r(\mathbb{R}^n)) - 1} a_{\nu_i},

    where :math:`\nu_i` is the i:th n-dimensional multi-index of norm <= r (see
    :func:`polynomials_on_simplices.algebra.multiindex.generate` and
    :func:`polynomials_on_simplices.algebra.multiindex.generate_all` for the sequence of and ordering of multi-indices).

    :param int r: Degree of the polynomial.
    :param a: Coefficients :math:`a_{\nu_i}` in the polynomial (array where element i is the coefficient
        :math:`a_{\nu_i}`)
    :param int n: Dimension of the unit simplex.
    :return: Evaluation of the integral of the polynomial over the unit simplex.

    .. rubric:: Examples

    :math:`\int_0^1 2(1 - x) + x \, dx = 1 + \frac{1}{2} = \frac{3}{2}`.

    >>> integrate_bernstein_polynomial_unit_simplex(1, [2, 1], 1) == 3 / 2
    True
    """
    assert len(a) == get_dimension(r, n)
    c = integrate_bernstein_basis_fn_unit_simplex(r, n)
    return c * sum(a)

def integrate_bernstein_basis_fn_simplex(r, vertices):
    r"""
    Integrate a degree r Bernstein base polynomial over an n-dimensional simplex T.

    .. math::

        \int_{T} b_{\nu, r}(x) \, dx
        = \int_{\Phi(\Delta_c^n)} \bar{b}_{\nu, r} \circ \Phi^{-1}(x)
        = |\det D\Phi| \int_{\Delta_c^n} \bar{b}_{\nu, r}(x)
        = |\det D\Phi| \frac{1}{\prod_{i = 1}^n (r + i)}.

    Note that this is independent of nu, i.e. each basis polynomial has the same integral over the simplex T.

    :param int r: Degree of the basis polynomial.
    :param vertices: Vertices of the simplex T ((n + 1) x n matrix where row i contains the i:th vertex of the
        simplex).
    :return: Evaluation of the integral of the basis polynomial over the simplex T.
    :rtype: float
    """
    n = dimension(vertices)
    iu = integrate_bernstein_basis_fn_unit_simplex(r, n)
    a, b = affine_transformation_from_unit(vertices)
    if isinstance(a, numbers.Number):
        det = a
    else:
        det = np.linalg.det(a)
    assert det > 0
    return det * iu

def integrate_bernstein_polynomial_simplex(r, a, vertices):
    r"""
    Integrate a degree r Bernstein polynomial over an n-dimensional simplex T.

    .. math::

        \int_{T} \sum_{i = 0}^{\dim(\mathcal{P}_r(\mathbb{R}^n)) - 1} a_{\nu_i} b_{\nu_i, r}(x) \, dx
        = \int_{\Phi(\Delta_c^n)} \sum_{i = 0}^{\dim(\mathcal{P}_r(\mathbb{R}^n)) - 1}
            a_{\nu_i} (bar{b}_{\nu_i, r} \circ \Phi^{-1})(x) \, dx
        = |\det D\Phi| \int_{\Delta_c^n} \sum_{i = 0}^{\dim(\mathcal{P}_r(\mathbb{R}^n)) - 1}
            a_{\nu_i} bar{b}_{\nu_i, r}(x) \, dx
        = |\det D\Phi| \frac{1}{\prod_{i = 1}^n (r + i)} \sum_{i = 0}^{\dim(\mathcal{P}_r(\mathbb{R}^n)) - 1} a_{\nu_i},

    where :math:`\nu_i` is the i:th n-dimensional multi-index of norm <= r (see
    :func:`polynomials_on_simplices.algebra.multiindex.generate` and
    :func:`polynomials_on_simplices.algebra.multiindex.generate_all` for the sequence of and ordering of multi-indices).

    :param int r: Degree of the polynomial.
    :param a: Coefficients :math:`a_{\nu_i}` in the polynomial (array where element i is the coefficient
        :math:`a_{\nu_i}`)
    :param vertices: Vertices of the simplex T ((n + 1) x n matrix where row i contains the i:th vertex of the
        simplex).
    :return: Evaluation of the integral of the polynomial over the simplex T.
    """
    assert len(a) == get_dimension(r, dimension(vertices))
    c = integrate_bernstein_basis_fn_simplex(r, vertices)
    return c * sum(a)


from math import *
from sympy import *
global t,deriv,dydx,count
count=0

def func( x ):
    return x*log(x)/log(10) - 1.2

def derivFunc( x ):
    return deriv.doit().subs({t:x})

# Function to find the root
def newtonRaphson( x ):
    global count
    h = func(x) / derivFunc(x)

    while abs(h) >= 0.01:
        count=count+1
	try:
      	    h = func(x)/derivFunc(x)
        except ZeroDivisionError:
            print("Error! - derivative zero for x = ", x)
        # x(i+1) = x(i) - f(x) / f'(x)
        x = x - h

    print("The value of the root is : ",
                             "%.4f"% x)
    print("the no of iterations is ",count)

x0 = input("enter the initial value : ") # Initial val
x0 = int(x0)
#x0 =5
t= Symbol('t')
f=t*log(t)/log(10) - 1.2
deriv= Derivative(f, t)
#print(deriv.doit())
dydx=deriv.doit()
#deriv.doit()
newtonRaphson(x0)


import numpy as np
import matplotlib.pyplot as plt

# defino una funcion para calcular un polinomio
def poly(x, beta):
    """
    Entradas:
    x : array de entrada.
    beta: coeficientes del polinomio: beta[i] = beta_i.

    Salida:
    y : array de salida
    """
    y = np.zeros(len(x))
    for i in range(len(beta)):
        y +=  beta[i] * x**i
    return y

def solve_poly(x, y, m):
    """
    Entradas:
    x : array unidimensional de valores de x.
    y : array unidimensiondal de valores de y.
    m : orden del polinomio que queremos ajustar.

    Salida:
    beta : array con los coeficientes del polinotmi.

    Este codigo resuelve el siguiente sistema de ecuaciones
    S * beta = Y, beta = S^{-1} * Y,
    donde S es una matriz que tiene como vectotes a columnas de potencias de X.

    """
    # matriz que guarda los vectores columna de potencias de x
    S = np.zeros([len(x), m+1])
    for i in range(m+1):
        S[:,i] = x**i

    # Calculo la inversa de S.
    S_inv = np.linalg.inv(S)

    # calculo beta
    beta = np.dot(S_inv, y)

    return beta

# cargo los datos para manipular
data = np.loadtxt("numeros_20.txt")

# diccionario para guardar todos los coeficientes
betas = {}

# distintos valores de m para probas

m_values = [1,2,3,4]

for m in m_values:
    X = data[: m+1, 0]
    Y = data[: m+1, 1]

    betas[m] = solve_poly(X, Y, m)

# preparo finalmene la grafica pedida

plt.figure()

for i,m in enumerate(m_values):
    # vuelvo a sacar los puntos para graficarlos
    X = data[: m+1, 0]
    Y = data[: m+1, 1]

    # array de valores de x que va del minimo al maximo valor considerados
    # para hacer los ajustes
    x_model = np.linspace(np.min(data[:10,0]), np.max(data[:10,0]), 100)

    # valores correspondientes al modelo de orden m
    y_model = poly(x_model, betas[m])

    plt.subplot(2,2,i+1)
    #grafica del modelo
    plt.plot(x_model, y_model)
    #grafica de los puntos
    plt.scatter(X, Y)
    plt.xlabel("X")
    plt.ylabel("Y")
    plt.title("M={}".format(m))

plt.subplots_adjust(hspace=0.5)
plt.savefig("polinomios.png", bbox_inches='tight')


import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from torch.utils.data import TensorDataset, DataLoader

# learning this
def f(x):
    return 2 * x + 1

# dataset
N = 100000
l, r = int(-N/2), int(N/2)
inputs = np.array(range(l, r))

# scaling doesn't allow huge gradients for large dataset
inputs = inputs / N
labels = f(inputs)

X = torch.tensor(inputs)
Y = torch.tensor(labels)

train_ds = TensorDataset(X, Y)
batch_size = 4
train_dl = DataLoader(train_ds, batch_size, shuffle=True)

# params init
a = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)

# gradient descent
optimizer = optim.SGD([a, b], lr=0.5)

for i in range(11):
    batch_in, batch_out = next(iter(train_dl))

    # linear regression model
    out = a * batch_in + b
    diff = (out - batch_out)

    loss = torch.sum(diff * diff) / diff.numel()
    # pytorch autograd
    loss.backward()

    # calculating gradients explicitly
    anal_a = (1 / batch_size) * torch.sum(2 * batch_in * ((a * batch_in + b) - batch_out))
    anal_b = (1 / batch_size) * torch.sum(2 * ((a * batch_in + b) - batch_out))

    # take step
    optimizer.step()

    print('step: {}, loss:{}, Y_:{}, a:{}, b {}'.format(i, loss, out, a, b))
    print('Pytorch grad a: {}\tPytorch  grad b: {}'.format(a.grad, b.grad))
    print('Analitical grad a: {}\tAnalitical grad b: {}'.format(anal_a, anal_b))

    optimizer.zero_grad()


import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import ui
import scipy.io as sio
import scipy.optimize as opt
import seaborn as sns

def load_data():
    """for ex5
    d['X'] shape = (12, 1)
    pandas has trouble taking this 2d ndarray to construct a dataframe, so I ravel
    the results
    """
    d = sio.loadmat('data/ex5data1.mat')
    return map(np.ravel, [d['X'], d['y'], d['Xval'], d['yval'], d['Xtest'], d['ytest']])

print('载入训练数据')
X, y, Xval, yval, Xtest, ytest = load_data()
print('可视化 X(水位的变化) 和 y(离开大坝的水流量 )')
df = pd.DataFrame({'water_level':X, 'flow':y})

sns.lmplot('water_level', 'flow', data=df, fit_reg=False, size=7)
plt.show()
print('设置训练、验证、测试集')
X, Xval, Xtest = [np.insert(x.reshape(x.shape[0], 1), 0, np.ones(x.shape[0]), axis=1) for x in (X, Xval, Xtest)]

print('代价函数')
def cost(theta, X, y):
    """
    X: R(m*n), m records, n features
    y: R(m)
    theta : R(n), linear regression parameters
    """
    m = X.shape[0]

    inner = X @ theta - y  # R(m*1)

    # 1*m @ m*1 = 1*1 in matrix multiplication
    # but you know numpy didn't do transpose in 1d array, so here is just a
    # vector inner product to itselves
    square_sum = inner.T @ inner
    cost = square_sum / (2 * m)

    return cost
print('用初始化参数计算 cost')
theta = np.ones(X.shape[1])
print(cost(theta, X, y))
print('定义梯度')
def gradient(theta, X, y):
    m = X.shape[0]

    inner = X.T @ (X @ theta - y)  # (m,n).T @ (m, 1) -> (n, 1)

    return inner / m
print('用初始化参数计算梯度')
print(gradient(theta, X, y))
print('正则化梯度')
def regularized_gradient(theta, X, y, l=1):
    m = X.shape[0]

    regularized_term = theta.copy()  # same shape as theta
    regularized_term[0] = 0  # don't regularize intercept theta

    regularized_term = (l / m) * regularized_term

    return gradient(theta, X, y) + regularized_term
print('用初始化参数计算正则化梯度')
print(regularized_gradient(theta, X, y))

print('拟合数据，无正则化项')
def linear_regression_np(X, y, l=1):
    """linear regression
    args:
        X: feature matrix, (m, n+1) # with incercept x0=1
        y: target vector, (m, )
        l: lambda constant for regularization

    return: trained parameters
    """
    # init theta
    theta = np.ones(X.shape[1])

    # train it
    res = opt.minimize(fun=regularized_cost,
                       x0=theta,
                       args=(X, y, l),
                       method='TNC',
                       jac=regularized_gradient,
                       options={'disp': True})
    return res

def regularized_cost(theta, X, y, l=1):
    m = X.shape[0]

    regularized_term = (l / (2 * m)) * np.power(theta[1:], 2).sum()

    return cost(theta, X, y) + regularized_term

theta = np.ones(X.shape[0])
final_theta = linear_regression_np(X, y, l=0).get('x')
print('绘制图像')
b = final_theta[0] # intercept
m = final_theta[1] # slope

plt.scatter(X[:,1], y, label="Training data")
plt.plot(X[:, 1], X[:, 1]*m + b, label="Prediction")
plt.legend(loc=2)
plt.show()

training_cost, cv_cost = [], []

print('1.使用训练集的子集来拟合应模型')
print('2.在计算训练代价和交叉验证代价时，没有用正则化')
print('3.记住使用相同的训练集子集来计算训练代价')
m = X.shape[0]
for i in range(1, m+1):
#     print('i={}'.format(i))
    res = linear_regression_np(X[:i, :], y[:i], l=0)

    tc = regularized_cost(res.x, X[:i, :], y[:i], l=0)
    cv = regularized_cost(res.x, Xval, yval, l=0)
#     print('tc={}, cv={}'.format(tc, cv))

    training_cost.append(tc)
    cv_cost.append(cv)

plt.plot(np.arange(1, m+1), training_cost, label='training cost')
plt.plot(np.arange(1, m+1), cv_cost, label='cv cost')
plt.legend(loc=1)
plt.show()
print('模型欠拟合')
ui.split_line2()
print('创建多项式特征')
def prepare_poly_data(*args, power):
    """
    args: keep feeding in X, Xval, or Xtest
        will return in the same order
    """
    def prepare(x):
        # expand feature
        df = poly_features(x, power=power)

        # normalization
        ndarr = normalize_feature(df).as_matrix()

        # add intercept term
        return np.insert(ndarr, 0, np.ones(ndarr.shape[0]), axis=1)

    return [prepare(x) for x in args]

def poly_features(x, power, as_ndarray=False):
    data = {'f{}'.format(i): np.power(x, i) for i in range(1, power + 1)}
    df = pd.DataFrame(data)

    return df.as_matrix() if as_ndarray else df

X, y, Xval, yval, Xtest, ytest = load_data()

print(poly_features(X, power=3))
print('准备多项式回归数据')
def normalize_feature(df):
    """Applies function along input axis(default 0) of DataFrame."""
    return df.apply(lambda column: (column - column.mean()) / column.std())

X_poly, Xval_poly, Xtest_poly= prepare_poly_data(X, Xval, Xtest, power=8)
print(X_poly[:3, :])

print('画出学习曲线')
def plot_learning_curve(X, y, Xval, yval, l=0):
    training_cost, cv_cost = [], []
    m = X.shape[0]

    for i in range(1, m + 1):
        # regularization applies here for fitting parameters
        res = linear_regression_np(X[:i, :], y[:i], l=l)

        # remember, when you compute the cost here, you are computing
        # non-regularized cost. Regularization is used to fit parameters only
        tc = cost(res.x, X[:i, :], y[:i])
        cv = cost(res.x, Xval, yval)

        training_cost.append(tc)
        cv_cost.append(cv)

    plt.plot(np.arange(1, m + 1), training_cost, label='training cost')
    plt.plot(np.arange(1, m + 1), cv_cost, label='cv cost')
    plt.legend(loc=1)

plot_learning_curve(X_poly, y, Xval_poly, yval, l=0)
plt.show()
print('无正则化，训练代价过低，过拟合')
ui.split_line2()

plot_learning_curve(X_poly, y, Xval_poly, yval, l=1)
plt.show()
print('l=1，减轻了过拟合')

ui.split_line2()

plot_learning_curve(X_poly, y, Xval_poly, yval, l=100)
plt.show()
print('l=100， 正则化过了，变成欠拟合')

print('找到最佳的正则化系数')
l_candidate = [0, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1, 3, 10]
training_cost, cv_cost = [], []
for l in l_candidate:
    res = linear_regression_np(X_poly, y, l)

    tc = cost(res.x, X_poly, y)
    cv = cost(res.x, Xval_poly, yval)

    training_cost.append(tc)
    cv_cost.append(cv)

plt.plot(l_candidate, training_cost, label='training')
plt.plot(l_candidate, cv_cost, label='cross validation')
plt.legend(loc=2)

plt.xlabel('lambda')

plt.ylabel('cost')
plt.show()

print('分别 计算 Cost')
for l in l_candidate:
    theta = linear_regression_np(X_poly, y, l).x
    print('test cost(l={}) = {}'.format(l, cost(theta, Xtest_poly, ytest)))

print('0.3 是最佳系数，这时测试误差最小')


# coding: utf-8

# ### DEMQUA03
# # Area under 1-D and 2-D curves, various methods
#
# Uni- and bi-vaiariate integration using Newton-Cotes, Gaussian, Monte Carlo, and quasi-Monte Carlo quadrature methods.

# In[1]:

import numpy as np
from compecon import qnwtrap, qnwsimp, qnwlege, demo
import matplotlib.pyplot as plt
import pandas as pd

# In[2]:

quadmethods = [qnwtrap, qnwsimp, qnwlege]

# ### Make support function

# In[3]:

a, b = -1, 1
nlist = [5, 11, 21, 31]
N = len(nlist)

def quad(func, qnw, n):
    xi, wi = qnw(n,a,b)
    return np.dot(func(xi),wi)

# ## Evaluating $\int_{-1}^1e^{-x}dx$

# In[4]:

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

f_quad = np.array([[quad(f, qnw, ni) for qnw in quadmethods] for ni in nlist])
f_true = np.exp(1) - 1/np.exp(1)
f_error = np.log10(np.abs(f_quad/f_true - 1))

# ## Evaluating $\int_{-1}^1\sqrt{|x|}dx$

# In[5]:

def g(x):
    return np.sqrt(np.abs(x))

g_quad = np.array([[quad(g, qnw, ni) for qnw in quadmethods] for ni in nlist])
g_true = 4/3
g_error = np.log10(np.abs(g_quad/g_true - 1))

# ## Make table with results

# In[6]:

results = pd.DataFrame(np.r_[f_error,g_error])

results.columns=['Trapezoid rule', "Simpson's rule", 'Gauss-Legendre']
results['Integral'] = [r'$\int_{-1}^1e^{-x}dx$']*N + [r'$\int_{-1}^1\sqrt{|x|}dx$']*N
results['Nodes n'] = nlist*2
results.set_index(['Integral', 'Nodes n'], inplace=True)
results

# ## Plot the functions

# In[7]:

a, b, n = -1, 1, 301
x = np.linspace(a, b, n)

plt.figure(figsize=[10,4])
demo.subplot(1, 2, 1, '$e^{-x}$','','',[a,b],[0,f(a)])
plt.plot(x, f(x), linewidth=3)
plt.xticks([-1,0,1])
plt.yticks([0])

demo.subplot(1, 2, 2, '$\sqrt{|x|}$','','',[a,b],[0,g(a)])
plt.plot(x, g(x), linewidth=3)
plt.xticks([-1,0,1])
plt.yticks([0])

# In[8]:

results.to_latex('figures/demqua03.tex', escape=False, float_format='%.1f')
demo.savefig([plt.gcf()])


"""
.. _ica:

==================================================
Background on Independent Component Analysis (ICA)
==================================================

.. contents:: Contents
   :local:
   :depth: 2

Many M/EEG signals including biological artifacts reflect non-Gaussian
processes. Therefore PCA-based artifact rejection will likely perform worse at
separating the signal from noise sources.
MNE-Python supports identifying artifacts and latent components using temporal ICA.
MNE-Python implements the :class:`mne.preprocessing.ICA` class that facilitates applying ICA
to MEG and EEG data. Here we discuss some
basics of ICA.

Concepts
========

ICA finds directions in the feature space corresponding to projections with high non-Gaussianity.

- not necessarily orthogonal in the original feature space, but orthogonal in the whitened feature space.
- In contrast, PCA finds orthogonal directions in the raw feature
  space that correspond to directions accounting for maximum variance.
- or differently, if data only reflect Gaussian processes ICA and PCA are equivalent.

**Example**: Imagine 3 instruments playing simultaneously and 3 microphones
recording mixed signals. ICA can be used to recover the sources ie. what is played by each instrument.

ICA employs a very simple model: :math:`X = AS` where :math:`X` is our observations, :math:`A` is the mixing matrix and :math:`S` is the vector of independent (latent) sources.

The challenge is to recover :math:`A` and :math:`S` from :math:`X`.

First generate simulated data
-----------------------------
"""  # noqa: E501

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

from sklearn.decomposition import FastICA, PCA

np.random.seed(0)  # set seed for reproducible results
n_samples = 2000
time = np.linspace(0, 8, n_samples)

s1 = np.sin(2 * time)  # Signal 1 : sinusoidal signal
s2 = np.sign(np.sin(3 * time))  # Signal 2 : square signal
s3 = signal.sawtooth(2 * np.pi * time)  # Signal 3: sawtooth signal

S = np.c_[s1, s2, s3]
S += 0.2 * np.random.normal(size=S.shape)  # Add noise

S /= S.std(axis=0)  # Standardize data
# Mix data
A = np.array([[1, 1, 1], [0.5, 2, 1.0], [1.5, 1.0, 2.0]])  # Mixing matrix
X = np.dot(S, A.T)  # Generate observations

###############################################################################
# Now try to recover the sources
# ------------------------------

# compute ICA
ica = FastICA(n_components=3)
S_ = ica.fit_transform(X)  # Get the estimated sources
A_ = ica.mixing_  # Get estimated mixing matrix

# compute PCA
pca = PCA(n_components=3)
H = pca.fit_transform(X)  # estimate PCA sources

plt.figure(figsize=(9, 6))

models = [X, S, S_, H]
names = ['Observations (mixed signal)',
         'True Sources',
         'ICA estimated sources',
         'PCA estimated sources']
colors = ['red', 'steelblue', 'orange']

for ii, (model, name) in enumerate(zip(models, names), 1):
    plt.subplot(4, 1, ii)
    plt.title(name)
    for sig, color in zip(model.T, colors):
        plt.plot(sig, color=color)

plt.tight_layout()

###############################################################################
# :math:`\rightarrow` PCA fails at recovering our "instruments" since the
# related signals reflect non-Gaussian processes.


from sympy import FiniteSet, pi
def time_period(length):
    g = 9.8
    T = 2*pi*(length/g)**0.5
    return T

if __name__ == '__main__':
    L = FiniteSet(15,18,21,22.5,25)
    for l in L:
        t = time_period(l/100);
        print('Length: {0} cm Time Period: {1:.3f} s'.format(float(l), float(t)))


from sympy import Eq, Matrix, cancel, expand, fraction, gcd_list, lcm_list, poly, solve, symbols

def sphere(P1, P2, P3, P4):
    # return F(x, y, z) such that F(x, y, z) = 0 is the sphere's equation
    g, h, j, k, x, y, z = symbols('g, h, j, k, x, y, z')
    sphere_eq = Eq(x**2 + y**2 + z**2 + g*x + h*y + j*z + k, 0)
    sphere_eqs = []
    sphere_eqs.append(sphere_eq.subs(x, P1[0]).subs(y, P1[1]).subs(z, P1[2]))
    sphere_eqs.append(sphere_eq.subs(x, P2[0]).subs(y, P2[1]).subs(z, P2[2]))
    sphere_eqs.append(sphere_eq.subs(x, P3[0]).subs(y, P3[1]).subs(z, P3[2]))
    sphere_eqs.append(sphere_eq.subs(x, P4[0]).subs(y, P4[1]).subs(z, P4[2]))
    s = solve(sphere_eqs, (g, h, j, k))
    return fraction(cancel(x**2 + y**2 + z**2 + s[g]*x + s[h]*y + s[j]*z + s[k]))[0]

def coplanar(P1, P2, P3, P4):
    mat = []
    mat.append([P1[0], P1[1], P1[2], 1])
    mat.append([P2[0], P2[1], P2[2], 1])
    mat.append([P3[0], P3[1], P3[2], 1])
    mat.append([P4[0], P4[1], P4[2], 1])
    return Eq(fraction(cancel(Matrix(mat).det()))[0], 0)

def plane(P1, P2, P3):
    x, y, z = symbols('x, y, z')
    return coplanar((x, y, z), P1, P2, P3)

def intersect(P1, P2, P3, slope, sphere_l, ai0, ai1, ai2):
    axes = symbols('x, y, z')
    axis0, axis1, axis2 = axes[ai0], axes[ai1], axes[ai2]
    # return the intersect point P of:
    # `axis2 = plane_p(axis0, axis1)` passing through P1, P2 and P3
    # `axis1 = plane_q(axis0)` with `slope = axis1/axis0` passing through P1
    # `sphere_l(x, y, z) = 0`
    plane_p = solve(plane(P1, P2, P3), axis2)[0]
    plane_q = slope*(axis0 - P1[ai0]) + P1[ai1]
    sphere_coeffs = poly(sphere_l.subs(axis2, plane_p).subs(axis1, plane_q), axis0).all_coeffs()
    P = {}
    P[axis0] = -sphere_coeffs[1]/sphere_coeffs[0] - P1[ai0]
    P[axis1] = plane_q.subs(axis0, P[axis0])
    P[axis2] = plane_p.subs(axis0, P[axis0]).subs(axis1, P[axis1])
    return P[axes[0]], P[axes[1]], P[axes[2]]

def multiplied(x, y, z, w):
    x1, y1, z1, w1 = fraction(cancel(x)), fraction(cancel(y)), fraction(cancel(z)), fraction(cancel(w))
    lcd = lcm_list([x1[1], y1[1], z1[1], w1[1]])
    return x1[0]*cancel(lcd/x1[1]), y1[0]*cancel(lcd/y1[1]), z1[0]*cancel(lcd/z1[1]), w1[0]*cancel(lcd/w1[1])

def to_homogeneous(P):
    return multiplied(P[0], P[1], P[2], 1)

def reduced(x, y, z, w):
    gcd = gcd_list([x, y, z, w])
    if gcd == 0:
        return 0, 0, 0, 1
    return cancel(x/gcd), cancel(y/gcd), cancel(z/gcd), cancel(w/gcd)

def cross(P1, P2, P3):
    x10, x11, x12, x13 = P1[0], P1[1], P1[2], P1[3]
    x20, x21, x22, x23 = P2[0], P2[1], P2[2], P2[3]
    x30, x31, x32, x33 = P3[0], P3[1], P3[2], P3[3]
    # generated by cross-3d.py
    x = -x11*x22*x33 + x11*x23*x32 + x12*x21*x33 - x12*x23*x31 - x13*x21*x32 + x13*x22*x31
    y = x10*x22*x33 - x10*x23*x32 - x12*x20*x33 + x12*x23*x30 + x13*x20*x32 - x13*x22*x30
    z = -x10*x21*x33 + x10*x23*x31 + x11*x20*x33 - x11*x23*x30 - x13*x20*x31 + x13*x21*x30
    w = x10*x21*x32 - x10*x22*x31 - x11*x20*x32 + x11*x22*x30 + x12*x20*x31 - x12*x21*x30
    return reduced(x, y, z, w)

def on_sphere(sphere_h, P):
    x, y, z, w = symbols('x, y, z, w')
    return expand(sphere_h.subs(x, P[0]).subs(y, P[1]).subs(z, P[2]).subs(w, P[3])) == 0

def main():
    # https://imomath.com/index.cgi?page=inversion (Problem 11)
    a, b, c, d, e, f, g, h, j, x, y, z, w = symbols('a, b, c, d, e, f, g, h, j, x, y, z, w')
    # quick test a special case
    # b, d, e = 0, 0, 0
    O, A, B, C = (0, 0, 0), (a, 0, 0), (b, c, 0), (d, e, f)
    sphere_l = sphere(O, A, B, C)
    print('Sphere Equation:', sphere_l, '= 0')
    # If we choose plane $y=kx$ through point O, we'll get G with too many terms:
    # G[0] has 12319 terms
    # G[1] has 9959 terms
    # G[2] has 6337 terms
    # G[3] has 8760 terms
    # D = intersect(O, B, C, g, sphere_l, 1, 2, 0)
    # E = intersect(O, C, A, h, sphere_l, 2, 0, 1)
    # F = intersect(O, A, B, j, sphere_l, 0, 1, 2)
    D = intersect(C, B, O, g, sphere_l, 1, 2, 0)
    E = intersect(A, C, O, h, sphere_l, 2, 0, 1)
    F = intersect(B, A, O, j, sphere_l, 0, 1, 2)
    sphere_h = poly(sphere_l, (x, y, z)).homogenize(w).expr
    print('Sphere Equation in Homogeneous:', sphere_h, '= 0')
    A, B, C = to_homogeneous(A), to_homogeneous(B), to_homogeneous(C)
    D, E, F = to_homogeneous(D), to_homogeneous(E), to_homogeneous(F)
    print('D:', D)
    print('E:', E)
    print('F:', F)
    print('Is A on Sphere?', on_sphere(sphere_h, A))
    print('Is B on Sphere?', on_sphere(sphere_h, B))
    print('Is C on Sphere?', on_sphere(sphere_h, C))
    print('Is D on Sphere?', on_sphere(sphere_h, D))
    print('Is E on Sphere?', on_sphere(sphere_h, E))
    print('Is F on Sphere?', on_sphere(sphere_h, F))
    CDE, BFD, AEF = cross(C, D, E), cross(B, F, D), cross(A, E, F)
    print('CDE:', CDE)
    print('BFD:', BFD)
    print('AEF:', AEF)
    G = cross(CDE, BFD, AEF)
    print('G:', G)
    print('G[0] has', len(G[0].args), 'terms')
    print('G[1] has', len(G[1].args), 'terms')
    print('G[2] has', len(G[2].args), 'terms')
    print('G[3] has', len(G[3].args), 'terms')
    print('Is G on Sphere?', on_sphere(sphere_h, G))

if __name__ == '__main__':
    main()


import sys
import scipy.io as sio
from pprint import pprint
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

def estimate_coef(x, y):
    n = np.size(x)
    m_x, m_y = np.mean(x), np.mean(y)
    # calculating regression coefficients
    Sxy = np.sum(y*x) - n*m_y*m_x
    Sxx = np.sum(x*x) - n*m_x*m_x
    theta_1 =  Sxy/Sxx
    theta_0 = m_y - theta_1*m_x
    return(theta_0, theta_1)

x = np.array([5, 15, 25, 35, 45, 55])
y = np.array([5, 20, 14, 32, 22, 38])

x = (x - x.min())/(x.max() - x.min())

theta = estimate_coef(x, y)
print("theta0:", theta[0], " theta1:", theta[1])

error = y - (theta[0] + theta[1] * x)
print("Error:", error)

plt.figure(figsize=(8, 6))
plt.scatter(x, y, color = "r", marker = "o", s = 30)
plt.title("Predicted Regression Line", fontsize=16)
y_pred = theta[0] + theta[1]*x
plt.plot(x, y_pred, color = "b")
plt.legend(['h(x)', '(x, y)'], loc='best')

plt.xlabel('X', fontsize=14)
plt.ylabel('Y', fontsize=14)
plt.show()


from numpy import eye
from numpy.linalg import solve

def MinRelEntNormal(mu_pri, s2_pri, v_mu, mu_view, v_sigma, sigma_view):
    # This function computes the Minimum Relative Entropy posterior parameters of the
    # normal distribution which satisfies the constraints due to the views on the expectations
    # and the covariances, and that minimizes the relative entropy with respect to the prior normal distribution of n_
    # arbitrary market variables.
    #  INPUTS
    #   mu_pri     : [vector] (n_ x 1)  prior expectation
    #   s2_pri     : [matrix] (n_ x n_) prior covariance matrix
    #   v_mu       : [matrix] (k_ x n_) matrix specifying the views for the expectations
    #   mu_view    : [vector] (k_ x 1)  vector quantifying the views for the expectations
    #   v_sigma    : [matrix] (s_ x n_) matrix specifying the views for the covariances
    #   sigma_view : [matrix] (s_ x s_) vector quantifying the views for the covariances
    #  OPS
    #   mu_pos     : [vector] (n_ x 1)  posterior expectation
    #   s2_pos     : [matrix] (n_ x n_) posterior covariance matrix

    # For details on the exercise, see here .
    s_, n_ = v_sigma.shape

    mu_pos = mu_pri + s2_pri@v_mu.T@solve(v_mu@s2_pri@v_mu.T,mu_view - v_mu@mu_pri)
    # a = inv(v_sigma@s2_pri@v_sigma.T)
    a = solve(v_sigma@s2_pri@v_sigma.T,eye(s_))
    s2_pos = s2_pri + s2_pri@v_sigma.T@(a@sigma_view@a - a)@v_sigma@s2_pri
    return mu_pos, s2_pos


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

np.random.seed(12456)
x1 = np.random.normal(size=200)  # random data, normal distribution
xs = np.linspace(x1.min()-1, x1.max()+1, 200)

kde1 = stats.gaussian_kde(x1)
kde2 = stats.gaussian_kde(x1, bw_method='silverman')

fig = plt.figure(figsize=(8, 6))

ax1 = fig.add_subplot(211)
ax1.plot(x1, np.zeros(x1.shape), 'b+', ms=12)  # rug plot
ax1.plot(xs, kde1(xs), 'k-', label="Scott's Rule")
ax1.plot(xs, kde2(xs), 'b-', label="Silverman's Rule")
ax1.plot(xs, stats.norm.pdf(xs), 'r--', label="True PDF")

ax1.set_xlabel('x')
ax1.set_ylabel('Density')
ax1.set_title("Normal (top) and Student's T$_{df=5}$ (bottom) distributions")
ax1.legend(loc=1)

x2 = stats.t.rvs(5, size=200)  # random data, T distribution
xs = np.linspace(x2.min() - 1, x2.max() + 1, 200)

kde3 = stats.gaussian_kde(x2)
kde4 = stats.gaussian_kde(x2, bw_method='silverman')

ax2 = fig.add_subplot(212)
ax2.plot(x2, np.zeros(x2.shape), 'b+', ms=12)  # rug plot
ax2.plot(xs, kde3(xs), 'k-', label="Scott's Rule")
ax2.plot(xs, kde4(xs), 'b-', label="Silverman's Rule")
ax2.plot(xs, stats.t.pdf(xs, 5), 'r--', label="True PDF")

ax2.set_xlabel('x')
ax2.set_ylabel('Density')

plt.show()


"""
Arthur & Vassilvitskii k-means++ algorithm

See: k-means++: The advantages of careful seeding
http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf
"""

import numpy as np

from initialisations.base import Initialisation
import kmeans

class Kmeansplusplus(Initialisation):
    """k-means++ initialisation algorithm"""

    def find_centers(self):

        # Initial centroid
        randindex = np.random.choice(self._num_samples, replace=False)
        centroids = np.array([self._data[randindex]])

        # Remaining required centroids
        while len(centroids) < self._num_clusters:

            distances = kmeans.distance_table(self._data, centroids)
            probabilities = distances.min(1)**2 / np.sum(distances.min(1)**2)

            randindex = np.random.choice(self._num_samples,
                                         replace=False,
                                         p=probabilities)
            centroids = np.append(centroids, [self._data[randindex]], axis=0)

        return centroids

def generate(data, num_clusters):
    """The common interface"""

    kmpp = Kmeansplusplus(data, num_clusters)
    return kmpp.find_centers()


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

nx=5
L=0.5
k=1000
TA=100
TB=500
A=10E-3
dx=L/float(nx)

x=np.zeros(nx)
for i in xrange(nx):
    x[i]=dx*(float(i)+0.5)

amat=np.zeros([nx,nx])
b=np.zeros(nx)

for i in xrange(1, nx-1):
    aw=ae=k/dx*A
    ap=aw+ae
    amat[i,i-1]=-aw
    amat[i,i]=ap
    amat[i,i+1]=-ae
    b[i]=0

aw=0
ae=k/dx*A
Sp=-2*k*A/dx
Su=2*k*A/dx*TA
ap=aw+ae-Sp

amat[0,0]=ap
amat[0,1]=-ae
b[0]=Su

aw=k*A/dx
ae=0
Sp=-2*k*A/dx
Su=2*k*A/dx*TB
ap=aw+ae-Sp

amat[nx-1,nx-2]=-aw
amat[nx-1,nx-1]=ap
b[nx-1]=Su

print amat
print b

T=np.linalg.solve(amat,b)
print T

Texact = np.zeros(nx)
for i in xrange(nx):
    x[i]=dx*(float(i)+0.5)
    Texact[i]=TA+(TB-TA)/L*x[i]
print Texact

fig=plt.figure()
plt.plot(x,T)
plt.plot(x,Texact,"*")

plt.xlim(0,L)

plt.savefig('main.png')


from sympy import *

def plucker_line_K(fx, fy, cx, cy):
    return Matrix([[fy,0,0],[0,fx,0],[-fy*cx,-fx*cy,fx*fy]])

def plucker_line_motion_matrix_cw(RT_wc):
    a=RT_wc[0,3]
    b=RT_wc[1,3]
    c=RT_wc[2,3]
    rt=Matrix([[RT_wc[0,0], RT_wc[1,0], RT_wc[2,0]],
               [RT_wc[0,1], RT_wc[1,1], RT_wc[2,1]],
               [RT_wc[0,2], RT_wc[1,2], RT_wc[2,2]]])
    tx=Matrix([[0,-c,b],[c,0,-a],[-b,a,0]])
    rttx=rt*tx
    return Matrix([[rt[0,0], rt[0,1], rt[0,2], -rttx[0,0], -rttx[0,1], -rttx[0,2]],
                   [rt[1,0], rt[1,1], rt[1,2], -rttx[1,0], -rttx[1,1], -rttx[1,2]],
                   [rt[2,0], rt[2,1], rt[2,2], -rttx[2,0], -rttx[2,1], -rttx[2,2]],
                   [0,0,0, rt[0,0], rt[0,1], rt[0,2]],
                   [0,0,0, rt[1,0], rt[1,1], rt[1,2]],
                   [0,0,0, rt[2,0], rt[2,1], rt[2,2]]])

def plucker_line_motion_matrix_wc(RT_wc):
    a=RT_wc[0,3]
    b=RT_wc[1,3]
    c=RT_wc[2,3]
    r=Matrix([[RT_wc[0,0], RT_wc[0,1], RT_wc[0,2]],
              [RT_wc[1,0], RT_wc[1,1], RT_wc[1,2]],
              [RT_wc[2,0], RT_wc[2,1], RT_wc[2,2]]])
    tx=Matrix([[0,-c,b],[c,0,-a],[-b,a,0]])
    txr=tx*r
    return Matrix([[r[0,0], r[0,1], r[0,2], txr[0,0], txr[0,1], txr[0,2]],
                   [r[1,0], r[1,1], r[1,2], txr[1,0], txr[1,1], txr[1,2]],
                   [r[2,0], r[2,1], r[2,2], txr[2,0], txr[2,1], txr[2,2]],
                   [0,0,0, r[0,0], r[0,1], r[0,2]],
                   [0,0,0, r[1,0], r[1,1], r[1,2]],
                   [0,0,0, r[2,0], r[2,1], r[2,2]]])


import numpy

from matplotlib import pyplot
from matplotlib import rcParams

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from math import pi

rcParams['font.family'] = 'serif'
rcParams['font.size'] = 16

def plot_3D(x, y, p):
    '''Creates 3D plot with appropriate limits and viewing angle

    Parameters:
    ----------
    x: array of float
        nodal coordinates in x
    y: array of float
        nodal coordinates in y
    p: 2D array of float
        calculated potential field

    '''
    fig = pyplot.figure(figsize=(11,7), dpi=100)
    ax = fig.gca(projection='3d')
    X,Y = numpy.meshgrid(x,y)
    surf = ax.plot_surface(X,Y,p[:], rstride=1, cstride=1, cmap=cm.viridis,
            linewidth=0, antialiased=False)

    #ax.set_xlim(0,1)
    #ax.set_ylim(0,1)
    ax.set_xlabel('$x$')
    ax.set_ylabel('$y$')
    ax.set_zlabel('$z$')
    ax.view_init(30,45)

def L2_error(p, pn):
	return numpy.sqrt(numpy.sum((p - pn)**2)/numpy.sum(pn**2))

def L1norm(new, old):
    norm = numpy.sum(numpy.abs(new-old))
    return norm

def initialise(nx, ny, xmax, xmin, ymax, ymin):
    '''Initialize the Poisson problem initial guess and other variables
    Parameters:
    ----------
    nx : int
        number of mesh points in x
    ny : int
        number of mesh points in y
    xmax: float
        maximum value of x in mesh
    xmin: float
        minimum value of x in mesh
    ymax: float
        maximum value of y in mesh
    ymin: float
        minimum value of y in mesh

    Returns:
    -------
    X  : 2D array of floats
        X-position of mesh
    Y  : 2D array of floats
        Y-position of mesh
    p_i: 2D array of floats
        initial guess of p
    dx : float
        mesh size in x direction
    dy : float
        mesh size in y direction
    '''

    dx = (xmax-xmin)/(nx-1)
    dy = (ymax-ymin)/(ny-1)

    # Mesh
    x  = numpy.linspace(xmin,xmax,nx)
    y  = numpy.linspace(ymin,ymax,ny)
    X,Y = numpy.meshgrid(x,y)

    # Initialize
    p_i  = numpy.zeros((ny,nx))

    return X, Y, x, y, p_i, dx, dy

def calculate(omega, psi, dx, dy, l1_target):
    '''Performs Jacobi relaxation

    Parameters:
    ----------
    omega : 2D array of floats
        Initial guess for omega
    psi : 2D array of floats
        Initial guess for psi
    dx: float
        Mesh spacing in x direction
    dy: float
        Mesh spacing in y direction
    l1_target: float
        Target difference between two consecutive iterates

    Returns:
    -------
    omega: 2D array of float
        Distribution after relaxation
    '''

    l1_norm_omega = 1.
    l1_norm_psi = 1.
    iterations = 0

    nx, ny = omega.shape
    u_j = 1.

    while l1_norm_omega > l1_target or l1_norm_psi > l1_target:

        # calculate psi

        psi_d = psi.copy()

        psi[1:-1,1:-1] = 1./(2.*(dx**2 + dy**2)) * \
                         ((psi_d[1:-1,2:]+psi_d[1:-1,:-2])*dy**2 +\
                         (psi_d[2:,1:-1] + psi_d[:-2,1:-1])*dx**2 -\
                         -omega[1:-1,1:-1]*dx**2*dy**2)

        # BCs for psi are automatically enforced

        l1_norm_psi = L1norm(psi_d, psi)

        # calculate omega

        omega_d = omega.copy()

        omega[1:-1,1:-1] = .25 * (omega_d[1:-1,2:] + omega_d[1:-1, :-2] \
                       + omega_d[2:, 1:-1] + omega_d[:-2, 1:-1])

        factor = (-1./(2.*(dy**2)));

        ##Neumann B.C.
        omega[-1, 1:-1] = (factor*((8.*psi[-2, 1:-1]) - psi[-3, 1:-1])) - ((3. * u_j) / dy)
        # bottom
        omega[0, 1:-1] = (factor*((8.*psi[1, 1:-1]) - psi[2, 1:-1]))
        # right
        omega[1:-1, -1] = (factor*((8.*psi[1:-1, -2]) - psi[1:-1, -3]))
        # left
        omega[1:-1, 0] = (factor*((8.*psi[1:-1, 1]) - psi[1:-1, 2]))

        l1_norm_omega = L1norm(omega_d, omega)

        iterations += 1

    print('Number of Jacobi iterations: {0:d}'.format(iterations))
    return omega, psi

##variable declarations
nx = 41
ny = 41

xmin = 0.
xmax = 1.
ymin = 0.
ymax = 1.
l1_target = 1e-6
X_omega, Y_omega, x_omega, y_omega, omega_i, dx, dy = initialise(nx, ny, xmax, xmin, ymax, ymin)
X_psi, Y_psi, x_psi, y_psi, psi_i, dx, dy = initialise(nx, ny, xmax, xmin, ymax, ymin)
#plot_3D(x, y, p_i)

omega, psi = calculate(omega_i.copy(), psi_i.copy(), dx, dy, l1_target)

# plot_3D(x_psi, y_psi, psi)
pyplot.contourf(x_psi,y_psi,psi,20, cmap=cm.viridis)

maxPsi = numpy.absolute(psi).max()

print('max psi: {0:f}'.format(maxPsi))

maxOmega = numpy.absolute(omega).max()

print('max omega: {0:f}'.format(maxOmega))

pts = numpy.round(psi[32,::8], 4)
print(pts)

pyplot.show()


import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import cholesky
from numpy import linalg

def gaussSample(mu, sigma, n):
    A = cholesky(sigma)
    Z = np.random.normal(loc=0, scale=1, size=(len(mu), n))
    return np.dot(A, Z).T + mu

def ridge(X, y, lam):
    """
    This function can be completed pinv as well
    """
    W = np.dot(linalg.pinv((np.dot(X.T, X) + np.sqrt(lam) * np.eye(X.shape[0]))), np.dot(X.T, y))
    return W

def basisExpansion(X, s=None, centers=[]):
    n = 25
    if not s:
        s = np.std(X) / np.sqrt(n)

    if not len(centers):
        centers = X[1:]

    Xbasis = np.ones((X.shape[0], n))
    for i in range(1, n):
        Xbasis[:, i] = np.ravel(np.exp((-1 / (2 * s ** 2)) * (X - centers[i - 1]) ** 2))
    return Xbasis, s, centers

def fun(X):
    """
    Cosine function
    """
    return np.cos(2 * np.pi * X)

def synthesizeData(n, d):
    sigma = np.array([[0.1]])
    mu = np.array([0])
    X = np.random.rand(n, d)
    y = fun(X) + gaussSample(mu, sigma, n)
    return X, y

n = 25
d = 1
lambdas = [np.exp(5), np.exp(-5)]
ndataSets = 100
showNsets = 20
np.random.seed(42)

domain = np.arange(0, 1, 0.0005)
fs = 16

nr = 2
nc = 2

fig, ax = plt.subplots(2, 2, figsize=(12, 9))
for lam in range(len(lambdas)):
    yhat = np.zeros((len(domain), ndataSets))
    for j in range(ndataSets):
        X, y = synthesizeData(n, d)
        X, s, centers = basisExpansion(X)
        W = ridge(X, y, lambdas[lam])
        yhat[:, j] = np.ravel(np.dot(basisExpansion(domain, s, centers)[0], W))

    ax[lam, 0].plot(domain[..., np.newaxis].repeat(20, axis=1), yhat[:, :showNsets], color='#ff7f00')
    ax[lam, 0].set_xlim([-0.1, 1.1])
    ax[lam, 0].set_ylim([-1.5, 1.5])
    ax[lam, 0].set_title('ln($\lambda$) = {}'.format(np.log(lambdas[lam])))

    ax[lam, 1].plot(domain, fun(domain), lineWidth=2.5)
    ax[lam, 1].plot(domain, np.mean(yhat, axis=1), linestyle=':', lineWidth=2.5)
    ax[lam, 1].set_title('ln($\lambda$) = {}'.format(np.log(lambdas[lam])))

fig.savefig('../figures/biasVarModelComplexityV3.png')


from __future__ import division
import doctest
import pylab as pl
import numpy as np
# a)	use newton's method to calculate roots
# 		equation = x^3 - x - 5
#		x_initial = 0.57735
#		max_iter = 50
#		print the results and explain them

## TODO : Write tests
# x^3 - x - 5 , re-written to get rid of powers
def f(x):
    '''
    >>> f(1)
    -5
    >>> f(2)
    1
    >>> f(3)
    19
    '''
    return (x * (x+1) * (x-1)) - 5

# 3 x^2 - 1 , re-written to get rid of powers
def f_dash(x):
    '''
    >>> f_dash(1)
    2
    >>> f_dash(2)
    11
    '''
    return (3 * (x + 1) * (x - 1)) + 2

def get_next_appox(x_n_minus_1):
    x = x_n_minus_1
    return x - (f(x) / f_dash(x))

if __name__ == '__main__':
    doctest.testmod()
    x_n_minus_1 = 0.57735 # x_initial
    # iterate for a maximum of 50 iterations
    max_iter = 50
    output = []
    for _ in xrange(max_iter):
    	x_n = get_next_appox(x_n_minus_1)
    	output.append((x_n_minus_1, f(x_n_minus_1)))
	x_n_minus_1 = x_n

    for val in output:
	print "& {} & {} & \\\\".format(val[0], val[1])

    pl.figure(1)
    # plot the curve
    x = np.linspace(-5,5,1000)
    y = [f(_) for _ in x]
    pl.grid(True)
    pl.plot(x,y)
    pl.scatter(output[-1][0],0)
    # pl.scatter([v[0] for v in output],[0]*len(output), c=[np.linspace(0,1,3) for _ in range(20)])
    # pl.xlim(xmin=-5,xmax=5)
    pl.savefig("solution2a_fig.pdf")
    pl.show()


import numpy as np
from lasso_utils import *

def iterative_soft_thresholding_lasso_solver(X, y, lam, t, tol, verbose=False):
    """
    Parameters:
    @X {2D numpy array} matrix of predictor variables
    @y {numpy array} continuous response variable
    @lam {float} regularization parameter
    @t {float} step size for use in proximal gradient descent
    @tol {float} converge tolerance (threshold)
    @verbose {optional, boolean} flag stating whether or not to print info
    on each iteration to the console
    Return:
    @beta_curr {1D numpy array} vector regression coefficients resulting from
    ISTA algorithm
    This function implements the iterative soft thresholding algorithm (ISTA)
    in order to estimate regression coefficients for the lasso problem.
    """

    # get the number of cols in X, i.e., the number of predictors
    n_col = X.shape[1]

    # transpose X
    XT = np.transpose(X)

    # initialize the parameter vectors from the previous iteration and the one
    # that results from the current iteration, respectively.
    beta_prev = np.zeros(n_col)
    beta_curr = np.zeros(n_col)

    # initialize the iteration counter
    k = 0

    # set the convergence flag to false
    converged = False

    # continue to loop and update parameter values until the convergence
    # threshold is reached
    while converged == False:
        # take a gradient step
        z_k = beta_prev + np.dot(t*XT, y - np.dot(X, beta_prev))

        # update beta by solving the prox with soft-thresholding

        # calc (|z_k,j|-t*lam))_+
        pos_part = np.abs(z_k) - t*lam

        # only keep the actual positive part
        for i in range(0, n_col):
            if (pos_part[i] <= 0):
                pos_part[i] = 0

        # update the current parameter vector
        beta_curr = np.sign(z_k) * pos_part

        # compute the value of the lass loss function for the current iteration
        loss_curr = lasso_loss(X, y, beta_curr, lam)

        # check to see if the 2-norm squared for beta_cur-beta_prev
        # is sufficiently small, if so we have converged, if not
        # do another iteration
        two_norm_sq = np.linalg.norm(beta_curr-beta_prev)**2

        if (two_norm_sq <= tol):
            converged = True

        # if verbose output is desired, print info on the current iteration
        if verbose:
            print "Iter %d done, l2^2=%.03f, Lasso Loss=%.03f" % (k, two_norm_sq, loss_curr)

        # overwrite the previous value of the parameter vector
        beta_prev = np.copy(beta_curr)

        # update the iteration count
        k = k + 1

    # once we have converged, return the parameter vector
    return beta_curr

def fast_iterative_soft_thresholding_lasso_solver(X, y, lam, t, tol, verbose=False):
    """
    Parameters:
    @X {2D numpy array} matrix of predictor variables
    @y {numpy array} continuous response variable
    @lam {float} regularization parameter
    @t {float} step size for use in proximal gradient descent
    @tol {float} converge tolerance (threshold)
    @verbose {optional, boolean} flag stating whether or not to print info
    on each iteration to the console
    Return:
    @beta_curr {1D numpy array} vector regression coefficients resulting from
    ISTA algorithm
    This function implements the FAST iterative soft thresholding algorithm (FISTA)
    in order to estimate regression coefficients for the lasso problem.
    It is quite similar to ISTA, but acceleration is used.
    """

    # get the number of cols, which is also the number of predictors
    n_col = X.shape[1]

    # transpose X
    XT = np.transpose(X)

    # initialize the parameter vectors from the previous iteration and one iterationand the one
    # that results from the current iteration, respectively.
    beta_prev_k_1 = np.zeros(n_col)
    beta_prev_k_2 = np.zeros(n_col)

    # top row is one iterations ago
    # bottom row is two iterations ago
    beta_prev = np.vstack((beta_prev_k_1, beta_prev_k_2))

    # initialize the iteration counter
    k = 2

    # set the convergence flag to false
    converged = False

    # continue to loop and update parameter values until the convergence
    # threshold is reached
    while converged==False:

        # update theta, convert to float to avoid integer division
        theta_k = beta_prev[0,:] + ((1.0*(k-2))/(1.0*(k+1))) * (beta_prev[0,:] - beta_prev[1,:])

        # update z_k, take a gradient step
        z_k = theta_k + np.dot(t*XT, y - np.dot(X,theta_k))

        # update beta by solving the prox
        # calc (|z_k,j|-t*lam))_+
        pos_part = np.abs(z_k) - t*lam

        # only keep the positive part
        for i in range(0,n_col):
            if (pos_part[i] <= 0):
                pos_part[i] = 0

        beta_curr = np.sign(z_k) * pos_part

        # check to see if the 2-norm squared for beta_cur-beta_prev
        # is sufficiently small, if so we have converged, if not
        # do another iteration
        two_norm = np.linalg.norm(beta_curr-beta_prev[0,:])**2

        # compute the value of the lasso loss function for the current iteration
        loss_curr = lasso_loss(X,y,beta_curr,lam)

        # if verbose output is desired, print info on the current iteration
        if verbose:
            print "Iter %d done, l2^2=%.03f, Lasso Loss=%.03f" % (k-1,two_norm,loss_curr)

        #beta_prev = np.copy(beta_curr)

        # move the top row (one iter ago) to the bottom row (so it is now two iter ago)
        # make the val for one iter ago the current val
        for j in range(0,n_col):
            beta_prev[1,j] = np.copy(beta_prev[0,j])
            beta_prev[0,j] = np.copy(beta_curr[j])

        # update the iteration count
        k = k + 1

        # check convergence
        if (two_norm <= tol):
            converged = True

    # return the parameter vector
    return beta_curr

def lasso_shooting(X, y, lam, tol, verbose=False):
    """
    Parameters:
    @X {2D numpy array} matrix of predictor variables
    @y {numpy array} continuous response variable
    @lam {float} regularization parameter
    @tol {float} converge tolerance (threshold)
    @verbose {optional, boolean} flag stating whether or not to print info
    on each iteration to the console
    Return:
    @beta_curr {1D numpy array} vector regression coefficients
    This function implements the shooting algorithm which uses coordinate
    descent in order to solve the lasso problem.
    """

    # get the number of cols in X, i.e., the number of predictors
    n_col = X.shape[1]

    # initialize the parameter vectors from the previous iteration and the one
    # that results from the current iteration, respectively.
    beta_prev = np.zeros(n_col)
    beta_curr = np.zeros(n_col)

    # initialize the iteration count
    k = 0

    # set the convergence flag to false
    converged = False

    # continue to loop and update parameter values until the convergence
    # threshold is reached
    while converged==False:
        # update the iteration count
        k = k + 1

        # get the beta vector from the previous iteration and store it before
        # any of its elements are modified
        beta_old = np.copy(beta_prev)

        for j in range(0,n_col):
            # del/del_w_j RSS(w) = a_j*w_j - c_j
            a_j = 2 * np.dot(X[:, j],X[:, j])

            # proportional to corr between jth variable and residual
            # excluding the jth variable
            c_j = 2 * np.dot(X[:, j], (y - np.dot(X, beta_prev) + beta_prev[j]*X[:, j]))

            # apply soft thresholding
            pos_part = np.abs(c_j/a_j) - (lam/a_j)
            if pos_part <= 0:
                pos_part = 0

            # update the jth coefficient of the parameter vector
            beta_curr[j] = np.sign(c_j/a_j) * pos_part

            # VERY IMPORTANT: NEED TO UPDATE BETA_PREV WITH THE NEW jth COEFF
            beta_prev[j] = np.copy(beta_curr[j])

        # check to see if the 2-norm squared for beta_cur-beta_prev
        # is sufficiently small, if so we have converged, if not
        # do another iteration
        two_norm = np.linalg.norm(beta_prev-beta_old)**2

        # compute the value of the lasso loss function for the current iteration
        loss_curr = lasso_loss(X, y, beta_curr, lam, True)

        # if verbose output is desired, print info on the current iteration
        if verbose:
            print "Iter %d done, l2^2=%.03f, Loss=%.03f" % (k, two_norm, loss_curr)

        # check convergence
        if (two_norm <= tol):
            converged = True

    # return the parameter vector
    return beta_curr


import timeit

import numpy as np
from tabulate import tabulate

from KMedoids import load_dataset
from Visualization import Cluster_viz

def compute_dist(pt1: np.ndarray, pt2: np.ndarray):
    return np.sqrt(np.sum((pt1 - pt2) ** 2))

def init_centroid(data_pt: np.ndarray, k: int):
    # np.random.seed(54)
    centroid_idx = np.random.randint(low=0, high=data_pt.shape[0], size=k)

    return centroid_idx

def cluster_assignment(data_pt: np.ndarray, data_idx: int, centroids: np.ndarray, clusters: dict):
    distance = dict()

    for centroid_idx in clusters.keys():
        distance[centroid_idx] = compute_dist(data_pt[data_idx], centroids[centroid_idx])

    # print('Distance :', distance)
    min_idx = min(distance, key=distance.get)

    return min_idx

def update_centroid(data_pt: np.ndarray, clusters: dict):
    centroids_updated = list()

    for data_idx in clusters.values():
        cl_data = data_pt[data_idx]
        mean = np.mean(cl_data, axis=0)

        centroids_updated.append(mean)

    return centroids_updated

def k_means(data_pt: np.ndarray, k: int, visualize=False):
    centroids = data_pt[init_centroid(data_pt, k)]

    i = 0

    if visualize:
        viz = Cluster_viz(data_pt)

    while True:
        i = i + 1
        y_pred = list()

        _clusters = np.zeros(data_pt.shape[0])
        clusters = {label: [] for label in range(k)}
        for data_idx in range(data_pt.shape[0]):
            min_idx = cluster_assignment(data_pt, data_idx, centroids, clusters)

            y_pred.append(min_idx)

            clusters[min_idx].append(data_idx)
            _clusters[data_idx] = min_idx

        # print(centroids)
        # print(update_centroid(data_pt, clusters))

        if np.array_equal(centroids, update_centroid(data_pt, clusters)):
            break

        centroids = update_centroid(data_pt, clusters)

        table = []

        for title, indices in clusters.items():
            table.append([title, len(indices)])

        print('\nIteration -', i)
        print(tabulate(table, headers=['Cluster', '# of Members'], tablefmt="fancy_grid"))
        if visualize:
            viz.visualize_iteration(i, _clusters)

    return centroids, clusters, y_pred

if __name__ == '__main__':
    # data_pt = load_dataset('Dataset/wine.data', exclude_cols=[0])
    data_pt, _ = load_dataset('Dataset/weather_madrid_LEMD_1997_2015.csv', exclude_cols=[0, 21], exclude_rows=[0])

    # print(data_pt)
    start = timeit.default_timer()
    centroids, clusters, _ = k_means(data_pt, k=6, visualize=True)
    stop = timeit.default_timer()

    print('\nTotal Time Elepsed (Sec) :', stop - start)


# -*- coding: utf-8 -*-
"""
Created on Tue Apr 14 17:00:34 2020

@author: Travis Czechorski tjczec01@gmail.com
"""
import numpy as np
import pprint as pp
import sympy as sp
import scipy.linalg as la
import scipy as sc
import decimal
from decimal import Decimal, getcontext, DefaultContext
De = decimal.Decimal
DefaultContext.prec = 25

pp = pp.pprint
EPS = np.finfo(float).eps
# print("{:.52f}".format(EPS))
As = sp.Matrix([[3, 2, 3],
     [4, 6, 6],
     [7, 4, 9]])
Bs = sp.Matrix([[5, 5], [6, 7], [9, 9]])
AM = [[3, 2, 3],      [4, 6, 6],      [7, 4, 9]]
B = [[5, 5], [6, 7], [9, 9]]
AN = np.array(AM)
# print(np.array(A).T)
# print(len("0000000000000002220446049250313080847263336181640625"))
flatten = lambda l: [item for sublist in l for item in sublist]

def shape(Ax):
       rows = len(Ax)
       cols = len(Ax[0])
       shape = list((rows, cols))
       ts = tuple((rows, cols))
       print("{} Rows x {} Columns".format(ts[0], ts[1]))
       print(ts)
       return shape

def zeros_matrix(rows, cols):
    """
    Creates a matrix filled with zeros.
        :param rows: the number of rows the matrix should have
        :param cols: the number of columns the matrix should have
        :return: list of lists that form the matrix
    """
    M = []
    while len(M) < rows:
        M.append([])
        while len(M[-1]) < cols:
            M[-1].append(0.0)

    return M

def identity_matrix(n):
    """
    Creates and returns an identity matrix.
        :param n: the square size of the matrix
        :return: a square identity matrix
    """
    IdM = zeros_matrix(n, n)
    for i in range(n):
        IdM[i][i] = 1.0

    return IdM

def copy_matrix(M):
    """
    Creates and returns a copy of a matrix.
        :param M: The matrix to be copied
        :return: A copy of the given matrix
    """
    # Section 1: Get matrix dimensions
    rows = len(M)
    cols = len(M[0])

    # Section 2: Create a new matrix of zeros
    MC = zeros_matrix(rows, cols)

    # Section 3: Copy values of M into the copy
    for i in range(rows):
        for j in range(cols):
            MC[i][j] = M[i][j]

    return MC

def check_matrix_equality(A, B, tol=None):
    """
    Checks the equality of two matrices.
        :param A: The first matrix
        :param B: The second matrix
        :param tol: The decimal place tolerance of the check
        :return: The boolean result of the equality check
    """
    # Section 1: First ensure matrices have same dimensions
    if len(A) != len(B) or len(A[0]) != len(B[0]):
        return False

    # Section 2: Check element by element equality
    #            use tolerance if given
    for i in range(len(A)):
        for j in range(len(A[0])):
            if tol is None:
                if A[i][j] != B[i][j]:
                    return False
            else:
                if round(A[i][j], tol) != round(B[i][j], tol):
                    return False

    return True

def check_squareness(A):
    """
    Makes sure that a matrix is square
        :param A: The matrix to be checked.
    """
    if len(A) != len(A[0]):
        raise ArithmeticError("Matrix must be square to inverse.")

def check_non_singular(A):
    """
    Ensure matrix is NOT singular
        :param A: The matrix under consideration
        :return: determinant of A - nonzero is positive boolean
                  otherwise, raise ArithmeticError
    """
    det = determinant_fast(A)
    if det != 0:
        return det
    else:
        raise ArithmeticError("Singular Matrix!")

def matrix_multiply(A, B):
    """
    Returns the product of the matrix A * B
        :param A: The first matrix - ORDER MATTERS!
        :param B: The second matrix
        :return: The product of the two matrices
    """
    # Section 1: Ensure A & B dimensions are correct for multiplication
    rowsA = len(A)
    colsA = len(A[0])
    rowsB = len(B)
    colsB = len(B[0])
    if colsA != rowsB:
        raise ArithmeticError(
            'Number of A columns must equal number of B rows.')

    # Section 2: Store matrix multiplication in a new matrix
    C = zeros_matrix(rowsA, colsB)
    for i in range(rowsA):
        for j in range(colsB):
            total = 0
            for ii in range(colsA):
                total += A[i][ii] * B[ii][j]
            C[i][j] = total

    return C

def determinant_recursive(A, total=0):
    """
    Find determinant of a square matrix using full recursion
        :param A: the matrix to find the determinant for
        :param total=0: safely establish a total at each recursion level
        :returns: the running total for the levels of recursion
    """
    # Section 1: store indices in list for flexible row referencing
    indices = list(range(len(A)))

    # Section 2: when at 2x2 submatrices recursive calls end
    if len(A) == 2 and len(A[0]) == 2:
        val = A[0][0] * A[1][1] - A[1][0] * A[0][1]
        return val

    # Section 3: define submatrix for focus column and call this function
    for fc in indices:  # for each focus column, find the submatrix ...
        As = copy_matrix(A)  # make a copy, and ...
        As = As[1:]  # ... remove the first row
        height = len(As)

        for i in range(height):  # for each remaining row of submatrix ...
            As[i] = As[i][0:fc] + As[i][fc+1:]  # zero focus column elements

        sign = (-1) ** (fc % 2)  # alternate signs for submatrix multiplier
        sub_det = determinant_recursive(As)  # pass submatrix recursively
        total += sign * A[0][fc] * sub_det  # total all returns from recursion

    return total

def determinant_fast(A):
    # Section 1: Establish n parameter and copy A
    n = len(A)
    AM = copy_matrix(A)

    # Section 2: Row ops on A to get in upper triangle form
    for fd in range(n): # A) fd stands for focus diagonal
        for i in range(fd+1,n): # B) only use rows below fd row
            if AM[fd][fd] == 0: # C) if diagonal is zero ...
                AM[fd][fd] == 1.0e-18 # change to ~zero
            # D) cr stands for "current row"
            crScaler = AM[i][fd] / AM[fd][fd]
            # E) cr - crScaler * fdRow, one element at a time
            for j in range(n):
                AM[i][j] = AM[i][j] - crScaler * AM[fd][j]

    # Section 3: Once AM is in upper triangle form ...
    product = 1.0
    for i in range(n):
        # ... product of diagonals is determinant
        product *= AM[i][i]

    return product

def print_matrix(M, decimals=3):
    """
    Print a matrix one row at a time
        :param M: The matrix to be printed
    """
    for row in M:
        print([round(x, decimals)+0 for x in row])

def invert_matrix(A, tol=None):
    """
    Returns the inverse of the passed in matrix.
        :param A: The matrix to be inversed

        :return: The inverse of the matrix A
    """
    # Section 1: Make sure A can be inverted.
    check_squareness(A)
    check_non_singular(A)

    # Section 2: Make copies of A & I, AM & IM, to use for row ops
    n = len(A)
    AM = copy_matrix(A)
    I = identity_matrix(n)
    IM = copy_matrix(I)

    # Section 3: Perform row operations
    indices = list(range(n)) # to allow flexible row referencing ***
    for fd in range(n): # fd stands for focus diagonal
        fdScaler = 1.0 / AM[fd][fd]
        # FIRST: scale fd row with fd inverse.
        for j in range(n): # Use j to indicate column looping.
            AM[fd][j] *= fdScaler
            IM[fd][j] *= fdScaler
        # SECOND: operate on all rows except fd row as follows:
        for i in indices[0:fd] + indices[fd+1:]:
            # *** skip row with fd in it.
            crScaler = AM[i][fd] # cr stands for "current row".
            for j in range(n):
                # cr - crScaler * fdRow, but one element at a time.
                AM[i][j] = AM[i][j] - crScaler * AM[fd][j]
                IM[i][j] = IM[i][j] - crScaler * IM[fd][j]

    # Section 4: Make sure IM is an inverse of A with specified tolerance
    if check_matrix_equality(I,matrix_multiply(A,IM),tol):
        return IM
    else:
           # return IM
            raise ArithmeticError("Matrix inverse out of tolerance.")

def permutations(iterable, r=None):
    # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) --> 012 021 102 120 201 210
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r > n:
        return
    indices = list(range(n))
    cycles = list(range(n, n-r, -1))
    yield tuple(pool[i] for i in indices[:r])
    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return

def rotate_left(x, y):
    """
    Left rotates a list x by the number of steps specified
    in y.
    Examples
    ========
    >>> from sympy.utilities.iterables import rotate_left
    >>> a = [0, 1, 2]
    >>> rotate_left(a, 1)
    [1, 2, 0]
    """
    if len(x) == 0:
        return []
    y = y % len(x)
    return x[y:] + x[:y]

def rotate_right(x, y):
    """
    Right rotates a list x by the number of steps specified
    in y.
    Examples
    ========
    >>> from sympy.utilities.iterables import rotate_right
    >>> a = [0, 1, 2]
    >>> rotate_right(a, 1)
    [2, 0, 1]
    """
    if len(x) == 0:
        return []
    y = len(x) - y % len(x)
    return x[y:] + x[:y]

def minlex(seq, directed=True, is_set=False, small=None):
    """
    Return a tuple where the smallest element appears first; if
    ``directed`` is True (default) then the order is preserved, otherwise
    the sequence will be reversed if that gives a smaller ordering.
    If every element appears only once then is_set can be set to True
    for more efficient processing.
    If the smallest element is known at the time of calling, it can be
    passed and the calculation of the smallest element will be omitted.
    Examples
    ========
    >>> from sympy.combinatorics.polyhedron import minlex
    >>> minlex((1, 2, 0))
    (0, 1, 2)
    >>> minlex((1, 0, 2))
    (0, 2, 1)
    >>> minlex((1, 0, 2), directed=False)
    (0, 1, 2)
    >>> minlex('11010011000', directed=True)
    '00011010011'
    >>> minlex('11010011000', directed=False)
    '00011001011'
    """
    is_str = type(seq)
    seq = list(seq)
    if small is None:
       small = min(seq)
    if is_set:
        i = seq.index(small)
        if not directed:
            n = len(seq)
            p = (i + 1) % n
            m = (i - 1) % n
            if seq[p] > seq[m]:
                seq = list(reversed(seq))
                i = n - i - 1
        if i:
            seq = rotate_left(seq, i)
        best = seq
    else:
        count = seq.count(small)
        if count == 1 and directed:
            best = rotate_left(seq, seq.index(small))
        else:
            # if not directed, and not a set, we can't just
            # pass this off to minlex with is_set True since
            # peeking at the neighbor may not be sufficient to
            # make the decision so we continue...
            best = seq
            for i in range(count):
                seq = rotate_left(seq, seq.index(small, count != 1))
                if seq < best:
                    best = seq
                # it's cheaper to rotate now rather than search
                # again for these in reversed order so we test
                # the reverse now
                if not directed:
                    seq = rotate_left(seq, 1)
                    seq = list(reversed(seq))
                    if seq < best:
                        best = seq
                    seq = list(reversed(seq))
                    seq = rotate_right(seq, 1)
    # common return
    if is_str == str:
        return ''.join(best)
    return tuple(best)

def cyclic_form(Pl):
        """
        This is used to convert to the cyclic notation
        from the canonical notation. Singletons are omitted.
        Examples
        ========
        >>> from sympy.combinatorics.permutations import Permutation
        >>> Permutation.print_cyclic = False
        >>> p = Permutation([0, 3, 1, 2])
        >>> p.cyclic_form
        [[1, 3, 2]]
        >>> Permutation([1, 0, 2, 4, 3, 5]).cyclic_form
        [[0, 1], [3, 4]]
        See Also
        ========
        array_form, full_cyclic_form
        """
        pt = type(Pl)
        if pt == tuple:
               Pl = list(Pl)
               # return list(Pl)
        elif pt == str:
               raise Exception('Given Value must be either tuple or list')
        elif pt == int:
               raise Exception('Given Value must be either tuple or list')
        elif pt == float:
               raise Exception('Given Value must be either tuple or list')
        elif pt == list:
               Pl = Pl
                # return Pl
        array_form = Pl
        unchecked = [True] * len(Pl)
        cyclic_form = []
        for i in range(len(Pl)):
            if unchecked[i]:
                cycle = []
                cycle.append(i)
                unchecked[i] = False
                j = i
                while unchecked[array_form[j]]:
                    j = array_form[j]
                    cycle.append(j)
                    unchecked[j] = False
                if len(cycle) > 1:
                    cyclic_form.append(cycle)
                    assert cycle == list(minlex(cycle, is_set=True))
        cyclic_form.sort()
        cyclic_form = cyclic_form[:]
        return cyclic_form

def transpositions(Pl):
        """
        Return the permutation decomposed into a list of transpositions.
        It is always possible to express a permutation as the product of
        transpositions, see [1]
        Examples
        ========
        >>> from sympy.combinatorics.permutations import Permutation
        >>> p = Permutation([[1, 2, 3], [0, 4, 5, 6, 7]])
        >>> t = p.transpositions()
        >>> t
        [(0, 7), (0, 6), (0, 5), (0, 4), (1, 3), (1, 2)]
        >>> print(''.join(str(c) for c in t))
        (0, 7)(0, 6)(0, 5)(0, 4)(1, 3)(1, 2)
        >>> Permutation.rmul(*[Permutation([ti], size=p.size) for ti in t]) == p
        True
        References
        ==========
        .. [1] https://en.wikipedia.org/wiki/Transposition_%28mathematics%29#Properties
        """
        al = [i for i in range(len(Pl))]
        if al == Pl is True:
               return [0, 0]
        else:
               a = cyclic_form(Pl)
               res = []
               for x in a:
                   nx = len(x)
                   if nx == 2:
                       res.append(tuple(x))
                   elif nx > 2:
                       first = x[0]
                       for y in x[nx - 1:0:-1]:
                           res.append((first, y))
               return res

def transpose(M):
       columns = len(M[0][:])
       rows = len(M)
       tmat = [[i*0.0 for i in range(rows)] for j in range(columns)]
       for i in range(rows):
              for j in range(columns):
                     tmat[j][i] = M[i][j]
       return tmat

# def transposed(M):
#        columns = len(M[0][:])
#        rows = len(M)
#        vf = 0.0
#        a = '{}:.{}f{}'.format('{', pr,'}')
#        a1 = '{}'.format(a)
#        a2 = str(a1)
#        a3 = str(a2.format(vf))
#        a4 = De(a3)
#        tmat = [[a4 for i in range(rows)] for j in range(columns)]
#        for i in range(rows):
#               for j in range(columns):
#                      vvf = M[i][j]
#                      ad = '{}:.{}f{}'.format('{', pr,'}')
#                      a1d = '{}'.format(ad)
#                      a2d = str(a1d)
#                      a3d = str(a2d.format(vvf))
#                      a4d = De(a3d)
#                      tmat[j][i] = afd
       # return tmat

def factorial(n):
     if n == 0:
            return 1
     else:
            nn = n
            ne = n - 1
            while ne >= 1:
                   nn = nn*ne
                   ne -= 1
            return nn

def perm(n, r):
       pp = factorial(n)/factorial(n-r)
       return int(pp)

def comb(n, r):
       cc = factorial(n)/(factorial(n-r)*factorial(r))
       return cc

def Gamma(n):
       if n <= 0:
              return None
       else:
              return factorial(n-1)

def dot(v1, v2):
     return sum([x*y for x,y in zip(v1,v2)])

def dotd(v1, v2, pr):
       vv = sum([x*y for x, y in zip(v1, v2)])
       aa = '{}:.{}f{}'.format('{', pr,'}')
       aa1 = '{}'.format(aa)
       aa2 = str(aa1)
       aa3 = str(aa2.format(vv))
       aa4 = De(aa3)
       return aa4

def matmul(A, B):
       acolumns = len(A[0][:])
       arows = len(A)
       bcolumns = len(B[0][:])
       brows = len(B)
       if acolumns == brows:
              nmat = [[i*0.0 for i in range(bcolumns)] for j in range(arows)]
              for i in range(arows):
                     Ar = A[i][:]
                     for j in range(bcolumns):
                            Bc = [B[i][j] for i in range(brows)]
                            Cij = dot(Ar, Bc) #sum([i*j for i, j in zip(Ar,Bc)])
                            nmat[i][j] = Cij
              return nmat

       elif acolumns != brows:
              raise Exception('Columns of matrix A ({}) needs to equal the Rows of Matrix B ({}) {} != {}'.format(acolumns, brows, acolumns, brows))

def kcycle(S, k):
       n = len(S)
       kn = factorial(n + 1)/(factorial(n - k + 1)*k)
       return kn

def multiply(n):
    total = 1
    for i in n:
        total *= i
    return total

def sgnf(m):
       sgn = (-1)**m
       return sgn

def det(A):
       det = []
       colsl = len(A[0][:])
       p = [list(i) for i in list(permutations([pi for pi in range(colsl)]))]
       ts = []
       tns = []
       ss = []
       ais = []
       for pi in range(len(p)):
              tl = transpositions(p[pi])
              ts.append(tl)
              tn = len(transpositions(p[pi]))
              tns.append(tn)
       for i in tns:
              ss.append(sgnf(i))
       for fi in range(len(p)):
                            σ = [i + 1 for i in p[fi]]
                            sig = ss[fi]
                            for i, j in enumerate(σ):
                                   ai = A[i-1][j-1]
                                   ais.append(ai)
                            fin = sig*multiply(ais)
                            det.append(fin)
                            ais.clear()
       return sum(det)

def iden(n):
       mm = []
       for ni in range(n):
              mm.append([mi*0.0 for mi in range(n)])

       for nn in range(n):
              mm[nn][nn] = 1
       return mm

def zero(n):
       mm = []
       for ni in range(n):
              mm.append([mi*0.0 for mi in range(n)])

       for nn in range(n):
              mm[nn][nn] = 0.0
       return mm

def zerod(n, pr):
       mm = []
       for ni in range(n):
               vf = 0.0
               a = '{}:.{}f{}'.format('{', pr,'}')
               a1 = '{}'.format(a)
               a2 = str(a1)
               a3 = str(a2.format(vf))
               a4 = De(a3)
               mm.append([a4 for mi in range(n)])

       for nn in range(n):
               vfb = 0.0
               ab = '{}:.{}f{}'.format('{', pr,'}')
               a1b = '{}'.format(ab)
               a2b = str(a1b)
               a3b = str(a2b.format(vfb))
               a4b = De(a3b)
               mm[nn][nn] = a4b
       return mm

def rowred(A):
       rows = len(A)
       cols = len(A[0])
       itern = 0
       II = iden(rows)
       for i in range(cols):
              start = A[i][i]
              vals = [A[j][i]/start for j in range(1 + itern, rows)]
              nr = [[vals[v]*rv for rv in A[i][:]] for v in range(len(vals))]
              ni = [[vals[v]*rv for rv in II[i][:]] for v in range(len(vals))]
              rrows = [A[iv] for iv in range(1 + itern, cols)]
              rrowsi = [II[iv] for iv in range(1 + itern, cols)]
              # print(np.array(A))
              for g in range(len(rrows)):
                     vv = nr[g]
                     nn = rrows[g]
                     nnr = [i - j for i, j in zip(nn, vv)]
                     vvi = ni[g]
                     nni = rrowsi[g]
                     nnii = [i - j for i, j in zip(nni, vvi)]
                     A[g+1+itern] = nnr
                     II[g+1+itern] = nnii
              itern += 1
       return A, II

def solsys(A, II):
       rows = len(A)
       cols = len(A[0])
       itern = 0
       for i in range(cols):
              start = A[-1-i][-1-i]
              vals = [A[j][-1 - itern]/start for j in range(0, rows-itern)]
              nr = [[vals[v]*rv for rv in A[-1 - itern][:]] for v in range(len(vals))]
              rrows = [A[iv] for iv in range(0, cols-itern-1)]
              ni = [[vals[v]*rv for rv in II[-1 - itern][:]] for v in range(len(vals))]
              rrowsi = [II[iv] for iv in range(0, cols-itern-1)]
              for g in range(len(rrows)):
                     vv = nr[g]
                     nn = rrows[g]
                     nnr = [round(i - j, 5) for i, j in zip(nn, vv)]
                     vvi = ni[g]
                     nni = rrowsi[g]
                     nnii = [round(i - j, 5) for i, j in zip(nni, vvi)]
                     A[g] = nnr
                     II[g] = nnii
              itern += 1
       for i in range(rows):
              start = A[i][i]
              IIv = [iv/start for iv in II[i]]
              AAv = [av/start for av in A[i]]
              A[i] = AAv
              II[i] = IIv
       return A, II

# def LUf(A):
#        rows = len(A)
#        cols = len(A[0])

#        def U_mat(row, col):

#               BMat = [[0.0*j for j in range(col)] for i in range(row)]

#               for i in range(row):

#                      for j in range(col):

#                             if i == j:
#                                    strv = "U_{}{}".format(i, j)
#                                    BMat[i][j] = strv
#                             elif i > j:
#                                    BMat[i][j] = 0.0
#                             elif i < j:
#                                    strv = "U_{}{}".format(i, j)
#                                    BMat[i][j] = strv
#               return BMat

#        def L_mat(row, col):

#               BMatl = [[0.0*j for j in range(col)] for i in range(row)]

#               for i in range(row):

#                      for j in range(col):

#                             if i == j:
#                                    strv = "L_{}{}".format(i, j)
#                                    BMatl[i][j] = 1.0
#                             elif i < j:
#                                    BMatl[i][j] = 0.0
#                             elif i > j:
#                                    strv = "L_{}{}".format(i, j)
#                                    BMatl[i][j] = strv
#               return BMatl

def matmullu(A, B):
              acolumns = len(A[0][:])
              arows = len(A)
              bcolumns = len(B[0][:])
              brows = len(B)
              if acolumns == brows:
                     nmat = [[i*0.0 for i in range(bcolumns)] for j in range(arows)]
                     for i in range(arows):
                            Ar = A[i][:]
                            for j in range(bcolumns):
                                   Bc = [B[i][j] for i in range(brows)]
                                   print(Bc)
                                   Cc = [A[ia][j] for ia in range(brows)]
                                   print(Cc)
                                   Cij = sum(["{}*{}".format(Bc[i], Cc[i])  for i in range(acolumns)])
                                   print(Cij)
                                   nmat[i][j] = Cij
                     return nmat

              elif acolumns != brows:
                     raise Exception('Columns of matrix A ({}) needs to equal the Rows of Matrix B ({}) {} != {}'.format(acolumns, brows, acolumns, brows))

def LU_decomposition(A):
    """Perform LU decomposition using the Doolittle factorisation."""

    L = zero(len(A))
    U = zero(len(A))
    N = len(A)

    def uvals(Um, k, n):
            ulist = []
            for i in range(k):
                  uu = Um[i][n]
                  ulist.append(uu)
            return ulist

    def lvals(Lm, k, n):
            llist = []
            lu = Lm[k]
            lul = lu[0:k]
            return lul

    for k in range(N):
        L[k][k] = 1
        U[k][k] = (A[k][k] - dot(lvals(L, k, k), uvals(U, k, k))) / L[k][k]
        for j in range(k+1, N):
            U[k][j] = (A[k][j] - dot(lvals(L, k, k), uvals(U, j, j))) / L[k][k]
        for i in range(k+1, N):
            L[i][k] = (A[i][k] - dot(lvals(L, i, i), uvals(U, k, k))) / U[k][k]

    return L, U

def LU_decompositiond(A, pr):
    """Perform LU decomposition using the Doolittle factorisation."""

    L = zerod(len(A), pr)
    U = zerod(len(A), pr)
    N = len(A)

    def uvals(Um, k, n):
            ulist = []
            for i in range(k):
                  uu = Um[i][n]
                  ulist.append(uu)
            return ulist

    def lvals(Lm, k, n):
            llist = []
            lu = Lm[k]
            lul = lu[0:k]
            # print(lul)
            return lul

    def uvalsd(Um, k, n, pr):
            ulist = []
            for i in range(k):
                  uu = Um[i][n]
                  aa = '{}:.{}f{}'.format('{', pr,'}')
                  aa1 = '{}'.format(aa)
                  aa2 = str(aa1)
                  aa3 = str(aa2.format(uu))
                  aa4 = De(aa3)
                  ulist.append(aa4)
            return ulist

    def lvalsd(Lm, k, n, pr):
            llist = []
            lu = Lm[k]
            lul = lu[0:k]
            # print(lul)
            for i in range(len(lul)):
                     val_ij = lul[i]
                     aa = '{}:.{}f{}'.format('{' , pr,'}')
                     aa1 = '{}'.format(aa)
                     aa2 = str(aa1)
                     aa3 = str(aa2.format(val_ij))
                     aa4 = De(aa3)
                     llist.append(aa4)
            # print(lul)
            # print(llist)
            return lul

    for k in range(N):
        v1 = 1.0
        a = '{}:.{}f{}'.format('{', pr,'}')
        a1 = '{}'.format(a)
        a2 = str(a1)
        a3 = str(a2.format(v1))
        a4 = De(a3)
        L[k][k] = a4
        v2 = (A[k][k] - dotd(lvalsd(L, k, k, pr), uvalsd(U, k, k, pr), pr)) / L[k][k]
        ab = '{}:.{}f{}'.format('{', pr,'}')
        ab1 = '{}'.format(ab)
        ab2 = str(ab1)
        ab3 = str(ab2.format(v2))
        ab4 = De(ab3)
        # print(ab4)
        U[k][k] = ab4
        for j in range(k+1, N):
              val_i = float((A[k][j] - dotd(lvalsd(L, k, k, pr), uvalsd(U, j, j, pr), pr)) / L[k][k])
              aa = '{}:.{}f{}'.format('{', pr,'}')
              aa1 = '{}'.format(aa)
              aa2 = str(aa1)
              aa3 = str(aa2.format(val_i))
              aa4 = De(aa3)
              U[k][j] = aa4
        for i in range(k+1, N):
              val_ib = float((A[i][k] - dotd(lvalsd(L, i, i, pr), uvalsd(U, k, k, pr), pr)) / U[k][k])
              aab = '{}:.{}f{}'.format('{', pr,'}')
              aa1b = '{}'.format(aab)
              aa2b = str(aa1b)
              aa3b = str(aa2b.format(val_ib))
              aa4b = De(aa3b)
              L[i][k] = aa4b

    return L, U

def backward_sub(U, y):
    """Given a lower triangular matrix U and right-side vector y,
    compute the solution vector x solving Ux = y."""

    # x = zero(len(y))
    x = [0.0 for ix in y]

    for i in range(len(x), 0, -1):
      x[i-1] = De((y[i-1] - dot(U[i-1][i:], x[i:])) / U[i-1][i-1])

    return x

def forward_sub(L, b):
    """Given a lower triangular matrix L and right-side vector b,
    compute the solution vector y solving Ly = b."""

    y = []
    for i in range(len(b)):
        y.append(b[i])
        for j in range(i):
            y[i]=y[i]-(L[i][j]*y[j])
        y[i] = y[i]/L[i][i]

    return y

    return x

def forward_subd(L, b, pr):
    """Given a lower triangular matrix L and right-side vector b,
    compute the solution vector y solving Ly = b."""

    y = []
    for i in range(len(b)):
        y.append(b[i])
        for j in range(i):
              val_i = y[i]-(L[i][j]*y[j])
              aa = '{}:.{}f{}'.format('{', pr,'}')
              aa1 = '{}'.format(aa)
              aa2 = str(aa1)
              aa3 = str(aa2.format(val_i))
              aa4 = De(aa3)
              y[i]= aa4
        y[i] = De(y[i])/De(L[i][i])

    return y

def backward_subd(U, y, pr):
    """Given a lower triangular matrix U and right-side vector y,
    compute the solution vector x solving Ux = y."""

    # x = zerod(len(y))
    x = [De(0.0) for ix in y]

    for i in range(len(x), 0, -1):
       val_i = (y[i-1] - dot(U[i-1][i:], x[i:])) / U[i-1][i-1]
       aa = '{}:.{}f{}'.format('{', pr,'}')
       aa1 = '{}'.format(aa)
       aa2 = str(aa1)
       aa3 = str(aa2.format(val_i))
       aa4 = De(aa3)
       x[i-1] = aa4

    return x

def lu_solve(L, U, b):
    # Step 1: Solve Uy = b using forward substitution
    # Step 2: Solve Lx = y using backward substitution
    y = forward_sub(L, b)
    x = backward_sub(U, y)
    return x

def linear_solve(A, b):
    L, U = LU_decomposition(A)
    x = lu_solve(L, U, b)
    return x

def lu_solved(L, U, b, pr):
    # Step 1: Solve Uy = b using forward substitution
    # Step 2: Solve Lx = y using backward substitution
    y = forward_subd(L, b, pr)
    yv = []
    for i in range(len(y)):
       val_yi = float(y[i])
       aa = '{}:.{}f{}'.format('{', pr,'}')
       aa1 = '{}'.format(aa)
       aa2 = str(aa1)
       aa3 = str(aa2.format(val_yi))
       aa4 = De(aa3)
       yv.append(aa4)
    x = backward_subd(U, yv, pr)
    return x

def linear_solved(Ad, bd, pr):
    Ld, Ud = LU_decompositiond(Ad, pr)
    x = lu_solved(Ld, Ud, bd, pr)
    return x

def LUdecomp(a):
    n = len(a)
    for k in range(0,n-1):
        for i in range(k+1,n):
            if a[i,k] != 0.0:
                lam = a[i,k]/a[k,k]
                a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
                a[i,k] = lam
    return a

def sparsity(A):
       rows = len(A)
       cols = len(A[0])
       Zs = 0
       Nzs = 0
       tot = 0
       for i in range(rows):
              for j in range(cols):
                     val_ij = A[i][j]
                     if val_ij == 0 or val_ij == 0.0:
                            Zs += 1
                            tot += 1
                     elif val_ij != 0 or val_ij != 0.0:
                            Nzs += 1
                            tot += 1
       return Zs, Nzs, tot, Zs/tot

def csc_groups(A):
       rows = len(A)
       cols = len(A[0])
       rows_l = []
       cols_l = []
       data_l = []
       Zs = []
       Nzs = []
       tot = 0
       for i in range(rows):
              for j in range(cols):
                     val_ij = A[i][j]
                     if val_ij == 0 or val_ij == 0.0:
                            pass
                     elif val_ij != 0 or val_ij != 0.0:
                            rows_l.append(int(i))
                            cols_l.append(int(j))
                            data_l.append(A[i][j])
       return rows_l, cols_l, data_l

def decfunc(Ax, p):
       rows = len(Ax)
       cols = len(Ax[0])
       AD = [[De(ij) for ij in Ax[ii]] for ii in range(len(Ax))]
       for i in range(rows):
              for j in range(cols):
                     decimal.getcontext().prec = 25
                     val_ij = Ax[i][j]
                     aa = '{}:.{}f{}'.format('{',p,'}')
                     aa1 = '{}'.format(aa)
                     aa2 = str(aa1)
                     aa3 = str(aa2.format(val_ij))
                     aa4 = De(aa3)
                     AD[i][j] = aa4
                     Ax[i][j] = aa4
       return AD, Ax

def decfuncl(Axl, p):
       vals = len(Axl)
       ADl = [De(il) for il in Axl]
       for i in range(vals):
              val_i = Axl[i]
              aa = '{}:.{}f{}'.format('{',p,'}')
              aa1 = '{}'.format(aa)
              aa2 = str(aa1)
              aa3 = str(aa2.format(val_i))
              aa4 = De(aa3)
              ADl[i] = aa4
       return ADl

Av, IIa = rowred(AM)
B, IF = solsys(Av, IIa)
# pp((np.array(IF)).tolist())
# print(det(A))
# pp(np.array(Av))
# pp(np.array(IIa))
# pp(np.array(B))
# pp(np.array(IF))
AA = np.array([[3, 2, 3],     [4, 6, 6],     [7, 4, 9]])
AAd = np.array([[3.0, 2.0, 3.0],     [4.0, 6.0, 6.0],     [7.0, 4.0, 9.0]])
Ab = [[3.0, 2.0, 3.0, 4.0],
      [4.0, 6.0, 6.0, 8.0],
      [7.0, 4.0, 9.0, 11.0],
      [5.0, 4.0, 9.0, 16.0]]

As = shape(Ab)
Adet = determinant_fast(Ab)
Abinv = invert_matrix(Ab, 1)
Abinv2 = la.inv(np.array(Ab))
print(Adet, np.linalg.det(np.array(Ab)), la.det(np.array(Ab)))
print_matrix(Abinv)
print(Abinv2)
AAD, AAX = decfunc(AAd, 25)
# print(AAX, AAD)
ATX = transpose(AAD)
aa = transpose([[3, 4, 7], [2, 6, 4], [3, 6, 9]])
bd = decfuncl([6.0, -4.0, 27.0], 25)
# print(bd)
L, U = LU_decomposition(AA)
Lx, UX = LU_decompositiond(AAD, 25)
# print(Lx, UX)
Px, Llx, UuX = la.lu(ATX)
LUX = la.lu_factor(AAD)
# print(U)
xd = linear_solved(AAD, bd, 25)
# print(xd)
# LUf(AM)
# print(P)
# print(np.array(L))
# print(np.array(U))

P, LL, UU = la.lu(AA.T)
# print(np.array(P))
# print(np.array(LL))
# print(np.array(UU))

b = [6, -4, 27]
As = [[3, 2, 3],     [4, 6, 6],     [7, 4, 9]]
LU = la.lu_factor(As)
x = la.lu_solve(LU, b)
# print(linear_solve(As,b))
# print(x)

ZS, NZS, TOT, sps = sparsity([[3, 0, 3],     [4, 0.0, 0],     [0, 4, 9]])
# print(ZS, NZS, TOT, sps)

S6 = 6**0.5
S6d = De(S6)
# print(S6d)
P = [[13/3 + 7*S6/3, -23/3 - 22*S6/3, 10/3 + 5 * S6],
    [13/3 - 7*S6/3, -23/3 + 22*S6/3, 10/3 - 5 * S6],
    [1/3, -8/3, 10/3]]
# print(P)
row = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2])
col = np.array([0, 1, 2, 0, 1, 2, 0, 1, 2])
data = np.array([3, 2, 3, 4, 6, 6, 7, 4, 9])
# print(sc.sparse.csc_matrix((data,(row,col)), shape=(3,3)).todense() )
rowsl, colsl, datal = csc_groups(As)
# print(rowsl, colsl, datal)
# print(sc.sparse.csc_matrix((datal,(rowsl,colsl)), shape=(3,3)).todense() )

import numpy,import scipy,import sympy,from sympy


import numpy as np

class Perceptron:
    def __init__(self, number_of_inputs, learning_rate):
        self.weights = np.random.rand(1, number_of_inputs + 1)[0]
        self.learning_rate = learning_rate

    """A step function where non-negative values are returned by a 1 and negative values are returned by a -1"""
    def activate(self, z):
        if z >= 0:
            return 1
        else:
            return -1

    def feed_forward(self, input_values):
        inputs = np.array([
            input_values[0], input_values[1], -1
        ])
        z = inputs.dot(self.weights.transpose())
        return self.activate(z)

    def update_weights(self, actual_x, error):
        x = np.array([
            actual_x[0], actual_x[1], -1
        ])
        self.weights += self.learning_rate*error*x

"""
Below code simulates a perceptron learning to act as an OR gate.
(-1) represents 0
(+1) represents 1

"""
if __name__ == "__main__":
    print("\nPerceptron learning the OR gate functionality\n")
    np.random.seed(1111)
    perceptron = Perceptron(2, 0.01)
    training_x = np.array([[-1, -1], [-1, 1], [1, -1], [1, 1]])
    training_y = np.array([[-1], [1], [1], [1]])

    for epoch in range(25):
        total_error = 0
        for example in range(len(training_x)):
            y_predicted = perceptron.feed_forward(training_x[example])
            y_expected = training_y[example][0]
            error = y_expected - y_predicted
            total_error += error
            perceptron.update_weights(training_x[example], error)
        print("epoch " + str(epoch) + " Total Error " + str(total_error))
        if total_error == 0:
            break
    print("Final Weights : " + str(perceptron.weights))

    "Testing final weights"
    print("\nTesting final weights")
    print('Input [-1, -1] Output ' + str(perceptron.feed_forward([-1, -1])))
    print('Input [-1, +1] Output ' + str(perceptron.feed_forward([-1, +1])))
    print('Input [+1, -1] Output ' + str(perceptron.feed_forward([+1, -1])))
    print('Input [+1, +1] Output ' + str(perceptron.feed_forward([+1, +1])))


import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import lagrange
from numpy import pi

def f(x):
    #f(x) = sin(x)
    return np.sin(x)

def main():
    #1)
    #Lagrange interpolation for f(x) with 9 equidistant points between [0,pi]
    x = np.linspace(0, pi, 9)
    y = f(x)
    polynomial = lagrange(x, y)

    #Evaluating polynomial in 1000 points between [0,pi]
    x2 = np.linspace(0, pi, 1000)
    g = polynomial(x2)

    #Plotting Lagrange interpolation evaluated in 1000 points between [0,pi]
    plt.plot(x2, g)
    plt.xlabel("Lagrange interpolation")
    plt.savefig('lagrange_interpolation.png', format='png')
    plt.show()

    #Evaluating sine in 1000 points between [0,pi]
    y2 = f(x2)

    #Plotting interpolation vs sine evaluated in 1000 points between [0,pi]
    plt.plot(x2, g, x2, y2)
    plt.xlabel("Lagrange interpolation vs original function")
    plt.savefig('interpolation_vs_sine_comparison.png', format='png')
    plt.show()

    #Calculating lagrange interpolation error by |f(x) - g(x)|
    error = abs(y2 - g)

    #Plotting error of Lagrange interpolation
    plt.plot(x2, error)
    plt.xlabel("Lagrange interpolation error")
    plt.savefig('lagrange_interpolation_error.png', format='png')
    plt.show()

main()