import numpy as np
from alphaomega.utils.exceptions import WrongDimension, WrongAttribute
from typing                      import Union

class LinearRegression:
    """
    Linear regression is a supervised regression algorithm.
    """
    def __init__(self):
        self.__W             = np.array([])
        self.__bias          = False
        self.__regularizator = None
        self.__penalizer     = 0.1

    def config(self, **kwargs) -> None:
        """
        Usage: use this method to configure the parameters of the LinearRegression instantiation.

        Inputs:
            regularizator: if "no_reg", no regularization is used. Other option is "ridge".
            penalizer    : penalizer is the regularization coefficent. It is ignored in the case of no regularization.
            bias         : If the bias is present in your features, flag this input as True, otherwise False.

        Returns: Nothing
        """
        for key, value in kwargs.items():

            if key == "bias":
                self.__bias = value

            elif key == "regularizator":
                if value not in ["no_reg", "ridge"]:
                    raise WrongAttribute("regularizator can only be 'no_reg' or 'ridge'.")
                self.__regularizator = value

            elif key == "penalizer":
                self.__penalizer = value

    def get(self, attribute: str) -> Union[np.ndarray, float]:
        """
        Usage: Use this method to get the attribute of interest.

        Inputs:
            attribute: The attribute of interest. It could be "weights" or "intercept".

        Returns: The desired attribute
        """
        if attribute == "intercept":
            return self.__W[0][0]

        if attribute == "weights":
            return self.__W[1:].reshape(-1)

        raise WrongAttribute("The specified attribute is not valid. Acceptable attributes are 'wights' and 'intercept'")

    def train(self, train_data: np.ndarray, labels: np.ndarray) -> None:
        """
        Usage  : Use this method to train the LinearRegression model. The trained parameteres are:
            weights  : The weights of the model
            intercept: The bias of the model

        Inputs :
            train_data: The feature matrix used to train the model.
            labels    : The corresponding labels for each data point in data.

        Returns: Nothing
        """
        #cheking if the shape of the input is correct.
        if (len(train_data.shape) != 2):
            raise WrongDimension("please provide featuers as a tabular shape: 2 dimensional. rows are samples and colums are features.")

        labels.reshape(-1, 1)
        data_process = train_data.copy()

        #checking if bias is present in feature maps
        if not self.__bias:
            bias_input   = np.ones((train_data.shape[0], 1))
            data_process = np.concatenate((bias_input, data_process), axis=1)

        #calculating the weights matrix
        if self.__regularizator == "no_reg":
            self.__W = np.linalg.inv(data_process.transpose() @ data_process) @ data_process.transpose() @ labels

        elif self.__regularizator == "ridge":
            L        = np.zeros((data_process.shape[1], data_process.shape[1]))
            L[0,0]   = 1
            L        = np.eye(data_process.shape[1]) - L
            self.__W = np.linalg.inv(data_process.transpose() @ data_process + self.__penalizer * L) @ data_process.transpose() @ labels

    def apply(self, test_data: np.ndarray, bias: bool = False) -> np.ndarray:
        """
        Usage: Use this method to evalute your LinearRegression Model on test data.

        Inputs:
            test_data: test data to be evaluated
            bias     : If the bias is present in your features, flag this input as True, otherwise False.

        Returns:
            - a numpy array which contains the labels for each data point in test data.

        """
        #cheking if the shape of the input is correct.
        if (len(test_data.shape) != 2):
            raise WrongDimension("please provide featuers as a tabular shape: 2 dimensional. rows are samples and colums are features.")

        data_process = test_data.copy()
        #checking if bias is present in feature maps
        if not bias:
            #checking if the features counts is correct
            if test_data.shape[1] != len(self.weights):
                raise WrongDimension("Number of features should be the same as training data.")

            bias_input   = np.ones((test_data.shape[0], 1))
            data_process = np.concatenate((bias_input, data_process), axis=1)

        else:
            #checking if the features counts is correct
            if test_data.shape[1] != len(self.__W.reshape(-1)):
                raise WrongDimension("Number of features should be the same as training data.")

        predict = data_process @ self.__W
        return predict.reshape(-1)

def linear_regression_train(train_data: np.ndarray, labels: np.ndarray, regularizator: str = "no-reg", penalizer: float = 0.1, bias: bool = False) -> np.ndarray:
    """
    Usage  : Use this function to train a linear regression model. You can use apply_linear_regression_func to predict the labels for test set with the help of the output of this funcion.

    Inputs :
        train_data   : The feature matrix used to train the model.
        labels       : The corresponding labels for each data point in data.
        reqularizator: if "no_reg", no regularization is used. Other option is "ridge".
        penalizer    : penalizer is the regularization coefficent. It is ignored in the case of no regularization.
        bias         : If the bias is present in your features, flag this input as True, otherwise False.

    Returns:
        - The weights matrix (bias included).
    """
    #cheking if the shape of the input is correct.
    if (len(train_data.shape) != 2):
        raise WrongDimension("please provide featuers as a tabular shape: 2 dimensional. rows are samples and colums are features.")

    labels.reshape(-1, 1)
    data_process = train_data.copy()

    #checking if bias is present in feature maps
    if not bias:
        bias_input   = np.ones((train_data.shape[0], 1))
        data_process = np.concatenate((bias_input, data_process), axis=1)

    #calculating the weights matrix
    if regularizator == "no-reg":
        weights = np.linalg.inv(data_process.transpose() @ data_process) @ data_process.transpose() @ labels

    elif regularizator == "ridge":
        L = np.zeros((data_process.shape[1], data_process.shape[1]))
        L[0,0] = 1
        L = np.eye(data_process.shape[1]) - L
        weights = np.linalg.inv(data_process.transpose() @ data_process + penalizer * L) @ data_process.transpose() @ labels

    else:
        raise WrongAttribute("regularizator can only be 'no_reg or 'ridge'.")

    return weights

def linear_regression_apply(test_data: np.ndarray, weights: np.ndarray, bias: bool = False):
    """
    Usage: Use this function to evalute your linear regression model on test data.

    Inputs:
        test_data: test data to be evaluated.
        weights  : The weights matrix to be applied to test data. Use train_linear_regression_func function to obtain this variable.
        bias     : If the bias is present in your features, flag this input as True, otherwise False.

    Returns:
        - a numpy array which contains the labels for each data point in test data.
    """
    #cheking if the shape of the input is correct.
    if (len(test_data.shape) != 2):
        raise WrongDimension("please provide featuers as a tabular shape: 2 dimensional. rows are samples and colums are features.")

    data_process = test_data.copy()
    #checking if bias is present in feature maps
    if not bias:
        #checking if the features counts is correct
        if test_data.shape[1] != (len(weights.reshape(-1))-1):
            raise WrongDimension("Number of features should be the same as training data.")

        bias_input   = np.ones((test_data.shape[0], 1))
        data_process = np.concatenate((bias_input, data_process), axis=1)
    else:
        #checking if the features counts is correct
        if test_data.shape[1] != len(weights.reshape(-1)):
            raise WrongDimension("Number of features should be the same as training data.")

    predict = data_process @ weights
    return predict.reshape(-1)


import math
import pandas as pd
import numpy as np

CC_LON = -8.615393063941816
CC_LAT = 41.15767687592546

def calHarDist(lat1, lon1, lat2, lon2):  # generally used geo measurement function
    R = 6378.137 # Radius of earth in KM
    dLat = lat2 * math.pi / 180 - lat1 * math.pi / 180
    dLon = lon2 * math.pi / 180 - lon1 * math.pi / 180
    a = math.sin(dLat/2) * math.sin(dLat/2) +\
    math.cos(lat1 * math.pi / 180) * math.cos(lat2 * math.pi / 180) *\
    math.sin(dLon/2) * math.sin(dLon/2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    d = R * c
    return d * 1000 # meters

def one_hot(df, column_type):
    if column_type == 0:
        column = 'CALL_TYPE'
    elif column_type == 1:
        column = 'ACTUAL_DAYTYPE'
    one_hot = pd.get_dummies(df[column])
    one_hot = one_hot.rename(columns={"A": column + "_A", "B": column + "_B", "C":column + "_C"})
    return one_hot

def heading(p1, p2):
    p1 = np.radians(p1)
    p2 = np.radians(p2)
    lat1, lon1 = p1[0], p1[1]
    lat2, lon2 = p2[0], p2[1]
    aa = np.sin(lon2 - lon1) * np.cos(lat2)
    bb = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(lon2 - lon1)
    return np.arctan2(aa, bb)


#!/usr/local/Cellar/python/2.7.6/bin/python
# -*- coding: utf-8 -*-

import sys
import scipy.optimize, scipy.special
from numpy import *
from matplotlib import pyplot, cm
from mpl_toolkits.mplot3d import Axes3D

def plot( data ):
	positives  = data[data[:,2] == 1]
	negatives  = data[data[:,2] == 0]

	pyplot.xlabel("Exam 1 score")
	pyplot.ylabel("Exam 2 score")
	pyplot.xlim([25, 115])
	pyplot.ylim([25, 115])

	pyplot.scatter( negatives[:, 0], negatives[:, 1], c='y', marker='o', s=40, linewidths=1, label="Not admitted" )
	pyplot.scatter( positives[:, 0], positives[:, 1], c='b', marker='+', s=40, linewidths=2, label="Admitted" )
	pyplot.legend()

def plotBoundary( data, X, theta ):
	plot( data )
	plot_x = array( [min(X[:,1]), max(X[:,1])] )
	plot_y = (-1./ theta[2]) * (theta[1] * plot_x + theta[0])
	pyplot.plot( plot_x, plot_y )

def sigmoid( z ):
	return scipy.special.expit(z)
	# return 1.0 / (1.0 + exp( -z ))

def computeCost( theta, X, y ):
	m = shape( X )[0]
	hypo = sigmoid(X.dot( theta ))
	term1 = log( hypo ).T.dot( -y )
	term2 = log( 1.0 - hypo ).T.dot( 1-y )
	return ((term1 - term2) / m).flatten()

def gradientCost( theta, X, y ):
	m = shape(X)[0]
	return ( X.T.dot(sigmoid( X.dot( theta ) ) - y)  ) / m

def costFunction( theta, X, y ):
	cost 	 = computeCost( theta, X, y )
	gradient = gradientCost( theta, X, y )
	# m 	  	 = shape( X )[0]
	# hypo  	 = sigmoid( X.dot( theta ) )
	# term1 	 = log( hypo ).transpose().dot( -y )
	# term2 	 = log( 1.0 - hypo ).transpose().dot( 1 - y )
	# cost  	 = ((term1 - term2) / m)
	# gradient = (X.transpose().dot( hypo - y) ) / m
	return cost

def findMinTheta( theta, X, y ):
	result = scipy.optimize.fmin( costFunction, x0=theta, args=(X, y), maxiter=500, full_output=True )
	return result[0], result[1]

def predict( theta, X, binary=True ):
	prob = sigmoid( theta.dot( X ))
	if binary :
		return 1 if prob > 0.5 else 0
	else:
		return prob

def part1_1():
	data = genfromtxt( "/Users/saburookita/Downloads/mlclass-ex2-004/mlclass-ex2/ex2data1.txt", delimiter = ',' )
	m, n = shape( data )[0], shape(data)[1] - 1
	X 	 = c_[ ones((m, 1)), data[:, :n] ]
	y 	 = data[:, n:n+1]

	plot( data )
	pyplot.show()

def part1_2():
	data  = genfromtxt( "/Users/saburookita/Downloads/mlclass-ex2-004/mlclass-ex2/ex2data1.txt", delimiter = ',' )
	m, n  = shape( data )[0], shape(data)[1] - 1
	X 	  = c_[ ones((m, 1)), data[:, :n] ]
	y 	  = data[:, n:n+1]
	theta = zeros( (n+1, 1) )

	print computeCost(theta, X, y)
	theta, cost = findMinTheta( theta, X, y )
	plotBoundary( data, X, theta )
	pyplot.show()

	test = array([1, 45, 85])
	print predict( test, theta )

def main():
	set_printoptions(precision=6, linewidth=200)
	part1_1()
	part1_2()

if __name__ == '__main__':
	main()


"""
Benchmark an implementation of the Black–Scholes model.
"""

import math

import numpy as np

# Taken from numba.tests.test_blackscholes

# XXX this data should be shared with bench_cuda.py
# (see https://github.com/spacetelescope/asv/issues/129)
N = 16384

RISKFREE = 0.02
VOLATILITY = 0.30

A1 = 0.31938153
A2 = -0.356563782
A3 = 1.781477937
A4 = -1.821255978
A5 = 1.330274429
RSQRT2PI = 0.39894228040143267793994605993438

callResultGold = np.zeros(N)
putResultGold = np.zeros(N)

stockPrice = np.random.RandomState(0).uniform(5.0, 30.0, N)
optionStrike = np.random.RandomState(1).uniform(1.0, 100.0, N)
optionYears = np.random.RandomState(2).uniform(0.25, 10.0, N)

args = (callResultGold, putResultGold, stockPrice, optionStrike,
        optionYears, RISKFREE, VOLATILITY)

def setup():
    """
    Precompile jitted functions.
    """
    global cnd, blackscholes
    from numba import jit

    @jit(nopython=True)
    def cnd(d):
        K = 1.0 / (1.0 + 0.2316419 * math.fabs(d))
        ret_val = (RSQRT2PI * math.exp(-0.5 * d * d) *
                (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5))))))
        if d > 0:
            ret_val = 1.0 - ret_val
        return ret_val

    @jit(nopython=True)
    def blackscholes(callResult, putResult, stockPrice, optionStrike,
                    optionYears, Riskfree, Volatility):
        S = stockPrice
        X = optionStrike
        T = optionYears
        R = Riskfree
        V = Volatility
        for i in range(len(S)):
            sqrtT = math.sqrt(T[i])
            d1 = (math.log(S[i] / X[i]) + (R + 0.5 * V * V) * T[i]) / (V * sqrtT)
            d2 = d1 - V * sqrtT
            cndd1 = cnd(d1)
            cndd2 = cnd(d2)

            expRT = math.exp((-1. * R) * T[i])
            callResult[i] = (S[i] * cndd1 - X[i] * expRT * cndd2)
            putResult[i] = (X[i] * expRT * (1.0 - cndd2) - S[i] * (1.0 - cndd1))

    blackscholes(*args)

class BlackScholes:

    def time_blackscholes(self):
        for i in range(10):
            blackscholes(*args)


from DiscreteTimeLib import DiscreteTimeSignal
import numpy as np
import matplotlib.pyplot as plt

data = [
    (n, np.sin(x)) for n, x in enumerate(np.linspace(0, 2 * np.pi, num=100))
]
x_n = DiscreteTimeSignal(data)
print(x_n)

markerline, stemlines, baseline = plt.stem(x_n.keys(), x_n.values())
plt.setp(markerline, 'markerfacecolor', 'salmon')
plt.setp(markerline, 'markeredgecolor', 'firebrick')
plt.setp(stemlines, 'color', 'orangered')
plt.setp(baseline, 'color', 'red')
plt.show()

data = [
    (n, np.cos(x)) for n, x in enumerate(np.linspace(0, 2 * np.pi, num=100))
]
h_n = DiscreteTimeSignal(data)
y_n = h_n * x_n
print(y_n)

markerline, stemlines, baseline = plt.stem(y_n.keys(), y_n.values())
plt.setp(markerline, 'markerfacecolor', 'lawngreen')
plt.setp(markerline, 'markeredgecolor', 'olive')
plt.setp(stemlines, 'color', 'palegreen')
plt.setp(baseline, 'color', 'green')
plt.show()

from DiscreteTimeLib import DiscreteTimeSystem

b = (1,)
a = (1, -1)
H = DiscreteTimeSystem(b, a)
y_n = H.filter(x_n)
print(y_n)

markerline, stemlines, baseline = plt.stem(y_n.keys(), y_n.values())
plt.setp(markerline, 'markerfacecolor', 'deepskyblue')
plt.setp(markerline, 'markeredgecolor', 'teal')
plt.setp(stemlines, 'color', 'turquoise')
plt.setp(baseline, 'color', 'cyan')
plt.show()

h_n, n = H.iztrans()
print(h_n)


#  1 Calcular derivada
from sympy import *
from numpy import linalg

#  equacao de primerio grau
x = Symbol('x')
#  2x+5=0
resposta = solve(2*x+5, x)
print(resposta)

#  2x² – 3x + 1

resultado = solve(2*x**2-3*x+1, x)
print(resultado)
#  Deriva a funcao 2*x**2-3*x+1 -> 2x² – 3x + 1
#  Pega a expressao
#  substitui o valor de x por 1, 2, 3

f = Lambda(x, (2*x**2-3*x+1) )
derivada = diff(f(x), x)
print(derivada)

N = 1
x1 = diff(f(x),x).subs(x,N)
print(f"f'({N}) igual a 1 = Y = {x1}")
N = 2
x1 = diff(f(x),x).subs(x,N)
print(f"f'({N}) igual a 1 = Y = {x1}")
N = 3
x1 = diff(f(x),x).subs(x,N)
print(f"f'({N}) igual a 1 = Y = {x1}")

# plot(f(x), (x, -2, 2))

Derivada_igual_a_0 = solve(diff(f(x), x), x)
print(f"Derivada igual a zero {Derivada_igual_a_0}")
#  Pegar a raiz da derivada e por em um float

#  Testando a prova, segundo grau.
#  10**-8*x**2-0.8x+10**-8

resposta = solve(10**(-8)*x**2-0.8*x+10**-8, x)
print(resposta)

#  plot(x+2, (x, -10, 10))


from sympy import *

# Implementation of QuaternionBase<Derived>::toRotationMatrix(void).
# The quaternion q is given as a list [qw, qx, qy, qz].
def QuaternionToRotationMatrix(q):
  tx  = 2 * q[1]
  ty  = 2 * q[2]
  tz  = 2 * q[3]
  twx = tx * q[0]
  twy = ty * q[0]
  twz = tz * q[0]
  txx = tx * q[1]
  txy = ty * q[1]
  txz = tz * q[1]
  tyy = ty * q[2]
  tyz = tz * q[2]
  tzz = tz * q[3]
  return Matrix([[1 - (tyy + tzz), txy - twz, txz + twy],
                 [txy + twz, 1 - (txx + tzz), tyz - twx],
                 [txz - twy, tyz + twx, 1 - (txx + tyy)]])

# Implementation of SO3Group<Scalar> expAndTheta().
# Only implementing the first case (of very small rotation) since we take the Jacobian at zero.
def SO3exp(omega):
  theta = omega.norm()
  theta_sq = theta**2

  half_theta = theta / 2

  theta_po4 = theta_sq * theta_sq
  imag_factor = Rational(1, 2) - Rational(1, 48) * theta_sq + Rational(1, 3840) * theta_po4;
  real_factor = 1 - Rational(1, 2) * theta_sq + Rational(1, 384) * theta_po4;

  # return SO3Group<Scalar>(Eigen::Quaternion<Scalar>(
  #     real_factor, imag_factor * omega.x(), imag_factor * omega.y(),
  #     imag_factor * omega.z()));
  qw = real_factor
  qx = imag_factor * omega[0]
  qy = imag_factor * omega[1]
  qz = imag_factor * omega[2]

  return QuaternionToRotationMatrix([qw, qx, qy, qz])

# Implementation of SE3Group<Scalar> exp().
# Only implementing the first case (of small rotation) since we take the Jacobian at zero.
def SE3exp(tangent):
  omega = Matrix(tangent[3:6])
  V = SO3exp(omega)
  rotation = V
  translation = V * Matrix(tangent[0:3])
  return rotation.row_join(translation)

# Main
init_printing(use_unicode=True)
print('Variant 1')
print('')

# Define the tangent vector with symbolic elements T_0 to T_5.
# (For a matrix, use: Matrix(3, 1, lambda i,j:var('S_%d%d' % (i,j))) )
T = Matrix(6, 1, lambda i,j:var('T_%d' % (i)))

# Compute transformation matrix from tangent vector.
T_matrix = SE3exp(T)

# Define the vector current_T * src:
S = Matrix(3, 1, lambda i,j:var('S_%d' % (i)))

# Matrix-vector multiplication with homogeneous vector:
result = T_matrix * S.col_join(Matrix([1]))

# Compute Jacobian:
# (Note: The transpose is needed for stacking the matrix columns (instead of rows) into a vector.)
jac = result.transpose().reshape(result.rows * result.cols, 1).jacobian(T)

# Take Jacobian at zero:
jac_subs = jac.subs([(T[0], 0), (T[1], 0), (T[2], 0), (T[3], 0), (T[4], 0), (T[5], 0)])

# Simplify and output:
jac_subs_simple = simplify(jac_subs)
pprint(jac_subs_simple)

print('')
print('')
print('Variant 2')
print('')

# Treat the function of which we want to determine the derivative as a list of nested functions.
# This makes it easier to compute the derivative of each part, simplify it, and concatenate the results
# using the chain rule.

### Define the function of which the Jacobian shall be taken ###

# Matrix-vector multiplication with homogeneous vector:
def MatrixVectorMultiplyHomogeneous(matrix, vector):
  return matrix * vector.col_join(Matrix([1]))

# Define the vector current_T * src:
S = Matrix(3, 1, lambda i,j:var('S_%d' % (i)))

# The list of nested functions. They will be evaluated from right to left
# (this is to match the way they would be written in math: f(g(x)).)
functions = [lambda matrix : MatrixVectorMultiplyHomogeneous(matrix, S), SE3exp]

### Define the variables wrt. to take the Jacobian, and the position for evaluation ###

# Chain rule:
# d(f(g(x))) / dx = (df/dy)(g(x)) * dg/dx

# Define the parameter with respect to take the Jacobian, y in the formula above:
parameters = Matrix(6, 1, lambda i,j:var('T_%d' % (i)))

# Set the position at which to take the Jacobian, g(x) in the formula above:
parameter_values = zeros(6, 1)

### Automatic Jacobian calculation, no need to modify anything beyond this point ###

# Jacobian from previous step, dg/dx in the formula above:
previous_jacobian = 1

# TODO: Test whether this works with non-matrix functions.
def ComputeValueAndJacobian(function, parameters, parameter_values):
  # Evaluate the function.
  values = function(parameter_values)
  # Compute the Jacobian.
  symbolic_values = function(parameters)
  symbolic_values_vector = symbolic_values.transpose().reshape(symbolic_values.rows * symbolic_values.cols, 1)
  parameters_vector = parameters.transpose().reshape(parameters.rows * parameters.cols, 1)
  jacobian = symbolic_values_vector.jacobian(parameters_vector)
  # Set in the evaluation point.
  for row in range(0, parameters.rows):
    for col in range(0, parameters.cols):
      jacobian = jacobian.subs(parameters[row, col], parameter_values[row, col])
  # Simplify the jacobian.
  jacobian = simplify(jacobian)
  return (values, jacobian)

# Print info about initial state.
print('Taking the Jacobian of these functions (sorted from inner to outer):')
for i in range(len(functions) - 1, -1, -1):
  print(str(functions[i]))
print('with respect to:')
pprint(parameters)
print('at position:')
pprint(parameter_values)
print('')

# Loop over all functions:
for i in range(len(functions) - 1, -1, -1):
  # Compute value and Jacobian of this function.
  (values, jacobian) = ComputeValueAndJacobian(functions[i], parameters, parameter_values)

  # Update parameter_values
  parameter_values = values
  # Update parameters (create a new symbolic vector of the same size as parameter_values)
  parameters = Matrix(values.rows, values.cols, lambda i,j:var('T_%d%d' % (i,j)))
  # Concatenate this Jacobian with the previous one according to the chain rule:
  previous_jacobian = jacobian * previous_jacobian

  # Print intermediate result
  print('Intermediate step ' + str(len(functions) - i) + ', for ' + str(functions[i]))
  print('Position after function evaluation (function value):')
  pprint(parameter_values)
  print('Jacobian of this function wrt. its input only:')
  pprint(jacobian)
  print('Cumulative Jacobian wrt. the innermost parameter:')
  pprint(previous_jacobian)
  print('')

# Print final result
print('Final result:')
pprint(previous_jacobian)

libvis/scripts/LMOptimizer SE3Optimization Test Jacobian derivation.py


#! /usr/bin/env python3

import numpy as np
import matplotlib.pyplot as plt

def hypothesis_linear(theta0=None, theta1=None, x=None):
    """
    Calculate h_{\theta} (x^{(i)}}
    """
    return theta0 + x*theta1

def gradient_descent(theta0=None, theta1=None, epsilon=None, golden=None, MAX_STEPS=10):
    """
    Calculate the cost function
    J(\theta_{0}, \theta_{1}) = \frac{1}{2m} \sum_{i=1}^{m} (h_{\theta}(x^{i})-y^{i})^2
    """

    print("Cost Function theta0={} theta1={} epsilon={} golden={}".format(
        theta0, theta1, epsilon, golden))
    # Number of training elements
    m = len(golden)

    x = np.arange(0, m, 1)
    theta0_range = np.arange(theta0, theta0+MAX_STEPS, epsilon)
    theta1_range = np.arange(theta1, theta1+MAX_STEPS, epsilon)

    # Start this out as HUGE so we should always find a lower cost than this
    final_cost = 10000000000

    # Go through all possible values for theta0 and theta1
    # This is not efficient, this is just trying everything........
    for z0 in theta0_range:
        for z1 in theta1_range:
            # Clear out the per loop values
            sum_diff_squared = 0

            # This is the \sum_{i=1}^{m} loop element
            for i in range(m):
                # find the difference between the calculated value from hypothesis_linear and
                # the actual value stored in the golden array
                diff = hypothesis_linear(z0, z1, x[i]) - golden[i]
                # Square and sum to avoid some being too high and some being too low.
                diff_squared = diff * diff
                sum_diff_squared = sum_diff_squared + diff_squared
            cost = 1/(2*m) * sum_diff_squared
            # print ("Z0 = {:02f} Z1 = {:02f} Cost = {:02f}".format(z0, z1, cost))

            # Find the lowest possible cost
            if (cost < final_cost):
                final_cost = cost
                final_theta0 = z0
                final_theta1 = z1
                print("Final Cost = {:2f} Theta0 = {:2f} Theta1 = {:2f}".format(
                    final_cost, final_theta0, final_theta1))
    return final_cost, final_theta0, final_theta1

Y_GOLDEN = [0.9, 1.6, 2.4, 2.3, 3.1, 3.6, 3.7, 4.5, 5.1, 5.3]
cost, theta0, theta1 = gradient_descent(-2, -2, 0.1, Y_GOLDEN, 10)
print("FOUND: Cost = {:02f} Theta0 = {:02f} Theta1 = {:02f}".format(
    cost, theta0, theta1))


import numpy as np

def findRoots(a):
	#roots = np.zeros(len(a)-1)
	roots = []
	for r in range(len(a)-1):
		root = newtonHorner(a,0) # initial guess of zero
		if root == None:
			break
		else:
			#print("r",r," = ",root)
			#roots[r] = root
			roots.append(root)
			a = quotient(a, root)

	return roots
def quotient(a, x):
	q = np.zeros(len(a))
	q[len(a)-1] = a[len(a)-1]
	for i in range(len(a)-2, -1, -1):
		q[i] = x * q[i+1]+a[i]
	return q[1:len(q)]

def newtonHorner(a, x):
	i = 0
	px = horner(a,x)
	qx = hornerDeriv(a,x)
	while abs(px) > 0.0000001 and i < 1000:
		if qx == 0: # avoid divide by zero
			return None
		x = x - px / qx
		px = horner(a,x)
		#qx = horner(q,x) # Power Rule
		qx = hornerDeriv(a,x) # Ruffini's Rule
		i = i + 1

	if i == 1000: # hit the iteration cap (divierging or stuck in loop)
		return None

	r = round(x) # check if rounding will give you a better answer
	if(abs(horner(a,r)) < abs(horner(a,x))):
		x = r
	return x

def hornerDeriv(a, x):
	q = quotient(a, x)
	return horner(q,x)
def horner(a, x):
	result = a[len(a)-1]
	for i in range(len(a)-2,-1,-1):
		result = a[i] + x * result
	return result

#p = [-3, -2, -4, -1, 1]
#s = list(reversed(p))
#p = [3,2,1]
#u = findRoots(p)
#print(u)

Topic 4 - Eigenvectors and Eigenvalues/23.Danilevski/mydata.py


# -*- coding: utf-8 -*-
"""hw3_4.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1nR9Fw0b0bDqb14CJp-XsMqNqAnxQahZY
"""

# Commented out IPython magic to ensure Python compatibility.
# %matplotlib inline
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
from numpy import shape
from numpy import mat
from numpy import arange

def sigmoid(x):
  return 1 / (1 + np.exp(-x))

def net_input(theta, x):
  return np.dot(x, theta)

def probability(theta, x):
  return sigmoid(net_input(theta, x))

def cost_func(x, y):
  # if transfer to matrix, there is no need to use np.sum
  total_cost = -(y * np.log(sigmoid(x)) + (1 - y) * np.log(1 - sigmoid(x)))
  return total_cost

"""create gd function"""

def gradient_descent(init, steps, learning_rate, y):
  # transfer init to matrix
  row, col = shape(init)
  init = mat(init)
  # create w vector
  xs = [np.ones((col, 1))]
  # x[m,n] * w[n,1] = y[m, 1]
  for step in range(steps):
    err = sigmoid(init * xs[-1]) - y
    xs.append(xs[-1] - learning_rate * init.T * err)
  return xs

"""create sgd function"""

def stochastic_gradient_descent(init, steps, learning_rate, y):
  row, col = shape(init)
  init = mat(init)
  xs = [np.ones((col, 1))]
  for step in range(steps):
    # use stochastic to get value y, need to generate y
    for index in range(row):
      err = sigmoid(init[index] * xs[-1]) - y[index]
      xs.append(xs[-1] - learning_rate * init[index].T * err)
  return xs

"""a. generate data points"""

standard_deviation = [[0.03,0],[0,0.03]]

mean1 = [0.5,0.5]
data1 = np.random.multivariate_normal(mean1, standard_deviation,100)
# print(data1.shape)

mean2 = [-0.5,-0.5]
data2 = np.random.multivariate_normal(mean2, standard_deviation, 100)

# cut the data set
# data1_x = data1[:, 0]
# data1_y = data1[:, 1]
# data2_x = data2[:, 0]
# data2_y = data2[:, 1]

"""b. use GD to train logisitc regression model"""

init = np.concatenate((data1, data2))
y = mat(([1] * 100 + [0] * 100)).T
gd = gradient_descent(init, 2000, 0.01, y)
sgd = stochastic_gradient_descent(init, 10, 0.1, y)

# get y index from x
temp_x = arange(-1,1,0.1)
x_node = []
y_gd_node = []
y_sgd_node = []
# print(len(temp_x))

for k in range(len(temp_x)):
  x_node.append(temp_x[k])

gd_w = gd[-1]
sgd_w = sgd[-1]

gd_y = (-gd_w[0] * temp_x) / gd_w[1]
sgd_y = (-sgd_w[0] * temp_x) / sgd_w[1]

temp_y_row, temp_y_col = gd_y.shape

for k in range(temp_y_col):
  y_gd_node.append(gd_y[0, k])
  y_sgd_node.append(sgd_y[0, k])

plt.plot(x_node, y_gd_node, c='r')
plt.plot(x_node, y_sgd_node, c='b')

print(y_gd_node)
print(y_sgd_node)

plt.ylim(-1.0, 1.0)
plt.xlim(-1.0, 1.0)

plt.scatter(data1[:, 0], data1[:, 1], c='blue')
plt.scatter(data2[:, 0], data2[:, 1], c='red')
plt.show()

"""c.get loss diagram of gd and sgd"""

cost_gd = []
cost_sgd = []

# print(type(gd_y))
# print(init.shape)
# print(y.shape)
# print(gd[0])

# transfer init to matrix so that we can perform matrix calculation directly
init = mat(init)
Y = np.squeeze(y)

for _ in gd:
  temp = init * _
  x = np.squeeze(temp).T
  cost_gd.append(np.asarray(cost_func(x, Y))[0][0])

# print(type(cost_gd))

for _ in sgd:
  temp = init * _
  x = np.squeeze(temp).T
  cost_sgd.append(np.asarray(cost_func(x, Y))[0][0])

plt.plot(range(len(cost_gd)), cost_gd, c = 'r')
plt.plot(range(len(cost_sgd)), cost_sgd, c = 'b')
plt.show()

# print(len(cost_sgd))
# print(len(cost_gd))
print('the blue line is cost result of sgd')
print('the red line is cost result of gd')


"""
The NumPy module also comes with a number of built-in routines for linear algebra calculations. These can be found in the sub-module linalg.

linalg.det

The linalg.det tool computes the determinant of an array.

print numpy.linalg.det([[1 , 2], [2, 1]])       #Output : -3.0

linalg.eig

The linalg.eig computes the eigenvalues and right eigenvectors of a square array.

vals, vecs = numpy.linalg.eig([[1 , 2], [2, 1]])
print vals                                      #Output : [ 3. -1.]
print vecs                                      #Output : [[ 0.70710678 -0.70710678]
                                                #          [ 0.70710678  0.70710678]]

linalg.inv

The linalg.inv tool computes the (multiplicative) inverse of a matrix.

print numpy.linalg.inv([[1 , 2], [2, 1]])       #Output : [[-0.33333333  0.66666667]
                                                #          [ 0.66666667 -0.33333333]]

Other routines can be found here

Task

You are given a square matrix
with dimensions X

. Your task is to find the determinant. Note: Round the answer to 2 places after the decimal.

Input Format

The first line contains the integer
.
The next lines contains the space separated elements of array

.

Output Format

Print the determinant of

.

Sample Input

2
1.1 1.1
1.1 1.1

Sample Output

0.0
"""

import numpy
n=int(input())
a=numpy.array([input().split() for _ in range(n)],float)
numpy.set_printoptions(legacy='1.13') #important
print(numpy.linalg.det(a))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Apr 13 12:40:01 2020

@author: usama
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.special import factorial

plt.close('all')

#Creating array for Poisson curve x axis
n = np.arange(0, 5, 0.1)

#Creating array for different Poisson curves
Mu=np.arange(0.1, 1, 0.1)

lstyle=['-','--','-.',':']

#Evaluating for each mu value
count=0
for mu in Mu:
    idx=count%len(lstyle)
    count=count+1
    #Solving Poisson Curve
    d = np.power(mu, n)*np.exp(-mu)/factorial(n) * 100
    plt.plot(n, d,label='$\mu={0:.2f}$'.format(mu),linestyle=lstyle[idx],linewidth=2.5)#=mrkr[idxMarker],c=clr[idxColor])

#Setting the plotted graph parameters
plt.grid(which='both')
plt.ylim(top=103)
plt.legend(fontsize='x-large')
plt.xlabel('Number of Photons',fontsize=16)
plt.ylabel('Probability (%)',fontsize=16)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)


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

def plot_vectors(data, row_names, col_names, delta_y=3.5, figname=None):
    # renormaliza los datos
    for i in range(len(col_names)):
        print(i, col_names[i], len(data[i,:]))
        data[i,:] = (data[i,:] - np.mean(data[i,:]))/np.std(data[i,:])

    # calcula vectores propios
    cov = np.cov(data)
    valores, vectores = np.linalg.eig(cov)
    orden_valores = np.argsort(valores)[::-1] # indices de los autovalores de mayor a menor
    print(valores[orden_valores])

    new_data = data.T @ vectores # esto hace el cambio de base al nuevo sistema de los autovectores

    plt.figure(figsize=(9,9))
    # Grafica los nombres
    for i, name in enumerate(row_names):
        plt.text(new_data[i,orden_valores[0]], new_data[i,orden_valores[1]], name, alpha=0.5, fontsize=12, color='Blue')

    # Grafica los loading vectors
    for i, name in enumerate(col_names):
        plt.arrow(0, 0, 3*vectores[i,orden_valores[0]], 3*vectores[i, orden_valores[1]], head_width=0.1)
        plt.text(3*vectores[i,orden_valores[0]], 3*vectores[i, orden_valores[1]], name, fontsize=14, color='Red')

    plt.xlabel('Primera Componente Principal')
    plt.ylabel('Segunda Componente Principal')
    plt.xlim(-delta_y,delta_y)
    plt.ylim(-3.5,3.5)
    plt.savefig(figname, box_inches='tight')

    plt.figure(figsize=(6,6))
    plt.plot(np.arange(len(col_names))+1, 100*np.cumsum(valores[orden_valores])/np.sum(valores))
    plt.scatter(np.arange(len(col_names))+1, 100*np.cumsum(valores[orden_valores])/np.sum(valores), s=100)
    plt.xlabel("Numero de autovalores")
    plt.ylabel("Porcentaje de Varianza explicada")
    plt.ylim([0,100])
    plt.xticks(np.arange(len(col_names))+1)
    plt.grid()
    plt.savefig("varianza_"+figname, box_inches='tight')

# primera grafica
indata = pd.read_csv('USArrests.csv')
col_names = ['Murder', 'Assault', 'UrbanPop', 'Rape']
data = np.array(indata[col_names]).T
names = np.array(indata['Unnamed: 0'])
plot_vectors(data, names, col_names, figname='arrestos.png')

#segunda grafica
indata = pd.read_csv('Cars93.csv')
col_names = ['MPG.city', 'MPG.highway', 'EngineSize', 'Horsepower',
           'RPM', 'Rev.per.mile', 'Fuel.tank.capacity', 'Length',
           'Width', 'Turn.circle', 'Weight']
data = np.array(indata[col_names]).T
names = np.array(indata['Model'])
plot_vectors(data, names, col_names, delta_y=6.0, figname='cars.png')


import numpy as np

def max_bpcs_complexity(ncols, nrows):
    return ((nrows-1)*ncols) + ((ncols-1)*nrows)

def arr_bpcs_complexity(arr):
    """
    arr is a 2-d numpy array
    returns the fraction of maximum bpcs_complexity in arr
        where bpcs_complexity is total sum of bit changes
        moving along each row and each column
    """
    nrows, ncols = arr.shape
    max_complexity = max_bpcs_complexity(nrows, ncols)
    nbit_changes = lambda items, length: sum([items[i] ^ items[i-1] for i in range(1, length)])
    k = 0
    for row in arr:
        k += nbit_changes(row, ncols)
    for col in arr.transpose():
        k += nbit_changes(col, nrows)
    return (k*1.0)/max_complexity

def checkerboard(h, w):
    """
    h, w are int
    returns a checkerboard array of shape == [h,w]
    """
    re = np.r_[ int(w/2)*[0,1] + ([0] if w%2 else [])]
    ro = 1-re
    return np.row_stack(int(h/2)*(re,ro) + ((re,) if h%2 else ()))

def conjugate(arr):
    """
    arr is a numpy array

    conjugates arr so that its complexity, s, is 1-s
    assert conjugate(conjugate(arr)) == arr
    """
    wc = checkerboard(arr.shape[0], arr.shape[1]) # white pixel at origin
    bc = 1-wc # black pixel at origin
    return np.array([[wc[i,j] if arr[i,j] else bc[i,j] for j, cell in enumerate(row)] for i, row in enumerate(arr)])


import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')

def f(x) :

    s = np.power(x,3)  - x - 1

    return s

def g(x) :

    s = np.sqrt(10/(4+x))

    return s

def fixed_point(x0,Tol,Nmax) :

    x=g(x0)
    e=np.abs(x-x0)
    n=1
    while n < Nmax and e > Tol :
          x0 = x
          x = g(x0)
          e=np.abs(x-x0)
          n += 1

    return x,n

#------------ MAIN PROGRAMME -------------#

x0=1.5 ;Tol=10**(-8) ;Nmax=100
x,n = fixed_point(x0,Tol,Nmax)

print ('Approximate Solution is ' , '%.6f' %x )
print ('number of steps is ' , n)

t=np.linspace(0,2,100)
y=f(t)

plt.plot(t,y,'cyan')
plt.legend(['f(x)'])
plt.ylabel('y')
plt.xlabel('x')
plt.show()


import numpy as np
from w2v_utils import *

words, word_to_vec_map = read_glove_vecs('data/glove.6B.50d.txt')

# GRADED FUNCTION: cosine_similarity

def cosine_similarity(u, v):
    """
    Cosine similarity reflects the degree of similariy between u and v

    Arguments:
        u -- a word vector of shape (n,)
        v -- a word vector of shape (n,)

    Returns:
        cosine_similarity -- the cosine similarity between u and v defined by the formula above.
    """

    distance = 0.0

    ### START CODE HERE ###
    # Compute the dot product between u and v (≈1 line)
    dot = np.dot(u.T, v)
    # Compute the L2 norm of u (≈1 line)
    norm_u = np.sqrt(np.sum(np.power(u, 2)))

    # Compute the L2 norm of v (≈1 line)
    norm_v = np.sqrt(np.sum(np.power(v, 2)))
    # Compute the cosine similarity defined by formula (1) (≈1 line)
    cosine_similarity = np.divide(dot, norm_u * norm_v)
    ### END CODE HERE ###

    return cosine_similarity

'''
 Word analogy task

In the word analogy task, we complete the sentence "a is to b as c is to __". An example is 'man is to woman as king is to queen'.
In detail, we are trying to find a word d, such that the associated word vectors ea,eb,ec,edea,eb,ec,ed are related in the following manner: eb−ea≈ed−eceb−ea≈ed−ec.
We will measure the similarity between eb−eaeb−ea and ed−eced−ec using cosine similarity.
'''
def complete_analogy(word_a, word_b, word_c, word_to_vec_map):
    """
    Performs the word analogy task as explained above: a is to b as c is to ____.

    Arguments:
    word_a -- a word, string
    word_b -- a word, string
    word_c -- a word, string
    word_to_vec_map -- dictionary that maps words to their corresponding vectors.

    Returns:
    best_word --  the word such that v_b - v_a is close to v_best_word - v_c, as measured by cosine similarity
    """

    # convert words to lower case
    word_a, word_b, word_c = word_a.lower(), word_b.lower(), word_c.lower()

    ### START CODE HERE ###
    # Get the word embeddings v_a, v_b and v_c (≈1-3 lines)
    e_a, e_b, e_c = word_to_vec_map[word_a], word_to_vec_map[word_b], word_to_vec_map[word_c]
    ### END CODE HERE ###

    words = word_to_vec_map.keys()
    max_cosine_sim = -100  # Initialize max_cosine_sim to a large negative number
    best_word = None  # Initialize best_word with None, it will help keep track of the word to output

    # loop over the whole word vector set
    for w in words:
        # to avoid best_word being one of the input words, pass on them.
        if w in [word_a, word_b, word_c]:
            continue

        ### START CODE HERE ###
        # Compute cosine similarity between the vector (e_b - e_a) and the vector ((w's vector representation) - e_c)  (≈1 line)
        cosine_sim = cosine_similarity(word_to_vec_map[w], e_c)

        # If the cosine_sim is more than the max_cosine_sim seen so far,
        # then: set the new max_cosine_sim to the current cosine_sim and the best_word to the current word (≈3 lines)
        if cosine_sim > max_cosine_sim:
            max_cosine_sim = cosine_sim
            best_word = w
        ### END CODE HERE ###

    return best_word

'''
Neutralize bias for non-gender specific words

The figure below should help you visualize what neutralizing does.
If you're using a 50-dimensional word embedding, the 50 dimensional space can be split into two parts: The bias-direction gg, and the remaining 49 dimensions, which we'll call g⊥g⊥.
In linear algebra, we say that the 49 dimensional g⊥g⊥ is perpendicular (or "othogonal") to gg, meaning it is at 90 degrees to gg.
The neutralization step takes a vector such as ereceptionistereceptionist and zeros out the component in the direction of gg, giving us edebiasedreceptionistereceptionistdebiased.

Even though g⊥g⊥ is 49 dimensional, given the limitations of what we can draw on a screen, we illustrate it using a 1 dimensional axis below.
'''

def neutralize(word, g, word_to_vec_map):
    """
    Removes the bias of "word" by projecting it on the space orthogonal to the bias axis.
    This function ensures that gender neutral words are zero in the gender subspace.

    Arguments:
        word -- string indicating the word to debias
        g -- numpy-array of shape (50,), corresponding to the bias axis (such as gender)
        word_to_vec_map -- dictionary mapping words to their corresponding vectors.

    Returns:
        e_debiased -- neutralized word vector representation of the input "word"
    """

    ### START CODE HERE ###
    # Select word vector representation of "word". Use word_to_vec_map. (≈ 1 line)
    e = word_to_vec_map[word]

    # Compute e_biascomponent using the formula give above. (≈ 1 line)
    e_biascomponent = np.divide(np.dot(e, g), np.linalg.norm(g) ** 2) * g

    # Neutralize e by substracting e_biascomponent from it
    # e_debiased should be equal to its orthogonal projection. (≈ 1 line)
    e_debiased = e - e_biascomponent
    ### END CODE HERE ###

    return e_debiased

def equalize(pair, bias_axis, word_to_vec_map):
    """
    Debias gender specific words by following the equalize method described in the figure above.

    Arguments:
    pair -- pair of strings of gender specific words to debias, e.g. ("actress", "actor")
    bias_axis -- numpy-array of shape (50,), vector corresponding to the bias axis, e.g. gender
    word_to_vec_map -- dictionary mapping words to their corresponding vectors

    Returns
    e_1 -- word vector corresponding to the first word
    e_2 -- word vector corresponding to the second word
    """

    ### START CODE HERE ###
    # Step 1: Select word vector representation of "word". Use word_to_vec_map. (≈ 2 lines)
    w1, w2 = pair
    e_w1, e_w2 = word_to_vec_map[w1], word_to_vec_map[w2]

    # Step 2: Compute the mean of e_w1 and e_w2 (≈ 1 line)
    mu = (e_w1 + e_w2) / 2.0

    # Step 3: Compute the projections of mu over the bias axis and the orthogonal axis (≈ 2 lines)
    mu_B = np.divide(np.dot(mu, bias_axis), np.linalg.norm(bias_axis) ** 2) * bias_axis
    mu_orth = mu - mu_B

    # Step 4: Use equations (7) and (8) to compute e_w1B and e_w2B (≈2 lines)
    e_w1B = np.divide(np.dot(e_w1, bias_axis), np.linalg.norm(bias_axis) ** 2) * bias_axis
    e_w2B = np.divide(np.dot(e_w2, bias_axis), np.linalg.norm(bias_axis) ** 2) * bias_axis

    # Step 5: Adjust the Bias part of e_w1B and e_w2B using the formulas (9) and (10) given above (≈2 lines)
    corrected_e_w1B = np.sqrt(np.abs(1 - np.sum(mu_orth ** 2))) * np.divide(e_w1B - mu_B, np.abs(e_w1 - mu_orth - mu_B))
    corrected_e_w2B = np.sqrt(np.abs(1 - np.sum(mu_orth ** 2))) * np.divide(e_w2B - mu_B, np.abs(e_w2 - mu_orth - mu_B))

    # Step 6: Debias by equalizing e1 and e2 to the sum of their corrected projections (≈2 lines)
    e1 = corrected_e_w1B + mu_orth
    e2 = corrected_e_w2B + mu_orth

    ### END CODE HERE ###

    return e1, e2

5. Sequence Models/week2/Operation on word vectors/operations_on_word_vectors.py


import numpy as np
from scipy.sparse import coo_matrix

def softmaxCost(W, b, numClasses, inputSize, decay, data, labels):
  """Computes and returns the (cost, gradient)

  W, b - the weight matrix and bias vector parameters
  numClasses - the number of classes
  inputSize - the size D of each input vector
  decay - weight decay parameter
  data - the D x N input matrix, where each column data[:,n] corresponds to
         a single sample
  labels - an N x 1 matrix containing the labels corresponding for the input data
  """

  N = data.shape[1]

  groundTruth = coo_matrix((np.ones(N, dtype = np.uint8),
                            (labels, np.arange(N)))).toarray()
  cost = 0;
  dW = np.zeros((numClasses, inputSize))
  db = np.zeros((numClasses, 1))

  ## ---------- YOUR CODE HERE --------------------------------------
  #  Instructions: Compute the cost and gradient for softmax regression.
  #                You need to compute dW, dW, and cost.
  #                The groundTruth matrix might come in handy.

  # This is the cost function from page 86 of lecture4
  Z = W.dot(data) + b
  Z = Z - np.max(Z, axis=0, keepdims=True)
  exp_Z = np.exp(Z)
  sum_emp = np.sum(exp_Z, axis=0, keepdims=True)
  prob = exp_Z / sum_emp
  log_prob = np.log(prob)
  tmp = np.multiply(groundTruth, log_prob)
  cost = (-1.0 / N) * np.sum(tmp)
  cost += (0.5 * decay) * np.sum(W**2)

  #This is the gradient function from page 86 of lecture4
  dW = (-1.0 / N) * (groundTruth - prob).dot(data.T)
  dW += decay * W

  #This is gradient b
  db = (-1.0 / N) * np.sum((groundTruth - prob), axis=1, keepdims=True)

  return cost, dW, db

def softmaxPredict(W, b, data):
  """Computes and returns the softmax predictions in the input data.

  W, b - model parameters trained using softmaxTrain,
         a numClasses x D matrix and a numClasses x 1 column vector.
  data - the D x N input matrix, where each column data[:,n] corresponds to
         a single sample.
  """

  #  Your code should produce the prediction matrix pred,
  #  where pred(i) is argmax_c P(c | x(i)).

  ## ---------- YOUR CODE HERE ------------------------------------------
  #  Instructions: Compute pred using W and b, assuming that the labels
  #                start from 0.
  Z = W.dot(data) + b
  pred = np.argmax(Z, axis=0)

  # ---------------------------------------------------------------------

  return pred


import numpy as np
from PIL import Image
from scipy.fftpack import dct

im = Image.open("test.png")
im = im.convert("L")
im = np.array(im)

#im = im[:128,:128,0]

# Save ori
im_out = Image.fromarray(im)
im_out.save("ori.png")

# VARIABLES
N = 8

# Divide in 8x8 blocks
blocks = []

# Works only on image sizes divisible by N
for y in range(0,im.shape[0],N):
	blocks.append([])
	for x in range(0,im.shape[1],N):
		blocks[y//N].append(im[y:y+N,x:x+N])
blocks = np.array(blocks).astype(np.float32) -128.0

# Compute matrix T
T = np.zeros([N,N],dtype=np.float32)

for j in range(N):
	for i in range(N):
		if j == 0:
			T[j,i] = 1.0 / np.sqrt(N)
		else:
			T[j,i] = 0.5 * np.cos((np.pi * (j * (2.0*i+1.0))) / (2.0*N))

print (np.around(T,4))

# Modify image

def DCT(block):
	return np.matmul(np.matmul(T,block),T.T)
def IDCT(block):
	return np.matmul(np.matmul(T.T,block),T)

print (IDCT(DCT(im[:8,:8])).astype(np.uint8))
print (im[:8,:8])

# Perform Dct on blocks
dct_blocks = np.zeros_like(blocks)
for y in range(blocks.shape[0]):
	for x in range(blocks.shape[1]):
		dct_blocks[y,x] = DCT(blocks[y,x])

# DCT Image
dct_changed_im_out = np.zeros_like(im)
for y in range(dct_blocks.shape[0]):
	for x in range(dct_blocks.shape[1]):
		dct_changed_im_out[y*N:y*N+N,x*N:x*N+N] = dct_blocks[y,x]

dct_changed_im_out = Image.fromarray(dct_changed_im_out)
dct_changed_im_out.save("dct_changed_im_out.png")

Q_mat = np.array([	[16, 11, 10, 16, 24, 40, 51, 61],
					[12,12,14,19,26,58,60,55],
					[14,13,16,24,40,57,69,56],
					[14,17,22,29,51,87,80,62],
					[18,22,37,56,68,109,103,77],
					[24,35,55,64,81,104,113,92],
					[49,64,78,87,103,121,120,101],
					[72,92,95,98,112,100,103,99]],dtype=np.float32)
Q_mat_flip = np.flipud(np.fliplr(Q_mat))

Z_mat_dist = 1
Z_mat = np.ones([N,N],dtype=np.float32)
for y in range(N):
	for x in range(N):
		if y >= Z_mat_dist or x >= Z_mat_dist:
			Z_mat[y,x] = 1024
print (Z_mat)
'''
Z_mat = np.array([	[1, 1, 1, 1, 255, 255, 255, 255],
					[1, 1, 1, 1, 255, 255, 255, 255],
					[1, 1, 1, 1, 255, 255, 255, 255],
					[1, 1, 1, 1, 255, 255, 255, 255],
					[255, 255, 255, 255, 255, 255, 255, 255],
					[255, 255, 255, 255, 255, 255, 255, 255],
					[255, 255, 255, 255, 255, 255, 255, 255],
					[255, 255, 255, 255, 255, 255, 255, 255]],dtype=np.float32)
'''
Z_mat_flip = np.flipud(np.fliplr(Z_mat))

def Q_im(mat,name):
	dct_blocks_MAT = np.zeros_like(dct_blocks)
	for y in range(dct_blocks.shape[0]):
		for x in range(dct_blocks.shape[1]):
			dct_blocks_MAT[y,x] = mat*np.round(dct_blocks[y,x]/mat)

	# DCT Out Image
	reconst_out_im = np.zeros_like(im)
	for y in range(dct_blocks_MAT.shape[0]):
		for x in range(dct_blocks_MAT.shape[1]):
			reconst_out_im[y*N:y*N+N,x*N:x*N+N] = dct_blocks_MAT[y,x]

	reconst_out_im = Image.fromarray(reconst_out_im)
	reconst_out_im.save(name+"_dct_out.png")

	# Reconstructed Image
	reconst_mat_im = np.zeros_like(im)
	for y in range(dct_blocks_MAT.shape[0]):
		for x in range(dct_blocks_MAT.shape[1]):
			reconst_mat_im[y*N:y*N+N,x*N:x*N+N] = (IDCT(dct_blocks_MAT[y,x])+128.0).astype(np.uint8)

	reconst_mat_im = Image.fromarray(reconst_mat_im)
	reconst_mat_im.save(name+".png")

Q_im(Q_mat,"q")
Q_im(Q_mat_flip, "q_flip")

Q_im(Z_mat,"z")
Q_im(Z_mat_flip, "z_flip")


import math
from scipy import optimize
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from sympy import symbols, Eq, solve

#Question 1
"""
As the model is a static model, and utility is strictly increasing with consumption the consumer will always choose to spend all his wealth on consumption, hence c = x,
which is substituted directly into the utility function.

"""
#Utility function defined
def u_func(c, l):
    return np.log(m + w * l - (t_0 * w * l + t_1 * max(w * l-kappa,0))) - v*l **(1+1/eps)/(1+1/eps)

#Variables defined
m = 1
v = 10
eps = 0.3
t_0 = 0.4
t_1 = 0.1
kappa = 0.4

#Lists pre defined
l_plot = []
c_plot = []
w_plot = []

def value_of_choice(l,m,v,eps,t_0,t_1,kappa,w):
        c = m + w * l - (t_0 * w * l + t_1 * max(w * l - kappa,0))
        return -u_func(c,l)

#There is added 0.05 to the end of the renge in order to account for zero indexing.
for w in np.arange(0.5,1.5 + 0.05,0.05):

    sol_case = optimize.minimize_scalar(
        value_of_choice,method='bounded',
        bounds=(0,1),args=(m,v,eps,t_0,t_1,kappa,w))

    l = sol_case.x
    c = m + w * l - (t_0 * w * l + t_1 * max(w * l-kappa,0))

    # Store results as lists
    l_plot.append(f'{l:.3}')
    c_plot.append(f'{c:.3}')
    w_plot.append(f'{w:.3}')

    #float l for plotting
    l_plot = [float(i) for i in l_plot]

#Print the newly stored lists (to see that they are stored correctly)
print('l results: ' + str(l_plot))
print('c results: ' + str(c_plot))
print('w results: ' + str(w_plot))

#Question 2
"""
Viewing the plot we see, that there is a break where higher wages will result in less time spent working. This is due to the new tax bracket off-setting the gain from an
increase in consumption for the consumer. Same can be seen for the consumption, here there is a point where consumption stagnates as the consumer when maximizing will
work less at the higher wage due to the tax bracket changing if he were to work more.
"""

#Plot of labour and wage
plt.plot(w_plot, l_plot)
plt.xlabel("Wage")
plt.ylabel("Optimal labour")
plt.title("Labour")
plt.show()

#Plot of consumption and wage
plt.plot(w_plot, c_plot)
plt.xlabel("Wage")
plt.ylabel("Optimal consumption")
plt.title("Consumption")
plt.show()

#Question 3 & 4
"""
The tax revenue increases when the elasticity of labour falls from 0.3 to 0.1.

"""
def u_func3(c, l):
    return np.log(m + wi * l - (t_0 * wi * l + t_1 * max(wi * l-kappa,0))) - v*l **(1+1/eps)/(1+1/eps)

def value_of_choice3(l,m,v,eps,t_0,t_1,kappa,wi):
    c = m + wi * l - (t_0 * wi * l + t_1 * max(wi * l - kappa,0))
    return -u_func3(c,l)

N = 10000

# w = np.random.uniform(size=N, low=0.5, high=1.5)
w = np.linspace(0.5, 1.5, N)

for eps in [0.3, 0.1]:

    l = []
    c = []
    tax_revenue = 0
    for i, wi in enumerate(w):

        sol_case = optimize.minimize_scalar(
            value_of_choice3, method='bounded',
            bounds=(0,1),args=(m,v,eps,t_0,t_1,kappa,wi))

        l.append(sol_case.x)
        c.append(m + wi * l[-1] - (t_0 * wi * l[-1] + t_1 * max(wi * l[-1] - kappa,0)))

        tax_revenue += t_0 * wi * l[i] + t_1 * np.max(wi * l[i] - kappa, 0)
    print(tax_revenue)

#Question 5
"""
From the perspective of the available tax revenue, the best case for t_0 is 0.78, with no top tax bracket (t_1 = 0) and some kappa above 0.78.
However, it is currently unclear how this would affect a deadweight loss and overall utility in the society, so it is not to say if this is actually
the best values for t_0, t_1 and Kappa for the society as a whole.

"""

N = 100
w = np.linspace(0.5, 1.5, N)
eps = 0.3
tax = {}

for t_0 in np.linspace(0,1, 10):
    for t_1 in np.linspace(0, 1, 10):
        for kappa in np.linspace(t_0, 1, 10):
            l = []
            c = []
            tax_revenue = 0
            for i, wi in enumerate(w):

                sol_case = optimize.minimize_scalar(
                    value_of_choice3, method='bounded',
                    bounds=(0,1),args=(m,v,eps,t_0,t_1,kappa,wi))

                l.append(sol_case.x)
                c.append(m + wi * l[-1] - (t_0 * wi * l[-1] + t_1 * max(wi * l[-1] - kappa,0)))

                tax_revenue += t_0 * wi * l[i] + t_1 * np.max(wi * l[i] - kappa, 0)
            key = (round(t_0, 2), round(t_1, 2), round(kappa, 2))
            tax[key] = tax_revenue
            print(f"{key} -> T = {round(tax_revenue,2)}")

"""
Overall the wage effect on l* and c* is linear only distorted at the point where the consumer has to think about if he/she wants to be in the top or lower tax bracket.
The tax revenue is affected positively by the elasticity of labour supply, which is useful to know when deciding what kind of tax percentage one would want to implement.
Also the maximum tax revenue is found at 78 % income tax, and no top income tax. However, it is unclear what is best for the society as a whole, as deadweight loss is not
accounted for.
"""

import numpy,from scipy,import sympy,from sympy


'''
Created on Aug 6, 2010

@author: Sven Kratz
(c) 2010, 2011, Sven Kratz, University of Munich

# helper module with maths functions

'''
import math
import numpy

SEP = "============================================"

def print_matlab_array(points):
	print SEP
	print "P=["
	for p in points:
		for pp in p:
			print pp,",",
		print ";"
	print "];"

def print_points(points):
	for i in xrange(len(points)):
		print i, points[i]

def norm3(v):
	'''euclidean norm of vector v, v in R^3'''
	return math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])

def subtract3(v1,v2):
	'''subtract v2 from v1 (v1,v2 in R^3)'''
	return [ v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2] ]

def rotate_points(axis, angle, points):
	l = norm3(axis)
	axis[0]/=l
	axis[1]/=l
	axis[2]/=l
	R = rotation_matrix3(axis,angle)
	rotated_points  = [numpy.dot(R,v) for v in points]
	return rotated_points

def rotation_matrix3(axis, angle):

	''' returns a rotation matrix from axis and angle'''

	s = numpy.sin(-angle)
	c = numpy.cos(-angle)
	x = axis[0]
	y = axis[1]
	z = axis[2]

	# rotation matrix from angle and axis
	#M = [(1+(1-c))*(x*x-1) -z*s+(1-c)*x*y y*s+(1-c)*x*z;
	#   z*s+(1-c)*x*y 1+(1-c)*(y*y*-1) -x *s+(1-c)*y*z;
	#   -y*s+(1-c)*x*z x*s+(1-c)*y*z 1+(1-c)*(z*z-1)];
	M = numpy.array([[x*x+(1-x*x)*c, x*y*(1-c)-z*s, x*z*(1-c)+y*s],
		[x*y*(1-c)+z*s, y*y+(1-y*y)*c, y*z*(1-c)-x*s],
		[x*z*(1-c)-y*s, y*z*(1-c)+x*s, z*z+(1-z*z)*c]], dtype=numpy.dtype('d'))
	return M

def scalar(p, q):
	sp  = 0
	if len(q) >= len(p):
		n = len(p)
	else:
		n = len(q)
	for i in xrange(n):
		sp+=p[i][0]*q[i][0]+p[i][1]*q[i][1]+p[i][2]*q[i][2]
	return sp

def mse(points1, points2):
	error = 0.0

	if len(points2) >= len(points1):
		n = len(points1)
	else:
		n = len(points2)
	for i in xrange(n):
		d = subtract3(points1[i],points2[i])
		error = error + (d[0]*d[0] + d[1]*d[1] + d[2]*d[2])
	return error / n


from scipy.integrate import quad
import numpy.polynomial.polynomial as poly
import numpy as np
import math

SMALL_EPS = 10 ** (-12)

def complex_from_angle(phi, radius):
    """Return the complex number with argument 'phi' and absolute value 'radius'."""
    return complex(math.cos(phi) * radius, math.sin(phi) * radius)

def integrand(phi, num_func, den_func, radius):
    """Compute the value of the integrand  z * num_func(z) / den_func(z),  where  z = z(phi) = radius * e^{i phi}."""
    x = complex_from_angle(phi, radius)
    return (x * poly.polyval(x, num_func) / poly.polyval(x, den_func)).real

def integrate(num_func, den_func, radius, epsilon):
    """Integrate the function   num_func(z) / ( 2 * pi * i * den_func(z)) dz =
    z num_func(z) / ( 2 * pi * den_func(z)) d phi  along the circle  S_radius
    with an absolute error tolerance of  'epsilon'. Return the real part of the integral."""
    integral_real, _ = quad(integrand, -math.pi, math.pi, args=(num_func, den_func, radius), epsabs=epsilon)
    return integral_real / (2 * math.pi)

def find_good_radius(polynom):
    """Find a good radius, i.e., such 'radius' that there are no roots in  R_radius = {z: 1 < |z| < radius}.
    'polynom' is such a polynomial that it has only one zero  z_{-1}  in  (1, \\infty),  polynom(1) = 0,
    polynom'(1) > 0, and there are no zeroes in  R_{z_{-1}}.
    Return 1 + 2^(-k) such that  'k'  is he first integer for which  polynom(1 + 2^(-k)) > 0.  """
    radius = 1.5
    while poly.polyval(radius, polynom) <= 0:
        radius = (radius + 1) / 2.
        if radius < 1. + SMALL_EPS:
            print("Warning: haven't found good radius by dividing!")
    return radius

def find_etas(polynom, n, epsilon):
    """Find  eta_k = \\sum_(j=1)^n \\hat z_j^k  for  k = 0, ..., n - 1.
    eta_k = -1 + 1 / (2 * pi * i) * \\int_{S_{1 + epsilon}} polynom'(z) z^k / polynom(z) dz."""
    etas = [float(n)]  # eta_0 = n
    radius = find_good_radius(polynom)
    der_zk = poly.polyder(polynom)  # polynom'(z) z^k,  starting with  k = 0
    for k in range(1, n + 1):
        der_zk = np.concatenate(([0], der_zk))
        etas.append(-1 + integrate(der_zk, polynom, radius, epsilon))
    return etas


import numpy as np
from scipy import linalg
from pressio4py import logger, solvers, ode

class RosenbrockSys:
  def createResidual(self):
    return np.zeros(6)

  def createJacobian(self):
    return np.zeros((6,4))

  def residual(self, x, R):
    x1,x2,x3,x4 = x[0],x[1],x[2],x[3]
    R[0] = 10.*(x4 - x3*x3)
    R[1] = 10.*(x3 - x2*x2)
    R[2] = 10.*(x2 - x1*x1)
    R[3] = (1.-x1)
    R[4] = (1.-x2)
    R[5] = (1.-x3)

  def jacobian(self, x, J):
    x1,x2,x3 = x[0],x[1],x[2]
    J[0,2] = -20.*x3
    J[0,3] = 10.
    J[1,1] = -20.*x2
    J[1,2] = 10.
    J[2,0] = -20.*x1
    J[2,1] = 10.
    J[3,0] = -1.
    J[4,1] = -1.
    J[5,2] = -1.

class MyLinSolver2:
  def solve(self, A,b,x):
    lumat, piv, info = linalg.lapack.dgetrf(A, overwrite_a=False)
    x[:], info = linalg.lapack.dgetrs(lumat, piv, b, 0, 0)

class MyWeigher:
  def __call__(self, operand, result):
    result[:] = np.copy(operand)

def test_gn_neq_rosenbrock():
  print("\n")
  logger.initialize(logger.logto.terminal)
  logger.setVerbosity([logger.loglevel.debug])

  state = np.array([-0.05, 1.1, 1.2, 1.5])
  sys = RosenbrockSys()
  lsO = MyLinSolver2()
  w = MyWeigher()
  nlsO = solvers.create_weighted_gauss_newton(sys, state, lsO, w)
  nlsO.setTolerance(1e-5)
  nlsO.solve(sys, state)
  print(state)

  gold = np.array([1.00000001567414e+00,
                   9.99999999124769e-01,
  		   9.99999996519930e-01,
                   9.99999988898883e-01])
  assert(np.allclose(gold, state))
  logger.finalize()


#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
import time

# Auto-regressive model example
#
# X[t]=a*X[t-1]+b*X[t-2]+c*X[t-3]+...+w[t]
#
# where `w' denotes white noise

# Example: AR(2)
#
# X[t]=a*X[t-1]+b*X[t-2]+w[t]

niter=200
X_range=range(0,niter)
X=[10]
w=[]
w.append(np.random.normal(scale=0.5))
a=0.85

np.random.seed(int(time.time()))

for i in range(1,niter):
    w.append(np.random.normal(scale=0.5))
    X.append(a*X[-1]+w[-1])

win_size=10
MA_range=range(0,niter-win_size)
MA=[]

for i in MA_range:
    MA.append(np.mean(X[i:i+win_size]))

# Linear interpolate moving average
#MA_int=np.interp(X_range,MA_range,MA)

fig=plt.figure("AR(1) model",figsize=(9,6))
fig.set_tight_layout(True)
ax=plt.subplot(211)
ax.set_title("AR(1)'s result after %d iterations with a=%.2f" \
        % (niter,a))
ax.plot(X_range,X,".",label=r"$X_t=a\cdot X_{t-1}+w_t$")
ax.plot(MA_range,MA,"-",
        label="Moving Average with window size %d" % win_size)
ax.legend()

ax2=plt.subplot(212)
ax2.set_title("Noise anaysis")
ax2.plot(X_range,w,"r.-",label="$w_t$")
ax2.legend()
plt.show()

print("Noise: mean=%.5f, std=%.5f" % (np.mean(w),np.std(w)))


import numpy as np

def back_tracking (x_k, d_k, f, f_grad, alpha = 0.1, ro = 0.9, c1 = 1e-4, params = {}) :
    '''
    Calcula tamaño de paso alpha

        Parámetros
        -----------
            x_k     : Vector de valores [x_1, x_2, ..., x_n]
            d_k     : Dirección de descenso
            f       : Función f(x)
            f_grad  : Función que calcula gradiente
            alpha   : Tamaño inicial de paso
            ro      : Ponderación de actualización
            c1      : Condición de Armijo
        Regresa
        -----------
            alpha_k : Tamaño actualizado de paso
    '''
    # Inicialización
    alpha_k = alpha

    # Repetir hasta que se cumpla la condición de armijo
    while f(x_k + alpha_k * d_k, params) > f(x_k, params) + c1 * alpha_k * f_grad(x_k, params).dot(d_k) :
        alpha_k = ro * alpha_k

    return alpha_k

def bisection (x_k, d_k, f, f_grad,  alpha_0 = 1.0, c1 = 1e-4, c2 = 0.9, max_iter = 100, params = {}) :
    '''
    Calcula tamaño de paso con método de Bisección

    Parámetros
    -----------
        x_k      : Vector de valores [x_1, x_2, ..., x_n]
        d_k      : Dirección de descenso
        f        : Función f(x)
        f_grad   : Función que calcula gradiente
        alpha_0  : Tamaño inicial de paso
        c1       : Condición de Wolfe
        c2       : Condición de Wolfe
        max_iter : Iteraciones Máximas permitadas
    Regresa
    -----------
        alpha_k  : Tamaño actualizado de paso
    '''
    grad_k = -f_grad(x_k, params)
    # Inicialización
    alpha    = 0.0
    beta     = np.inf
    counter   = 0

    alpha_k = 1

    while (f(x_k + alpha_k * d_k, params) > f(x_k, params) + c1 * alpha_k * f_grad(x_k, params).dot(d_k)) or f_grad(x_k + alpha_k * d_k, params).dot(d_k) < c2 * f_grad(x_k, params).dot(d_k):
        if f(x_k + alpha_k * d_k, params) > f(x_k, params) + c1 * alpha_k *  f_grad(x_k, params).dot(d_k) :
            beta = alpha_k
            alpha_k    = 0.5*(alpha + beta)
        else :
            alpha = alpha_k
            if beta == np.inf :
                alpha_k = 2.0 * alpha
            else :
                alpha_k = 0.5* (alpha + beta)

        counter = counter + 1

        if counter > max_iter :
            break

    return alpha_k

# Descenso Gradiente

def des_grad (params = []) :
    '''
    '''
    # Cargo parámetros
    x_k        = params['x_0']
    x_k_next   = None

    f          = params['f']
    f_grad     = params['f_grad']
    max_iter   = params['max_iter']
    tau_x      = params['tau_x']
    tau_f      = params['tau_f']
    tau_f_grad = params['tau_grad']

    # Identifico parámetros especiales
    if f.__name__ == 'f3' :
        sub_params = {
                        'lambda' : params['lambda'],
                        'eta'    : params['eta'],
                        'sigma'  : params['sigma']
        }
        # Guardo Parámetros
        x_hist = []
        x_hist.append(x_k)
    elif f.__name__ == 'log_likelihood' :
      sub_params = {
        'x'       : params['x'],
        'y'       : params['y'],
        'epsilon' : params['epsilon']
        }
      # 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)))
    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)))

    # Identifico función para paso
    if (params['method'] == 'BackTracking') :
        alpha = params['BackTracking']['alpha']
        ro    = params['BackTracking']['ro']
        c1    = params['BackTracking']['c1']

    elif (params['method'] == 'Bisection') :
        alpha        = params['Bisection']['alpha']
        c1           = params['Bisection']['c1']
        c2           = params['Bisection']['c2']
        max_iter_bic = params['Bisection']['max_iter']
    else :
        print('None')
        return

    # Comienza descenso
    k = 0

    while True:
        # Calculo gradiente
        d_k = - f_grad(x_k, params = sub_params)
        # Cálculo tamaño de paso
        if (params['method'] == 'BackTracking') :
            alpha_k = back_tracking(x_k, d_k, f, f_grad, alpha, ro, c1, params = sub_params)
        elif (params['method'] == 'Bisection') :
            alpha_k = bisection(x_k, d_k, f, f_grad, alpha, c1, c2, max_iter_bic, params = sub_params)
        # Calculo siguiente valor x_k+1
        x_k_next = x_k + alpha_k * d_k

        ''' Guardo Resultados '''
        if f.__name__ == 'f3' :
            # Guardo Parámetros
            x_hist.append(x_k_next)
        else :
            # Guardo Parámetros
            f_hist.append(f(x_k_next, sub_params))
            g_hist.append(np.linalg.norm(f_grad(x_k_next, params = sub_params)))

        # Criterios de paro
        if (k > max_iter) :
            print('Iteraciones: ', k, ' , valor: ', x_k)
            break
        g_k = np.linalg.norm(f_grad(x_k_next, sub_params))
        if g_k < tau_f_grad :
            print('Iteraciones: ', k, ' , valor: ', x_k)
            break

        # Guardo valor anterior
        x_k = x_k_next
        k   = k + 1
        print('iteración: ', k, 'Norma de gradiente: ', g_k)

    if f.__name__ == 'f3' :
        return np.array(x_k_next)
    else :
        return np.array(f_hist), np.array(g_hist), x_k_next


import scipy.fftpack as fp
from scipy import signal
import numpy as np
import numpy.fft
import cv2
import matplotlib.pylab as pylab

# carrega a imagem
img = cv2.imread(input("Digite o caminho para a imagem de entrada: "))[:, :, 0]

# lê o valor de D
D = int(input("Digite o valor de D para o kernel: "))

# obtém o filtro gaussiano no domínio espacial
gauss_kernel = np.outer(signal.gaussian(
    img.shape[0], D), signal.gaussian(img.shape[1], D))

# obtém a imagem e o filtro no domínio da frquência
freq_img = fp.fft2(img)
freq_kernel = fp.fft2(fp.ifftshift(gauss_kernel))
assert(freq_img.shape == gauss_kernel.shape)

# aplica convolução (borramento)
convolved_img = freq_img * freq_kernel
blured_img = fp.ifft2(convolved_img).real

blured_img = np.abs(blured_img)
blured_img = blured_img * 255 / blured_img.max()
blured_img = blured_img.astype(np.uint8)

####################### Mostra todos os passos da convolução na frequência #######################
pylab.figure(figsize=(30, 25))
pylab.gray()

pylab.subplot(2, 2, 1), pylab.imshow(img), pylab.title(
    'Imagem original', size=10), pylab.axis('off')

pylab.subplot(2, 2, 4), pylab.imshow(blured_img)
pylab.title('Imagem após convolução', size=10), pylab.axis('off')

pylab.subplot(2, 2, 2), pylab.imshow(
    (20*np.log10(0.1 + fp.fftshift(freq_kernel))).astype(int))
pylab.title('Transformada do filtro', size=10), pylab.axis('off')

pylab.subplot(2, 2, 3), pylab.imshow(
    (20*np.log10(0.1 + fp.fftshift(convolved_img))).astype(int))
pylab.title('Imagem borrada no domínio da frequência',
            size=10), pylab.axis('off')

pylab.subplots_adjust(wspace=0.2, hspace=1)
pylab.show()
##################################################################################################

# lê o valor de k
k = int(input("Digite o valor de k: "))

# obtem a máscara (arestas)
mask = cv2.subtract(img, blured_img)
mask = k * mask

####################### Mostra todos os passos da convolução #######################
pylab.subplot(2, 3, 1), pylab.imshow(img), pylab.title(
    'Imagem original', size=10), pylab.axis('off')

pylab.subplot(2, 3, 3), pylab.imshow(blured_img)
pylab.title('Imagem após convolução', size=10), pylab.axis('off')

pylab.subplot(2, 3, 5), pylab.imshow(mask)
pylab.title('Arestas para aguçamento', size=10), pylab.axis('off')

pylab.subplots_adjust(wspace=0.2, hspace=1)
pylab.show()
##################################################################################################

# obtem imagem final
final_image = cv2.add(img, mask)
cv2.imwrite(input("Digite o caminho para a imagem de saída: "), final_image)
print("Imagem final salva!")


#########################
##                     ##
## Irving Gomez Mendez ##
##  October 10, 2021   ##
##                     ##
#########################

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import confusion_matrix
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

import warnings
warnings.filterwarnings("ignore")

dat = load_breast_cancer(return_X_y = True, as_frame = True)

X = dat[0]
y = dat[1]
p = X.shape[1]

np.linalg.cond(X)

X = X.mask(X == 0).fillna(0.001)
X = np.log(X)

np.linalg.cond(X)

y_M = y[y == 0]
X_M = X.iloc[y_M.index]
n_M = X_M.shape[0]
mean_M = np.array(X_M.mean()).reshape(p, 1)
S_M = X_M.cov()

y_B = y[y == 1]
X_B = X.iloc[y_B.index]
n_B = X_B.shape[0]
mean_B = np.array(X_B.mean()).reshape(p, 1)
S_B = X_B.cov()

n = n_B + n_M
S = ((n_B-1)*S_B + (n_M-1)*S_M)/(n-2)
pi_M = n_M/n
pi_B = n_B/n

# discriminant function (coefficients)
b  = np.linalg.solve(S, mean_B-mean_M)
b0 = np.log(pi_B/pi_M) - 1/2 * (mean_B + mean_M).T @ b

scores_M = b0 + X_M @ b
scores_B = b0 + X_B @ b

plt.figure(figsize = (10,7.5))
sns.distplot(scores_M, label = 'Malign')
sns.distplot(scores_B, label = 'Benign')
plt.axvline(x = 0, color = 'r', linestyle = '--')
plt.legend()

# How good is the classifier ?
round((100*((scores_M > 0).sum() + (scores_B < 0).sum())/n).values[0],2)

# We can use CV to get a more realistic estimation of the error
classifCV = []
classify_sklearn = []

for i in range(n):
    X_i = X.iloc[i]
    y_i = y.iloc[i]

    y_mi = y.drop(i)

    y_M = y_mi[y_mi == 0]
    X_M = X.iloc[y_M.index]
    n_M = X_M.shape[0]
    mean_M = np.array(X_M.mean()).reshape(p, 1)
    S_M = X_M.cov()

    y_B = y_mi[y_mi == 1]
    X_B = X.iloc[y_B.index]
    n_B = X_B.shape[0]
    mean_B = np.array(X_B.mean()).reshape(p, 1)
    S_B = X_B.cov()

    n_cv = n_B + n_M
    S = ((n_B-1)*S_B + (n_M-1)*S_M)/(n_cv-2)
    pi_M = n_M/n_cv
    pi_B = n_B/n_cv

    # discriminant function (coefficients)
    b  = np.linalg.solve(S, (mean_B - mean_M))
    b0 = np.log(pi_B/pi_M) - 1/2 * (mean_B + mean_M).T @ b

    score = b0 + X_i @ b
    classifCV.append(0) if score < 0  else classifCV.append(1)

    lda = LinearDiscriminantAnalysis()
    lda.fit(pd.concat([X_M, X_B]), pd.concat([y_M, y_B]))
    classify_sklearn.append(lda.predict(np.array([X_i])))

conf_matrix = confusion_matrix(y, classifCV)
round(100*(1-sum(np.diag(conf_matrix))/n),2)

conf_matrix_sklearn = confusion_matrix(y, classify_sklearn)
round(100*(1-sum(np.diag(conf_matrix))/n),2)

####


#simple linear regression
##Used Modules
from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
import random
##Used Pyplot style
plt.style.use('seaborn-darkgrid')
##Definition of regression function
def FitLine(X,Y):
    slope=(((mean(X) * mean(Y))-mean(X*Y))/
       ((mean(X)**2)-mean(X**2)))
    intercept=mean(Y)-slope*mean(X)
    rLine=[(slope*x)+intercept for x in X]
    LYmean=[mean(Y) for y in Y]
    return slope,intercept,rLine,(1-(sum((Y-rLine)**2)/sum((Y-LYmean)**2)))
##Define Input Data
X=np.array([1,2,3,4,5,6,7,8,9,10], dtype=np.float64)
Y=np.array([3,2,4,5,6,7,10,9,14,12], dtype=np.float64)
##Call Functions
slope,intercept,rLine,rs=FitLine(X,Y)
print('Slope (m) = ',slope ,' , Y Intercept= ',intercept,
      ' , R^2 = ',rs)
##Predict Values
NewX=11
NewY=(slope*NewX)+intercept
##Plot Data
plt.scatter(X,Y)
plt.scatter(NewX,NewY,color=[tuple(e/255 for e in (200,0,200))])
plt.plot(X,rLine)
plt.title('Fit Line to Data.')
plt.xlabel('X')
plt.ylabel('Y')
plt.show()


import numpy as np
import matplotlib.pyplot as plt

#rectangle rule
#int 3x^2 from 0..10
#the analytic answer=1000
def function(x):
    return 1/(1+x**2)

def rectangle_rule(initial,final,N):
    initial=initial
    final=final
    N=N+1
    h=(final-initial)/(N-1)
    x=np.linspace(initial,final,N)
    y=[]
    for i in x:
        y.append(function(i)*h)
    return np.sum(y)

#trapezoid rule (decrease the error from the rectangle rule)
def trapezoid_rule(initial,final,N):
    initial=initial
    final=final
    N=N+1
    h=(final-initial)/(N-1)
    x=np.linspace(initial,final,N)
    y=[]
    for i in range(len(x)-1):
        y.append((function(x[i])+function(x[i+1]))*h/2)
    return np.sum(y)

def simpson_rule(initial,final,N):
    initial=initial
    final=final
    N=N+1
    h=(final-initial)/(N-1)
    x=np.linspace(initial,final,N)
    y=[]
    for i in range(len(x)):
        if i==0 or i==len(x)-1:
            y.append((h/3)*function(x[i]))
        elif i%2==1:
            y.append((h/3)*4*function(x[i]))
        else:
            y.append((h/3)*2*function(x[i]))
    return (np.sum(y))

area=simpson_rule(0,1,4)
print(area)
area=rectangle_rule(0,1,4)
print(area)
area=trapezoid_rule(0,1,4)
print(area)


from math import factorial
import numpy as np
from matplotlib import pyplot as plt

#Function by https://stackoverflow.com/questions/4941753/is-there-a-math-ncr-function-in-python
import operator as op
def ncr(n, r):
    r = min(r, n-r)
    if r == 0: return 1
    numer = reduce(op.mul, xrange(n, n-r, -1))
    denom = reduce(op.mul, xrange(1, r+1))
    return numer//denom

# print ncr(24, 15)

#Interesting observation, a wave is made when you print ncr
def solution(limit=100):
	c = 0
	for n in range(23, limit + 1):
		for r in range(2, n):
			if ncr(n, r) > 1000000:
				c += 1
				# print ncr(n, r)
	print 'Number of values of nCr greater than 1000000: %s' % c

solution()

def list_of_ncr(limit=100):
	ncr_list = []
	for n in range(23, limit + 1):
		for r in range(2, n):
			if ncr(n, r) > 1000000:
				ncr_list.append(ncr(n, r))

	return ncr_list

plt.plot(list_of_ncr()[:100])
plt.ylabel("Pascal numbers > million")
plt.show()


import numpy as np
from numpy.linalg import norm
from scipy.linalg import eig

def poweriteration(A, tau=10e-8, x=None):
    """
    Approximates the eigenvalue with the greatest absolute value of a
    symmetric matrix A.
    """
    if A.shape[0] != A.shape[1]:
        raise ValueError("Matrix A is not symmetric.")
    if not x:
        x = np.ones((A.shape[0]))
    lam = x.dot(np.matmul(A, x))

    while True:
        x = np.matmul(A, x)/norm(np.matmul(A, x))
        lam_prev = lam
        lam = x.dot(np.matmul(A, x))

        if abs(lam) <= tau:
            cond = tau
        else:
            cond = tau*abs(lam)

        if norm(np.matmul(A, x) - lam*x) <= tau and abs(lam_prev - lam) <= cond:
            break

    return lam, x

def main():
    # Test 2x2
    print("### Test 2x2")
    A = np.array([[6, -1], [2, 3]])
    print(poweriteration(A)[0])
    print(eig(A)[0].real)

    # Test 3x3
    print("\n### Test 3x3")
    A = np.array([[1, 2, 1], [6, -1, 0], [-1, -2, -1]])
    print(poweriteration(A)[0])
    print(eig(A)[0].real)

if __name__ == "__main__":
    main()


'''
Munivariate statistics exercises
================================
'''
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#%matplotlib inline
np.random.seed(seed=42)  # make the example reproducible

'''
### Dot product and Euclidean norm
'''

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

def euclidian(x):
    return np.sqrt(np.dot(x, x))

euclidian(a)

euclidian(a - b)

np.dot(b, a / euclidian(a))

X = np.random.randn(100, 2)
np.dot(X, a / euclidian(a))

'''
### Covariance matrix and Mahalanobis norm
'''

N = 100
mu = np.array([1, 1])
Cov = np.array([[1, .8],
                [.8, 1]])

X = np.random.multivariate_normal(mu, Cov, N)

xbar = np.mean(X, axis=0)
print(xbar)

Xc = (X - xbar)

np.mean(Xc, axis=0)

S = 1 / (N - 1) * np.dot(Xc.T, Xc)
print(S)

#import scipy

Sinv = np.linalg.inv(S)

def mahalanobis(x, xbar, Sinv):
    xc = x - xbar
    return np.sqrt(np.dot(np.dot(xc, Sinv), xc))

dists = pd.DataFrame(
[[mahalanobis(X[i, :], xbar, Sinv),
  euclidian(X[i, :] - xbar)] for i in range(X.shape[0])],
            columns = ['Mahalanobis', 'Euclidean'])

print(dists[:10])

x = X[0, :]

import scipy.spatial
assert(mahalanobis(X[0, :], xbar, Sinv) == scipy.spatial.distance.mahalanobis(xbar, X[0, :], Sinv))
assert(mahalanobis(X[1, :], xbar, Sinv) == scipy.spatial.distance.mahalanobis(xbar, X[1, :], Sinv))


import jax.numpy as jnp

from ._base import ValueTransform

class LogTransform(ValueTransform):
    r"""

    A simple invertible log-transform.

    .. math::

        x\ \mapsto\ y\ =\ \lambda\,\text{sign}(x)\,
            \log\left(1+\frac{|x|}{\lambda}\right)

    with inverse:

    .. math::

        y\ \mapsto\ x\ =\ \lambda\,\text{sign}(y)\,
            \left(\text{e}^{|y|/\lambda} - 1\right)

    This transform logarithmically supresses large values :math:`|x|\gg1` and smoothly interpolates
    to the identity transform for small values :math:`|x|\sim1` (see figure below).

    .. image:: /_static/img/log_transform.svg
        :alt: Invertible log-transform
        :width: 640px

    Parameters
    ----------
    scale : positive float, optional

        The scale :math:`\lambda>0` of the linear-to-log cross-over. Smaller
        values for :math:`\lambda` translate into earlier onset of the
        cross-over.

    """
    __slots__ = ValueTransform.__slots__ + ('scale',)

    def __init__(self, scale=1.0):
        assert scale > 0
        self.scale = scale

        def transform_func(x):
            return jnp.sign(x) * scale * jnp.log(1 + jnp.abs(x) / scale)

        def inverse_func(x):
            return jnp.sign(x) * scale * (jnp.exp(jnp.abs(x) / scale) - 1)

        self._transform_func = transform_func
        self._inverse_func = inverse_func


# XOR problem using logistic regression

# Code Flow:
    # 1. Import all relevant libraries.
    # 2. Generate sample data.
    # 3. Generate random weights.
    # 4. Define sigmoid function.
    # 5. Calculate Y.
    # 6. Define Cross-entropy error function.
    # 7. Gradient Descent.
    # 8. Plots - Error & Weights-Mag.
    # 9. Classification Rate.

# 1. Import all relevant libraries:
import numpy as np
import matplotlib.pyplot as plt

# 2. Create input data:
N = 4 # No. of samples
D = 2 # No. of features

# XOR
X = np.array([
    [0, 0],
    [0, 1],
    [1, 0],
    [1, 1],
])

# Target:
T = np.array([0, 1, 1, 0])

# Display the data:
plt.figure(1)
plt.scatter(X[:,0], X[:,1], s=100, c=T, alpha=0.5)
plt.xlabel('X1')
plt.ylabel('X2')
plt.title('XOR Data')

# Bias term: Add a column of ones
ones = np.ones((N, 1))

# Concatenate X & bias term together:
Xb = np.concatenate((ones, X), axis=1)

# 3. Generate random weights:
w = np.random.randn(D + 1)

# 4. Sigmoid function
def sigmoid(z):
    return 1/(1 + np.exp(-z))

# 5. Calculate Y:
z = Xb.dot(w)
Y = sigmoid(z)

# 6. Cross-entropy error function:
def cross_entropy(T, Y):
    return -(T*np.log(Y) + (1-T)*np.log(1-Y)).sum()

# 7. Gradient Descent:
# Section 3D of the Jupyter Notebook for derivation/theory.

learning_rate = 0.001 # trial & error
error = [] # keep track of cross-entropy error
w_mags = [] # keep track of magnitude of weights

for i in range(20000):
    e = cross_entropy(T, Y)
    error.append(e) # append error
    if i % 1000 == 0:
        # print error every 10 epochs
        print(e)

    # Gradient descent weight update:
    w += learning_rate * Xb.T.dot(T - Y)

    w_mags.append(w.dot(w)) # append w_mag

    # Calculate Y:
    Y = sigmoid(Xb.dot(w))

print('Final w:', w)

# 8. Plots - Error & Weights-Mag:
plt.figure(2)
plt.plot(error)
plt.xlabel('Iteration')
plt.ylabel('Cross-Entropy Error')
plt.title("Cross-Entropy Error vs. Iteration")

plt.figure(3)
plt.plot(w_mags)
plt.xlabel('Iteration')
plt.ylabel('Magnitude of weights')
plt.title('w - magnitudes')

# 9. Classification Rate:
# This tells us what % of values are classified correctly.
# We round the output of sigmoid to get either 0 or 1.
# Then we calcualte the sum of the difference between T and round(Y).
# Divide that by N
# Subtract from 1 to get classification rate.
print('Final classification rate:', 1 - np.abs(T - np.round(Y)).sum() / N)
print('As you can see the classification rate in bad!')

2.Logistic Regression/1.Code - Using Theory/5.XOR_Bad.py


import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
from sklearn.decomposition import PCA

#rng = np.random.RandomState(1)
#X = np.dot(rng.rand(2, 2), rng.randn(2, 200)).T

def draw_base():
    rng = np.random.RandomState(1)
    X = np.dot(rng.rand(2, 2), rng.randn(2, 200)).T

    plt.scatter(X[:, 0], X[:, 1])
    plt.axis('equal');

def DRAW_VECTOR():
    rng = np.random.RandomState(1)
    X = np.dot(rng.rand(2, 2), rng.randn(2, 200)).T

    pca = PCA(n_components=2)
    pca.fit(X)

    print(pca.components_)
    print(pca.explained_variance_)

    def draw_vector(v0, v1, ax=None):
        ax = ax or plt.gca()
        arrowprops=dict(arrowstyle='->',
                    linewidth=2,
                    shrinkA=0, shrinkB=0)
        ax.annotate('', v1, v0, arrowprops=arrowprops)

    # plot data
    plt.scatter(X[:, 0], X[:, 1], alpha=0.2)
    for length, vector in zip(pca.explained_variance_, pca.components_):
        v = vector * 3 * np.sqrt(length)
        draw_vector(pca.mean_, pca.mean_ + v)
    plt.axis('equal');

def dimension_red():
    rng = np.random.RandomState(1)
    X = np.dot(rng.rand(2, 2), rng.randn(2, 200)).T

    pca = PCA(n_components=1)
    pca.fit(X)
    X_pca = pca.transform(X)
    print("original shape:   ", X.shape)
    print("transformed shape:", X_pca.shape)

    X_new = pca.inverse_transform(X_pca)
    plt.scatter(X[:, 0], X[:, 1], alpha=0.2)
    plt.scatter(X_new[:, 0], X_new[:, 1], alpha=0.8)
    plt.axis('equal');

plt.figure(1)
draw_base()

plt.figure(2)
DRAW_VECTOR()

plt.figure(3)
dimension_red()

plt.show()


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

import numpy as np
import numpy.linalg as LA

import matplotlib as mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['font.serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
import matplotlib.pyplot as plt

def curvature_3point(x, y):
    '''
    根据三个离散点计算曲率

    Parameters
    ----------
    x : list
        三个点x轴坐标列表
    y : list
        三个点y轴坐标列表

    Returns
    -------
    curvature : float
        曲率大小
    dircts : np.array
        曲率方向（标准化）

    References
    ----------
    - https://github.com/Pjer-zhang/Curvature_3Point
    - https://zhuanlan.zhihu.com/p/72083902
    '''

    t_a = LA.norm([x[1]-x[0], y[1]-y[0]])
    t_b = LA.norm([x[2]-x[1], y[2]-y[1]])

    M = np.array([[1, -t_a, t_a**2],
                  [1, 0, 0],
                  [1, t_b, t_b**2]])

    a = np.matmul(LA.inv(M), x)
    b = np.matmul(LA.inv(M), y)

    curvature = 2 * (a[2]*b[1] - b[2]*a[1]) / (a[1]**2.0 + b[1]**2.0) ** 1.5
    dircts = [b[1], -a[1]] / np.sqrt(a[1]**2.0 + b[1]**2.0)

    return curvature, dircts

def rolling_curvature_3point(data, xcol, ycol, n_pre=3, n_post=3, gap=1):
    '''
    | 三点法滚动计算曲率
    | data(`pd.DataFrame`)应包含[xcol, ycol]列
    | 返回df包含['curvature', 'dirct_x', 'dirct_y']三列
    '''

    df = data.reindex(columns=[xcol, ycol])
    df['curvature'] = np.nan
    df['dirct_x'] = np.nan
    df['dirct_y'] = np.nan

    for k in range(n_pre, df.shape[0]-n_post, gap):
        x = [df.loc[df.index[k-n_pre], xcol], df.loc[df.index[k], xcol],
             df.loc[df.index[k+n_post], xcol]]
        y = [df.loc[df.index[k-n_pre], ycol], df.loc[df.index[k], ycol],
             df.loc[df.index[k+n_post], ycol]]

        curvature, dircts = curvature_3point(x, y)

        df.loc[df.index[k], 'curvature'] = curvature
        df.loc[df.index[k], 'dirct_x'] = dircts[0]
        df.loc[df.index[k], 'dirct_y'] = dircts[1]

    return df[['curvature', 'dirct_x', 'dirct_y']]

def plot_curvature(data, cols, ax=None, plot_xy=True, std_dirt=False,
                   ax_equal=True, quvier_clor=None, xyline_styl=None,
                   figsize=(9, 6)):
    '''
    | 绘制曲率图像
    | data中的cols应包含绘制曲率所需的全部列，包括：
    | [x坐标列, y坐标列, 曲率值列, x方向列, y方向列]，且遵循此顺序
    '''

    df = data.reindex(columns=cols)
    df.columns = ['x', 'y', 'curvature', 'dirct_x', 'dirct_y']

    if not std_dirt:
        df['dirct_x'] = df['curvature'] * df['dirct_x']
        df['dirct_y'] = df['curvature'] * df['dirct_y']

    if ax is None:
        _, ax = plt.subplots(figsize=figsize, dpi=120)

    if plot_xy:
        if xyline_styl is not None:
            ax.plot(df['x'], df['y'], xyline_styl)
        else:
            ax.plot(df['x'], df['y'])
    ax.quiver(df['x'], df['y'], df['dirct_x'], df['dirct_y'],
              color=quvier_clor)

    if ax_equal:
        ax.axis('equal')

    if ax is None:
        plt.show()
    else:
        return ax

if __name__ == '__main__':
    import pandas as pd

    #%%
    # 圆
    df = pd.DataFrame({'theta': np.linspace(0, np.pi*2+0.2, 64)})
    df['x'] = 5 * np.cos(df['theta'])
    df['y'] = 5 * np.sin(df['theta'])

    n_pre = 1
    n_post = 1

    results = rolling_curvature_3point(df, 'x', 'y', n_pre, n_post)
    df = pd.merge(df, results, how='left', left_index=True, right_index=True)

    _, ax = plt.subplots(figsize=(6, 6), dpi=120)
    ax = plot_curvature(df, ['x', 'y', 'curvature', 'dirct_x', 'dirct_y'], ax,
                        plot_xy=False, std_dirt=True, ax_equal=False)
    plt.show()

    #%%
    # 正弦曲线
    df = pd.DataFrame({'theta': np.linspace(0, np.pi*2 + 0.2, 64)})
    df['x'] = 5 * (df['theta'])
    df['y'] = 5 * np.sin(df['theta'])

    n_pre = 1
    n_post = 1

    results = rolling_curvature_3point(df, 'x', 'y', n_pre, n_post)
    df = pd.merge(df, results, how='left', left_index=True, right_index=True)

    ax = plot_curvature(df, ['x', 'y', 'curvature', 'dirct_x', 'dirct_y'],
                        figsize=(6, 4), quvier_clor='r', xyline_styl='.-k')


import math
import numpy as np

_lambda = 0.02
r_c = 50

def prob(X: list, Y: list) -> float:
    return math.pow(_lambda, len(X)) * math.pow(1 - _lambda, len(Y))

def f(Y: list, T: list) -> int:

    dist = lambda a, b : np.sqrt(np.power(a[0]-b[0], 2) + np.power(a[1]-b[1], 2))
    cnt = 0

    # print('Y:', Y, 'T:', T)

    for node in Y:
        for target in T:
            if dist(node, target) >= r_c:
                cnt += 1

    return 1 if cnt >= 0 else 0

def Reliability(S: list, T: list) -> float:
    n = len(S)
    final_res = 0

    for ind in range(1 << n):
        X = []
        Y = []

        for i in range(n):
            if ind & (1 << i):
                X.append(S[i])
            else:
                Y.append(S[i])
        # print("X = ", X, "Y = ", Y)
        res = prob(X, Y)
        res *= f(Y, T)
        final_res += res

    return final_res

def Reliability_Super(S: list, T:list) -> float:
    relia = 1
    mul = 1
    for k in range(len(S)):
        mul *= abs(1 - Reliability(S[k], T))
    relia -= mul
    return relia


# coding: utf-8

## Simulated Annealing

# In[1]:

get_ipython().magic('matplotlib inline')

# In[3]:

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

## Definición del problema

# El objetivo de este ejemplo es encontrar el máximo de la función $f(x) = x \, \sin(4x \, - \, 2$), evaluada dentro del intervalo $[-2, 3]$. Supongamos que los números con los que vamos a trabajar los queremos representar con una presición de dos dígitos decimales (es decir, no habrán valores representables entre 0 y 0.01, por ejemplo).
#
# Para comenzar, definimos la función en cuestión:

# In[4]:

maximo = 3
minimo = -2
prec = 0.01

def func(x):
    return x * np.sin(4 * x - 2)

# En este caso, que es un problema ficticio, podemos graficar la función y observar donde se encuentra el máximo.
#
# O mejor, aplicando análisis matemático, podemos calcular la derivada de la función y calcular sus ceros, para luego ver cual de ellos es el máximo global.
#
# $\frac{df(x)}{dx} = \frac{d \left( x\,\sin(4x\,-\,2) \right)}{dx} = 4x\,\cos(2\,-\,4x)\,-\,\sin(2\,-\,4x) = 0$
#
# Esto permite estimar el máximo en $ x \approx 2.48852668729269 $

# In[5]:

X = np.arange(minimo, maximo, prec)
plt.plot(X, func(X))

maximo_x = 2.48852668729269
plt.plot(maximo_x, func(maximo_x), 'or')

## Implementación del Algoritmo

# Al igual que con *Hill Climbing*, si se quiere utilizar a *Simulated Annealing* como heurística de búsqueda, debemos definir estas tres cuestiones:
#
# 1. Cómo se van a representar las potenciales soluciones al problema y de que manera se podrán generar soluciones al azar.
#
# 2. Cómo calificar una soluciones potencial, para comparar dos soluciones representadas y poder determinar cual de las dos es mejor que la otra intentando resolver el problema (aunque ninguna de las dos realmente lo resuelva).
#
# 3. Cómo generar a partir de una solución dada un conjunto de nuevas soluciones vecinas a esta, lo que implica definir además una métrica de distancia entre soluciones.
#
# El algoritmo puede implementarse de la siguiente forma:

# In[6]:

def simulated_annealing(solucion_inicial, evaluacion, obtener_vecinos,
                        T_max, T_min, reduccion):
    """
    Simulated Annealing.
    """
    from random import random

    solucion_mejor = solucion_actual = solucion_inicial
    evaluacion_mejor = evaluacion_actual = evaluacion(solucion_actual)
    soluciones_evaluadas = 1

    T = T_max
    while T >= T_min:
        vecinos = obtener_vecinos(solucion_actual)
        for vecino in vecinos:
            evaluacion_vecino = evaluacion(vecino)
            soluciones_evaluadas += 1

            if (evaluacion_vecino > evaluacion_actual or
                random() < np.exp((evaluacion_vecino - evaluacion_actual) / T)):
                solucion_actual = vecino
                evaluacion_actual = evaluacion_vecino
                if evaluacion_mejor < evaluacion_actual:
                    solucion_mejor = solucion_actual
                    evaluacion_mejor = evaluacion_actual

        T = reduccion * T

    return solucion_mejor, soluciones_evaluadas

## Representación y generación de potenciales soluciones

# Si a las soluciones se las representan como valores en punto flotante, puede generarse una solución al azar usando la función `random()` y escalando el valor al intervalo de trabajo, redondeando con `round()` a la cantidad de decimales que define la precisión.

# In[7]:

def nueva():
    return round(minimo + random.random() * (maximo - minimo), 2)

# In[11]:

print(nueva(), nueva(), nueva(), nueva(), nueva(), nueva(), sep='    ')

## Evaluación de las soluciones

# Para evaluar una solución, podemos usar directamente la función que queremos maximizar ya que si la calificación de un punto será el valor de la función evaluada en ese punto, cuanto mayor sea mejor será resolviendo el problema.

# In[12]:

def evaluacion(x):
    return func(x)

## Generación de soluciones vecinas

# Para generar soluciones dentro de la vecindad de una solución dada, simplemente generamos 4 nuevas soluciones: $[ x-0.03, x-0.01, x+0.01, x+0.03]$. Estas cuatro soluciones tienen la particularidad que se encuentran dentro del entorno $(x \pm 0.05)$ y son valores representados con la precisión deseada.
#
# Es importante tomar la precaución de no generar soluciones fuera del rango permitido. Por ejemplo, si $x=3$, no deberían generarse soluciones mayores que x pues se encontrarían fuera del rango fijado para el problema.

# In[13]:

def vecinos(x):
    v = []
    d = -0.03
    for i in range(4):
        n = x + d
        if n >= minimo and n <= maximo:
            v.append(n)
        d += 0.02
    return v

# In[14]:

print(vecinos(0.5), vecinos(1), vecinos(minimo), vecinos(maximo), sep="\n")

## Parámetros del algoritmo

# Para determinar la temperatura inicial (`T_max`) o la temperatura final (`T_min`), debemos preguntarnos:
#
# ¿Con qué probabilidad $prob$ quiero que se elijan soluciones peores que la actual en $E = eval(vecino) - eval(actual)$?
#
# Sabiendo que $prob = e^{\frac{eval(vecino) - eval(actual)}{t}}$, obtenemos que $T = \frac{eval(vecino) - eval(actual)}{ln(prob)}$.
#
# Así, si se quiere que inicialmente no se acepten soluciones peores que la actual en $0.5$, usando como $prob = 0.01$, se obtiene:

# In[15]:

dif  = 0.5
prob = 0.01

T_max = -dif / np.log(prob)
T_max

# Graficando la curva de probabilidad de aceptración para `T_max`:

# In[16]:

X = np.linspace(-2*dif, 0, 100)
plt.plot(X, np.exp(X / T_max), -dif, prob, '+r');

# De la misma forma, si al final de la ejecución no se quieren soluciones peores que la actual en $0.01$, usando como $prob = 0.01$, se obtiene:

# In[17]:

dif  = 0.01
prob = 0.01

T_min = -dif / np.log(prob)
T_min

# In[18]:

X = np.linspace(-2*dif, 0, 100)
plt.plot(X, np.exp(X / T_min), -dif, prob, '+r');

# El factor de reducción estará asociado a la cantidad de pasos que queremos realizar para ir desde `T_max` hasta `T_min`. El algoritmo funcionará reduciendo la temperatura actual multiplicándola por el factor de reducción: $T_{(i)} = r \cdot T_{(i-1)}$
#
# Partiendo con $T = T_{max}$ y reduciendo la temperatura de la forma indicada, $T < T_{min}$ cuando $T_{max} \cdot  r \cdot  r \cdot \, ... \, \cdot  r < T_{min}$, o sea,  $T_{max} \cdot  r ^ N < T_{min}$.
#
# Así, para determinar el factor de reducción $r$ a partir de la cantidad de reducciones $N$ que se quiera hacer, simplemente puede calcularse:
#
# $T_{max} \cdot  r ^ N = T_{min}
# \quad \Rightarrow \quad
# r ^ N = \frac{T_{min}}{T_{max}}
# \quad \Rightarrow \quad
# \log {r ^ N} = \log {\frac{T_{min}}{T_{max}}}
# \quad \Rightarrow \quad
# N \cdot \log r = \log {\frac{T_{min}}{T_{max}}}
# \quad \Rightarrow \quad
# \log r = \frac {\log {\frac{T_{min}}{T_{max}}}} {N}
# \quad \Rightarrow \quad
# r = e ^ {\left ( {\frac {\log {\frac{T_{min}}{T_{max}}}} {N}} \right ) }$
#
# Si $N = 30$, el factor de reducción $r$ a usar sería:

# In[19]:

N = 30
reduccion = np.exp(np.log(T_min / T_max) / N)
reduccion

# Podemos comprobar que eso es efectivamente así:

# In[20]:

i = 0
T = T_max
while T >= T_min:
    print("Iteración", i, "- Temperatura:", T)
    T = reduccion * T
    i += 1

## Obteniendo resultados

# Finalmente, podemos invocar al algoritmo generando una solución inicial al azar y pasando la función de evaluación y la función generadora de vecinos:

# In[21]:

inicial = nueva()
mejor, evaluaciones = simulated_annealing(inicial, evaluacion, vecinos,
                                          T_max=T_max,
                                          T_min=T_min,
                                          reduccion=reduccion)

# Podemos ver el resultado de la ejecución junto con la cantidad de soluciones que tuvieron que evaluarse para conseguirla:

# In[22]:

X = np.arange(minimo, maximo, prec)

plt.plot(X, func(X),
         maximo_x, func(maximo_x), 'or',  # Punto rojo: Máximo de la función
         inicial, func(inicial), 'oy',    # Punto amarillo: Solución inicial
         mejor, func(mejor), 'ob')        # Punto azul: Mejor solución encontrada

print("Cantidad de soluciones evaluadas:", evaluaciones)
print("Solución inicial:", inicial)
print("Solución obtenida:", mejor)
print("Valor de func para esa solución:", func(mejor))

# Podemos interactuar con el algoritmo observando todas las soluciones que generó a lo largo de su ejecución. Para ello, modificamos el algoritmo para que las devuelva:

# In[23]:

def simulated_annealing(solucion_inicial, evaluacion, obtener_vecinos,
                        T_max, T_min, reduccion):
    """
    Simulated Annealing.
    """
    from random import random

    soluciones = [solucion_inicial]
    solucion_mejor = solucion_actual = solucion_inicial
    evaluacion_mejor = evaluacion_actual = evaluacion(solucion_actual)
    soluciones_evaluadas = 1

    T = T_max
    while T >= T_min:
        vecinos = obtener_vecinos(solucion_actual)
        for vecino in vecinos:
            evaluacion_vecino = evaluacion(vecino)
            soluciones_evaluadas += 1

            if (evaluacion_vecino > evaluacion_actual or
                random() < np.exp((evaluacion_vecino - evaluacion_actual) / T)):
                solucion_actual = vecino
                evaluacion_actual = evaluacion_vecino
                soluciones.append(vecino)
                if evaluacion_mejor < evaluacion_actual:
                    solucion_mejor = solucion_actual
                    evaluacion_mejor = evaluacion_actual

        T = reduccion * T

    return solucion_mejor, soluciones_evaluadas, soluciones

# In[24]:

from IPython.html.widgets import interact, IntSliderWidget

# El siguiente código ejecuta el algorimo modificado y grafica en forma interactiva cada una de las soluciones elegidas (en color verde) comenzando a partir de la solución inicial (color amarillo). También indica el máximo de la función (color rojo) y la mejor solución encontrada durante la ejecución (color azul):

# In[25]:

# Usando una solución inicial fija, puede verse como cambia el comportamiento del algoritmo.
inicial = 1.6

mejor, evaluaciones, soluciones = simulated_annealing(inicial, evaluacion, vecinos,
                                          T_max=T_max, T_min=T_min, reduccion=reduccion)

X = np.arange(minimo, maximo, prec)
puntos = np.array(soluciones)

def p(t):
    plt.plot(X, func(X),
         maximo_x, func(maximo_x), 'or',    # Punto rojo: Máximo de la función
         inicial, func(inicial), 'oy',      # Punto amarillo: Solución inicial
         puntos[t], func(puntos[t]), 'og',  # Punto verde: Solución intermedia
         mejor, func(mejor), 'ob')          # Punto azul: Mejor solución hallada
    plt.show()

i = interact(p, t=IntSliderWidget(min=0, max=len(soluciones)-1, step=1, value=0))

print("Cantidad de soluciones evaluadas:", evaluaciones)

# In[ ]:

Argentina - Mondiola Rock - 90 pts/Practica/Simulated Annealing/Simulated Annealing.py

Argentina - Mondiola Rock - 90 pts/Practica/TP2/Implementaciones de German/Simulated Annealing/Simulated Annealing.py