import numpy as np
from matplotlib import pyplot as plt

def shiftByMean(A):
    '''
    Shift the columns of the input array by their respective means.
    Inputs:
        A -- an (m,n) array
    Return:
        a (m,n) array whose columns are the mean-shifted counterparts to A.
    '''
    return A - A.mean(axis=0)

def computeVariance(A):
    '''
    Calculate the variance of each column of the input array.
    Inputs:
        A -- an (m,n) array, not necessarily mean-shifted.
    Return:
        a 1-d array with n entries, each giving the variance of the corresponding column.
    '''
    return (shiftByMean(A)**2).sum(axis=0)/np.float(A.shape[0])

def reportStDev():
    '''
    Print the answer to the question in problem 1.
    You may also include the necessary code to import and process the data, but this
    need not necessarily be in the body of this function.
    '''
    data = np.genfromtxt('weight_age_fat.txt', skip_header=1, usecols=[2,3,4])
    col_titles = ['weight', 'age', 'blood fat content']
    stds = np.sqrt(computeVariance(data))
    ind = np.argmin(stds)
    min_std = np.min(stds)
    print col_titles[ind], min_std

def corrMatrix(W):
    '''
    Compute the correlation matrix for the columns of input array W
    '''
    X = W - W.mean(axis=0)
    Y = X/np.sqrt((X**2).sum(axis=0))
    return np.dot(Y.T, Y)

def reportCorr():
    '''
    Print the answers to the questions in problem 3. Ther are three distinct questions,
    so there should be three distinct print statements, indicating which question is
    being answered. Report the columns as their number in the original data file. The first
    column, which we omitted, would be column 0, and the last column (giving the mortality)
    would be column 16.
    Finally, plot the data as described in the problem statement.
    '''
    data = np.genfromtxt('mortality.txt', skip_header=17, usecols=range(1,17))
    corr_mat = corrMatrix(data)

    # find the column whose correlation with the last column is closest to zero
    min_col = np.argmin(np.abs(corr_mat[:,-1]))
    print "The column most nearly uncorrelated with mortality rate is",  min_col + 1

    # now find the pair of distinct columns with highest correlation.
    # set diagonals to zero, since we want to consider only distinct columns
    np.fill_diagonal(corr_mat,0)
    max_rows = corr_mat.max(axis=1)
    max_row = np.argmax(max_rows)
    max_col = np.argmax(corr_mat[max_row,:])
    print "The pair of columns with largest correlation is", (max_row+1,max_col+1)

    # find column with highest correlation to mortality
    min_mort = np.argmin(corr_mat[:,-1])
    print "The column that is most negatively correlated with mortality is", min_mort + 1

    plt.subplot(311)
    plt.scatter(data[:,max_row], data[:, max_col])
    plt.subplot(312)
    plt.scatter(data[:,min_mort], data[:,-1])
    plt.subplot(313)
    plt.scatter(data[:,min_col], data[:,-1])
    plt.show()
    plt.clf()

def covariance():
    data = np.genfromtxt('mortality.txt', skip_header=17, usecols=range(1,17))
    return np.cov(data, rowvar=0)


# -*- coding: utf-8 -*-
"""
Amicable numbers
================
https://projecteuler.net/problem=21

Let d(n) be defined as the sum of proper divisors of n (numbers less than n
which divide evenly into n). If d(a) = b and d(b) = a, where a ≠ b, then a and b
are an amicable pair and each of a and b are called amicable numbers.

For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55
and 110; therefore d(220) = 284. The proper divisors of 284 are 1, 2, 4, 71 and
142; so d(284) = 220.

Evaluate the sum of all the amicable numbers under 10000.
"""
import numpy as np

from .utils import print_result

def divisor_sums(n: int) -> np.ndarray:
    """Generate a flat array of sums of proper divisors of numbers less than n."""
    sums = np.zeros(n, dtype=int)
    for i in range(1, n):
        j = 2 * i
        while j < n:
            sums[j] += i
            j += i
    return sums

@print_result
def solve() -> int:
    n = 10000
    sums = divisor_sums(n)
    total = 0
    for a, b in enumerate(sums):
        if b < n and b != a and sums[b] == a:
            total += a
    return total

if __name__ == "__main__":
    solve()


from math import cos, pi
from sympy import Poly, symbols, lambdify
import numpy as np

def dividedDiffernce(f, xlow, xhigh, deg=4, var_name='x'):
    """
    INPUT:
        f: The function to be interpolated as SymPy expression.
        xlow: The lower bound of the interval.
        xhigh: The upper bound of the interval.
        var_name(default='x'): The name of the varible of the
            interpolating polynomial.

    OUTPUT:
        P: An n degree polynomial that interpolates the function
            f with respect to the points (x_i, y_i), where n is
            the number of points minus one.
        R_bar_squared: The adjusted R-squared coefficient for the
            polynomial fit.

    Performs Newton's Divided Differences method to interpolate a function f,
    with respect to the x-values, x_0, x_1, ..., x_n, on the
    interval [xlow, xhigh]. These values are taken to be the n+1 Chebyshev points
    mapped to the given interval. The interpolating polynomaial P_n(x) takes the form,

    P_n(x) = a_0 + a_1*(x-x_0) + a_2*(x-x_0)*(x-x_1)
            + ... + a_n*(x-x_0)*...*(x-x_n-1),

    where each a_k is the kth divided difference coefficient. We can solve
    for each a_k by using the following notation,

    a_k = f[x_0,x_1,...,x_k],

    where each f[x_0,x_1,...,x_k] is recursivly calculated using the
    two previous coefficients as,

    f[x_i,x_i+1,...,x_i+k] = (f[x_i+1,x_i+2,...,x_i+k]
                            - f[x_i,x_i+1,...,x_i+k-1]) / (x_i+k - x_i),

    starting with f[x_0] = f(x_0).

    The recursion can be illustrated in the following way,

    f[x_0]
    f[x_1]   f[x_0,x_1]
    f[x_2]   f[x_1,x_2]   f[x_0,x_1,x_2]
    f[x_3]   f[x_2,x_3]   f[x_1,x_2,x_3]
    f[x_4]   f[x_3,x_4]   f[x_2,x_3,x_4]
    .         .            .               .
    .         .            .                 .
    .         .            .                   .
    f[x_n]   f[x_n-1,x_n] f[x_n-2,x_n-1,x_n] ... f[x_0,x_,...,x_n]

    where the kth coefficient along the diagonal is equal to a_k. Using
    the n coefficients, the n degree polynomial P_n(x) is generated
    for the set of given x values.
    """

    NUM_NODES = deg + 1
    xvals = chebNodes(xlow, xhigh, NUM_NODES)
    yvals = [f.subs(var_name, xi) for xi in xvals]

    x = symbols(var_name)

    # The 2-D list of divided-difference coefficients
    # (The coefficients that correspond to each a_k will be along
    # the diagonal of the list.
    coeffs = []

    # Fill each element in the first column, with the corresponding y
    # value, i.e., coeffs_{i,0} = f(x_{i}).
    for row in range(NUM_NODES):
        coeffs.append([])
        coeffs[row].append(yvals[row])

    # Iterate through every other column, and recursivly generate the
    # divided-difference coefficients.
    for col in range(1, NUM_NODES):
        for row in range(col, NUM_NODES):
            # coeffs_{i,j} = (coeffs_{i,j-1} - coeffs_{i-1,j-1}) / (x_{i} - x_{i-j})
            coeffs[row].append((coeffs[row][col-1] - coeffs[row-1][col-1])/(xvals[row]
                                                             - xvals[row-col]))

    # Using the coefficients, a_{n} = coeffs_{i} = coeffs[i][i], construct the
    # interpolating polynomial P(x), such that...,
    # P_{n}(x) = coeffs_{0} + coeffs_{1}*(x - x_{0}) + coeffs_{2}*(x - x_{0})*(x - x_{1})
    #           + ... + coeffs_{n}*(x - x_{0})*(x - x_{1})*...(x - x_{n-1})
    P = Poly([coeffs[0][0]], x)
    for i in range(1, NUM_NODES):
        term = coeffs[i][i]
        for k in range(i):
            term *= (x - xvals[k])

        P += term

    # Calculate the adjusted R-squared, using 50 test points.
    R_bar_sqr = _getRbarSquared(f, P, var_name, xlow, xhigh, n_steps=50)

    return P, R_bar_sqr

def chebNodes(xlow, xhigh, npoints):
    """
    Returns a set of Chebyshev nodes on the interval [xlow, xhigh].
    """

    cheb_nodes = [_kNode(i, npoints) for i in range(npoints)]

    # Translate nodes to the interval xlow to xhigh.
    cheb_nodes = list(map(lambda c: 0.5*(xlow + xhigh)
                          + 0.5*(xhigh - xlow)*c, cheb_nodes))
    cheb_nodes.sort()
    return cheb_nodes

def _kNode(k, n):
    """
    Returns the kth Chebyshev node for a given number of n
    points, for an interval of -1 to 1.
    """
    return cos((2*k + 1)*pi/(2*(n - 1) + 2))

def _getRbarSquared(f, polyfit, var_name, xmin, xmax, n_steps=50):
    endpoint_shift = 0.0001           # To ensure all domian boundaries yield real y values.
    step = abs(xmin - xmax)/n_steps
    x = np.arange(xmin + endpoint_shift, xmax - endpoint_shift, step)

    lam_f = lambdify(var_name, f, 'numpy')
    lam_poly = lambdify(var_name, polyfit.as_expr(), 'numpy')
    y = lam_f(x)
    yfit = lam_poly(x)

    yfit = yfit[np.isreal(y)]
    y = y[np.isreal(y)]

    y_resid = list(map(lambda yi, yfi: yi - yfi, y, yfit))

    SSresid = sum(map(lambda r: r**2, y_resid))
    SStotal = (len(y) - 1)*np.var(y)

    rsq_bar = 1 - SSresid/SStotal*(len(y) - 1)/(len(y) - polyfit.degree() - 1)
    return float(rsq_bar)


import numpy as np
import matplotlib.pyplot as plt
import os
figdir = os.path.join(os.environ["PYPROBML"], "figures")
def save_fig(fname): plt.savefig(os.path.join(figdir, fname))

x = np.linspace(0,1,10000)

H = lambda x: -(x*np.log2(x) + (1-x) * np.log2(1-x))

plt.plot(x, H(x), '-b', lw=3)
plt.xlim((-0.01, 1.01))
plt.ylim((0, 1.01))

plt.xlabel("p(X = 1)")
plt.ylabel("H(X)")

ticks = [0, 0.5, 1]
plt.xticks(ticks)
plt.yticks(ticks)

save_fig("bernoulliEntropy.pdf")
plt.show()


import numpy as np

def Jacobi(A, e):
    n = A.shape[1]
    Q = np.eye(N=n, M=n, dtype=np.float64)
    while np.sum([A[i][j]**2 for j in range(n) for i in range(n) if i != j]) > e:
    #for t in range(10):
        p = 0
        q = 1
        for i in range(n):
            for j in range(i + 1, n):
                if abs(A[p][q]) < abs(A[i][j]):
                    p, q = i, j
        s = (A[q][q] - A[p][p])/(2*A[p][q])
        if s == 0:
            t = 1
        else:
            t1 = -s-(s*s+1)**0.5
            t2 = -s+(s*s+1)**0.5
            if abs(t1) > abs(t2):
                t = t2
            else:
                t = t1
        c = 1/((t*t+1)**0.5)
        d = t/((t*t+1)**0.5)

        # 书上方法 开始
        for i in range(n):
            if i != p and i != q:
                A[i][p] = A[p][i] = c*A[p][i]-d*A[q][i]
                A[i][q] = A[q][i] = d*A[p][i]+c*A[q][i]
        A[p][p] = A[p][p]-t*A[p][q]
        A[q][q] = A[q][q]+t*A[p][q]
        A[p][q] = A[q][p] = 0
        # 书上方法 结束

        tempQ = np.eye(N=n, M=n, dtype=np.float64)
        tempQ[p][p] = tempQ[q][q] = c
        tempQ[p][q] = d
        tempQ[q][p] = -d
        Q = np.matmul(Q, tempQ)

        # 直接乘 开始
        # A = np.matmul(np.matmul(tempQ.T, A), tempQ)
        # 直接乘 结束

    eigenvalue = np.diag(A)
    vectors = Q.swapaxes(0, 1)
    return eigenvalue, vectors, Q

def main():
    A = np.array([[3,2,5,4,6],
                  [2,1,3,-7,8],
                  [5,3,2,5,-4],
                  [4,-7,5,1,3],
                  [6,8,-4,3,8]],dtype=np.float64)

    e = 1e-13
    eigenvalue, vectors, Q = Jacobi(A.copy(), e)
    for i in range(A.shape[1]):
        np.set_printoptions(precision=15)
        print('r{}={:.15e},\nv{}=({:.5e}'.format(i+1, eigenvalue[i], i+1, vectors[i][0]), end='')
        for j in range(1, A.shape[1]):
            print(', {:.5e}'.format(vectors[i][j]), end='')
        print(')')

if __name__ == '__main__':
    main()


"""
@file           qr_factorization.py
@description    QR facorization using Gram-Schmidt process
@author         Hiukong Dan
@version        1.0
@copyright      (c) Hiukong Dan 2021
@contact        ifsoboy9148@hotmail.com
"""

#!/bin/python
import numpy as np
from numpy import linalg
from gram_schmidt import *
from sympy import Matrix

def qr_factorization(A):
    """
    @description:
        QR Factorize a matrix A using gram schmidt process
    @param A:
        Matrix to be factored
    @return (Q, R):
        Q, R tuple of the result
    """
    # linear independence check
    assert np.linalg.matrix_rank(A) == A.shape[1], "The columns are not linear independent"

    Q = orthonormalize(gram_schmidt(A))
    R = np.matmul(Q.T, A)
    return (Q, R)

if __name__ == "__main__":
    """
    A = np.array([[-10,13,7,-11],[2,1,-5,3],[-6,3,13,-3],[16,-16,-2,5],[2,1,-5,-7]])
    print(A)
    print(Matrix.rref(Matrix(A)))
    ret = qr_factorization(A)
    print(ret)
    print(np.matmul(ret[0], ret[1]))
    """


import numpy as np
import matplotlib.pyplot as plt

## Interpolare polinomială

# Definesc funcția
def f(x):
    return np.cos(2 * x) - 2 * np.sin(3 * x)

# Definesc domeniul pe care fac interpolarea
left = -np.pi
right = +np.pi

# Gradul polinomului de interpolare
N = 10

# Definesc polii de interpolare
x = np.linspace(left, right, N + 1)
y = f(x)

# Construiesc matricea Vandermonde pentru sistemul pe care vreau să-l rezolv
A = np.vander(x, N=N + 1, increasing=True)

a = np.linalg.solve(A, y)

def polynomial(x):
    "Evaluează polinomul cu coeficienții `a` în punctul `x`"
    s = 0
    for i in range(N + 1):
        s += a[i] * (x ** i)
    return s

def polynomial_vector(v):
    "Evaluează polinomul cu coeficienții `a` pe vectorul `v`"
    return np.array([polynomial(x) for x in v])

# Afișez funcția
xs = np.linspace(left, right, 5000)
plt.plot(xs, f(xs), linestyle='-', label='funcția f')

# Afișez polii interpolării
plt.scatter(x, y, c='red')

# Afișez interpolarea
xs = np.linspace(left, right, 100)
plt.plot(xs, polynomial_vector(xs),
    c='orange',
    linestyle='--',
    label=f'aproximare cu polinom de gradul {N}')

# Afișez o legendă
plt.legend()

# Afișez tot graficul
plt.show()

# Afișăm eroarea aproximării
show_error = False
if show_error:
    y_real = f(xs)
    y_approx = polynomial_vector(xs)

    plt.plot(xs, np.abs(y_approx - y_real), label=f'eroare pentru N={N}')
    plt.legend()
    plt.show()


# Neural network has 2 inputs, 1 hidden layer with 2 nodes and 1 output layer with one node.
# x1,x2 - I/P, w1,w2,w3,w4 - I/P weights, b1,1 b1,2 - biases, w5,w6 - Hidden layer weights
# b2 - bias, a2 - O/P, z1,1 z1,2 z2 - linear combination of weights and biases

#Initializing weights and biases
import numpy as np
weights=np.around(np.random.uniform(size=6),decimals=2)
biases=np.around(np.random.uniform(size=3),decimals=2)

# Given x1 x2
x1=0.5
x2=0.85

with open ("Lab1_ANN.txt",'a') as f:
    print("The weights are: ", weights,file=f)
    print("The biases are: ", biases,file=f)
    print("x1 is {} and x2 is {}".format(x1,x2),file=f)

# Computing z1,1 z1,2

z11=x1*weights[0]+x2*weights[1]+biases[0]
z12=x1*weights[2]+x2*weights[3]+biases[1]

with open ("Lab1_ANN.txt",'a') as f:
    print('The weighted sum of the inputs at the first node in the hidden layer is {}'.format(z11, decimals=3),file=f)
    print('The weighted sum of the inputs at the second node in the hidden layer is {}'.format(z12, decimals=3),file=f)

#using sigmoid function to compute a1,1 a1,2

a11=1.0/(1.0+np.exp(-z11))
a12=1.0/(1.0+np.exp(-z12))

with open ("Lab1_ANN.txt",'a') as f:
    print('The activation of the first node in the hidden layer is {}'.format(a11, decimals=3),file=f)
    print('The activation of the second node in the hidden layer is {}'.format(a12, decimals=3),file=f)

# computing z2 for O/P layer using hidden layer inputs a1,1 a1,2
z2=a11*weights[4]+a12*weights[5]+biases[2]

# Computing O/P layer term a2
a2=1.0/(1.0+np.exp(-z2))

with open ("Lab1_ANN.txt",'a') as f:
    print('The weighted sum of the inputs at the node in the output layer is {}'.format(z2, decimals=3),file=f)
    print('The output of the network for x1 = 0.5 and x2 = 0.85 is {}'.format(a2, decimals=3),file=f)

Introduction to Deep Learning & Neural Networks with Keras/Module1/Lab1_Artificial_Neural_Networks_Simple_computation.py


import jax.numpy as np
from .normrow import normrow

def normalizerow(X):
	"""
	NORMALIZEROW normalizes the l2-norm of each row in a np array

	Input:
	  X: (n,m) numpy array
	Output:
	  X_normalized: (n,m) row normalized numpy array
	"""
	l2Norm = normrow(X)
	X_normalized = X / (l2Norm.reshape(X.shape[0],1))
	return X_normalized


import matplotlib.pyplot as plt
import numpy as np

class LinearRegressionUsingGD:
    """
    A class used to represent Gradient Decent for Linear Regression
    ...

    Attributes
    ----------
    n_iterations : int
        number of gradient decent iterations
    lr : float
        learning rate  - make sure to set this small e.g. < 0.001
    w : float
        weights variable, in this case w is the gradient tern in y = mx +c ---> y = wc + b
    b : float
        bias variable, in this case w is the gradient tern in y = mx +c ---> y = wc + b

    Public Methods
    -------
    fit(x, y)
        Fit model h(x) to the training data
    predict(x)
        Predict Y given the training data X, using the trained model h(x)
    """

    def __init__(self, lr=0.0005, n_iterations=100):
        self.n_iterations = n_iterations
        self.lr = lr
        self.w = np.random.rand(1)
        self.b = np.random.rand(1)

    def _cost_function(self, x: object, y: object) -> object:
        total = len(x)
        total_error = 0.0
        for i in range(total):
            total_error += (y[i] - (self.w * x[i] + self.b)) ** 2
        return total_error / total

    def _update_weights(self, x: object, y: object):
        weight_deriv = 0
        bias_deriv = 0
        length = len(x)
        for i in range(length):
            # Calculate partial derivatives

            # -2x(y - (mx + b))
            weight_deriv += -2 * x[i] * (y[i] - (self.w * x[i] + self.b))

            # -2(y - (mx + b))
            bias_deriv += -2 * (y[i] - (self.w * x[i] + self.b))

        # We subtract because the derivatives point in direction of steepest ascent
        self.w -= (weight_deriv / length) * self.lr
        self.b -= (bias_deriv / length) * self.lr

    def fit(self, x, y):
        """Fit model h(x) to the training data
        Parameters
        ----------
        x : array-like, shape = [n_samples, n_features]
            Training samples
        y : array-like, shape = [n_samples, n_target_values]
            Target values
        """
        cost_x = []
        cost_y = []

        for i in range(self.n_iterations):
            self._update_weights(x, y)

            # Calculate cost for auditing purposes
            cost = self._cost_function(x, y)
            cost_x.append(i)
            cost_y.append(cost)

            # Log Progress
            if i % 10 == 0:
                print(
                    "iter={},    weight={}    bias={}    cost={}".format(i, self.w, self.b, cost)
                )

        plt.subplots(1, 1)
        plt.plot(cost_x, cost_y)
        plt.xlabel('Iterations')
        plt.ylabel('J(\u03B80, \u03B81)')
        plt.show()

    def predict(self, x):
        """Predict Y given the training data X, using the trained model h(x)
        Parameters
        ----------
        x : array-like, shape = [n_samples, n_features]
          Training samples

        Returns
        ----------
        y : array-like, shape = [n_samples, n_target_values]
            Target values
        """
        return self.w * x + self.b


def scrambled_halton_sequence_generator(dimension=1, permutation=None):

    primes = prime_generator()
    bases = [next(primes) for _ in range(dimension)]
    if permutation is None:
        generators = [halton_sequence_generator(base) for base in bases]
    elif permutation == 'reverse':
        generators = [halton_sequence_generator(base, permutation=[0] + [base-i for i in range(1,base)]) for base in bases]
    elif permutation == 'Braaten-Weller':
        assert dimension<=16, "Braaten-Weller only supports dimensions up to 16"
        perms ={2: [0, 1],
                3: [0, 2, 1],
                5: [0, 2, 4, 1, 3],
                7: [0, 3, 5, 1, 6, 2, 4],
                11: [0 ,5 ,8 ,2 ,10 ,3, 6, 1, 9, 4, 7],
                13: [0, 6, 10, 2, 8, 4, 12, 1, 9, 5, 11, 3, 7],
                17: [0, 8, 13, 3, 11, 5, 16, 1, 10, 7, 14, 4, 12, 2, 15, 6, 9],
                19: [0, 9, 14, 3, 17, 6, 11, 1, 15, 7, 12, 4, 18, 8, 2, 16, 10, 5, 13],
                23: [0, 11, 17, 4, 20, 7, 13, 2, 22, 9, 15, 5, 18, 1, 14, 10, 21, 6, 16, 3, 19, 8, 12],
                29: [0, 14, 22, 5, 18, 9, 27, 2, 20, 11, 25, 7, 16, 3, 24, 13, 19, 6, 28, 10, 1, 23, 15, 12, 26, 4, 17, 8, 21],
                31: [0, 16, 8, 26, 4, 22, 13, 29, 2, 19, 11, 24, 6, 20, 14, 28, 1, 17, 9, 30, 10, 23, 5, 21, 15, 3, 27, 12, 25, 7, 18],
                37: [0, 18, 28, 6, 23, 11, 34, 3, 25, 14, 31, 8, 20, 36, 1, 16, 27, 10, 22, 13, 32, 4, 29, 17, 7, 35, 19, 2, 26, 12, 30, 9, 24, 15, 33, 5, 21],
                41: [0, 20, 31, 7, 26, 12, 38, 3, 23, 34, 14, 17, 29, 5, 40, 10, 24, 1, 35, 18, 28, 9, 33, 15, 21, 4, 37, 13, 30, 8, 39, 19, 25, 2, 32, 11, 22, 36, 6, 27, 16],
                43: [0, 21, 32, 7, 38, 13, 25, 3, 35, 17, 28, 10, 41, 5, 23, 30, 15, 37, 1, 19, 33, 11, 26, 42, 8, 18, 29, 4, 39, 14, 22, 34, 6, 24, 12, 40, 2, 31, 20, 16, 36, 9, 27],
                47: [0, 23, 35, 8, 41, 14, 27, 3, 44, 18, 31, 11, 37, 5, 25, 39, 16, 21, 33, 1, 46, 12, 29, 19, 42, 7, 28, 10, 36, 22, 4, 43, 17, 32, 13, 38, 2, 26, 45, 15, 30, 6, 34, 20, 40, 9, 24],
                53: [0, 26, 40, 9, 33, 16, 49, 4, 36, 21, 45, 12, 29, 6, 51, 23, 38, 14, 43, 1, 30, 19, 47, 10, 34, 24, 42, 3, 27, 52, 15, 18, 39, 7, 46, 22, 32, 5, 48, 13, 35, 25, 8, 44, 31, 17, 50, 2, 37, 20, 28, 11, 41],
        }
        generators = [halton_sequence_generator(base, permutation=perms[base]) for base in bases]
    elif permutation == 'random':
        generators = [halton_sequence_generator(base, permutation='pseudo-random') for base in bases]
    else:
        print("Permutation %s not supported"%permutation)
    while True:
        yield [next(gen) for gen in generators]

quasi_random_number_generator = scrambled_halton_sequence_generator

def prime_generator():
    D = {}
    q = 2
    while True:
        if q not in D:
            yield q
            D[q * q] = [q]
        else:
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]
        q += 1

def vdc(n, q, base, permutation):
    vdc, denom = 0,1
    while n:
        denom *= base
        q /= base
        n, remainder = divmod(n, base)
        vdc += permutation[remainder] * q
    return vdc

def halton_sequence_generator(base, permutation=None, rational=False):
    if permutation is None:
        permutation = list(range(base)) #no permutation
    if permutation == 'pseudo-random':
        import random

    denominator = base
    while True:
        for i in range(denominator/base,denominator):
            if permutation == 'pseudo-random':
                l = list(range(1,base))
                random.shuffle(l)
                permutation = [0] + l

            numerator = vdc(i, denominator, base, permutation)
            #print numerator,denominator

            if rational:
                yield numerator,denominator
            else:
                yield numerator / float(denominator)
        denominator *= base

""" for comparison """
def pseudo_random_sequence_generator(dimension=1):
    import random
    while True:
        yield [random.random() for _ in range(dimension)]

if __name__=="__main__":

    """Plot the worst dimensions of the quasi-random number generators, together with a pseudo-random number generator"""

    import numpy as np
    import matplotlib.pyplot as plt
    d = 3
    points = 100
    plt.figure('Normal quasi-random')
    generator = scrambled_halton_sequence_generator(dimension = d)
    samples = np.array([next(generator) for _ in range(points)])
    plt.scatter(samples[:,-2],samples[:,-1],marker=',',edgecolor='none',s=1)
    plt.xlim([0,1])
    plt.ylim([0,1])
    plt.figure('quasi-random with reverse permutation')
    generator = scrambled_halton_sequence_generator(dimension = d, permutation='reverse')
    samples = np.array([next(generator) for _ in range(points)])
    #print samples[0,:]
    plt.scatter(samples[:,-2],samples[:,-1],marker=',',edgecolor='none',s=1)
    plt.xlim([0,1])
    plt.ylim([0,1])
    plt.figure('quasi-random with Braaten-weller permutation')
    generator = scrambled_halton_sequence_generator(dimension = d, permutation='Braaten-Weller')
    samples = np.array([next(generator) for _ in range(points)])
    plt.scatter(samples[:,-2],samples[:,-1],marker=',',edgecolor='none',s=1)
    plt.xlim([0,1])
    plt.ylim([0,1])
    plt.figure('quasi-random with pseudo-random permutation')
    generator = scrambled_halton_sequence_generator(dimension = d, permutation='random')
    samples = np.array([next(generator) for _ in range(points)])
    plt.scatter(samples[:,-2],samples[:,-1],marker=',',edgecolor='none',s=1)
    plt.xlim([0,1])
    plt.ylim([0,1])
    plt.figure('actual pseudo-random')
    generator = pseudo_random_sequence_generator(dimension = d)
    samples = np.array([next(generator) for _ in range(points)])
    plt.scatter(samples[:,-2],samples[:,-1],marker=',',edgecolor='none',s=1)
    plt.xlim([0,1])
    plt.ylim([0,1])
    plt.show()


import numpy as np
from scipy import special

#define sine integral -pi/2
def sine_integral_func(x):
    tmp = special.sici(x)
    tmp2 = -np.pi/2.0 + tmp[0]
    return tmp2

#construct sine integral over range 0<x<2*pi
x = np.arange(0.0,2.0*np.pi,0.001)
y = []
[y.append(sine_integral_func(i)) for i in x]

#save the x and y vectors to a file
save_list = [x,y]
path2file='example_data/root_finding_unknown_function.txt'
np.savetxt(path2file,save_list)
print "x and y data for unknown function saved in ",path2file
print "Name of unknown function is sine_integral_func"


# =============================================================================
#  ***** earth_orbit_sun_ch3.py *****
#  Python script to solve and plot numerical solution for Newtonian
#  orbit of Earth around Sun using RK4.
#
#  Author:     Ryan Clement
#  Created:    July 2021
#
#  Change Log:
#  Who:
#  Date:       MM/DD/YYY
#  What:
#
#  Who:
#  Date:       MM/DD/YYYY
#  What:
# =============================================================================

import numpy as np
from matplotlib import pyplot as plt

# Earth Orbit Parameters
aEarth = 1.0                                                # semi-major in AU
eEarth = 0.0167086                                          # eccentricity
bEarth = aEarth*np.sqrt(1 - eEarth**2)
mu     = 4.0*np.pi**2                                       # mu = 4Pi**2 (units!)
twoPi  = 2.0*np.pi
vEarth = twoPi*np.sqrt((1 + eEarth)/(1.0 - eEarth))     # VisViva @ Perihelion

# Earth Orbit as Ellipse
def rOrbit(a,e,t):
    return a*(1.0 - e**2)/(1.0 + e*np.cos(t))
def xOrbit(a,e,t):
    return rOrbit(a,e,t)*np.cos(t)
def yOrbit(a,e,t):
    return rOrbit(a,e,t)*np.sin(t)
t = np.linspace(0,twoPi,100)
xE = xOrbit(aEarth,eEarth,t)
yE = yOrbit(aEarth,eEarth,t)

# Earth Orbit Initial Conditions
x0  = 1.0 - eEarth
y0  = 0.0
vx0 = 0.0
vy0 = vEarth
nSteps = 12    # to complete one orbit
dt  = 1.0/nSteps
dt6 = dt/6.0

# Newton's Equations
def calcR3(x,y):
    return (x**2 + y**2)**(3/2)
# xD = vx
# yD = vy
# vxD = -mu*x/r3
# vyD = -mu*y/r3

# Earth Orbit via RK4
def update(x,y,vx,vy):
    # k1
    r3 = calcR3(x, y)
    k1x = vx
    k1y = vy
    k1vx = -mu*x/r3
    k1vy = -mu*y/r3
    xu = x + k1x*dt/2.0
    yu = y + k1y*dt/2.0
    # k2
    r3 = calcR3(xu, yu)
    k2x = vx + k1vx*dt/2.0
    k2y = vy + k1vy*dt/2.0
    k2vx = -mu*xu/r3
    k2vy = -mu*yu/r3
    # k3
    xu = x + k2x*dt/2.0
    yu = y + k2y*dt/2.0
    r3 = calcR3(xu, yu)
    k3x = vx + k2vx*dt/2.0
    k3y = vy + k2vy*dt/2.0
    k3vx = -mu*xu/r3
    k3vy = -mu*yu/r3
    # k4
    xu = x + k3x*dt
    yu = y + k3y*dt
    r3 = calcR3(xu, yu)
    k4x = vx + k3vx*dt
    k4y = vy + k3vy*dt
    k4vx = -mu*xu/r3
    k4vy = -mu*yu/r3
    # Weighted Sum
    kx = k1x + 2.0*k2x + 2.0*k3x + k4x
    ky = k1y + 2.0*k2y + 2.0*k3y + k4y
    kvx = k1vx + 2.0*k2vx + 2.0*k3vx + k4vx
    kvy = k1vy + 2.0*k2vy + 2.0*k3vy + k4vy
    # Updates
    xN = x + dt6*kx
    yN = y + dt6*ky
    vxN = vx + dt6*kvx
    vyN = vy + dt6*kvy
    return xN,yN,vxN,vyN

xN = x0
yN = y0
vxN = vx0
vyN = vy0
xArr = [x0]
yArr = [y0]
vxArr = [vx0]
vyArr = [vy0]
tArr = [0]
for i in range(nSteps):
    xN, yN, vxN, vyN = update(xN,yN,vxN,vyN)
    xArr.append(xN)
    yArr.append(yN)
    vxArr.append(vxN)
    vyArr.append(vyN)
    tArr.append(dt*(i+1))

print(f'xAnal: {x0}, xComp: {xArr[-1]}')
print('X Percent Difference: ', 100*(x0-xArr[-1])/x0)
print(f'yAnal: {y0}, yComp: {yArr[-1]}')
# print('Y Percent Difference: ', 100*(y0-yArr[-1])/y0)
print(f'vxAnal: {vx0}')
print(f'vyAnal: {vy0}')

# Orbit plot
figE, axE = plt.subplots()
axE.set_title('Earth Orbit')
axE.set_xlabel('x (AU)')
axE.set_ylabel('y (AU)')
axE.plot(xArr,yArr,'+',color='red',label='RK4')
axE.plot(xE, yE, color='blue',label='Analytic')
axE.plot(aEarth*eEarth,0,'o',color='orange',label='Sun')
axE.grid('true')
axE.axis('equal')
axE.set(xlim=(-1.1,1.1),ylim=(-1.1,1.1))
axE.legend()
# figE.savefig('earth_orbit.png')

# Angular momentum plot
xA = np.array(xArr)
yA = np.array(yArr)
vxA = np.array(vxArr)
vyA = np.array(vyArr)
rxvAnal = (x0*vy0 - y0*vx0)
rxvAnalArr = [rxvAnal]*len(tArr)
rxvAA = np.array(rxvAnalArr)
rxvCompArr = xA*vyA - yA*vxA
perDiffArr = 100*(rxvAA - rxvCompArr)/rxvAA
figL, axL = plt.subplots()
axL.set_title('Angular Momentum')
axL.set_xlabel('t (periods)')
axL.set_ylabel(r'r $x$ v')
axL.plot(tArr,rxvAA,color='blue',label='Exact')
axL.plot(tArr,rxvCompArr,color='red',label='Computed')
axL.legend()
# figL.savefig('earth_orbit_Lcon.png')

# Transformed Polar Example
eEarth = 0.0167086                                          # eccentricity
mu     = 4.0*np.pi**2                                       # mu = 4Pi**2 (units!)
aS     = mu*(1.0 - eEarth**2)
ma     = 1.0/(1.0 - eEarth**2)
twoPi  = 2.0*np.pi
print(f'Analytical Minimum Radius: {1.0-eEarth} au')
print(f'Analytical Maximum Radius: {1.0+eEarth} au')
def q2r(q):
    return 1.0/q
# IC
q0  = 1.0/(1.0 - eEarth)
z0  = 0.0
nSteps = 12
dTh = twoPi/nSteps
def newQZ_FE(dTh, q, z):
    c = 1.0/(1.0 - eEarth**2)
    qN  = q + z*dTh
    zN  = z + (c-q)*dTh
    return qN, zN
def newQZ_RK(dTh, q, z):
    c = 1.0/(1.0 - eEarth**2)
    dTh6 = dTh/6.0
    aq0 = q
    az0 = z
    kq1 = az0
    kz1 = c - aq0
    aq1 = aq0 + dTh*kq1/2.0
    az1 = az0 + dTh*kz1/2.0
    kq2 = az1
    kz2 = c - aq1
    aq2 = aq0 + dTh*kq2/2.0
    az2 = az0 + dTh*kz2/2.0
    kq3 = az2
    kz3 = c - aq2
    aq3 = aq0 + dTh*kq3
    az3 = az0 + dTh*kz3
    kq4 = az3
    kz4 = c - aq3
    kq  = kq1 + 2.0*kq2 + 2.0*kq3 + kq4
    kz  = kz1 + 2.0*kz2 + 2.0*kz3 + kz4
    qN  = aq0 + dTh6*kq
    zN  = az0 + dTh6*kz
    return qN, zN
rArr = [q2r(q0)]
thArr = [0.0]
qO = q0
zO = z0
for i in range(1,nSteps+1):
    qN, zN = newQZ_RK(dTh,qO,zO)
    rArr.append(q2r(qN))
    thArr.append(i*dTh)
    qO = qN
    zO = zN
figP, axP = plt.subplots(subplot_kw={'projection': 'polar'})
axP.plot(thArr,rArr,'.',color='blue',label='Earth')
axP.plot(0,0,'o',color='orange',label='Sun')
axP.set_title('Earth Orbit')
axP.legend(loc='best', bbox_to_anchor=(1.1, 1.2))
# figP.savefig('earth_orbit_PolarTrans.png')

print(f'Min computed radius: {min(rArr)} au')
print(f'Max computed radius: {max(rArr)} au')


from fenics import *
import numpy as np
from ufl import nabla_div, max_value
import sympy as sym

x, y = sym.symbols('x[0], x[1]')

# Creating mesh and defining function space
E = []
DOF = []
mesh = UnitSquareMesh(60, 60)
V = FunctionSpace(mesh, 'P', 1)

# Defining Exact Solution for Pressure Distribution
p_e = Expression('1 - x[0]*x[0]', degree=2)

# Defining Dirichlet boundary
p_L = Constant(1.0)

def boundary_L(x, on_boundary):
    return on_boundary and near(x[0], 0)

bc_L = DirichletBC(V, p_L, boundary_L)

p_R = Constant(0.0)

def boundary_R(x, on_boundary):
    return on_boundary and near(x[0], 1)

bc_R = DirichletBC(V, p_R, boundary_R)

bcs = [bc_L, bc_R]

# Defining variational problem
p = TrialFunction(V)
v = TestFunction(V)
d = 2
I = Identity(d)
x, y = SpatialCoordinate(mesh)
M = max_value(Constant(0.10), exp(-(10*y-1.0*sin(10*x)-5.0)**2))
K = M*I
a = dot(K*grad(p), grad(v))*dx
f1 = as_vector((-2*x, 0))
f = nabla_div(dot(-K, f1))
L = inner(f, v)*dx

# Computing solutions
p = Function(V)
solve(a == L, p, bcs)
u_bar = -K*grad(p)

# Projecting the Velocity profile
W = VectorFunctionSpace(mesh, 'P', 1)
u_bar1 = project(u_bar, W)

# Evaluating difference between Numerical pressure, p and Exact pressure, p_e
p_e_f = interpolate(p_e, V)
diff_p = Function(V)
diff_p.vector()[:] = p.vector() - p_e_f.vector()

# Computing Error in L2 norm for Pressure
E1 = errornorm(p_e_f, p, 'L2')
print('E1 =', E1)
E.append(E1)
DOF.append(len(V.dofmap().dofs()))

# Plotting Pressure Gradient, Difference_in_Numeric(p)_&_Exact_Pressure(p_e)_and Velocity_Profile
xdmf = XDMFFile('Numerical_Pressure_Gradient.xdmf')
xdmf.write(p)
xdmf.close()

xdmf = XDMFFile('Difference_in_Pressures.xdmf')
xdmf.write(diff_p)
xdmf.close()

xdmf = XDMFFile('Analytical_Pressure_Gradient.xdmf')
xdmf.write(p_e_f)
xdmf.close()

xdmf = XDMFFile('Velocity_Profile.xdmf')
xdmf.write(u_bar1)
xdmf.close()

print(E)
print(DOF)
Ea = np.array(E)
DOFa = np.array(DOF)
LogEa = np.log(Ea)
LogDOFa = np.log(DOFa)
print(LogEa)
print(LogDOFa)
print(type(p))
print(type(p_e_f))


import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-5, +5, 1000)

# Classification loss functions
y_error = (1 - np.sign(x)) / 2
y_hinge = np.maximum(1 - x, 0)
y_savage = 1 / np.square(1 + np.exp(x))
y_tangent = np.square(2 * np.arctan(x) - 1)
y_logistic = np.log(1 + np.exp(-x))
y_exponential = np.exp(-x)

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1)
ax.set_xlim([-5.0,+5.0])
ax.set_ylim([-0.3,+8.0])
ax.set_title("classification loss functions")
ax.set_xlabel("edge = target * output")
ax.set_ylabel("loss(edge)")
ax.grid(color='grey', linestyle='-', linewidth=0.25, alpha=0.5)

plt.plot(x, y_error, 'k', label="0-1 loss", linewidth=2, linestyle="--")
plt.plot(x, y_hinge, 'r', label="hinge", linewidth=2)
plt.plot(x, y_savage, 'm', label="savage", linewidth=2)
plt.plot(x, y_tangent, 'c', label="tangent", linewidth=2)
plt.plot(x, y_logistic, 'g', label="logistic", linewidth=2)
plt.plot(x, y_exponential, 'b', label="exponential", linewidth=2)

plt.legend(loc='upper right')
plt.savefig("plot_losses_classification.png")
plt.clf()

# Regression loss functions
y_absolute = np.abs(x)
y_squared = np.square(x) / 2
y_cauchy = np.log(1 + np.square(x)) / 2

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1)
ax.set_xlim([-5.0,+5.0])
ax.set_ylim([-0.3,+8.0])
ax.set_title("regression loss functions")
ax.set_xlabel("diff = target - output")
ax.set_ylabel("loss (diff)")
ax.grid(color='grey', linestyle='-', linewidth=0.25, alpha=0.5)

plt.plot(x, y_absolute, 'r', label="absolute", linewidth=2)
plt.plot(x, y_squared, 'g', label="squared", linewidth=2)
plt.plot(x, y_cauchy, 'b', label="cauchy", linewidth=2)

plt.legend(loc='upper right')
plt.savefig("plot_losses_regression.png")


# Bisection method root finding tests

from bisect import bisect
from math import sin, cos, exp

import numpy as np

# Get machine precision
eps = np.finfo(float).eps

################################################################################

print('-----------------------------------------------------------------------')
print('f(x) = sin(x)-cos(x)^2')
f = lambda x: sin(x)-cos(x)**2
x,steps = bisect(-1,1,f,eps)
print(" x = {}\n steps = {}\n error = {}".format(x,steps,f(x)))

################################################################################

print('-----------------------------------------------------------------------')
print('f(x) = x^2-8*x+2')
f = lambda x: x**2-8*x+2
x,steps = bisect(0,5,f,eps)
print(" x = {}\n steps = {}\n error = {}".format(x,steps,f(x)))

################################################################################

print('-----------------------------------------------------------------------')
print('f(x) = 2**x-cos(x+3)**2+exp(x+2)')
f = lambda x: 2**x-(cos(x+3))**2+exp(x+2)
x,steps = bisect(-10,10,f,eps)
print(" x = {}\n steps = {}\n error = {}".format(x,steps,f(x)))


import sys
from math import e, pi, sqrt
import random
import numpy as np
from numpy.core.numeric import Infinity

sys.path.insert(0, '../utilities')

import PlottingUtility as pu

# === Region Colors ===
# 0 : Red
# 1 : Orange
# 2 : Yellow
# 3 : Green

class Stats:
    def __init__(self, input_vect, region_colors):
        self.data = input_vect
        self.size = len(self.data)
        self.mean = np.average(self.data)

        # Calculate stdev
        acc = 0
        for i in range(0, self.size):
            acc += (self.data[i] - self.mean) ** 2
        acc = acc / self.size
        self.stdev = sqrt(acc)

        self.regions = {
            0 : (-float('inf'), self.mean - (3 * self.stdev)),
            1 : (self.mean - (3 * self.stdev), self.mean - (2 * self.stdev)),
            2 : (self.mean - (2 * self.stdev), self.mean - (self.stdev)),
            3 : (self.mean - (self.stdev), self.mean),
            4 : (self.mean, self.mean + (self.stdev)),
            5 : (self.mean + (self.stdev), self.mean + (2 * self.stdev)),
            6 : (self.mean + (2 * self.stdev), self.mean + (3 * self.stdev)),
            7 : (self.mean + (3 * self.stdev), float('inf')),
        }

        if(len(region_colors) != 8):
            sys.exit('region_colors parameter must be a list of 8 integers')
        self.region_colors = region_colors

    def display_dataset(self):
        for i in range(0, len(self.data)):
            print(i, ':', self.data[i])

    def plot_gauss_bell(self, ofile, title):
        xbell = np.array([])
        xdata = np.array([])
        ybell = np.array([])
        ydata = np.array([])

        low_range = self.mean - (self.stdev * 4)
        high_range = self.mean + (self.stdev * 4)
        increments = (high_range - low_range) / 1000
        count = low_range
        while (count < high_range):
            val = (1.0 / (self.stdev * sqrt(2 * pi))) * e ** (-0.5 * ((count - self.mean) / self.stdev) ** 2)
            xbell = np.append(xbell, count)
            ybell = np.append(ybell, val)
            count += increments

        max_y_curve = np.max(ybell)

        for i in range(0, len(self.data)):
            xdata = np.append(xdata, self.data[i])
            max_y = (1.0 / (self.stdev * sqrt(2 * pi))) * e ** (-0.5 * ((self.data[i] - self.mean) / self.stdev) ** 2)
            ydata = np.append(ydata, random.uniform(0, max_y))

        xmean = np.array([self.mean, self.mean])
        ymean = np.array([0, max_y_curve])
        xsd1 = np.array([self.mean - (self.stdev * 3), self.mean - (self.stdev * 3)])
        ysd1 = np.array([0, max_y_curve])
        xsd2 = np.array([self.mean - (self.stdev * 2), self.mean - (self.stdev * 2)])
        ysd2 = np.array([0, max_y_curve])
        xsd3 = np.array([self.mean - (self.stdev), self.mean - (self.stdev)])
        ysd3 = np.array([0, max_y_curve])
        xsd4 = np.array([self.mean + (self.stdev), self.mean + (self.stdev)])
        ysd4 = np.array([0, max_y_curve])
        xsd5 = np.array([self.mean + (self.stdev * 2), self.mean + (self.stdev * 2)])
        ysd5 = np.array([0, max_y_curve])
        xsd6 = np.array([self.mean + (self.stdev * 3), self.mean + (self.stdev * 3)])
        ysd6 = np.array([0, max_y_curve])

        n_datasets = 9
        colors_ds = ['#0743E8', '#E80730', '#009E1A', '#000000', '#000000',
                     '#000000', '#000000', '#000000', '#000000', '#000000']
        linestyle_ds = ['-', '', '-', '--', '--', '--', '--', '--', '--']
        markers_ds = ['', 'o', '', '', '', '', '', '', '']
        title_plot = title
        enable_rolling_avg_ds = [False, False, False, False, False, False, False, False, False]
        x_label = ''
        y_label = ''
        stitle = ''
        out_file = ofile
        ravg_days_ds = [None, None, None, None, None, None, None, None, None]
        ravg_labels_ds = [False, False, False, False, False, False, False, False, False]
        ravg_ydata_ds = [None, None, None, None, None, None, None, None, None]

        self.plot = pu.LayeredScatterPlot(
            n_datasets,
            colors_ds,
            linestyle_ds,
            markers_ds,
            title_plot,
            enable_rolling_avg_ds,
            x_label,
            y_label,
            [xbell, xdata, xmean, xsd1, xsd2, xsd3, xsd4, xsd5, xsd6],
            [ybell, ydata, ymean, ysd1, ysd2, ysd3, ysd4, ysd5, ysd6],
            stitle,
            out_file,
            ravg_days_ds,
            ravg_labels_ds,
            ravg_ydata_ds
        )
        self.plot.export()

    def plot_gauss_bell_datapoint(self, ofile, title, idx):
        xbell = np.array([])
        xdata = np.array([])
        ybell = np.array([])
        ydata = np.array([])

        low_range = self.mean - (self.stdev * 4)
        high_range = self.mean + (self.stdev * 4)
        increments = (high_range - low_range) / 1000
        count = low_range
        while (count < high_range):
            val = (1.0 / (self.stdev * sqrt(2 * pi))) * e ** (-0.5 * ((count - self.mean) / self.stdev) ** 2)
            xbell = np.append(xbell, count)
            ybell = np.append(ybell, val)
            count += increments

        max_y_curve = np.max(ybell)

        for i in range(0, len(self.data)):
            xdata = np.append(xdata, self.data[i])
            max_y = (1.0 / (self.stdev * sqrt(2 * pi))) * e ** (-0.5 * ((self.data[i] - self.mean) / self.stdev) ** 2)
            ydata = np.append(ydata, random.uniform(0, max_y))

        xmean = np.array([self.mean, self.mean])
        ymean = np.array([0, max_y_curve])
        xsd1 = np.array([self.mean - (self.stdev * 3), self.mean - (self.stdev * 3)])
        ysd1 = np.array([0, max_y_curve])
        xsd2 = np.array([self.mean - (self.stdev * 2), self.mean - (self.stdev * 2)])
        ysd2 = np.array([0, max_y_curve])
        xsd3 = np.array([self.mean - (self.stdev), self.mean - (self.stdev)])
        ysd3 = np.array([0, max_y_curve])
        xsd4 = np.array([self.mean + (self.stdev), self.mean + (self.stdev)])
        ysd4 = np.array([0, max_y_curve])
        xsd5 = np.array([self.mean + (self.stdev * 2), self.mean + (self.stdev * 2)])
        ysd5 = np.array([0, max_y_curve])
        xsd6 = np.array([self.mean + (self.stdev * 3), self.mean + (self.stdev * 3)])
        ysd6 = np.array([0, max_y_curve])

        xdatapoint = np.array([xdata[idx]])
        ydatapoint = np.array([ydata[idx]])

        n_datasets = 10
        colors_ds = ['#0743E8', '#E80730', '#009E1A', '#000000', '#000000',
                     '#000000', '#000000', '#000000', '#000000', '#000000']
        linestyle_ds = ['-', '', '', '-', '--', '--', '--', '--', '--', '--']
        markers_ds = ['', 'o', 'o', '', '', '', '', '', '', '']
        enable_rolling_avg_ds = [False, False, False, False, False, False, False, False, False, False]
        title = 'Datapoint x:' + str(xdata[idx])
        x_label = ''
        y_label = ''
        stitle = ''
        out_file = ofile
        ravg_days_ds = [None, None, None, None, None, None, None, None, None, None]
        ravg_labels_ds = [False, False, False, False, False, False, False, False, False, False]
        ravg_ydata_ds = [None, None, None, None, None, None, None, None, None, None]

        self.plot = pu.LayeredScatterPlot(
            n_datasets,
            colors_ds,
            linestyle_ds,
            markers_ds,
            title,
            enable_rolling_avg_ds,
            x_label,
            y_label,
            [xbell, xdata, xdatapoint, xmean, xsd1, xsd2, xsd3, xsd4, xsd5, xsd6],
            [ybell, ydata, ydatapoint, ymean, ysd1, ysd2, ysd3, ysd4, ysd5, ysd6],
            stitle,
            out_file,
            ravg_days_ds,
            ravg_labels_ds,
            ravg_ydata_ds
        )
        self.plot.export()

    def get_indicator(self, idx):
        if(idx < 0 or idx >= len(self.data)):
            sys.exit('idx cannot be < 0 or >= len(self.data) ->', str(len(self.data)))
        datapoint = self.data[idx]
        color_val = 0
        for i in range(0, len(self.regions)):
            if(datapoint >= self.regions[i][0] and datapoint < self.regions[i][1]):
                color_val = self.region_colors[i]
                break
        indicator = 0
        # return red circle
        if(color_val == 0):
            indicator = u'\U0001F534'
        # return orange circle
        elif(color_val == 1):
            indicator = u'\U0001F7E0'
        # return yellow circle
        elif(color_val == 2):
            indicator = u'\U0001F7E1'
        # return green circle
        elif(color_val == 3):
            indicator = u'\U0001F7E2'
        return indicator


import numpy as np
import pandas as pd
import scipy.stats
def drawdown(return_series: pd.Series):
    """
    Takes a Time Series of asset returns and computes and returns a dataframe that contains
    1) Wealth Idex
    2) Previous Peaks
    3) Percentage of drawdowns
    """
    wealth_index = 1000*(1+return_series).cumprod()
    previous_peaks = wealth_index.cummax()
    drawdowns = (wealth_index-previous_peaks)/previous_peaks
    return pd.DataFrame(dict(Wealth = wealth_index, Peaks = previous_peaks, Drawdowns = drawdowns))

def get_ffme_returns():
    """
    Load the Farma-French Dataset for the returns of the top and bottom deciles by Market Cap
    """
    me_m = pd.read_csv('Portfolios_Formed_on_ME_monthly_EW.csv', header=0, index_col=0, na_values = -99.99)
    rets = me_m[['Lo 10', 'Hi 10']]
    rets.columns = ['SmallCap', 'LargeCap']
    rets = rets/100
    rets.index = pd.to_datetime(rets.index, format="%Y%m").to_period('M')
    return rets

def get_hfi_returns():
    """
    Load the Edhec Hedge Fund Dataset for the returns of the top and bottom deciles by Market Cap
    """
    hfi = pd.read_csv('edhec-hedgefundindices.csv', header=0, index_col=0, parse_dates=True)
    hfi = hfi/100
    hfi.index = hfi.index.to_period('M')
    return hfi

def skewness(r):
    """
    Alternative method to scipy.stats.skew()
    Computes the skewness of the supplied Series or Dataframe
    Retruns a float or a series
    """
    demeaned_r = r - r.mean()
    #use the population standard deviation so set dof=0
    sigma_r = r.std(ddof=0)
    exp = (demeaned_r**3).mean()
    return exp/(sigma_r**3)

def kurtosis(r):
    """
    Alternative method to scipy.stats.kurtosis()
    Computes the kurtosis of the supplied Series or Dataframe
    Retruns a float or a series
    """
    demeaned_r = r - r.mean()
    #use the population standard deviation so set dof=0
    sigma_r = r.std(ddof=0)
    exp = (demeaned_r**4).mean()
    return exp/(sigma_r**4)

def is_normal(r, level = 0.01):
    """
    Applies the Jarque-Bera test to determine if a series is normal or not
    Test is applied at the 1% level by default
    Returns true if the hypothesis of normality is accepted otherwise returns False
    """
    statistic, p_value = scipy.stats.jarque_bera(r)
    return p_value>level


import numpy as np
from operator import itemgetter

def kruskal(nodes,A):
    group=createDict(nodes)
    sortedEdges=A.take(A[:,2].astype(int).argsort(),axis=0)
    s=len(nodes)
    i=0
    j=0
    edges=np.empty((s-1,3),dtype='|S2')
    while j<s-1:
        if group.find(sortedEdges[i,0])!=group.find(sortedEdges[i,1]):
            edges[j,:]=sortedEdges[i]
            group.union(group.find(sortedEdges[i,0]),group.find(sortedEdges[i,1]))
            j=j+1
        i=i+1
    return edges

def createDict(nodes):
    group=DisjointSet()
    s=len(nodes)
    for i in nodes:
        group.add(i)
    return group

#This is a solution to problem about disjoint sets that comes from http://programmingpraxis.com/

class DisjointSet(dict):
    def add(self, item):
        self[item] = item

    def find(self, item):
        parent = self[item]

        while self[parent] != parent:
            parent = self[parent]

        self[item] = parent
        return parent

    def union(self, item1, item2):
        self[item2] = self[item1]

nodes = list( "ABCDEFG" )
edges = [ ("A", "B", 7), ("A", "D", 5),
          ("B", "C", 8), ("B", "D", 9), ("B", "E", 7),
      ("C", "E", 5),
      ("D", "E", 15), ("D", "F", 6),
      ("E", "F", 8), ("E", "G", 9),
      ("F", "G", 11)]

kruskal(nodes,np.array(edges))

def prims(nodes,edges):
    def change(t):
        for q in list(canid):
            if q[0]==t or q[1]==t:
                canid.remove(q)

        for q in list(m):
            if q[0]==t or q[1]==t:
                m.remove(q)
                canid.append(q)
        return
    m=list(edges)
    sol=[]
    canid=[]
    nadded=[]
    temp=min(m,key=itemgetter( 2 ))
    sol.append(temp)
    change(temp[0])
    change(temp[1])
    nadded.append(temp[0])
    nadded.append(temp[1])
    sz = len(nodes) - 2
    for i in xrange(sz):
        temp=min(canid,key=itemgetter( 2 ))
        sol.append(temp)
        if nadded.count(temp[0])==0:
            change(temp[0])
            nadded.append(temp[0])
        else:
            change(temp[1])
            nadded.append(temp[1])
    return sol

prims(nodes,edges)

def formChanger(oldData):
    newData=[]
    for i in oldData:
        newData.append((i[0],i[1],int(i[2])))
    return newData

q=np.load("MSTdata.npy")
edges=formChanger(q)
nodes = list( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcd")

kruskal(nodes,np.array(edges))
prims(nodes,edges)


# Laboratório Computacional 2

No laboratório computacional, você praticará o que aprendeu. Resolva os problemas com o auxílio do Python pesquisando apenas as informações essenciais de que precisa. Não use respostas prontas.

**Problema:** Uma fórmula usada para modelar o crescimento populacional é a função exponencial $P(t) = Ce^{rt}$, onde $C$ é a população inicial, $r$ a *taxa de crescimento* e $t$ o tempo decorrido. De acordo com o [[IBGE]](https://cidades.ibge.gov.br/brasil/pb), a população estimada no estado da Paraíba no último censo em 2010 era de 3.766.528 pessoas. Em 2019, este numéro saltou para 4.018.127 pessoas. Diante disso, crie um modelo simbólico para responder os itens a seguir:

- considerando o interstício de 2010 a 2019, qual foi a taxa de crescimento $r$ do estado?

- supondo que a população cresça com na mesma taxa encontrada no item anterior, qual será a população do estado em 2037? E em 2055?

**Problema:** O autor do código

```python
def minha_equacao_simbolica_legal():
    ...
    x = symbols('x')
    ...
    return (1/2*x^2 + 2*tg(x) + 3/4)^(3/2)
```

gostaria de obter o seguinte resultado simbólico

$$\left(\frac{\left(y + 2\right)^{2}}{2} + 2 \tan{\left(y + 2 \right)} + 0.75\right)^{1.5}$$

Porém, ele está com dificuldades. Ajude-o a detectar os problemas e corrija o código para que a função retorne a expressão desejada.

def minha_equacao_simbolica_legal():
    from sympy import symbols, tan
    x, y = symbols('x y')
    f = (x**2/2 + 2*tan(x) + 3/4)**(3/2)
    return f.subs(x,y+2)
minha_equacao_simbolica_legal()

**Problema:** Suponha que você é um cientista de dados trabalhando em um projeto que busca compreender como a iluminação de um ambiente afeta o desempenho de leitura em crianças na primeira infância.

A *iluminância* é uma grandeza física que mede o fluxo luminoso total sobre uma superfície por metro quadrado, isto é, a quantidade de luz incidente que ilumina a superfície. A fórmula de cálculo da iluminância produzida por um raio luminoso é dada por

$$E = \dfrac{I\cos(\theta)}{d^2},$$

onde $E$ é a iluminância, medida em $lux$ ou $lm/m^2$ (1 lux = 1 lúmen por metro quadrado), $I$ é o fluxo luminoso, medido em $lm$ (lúmen), $\theta$ é o ângulo entre o raio luminoso e a perpendicular à superfície no ponto de incidência, medido em graus, e $d$ é a distância entre a fonte luminosa e o ponto de incidência ("comprimento" do raio luminoso), medida em $m$ (metro). Quando a fonte está posta perpendicularmente ao ponto de incidência, a fórmula acima reduz-se a $E = I/d^2$.

A figura abaixo exemplifica os casos para $\theta = 0$ e $\theta = 45^{\circ}$.

- Sabendo que 100 lúmens = 1 Watt (W), prencha a tabela a seguir com os valores de $E$ correspondentes para quatro diferentes  potências de lâmpadas de LED e ângulos de incidência da fonte luminosa considerando $d = 2.5 \, m$. O valor para 6 W e 0 grau está preenchido como exemplo.

| LED | 0 | 30 | 45 | 60 |
|---|---|---|---|---|
|6 W| 96 lux  |
|8 W|
|10 W|
|12 W|

- Com a fórmula acima, é possível descobrir o percentual de iluminância que se ganha ou perde ao se inclinar a fonte luminosa a partir de 0 grau. Por exemplo, a iluminância a 45 graus é aproximadamente 71% superior em relação aquela obtida a 0 grau. Estime o percentual de iluminância relativo a 0 grau para os ângulos da tabela a seguir.

|ângulo | % |
|---|---|
| 0 | - |
| 36 | |
| 45 | 71|
| 72 |
| 88 |

**Obervações:**

- use livremente computação simbólica, funções normais, funções embutidas ou anônimas e/ou substituições como quiser.
- certifique-se de que os ângulos foram convertidos para radianos a fim de usar as funções do Python corretamente.

""" solucao
I = 6 theta = 0 iluminancia = 96.0000000000000 lux
I = 6 theta = 30 iluminancia = 48.0*sqrt(3) lux
I = 6 theta = 45 iluminancia = 48.0*sqrt(2) lux
I = 6 theta = 60 iluminancia = 48.0000000000000 lux
I = 8 theta = 0 iluminancia = 128.000000000000 lux
I = 8 theta = 30 iluminancia = 64.0*sqrt(3) lux
I = 8 theta = 45 iluminancia = 64.0*sqrt(2) lux
I = 8 theta = 60 iluminancia = 64.0000000000000 lux
I = 10 theta = 0 iluminancia = 160.000000000000 lux
I = 10 theta = 30 iluminancia = 80.0*sqrt(3) lux
I = 10 theta = 45 iluminancia = 80.0*sqrt(2) lux
I = 10 theta = 60 iluminancia = 80.0000000000000 lux
I = 12 theta = 0 iluminancia = 192.000000000000 lux
I = 12 theta = 30 iluminancia = 96.0*sqrt(3) lux
I = 12 theta = 45 iluminancia = 96.0*sqrt(2) lux
I = 12 theta = 60 iluminancia = 96.0000000000000 lux
""";


import numpy as np

a = np.arange(12).reshape(3, 4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

b = np.arange(12).reshape(4, 3).T
print(b)
# [[ 0  3  6  9]
#  [ 1  4  7 10]
#  [ 2  5  8 11]]

a_compare = a < b
print(a_compare)
# [[False  True  True  True]
#  [False False  True  True]
#  [False False False False]]

print(type(a_compare))
# <class 'numpy.ndarray'>

print(a_compare.dtype)
# bool

print(a > b)
# [[False False False False]
#  [ True  True False False]
#  [ True  True  True False]]

print(a <= b)
# [[ True  True  True  True]
#  [False False  True  True]
#  [False False False  True]]

print(a >= b)
# [[ True False False False]
#  [ True  True False False]
#  [ True  True  True  True]]

print(a == b)
# [[ True False False False]
#  [False False False False]
#  [False False False  True]]

print(a != b)
# [[False  True  True  True]
#  [ True  True  True  True]
#  [ True  True  True False]]

b_float = b.astype(float)
print(b_float)
# [[ 0.  3.  6.  9.]
#  [ 1.  4.  7. 10.]
#  [ 2.  5.  8. 11.]]

print(b_float.dtype)
# float64

print(a == b_float)
# [[ True False False False]
#  [False False False False]
#  [False False False  True]]

b_1d = np.arange(4, 8)
print(b_1d)
# [4 5 6 7]

print(a < b_1d)
# [[ True  True  True  True]
#  [False False False False]
#  [False False False False]]

print(a < 6)
# [[ True  True  True  True]
#  [ True  True False False]
#  [False False False False]]

print(a % 2)
# [[0 1 0 1]
#  [0 1 0 1]
#  [0 1 0 1]]

print(a % 2 == 0)
# [[ True False  True False]
#  [ True False  True False]
#  [ True False  True False]]

print(np.count_nonzero(a < 6))
# 6

print(np.all(a < 6))
# False

print(np.all(a < 6, axis=1))
# [ True False False]

print(np.any(a < 6))
# True

print(np.any(a < 6, axis=1))
# [ True  True False]

a_nan = np.array([0, 1, np.nan])
print(a_nan)
# [ 0.  1. nan]

print(a_nan == np.nan)
# [False False False]

print(np.isnan(a_nan))
# [False False  True]

print(a_nan > 0)
# [False  True False]
#
# /usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in greater
#   """Entry point for launching an IPython kernel.

a_nan_only = np.array([np.nan])
print(a_nan_only)
# [nan]

print(a_nan_only > 0)
# [False]

print(np.nan > 0)
# False

x = 6

print(4 < x < 8)
# True

# print(4 < a < 8)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

print((a > 4) & (a < 8))
# [[False False False False]
#  [False  True  True  True]
#  [False False False False]]

# print((a > 4) and (a < 8))
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

# print(a > 4 & a < 8)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

x = 6
y = 6
z = 6

print(x == y == z)
# True

c = np.zeros((3, 4), int)
print(c)
# [[0 0 0 0]
#  [0 0 0 0]
#  [0 0 0 0]]

# print(a == b == c)
# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

print((a == b) & (b == c))
# [[ True False False False]
#  [False False False False]
#  [False False False False]]


import numpy as np

def mult_matrix(M, N):
    """Multiply square matrices of same dimension M and N"""
    # Converts N into a list of tuples of columns
    tuple_N = zip(*N)

    # Nested list comprehension to calculate matrix multiplication
    return [[sum(el_m * el_n for el_m, el_n in zip(row_m, col_n)) for col_n in tuple_N] for row_m in M]

def QR(A):
    m, n = A.shape
    Q = np.eye(m)
    for i in range(n - (m == n)):
        H = np.eye(m)
        H[i:, i:] = Householder(A[i:, i])
        Q = np.dot(Q, H)  # Create Q
        A = np.dot(H, A)  # Create R
    return Q, A

def Householder(a):
    v = a / (a[0] + np.copysign(np.linalg.norm(a), a[0]))
    v[0] = 1
    H = np.eye(a.shape[0])
    H -= (2 / np.dot(v, v)) * np.dot(v[:, None], v[None, :])
    return H

def SolveQR(a, b):
    q, r = QR(a)
    _, n = r.shape
    return np.linalg.solve(r[:n, :], np.dot(q.T, b)[:n])

a = np.array([[2, 3, 1],
              [5, 1, 2],
              [2, 8, 1]])
b = np.array([6, 8, 11])
Q, R = QR(a)
Q = Q.round(6)
R = R.round(6)
qq, rr = np.linalg.qr(a)
print("Q:")
print(Q)
print(qq)
print("R:")
print(R)
print(rr)

x = SolveQR(a,b)
print("X: ",x)


import numpy as np
import utils
import math
import scipy
from scipy import optimize
import random
from scipy.special import xlogy

class LogisticRegressor:

    def __init__(self):
        self.theta = None

    def train(self,X,y,num_iters=1000):

        """
        Train a linear model using scipy's function minimization.

        Inputs:
        - X: N X D array of training data. Each training point is a D-dimensional
         row.
        - y: 1-dimensional array of length N with values in the reals.
        - num_iters: (integer) number of steps to take when optimizing

        Outputs:
        - optimal value for theta
        """

        num_train,dim = X.shape

        # standardize X so that each column has zero mean and unit variance
        # remember to take out the first column and do the feature normalize

        X_without_1s = X[:,1:]
        X_norm, mu, sigma = utils.std_features(X_without_1s)

        # add the ones back and assemble the XX matrix for training

        XX = np.vstack([np.ones((X_norm.shape[0],)),X_norm.T]).T
        theta = np.zeros((dim,))

        # Run scipy's fmin algorithm to run gradient descent

        theta_opt_norm = scipy.optimize.fmin_bfgs(self.loss, theta, fprime = self.grad_loss, args=(XX,y),maxiter=num_iters)

        # convert theta back to work with original X
        theta_opt = np.zeros(theta_opt_norm.shape)
        theta_opt[1:] = theta_opt_norm[1:]/sigma
        theta_opt[0] = theta_opt_norm[0] - np.dot(theta_opt_norm[1:],mu/sigma)

        return theta_opt

    def loss(self, *args):
        """
        Compute the logistic loss function

        Inputs:
        - X: N x D array of data; each row is a data point.
        - y: 1-dimensional array of length N with real values.

        Returns: loss as a single float
        """
        theta,X,y = args
        m,dim = X.shape
        J = 0

        ##########################################################################
        # Compute the loss function for unregularized logistic regression        #
        # TODO: 1-2 lines of code expected                                       #
        ##########################################################################
        h = utils.sigmoid(X@theta)
        J = -1.0 / m * (y.T@np.log(h) + (1 - y).T@np.log(1 - h))
        ###########################################################################
        #                           END OF YOUR CODE                              #
        ###########################################################################
        return J

    def grad_loss(self, *args):
        """
        Compute the gradient logistic loss function

        Inputs:
        - X: N x D array of data; each row is a data point.
        - y: 1-dimensional array of length N with real values.

        Returns:  gradient with respect to theta; an array of the same shape as theta
        """
        theta,X,y = args
        m,dim = X.shape
        grad = np.zeros((dim,))

        ##########################################################################
        # Compute the gradient of the loss function for unregularized logistic   #
        # regression                                                             #
        # TODO: 1 line of code expected                                          #
        ##########################################################################
        grad = 1.0 / m * (X.T@(utils.sigmoid(X@theta) - y.T))
        ###########################################################################
        #                           END OF YOUR CODE                              #
        ###########################################################################
        return grad

    def predict(self, X):
        """
        Use the trained weights of this linear classifier to predict probabilities for
        data points.

        Inputs:
        - X: N x D array of training data. Each row is a D-dimensional point.

        Returns:
        - y_pred: Predicted output for the data in X. y_pred is a 1-dimensional
          array of length N, and each element is the probability of being in class 1
        """
        y_pred = np.zeros(X.shape[0])

        ###########################################################################
        # Compute the predicted outputs for X                                     #
        # TODO: 1 line of code expected                                           #
        ###########################################################################
        y_pred = np.round(utils.sigmoid(X@self.theta))
        ###########################################################################
        #                           END OF YOUR CODE                              #
        ###########################################################################
        return y_pred

class RegLogisticRegressor:

    def __init__(self) -> object:
        self.theta = None

    def train(self,X,y,reg=1e-5,num_iters=1000,norm=True):

        """
        Train a linear model using scipy's function minimization.

        Inputs:
        - X: N X D array of training data. Each training point is a D-dimensional
         row.
        - y: 1-dimensional array of length N with values in the reals.
        - reg: (float) regularization strength.
        - num_iters: (integer) number of steps to take when optimizing
        - norm: a boolean which indicates whether the X matrix is standardized before
                solving the optimization problem

        Outputs:
        - optimal value for theta
        """

        num_train,dim = X.shape

        # standardize features if norm=True

        if norm:
            # take out the first column and do the feature normalize
            X_without_1s = X[:,1:]
            X_norm, mu, sigma = utils.feature_normalize(X_without_1s)
            # add the ones back
            XX = np.vstack([np.ones((X_norm.shape[0],)),X_norm.T]).T
        else:
            XX = X

        # initialize theta
        theta = np.zeros((dim,))

        # Run scipy's fmin algorithm to run gradient descent
        theta_opt_norm = scipy.optimize.fmin_bfgs(self.loss, theta, fprime = self.grad_loss, args=(XX,y,reg),maxiter=num_iters)

        if norm:
            # convert theta back to work with original X
            theta_opt = np.zeros(theta_opt_norm.shape)
            theta_opt[1:] = theta_opt_norm[1:]/sigma
            theta_opt[0] = theta_opt_norm[0] - np.dot(theta_opt_norm[1:],mu/sigma)
        else:
            theta_opt = theta_opt_norm

        return theta_opt

    def loss(self, *args):
        """
        Compute the logistic loss function

        Inputs:
        - X: N x D array of data; each row is a data point.
        - y: 1-dimensional array of length N with real values.
        - reg: (float) regularization strength.

        Returns: A tuple containing:
        - loss as a single float
        - gradient with respect to self.theta; an array of the same shape as theta
        """
        theta,X,y,reg = args
        m,dim = X.shape
        J = 0

        ##########################################################################
        # Compute the loss function for regularized logistic regression          #
        # TODO: 1-2 lines of code expected                                       #
        ##########################################################################
        h = utils.sigmoid(X@theta)
        J = 1 / m * (-y.T@np.log(h) - (1 - y.T)@np.log(1 - h)) + reg / (2 * m) * (theta[1:].T@theta[1:])
        ###########################################################################
        #                           END OF YOUR CODE                              #
        ###########################################################################
        return J

    def grad_loss(self, *args):
        """
        Compute the gradient logistic loss function

        Inputs:
        - X: N x D array of data; each row is a data point.
        - y: 1-dimensional array of length N with real values.
        - reg: (float) regularization strength.

        Returns: A tuple containing:
        - loss as a single float
        - gradient with respect to self.theta; an array of the same shape as theta
        """
        theta,X,y,reg = args
        m,dim = X.shape
        grad = np.zeros((dim,))
        ##########################################################################
        # Compute the gradient of the loss function for regularized logistic
        # regression                                                             #
        # TODO: 2 lines of code expected                                          #
        ##########################################################################
        h = utils.sigmoid(X@theta)
        theta[0] = 0
        grad = 1 / m * (X.T@(h - y.T)) + reg / m * theta

        ###########################################################################
        #                           END OF YOUR CODE                              #
        ###########################################################################
        return grad

    def predict(self, X):
        """
        Use the trained weights of this linear classifier to predict probabilities for
        data points.

        Inputs:
        - X: N x D array of training data. Each row is a D-dimensional point.

        Returns:
        - y_pred: Predicted output for the data in X. y_pred is a 1-dimensional
        array of length N, and each element is a real number.
        """
        y_pred = np.zeros(X.shape[0])

        ###########################################################################
        # Compute the predicted outputs for X                                     #
        # TODO: 1 line of code expected                                           #
        #                                                                         #
        ###########################################################################
        y_pred = np.round(utils.sigmoid(X@self.theta))

        ###########################################################################
        #                           END OF YOUR CODE                              #
        ###########################################################################
        return y_pred

if __name__ == '__main__':
    A = np.array([0, 3, 1, 3, 0, 1, 1, 1]).reshape((4, 2))
    #prepend 1
    A = np.c_[np.ones(A.shape[0]), A]
    y = np.array([1, 1, 0, 0]).reshape(1, 4)
    theta = np.array([0, -2, 1]).reshape(3, 1)
    reg = 0.07
    #grad + hess
    h = utils.sigmoid(A @ theta)
    m, _ = A.shape
    test = h - y.T
    grad = 1 / m * A.T@(h - y.T) + reg / m * theta
    I = np.identity(grad.shape[0])
    HESS = 1 / m * (np.dot(A.T, A) * np.diag(h) * np.diag(1 - h)) + reg / m * I
    #Newton's law
    hessianInv = np.linalg.inv(HESS)
    theta1 = theta - np.dot(hessianInv, grad)
    theta2 = theta1 - np.dot(hessianInv, grad)
    print(f'After the first iteration is\n {theta1}')
    print(f'After the first iteration is\n {theta2}')


import numpy as np

def generate_circle_points(radius, initial_angle, final_angle, points=199):
    """
    This methods generates points in a circle shape at (0,0) with a specific radius and from a
    starting angle to a final angle.

    Args:
        radius: radius of the circle in microns
        initial_angle: initial angle of the drawing in degrees
        final_angle: final angle of the drawing in degrees
        points: amount of points to be generated (default 199)

    Returns:
        Set of points that form the circle
    """
    theta = np.linspace(    np.deg2rad(initial_angle),
                            np.deg2rad(final_angle),
                            points)

    return  radius * np.cos(theta) , radius * np.sin(theta)


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

# ## Van der Pohls euqation
# https://en.wikipedia.org/wiki/Van_der_Pol_oscillator

# In[1]:

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

saveFigure = False

def vdp(t, y, la):
    return [y[1], la*(1-y[0]**2)*y[1]-y[0]]

# In[2]:

t_span = (0, 40)

y0 = [2, 0]
la = 1

# Solve for Van der Pohls equation
sol_RK = solve_ivp(lambda t,y: vdp(t,y,la), t_span, y0, method='RK45' )
sol_BDF = solve_ivp(lambda t,y: vdp(t,y,la), t_span, y0, method='BDF' )

# Plot
fig, axs = plt.subplots(4, 1, figsize=(8, 8))
fig.tight_layout(pad=0, w_pad=0, h_pad=4)

ax =axs[0]
ax.plot(sol_RK.t,sol_RK.y[0].T )
ax.plot(sol_RK.t,sol_RK.y[1].T )
ax.autoscale(enable=True, axis='both', tight=True)
ax.set_ylabel(r'$y(t)$')
ax.set_xlabel(r'$t$')
ax.set_title('Solutions of $\lambda=$' + str(la) + ' with Runge-Kutta')

ax =axs[1]
ax.plot(sol_BDF.t,sol_BDF.y[0].T )
ax.plot(sol_BDF.t,sol_BDF.y[1].T )
ax.autoscale(enable=True, axis='both', tight=True)
ax.set_ylabel(r'$y(t)$')
ax.set_xlabel(r'$t$')
ax.set_title('Solutions of $\lambda=$' + str(la) + ' with BDF')

ax =axs[2]
ax.plot(sol_RK.t[0:-1], np.diff(sol_RK.t), )
ax.autoscale(enable=True, axis='both', tight=True)
ax.set_ylabel(r'$\Delta t$')
ax.set_xlabel(r'$t$')
ax.set_title('Step sizes with Runge-Kutta for $\lambda=$' + str(la))

ax =axs[3]
ax.plot(sol_BDF.t[0:-1], np.diff(sol_BDF.t), )
ax.autoscale(enable=True, axis='both', tight=True)
ax.set_ylabel(r'$\Delta t$')
ax.set_xlabel(r'$t$')
ax.set_title('Step sizes with BDF for $\lambda=$' + str(la))

if saveFigure:
    filename = 'vdp_la'+str(la)+'.pdf'
    fig.savefig(filename, format='pdf', dpi=1000, bbox_inches='tight')

# ## Satellites Orbit

# In[10]:

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

def satellite(t, y, k):
    sqt = (y[0]**2+y[1]**2)**(-1.5)
    return [y[2], y[3], -k*y[0]*sqt, -k*y[1]*sqt]
a = 0
b = 10

y0 = [a, b, 0.25, 0.25]
k = 10
t_span = (0, 100)
t_eval = np.linspace(t_span[0], t_span[1], 10000)

# Solve for orbiting equation
sol = solve_ivp(lambda t,y: satellite(t,y,k), t_span, y0,
                method='RK45', t_eval=t_eval)
fig, ax = plt.subplots(1, figsize=(6, 4.5))

ax.plot(sol.y[0].T,sol.y[1].T )
ax.plot(0, 0, 'ro', markersize=10)
ax.plot(a, b, 'k*', markersize=10)

ax.set_ylabel(r'$y$')
ax.set_xlabel(r'$x$')

# ## Stability Region

# ### Explicit

# In[4]:

# Grid
nx, ny = (100, 100)
x = np.linspace(-3, 3, nx)
y = np.linspace(-3, 3, ny)
X, Y = np.meshgrid(x, y)
Z = X + Y*1j

# Euler Forward
EulerF = abs(1+Z)

# Heun's method
Heuns = abs(0.5*((Z+1)**2+1))

# Runge-Kutta 4
RK4 = abs(1+Z+0.5*Z**2+Z**3/6+Z**4/24)

# Plot
fig, ax = plt.subplots(1, figsize=(6, 6))
ax.contour(X, Y, EulerF, levels = [1], colors='C0')
ax.contour(X, Y, Heuns, levels = [1], colors='C1')
ax.contour(X, Y, RK4, levels = [1], colors='C2')

ax.set_xlim([-3, 3])
ax.set_ylim([-3, 3])
ax.grid(True)
ax.axhline(y=0, color='k')
ax.axvline(x=0, color='k')
ax.set_xlabel(r'$\Re(h\lambda)$')
ax.set_ylabel(r'$\Im(h\lambda)$')

proxy = [plt.Rectangle((0,0),1,1,fc ='C'+str(pc))  for pc in range(3)]
plt.legend(proxy, ['Euler Forward', 'Heuns Method', 'Classical Runge-Kutta 4'], loc='center right')

if saveFigure:
    filename = 'Explicit.pdf'
    fig.savefig(filename, format='pdf', dpi=1000, bbox_inches='tight')

# ### Implicit

# In[5]:

# Euler Backward
EulerBack = abs(1/(1-Z))

# Trapzoidal method
Trap = abs((2+Z)/(2-Z))

# Plot
fig, ax = plt.subplots(1, figsize=(6, 6))
ax.contour(X, Y, EulerBack, levels = [1], colors='C0')
ax.contour(X, Y, Trap, levels = [1], colors='C1')

ax.set_xlim([-3, 3])
ax.set_ylim([-3, 3])
ax.grid(True)
ax.axhline(y=0, color='k')
# ax.axvline(x=0, color='k')
ax.set_xlabel(r'$\Re(h\lambda)$')
ax.set_ylabel(r'$\Im(h\lambda)$')

proxy = [plt.Rectangle((0,0),1,1,fc ='C'+str(pc))  for pc in range(3)]
plt.legend(proxy, ['Euler Backward', 'Trapzoidal Method'], loc='upper right')

if saveFigure:
    filename = 'Implicit.pdf'
    fig.savefig(filename, format='pdf', dpi=1000, bbox_inches='tight')

# ### BDF methods

# In[6]:

n = 500
z = np.linspace(0, n-1, n)
z = z * 2 *np.pi/n

z = np.exp(z*1j)

# BDF methods
d = 1 - 1.0/z
r = 0
fig, ax = plt.subplots(1, figsize=(6, 6))
# Bdf 1 to 6
for i in range(6):
    r = r + (d**(i+1))/(i+1)
    ax.plot(r.real, r.imag, label='BDF-'+str(i+1))

ax.grid(True)
ax.axhline(y=0, color='k')
ax.axvline(x=0, color='k')
ax.set_xlabel(r'$\Re(h\lambda)$')
ax.set_ylabel(r'$\Im(h\lambda)$')
plt.legend()

if saveFigure:
    filename = 'BDF.pdf'
    fig.savefig(filename, format='pdf', dpi=1000, bbox_inches='tight')

# Zoom in
ax.set_xlim([-6, 6])
ax.set_ylim([-6, 6])

if saveFigure:
    filename = 'BDF_zoom.pdf'
    fig.savefig(filename, format='pdf', dpi=1000, bbox_inches='tight')

# ## $\theta$-method

# In[7]:

#
nt = 10
ths = np.linspace(0, 1, nt)

# Plot
fig, ax = plt.subplots(1, figsize=(6, 6))
for i in range(nt):
    theta = abs((1-(1-ths[i])*Z)/(1+ths[i]*Z))
    ax.contour(X, Y, theta, levels = [1], colors='C'+str(i))

ax.set_xlim([-3, 3])
ax.set_ylim([-3, 3])
ax.grid(True)
ax.axhline(y=0, color='k')
# ax.axvline(x=0, color='k')
ax.set_xlabel(r'$\Re(h\lambda)$')
ax.set_ylabel(r'$\Im(h\lambda)$')

proxy = [plt.Rectangle((0,0),1,1,fc ='C'+str(pc))  for pc in range(3)]
plt.legend(proxy, ['theta'], loc='upper right')

# if saveFigure:
#     filename = 'Implicit.pdf'
#     fig.savefig(filename, format='pdf', dpi=1000, bbox_inches='tight')

# ## Reaction problem

# In[8]:

def reaction(t, y, s, q, w):
    return [s*(y[1]-y[0]*y[1]+y[0]-q*y[0]*y[0]),
           1.0/s*(-y[1]-y[0]*y[1]+y[2]),
           w*(y[0]-y[2])]

# In[9]:

# Time interval for the simulation
t0 = 0
t1 = 10
t_span = (t0, t1)
t_eval = np.linspace(t_span[0], t_span[1], 100)

# Initial conditions, e.g. number of predator and prey at time zero
alpha0 = 30
beta0 = 1
gamma0 = 30
y0 = [alpha0, beta0, gamma0]

# Set parameters
s = 77.27;
w = 0.161;
q = 8.375e-6;

# Solve for Van der Pohls equation
sol_BDF = solve_ivp(lambda t,y: reaction(t, y, s, q, w),
                t_span, y0, method='BDF')

sol_RK = solve_ivp(lambda t,y: reaction(t, y, s, q, w),
                t_span, y0, method='RK45')

fig, ax = plt.subplots(2, 1, figsize=(6, 6))
ax[0].plot(sol_BDF.t, sol_BDF.y.T)
ax[1].plot(sol_BDF.t[0:-1], np.diff(sol_BDF.t))

fig, ax = plt.subplots(2, 1, figsize=(6, 6))
ax[0].plot(sol_RK.t, sol_RK.y.T)
ax[1].plot(sol_RK.t[0:-1], np.diff(sol_RK.t))


import numpy as np

def cart2pol(x, y):
    rho = np.sqrt(x ** 2 + y ** 2)
    phi = np.arctan2(y, x)

    return rho, phi

def pol2cart(rho, phi):
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)

    return x, y


import numpy as np

# Activation
def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))

def sigmoid_derivatif(x):
    return sigmoid(x) * (1.0 - sigmoid(x))

def tanh(x):
    return np.tanh(x)

def tanh_derivatif(x):
    return 1.0 - np.square(np.tanh(x))

def relu(x):
    return x * (x > 0)

def relu_derivatif(x):
    return 1 * (x > 0)

# # Loss
# def logloss(y, a):
#     return -(y * np.log(a) + (1.0 - y) * np.log(1.0 - a))

# def logloss_derivatif(y, a):
#     return (a - y) / (a * (1 - a))

map_activation = {
    'sigmoid': (sigmoid, sigmoid_derivatif),
    'tanh': (tanh, tanh_derivatif),
    'relu': (relu, relu_derivatif)
}


###############################################################################
#####                      CONJUGATE GRADIENT CLASS                       #####
###############################################################################

import numpy as np
import math
from scipy.sparse import csr_matrix as csr

class cg_serial ():
    def __init__(self, A , b):

        self.n = b.size
        self.b = b
        self.A = A

        self.converged = False
        self.numIter   = 0
        self.sol       = None
        self.residual  = -1
        return

    def solve(self,x0,tol=1.e-10):
        print('  > STARTING CONVERGENCE LOOP')
        # Initialisation
        k = 0
        xk = np.array(x0)
        rk = self.b - self.A @ xk
        dk = rk

        # Main loop
        while (np.linalg.norm(rk) > tol and k < 100.0*self.n):
            if (k % 1000 == 0) : print('    > Iteration k = ', k, ' , Residual = ', np.linalg.norm(rk))
            Adk = self.A @ dk

            # Alpha_k and x_{k+1}
            alpha = (dk.transpose() @ rk) / (dk.transpose() @ Adk)
            xk = xk + alpha * dk

            # r_{k+1}, we save r_k
            rkm1 = rk
            rk = rk - alpha * Adk

            # Beta_k and d_{k+1}
            beta = (rk.transpose() @ rk) / (rkm1.transpose() @ rkm1)
            dk = rk + beta * dk

            k = k+1
        self.sol = xk
        self.numIter = k
        self.residual  = np.linalg.norm(rk)
        self.converged = (self.residual <= tol)


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from scipy import signal
from matplotlib.ticker import LinearLocator, FormatStrFormatter

class GaussianStatistics():
    """
    """
    def MultivariateGaussian(self, mu, sd, N = 10, A = 1):
        """
            mu: location of peak value
                array
                    ex: 2D = [5., 5.]
            sd: spread of gaussian
                covariance cross-covariance matrix
                    ex: 2D = [[20., -1.],
                               [-1., 20]]
            N: spread radius
                any real number -> scalar field
            A: Amplitude of gaussian
                any real number -> scalar field
        """

        X = np.linspace(0, N, N)
        Y = np.linspace(0, N, N)
        X, Y = np.meshgrid(X, Y)

        pos = np.empty(X.shape + (2,))
        pos[:, :, 0] = X
        pos[:, :, 1] = Y

        n = mu.shape[0]
        sd_det = np.linalg.det(sd)
        sd_inv = np.linalg.inv(sd)
        N = np.sqrt((2*np.pi)**n * sd_det)

        fac = np.einsum('...k,kl,...l->...', pos-mu, sd_inv, pos-mu)

        return A * np.exp(-fac / 2)

    def DOG(self, iRad, eRad, iA, eA):
        """
        """
        eSd = np.array([[eRad**2, -1.],
                        [-1., eRad**2]])
        iSd = np.array([[(iRad)**2, -1.],
                        [-1., (iRad)**2]])
        mu  = np.array([iRad, iRad])
        self.iRad = iRad
        self.eRad = eRad
        self.pos = self.MultivariateGaussian(mu, eSd, N = 2*iRad, A = eA)
        self.neg = self.MultivariateGaussian(mu, iSd, N = 2*iRad, A = iA)
        return self.pos, self.neg

    def Visualize(self, Gaussian, _type = '2d'):
        """
        """
        X = np.arange(0, Gaussian.shape[0])
        Y = np.arange(0, Gaussian.shape[1])
        X, Y = np.meshgrid(X, Y)

        if _type == '3d':
            fig = plt.figure()
            ax = fig.gca(projection='3d')
            surf = ax.plot_surface(X, Y, Gaussian, cmap=cm.coolwarm,
                           linewidth=0, antialiased=False)
            # Customize the z axis.
            ax.set_zlim(-1.01, 1.01)
            ax.zaxis.set_major_locator(LinearLocator(10))
            ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
            fig.colorbar(surf, shrink=0.5, aspect=5)
            plt.show()
        else:
            plt.imshow(Gaussian)
            plt.show()

    def OrientationBar(self, N = 10,
                    mu=np.array([5., 5.]),
                    Sigma=np.array([[20., -19.8], [-19.8, 20]]),
                    theta=0, display=False):
        """
        """
        if (theta > 90.): theta = theta - 180.
        theta = theta*np.pi/180.

        # update Sigma based om theta
        Sigma = np.array([[ Sigma[0,0]*(np.pi*0.5-theta)**2 +1.,
                            Sigma[0,1]*theta*(np.pi*0.5-theta) + 1.],
                          [ Sigma[1,0]*theta*(np.pi*0.5-theta) + 1.,
                            Sigma[1,1]*(theta)**2 +1.]])

        _bar = self.MultivariateGaussian(mu, Sigma, N)
        if display: self.Visualize(_bar)
        return _bar


import mpmath as mp
import warnings

def zeta_function(s,a = 1,derivative = 0):
    """
    Returns the value for hurwitz-zeta function at s

    Parameters
    ----------
    s : int, float, complex
        denotes the value for which zeta function needs to be calculated
    a : int, float, complex, tuple
        denotes constant a in hurwitz zeta function
        default value is 1
        can be rational also like p/q, input must be a tuple (p,q) for this case, also p and q must be integers
    derivative : int
        denotes the n'th derivative
        default value is 0
    return : float, complex
        returns tha value of zeta function or its derivative

    """
    return mp.zeta(s,a,derivative)

def altzeta_function(s):
    """
    Returns tha value for dirichlet eta function (also alternating zeta function) at s.

    Parameters
    ----------
    s : int, float, complex
        denotes the value for which alternating zeta function needs to be calculated
    return : float, complex
        returns the value of alternating zeta function

    """
    return mp.altzeta(s)

def gamma_function(s):
    """
    Returns tha value for gamma function at s

    Parameters
    ----------
    s : int, float, complex
        denotes the value for which gamma function needs to be calculated
    return : float, complex
        returns the value for gamma function

    """
    return mp.gamma(s)

def bernoulli_num(n):
    """
    Returns n'th bernoulli number

    Parameters
    ----------
    n : int
        positive integer value
    return : float
        returns bernoulli number

    """
    return mp.bernoulli(n)


import numpy as np
import pylab as pl

from numpy import pi, sin, cos

# ***************************************************
#
#                       TEST 102
#
# ***************************************************
eps = 1.e-1
m = 2 ; n = 1

f = lambda y :  sin( n*pi * ( 4*y*(1-y) ) )
df = lambda y : (-4*pi*n*y + 4*pi*n*(-y + 1))*cos(4*pi*n*y*(-y + 1))

u = lambda x,y : eps*sin(n*pi*y)*cos(m*pi*x) + f(y)

t = np.linspace(0.,1.,200)
x,y = np.meshgrid(t,t)

# ...
# plot of f
# ...
pl.plot(t, f(t))
pl.title("plot of $f(y) = \sin( n\pi ( 4 y (1-y) ) )$ ")
pl.show()
# ...

# ...
# plot of df
# ...
pl.plot(t, df(t))
pl.title("plot of $df(y) = -4 \pi n y + 4 \pi n (-y + 1)) \cos(4 \pi n y (-y + 1) $ ")
pl.show()
# ...

# ...
# plot of u
# ...
pl.contourf(x,y,u(x,y))
pl.colorbar()
pl.title("plot of $u (x,y) = \epsilon \sin(n \pi y) \cos(m \pi x) + \sin( n \pi ( 4 y (1-y) ) )$ ")
pl.show()
# ...

# ...
# plot of u - limit
# ...
eps = 0.
pl.contourf(x,y,u(x,y))
pl.colorbar()
pl.title("plot of $u (x,y) =  \sin( n \pi ( 4 y (1-y) ) )$ ")
pl.show()
# ...

# ***************************************************

# ***************************************************
#
#                       TEST 101
#
# ***************************************************
eps = 1.e-1
m = 2 ; n = 1

u = lambda x,y : sin(n*pi*y) + eps * cos(m*pi*x) * sin(n*pi*y)

t = np.linspace(0.,1.,201)
x,y = np.meshgrid(t,t)

# ...
# plot of u
# ...
pl.contourf(x,y,u(x,y))
pl.colorbar()
pl.title("plot of $u (x,y) = \sin(n \pi y) ( 1 + \epsilon \cos(m \pi x) )$ ")
pl.show()
# ...

# ...
# plot of u limit
# ...
eps = 0.
pl.contourf(x,y,u(x,y))
pl.colorbar()
pl.title("plot of $u (x,y) = \sin(n \pi y) $ ")
pl.show()
# ...

# ***************************************************

# ***************************************************
#
#                       TEST 110
#
# ***************************************************
eps = 1.e-4
alpha = 2.0
m = 2
n = 1

u = lambda x,y : eps*sin(n*pi*y)*cos(m*pi*x) + sin(alpha*(y**2 - y)*cos(m*pi*x) + n*pi*y)

t = np.linspace(0.,1.,201)
x,y = np.meshgrid(t,t)

# ...
# plot of u
# ...
pl.contourf(x,y,u(x,y))
pl.colorbar()
pl.title("plot of $u (x,y) = \epsilon \sin(n \pi y) \cos(m \pi x) + \sin( \\alpha (y^2 - y) \cos(m \pi x) + n \pi y)$ ")
pl.show()
# ...

# ...
# plot of u - limit
# ...
eps = 0.
pl.contourf(x,y,u(x,y))
pl.colorbar()
pl.title("plot of $u (x,y) = \sin( \\alpha (y^2 - y) \cos(m \pi x) + n \pi y)$ ")
pl.show()
# ...

# ***************************************************


# -*- coding: utf-8 -*-
"""A variants of Discounted Cumulative Gain.
"""
from typing import Optional, Sequence
import numpy as np
from .types import Scalar

def _calc_numer(rels):
    """Calculate numerator of DCG

    params
    ------
    rels :
        np.ndarray, shape = (n_entities, )

    return
    ------
    numer :
        np.ndarray, The numerator of DCG
    """
    return np.array([2.0 ** rel - 1 for rel in rels])

def _calc_denom(n_entities):
    """Calculate denominator of DCG

    params
    ------
    n_entities :
        int, # of entities.

    return
    ------
    denom :
        np.ndarray, The denominator of DCG
    """
    return np.log2(np.arange(2, n_entities + 2))

def DCG(rels):
    """Calculate DCG based on given relevances of entities.

    params
    ------
    rels :
        array-like, shape = (n_entities, )
        i-th element represents the relevance score of i-th item.

    return
    ------
    dcg :
        float, The value of DCG.
    """
    numer = _calc_numer(rels)
    denom = _calc_denom(len(rels))
    return (numer / denom).sum()

def NDCG(rels: Sequence[Scalar], topk: Optional[int] = None) -> Scalar:
    """Compute NDCG based on given relevances of entities.

    Parameters:
        rels: array-like, shape (n, )
              i-th element represents the relevance score of i-th item.
        topk: int, k for NDCG@`k`. defaults to None.
              all of the relevance scores will be considerd if k is None.

    Retuls:
        ndcg: NDCG score
    """
    obs_rels: Sequence[Scalar]
    opt_rels: Sequence[Scalar]
    if topk is None:
        obs_rels = rels
        opt_rels = np.sort(rels)[::-1]
    else:
        obs_rels = rels[:topk]
        opt_rels = np.sort(rels)[::-1][:topk]
    dcg_obs = DCG(obs_rels)
    dcg_opt = DCG(opt_rels)
    return dcg_obs / dcg_opt


#!/usr/bin/env python3

"""
This script should show some of the power
of numpy arrays, and demonstrate their usage.

See the numpy documentation for more information
http://docs.scipy.org/doc/numpy/
start with the User Guide's quickstart
"""

import numpy as np

### The numpy array

# Numpy arrays are defined with an initial size, which cannot
# change without redefining the array. Furthermore, all elements
# of an array are of the same type, and must be a number. By
# default, this uses a float (think of a  float as a round rubber tube,
# j.k. its actually how computers store real numbers instead of integers)
# We can start by building an array with each element equal to 1
# it will have 2 dimensions, the first of which is 3 and the second
# of which is 4. Typically, this is thought of as 3 rows with 4 elements
# per row (4 columns), similiar to matrices.
a = np.ones((3,4))
print(a)

# Let us define an array b, and fill it with a multiplication table
b = np.zeros((3,4))
print(b)
# every array has a shape attribute, which stores its shape as tuple
print(b.shape)
for i in range(b.shape[0]):
    for j in range(b.shape[1]):
        # We can access the i row, j column entry using
        # either b[i][j], or b[i,j]
        b[i,j] = i*j
print(b)

# We can perform numerical operations on array, and they happen element-wise.
# So squaring b is equivalent to squaring each element of b
print('a')
print(a)
print('b')
print(b)
print('b**2')
print(b**2)
print('a + b')
print(a+b)
print('a*b')
print(a*b)
print('3*b - 2')
print(3*b - 2)

# Numpy has functions that will work element-wise on arrays
print('sqrt(b)')
print(np.sqrt(b))
print('arctan(b) in degrees')
print(np.rad2deg(np.arctan(b)))

# arithmetic does NOT work on arrays of different shapes if they both have
# the same dimension

# Turn a on its side
a = a.transpose()
print('a transpose')
print(a)

print('a + b')
try:
    print(a + b)
except Exception as e:
    print(e.args)

# But consider the following 0-dimension array
c = np.zeros((3,))
for i in range(c.shape[0]):
    c[i] = i+1
d = np.zeros((4,))
for i in range(d.shape[0]):
    d[i] = i+5

print('c')
print(c)
print('d')
print(d)

# Numpy will try to do a sensible 'broadcasting' to make arithemetic possible
print('b + c')
try:
    print(b + c)
except Exception as e:
    print(e.args)
print('b + d')
print(b + d)

# BEWARE THE NUMPY BROADCAST. it can be helpful, but also leads to weird bugs that are hard to track down

# We can also tranform numpy arrays to lists, and vice versa

# array to list. This transform preserves the [i][j] indexing
b_list = b.tolist()
print('list b')
print(b_list)

# list to array
c = np.array(b_list)
print('c array = np.array(b_list)')
print(c)

# BEWARE THE LIST/ARRAY CONFUSION.

# Note the difference between the array times a number and list times a number.
# It is easy to get this mixed up if you transform between these often.
print('b*3')
print(b*3)
print('np.array((list b)*3)')
print(np.array(b_list*3))

# You cannot add a non-list to a list, but you can for an array
print('b + 1')
print(b + 1)
print('(list b) + 1')
try:
    print(b_list + 1)
except Exception as e:
    print(e.args)


from sympy import symbols, integrate, Rational, lambdify, sqrt, solve
import matplotlib.pyplot as plt
import numpy as np

def plot_fun( fun, name, col ):
	x_vals = np.linspace( g_xlim[0], g_xlim[1], 1000, endpoint=True )
	y_vals = fun( x_vals )
	plt.plot( x_vals, y_vals, label = name, color = col )

g_xlim = [ 1, 50 ]
g_ylim = [ -5, 15 ]

q = symbols( 'q', positive = True )

# The supply function for oil is given (in dollars) by
S = q**2 + 11*q

# and the demand function is given (in dollars) by:
D = 1054 - 17*q - q**2

# Find the point at which supply and demand are in equilibrium.
E = S - D

equilibrium_x = solve( E, q )[ 0 ]
equilibrium_y = S.subs( { q: equilibrium_x } )

# Find the consumers' surplus.

CS = D - equilibrium_y
consumer_surplus = integrate( CS, ( q, 0, equilibrium_x ) ).evalf()
round( consumer_surplus, 2 )

# Find the consumers' surplus.

SS = equilibrium_y - S
suplier_surplus = integrate( SS, ( q, 0, equilibrium_x ) ).evalf()
round( suplier_surplus, 2 )

# Graph
lam_s = lambdify( q, S, np )
plot_fun( lam_s, 'Supply', 'B' )

lam_d = lambdify( q, D, np )
plot_fun( lam_d, 'Demand', 'R' )

plt.legend()
plt.show()


from sympy.ntheory import factorint
import warnings
import numpy as np

def is_optimal_for_cuFFT(n: int, allowed_factors) -> bool:
    factorization = factorint(n)
    if len(factorization) == 0: # factorint(1) returns empyt dict
        return False
    factors = set(factorization.keys())
    return factors.issubset(set(allowed_factors))

def _closest_optimal(n: int, search_next_largest: bool, allowed_factors) -> int:
    while(not is_optimal_for_cuFFT(n, allowed_factors) and n>=1):
        if search_next_largest:
            n += 1
        else:
            n -= 1
    # edge case: decreasing search with start value smaller than allowed factor
    if n < min(allowed_factors):

        warnings.warn(f"{n}One provided dimension is smaller than smallest allowed factor and search direction is decreasing")
        return(min(allowed_factors))
    return n

def closest_optimal(n, search_next_largest: bool=True, allowed_factors=(2,3,5,7)):
    """ Finds closest optimal array dimensions for cuFFT

    Parameters
    ----------
    n : iterable of integers
        Input dimensions
    search_next_largest : bool
        if True (default) search closest optimal dimensions that are larger or equal to original
        otherwise look for smaller ones.
    allowed_factor: tuple of integers
        allowed factors in decomposition. Defaults to (2,3,5,7) which are the factors listed in
        the cuFFT documentation.

    Returns
    -------
    np.array of ints
        optimal dimensions for cuFFT

    See also
    --------
    https://docs.nvidia.com/cuda/cufft/index.html

    """
    n = np.asarray(n)
    scalar_input = False
    if n.ndim == 0:
        n = n[None]
        scalar_input = True
    ret = np.array([_closest_optimal(ni, search_next_largest, allowed_factors) for ni in n])
    if scalar_input:
        return ret[0]
    return ret


# -*- coding: utf-8 -*-
"""
Created on Wed May 23 14:39:09 2018

@author: gawe
"""

import numpy as _np
import numpy.linalg as _la
import matplotlib.pyplot as _plt

def cov(data):
    """
    Covariance matrix
    note: specifically for mean-centered data
    note: numpy's `cov` uses N-1 as normalization
    """
    return _np.dot(data.T, data) / data.shape[0]

def basic_pca(data, pc_count = None):
    """

   Principal component analysis using eigenvalues
    note: this mean-centers and auto-scales the data (in-place)
    """
    # data = data.copy()
    # data = data/_np.mean(data, 0)   # normalize the data

    data -= _np.mean(data, 0)
    data /= _np.std(data, 0)
    # C = _np.cov(data.T)   # numpy's covaraince calculation
    C = cov(data)       # simple one
    E, V = _la.eigh(C)
    key = _np.argsort(E)[::-1][:pc_count]
    E, V = E[key], V[:, key]
    # U = _np.dot(data, V)  # numpy's cov ....used to be _np.dot(V.T, data.T).T
    U = _np.dot(data, V)  # simple cov .... two PC's?
    return U, E, V

def test(data=None):
    if data is None:
        data = test_data()
    # end if

    """ visualize """
    trans = basic_pca(data, 3)[0]
    fig, (ax1, ax2) = _plt.subplots(1, 2)

    ax1.scatter(data[:50, 0], data[:50, 1], c = 'r')
    ax2.scatter(trans[:50, 0], trans[:50, 1], c = 'r')

    ax1.scatter(data[50:, 0], data[50:, 1], c = 'b')
    ax2.scatter(trans[50:, 0], trans[50:, 1], c = 'b')
    _plt.show()
# end def test()

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

def PCA(data, dims_rescaled_data=2):
    """
    returns: data transformed in 2 dims/columns + regenerated original data
    pass in: data as 2D NumPy array
    """
    m, n = data.shape
    # mean center the data
    data -= data.mean(axis=0)
    # calculate the covariance matrix
    R = _np.cov(data, rowvar=False)
    # calculate eigenvectors & eigenvalues of the covariance matrix
    # use 'eigh' rather than 'eig' since R is symmetric,
    # the performance gain is substantial
    evals, evecs = _la.eigh(R)
    # sort eigenvalue in decreasing order
    idx = _np.argsort(evals)[::-1]
    evecs = evecs[:,idx]
    # sort eigenvectors according to same index
    evals = evals[idx]
    # select the first n eigenvectors (n is desired dimension
    # of rescaled data array, or dims_rescaled_data)
    evecs = evecs[:, :dims_rescaled_data]
    # carry out the transformation on the data using eigenvectors
    # and return the re-scaled data, eigenvalues, and eigenvectors
    return _np.dot(evecs.T, data.T).T, evals, evecs

def test_PCA(data=None, dims_rescaled_data=2):
    '''
    test by attempting to recover original data array from
    the eigenvectors of its covariance matrix & comparing that
    'recovered' array with the original data
    '''
    if data is None:
        data = test_data()
    # end if

    m, n = data.shape
    _ , _ , eigenvectors = PCA(data.copy(), dims_rescaled_data=dims_rescaled_data)
    # data_recovered = _np.dot(eigenvectors, m).T
    # data_recovered += data_recovered.mean(axis=0)
    # data_recovered = _np.dot(eigenvectors, _np.ones((n,m))).T
    data_recovered = _np.dot(eigenvectors.T, data.T).T
    # data_recovered += data.mean(axis=0)
    # data_recovered += data_recovered.mean(axis=0)

    _plt.figure()
    _plt.plot(data, '-')
    _plt.plot(data_recovered, '.')
    # assert _np.allclose(data, data_recovered)

    plot_pca(data)
# end def

def plot_pca(data, pcindices = [0,1]):
    clr1 =  '#2026B2'
    fig = _plt.figure()
    ax1 = fig.add_subplot(111)
    data_resc, eigenval, eivenvec = PCA(data.copy())

    # _plt.figure()
    # ax1.plot(data_resc[:, 0], data_resc[:, 1], '.', mfc=clr1, mec=clr1)

    _plt.figure()
    _ax1 = _plt.subplot(3, 1, 1)
    _ax1.plot(data, '-', data_resc, '.')

    _ax2 = _plt.subplot(3, 1, 2)
    _ax2.plot(1+_np.arange(0, len(eigenval)), eigenval, 's-')
    _ax2.set_ylabel('eigval')

    _ax3 = _plt.subplot(3, 1, 3)
    _ax3.plot(data_resc[:,pcindices[0]], data_resc[:,pcindices[1]], '.', mfc=clr1, mec=clr1)
    _ax3.set_xlabel('PC%i'%(pcindices[0],))
    _ax3.set_ylabel('PC%i'%(pcindices[1],))
    _plt.show()

def test_data():
    """ test data """
    data = _np.array([_np.random.randn(8) for k in range(150)])
    data[:50, 2:4] += 5
    data[50:, 2:5] += 5
    return data
# end def

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

if __name__=="__main__":

    test()
    test_PCA()
# end if