from typing import Callable, Tuple, Union

import numpy as np

from .utils import isType

def exponential(x: Union[int, float], n: int) -> float:
    """
    Computes an approximation of e^x

    :param x: x
    :param k: k

    :return: the approximated value
    """

    acc = 0

    try:
        if isType(x, [int, float]) and isType(n, int) and n >= 0:
            for i in range(0, n):
                acc += pow(x, i) / np.math.factorial(i)

            return acc
        else:
            return np.nan
    except OverflowError:
        return acc

def coskx1(k: int, x: Union[int, float]) -> float:
    """
    Computes an approximation of cos(kx) using the formula:

    .. math::
        cos((m+1)x) = 2cos(x) * cos(mx) - cos((m-1)x)

    :param x: x
    :param k: k

    :return: the approximated value
    """

    try:
        if isType(k, int) and k >= 0 and isType(x, [int, float]):
            m1 = k - 1
            m2 = m1 - 1

            if m1 >= 1:
                if m1 == 1:
                    e1 = np.math.cos(x)
                else:
                    e1 = coskx1(m1, x)
            else:
                e1 = 1

            if m2 >= 1:
                if m2 == 1:
                    e2 = np.math.cos(x)
                else:
                    e2 = coskx1(m2, x)
            else:
                e2 = 1

            if k == 0:
                return np.math.cos(0)
            elif k == 1:
                return np.math.cos(x)
            else:
                return 2 * np.math.cos(x) * e1 - e2
        else:
            return np.nan
    except:
        return np.nan

def cossinkx2(k: int, x: Union[int, float]) -> Tuple[float, float]:
    """
    Computes an approximation of cos(kx) & sin(kx) using the formulas:

    .. math::
        cos(mx) = cosx \\cdot cos(m-1)x - sinx \\cdot sin(m-1)x
    .. math::
        sin(mx) = sinx \\cdot cos(m-1)x + cosx \\cdot sin(m-1)x

    :param x: x
    :param k: k

    :return: approximated cos(kx) and sin(kx) values
    """

    try:
        if isType(k, int) and k >= 0 and isType(x, [int, float]):
            if k == 0:
                return (np.math.cos(0), np.math.sin(0))
            elif k == 1:
                return (np.math.cos(x), np.math.sin(x))
            else:
                (cosKLess1, sinKLess1) = cossinkx2((k - 1), x)
                return (np.math.cos(x) * cosKLess1 - np.math.sin(x) * sinKLess1, np.math.sin(x) * cosKLess1 + np.math.cos(x) * sinKLess1)
        else:
            return np.nan, np.nan
    except:
        return np.nan, np.nan

def pi(n: int) -> float:
    """Computes an approximate value of Pi, using the formula:

    .. math::
        \\sum_{n=1}^{\\infty} \\cfrac{1}{n^2} = \\cfrac{1}{6} \\pi^2

    :param n: the amount of sum elements

    :return: the approximated value of pi
    """

    if isType(n, int) and n > 0:
        acc = 0

        for i in range(1, n + 1):
            acc += 1 / pow(i, 2)

        return np.math.sqrt(acc * 6)
    else:
        return np.nan

def approxZeroBisection(a: Union[int, float], b: Union[int, float], f: Callable[[float], float], epsilon: float, iteration: int) -> Tuple[float, int]:
    """
    Approximates the solution to f(x) = 0 in range [a, b] using bisection method

    :param a: left range bound
    :param b: right range bound
    :param f: callable function of variable x
    :param epsilon: the desired precision (stop condition)
    :param iteration: maximum iterations count limit (to prevent an infinite loop)

    :raises ValueError: when input types are wrong or f has same signs on both range bounds

    :return: a tuple containing (in order): an approximate solution x, iterations made count
    """

    if not isType(a, [int, float]) or not isType(b, [int, float]) or not isType(f, Callable) or not isType(epsilon, float) or not isType(iteration, int) or epsilon < 0 or iteration < 0 or b < a:
        raise ValueError("Wrong input types")

    if np.sign(f(a)) * np.sign(f(b)) >= 0:
        raise ValueError(
            "Function f has to be of different signs at the edges of the range [a, b]")

    i = 0
    c = (a + b) / 2
    while np.abs(f(c)) > epsilon and i < iteration:
        c = (a + b) / 2

        if np.sign(f(a)) != np.sign(f(c)):
            b = c
        else:
            a = c

        i += 1

    return c, i - 1

def approxZeroSecant(a: float, b: float, f: Callable[[float], float], epsilon: float, iteration: int) -> Tuple[float, int]:
    """
    Approximates the solution to f(x) = 0 in range [a, b] using secant method

    :param a: left range bound
    :param b: right range bound
    :param f: callable function of variable x
    :param epsilon: the desired precision (stop condition)
    :param iteration: maximum iterations count limit (to prevent an infinite loop)

    :raises ValueError: when input types are wrong or f has same signs on both range bounds

    :return: a tuple containing (in order): an approximate solution x, iterations made count
    """

    if not isType(a, [int, float]) or not isType(b, [int, float]) or not isType(f, Callable) or not isType(epsilon, float) or not isType(iteration, int) or epsilon < 0 or iteration < 0 or b < a or np.sign(f(a)) * np.sign(f(b)) >= 0:
        raise ValueError("Wrong input types")

    if np.sign(f(a)) * np.sign(f(b)) >= 0:
        raise ValueError(
            "Function f has to be of different signs at the edges of the range [a, b]")

    f_a = f(a)
    f_b = f(b)
    x0 = 0
    i = 0
    while iteration > -1 and (abs(a - b) > epsilon):
        i += 1

        if abs(f_a - f_b) < epsilon:
            break

        x0 = a - f(a) * (a - b) / (f(a) - f(b))
        f0 = f(x0)

        if abs(f0) < epsilon:
            break

        if f(a) * f0 < 0:
            b = x0

        elif f(b) * f0:
            a = x0

        elif f0 == 0:
            return x0, iteration

        iteration -= 1

    return x0, i - 1

def approxZeroNewton(f: Callable[[float], float], df: Callable[[float], float], ddf: Callable[[float], float], a: Union[int, float], b: Union[int, float], epsilon: float, iteration: int) -> Tuple[float, int]:
    """
    Approximates the solution to f(x) = 0 in range [a, b] using Newton's method

    :param f: callable function of variable x
    :param df: callable derivate of f (f')
    :param ddf: callable second-order derivate of f (f'')
    :param a: left range bound
    :param b: right range bound
    :param epsilon: the desired precision (stop condition)
    :param iteration: maximum iterations count limit (to prevent an infinite loop)

    :raises ValueError: when input types are wrong or either df or dff has same different on both range bounds

    :return: a tuple containing (in order): an approximate solution x, iterations made count
    """

    if not isType(a, [int, float]) or not isType(b, [int, float]) or not isType(f, Callable) or not isType(df, typing.Callable) \
            or not isType(ddf, Callable) or not isType(epsilon, float) or not isType(iteration, int) or epsilon < 0 or iteration < 0 or b < a or np.sign(f(a)) * np.sign(f(b)) >= 0:
        raise ValueError("Wrong input types")

    if np.sign(df(a)) * np.sign(df(b)) <= 0:
        raise ValueError(
            "Df has to be of different signs at the edges of range [a, b]")

    if np.sign(ddf(a)) * np.sign(ddf(b)) <= 0:
        raise ValueError(
            "Ddf has to be of different signs at the edges of range [a, b]")

    x0 = a
    for x0 in [a, b]:
        if(f(x0) * ddf(x0) > 0):
            # initial condition found
            break

    i = 0
    while np.abs(f(x0)) > epsilon and i < iteration:
        x0 = x0 - f(x0) / df(x0)

        i += 1

    return x0, i


"""
Created on Thu Apr  1 01:12:40 2021
"""

import numpy as np

def cartesian2spherical(rectangular_coordinates):
    """
    Converts Cartesian coordinates to spherical coordinates.

    Parameters
    ----------
    rectangular_coordinates:
        A list of coordinates of the point in Cartesian space.

    Returns
    -------
    list:
        Spherical coordinates of the input Cartesian coordinates.
    """

    # initialize the coordinates
    x, y, z = rectangular_coordinates

    # calculate rho
    rho = np.sqrt(x**2 + y**2 + z**2)

    # calculate theta, the reason for using arctan2 is that the range of theta
    # parameter is -180 < theta < 180.
    theta = np.arctan2(y, x)

    # calculate phi
    phi = np.arccos(z / rho)

    return rho, theta, phi


"""Algorithms ranking nodes in a graph."""

import numpy as np
import pandas as pd

def hubs_and_authorities(matrix, limit=25, disp_iterations=False):
    """Hubs and authorities algorithm.
    Also known as Hyperlink-Induced Topic Search.

    Gives each node two scores:
    Hub score is based on how many other nodes the node links to. It is
    calculated as a sum of authority scores of nodes the node points to.
    Authority score is based on how many other nodes link to the node. It is
    calculated as a sum of hub scores of nodes pointing to the node.

    After each iteration the scores are normalized.

    Full description at:
    https://en.wikipedia.org/wiki/HITS_algorithm

    Arguments:
        matrix {DataFrame} -- Pandas dataframe containing square adjacency matrix.

    Keyword Arguments:
        limit {int} -- Number of iterations. (default: {25})
        disp_iterations {bool} -- Print status every iteration. (default: {False})

    Returns:
        DataFrame -- Results.
    """

    matrix_values = matrix.values
    rows, columns = matrix_values.shape

    assert rows == columns, ("Number of rows and columns in matrix does not "
                             "match")

    # Initialize values
    init_value = 1.0 / np.sqrt(rows)
    hubs = np.ones((rows, 1)) * (init_value)
    authorities = np.ones((rows, 1)) * (init_value)

    # Main iteration loop
    for counter in range(limit):

        h_old = hubs.copy()
        a_old = authorities.copy()

        for node in range(rows):
            # Nodes the current node points to
            a_mask = matrix_values[node, :] > 0
            # Nodes pointing to the current node
            h_mask = matrix_values[:, node] > 0

            hubs[node] = authorities[a_mask].sum()
            authorities[node] = hubs[h_mask].sum()

        # Normalize scores
        hubs = hubs / np.linalg.norm(hubs)
        authorities = authorities / np.linalg.norm(authorities)

        if (counter == limit-1) or disp_iterations:
            print(f"Iteration: {counter+1}")
            print("Change: {}, {}".format(
                np.sqrt(np.sum((authorities - a_old) ** 2)),
                np.sqrt(np.sum((hubs - h_old) ** 2))
            ))

    df_out = pd.DataFrame(index=matrix.index)
    df_out['Hubs'] = hubs
    df_out['Authorities'] = authorities
    df_out['Initial'] = init_value

    return df_out

def page_rank(matrix, epsilon=0.001, limit=50):
    """PageRank algorithm.

    Ranks nodes in a manner that gives high score to nodes which are linked to
    by other nodes with high scores.
    Final ranks are equivalent to probability distribution of random traveller
    arraiving to specific node.

    The algorithm is implemented as follows:
    First, the matrix F is calculated. Its elements are defined as:
    F_st = (1 - eps) / deg(t) + eps / N, if t links to s,
         = 1 / N                       , if t doesn't link anywhere,
         = eps / N                     , otherwise.
    Here, deg(t) is the number of links from t, N is the total number of nodes
    and eps is some small constant.
    Second, the final ranks r_n are computed iteratively r_i = F * r_i-1 until
    iteration limit i=n is reached.

    Full description at:
    https://en.wikipedia.org/wiki/PageRank

    Arguments:
        matrix {DataFrame} -- Pandas dataframe containing square adjacency matrix.

    Keyword Arguments:
        epsilon {float} -- Constant value. (default: {0.001})
        limit {int} -- Number of iterations. (default: {50})

    Returns:
        DataFrame -- Results.
    """

    matrix_values = matrix.values
    rows, columns = matrix_values.shape

    ranks = np.zeros((rows, columns))

    for s in range(rows):
        for t in range(columns):
            t_n_links = matrix_values[t, :].sum()
            # If t is connected to s:
            if matrix_values[t, s] == 1:
                ranks[s, t] = epsilon / float(rows)
                assert not np.isclose(t_n_links, 0)
                ranks[s, t] += (1.0 - epsilon) / t_n_links
            elif np.isclose(t_n_links, 0):
                ranks[s, t] = 1.0 / float(rows)
            else:
                ranks[s, t] = epsilon / float(rows)

    relevance = np.ones((rows, 1)) * (1.0 / float(rows))
    old_relevance = relevance.copy()

    final_ranks = np.zeros((rows, 1))

    for i in range(limit):
        for x in range(rows):
            row_count = np.sum(ranks[x, :] * old_relevance.ravel())
            relevance[x] = row_count
            if i == limit - 1:
                final_ranks[x] = relevance[x]
        old_relevance = relevance.copy()

    df_out = pd.DataFrame(final_ranks, index=matrix.index, columns=['Rank'])

    return df_out


import numpy as np
import math
import csv
import matplotlib.pyplot as plt

## Reading the csv values into arrays:

data_x0 = []
data_x1 = []
y = []

# Using CSV functions to read the values row-wise
with open("/home/vedant/Downloads/COL774/Ass1/data/q3/logisticX.csv") as f:
	reader = csv.reader(f)
	for row in reader:
		data_x0.append(row[0])
		data_x1.append(row[1])

with open("/home/vedant/Downloads/COL774/Ass1/data/q3/logisticY.csv") as f:
	reader = csv.reader(f)
	for row in reader:
		y.append(row[0])

# Converting the values in float
data_x0 = np.asarray(data_x0).astype(np.float64)
data_x1 = np.asarray(data_x1).astype(np.float64)
y = np.asarray(y).astype(np.float64)

## Normalization step:
data_x0 = (data_x0-(np.mean(data_x0)))/(math.sqrt(np.var(data_x0)))
data_x1 = (data_x1-(np.mean(data_x1)))/(math.sqrt(np.var(data_x1)))

### Part (A) : Implementing Newton's method

# Initializing the thetha vector
thetha = np.array([0,0,0])
m = len(y)

## Defining the functions to calculate h(x), gradient, and Hessian

# Getting the h(x)
def h(data_x0,data_x1,thetha):
	z = thetha[0] + thetha[1]*data_x0 + thetha[2]*data_x1
	return (1/(1 + math.exp(-z)))

# Getting the gradient
def grad(data_x0,data_x1,y,thetha,m):
	output = np.array([0,0,0])
	for j in range(3):
		sumVal = 0
		for i in range(m):
			if (j==0):
				xj=1
			elif (j==1):
				xj = data_x0[i]
			else:
				xj = data_x1[i]

			partial_grad = (y[i] - h(data_x0[i],data_x1[i],thetha))*xj
			sumVal = sumVal + partial_grad

		output[j] = sumVal

	return output

# Getting the Hessian
def hessian(data_x0,data_x1,y,thetha,m):
	a = [[0,0,0],[0,0,0],[0,0,0]]
	output = np.array(a)

	# Obtaining H(j,k)
	for j in range(3):
		for k in range(3):
			sumVal=0
			for i in range(m):

				# xj value
				if (j==0):
					xj=1
				elif (j==1):
					xj = data_x0[i]
				else:
					xj = data_x1[i]

				# xk value
				if (k==0):
					xk=1
				elif (k==1):
					xk = data_x0[i]
				else:
					xk = data_x1[i]

				sumVal = sumVal - (h(data_x0[i],data_x1[i],thetha)) * (1-h(data_x0[i],data_x1[i],thetha))*xj*xk

			output[j][k] = sumVal

	return output

## The newton's method

thetha_old = thetha

# Running the algorithm for at max 1000 steps and breaking when the consequtive thethas are very close to each other, which will be a stopping condition.
for i in range(8):
	thetha = thetha - np.dot((np.linalg.inv(hessian(data_x0,data_x1,y,thetha,m))),(grad(data_x0,data_x1,y,thetha,m)))

	diff = np.linalg.norm(thetha - thetha_old)
	# The comparision between the consequtive values of the parameters
	if (diff < 10**-10):
		break
	thetha_old = thetha

print(thetha)

### Part (B): Plots

# Plotting the scatters
for i in range(m):
	if (y[i]==0):
		plt.scatter(data_x0[i],data_x1[i],color = 'red')
	else:
		plt.scatter(data_x0[i],data_x1[i],color = 'blue')

# Getting the labels for the individual points by taking known points from the CSV files
plt.scatter(data_x0[0],data_x1[0],color = 'red', label = 'Points with y=0')
plt.scatter(data_x0[51],data_x1[51],color = 'blue', label = 'Points with y=1')

# Plotting the line: (thetha)' * X = 0
x = np.linspace(-3,3,1000)
plt.plot(x,-(thetha[0]/thetha[2]) - (thetha[1]/thetha[2])*x, color='black', label = 'Boundary line')

# Setting the Graph
plt.legend()
plt.xlabel('x1 axis')
plt.ylabel('x2 axis')
plt.savefig('Q3_plot.png', dpi=1000, bbox_inches='tight')


"""
inner

The inner tool returns the inner product of two arrays.

import numpy

A = numpy.array([0, 1])
B = numpy.array([3, 4])

print numpy.inner(A, B)     #Output : 4
outer

The outer tool returns the outer product of two arrays.

import numpy

A = numpy.array([0, 1])
B = numpy.array([3, 4])

print numpy.outer(A, B)     #Output : [[0 0]
                            #          [3 4]]
Task

You are given two arrays:  and .
Your task is to compute their inner and outer product.

Input Format

The first line contains the space separated elements of array .
The second line contains the space separated elements of array .

Output Format

First, print the inner product.
Second, print the outer product.

Sample Input

0 1
2 3
Sample Output

3
[[0 0]
 [2 3]]
"""

import numpy as np

np.set_printoptions(legacy="1.13")

arr1 = np.array(list(map(int, input().split())), int)
arr2 = np.array(list(map(int, input().split())), int)
print(np.inner(arr1, arr2))
print(np.outer(arr1, arr2))


# -*- coding: utf-8 -*-
# gradient descent without tuning, no epsilon in the gamma = float(np.matmul((x-xprev).T,(d-dprev)))/den**2
# gamma = float(np.matmul((x-xprev).T,(d-dprev)))/den**2 + epsilon; if we add epsilon then it will tune
# Gradient descent for minimizing a function - Barzilai-Borwein method
import numpy as np

def Rosenbrock(x):
    a = 2
    b = 100
    y = b*(x[1]-x[0]**2)**2+(x[0]-a)**2

    return y

def gradf(f,x,n,epsilon):
    y = np.zeros((n,1))
    for i in range (0,n):
        xperturbed = np.copy(x)
        xperturbed[i] = x[i] + epsilon
        y1 = f(xperturbed)
        xperturbed[i] = x[i] - epsilon
        y2 = f(xperturbed)
        y[i] = (y1-y2)/(2*epsilon)
    return y

def Gradient_Descent_BB(f,n):
    x = np.zeros((n,1))
    x[0] = float(1)
    x[1] = float(2)
    dprev = np.zeros((n,1))
    xprev = np.zeros((n,1))
    tol = float(1e-9)
    epsilon = float(1e-8)

    kmax = int(1000)
    k = int(1)

    d = gradf(f,x,n,epsilon)
    f_x = f(x)
    s = np.linalg.norm(d)

    while (s > tol*(1+ np.absolute(f_x)) and k < kmax):
        if (k == 1):
            gamma = float(np.matmul(x.T,d))/s**2 + epsilon;
        else:
            den = np.linalg.norm(d-dprev)
            gamma = float(np.matmul((x-xprev).T,(d-dprev)))/den**2
        dprev = d
        xprev = x
        x = x - gamma*d
        f_x = f(x)
        d = gradf(f,x,n,epsilon)
        k = k+1
        s = np.linalg.norm(d)

    f_min = f_x;

    return x, f_min;

def main():
    n = 2
    x , f_min = Gradient_Descent_BB(Rosenbrock, n)
    print(x)

if __name__ == "__main__":  ## This command executes the main function
    main()


# A rectangular tank with a square base, an open top, and a volume of 500 ft cubed is to be constructed of sheet steel.
# Find the dimensions of the tank that has the minimum surface area.

from sympy import symbols, solve, diff, pprint

volume = 4000

s, h = symbols( 's, h' )
A = s**2 + 4*s*h
V = s**2 * h

H = solve( V - volume, h )[ 0 ]

pprint( H )

Ah = A.subs( { h: H } ).evalf()

dA_ds = diff( Ah, s )

min_side = solve( dA_ds, s )[ 0 ]
min_height = H.subs( { s: min_side } ).evalf()

if __name__ == '__main__':
	print( 'The dimensions of the tank with the minimum surface area are: ' )
	print( 'Min side: {0:.0f} ft x {0:.0f} ft x {1:.0f} ft'.format( min_side, min_height ) )
	print( '{0:.0f}, {0:.0f}, {1:.0f}'.format( min_side, min_height ) )


import numpy as np
from scipy.linalg import lu_factor, lu_solve, expm
import matplotlib.pyplot as plt

def eig_qr(A, max_n = 100, tol=1e-10):
    d = A.shape[0]
    n = 0
    M = A.copy()

    while n<max_n and np.linalg.norm(np.tril(M, -1))**2/A.size>tol:
        ew,ev = np.linalg.eig(M[-2:,-2:])
        k = np.argmin(ew-M[-1,-1])
        shift_value = ew[k]
        Q, R = np.linalg.qr(M-shift_value*np.eye(d))
        M = np.matmul(Q.T, np.matmul(M, Q))
        n+= 1

    return M.diagonal()

def largest_eval(A, n_max = 500):
    z = np.random.rand((A.shape[0]))
    z /= np.linalg.norm(z)
    n = 0

    while n<n_max:
        w = np.matmul(A, z)
        z = w/np.linalg.norm(w)
        n+=1

    return z, np.dot(np.conj(z), np.matmul(A, z))

def smallest_eval(A, n_max = 500):
    LUP = lu_factor(A)
    n = 0
    z = np.random.rand((A.shape[0]))
    z /= np.linalg.norm(z)

    while n<n_max:
        w = lu_solve(LUP, z)
        z = w/np.linalg.norm(w)
        n += 1

    return z, np.dot(np.conj(z), np.matmul(A, z))

def closest_eval(A, ev, n_max = 500,):
    z, e = smallest_eval(A-ev*np.eye(A.shape[0]), n_max)
    return z, ev+e

def closest_eval_shifted(A, ev, n_max = 500, k = 10):
    n = 0
    z = np.random.rand((A.shape[0]))
    z /= np.linalg.norm(z)
    while n<n_max:
        if n%k == 0:
            if n != 0:
                ev = np.dot(np.conj(z), np.matmul(A, z))
                M = A-ev*np.eye(A.shape[0])
            else:
                M = A-ev*np.eye(A.shape[0])

            LUP = lu_factor(M)

        w = lu_solve(LUP, z)
        z = w/np.linalg.norm(w)
        n += 1

    return z, np.dot(np.conj(z), np.matmul(A, z))

def arnoldi(A, v0, k):
    v = v0/np.linalg.norm(v0)
    V = np.zeros((v0.size, k+1))
    V[:, 0] = v
    v_bar = v
    H = np.zeros((k+1, k))

    for l in range(k):
        v_bar = np.matmul(A, V[:,l])
        for i in range(l+1):
            h = np.dot(V[:, i], v_bar)
            v_bar -= h*V[:, i]
            H[i, l] = h
        h = np.linalg.norm(v_bar)
        H[l+1, l] = h
        V[:, l+1] = v_bar/h

    return H, V

def lanczos(A, v0, k):
    v = v0/np.linalg.norm(v0)
    V = np.zeros((v0.size, k+1))
    V[:, 0] = v
    v_bar = v
    H = np.zeros((k+1, k))

    for l in range(k):
        v_bar = np.matmul(A, V[:,l])
        for i in range(l-1, l+1):
            h = np.dot(V[:, i], v_bar)
            v_bar -= h*V[:, i]
            H[i, l] = h
        h = np.linalg.norm(v_bar)
        H[l+1, l] = h
        V[:, l+1] = v_bar/h

    return H, V


# 21 February 2018 Miroslav Gasparek
# Practice with NumPy

import numpy as np

# Practice 1
# Generate array of 0 to 10
my_ar1 = np.arange(0,11,dtype='float')
print(my_ar1)

my_ar2 = np.linspace(0,10,11,dtype='float')
print(my_ar2)

# Practice 2
# Load in data
xa_high = np.loadtxt('data/xa_high_food.csv',comments='#')
xa_low = np.loadtxt('data/xa_low_food.csv',comments='#')

def xa_to_diameter(xa):
    """ Convert an array of cross-sectional areas to diameters with
    commensurate units."""

    # Compute diameter from area
    diameter = np.sqrt((4*xa)/np.pi)

    return diameter

# Practice 3
# Create matrix A
A = np.array([[6.7, 1.3, 0.6, 0.7],
              [0.1, 5.5, 0.4, 2.4],
              [1.1, 0.8, 4.5, 1.7],
              [0.0, 1.5, 3.4, 7.5]])

# Create vector b
b = np.array([1.1, 2.3, 3.3, 3.9])

# 1. Print row 1 (remember, indexing starts at zero) of A.
print(A[0,:])
# 2. Print columns 1 and 3 of A.
print(A[:,(0,2)])
# 3. Print the values of every entry in A that is greater than 2.
print(A[A > 2])
# 4. Print the diagonal of A. using the np.diag() function.
print(np.diag(A))

# 1. First, we'll solve the linear system  A⋅x=bA⋅x=b .
# Try it out: use np.linalg.solve().
# Store your answer in the Numpy array x.
x = np.linalg.solve(A,b)
print('Solution of A*x = b is x = ',x)
# 2. Now do np.dot(A, x) to verify that  A⋅x=bA⋅x=b .
b1 = np.dot(A,x)
print(np.isclose(b1,b))
# 3. Use np.transpose() to compute the transpose of A.
AT = np.transpose(A)
print('Transpose of A is AT = \n',AT)
# 4. Use np.linalg.inv() to compute the inverse of A.
AInv = np.linalg.inv(A)
print('Inverse of A is AInv = \n',AInv)

# 1. See what happens when you do B = np.ravel(A).
B = np.ravel(A)
print(B)
# 2. Look of the documentation for np.reshape(). Then, reshape B to make it look like A again.
C  = B.reshape((4,4))
print(C)


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

# # Due Dec 5, 11:59 PM
# Programming Assignment
# 15 points
# Shovon Bhowmik Nov 17 (Edited Nov 17)
# Problems that has to be solved using python or matlab:
#
# 1.
#  * Make an excel sheet of the datas given for the correlation example in class.
#  * Calculate correlation coefficient and find the relationship between the datas.
#  * Also compare the result with the defined function used by python or matlab for establishing correlation between datas.
#  * Also visualize the graphical representation of the datas.
#
#
#
# 2.
#  * Collect any dataset with two variables x and y from any site. **The dataset must be atleast of 100 items.**
#  * Find the predicted y value based on x using **simple linear regression equation.**
#
#  Then find
#
#  1. SSR,
#  2. SSE
#  and
#  3. Coefficient of determination by calculating difference between the actual y value and the predicted y value.
#  4. Finally show the regression line based on your result graphically and find the accuracy of your model's performance.
#
# N. B:
#
# 1) You all are requested to submit the problems in the following link:
# https://drive.google.com/drive/folders/1qreLOzd0iN3ye4JfZ9S_O9O5qzTMnD-N?usp=sharing
# Upload your zip file which will be named after your roll number and there will be two files inside the zip file. Both files will be in .py or .mat format
#
# 2) If I find same code, the code that will come second or next will be not accepted and he or she will get no marks in the assignment.
#
# Please knock me if there is any question regarding the assignment.
# Thank you.

# In[68]:

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt

# DATASET
# > https://archive.ics.uci.edu/ml/datasets/student+performance

# In[69]:

df = pd.read_csv('student-mat.csv', delimiter=';')

# In[70]:

df.head()

# In[71]:

mydf = df.iloc[ : , -3:-1]

# In[72]:

# G1 = Grade1
# G2 = Grade2
#
mydf.head()

# In[73]:

dataX = mydf.iloc[ : , 0:1]
dataY = mydf.iloc[ : , 1:]

# In[74]:

dataX.head()

# In[75]:

dataY.head()

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# \begin{equation} r = ( sum((Xi - meanX) * (Yi - meanY) ) / sqrt( sum( squre( Xi - meanX) * squre(Yi - meanY) ) )) \end{equation}

# In[76]:

r = ( np.sum( (dataX.values - dataX.values.mean()) * (dataY.values - dataY.values.mean()) ) / np.sqrt( np.sum( np.square(dataX.values - dataX.values.mean()) ) * np.sum( np.square(dataY.values - dataY.values.mean()) ) ) )

# In[77]:

r

# In[78]:

meanX = dataX.values.mean()

# In[79]:

meanY = dataY.values.mean()

# In[80]:

sumOfMultiplyOfMeanXY = np.sum((dataX.values - meanX) * (dataY.values-meanY))

# In[81]:

meanX

# In[82]:

meanY

# In[83]:

sumOfMultiplyOfMeanXY

# In[84]:

np.sum( (dataX.values - dataX.values.mean()) * (dataY.values - dataY.values.mean()))

# In[85]:

np.sqrt( np.sum( np.square(dataX.values - dataX.values.mean()) ) * np.sum( np.square(dataY.values - dataY.values.mean()) ) )

# In[86]:

sumOfSqureOfX = np.sum(np.square(dataX.values - dataX.values.mean()))

# In[87]:

sumOfSqureOfY = np.sum(np.square(dataY.values - dataY.values.mean()))

# In[88]:

sqrtOfSSXY = np.sqrt( sumOfSqureOfX * sumOfSqureOfY )

# In[89]:

sqrtOfSSXY

# In[90]:

check_r = sumOfMultiplyOfMeanXY / sqrtOfSSXY

# In[91]:

check_r

# # $$y = a + bx$$
#
#
# ## $$b = r * (sdY/sdX)$$
#
# ## $$a = meanY - b * meanX$$
#

# In[92]:

b = r * (np.std(dataY.values) / dataX.values.std())

# In[93]:

b

# In[94]:

np.std(dataY.values)

# In[95]:

dataY.values.std()

# In[96]:

a = dataY.values.mean() - ( b * dataX.values.mean())

# In[97]:

a

# In[98]:

def Yprediction(x):
    return (a + (b * x))

# In[99]:

# Calculating Predicted Y
predictedY = dataX.apply(Yprediction)

# print(predictedY.head())?
# print(dataY.head())

# In[ ]:

# In[ ]:

# In[100]:

r == check_r

# In[101]:

print(type(dataY), type(predictedY))

# In[102]:

# >>> d = {'col1': [1, 2], 'col2': [3, 4]}
# >>> df = pd.DataFrame(data=d)
# >>> df

testdf = dataY
testdf['G2_Predicted'] = predictedY

# In[103]:

testdf.head()

# In[ ]:

# In[ ]:

# In[104]:

# type(testdf['G2_Predicted'].values)

# In[105]:

# SSR = np.sum( testdf['G2_Predicted'].values, testdf['G2'].values.mean() )
# testdf['G2'].values.mean()
# testdf['G2_Predicted'].values
# np.sum( np.square( testdf['G2_Predicted'].values, testdf['G2'].values.mean() ) )

# np.sum(np.square(testdf['G2_Predicted'].values - testdf['G2'].values.mean()))

# In[106]:

SSR = np.sum(np.square( np.subtract(testdf['G2_Predicted'].values , testdf['G2'].values.mean()) ))

# In[107]:

SSE = np.sum(np.square( np.subtract(testdf['G2'].values , testdf['G2_Predicted'].values.mean()) ))

# In[108]:

CoeffOfDetermination = (SSR / SSE)

# In[109]:

CoeffOfDetermination

# In[110]:

np.sqrt(CoeffOfDetermination)

# In[111]:

r

# In[ ]:

# In[ ]:

# In[ ]:

# In[112]:

plt.scatter(df.G1, df.G2)
plt.plot(df.G1, testdf[['G2_Predicted']])
plt.show()

# In[113]:

from sklearn import linear_model

reg = linear_model.LinearRegression()

# In[114]:

reg.fit(df[['G1']], df.G2)

# In[115]:

y_cap = reg.predict(df[['G1']])
reg.coef_

# In[116]:

# %matplotlib inline

plt.scatter(df.G1,df.G2,color='red', marker='o')
plt.plot(df.G1,y_cap)
plt.show()

# In[ ]:

# # Mean absolute error(MAE)
#
# $$MAE = \frac{1}{n} \sum_{}\left\lvert{y- \hat{y}}\right\rvert$$
#
# # Mean Squared Error
#
# $$MSE = \frac{1}{n} \sum_{}{(y- \hat{y})^2}$$
#
# # Root Mean Squared Error
#
# $$RMSE = \sqrt{ \frac{1}{n} \sum_{}{(y- \hat{y})^2}}$$
#

# In[117]:

# testdf.G2 - testdf.G2_Predicted

# In[118]:

MAE = ( (1/len(testdf.G2)) * np.sum(abs(np.subtract(testdf.G2 , testdf.G2_Predicted))) )

# In[119]:

MAE

# In[120]:

YY = testdf.G2
YY.head()
YYpredict = testdf.G2_Predicted
YYpredict.head()
# ( (1 / len(YY)) * ( np.sum(np.abs(np.subtract(YY,YYp)))) )
MSE = ( (1 / len(YY)) * ( np.sum(np.square(np.subtract(YY,YYpredict)))) )

# In[121]:

MSE

# In[122]:

RMSE = np.sqrt(MSE)

# In[123]:

RMSE

# In[124]:

from sklearn.metrics import mean_squared_error

from math import sqrt

rmse = sqrt(mean_squared_error(YY, YYpredict))

print(rmse)

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:


import pandas as pd

from scipy import misc
from mpl_toolkits.mplot3d import Axes3D
import matplotlib
import matplotlib.pyplot as plt

# Look pretty...
matplotlib.style.use('ggplot')

#
# TODO: Start by creating a regular old, plain, "vanilla"
# python list. You can call it 'samples'.
#
# .. your code here ..

samples = []

#
# TODO: Write a for-loop that iterates over the images in the
# Module4/Datasets/ALOI/32/ folder, appending each of them to
# your list. Each .PNG image should first be loaded into a
# temporary NDArray, just as shown in the Feature
# Representation reading.
#
# Optional: Resample the image down by a factor of two if you
# have a slower computer. You can also convert the image from
# 0-255  to  0.0-1.0  if you'd like, but that will have no
# effect on the algorithm's results.
#
# .. your code here ..
import os
for image in os.listdir('Module4/Datasets/ALOI/32/'):
  samples.append(misc.imread('Module4/Datasets/ALOI/32/' + image).reshape(-1))

df = pd.DataFrame(samples)
from sklearn import manifold

for n_neighbor in range (1,7):
  iso = manifold.Isomap(n_neighbors=n_neighbor, n_components=3)
  iso.fit(df)
  T = iso.transform(df)

  # 2D Scatterplot
  fig = plt.figure()
  ax = fig.add_subplot(111)
  ax.set_title("ISO map on ALOI with {n} neighbors".format(n=n_neighbor))
  ax.set_xlabel('Manifold Component: 0')
  ax.set_ylabel('Manifold Component: 1')
  ax.scatter(T[:,0],T[:,1], marker='.',alpha=0.7)

  # 3D scatterplot
  fig = plt.figure()
  ax = fig.add_subplot(111, projection='3d')
  ax.set_title("ISOMAP 3D map on ALOI with {n} neighbors".format(n=n_neighbor))
  ax.set_xlabel('Manifold Component: 0')
  ax.set_ylabel('Manifold Component: 1')
  ax.set_zlabel('Manifold Component: 2')
  ax.scatter(T[:,0], T[:,1], T[:,2], c='green', marker='.', alpha=0.75)

#
# TODO: Once you're done answering the first three questions,
# right before you converted your list to a dataframe, add in
# additional code which also appends to your list the images
# in the Module4/Datasets/ALOI/32_i directory. Re-run your
# assignment and answer the final question below.
#
# .. your code here ..
mixed_samples = list(samples)
colors = []
for i in mixed_samples:
  colors.append('b')
for image in os.listdir('Module4/Datasets/ALOI/32i/'):
  mixed_samples.append(misc.imread('Module4/Datasets/ALOI/32i/' + image).reshape(-1))
  colors.append('r')

#
# TODO: Convert the list to a dataframe
#
# .. your code here ..
mixed_df = pd.DataFrame(mixed_samples)

#
# TODO: Implement Isomap here. Reduce the dataframe df down
# to three components, using K=6 for your neighborhood size
#
# .. your code here ..
mixed_iso = manifold.Isomap(n_neighbors=6, n_components=3)
mixed_iso.fit(mixed_df)
mixed_T = mixed_iso.transform(mixed_df)

#
# TODO: Create a 2D Scatter plot to graph your manifold. You
# can use either 'o' or '.' as your marker. Graph the first two
# isomap components
#
# .. your code here ..

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title("ISO map on ALOI with 6 neighbors")
ax.set_xlabel('Manifold Component: 0')
ax.set_ylabel('Manifold Component: 1')
ax.scatter(mixed_T[:,0],mixed_T[:,1], marker='.',alpha=0.7,c=colors)

#
# TODO: Create a 3D Scatter plot to graph your manifold. You
# can use either 'o' or '.' as your marker:
#
# .. your code here ..

# 3D scatterplot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_title("ISOMAP 3D map on ALOI with 6 neighbors")
ax.set_xlabel('Manifold Component: 0')
ax.set_ylabel('Manifold Component: 1')
ax.set_zlabel('Manifold Component: 2')
ax.scatter(mixed_T[:,0], mixed_T[:,1], mixed_T[:,2], marker='.', alpha=0.75,c=colors)


import numpy as np
x=np.array([100,200,300,400]) #each member as a
y=np.array([400,500,600,700]) #each member as b
condition=np.array([True,True,False,False]) #each member cond
#if true take x else y
z= [a if cond else b for a,cond, b in zip(x,condition,y)]
print(z)

#same opeartion usiing numpy
#np.where(#condition,#value for yes,#value for no)
z2=np.where(condition,x,y)

z3=np.where(x>200,0,1) #if x is > 200 replace it by 0 else 1
print(z3)

#sum
print(x.sum())

n=np.array([[10,20,30],[40,50,60],[70,80,90]])
print("N=",n)
#colomun sum
print("Sum of Coloums=",n.sum(0))
#row sum
print("Sum of Rows=",n.sum(1))

#mean
mean=x.mean()
print("Mean=",mean)
#std deviation
std_dev=x.std()
print("Standard Deviation=",std_dev)

#varinace
var=x.var()
print("Variane=",var)

#logical operations

condition2=np.array([True,False,True])
print("OR operator",condition2.any())
print("AND operator",condition2.all())

#sorting in numpy arrays
unsorted=np.array([2,5,9,4,-6,3,1])
print("Unsorted Array=",unsorted)
unsorted.sort()
print("Sorted Array=",unsorted)

s=np.array(["Solid","Solid","Liquid","Liquid","Liquid","Gas","Gas"])
n=np.array([1,2,3,1,2,4,4,8,9,9,3,4,4])
print(np.unique(s))
print(np.unique(n))

print(np.in1d([2,6,9],n))


import pytest
import numpy as np

from superdifferentiator.forward.functions import X
from superdifferentiator.forward.Vector import Vector

def test_vector1():
	xV, yV = 2, 3
	x, y = X(xV, 'x'), X(yV, 'y')
	f1, f2 = 2 * (x ** 2) * y, 3 * (x ** 2) + (y ** 3)

	v = Vector([f1, f2])
	assert v.val[0][0, 0] == 2 * (xV ** 2) * yV
	assert v.val[0][1, 0] == 3 * (xV ** 2) + (yV ** 3)

	allVars, jac = v.jacobian()
	jac = jac[0]
	xIdx = allVars.index('x')
	yIdx = allVars.index('y')
	assert jac[0, xIdx] == 4 * xV * yV
	assert jac[0, yIdx] == 2 * (xV ** 2)
	assert jac[1, xIdx] == 6 * xV
	assert jac[1, yIdx] == 3 * (yV ** 2)

def test_vector2():
	xV, yV = 2, 3
	x, y = X(xV, 'x'), X(yV, 'y')
	f1, f2 = (x ** 2) + (2 * x) + 3, 3 * (y ** 2) + (4 * y) + 8

	v = Vector([f1, f2])
	assert v.val[0][0, 0] == (xV ** 2) + (2 * xV) + 3
	assert v.val[0][1, 0] == 3 * (yV ** 2) + (4 * yV) + 8

	allVars, jac = v.jacobian()
	jac = jac[0]
	xIdx = allVars.index('x')
	yIdx = allVars.index('y')
	assert jac[0, xIdx] ==  2 * xV + 2
	assert jac[0, yIdx] == 0
	assert jac[1, xIdx] == 0
	assert jac[1, yIdx] == 6 * yV + 4

def test_vector_operators():
	x1 = X(3)
	v1 = Vector([x1])
	x2 = X(4)
	v2 = Vector([x2])

	assert v1 < v2
	assert v1 <= v2
	assert not v1 > v2
	assert not v1 >= v2

	assert v1 > 2
	assert v1 >= 2
	assert v1 < 4
	assert v1 <= 4

	assert v1 == v1
	assert v1 != v2

	x3 = X(5, 'x3')
	v3 = Vector([x1, x3])
	assert v1 != v3


# Alexis Mendoza Valencia
# Estudiante Tecnológico de Monterrey, campus guadalajara

# Convergencia

"""
Nota: Para explicar este método recomiendo utilizar
      una pagina web llamada Cocalc para correr el metodo.
      Al menos es la pagina que yo utilice, pues ya tiene
      incluidas las librerias necesarias: numpy y mathplotlib.
      La liga es: cocalc.com
"""

import numpy as np
import matplotlib.pyplot as plt

"""
Definicion: La convergencia es un termino que se utiliza en
            limites. Decimos que una funcion converge a un
            valor cuando se acerca hasta este.
            Por ejemplo, si graficamos un logaritmo natural,
            podemos ver que si el numero se hace mas pequeño
            el valor tiende a cero. En este caso se acerca,
            mas nunca lo toca (sabemos que ln(0) no esta definido.

            Vamos a ver ejemplos entonces.
"""

import math
def logaritmo(x):
    return math.log(x)

def convergencia(funcion, valor_inferior, valor_superior):
    valores_X = np.linspace(valor_inferior, valor_superior, 100)
    valores_Y = [funcion(valores_X[i]) for i in range(len(valores_X))]

    plt.plot(valores_X, valores_Y)
    plt.grid()
    plt.show()

print("Ejemplo 1")
convergencia(logaritmo, 0.01, 10)
convergencia(logaritmo, 0.000001, 1)
"""
Veremos entonces al correr el programa que mientras el valor inferior
sea mas pequeño tendremos un valor de Y que se acerca cada vez mas a cero.

Pero ahora, podemos ver esto de diferentes formas. Un ejemplo es la raiz
de dos.

Si tenemos que x = raiz(2)
podemos decir que x^2 = 2
Por lo tanto, x^2 - 2 = 0

Estariamos buscando entonces el valor mas pequeño para el cual es valida
esta funcion, que buscamos que tenga convergencia a cero.
"""

def raiz_dos(x):
    return abs(x**2 - 2)

print("Ejemplo 2")
convergencia(raiz_dos, 0, 2)
convergencia(raiz_dos, 1.25, 1.75)
convergencia(raiz_dos, 1.4, 1.44)

"""
Y asi, poco a poco, nos acercamos al valor deseado.
"""


import math
import numpy as np

def evaluate(job_id, params):

    x = params['X']
    y = params['Y']

    #print 'Evaluating at (%f, %f)' % (x, y)

    # if x < 0 or x > 5.0 or y > 5.0:
    #     return np.nan
    # Feasible region: x in [0,5] and y in [0,5]

    obj1 = michalewicz(75, x, y)
    obj2 = michalewicz(100, x, y)
    obj3 = beale(x, y)
    obj4 = stybilinski(x, y)

    return {
        "michalewicz_75"        : obj1,
        "michalewicz_100"       : obj2,
        "beale"                 : obj3,
        "stybilinski"           : obj4,
    }

def michalewicz(m,x,y):
    x = scale_from_unity(x,0,np.pi)
    y = scale_from_unity(y,0, np.pi)
    return -1*(np.sin(x)*np.sin(x**2/np.pi)**(2*m) + np.sin(y)*np.sin(2*y**2/np.pi)**(2*m))

def easom(x,y):
    x = scale_from_unity(x,-100, 100)
    y = scale_from_unity(y,-100, 100)
    return -np.cos(x)*np.cos(y)*np.exp(-(x-np.pi)**2 - (y-np.pi)**2)

def beale(x,y):
    x = scale_from_unity(x,-4.5, 4.5)
    y = scale_from_unity(y,-4.5, 4.5)
    return ((1.5-x+x*y)*2 + (2.25-x+x*y**2)**2 + (2.625-x+x*y**3)**2)/10000

def stybilinski(x,y):
    x = scale_from_unity(x,-5, 5)
    y = scale_from_unity(y,-5, 5)
    return (x**4 + y**4 - 16*(x**2 + y**2) + 5*(x + y))/100

def gramacy(x,y):
    x = scale_from_unity(x, -2, 6)
    y = scale_from_unity(y, -2, 6)
    return x*np.exp(-x**2 -y**2)

def griewank(x,y):
    x = scale_from_unity(x, -600, 600)
    y = scale_from_unity(y, -600, 600)
    return (x**2 + y**2)/4000 - np.cos(x)*np.cos(y/np.sqrt(2))+1

def branin(x,y):
    x = scale_from_unity(x, -10, 10)
    y = scale_from_unity(y, -10, 10)

    return float(np.square(y - (5.1/(4*np.square(math.pi)))*np.square(x) + (5/math.pi)*x- 6) + 10*(1-(1./(8*math.pi)))*np.cos(x) + 10)

def levy(x,y):
    x = scale_from_unity(x, -5, 10)
    y = scale_from_unity(y, 0, 15)
    w1 = (x-1)/4 + 1
    w2 = (y-1)/4 + 1
    return np.square(np.sin(np.pi*w1)) + (w2-1)**2*(1 + np.sin(2* np.pi*w2)**2)

scale = lambda x, a, b, a2, b2: (x-a)*(b2-a2)/(b-a)+a2
def scale_from_unity(x, a, b):
    return scale(x, -1,1,a,b)

def main(job_id, params):
    try:
        return evaluate(job_id, params)
    except Exception as ex:
        print ex
        print 'An error occurred in parabolic.py'
        return np.nan


import numpy as np

def sigmoid(X):
    """
    Apply Sigmoid on X Vector.

    PARAMETERS
    ==========

    X: ndarray(dtype=float, ndim=1)
        Array containing Input Values.

    RETURNS
    =======

    ndarray(dtype=float,ndim=1)
        Output Vector after Vectorised Operation.
    """
    return 1/(1 + np.exp(-X))

def tanh(X):
    """
    Apply Inverse of Tan on X Vector.

    PARAMETERS
    ==========

    X: ndarray(dtype=float, ndim=1)
        Array containing Input Values.

    RETURNS
    =======

    ndarray(dtype=float,ndim=1)
        Output Vector after Vectorised Operation.
    """
    return np.tanh(X)

def softmax(X):
    """
    Apply Softmax on X Vector.

    PARAMETERS
    ==========

    X: ndarray(dtype=float, ndim=1)
        Array containing Input Values.
    Sum: float
        Sum of values of Input Array.

    RETURNS
    =======

    ndarray(dtype=float,ndim=1)
        Output Vector after Vectorised Operation.
    """
    Sum = np.sum(np.exp(X))
    return np.exp(X)/Sum

def softsign(X):
    """
    Apply Softsign on X Vector.

    PARAMETERS
    ==========

    X: ndarray(dtype=float, ndim=1)
        Array containing Input Values.

    RETURNS
    =======

    ndarray(dtype=float,ndim=1)
        Output Vector after Vectorised Operation.
    """
    return X/(np.abs(X) + 1)

def relu(X):
    """
    Apply Rectified Linear Unit on X Vector.

    PARAMETERS
    ==========

    X: ndarray(dtype=float, ndim=1)
        Array containing Input Values.

    RETURNS
    =======

    ndarray(dtype=float,ndim=1)
        Output Vector after Vectorised Operation.
    """
    return np.maximum(0, X)

def leakyRelu(X):
    """
    Apply Leaky Rectified Linear Unit on X Vector.

    PARAMETERS
    ==========

    X: ndarray(dtype=float, ndim=1)
        Array containing Input Values.

    RETURNS
    =======

    ndarray(dtype=float,ndim=1)
        Output Vector after Vectorised Operation.
    """
    return np.maximum(0.01*X, X)

def elu(X, alpha=1.0):
    """
    Apply Exponential Linear Unit on X Vector.

    PARAMETERS
    ==========

    X: ndarray(dtype=float, ndim=1)
        Array containing Input Values.
    alpha: float
        Curve Constant for Values of X less than 0.

    RETURNS
    =======

    ndarray(dtype=float,ndim=1)
        Output Vector after Vectorised Operation.
    """
    assert(alpha > 0)
    return np.maximum(0, X) + np.minimum(0, alpha*(np.exp(X) - 1))


import numpy as np
import matplotlib.pyplot as plt

# Función de Rosembrock
def Rosembrock(x, params = {}):
    '''
    Parámetros
    -----------
        n    : dimensión de muestra
        x    : vector de valores [x_1, x_2, ..., x_n]
    Regresa
    -----------
        fx : Evaluación en la función de Rosembrock
    '''
    n  = x.shape[0]
    fx = 0.0

    for i in range(n-1) :
        fx += 100.0 * (x[i+1] - x[i]* x[i]) * (x[i+1] - x[i]* x[i])
        fx += (1 - x[i])*(1 - x[i])

    return fx

def Rosembrock_Grad(x, params = {}) :
    '''
    Calcula el gradiente de la función de Rosembrock

    Parámetros
    -----------
        x   : Vector de valores [x_1, x_2, ..., x_n]
    Regresa
    -----------
        Gfx : Vector gradiente [D_x_1 f(x), D_x_2 f(x), ..., D_x_n (fx)]

    '''

    n   = x.shape[0]
    Gfx = np.zeros(n, dtype = np.float64)

    # Gfx[0]  = - 400.0 * (x[1] - x[0]* x[0])* x[0] - 2.0 * ( 1- x[0])

    # val = [i+1 for i in range(n-1)]
    for i in range(n-1) :
        Gfx[i] = -400.0 * x[i+1]*x[i] + 400.0 * x[i]**3 + 2*x[i] - 2.0

    Gfx[n-1] = 200.0 * (x[n-1] - x[n-2]**2)

    return Gfx

def Rosembrock_Hess(x, params = {}) :
    '''
    Calcula la matrix Hessiana de la función de Rosembrock

    Parámetros
    -----------
        x  : Array de valores [x_1, x_2, ..., x_n]
    Regresa
    -----------
        Hf : Matrix Hesianna
    '''

    n = x.shape[0]
    Hf = np.zeros((n,n), dtype = np.float64)

    for i in range(n-1) :
        Hf[i][i]   = 1200.0 * x[i] ** 2 - 400.0 * x[i+1] + 2.0
        Hf[i][i+1] = - 400.0 * x[i]
    Hf[n-1][n-2] = -400.0 * x[n-2]
    Hf[n-1][n-1] = 200.0

    return Hf
# Función de Wood

def Wood(x, params = {}) :
    '''
    Calcula el valor de la función de Wood f(x)
    Parámetros
    -----------
         x: Array de valores [x_1, x_2, ..., x_n]
    Regresa
    -----------
        fx : Valor f(x)
    '''
    # Reviso Dimensión
    if (x.shape[0] != 4):
        print('Revisar vector x')
        return

    fx  = 100.0 * (x[0] * x[0] - x[1]) *  (x[0] * x[0] - x[1])
    fx += (x[2]* x[2] - 1.0) * (x[2]* x[2] - 1.0)
    fx += 90.0 * (x[2]*x[2] - x[3]) * (x[2]*x[2] - x[3])
    fx += 10.1 * ( (x[1] - 1.0) * (x[1] - 1.0) + (x[3] - 1.0) * (x[3] - 1.0) )
    fx += 19.8 * (x[1] - 1.0) * (x[3] - 1.0)

    return fx

def Wood_Grad(x, params = {}) :
    '''
    Calcula el gradiente de la función de Wood
    Parámetros
    -----------
         x  : Array de valores [x_1, x_2, ..., x_n]
    Regresa
    -----------
        Gfx : Vector gradiente Gfx
    '''

    # Reviso Dimensión
    if (x.shape[0] != 4):
        print('Revisar vector x')
        return

    n   = x.shape[0]
    Gfx = np.zeros(n, dtype = np.float64)

    Gfx[0] = 400.0 * (x[0]* x[0] - x[1]) * x[0] + 2.0 * (x[0] - 1.0)
    Gfx[1] = - 200.0 * (x[0]* x[0] - x[1]) + 20.2 * (x[1] - 1.0) + 19.8 * (x[3] - 1.0)
    Gfx[2] = 2.0 * (x[2] - 1.0) + 360.0 * (x[2]* x[2] - x[3]) * x[2]
    Gfx[3] = -180.0 * (x[2]* x[2] - x[3]) + 20.2 * (x[3] - 1.0) + 19.8 * (x[1] - 1)

    return Gfx

def Wood_Hess(x, params = {}) :
    '''
    Calcula matrix Hessiana de la función de Wood
    Parámetros
    -----------
         x  : Array de valores [x_1, x_2, ..., x_n]
    Regresa
    -----------
        Hfx : Matrix Hessiana Hfx
    '''
    # Reviso Dimensión
    if (x.shape[0] != 4):
        print('Revisar vector x')
        return

    n   = x.shape[0]

    Hfx = np.zeros((n, n), dtype=np.float64)

    Hfx[0][0] = 1200.0 * x[0]*x[0] - 400.0 * x[1] + 2.0
    Hfx[0][1] = Hfx[1][0] = - 400.0 * x[0]
    Hfx[1][1] = Hfx[3][3] = 220.2
    Hfx[2][2] = 1080.0 * x[2]* x[2] - 360 * x[3] + 2.0
    Hfx[3][1] = Hfx[1][3] = 19.8
    Hfx[3][2] = Hfx[2][3] = - 360.0 * x[2]

    return Hfx

def Branin(x, params = {}) :
  '''
  Calcula valor de la función de Branin
  '''
  n = x.shape[0]
  a = params['a']
  b = params['b']
  c = params['c']
  r = params['r']
  s = params['s']
  t = params['t']

  f  = a * (x[1] - b * x[0]**2 + c * x[0] - r)**2
  f += s * (1.0 - t) * np.cos(x[0]) + s

  return f

def Branin_Grad (x, params = {}) :
  n = x.shape[0]
  a = params['a']
  b = params['b']
  c = params['c']
  r = params['r']
  s = params['s']
  t = params['t']

  f_grad = np.zeros(2, dtype = np.float64)

  f_grad[0]  = 2.0 * a * (x[1] - b * x[0]**2 + c * x[0] - r) * (-2.0 * b * x[0] + c)
  f_grad[0] -= s * (1.0 - t) * np.sin(x[0])

  f_grad[1] = 2.0 * a * (x[1] - b * x[0]**2 + c * x[0] - r)

  return f_grad

def Branin_Hess(x, params = {}) :
  n = x.shape[0]
  a = params['a']
  b = params['b']
  c = params['c']
  r = params['r']
  s = params['s']
  t = params['t']

  f_Hess = np.zeros((2,2), dtype = np.float64)

  f_Hess[0] = -4.0 * a * b * (x[1] - b * x[0]**2 + c * x[0] - r)
  f_Hess[0] += 2.0 * a * (-2.0 * b * x[0] + c) **2 - s * (1.0 - t) * np.cos(x[1])

  f_Hess[0][1] = f_Hess[1][0] = 2.0 * a * ( -2.0 * b * x[0] + c)

  f_Hess[1][1] = 2.0 * a

  return f_Hess

def get_newton_direction(g_k, d_k) :
  p_k = np.linalg.solve(d_k, -g_k)
  return p_k

def cholesky_plus_identity(A, beta = 1e-3, max_iter = 100):
  diag = np.diag(A)
  min_aii = min(diag)
  if min_aii > 0 :
    tau = 0.0
  else :
    tau = - min_aii + beta

  k = 0
  while k < max_iter :
    A = A + tau * np.eye(diag.shape[0])
    try :
      A = np.linalg.cholesky(A)
    except :
      tau = max(2.0 * tau, beta)
    else :
      k = max_iter
    k += 1

  return A

def newton_modified (params = []) :
    '''
    '''
    # Cargo parámetros
    x_k        = params['x_0']
    x_k_next   = None
    f          = params['f']
    f_grad     = params['f_grad']
    f_hess     = params['f_hess']
    max_iter   = params['max_iter']
    tau_x      = params['tau_x']
    tau_f      = params['tau_f']
    tau_f_grad = params['tau_grad']
    beta       = params['beta']
    max_iter_c = params['cholesky']['max_iter']

    # Subpámetros para la función
    if f.__name__ == 'Branin' :
      sub_params = {
                    'a' : params['a'],
                    'b' : params['b'],
                    'c' : params['c'],
                    'r' : params['r'],
                    's' : params['s'],
                    't' : params['t']
                  }
    else :
      sub_params = {}

    # Guardo Parámetros
    f_hist = []
    f_hist.append(f(x_k, params = sub_params))

    g_hist = []
    g_hist.append(np.linalg.norm(f_grad(x_k, params = sub_params)))

    # Comienza descenso
    k = 0

    while True:
        # Calculo Gradiente
        g_k = f_grad(x_k, params = sub_params)
        # Calculo Hessiano
        H_k = f_hess(x_k, params = sub_params)
        # Factorizo usando Cholesky Modificado
        L = cholesky_plus_identity(H_k, beta, max_iter_c)
        # Resuelvo sistema hacia atrás
        y   = np.linalg.solve(L, -g_k)
        d_k = np.linalg.solve(L.T, y)

        # Calculo siguiente valor x_k+1
        x_k_next = x_k + d_k

        # Guardo Parámetros
        f_hist.append(f(x_k_next, sub_params))
        g_hist.append(np.linalg.norm(f_grad(x_k_next, sub_params)))

        # Criterios de paro
        if (k > max_iter) :
            break

        if np.linalg.norm(x_k_next - x_k)/max(np.linalg.norm(x_k), 1.0) < tau_x :
            break

        if np.abs(f(x_k_next, sub_params) - f(x_k, sub_params)) / max(np.linalg.norm(f(x_k, sub_params)), 1.0) < tau_f :
            break

        if np.linalg.norm(f_grad(x_k_next, sub_params)) < tau_f_grad :
            break

        # Guardo valor anterior
        x_k = x_k_next
        k   = k + 1

    return np.array(f_hist), np.array(g_hist), x_k_next

def get_graf(f_hist, g_hist) :
  plt.plot(range(f_hist.shape[0]), f_hist)
  # naming the x axis
  plt.xlabel('k')
  # naming the y axis
  plt.ylabel('f_k')

  # giving a title to my graph
  plt.title('Iteration vs Fuction Value')

  # function to show the plot
  plt.show()

  plt.plot(range(g_hist.shape[0]), g_hist)
  # naming the x axis
  plt.xlabel('k')
  # naming the y axis
  plt.ylabel('g_k')

  # giving a title to my graph
  plt.title('Iteration vs Gradient Norm')

  # function to show the plot
  plt.show()


#%%
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import dm6103 as dm

# Part I
titanic = dm.api_dsLand('Titanic', 'id')

# Part II
nfl = dm.api_dsLand('nfl2008_fga')
nfl.dropna(inplace=True)

#%% [markdown]

# # Part I
# Titanic dataset - statsmodels
#
# | Variable | Definition | Key/Notes  |
# | ---- | ---- | ---- |
# | survival | Survived or not | 0 = No, 1 = Yes |
# | pclass | Ticket class | 1 = 1st, 2 = 2nd, 3 = 3rd |
# | sex | Gender / Sex |  |
# | age | Age in years |  |
# | sibsp | # of siblings / spouses on the Titanic |  |
# | parch | # of parents / children on the Titanic |  |
# | ticket | Ticket number (for superstitious ones) |  |
# | fare | Passenger fare |  |
# | embarked | Port of Embarkation | C: Cherbourg, Q: Queenstown, S: Southampton  |
#
#%%
# ## Question 1
# With the Titanic dataset, perform some summary visualizations:
#
# ### a. Histogram on age. Maybe a stacked histogram on age with male-female as two series if possible
titanic.dropna()
titanic.pivot(columns='sex').age.plot(kind = 'hist', stacked=True)
plt.title("Stacked histogram on age with sex")
plt.xlabel("Age")
plt.show()
# ### b. proportion summary of male-female, survived-dead
crosstab = pd.crosstab(titanic['survived'], titanic['sex'], margins = True)
print(crosstab)
# ### c. pie chart for “Ticketclass”
Ticketclass = titanic['pclass'].value_counts()
fig, ax = plt.subplots()
ax.pie(Ticketclass, labels=Ticketclass.index, autopct='%1.1f%%')
plt.title("pie chart for Ticketclass")
plt.legend()
plt.show()
# ### d. A single visualization chart that shows info of survival, age, pclass, and sex.
sexcolors = np.where(titanic['pclass']==1,'r','-')
sexcolors[titanic['pclass']==2] = 'b'
sexcolors[titanic['pclass']==3] = 'g'

ax1 = titanic[titanic.sex=='male'].plot(x="age", y="survived", kind="scatter", color=sexcolors[titanic.sex=='male'], marker='^', s=7, label='male')
titanic[titanic.sex=='female'].plot(x="age", y="survived", kind="scatter", color=sexcolors[titanic.sex=='female'], marker='+', s=7, label='female', ax = ax1)
plt.title("Age vs Survival")
plt.show()

#%%
# ## Question 2
# Build a logistic regression model for survival using the statsmodels library. As we did before, include the features that you find plausible. Make sure categorical variables are use properly. If the coefficient(s) turns out insignificant, drop it and re-build.
import statsmodels.api as sm
from statsmodels.formula.api import glm
Logit = glm(formula='survived~C(pclass)+age+C(sex)+sibsp+parch', data=titanic, family=sm.families.Binomial())
LogitFit = Logit.fit()
print(LogitFit.summary())

# When alpha = 0.05, the p-value of "parch" is 0.840. Thus we fail to reject the null hypothesis that the coefficient of "parch" is 0.
#%%
# Re-build the model.
Logit_re = glm(formula='survived~C(pclass)+age+C(sex)+sibsp', data=titanic, family=sm.families.Binomial())
LogitFit_re = Logit_re.fit()
print(LogitFit_re.summary())

#%%
# ## Question 3
# Interpret your result. What are the factors and how do they affect the chance of survival (or the survival odds ratio)? What is the predicted probability of survival for a 30-year-old female with a second class ticket, no siblings, 3 parents/children on the trip? Use whatever variables that are relevant in your model.

# Using pclass =1 as the base line, when the pclass =2, the odds ratio of survived becomes exp(-0.9510) times. When the pclass =3, the odds ratio of survived becomes exp(-2.1575) times.
# Using female as the base line, the odds ratio of male survived becomes exp(-2.7393) times.
# 1 degree increase in age makes odds ratio of survived exp(-0.0181) times as before while other variables stay the same.
# 1 degree increase in age makes odds ratio of survived exp(-0.2804) times as before while other variables stay the same.
pred = pd.DataFrame({'pclass':2, 'sex':"female", 'age':30, 'sibsp':0}, pd.Index(range(1)))
print("The predicted probability of survival for a 30-year-old female with a second class ticket, no siblings is %.2f" % LogitFit_re.predict(pred))

#%%
# ## Question 4
# Try three different cut-off values at 0.3, 0.5, and 0.7. What are the a) Total accuracy of the model b) The precision of the model (average for 0 and 1), and c) the recall rate of the model (average for 0 and 1)
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report

titanic['sex'][titanic['sex']=='male'] = 0
titanic['sex'][titanic['sex']=='female'] = 1

xsurvival = titanic[['pclass', 'age', 'sex', 'sibsp']]
ysurvival = titanic['survived']

x_trainSurvival, x_testSurvival, y_trainSurvival, y_testSurvival = train_test_split(xsurvival, ysurvival, random_state=1 )

survivallogit = LogisticRegression()
survivallogit.fit(x_trainSurvival, y_trainSurvival)

def predictcutoff(arr, cutoff):
  arrbool = arr[:,1]>cutoff
  arr= arr[:,1]*arrbool/arr[:,1]
  return arr.astype(int)

test = survivallogit.predict_proba(x_testSurvival)
# a
y_pred3 = predictcutoff(test, 0.3)
print(classification_report(y_testSurvival, y_pred3))
# a) The accuracy of the model is 0.78. The precision for "0" is 0.86, for "1" is 0.70. The recall rate for "0" is 0.73, for "1" is 0.84.
print('----------------------------------------------')
y_pred5 = predictcutoff(test, 0.5)
print(classification_report(y_testSurvival, y_pred5))
# b) The accuracy of the model is 0.79. The precision for "0" is 0.79, for "1" is 0.80. The recall rate for "0" is 0.88, for "1" is 0.68.
print('----------------------------------------------')
y_pred7 = predictcutoff(test, 0.7)
print(classification_report(y_testSurvival, y_pred7))
# c) The accuracy of the model is 0.75. The precision for "0" is 0.70, for "1" is 0.95. The recall rate for "0" is 0.98, for "1" is 0.43.
print('----------------------------------------------')
#%%[markdown]
# # Part II
# NFL field goal dataset - SciKitLearn
#
# | Variable | Definition | Key/Notes  |
# | ---- | ---- | ---- |
# | AwayTeam | Name of visiting team | |
# | HomeTeam | Name of home team | |
# | qtr | quarter | 1, 2, 3, 4 |
# | min | Time: minutes in the game |  |
# | sec | Time: seconds in the game |  |
# | kickteam | Name of kicking team |  |
# | distance | Distance of the kick, from goal post (yards) |  |
# | timerem | Time remaining in game (seconds) |  |
# | GOOD | Whether the kick is good or no good | If not GOOD: |
# | Missed | If the kick misses the mark | either Missed |
# | Blocked | If the kick is blocked by the defense | or blocked |
#
#%%
# ## Question 5
# With the nfl dataset, perform some summary visualizations.
print(nfl.info())

nfl.pivot(columns='GOOD').timerem.plot(kind = 'hist', stacked=True)
plt.title("Stacked histogram on Time remaining with GOOD")
plt.xlabel("Time remaining in game (seconds)")
plt.show()

qtrclass = nfl['qtr'].value_counts()
fig, ax = plt.subplots()
ax.pie(qtrclass, labels=qtrclass.index, autopct='%1.1f%%')
plt.show()

sns.boxplot(x="GOOD", y="distance", data=nfl)
plt.title("Boxplot for GOOD VS distance")
plt.show()
# %%
# ## Question 6
# Using the SciKitLearn library, build a logistic regression model overall (not individual team or kicker) to predict the chances of a successful field goal. What variables do you have in your model?
Logit2 = glm(formula='GOOD~min+sec+distance+timerem', data=nfl, family=sm.families.Binomial())
Logit2Fit = Logit2.fit()
print(Logit2Fit.summary())

# When alpha = 0.05, only the p-value of "distance" is less than 0.05. Thus we only include "distance" in our model using the scikitlearn library.

#%%
xgood = nfl[['distance']]
ygood = nfl['GOOD']

x_traingood, x_testgood, y_traingood, y_testgood = train_test_split(xgood, ygood, random_state=1 )

goodlogit = LogisticRegression()
goodlogit.fit(x_traingood, y_traingood)
print('The coeficient of distance is',goodlogit.coef_)
print('The intercept of distance is',goodlogit.intercept_)

#%%
# ## Question 7
# Someone has a feeling that home teams are more relaxed and have a friendly crowd, they should kick better field goals. Use your model to find out if that is subtantiated or not.
nfl['ifhome'] = 0
boolhome = nfl['HomeTeam']==nfl['kickteam']
nfl['ifhome'] = nfl['ifhome'].add(boolhome)

Logitifhome = glm(formula='GOOD~C(ifhome)', data=nfl, family=sm.families.Binomial())
LogitFitifhome = Logitifhome.fit()
print(LogitFitifhome.summary())
#
#  When alpha = 0.05, the p-value of "ifhome" is 0.093. Thus we fail to reject the null hypothesis that the coefficient of "ifhome" is 0.
# %%
# ## Question 8
# From what you found, do home teams and road teams have different chances of making a successful field goal? If one does, is that true for all distances, or only with a certain range?

# According to the results of question 7, there is no different of making a successful field goal between two home teams and road teams. We also train models of different teams below, the coefficient of distance in two models are close.
hometeam = nfl[nfl['HomeTeam']==nfl['kickteam']]
roadteam = nfl[nfl['AwayTeam']==nfl['kickteam']]

xgoodhome = hometeam[['distance']]
ygoodhome = hometeam['GOOD']

x_traingoodhome, x_testgoodhome, y_traingoodhome, y_testgoodhome = train_test_split(xgoodhome, ygoodhome, random_state=1 )

goodhomelogit = LogisticRegression()
goodhomelogit.fit(x_traingoodhome, y_traingoodhome)
print('When being hometeams, the coeficient of distance is',goodhomelogit.coef_)
print('When being hometeams, the intercept of distance is',goodhomelogit.intercept_)
hometeam = nfl[nfl['HomeTeam']==nfl['kickteam']]
roadteam = nfl[nfl['AwayTeam']==nfl['kickteam']]

xgoodroad = roadteam[['distance']]
ygoodroad = roadteam['GOOD']

x_traingoodroad, x_testgoodroad, y_traingoodroad, y_testgoodroad = train_test_split(xgoodroad, ygoodroad, random_state=1 )

goodroadlogit = LogisticRegression()
goodroadlogit.fit(x_traingoodroad, y_traingoodroad)
print('When being roadteams, the coeficient of distance is',goodroadlogit.coef_)
print('When being roadteams, the intercept of distance is',goodroadlogit.intercept_)

# %%
# titanic.dropna()

import numpy,import statsmodels,from statsmodels


import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 5, 11)
y = x ** 2

# Functional method
plt.plot(x, y)
plt.xlabel('X Label')
plt.ylabel('Y Label')
plt.title('My graph')
#plt.show()

plt.subplot(1,2,1)
plt.plot(x,y,'r')
plt.subplot(1,2,2)
plt.plot(y,x,'b')
#plt.show()

# Object Oriented method
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 0.8, 0.8])
axes.plot(x, y)
axes.set_xlabel('My new X label')
axes.set_ylabel('My new Y label')
axes.set_title('My new Title')
#fig.show()

fig1 = plt.figure()
axes1 = fig1.add_axes([0.1, 0.1, 0.8, 0.8])
axes2 = fig1.add_axes([0.2, 0.5, 0.4, 0.3])
axes1.plot(x,y)
axes2.plot(y,x)
axes2.set_title('SMALLER PLOT')
axes1.set_title('LARGER PLOT')
#fig1.show()

fig, axes = plt.subplots(nrows=1, ncols=2) # Tuple unpacking
plt.tight_layout()
print(type(axes))
#for current_axis in axes:
#    current_axis.plot(x, y)
axes[0].plot(x, y)
axes[0].set_title('First Plot')
axes[1].plot(x, y)
axes[1].set_title('Second Plot')
#fig.show()

# Figure size and DPI
fig = plt.figure(figsize=(8, 2))
ax = fig.add_axes([0, 0, 1, 1])
ax.plot(x, y)
#fig.show()

# With subplots
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(8,2))
axes[0].plot(x, y)
axes[1].plot(y, x)
#fig.show()

# How to add legends
fig_new = plt.figure()
#ax1 = fig_new.add_axes([0, 0, 1, 1])
ax1 = fig_new.add_subplot(111) # 111 as the default value
ax1.plot(x, x**2, label='X Square', color='blue', linewidth=1, linestyle='--', marker='o', markerfacecolor='orange', markeredgecolor='yellow')
ax1.plot(x, x**3, label='X Cubed', color='green', alpha=0.5, linestyle='-.', marker='+', markersize=15, markeredgewidth=3)
ax1.set_title('My Graph')
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.legend(loc=0)

# Control over axis appearance and Plot range
ax1.set_xlim([0, 1])
ax1.set_ylim([0, 1])

fig_new.show()
# Saving a figure
fig_new.savefig('my_picture.jpg', dpi=300)

# Special Plot Types


def valor_presente(precio, cuotas, inflacion):
    inflacion_mensual = 1. / (inflacion / 100. + 1.)**(1. / 12.)
    return (precio/cuotas) * (1-inflacion_mensual**cuotas)/(1-inflacion_mensual)

precio = float(input("Precio: "))
cuotas = int(input("Cuotas sin interés: "))
inflacion = float(input("Inflación esperada mientras pagás las cuotas: "))
valor = valor_presente(precio, cuotas, inflacion)

print("Con esa inflación, ese plan de pagos es equivalente a pagar $" + \
    str(round(valor,2)) + " al contado.")

precio_contado = float(input("Precio al contado: "))

print("Te conviene pagarlo " + \
    ("al contado." if precio_contado < valor else "en cuotas."))
print("Te ahorrás $" + str(round(abs(precio_contado-valor),2)))

from scipy import optimize

def dif_contado(infla):
    return valor_presente(precio, cuotas, infla) - precio_contado

inflacion_equivalente = optimize.newton(dif_contado, .99)

print("Para que las dos formas de pago sean igualmente convenientes, la " + \
    "inflación debería ser de " + str(round(inflacion_equivalente,2)) + "%")
print("Si esperás que la inflación sea mayor, te conviene pagarlo en " + \
    "cuotas. Si no, al contado.")


'''
Monte Carlo simulation for calculating the avarage distance between any two points sampled uniformily on a circle of radius R.
'''
#import relevant libraries
import numpy as np
import matplotlib.pyplot as plt
from random import uniform
import pandas as pd

def sample_circle(number_of_points, R = 1, plot = False):
    '''
    Samples uniformly on a circle of radius R through the algorithm defined on the main paper.
    If plot = True, shows a scatter drawing of the sampled points and the circle of radius R
    Input: number_of_points, R = 1, plot = False
    Output: the x and y coordinates of the sampled points
    '''
    x_coord = []
    y_coord = []
    #sample on the square RxR until we get exactly number_of_points inside the circle
    while(len(x_coord))<number_of_points:
        x = uniform(-R, R)
        y = uniform(-R,R)

        #as defined in the main paper, we accept the sampled point if inside the circle of radius R, otherwise reject it
        if np.sqrt(x**2+y**2)< R:
            x_coord.append(x)
            y_coord.append(y)

    #plots the sampled points if plot = True, together with the circle of radius R
    if plot == True:
        x_circ = np.linspace(-R, R, 100)
        y_circ_pos = np.sqrt(R**2-x_circ**2)
        y_circ_neg = -np.sqrt(R**2-x_circ**2)

        plt.scatter(x_coord, y_coord)
        plt.plot(x_circ, y_circ_pos, c = 'r', linewidth=4)
        plt.plot(x_circ, y_circ_neg, c = 'r', linewidth=4)
        plt.axis("equal")
        plt.title('Uniform Sampling on The Circle of Radius R')
        plt.show()

    #transform the coordinates into numpy arrays
    x_coord = np.array(x_coord)
    y_coord = np.array(y_coord)
    x_coord = x_coord.reshape(-1,1)
    y_coord = y_coord.reshape(-1,1)
    coordinates = np.concatenate((x_coord,y_coord),axis=1)

    return coordinates

def estimator(number_of_points, R):
    '''
    Samples 2x(number_of_points) on circles of radius R, then calculates the Monte Carlo estimator and its standard deviaton,as
    described in the main paper
    Input: number_of_points, R
    Output: (Monte Carlo estimator, standard deviation of the Monte Carlo estimator)
    '''
    points_1 = sample_circle(number_of_points, R)
    points_2 = sample_circle(number_of_points, R)
    dist_vector = []
    #calculates the distance between points in each of the sampled circles
    for i in range(len(points_1)):
        for j in range(len(points_2)):
            dif = points_1[i]-points_2[j]
            dist_vector.append(np.sqrt(np.dot(dif,dif)))
    return (np.mean(dist_vector), np.std(dist_vector))


import numpy as np

# Collection of activation functions
# Reference: https://en.wikipedia.org/wiki/Activation_function

class Sigmoid():
    def __init__(self): pass

    def function(self, x):
        return 1 / (1 + np.exp(-x))

    def gradient(self, x):
        return self.function(x) * (1 - self.function(x))

class TanH():
    def __init__(self): pass

    def function(self, x):
        return 2 / (1 + np.exp(-2*x)) - 1

    def gradient(self, x):
        return 1 - np.power(self.function(x), 2)

class ReLU():
    def __init__(self): pass

    def function(self, x):
        # If x < 0: output = 0 else: output = x
        h = x
        h[x < 0] = 0
        return h

    def gradient(self, x):
        # If x < 0: output = 0 else: output = 1
        dx = np.zeros(np.shape(x))
        dx[x >= 0] = 1
        return dx

class LeakyReLU():
    def __init__(self): pass

    def function(self, x):
        # If x < 0: output = 0.01*x else: output = x
        h = x
        h[h < 0] = 0.01 * h[h < 0]
        return h

    def gradient(self, x):
        # If x < 0: output = 0.01 else: output = 1
        dx = np.ones(np.shape(x))
        dx[x < 0] = 0.01
        return dx

class ExpLU():
    def __init__(self, alpha=0.1):
        self.alpha = alpha

    def function(self, x):
        # If x < 0: output = alpha*(exp(x)-1) else: output = x
        h = x
        h[h < 0] = self.alpha * (np.exp(h[h < 0]) - 1)
        return h

    def gradient(self, x):
        # If x < 0: output = f(alpha, x) + alpha else: output = 1
        dx = self.function(x) + self.alpha
        dx[x >= 0] = 1
        return dx

class SoftPlus():
    def __init__(self): pass

    def function(self, x):
        return np.log(1 + np.exp(x))

    def gradient(self, x):
        return Sigmoid().function(x)


import numpy as np
from math import sqrt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.datasets import make_circles
import matplotlib.pyplot as plt
import pylab as pl

"""
Demonstrates how a linearly nonseparable dataset in R^2 can be
linearly separable in R^3 after a transformation via an appropriate
kernel function.

Generates a 2D non-separable dataset, and projects it to R^3 using
a polynomial kernel [x1, x2] -> [x1, x2, x1^2.0 + x2^2.0], where
it is now linearly separable in R^3.

Usage:
    $ python demo_data_transform.py
"""

def randrange(n, vmin, vmax):
    return (vmax-vmin)*np.random.rand(n) + vmin

def fn_kernel(x1, x2):
    """ Implements a kernel phi(x1,y1) = [x1, y1, x1^2 + y1^2] """
    return np.array([x1, x2, x1**2.0 + x2**2.0])

""" Generate linearly nonseparable dataset (in R^2) """

n = 200

X, Y = make_circles(n_samples=n, noise=0.07, factor=0.4)

A = X[np.where(Y == 0)]
B = X[np.where(Y == 1)]

X0_orig = A[:, 0]
Y0_orig = A[:, 1]

X1_orig = B[:, 0]
Y1_orig = B[:, 1]

frac0 = len(np.where(Y == 0)[0]) / float(len(Y))
frac1 = len(np.where(Y == 1)[0]) / float(len(Y))

print("Percentage of '0' labels:", frac0)
print("Percentage of '1' labels:", frac1)

A = np.array([fn_kernel(x,y) for x,y in zip(np.ravel(X0_orig), np.ravel(Y0_orig))])
X0 = A[:, 0]
Y0 = A[:, 1]
Z0 = A[:, 2]

A = np.array([fn_kernel(x,y) for x,y in zip(np.ravel(X1_orig), np.ravel(Y1_orig))])
X1 = A[:, 0]
Y1 = A[:, 1]
Z1 = A[:, 2]

def plot_no_decision_boundary():
    fig = plt.figure(figsize=(20,8))
    ax = fig.add_subplot(122, projection='3d')

    ax.scatter(X0, Y0, Z0, c='r', marker='o')
    ax.scatter(X1, Y1, Z1, c='b', marker='^')

    ax.set_xlabel('X Label')
    ax.set_ylabel('Y Label')
    ax.set_zlabel('Z Label')
    ax.set_title("Data in R^3 (separable)")

    # Project data to X/Y plane
    ax2d = fig.add_subplot(121)
    ax2d.scatter(X0, Y0, c='r', marker='o')
    ax2d.scatter(X1, Y1, c='b', marker='^')

    ax2d.set_xlabel('X Label')
    ax2d.set_ylabel('Y Label')
    ax2d.set_title("Data projected to R^2 (nonseparable)")

    plt.show()

def plot_decision_boundary():
    fig = plt.figure(figsize=(20,8))
    ax = fig.add_subplot(121, projection='3d')

    ax.scatter(X0, Y0, Z0, c='r', marker='o')
    ax.scatter(X1, Y1, Z1, c='b', marker='^')

    ax.set_xlabel('X Label')
    ax.set_ylabel('Y Label')
    ax.set_zlabel('Z Label')
    ax.set_title("Data in R^3 (separable w/ hyperplane)")

    x = np.arange(-1.25, 1.25, 0.1)
    y = np.arange(-1.25, 1.25, 0.1)
    X, Y = np.meshgrid(x, y)

    Z = np.zeros(X.shape)
    Z[:,:] = 0.5

    ax.plot_surface(X, Y, Z, color='#09F911')

    # Project data to X/Y plane
    ax2d = fig.add_subplot(122)
    ax2d.scatter(X0, Y0, c='r', marker='o')
    ax2d.scatter(X1, Y1, c='b', marker='^')

    ax2d.add_patch(pl.Circle((0,0), radius=sqrt(0.5),
                   fill=False, linestyle='solid', linewidth=4.0,
                   color='black'))
    ax2d.add_patch(pl.Circle((0,0), radius=sqrt(0.5),
                   fill=False, linestyle='dashed', linewidth=1.5,
                   color='#09F911'))

    ax2d.set_xlabel('X Label')
    ax2d.set_ylabel('Y Label')
    ax2d.set_title("Data projected to R^2 (hyperplane projection shown)")

    plt.show()

def main():
    print("...Projecting dataset to R^3 (no decision boundary)...")
    plot_no_decision_boundary()
    print("...Projecting dataset to R^3 (with decision boundary)...")
    plot_decision_boundary()
    print("...Done.")

if __name__ == '__main__':
    main()


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy import optimize

def func(x):
    return ((x[0] - 0.5)**2 + (x[1] + 0.5)**2)*np.cos(0.5*x[0]*x[1])

x_r = np.linspace(-1, 1)
y_r = np.linspace(-2, 2)

x, y = np.meshgrid(x_r, y_r)

z = func([x, y])

fig = plt.figure(tight_layout=True)
ax = fig.add_subplot(projection="3d")
ax.tick_params(axis="both", which="major", labelsize=9)
ax.set(xlabel="x", ylabel="y", zlabel="z")
ax.set_title("Objective function")

ax.plot_surface(x, y, z, alpha=0.7)

x0 = np.array([-0.5, 1.0])
ax.plot([x0[0]], [x0[1]], func(x0), "r*")

result = optimize.minimize(func, x0, tol=1e-6, method="Nelder-Mead")
print(result)

ax.plot([result.x[0]], [result.x[1]], [result.fun], "r*")

plt.show()


'''
A Pythagorean triplet is a set of three natural numbers, a < b < c, for which,

a^2 + b^2 = c^2
For example, 3^2 + 4^2 = 9 + 16 = 25 = 5^2.

There exists exactly one Pythagorean triplet for which a + b + c = 1000.
Find the product a*b*c.
'''

# Use diophantine equation solver from SymPy to solve diophantine (integer) equations.
#
# Manual steps:
#
# 1 - get general solution to x^2 + y^2 = z^2 - returns solution(s) in terms of integers p and q:
#     diophantine(x * x + y * y - z * z)
#     -> set([(2*p*q, p**2 - q**2, p**2 + q**2)])
#
# 2 - sub values for x, y, z (in terms of p & q) into x + y + z = limit to get another diophantine equation:
#     sum([2*p*q, p**2 - q**2, p**2 + q**2])
#     -> 2*p**2 + 2*p*q
#
# We now have a diophantine equation for the perimeter of a diophantine right triangle - so we get:
# 2 * p * q + 2 * p^2 = limit
# -> p * p + p * q = limit / 2
#
# Automation:
#
# Solve the diophantine equation, and get multiple candidates for p, q.
# For each one, substitute back in to get x, y, z.  In all cases, x * x + y * y = z * z; and x + y + z = limit
# so we need to find out which solutions actually form a real triangle: (x, y, z) all > 0

from sympy.solvers.diophantine import diophantine
from sympy import symbols

def main_body(limit):
    p, q = symbols('p q', integer = True)
    return [x * y * z for x, y, z in ((2 * a * b, a * a - b * b, a * a + b * b) for a, b in diophantine(p * p + q * p - limit / 2)) if x > 0 and y > 0][0]

def time_test(limit, count = 1000):
    import timeit
    return str(timeit.timeit(stmt = 'main_body(' + str(limit) + ')', setup = 'from __main__ import main_body', number = count) / count * 1000) + ' ms'

if __name__ == '__main__':
    assert main_body(12) == 60

    limit = 1000
    print time_test(limit, 300)
    result = main_body(limit)
    print result

    assert result == 31875000


from __future__ import division

import math
import numpy as np
import ittk_helpers as hlp
from ittk_exceptions import ITTKException
from ittk_helpers import probs
from ittk_helpers import cond_probs
from numpy import array, shape, where, in1d

# Note: All functions default to log base 2.

def check_prob_sum(arr):
    return int(sum(arr)) == 1

def entropy(x):
    x = probs(x)
    total = 0
    for x_i in x:
        if x_i == 0:
            continue
        total -= x_i * np.log2(x_i)
    return total

def mutual_information(x, y, normalized=False, base=2):
    """
    Compute the mutual information between two sets of observations.
    First converts observations to discrete conditional probability distribution, then computes their MI.

    :param x:
     List or numpy array.
    :param y:
     List or numpy array.
    :param normalized:
     Normalize the inputs. Defaults to False.
    :param base:
     The log base used in the MI calculation. Defaults to 2.
    :return:
     Float: the mutual information between x and y.
    """
    x = hlp.check_numpy_array(x)
    y = hlp.check_numpy_array(y)
    numobs = len(x)
    if numobs != len(y):
        raise Exception("Inputs not of matching length")
    mutual_info = 0.0
    uniq_x = set(x)
    uniq_y = set(y)
    for _x in uniq_x:
        for _y in uniq_y:
            px = shape(where(x == _x))[1] / numobs
            py = shape(where(y == _y))[1] / numobs
            pxy = len(where(in1d(where(x == _x)[0],
                                 where(y == _y)[0]) == True)[0]) / numobs
            if pxy > 0.0:
                mutual_info += pxy * math.log((pxy / (px * py)), base)
    if normalized: mutual_info = mutual_info / np.log2(numobs)
    return mutual_info

# Variation of information
def information_variation(x, y):
    """

    :param x:
     List or numpy array
    :param y:
     List or numpy array
    :return:
     Float: the information variation of x and y
    """
    x = hlp.check_numpy_array(x)
    y = hlp.check_numpy_array(y)
    return entropy(x) + entropy(y) - (2 * mutual_information(x, y))

def kldiv(X, Y, isprobs=False):
    if isprobs == False:
        p = probs(X)
        q = probs(Y)
    else:
        p = X
        q = Y
    p, q = hlp.match_arrays(p, q)
    logpq = np.array([])
    for i in range(len(p)):
        if q[i] == 0 or p[i] == 0:
            logpq = np.append(logpq, 0)
        else:
            logpq = np.append(logpq, np.log2(p[i] / q[i]))
    kldivergence = np.dot(p, logpq)
    return kldivergence

def lag(x, y, lag_points=1):
    """
    Example:

    x, y = lag(x, y, 5)

    Note: this will reduce the length of the sequence by the number of lag points; in the above case, 5.

    :param x:
     numpy array
    :param y:
     numpy array
    :param lag_points:
     Integer. Number of places to shift by. Defaults to 1.
    :return:
     Tuple: (numpy array, numpy_array)
    """
    for i in range(lag_points):
        x.pop(0)
        y.pop()
    return x, y

def lagged_mutual_information(x, y, lag_points=1):
    """
    Convenience method to lag and do mutual information in one shot.

    :param x:
     numpy array
    :param y:
     numpy array
    :param lag_points:
     Integer. Number of places to shift by. Defaults to 1.
    :return:
     Float: the mutual information of x and y, after lag is applied.
    """
    x, y = lag(x, y, lag_points)
    return mutual_information(x, y)

def tsallis_entropy(X, entropic_index=0.99):
    """
    :param X:
        Python list or Numpy array. Should sum to 1.
    :param entropic_index:
        Real number [0, 1]. Defaults to 0.99.
    :return:
        Returns the real-valued Tsallis entropy.
    :raises:
        Raises generic ITTKException if 1 is passed, since this would result in a division by zero.
    """
    if int(entropic_index) == 1:
        raise ITTKException("Entropic index, 'q', cannot be 1 for tsallis entropy. Must be on real interval (0, 1).")
    return (1.0/(entropic_index-1)) * (1.0 - sum([p ** entropic_index for p in X]))


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

np.random.seed(0)

#mean = 50 * exp(0.1 * 3) = 67.49294
#var = 580.786

x0 = 50
T = 3
mu = 0.1
sigma = 0.2
time_step = 0.01
n_steps = int(T / time_step)

n_runs = 10**2
n_runs_to_plot = 5
delta_bm = np.random.normal(0, math.sqrt(time_step), (n_steps, n_runs))
geometric_bm = np.zeros((n_steps+1, n_runs))
geometric_bm[0,:] = x0

for i in np.arange(0, n_steps):
	geometric_bm[i+1,:] = geometric_bm[i,:] * (1 + mu * time_step + sigma * delta_bm[i])

mean = np.mean(geometric_bm[n_steps-1,:])
var = np.var(geometric_bm[n_steps-1,:])
print('mean: %s' % mean)
print('var: %s' % var)
plt.plot(np.linspace(0, T, n_steps+1), geometric_bm[:,0:(n_runs_to_plot-1)])
plt.axis([0, T, 0, geometric_bm[:,0:(n_runs_to_plot-1)].max()+5])
plt.ylabel('X_t')
plt.xlabel('Time')
#plt.show()

################
## correlated GBM
################
T = 60
time_step = 0.1
n_steps = int(T / time_step)
dim = 5
n_runs = 10**5
x0 = np.array([50, 20, 30, 45, 70])
mus = np.array([0.1,0.05,0.12,0.2,0.08])
sigmas = np.array([0.2, 0.15, 0.3, 0.12, 0.15])

#upper = np.matrix([[0,0.5,0.5,0.5,0.5],[0,0,0.5,0.5,0.5],[0,0,0,0.5,0.5],[0,0,0,0,0.5],[0,0,0,0,0]])
W = np.random.rand(dim, dim)
#B is pos. def. by Gershgorin's theorem

#use for positive correlation
B = np.dot(W, np.transpose(W))

#use for negative correlation
#B = np.dot(W, np.transpose(W)) + 3*np.diag([2, 3, 3, 5, 4])

E = np.diag(np.sqrt(1./np.diagonal(B))) #used to put ones on the diagonals
corr = E.dot(B).dot(E)
print('\nCorrelation matrix: ')
print(corr)
L = np.linalg.cholesky(corr)

prices_at_final_time = np.empty((dim, n_runs))

delta_bm = np.random.normal(0, math.sqrt(time_step), (dim, n_runs, n_steps))

for d in np.arange(0, dim):
	corr_geometric_bm = np.zeros((n_steps+1, n_runs))
	corr_geometric_bm[0,:] = x0[d]

	for i in np.arange(0, n_steps):
		#for delta_corr_bm: rows = which time point, columns = which run
		delta_corr_bm = np.transpose(L[d,:].dot(delta_bm[:,:,i]))
		corr_geometric_bm[i+1,:] = corr_geometric_bm[i,:] * (1 + mus[d] * time_step + sigmas[d] * delta_corr_bm)

	prices_at_final_time[d,:] = corr_geometric_bm[-1,:]

print('\nExpected means: ')
expected_means = x0 * np.exp(mus * T)
print(expected_means)

print('\nSample means: ')
sample_means = np.mean(prices_at_final_time, axis = 1)
print(sample_means)

print('\nSample correlation coefficients:')
corr_coeffs = np.corrcoef(prices_at_final_time, rowvar = 1)
print(corr_coeffs)

plt.plot(np.linspace(0, T, n_steps+1), corr_geometric_bm[:,0:(n_runs_to_plot-1)])
plt.axis([0, T, 0, corr_geometric_bm[:,:].max()])
plt.ylabel('X_t')
plt.xlabel('Time')
#plt.show()


import numpy as np

__author__ = "Vincent Levorato"
__credits__ = "https://github.com/octo-technology/bdacore"
__license__ = "Apache 2.0"

def mean_absolute_deviation(x):
    """
    Mean Absolute Deviation
    """
    return np.nanmean(np.abs(x - np.nanmean(x)))

def median_absolute_deviation(x):
    """
    Median Absolute Deviation
    """
    return np.nanmedian(np.abs(x - np.nanmedian(x)))

def double_median_absolute_deviation(x):
    """
    If the underlying distribution of the data is unsymmetric, one should use the double median
    absolute deviation, instead of the classic mad.

    Parameters
    ----------
    x : array-like

    Returns
    -------
    Couple of values: left mad, right mad

    """
    median = np.nanmedian(x)
    absolute_deviation = np.abs(x - median)

    left_mad = np.nanmedian(absolute_deviation[absolute_deviation <= median])
    right_mad = np.nanmedian(absolute_deviation[absolute_deviation >= median])

    return left_mad, right_mad

def mad_outliers(x, cutoff=2, z_score_coeff=0.6745):
    """
    Median Absolute Deviation outliers method.

    It returns values as outliers if their residuals relative to the median exceeds 'n' times the ratio
    to the MAD. The cutoff parameter represents this 'n' times quantity.

    Main code: x_mad = np.abs(x - np.median(x)) / mad_value
    if x_mad contains a value above the cutoff parameter, it is considered as an outlier.

    Parameters
    ----------
    x : array-like

    cutoff : int, optional (default=2)
        amount of times residuals relative to the median exceed the ratio to the MAD

    z_score_coeff : float, optional (default=0.6745)
        0.6745 is the 0.75th quartile of the standard normal distribution, to which the MAD converges to.

    Returns
    -------
    Boolean array with outlier tag

    """
    mad_value = median_absolute_deviation(x)
    if mad_value == 0:
        return x * np.nan

    x_mad = (z_score_coeff * np.abs(x - np.nanmedian(x))) / mad_value
    return x_mad > cutoff

def fft_outliers(x, freq_cut_index=None, outlier_proportion=0.1):
    """
    Fast Fourier Transformation outliers method.

    The idea is to isolate high frequencies in frequency domain, i.e. fast variations. Isolating such frequencies
    allows to identify abrupt change in numerical series, which are considered here as outliers.

    Parameters
    ----------
    x : array-like

    freq_cut_index : int, optional (default=None)
        set the index in the FFT numpy array beyond which the frequency is considered to be filtered. The highest,
        the less filtering. By default, it sets this index to 0.9 of x length

    outlier_proportion : float, optional (default=0.1)
        set the proportion of outliers to return

    Returns
    -------
    Boolean array with outlier tag

    """
    if freq_cut_index is None:
        freq_cut_index = int(0.9 * float(len(x)))

    fft_x = np.fft.fft(x)
    for freq_index in range(0, len(fft_x)):
        if freq_index >= freq_cut_index:
            fft_x[freq_index] = 0

    ifft_x = np.real(np.fft.ifft(fft_x).round())
    diff_fft_x = np.abs(x - ifft_x)
    outlier_amount = int(len(x) * outlier_proportion)
    outliers = np.zeros(len(x), dtype='bool')
    for i in range(0, outlier_amount):
        position_outlier = np.argmax(diff_fft_x)
        outliers[position_outlier] = True
        diff_fft_x[position_outlier] = -1

    return outliers


import numpy as np
import matplotlib.pyplot as plt

def pdf(costh, P_mu):
    # define our probability density function
    return 0.5 * (1.0 - 1.0 / 3.0 * P_mu * costh)

def acc_rej(N_measurements, P_mu):
    x = np.random.uniform(-1.0, 1.0, size=N_measurements)

    x_axis = np.linspace(-1.0,1.0, 1000)
    fmax= np.amax(pdf(x_axis,P_mu)) #find the maximum of the function

    u = np.random.uniform(0, fmax, size= N_measurements)

    # we use a mask in order to reject the values we don't want
    data_pdf = x[u < pdf(x,P_mu)]
    return data_pdf

N_measurements=100000

first_experiment=acc_rej(N_measurements,0.5)
mean = np.mean(first_experiment)
P_mu_first=-9.*mean
print(P_mu_first)

P_mu_est = np.array([])
N_experiments=0
while N_experiments < 1000:
    data_pdf = acc_rej(N_measurements, P_mu_first)
    mean = np.mean(data_pdf)
    P_mu_est = np.append(P_mu_est,-9. * mean)
    N_experiments += 1

variance = np.var(P_mu_est)
sigma = np.sqrt(variance)
print(variance)
print(sigma)

plt.hist(P_mu_est, bins=30, histtype="step", color="black", linewidth="1")
plt.xlabel(r'$\hat{P}_\mu$')
plt.ylabel(r'$N(\hat{P}_\mu)$')

# plt.savefig(r"C:\Users\Aleix López\Desktop\acc_rej.jpg")
plt.show()

Projects/Probability and Monte Carlo Techniques/_3_variance_parameter.py


## Import modules
import numpy as np
import scipy.linalg as la

'''
Description.
Function used to compute the center of the circle given two points and a
radius. Takes only real inputs and gives only real outputs.
Function originally named: circ_cent.m for MatLab(R)
By: Chiran 2010, BST license

# Input(s):
First circle point (pt1Vec).

Second circle point (pt2Vec).

Circle radius (radius).

# Output(s):
Row vector of the point where the first possible center (centerVec1);

Row vector of the point where the second possible center (centerVec2).

# Example1:
pt1Vec = np.array([40, 12]); pt2Vec = np.array([4.347, 24]); radius = 34.4848
One of the solutions is: np.array([31.3936, 45.3936]).

---
centerVec1, centerVec2 = circleby2ptsradius(pt1Vec, pt2Vec, radius)
'''
def circleby2ptsradius(pt1Vec, pt2Vec, radius):

    if radius < la.norm(pt1Vec-pt2Vec)/2:
        centerVec1 = np.array(['NaN', 'NaN'])
        centerVec2 = np.array(['NaN', 'NaN'])
    else:
        a = pt1Vec[0]**2-pt2Vec[0]**2
        b = pt1Vec[1]**2-pt2Vec[1]**2
        c = -2*(pt1Vec[0]-pt2Vec[0])
        d = -2*(pt1Vec[1]-pt2Vec[1])
        e = a+b
        Coeff1 = 1+(d/c)**2
        Coeff2 = ((2*d*e/c**2) +(pt2Vec[0]*2*d/c) -2*pt2Vec[1])
        Coeff3 = ((e/c)**2+(2*pt2Vec[0]*e/c)+pt2Vec[0]**2+pt2Vec[1]**2-\
            radius**2)

        All_coeff = np.array([Coeff1, Coeff2, Coeff3])
        Eq_root = np.roots(All_coeff)

        # centersArray--center of the circle. It is a 2x2 matrix. First row
        # represents first possible center (x1, y1) and second row is the
        # second possible center.
        centersArray = np.zeros((len(Eq_root),2))

        for i in list(range(len(Eq_root))):
            x = -(e+d*Eq_root[i])/c
            centersArray[i,0] = x
            centersArray[i,1] = Eq_root[i]

        # Check if values in centerArray are real or unreal numbers
        if la.norm(centersArray.imag) != 0:
            print('Circle with the specified radius does not pass through',
                'specified points', sep=" ")
            centerVec1 = np.array(['NaN', 'NaN'])
            centerVec2 = np.array(['NaN', 'NaN'])
        else:
            centerVec1 = centersArray[0, :]
            centerVec2 = centersArray[1, :]

    return centerVec1, centerVec2
'''
BSD 2 license.

Copyright (c) 2016, Universidad Nacional de Colombia, Ludger O.
   Suarez-Burgoa and Exneyder Andrés Montoya Araque.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''


#!/usr/bin/python
# -*- coding: utf8 -*-
import sys, math, numpy
from decimal import *
from math import factorial
from time import time
from numba import jit

# http://thelivingpearl.com/2013/05/28/computing-pi-with-python/
# http://www.numberworld.org/misc_runs/pi-5t/details.html

# Bellard's Formula PI

#@jit(nopython=True) # Set "nopython" mode for best performance, equivalent to @njit
def bellard(n):
    pi = Decimal(0)
    k = 0
    while k < n:
      pi += (Decimal(-1)**k/(1024**k))*( Decimal(256)/(10*k+1) + Decimal(1)/(10*k+9) - Decimal(64)/(10*k+3) - Decimal(32)/(4*k+1) - Decimal(4)/(10*k+5) - Decimal(4)/(10*k+7) - Decimal(1)/(4*k+3))
      k += 1
    pi = pi * 1/(2**6)
    return pi

# Bailey-Borwein-Plouffe formula

#@jit(nopython=True)
def bbp(n):
    pi = 0
    k = 0
    while k < n:
        pi += (Decimal(1)/(16**k))*((Decimal(4)/(8*k+1))-(Decimal(2)/(8*k+4))-(Decimal(1)/(8*k+5))-(Decimal(1)/(8*k+6)))
        k += 1
    return pi

# http://www.craig-wood.com/nick/articles/pi-chudnovsky/

#@jit(nopython=True) # Set "nopython" mode for best performance, equivalent to @njit
def chudnovsky(n):
    pi = Decimal(0)
    k = 0
    while k < n:
        pi += (Decimal(-1)**k)*(Decimal(factorial(6*k))/((factorial(k)**3)*(factorial(3*k)))* (13591409+545140134*k)/(640320**(3*k)))
        k += 1
    pi = pi * Decimal(10005).sqrt()/4270934400
    pi = pi**(-1)
    return pi

@jit(nopython=True)
def chudnovsky2(n):
    pi = 13591409
    ak = 1
    k = 1
    while k < n:
        ak *= -((6*k-5)*(2*k-1)*(6*k-1))/(k*k*k*26680*640320*640320)

        val = ak*(13591409 + 545140134*k)

        #d = Decimal((6*k-5)*(2*k-1)*(6*k-1))/Decimal(k*k*k*26680*640320*640320)

        pi += val
        k += 1
    pi = pi * numpy.sqrt(10005)/4270934400.
    pi = pi**(-1)
    return pi

# arctan
# http://www.craig-wood.com/nick/articles/pi-machin/

def arctan(x):
    """
    Calculate arctan(1/x)

    arctan(1/x) = 1/x - 1/3*x**3 + 1/5*x**5 - ... (x >= 1)

    This calculates it in fixed point, using the value for one passed in
    """
    x2 = x*x
    x = Decimal(x)

    total = Decimal(0)
    sign = 1
    for i in range(1, 512, 2):
        #total += sign / (i * x)
        total += sign / (i * x ** i)
        sign = -sign
        #x *= x2
        #print(total)
    return total

def pi_machin(n):
    return 4*(4*arctan(5) - arctan(239))

def pi_gauss(one):
    return 4*(12*arctan(18) + 8*arctan(57) - 5*arctan(239))

if __name__ == "__main__":

  N = 768 # вывод до числа Фейнмана

  getcontext().prec = N

  start_all = time()

  print("Atan")
  start = time()
  my_pi = pi_machin(N)
  print("Pi = {}".format(str(my_pi)))
  print("Time {} ms".format(float(round((time()-start)*1e6)/1e3)))

  print("\nBBP")
  start = time()
  my_pi = bbp(N)
  print("Pi = {}".format(str(my_pi)))
  print("Time {} ms".format(float(round((time()-start)*1e6)/1e3)))

  print("\nBellard")
  start = time()
  my_pi = bellard(N)
  print("Pi = {}".format(str(my_pi)))
  print("Time {} ms".format(float(round((time()-start)*1e6)/1e3)))

  print("\nChudnovsky")
  start = time()
  my_pi = chudnovsky(N/14)
  print("Pi = {}".format(str(my_pi)))
  print("Time {} ms".format(float(round((time()-start)*1e6)/1e3)))

  print("\nChudnovsky2")
  start = time()
  my_pi = chudnovsky2(N/14)
  print("Pi = {}".format(str(my_pi)))
  print("Time {} ms".format(float(round((time()-start)*1e6)/1e3)))

  print("\nAll Time {} millisecond, {} microseconds, and {} nanoseconds".format(int(round((time()-start_all)*1e6)/1e3),
  	int((float(round((time()-start_all)*1e6)/1e3)-int(round((time()-start_all)*1e6)/1e3))*1000),
  	abs(int((float(round((time()-start_all)*1e9)/1e3)-int(round((time()-start_all)*1e9)/1e3))*100))))


import numpy as np

class Perceptron:
    def __init__(self, num_features):
        self.num_features = num_features
        self.weights = np.zeros((num_features, 1), dtype=np.float64)
        self.bias = np.zeros(1, dtype=np.float64)

    def activation_function(self, linear):
        return np.where(linear > 0., 1, 0)

    def forward(self, x):
        linear = np.dot(x, self.weights) + self.bias
        predictions = self.activation_function(linear)
        return predictions

    def backward(self, x, y):
        predictions = self.forward(x)
        errors = y - predictions
        return errors

    def train(self, x, y, epochs):

        for _ in range(epochs):
            for i in range(y.shape[0]):
                errors = self.backward(x[i].reshape(1, self.num_features), y[i]).reshape(-1)
                self.weights += (errors*x[i]).reshape(self.num_features, 1)
                self.bias += errors

    def evaluate(self, x, y):
        predictions = self.forward(x).reshape(-1)
        accuracy = np.sum(predictions == y) / y.shape[0]
        return accuracy

if __name__ == "__main__":
    from data import X_train, y_train, X_test, y_test

    perceptron = Perceptron(num_features=2)
    perceptron.train(X_train, y_train, epochs=5)

    train_acc = perceptron.evaluate(X_train, y_train)
    print(f"Train set accuracy: {(train_acc*100):.2f}%")

    test_acc = perceptron.evaluate(X_test, y_test)
    print(f"Test set accuracy: {(test_acc*100):.2f}%")


"""
Largest prime factor
Problem 3
The prime factors of 13195 are 5, 7, 13 and 29.

What is the largest prime factor of the number 600851475143 ?
"""
import numpy as np

def find_smallest_prime_factor(number):
    upper_bound = int(np.sqrt(number)) + 1

    for i in range(2, upper_bound):
        if number % i == 0:
            return i

    return number

def find_largest_prime_factor(number):
    while True:
        smallest_factor = find_smallest_prime_factor(number)

        if smallest_factor < number:
            number //= smallest_factor
        else:
            return number

result = find_largest_prime_factor(600851475143)
print('result: ', result)

"""
6857
"""


from numpy import random

EPOCHS = 40_000
RANDOM_ADJUSTMENTS = 32
STEP_SIZE = 0.0001
VERBOSE = False

def cost_function(outputs, expected_values):
    total_cost = 0

    # evaluate the sum of every
    # output comparing it to the
    # expected value, squaring
    # the difference
    for o, y in zip(outputs, expected_values):
        total_cost += (o - y) * (o - y) # () ** 2 is stupid

    # return the sum divided
    # by the number of outputs
    return total_cost / len(outputs)

def evaluate_outputs(coeff, y_int, inputs):
    # compute the outputs by
    # plugging the inputs in the
    # current line to get the outputs
    return [coeff * x + y_int for x in inputs]

def min_random_adjustment(coeff, y_int, inputs, expected_values):
    # generating random numbers
    # without using the random
    # standard library because it'ss dumb
    random_adj_coeff = [random.normal(0, 1) * STEP_SIZE for _ in range(RANDOM_ADJUSTMENTS)]
    random_adj_y_int = [random.normal(0, 1) * STEP_SIZE for _ in range(RANDOM_ADJUSTMENTS)]

    average_costs = []

    # compute the average cost
    # of every single adjustment
    for idx, (rand_coeff, rand_y_int) in enumerate(zip(random_adj_coeff, random_adj_y_int)):
        # compute the outputs with the adjustments
        # applied to the current values
        outputs_adj = evaluate_outputs(coeff + rand_coeff, y_int + rand_y_int, inputs)

        # compute the current average cost
        average_cost_adj = cost_function(outputs_adj, expected_values)

        average_costs.append((idx, average_cost_adj))

    index = min(average_costs, key=lambda t: t[1])[0]

    # return the best adjustments
    return random_adj_coeff[index], random_adj_y_int[index]

dataset = [(-2, 5), (3, 7), (6, 8.2), (-10, 1.8), (-5, 3.8)]

# extract the input
# and the output vectors
# from the given dataset
inputs = [x for x, _ in dataset]
expected_values = [y for _, y in dataset]

# starting values
random_coeff = 3.6
random_y_int = 5.2

for epoch in range(EPOCHS):
    # compute the best adjustments
    best_adj_coeff, best_adj_y_int = min_random_adjustment(random_coeff, random_y_int, inputs, expected_values)

    # implement the adjustments
    random_coeff += best_adj_coeff
    random_y_int += best_adj_y_int

    # print the current average cost
    if VERBOSE:
        # compute the outputs
        outputs = evaluate_outputs(random_coeff, random_y_int, inputs)

        # compute the average cost
        average_cost = cost_function(outputs, expected_values)

        print(f"Epoch number {epoch + 1}, average cost is: {average_cost:.15f}") # should decrease with each epoch

# print the final result
print(random_coeff, random_y_int)