# -----------------------------------------------------------------------------
# Copyright (c) 2020, Miguel Angel Avila Torres. All Rights Reserved.
# -----------------------------------------------------------------------------
import model.hydrogen as hy
import model.numerical_methods as methods

import numpy as np

from tkinter import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure

class MonteCarloProbabilityGraph(object):

    def __init__(self, window: Frame, arguments: dict, meridians=50):
        """
        :param window: The frame which will draw the plot
        :param arguments: a dictionary with {str: StringVar} entries
        :param meridians: the number of meridians enclosing the radius r_b in the integral
        """
        self.window = window
        self.arguments = arguments
        self.meridians = meridians
        self.button = Button(master=window, text='Simulate', command=self.__plot_monte_carlo_probability)
        self.button.pack()
        self.probability = Label(master=window)
        self.probability.pack()
        self.error = Label(master=window)
        self.error.pack()
        self.fig = Figure(figsize=(5, 5))
        self.points = self.fig.add_subplot(111, projection='3d')
        self.canvas = FigureCanvasTkAgg(self.fig, master=window)
        self.__plot_monte_carlo_probability()

    @staticmethod
    def wire_frame_sphere(radius=1E-10, n_meridians=20, n_circles_latitude=None):
        """
        :param radius: the radius for the sphere limiting with the meridians
        :param n_meridians: the number of meridians
        :param n_circles_latitude: the latitude of the meridians
        :return: the coordinates for the meridians
        """
        if n_circles_latitude is None:
            n_circles_latitude = max(n_meridians / 2, 4)
        u, v = np.mgrid[0:2 * np.pi:n_meridians * 1j, 0:np.pi:n_circles_latitude * 1j]
        sphere_x = radius * np.cos(u) * np.sin(v)
        sphere_y = radius * np.sin(u) * np.sin(v)
        sphere_z = radius * np.cos(v)
        return sphere_x, sphere_y, sphere_z

    def __plot_monte_carlo_probability(self):
        """
        plots the monte carlo integral of the absolute squared
        hydrogen 1s wave function
        :return: None
        """
        r_a, r_b, theta_a, theta_b, phi_a, phi_b, samples = [float(e.get()) for e in self.arguments.values()]

        def psi_100_(r_i, theta_i, phi_i):
            return np.abs(hy.hydrogen_1s(r_i, theta_i, phi_i)) ** 2

        p_1s, r, theta, phi, volume = methods.spherical_monte_carlo_integral(
            psi_100_, r_a, r_b, theta_a, theta_b, phi_a, phi_b, int(samples)
        )

        self.points.clear()
        self.points.set_title('\nMonte Carlo Integral of |\u03A8|^2\nin three dimensions', fontsize=18)

        x_ = r * np.sin(theta) * np.cos(phi)
        y_ = r * np.sin(theta) * np.sin(phi)
        z_ = r * np.cos(theta)

        self.points.scatter3D(x_, y_, z_, color='blue', linewidths=0.1, alpha=0.1)
        self.points.plot_wireframe(*self.wire_frame_sphere(r_b, self.meridians), color='orange', alpha=0.2)
        # self.fig.savefig(fname='monte-carlo.png')  # it saves locally the plotted image
        self.probability['text'] = '\nProbability: ' + str(p_1s) + '\n'
        self.error['text'] = 'Error: ' + str(methods.spherical_monte_carlo_estimation_error(psi_100_, r, theta, phi,
                                                                                volume, samples))
        self.canvas.get_tk_widget().pack()
        self.canvas.draw()


############################################
# n by n System of Linear Equations Solver #
#             Radin Shayanfar              #
#                 9731032                  #
############################################

from sys import exit
import numpy as np

def count_zeros(arr):
    zeros = []
    for row in arr:
        for ind, element in enumerate(row):
            if element != 0:
                zeros.append(ind)
                break
            elif ind == arr.shape[1] - 1:
                zeros.append(ind + 1)
                break

    return zeros

def sort_by_zeros(arr):
    zeros = count_zeros(arr)
    arr_sorted = arr[np.argsort(zeros)]

    return np.sort(zeros), arr_sorted

def print_mat(matrix):
    print(matrix)
    print('==============')

def rr_down(sliced):
    sliced[0] = sliced[0] / sliced[0][0]
    for r_ind in range(1, sliced.shape[0]):
        coef = -1 * sliced[r_ind][0]
        sliced[r_ind] = sliced[r_ind] + coef * sliced[0]

def rr_up(sliced):
    for r_ind in range(0, sliced.shape[0] - 1):
        coef = -1 * sliced[r_ind][0]
        sliced[r_ind] = sliced[r_ind] + coef * sliced[-1]

def forward_phase(augmented):
    for i in range(augmented.shape[0]):
        sliced = augmented[i:, :]
        zeros, sliced = sort_by_zeros(sliced)
        if zeros[-1] == n + 1:
            print("Unlimited answers")
            exit(0)
        elif zeros[-1] == n:
            print("No answer")
            exit(0)
        pivot_position = zeros[0]
        rr_down(sliced[:, pivot_position:])
        augmented[i:, :] = sliced
        print_mat(augmented)

def backward_phase(augmented):
    for i in reversed(range(1, augmented.shape[0])):
        sliced = augmented[:i + 1, :]
        rr_up(sliced[:, i:])
        augmented[:i + 1, :] = sliced
        print_mat(augmented)

n = int(input("Enter dimension:\n"))
print("Enter coefficients matrix:")
coefs = []
for i in range(n):
    coefs.append(list(map(float, input().split())))
b = list(map(float, input("Enter vector b:\n").split()))

coefs = np.array(coefs, dtype=np.double)
b = np.array([b], dtype=np.double).T

augmented = np.concatenate((coefs, b), axis=1)
print("Augmented Matrix:")
print(augmented)

print("Forward Phase:")
forward_phase(augmented)
print("Backward Phase:")
backward_phase(augmented)

for i in range(n):
    print(f"x{i + 1}:\t{augmented[i, -1]}")


import sympy

class Problem:
    def __init__(self):
        '''创建目标函数'''

        self.x = sympy.symbols('x')
        self.create_y()

    def create_y(self):
        '''创建目标函数及其一、二阶导数'''

        self.y = sympy.log(sympy.exp(self.x) + sympy.exp(- self.x)) # 目标函数
        self.dy = sympy.diff(self.y,self.x) # 一阶导
        self.dy2 = sympy.diff(self.dy,self.x) # 二阶导

    def newton(self,x0,x_tol=1e-8,fun_tol=1e-8,max_iter=20):
        """使用牛顿法求解

        Arguments:
            x0 {[float]} -- [初始值]

        Keyword Arguments:
            x_tol {[float]} -- [variable tolerance] (default: {1e-8})
            fun_tol {[float]} -- [function value tolerance] (default: {1e-8})
            max_iter {[float]} -- [最大迭代次数] (default: {20})

        Returns:
            [list] -- [返回每次迭代的变量值和函数值]
        """

        alpha = 0.2
        beta = 0.5
        x = x0
        num = 0 # 记录迭代次数
        iter_x = [x0] # 记录每次迭代的 x
        iter_y = [float(self.y.subs(self.x,iter_x[-1]))] # 记录每次迭代的 y
        while 1:
            '''牛顿迭代'''
            delta_x = float((- self.dy / self.dy2).subs(self.x,x))
            t = 1
            while float(self.y.subs(self.x,x + t * delta_x)) > float(self.y.subs(self.x,x)) + alpha * t * float(delta_x * self.dy.subs(self.x,x)): # backtracking stop condition
                '''backtracking'''
                t = beta * t
            if abs(t * delta_x) < x_tol:
                break # stop criterior
            if abs(alpha * t * float(delta_x * self.dy.subs(self.x,x))) < fun_tol:
                break # stop criterior
            x = x + t * delta_x
            iter_x.append(x)
            iter_y.append(float(self.y.subs(self.x,iter_x[-1])))
            num += 1
            if num > max_iter:
                break
        return iter_x,iter_y

if __name__=='__main__':
    fun_tol = 1e-8
    x_tol = 1e-8
    max_iter = 5
    problem = Problem()
    iter_x,iter_y = problem.newton(1,x_tol,fun_tol,max_iter)
    print(iter_x)
    print(iter_y)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

''' Linear regression used to predict quantitive response (y)
    from the predictor variable (x)
'''

import numpy as np
np.set_printoptions(precision=2)

''' https://www.desmos.com/calculator '''
''' linear regression using Ordinary Least Squares
'''

import random

def f(x):
    res = -(x* 10) + 45
    error = res * random.uniform(-0.18, 0.18)
    return res + error

values = []
''' now using f(x) we are going to create 300 values. '''
for i in range(0, 50):
    x = random.uniform(1, 50)
    x = np.round(x)
    y = f(x)
    y = np.round(y)
    values.append((x, y))

from sklearn import linear_model
regr = linear_model.LinearRegression(
        fit_intercept=True,
        normalize=False,
        copy_X=True,
        n_jobs=26)
''' split the values into two series
    instead a list of tuples  '''
x, y = zip(*values)
max_x = max(x)
min_x = min(x)
cut_value = 10

''' split the values in train data and test data. '''
train_data_X = map(lambda x: [x], list(x[:-cut_value]))
train_data_X = np.array(list(train_data_X))
train_data_Y = list(y[:-cut_value])
train_data_Y = np.array(list(train_data_Y))

test_data_X = map(lambda x: [x], list(x[-cut_value:]))
test_data_X = np.array(list(test_data_X))
test_data_Y = list(y[-cut_value:])
test_data_Y = np.array(list(test_data_Y))

''' feed the linear regression with the train data
    to obtain a model. '''
regr.fit(train_data_X, train_data_Y)

''' check that the coeffients are the expected ones. '''
''' y = mx + b '''
m = regr.coef_[0]
b = regr.intercept_
print(' y = {0} * x + {1}'.format(m, b))

import matplotlib.pyplot as plt

''' now we are going to plot the points
    and the model obtained '''
plt.scatter(train_data_X, train_data_Y, color='blue')
plt.scatter(test_data_X,  test_data_Y,  color='green')
plt.plot([min_x, max_x], [b, m*max_x + b], 'r')
plt.title('Fitted linear regression (blue: train, green: test)', fontsize=16)
plt.xlabel('x', fontsize=13)
plt.ylabel('y', fontsize=13)

import numpy as np
''' Mean squared error '''
print("Mean squared error: %.2f" % np.mean((
        regr.predict(test_data_X) - test_data_Y) ** 2))
''' Variance : 1 is perfect prediction '''
print('Variance score: %.2f' % regr.score(
        test_data_X, test_data_Y))


import numpy as np
import random as rd
from scipy.optimize import curve_fit
from matplotlib import pyplot as plt

def model_function(x, p1, p2):
    """
    - the function that needs to be modeled by the data
    - the param_set is a list of two parameters (p1, p2)
    """
    # p1, p2 = param_set
    try:
        assert p1 >= 0 and p2 >= 0
    except AssertionError as error:
        print(
            f'Cannot compute the model function since the two parameters are invalid -> {param_set}')
        return -1
    else:
        pass
    f = pow(x, 2) * p1 - (pow(p2, 3) + 2)
    return f

def generateTestData(param_set):
    x_0 = 5.0
    shift_quantity = 2.5
    step = 0.1

    p1, p2 = param_set

    x_data = np.arange(-x_0, x_0 + step, step)

    y_data = [model_function(x, p1, p2) for x in x_data]
    y_data_shifted = [model_function(
        x, p1, p2) + rd.uniform(-shift_quantity, shift_quantity) for x in x_data]

    data = [[round(x[0], 4), round(x[1], 4), round(x[2], 4)]
            for x in zip(x_data, y_data, y_data_shifted)]

    with open('data.dat', 'w+') as f:
        for data_element in data:
            f.write(str(data_element[0]) + ' ' +
                    str(data_element[1]) + ' ' + str(data_element[2]))
            f.write('\n')

    return (x_data, y_data, y_data_shifted)

def find_fit(data, initial_guess):
    x_data = data[0]
    y_data_th = data[2]
    if(initial_guess == -1):
        fittedParameters, pcov = curve_fit(model_function, xdata=x_data,
                                           ydata=y_data_th)
    else:
        fittedParameters, pcov = curve_fit(model_function, xdata=x_data,
                                           ydata=y_data_th, p0=initial_guess)

    return fittedParameters

def plotFitResults(data, model_params, curve_fit_params):
    """
    - create a plot comparison between the data evaluated with the initial set of parameters and the parameters obtained via the curve fit
    """

    x_data = data[0]

    p1_exp, p2_exp = model_params
    y_data_exp = [model_function(x, p1_exp, p2_exp) for x in x_data]

    p1_th, p2_th = curve_fit_params
    y_data_th = [model_function(x, p1_th, p2_th) for x in x_data]

    plt.plot(x_data, y_data_exp, 'ok', label='exp.')
    plt.plot(x_data, y_data_th, '-r', label='th.')
    plt.legend(loc='best')
    plt.xlabel('x')
    plt.ylabel('f(x)')
    plt.tight_layout()
    plt.savefig('curve_fit_plot.pdf', dpi=300, bbox_inches='tight')
    plt.close()

def main():
    test_param_set = [2.2, 2.5]
    initial_guess = [1, 3]
    data = generateTestData(test_param_set)
    cvf1 = find_fit(data, -1)
    cvf2 = find_fit(data, initial_guess)
    print(
        f'results for the fit without initial guess -> {cvf1}\nThe actual model parameters -> {test_param_set}')
    print(
        f'results for the fit WITH initial guess -> {cvf2}\nThe actual model parameters -> {test_param_set}')
    plotFitResults(data, test_param_set, cvf1)

if __name__ == '__main__':
    main()


# This function takes the D x N data matrix with columns indicating
# different data points and project the D dimensional data into the r
# dimensional space. Different types of projections are possible:
# (1) Projection using PCA
# (2) Projection using random projections with iid elements from N(0,1/r)
# (3) Projection using random projections with iid elements from symmetric
# bernoulli distribution: +1/sqrt(r),-1/sqrt(r) elements with same probability
# X: D x N data matrix of N data points
# r: dimension of the space to project the data to
# type: type of projection, {'PCA','NormalProj','BernoulliProj'}
# Xp: r x N data matrix of N projectred data points

import numpy as np
import math

def DataProjection(X, r, type='NormalProj'):
    Xp = None
    D, N = X.shape
    if r == 0:
        Xp = X
    else:
        if type == 'PCA':
            isEcon = False
            if D > N:
                isEcon = True
            U, S, V = np.linalg.svd(X.T, full_matrices=isEcon)
            Xp = U[:, 0:r].T
        if type == 'NormalProj':
            normP = (1.0 / math.sqrt(r)) * np.random.randn(r * D, 1)
            PrN = normP.reshape(r, D, order='F')
            Xp = np.matmul(PrN, X)
        if type == 'BernoulliProj':
            bp = np.random.rand(r * D, 1)
            Bp = (1.0 / math.sqrt(r)) * (bp >= .5) - (1.0 / math.sqrt(r)) * (bp < .5)
            PrB = Bp.reshape(r, D, order='F')
            Xp = np.matmul(PrB, X)
    return Xp

if __name__ == "__main__":
    pass


"""
Title: Finding where the function f(x) = x^4 - 7x + 10 has its minimum and determining the minimum value of the function

Solution to Problem Set 1, Problem 4
~ Arsh R. Nadkarni

To run:
python newton_1_4.py

Can change initial guess (x0), tolerance (eps), and number of iterations (n) in the function call on line 37

"""
# import libraries
import numpy as np

# define f(x)
def f(x):
    return x**4-7*x+10
# define the first derivative of f(x)
def derf(x):
    return 4*x**3-7
# construct the newton method function
def newton(x0,eps,n):
    init = 1 # initial counter variable
    inf_init = 1 # initial infinite loop counter variable
    while True:
        xmin = x0 - f(x0)/derf(x0) # newton's method condition
        print('Iteration No-%d: xmin = %0.3f and f(xmin) = %0.3f' % (init, xmin, f(xmin)))
        x0 = xmin # update x0
        init += 1 # iterate the counter variable
        if init > n: # break the loop if it goes to infinite iterations
            inf_init = 0
            break
    print("Minimum:", np.min(xmin)) # print the minimum for the function
    print("The minimum value of f(x):", np.min(f(xmin))) # print the minimum value for the function

newton(3,1e-3,5) # call the function

Problem Sets/Problem Set 1/newton_1_4.py


import io
import base64
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('Qt5agg')
from sympy import Function, Symbol, diff
from sympy.parsing.sympy_parser import parse_expr

class Grapher:

    def __init__(self, function, a=-10, b=10, points=100):
        self.f = parse_expr(function)
        self.x = Symbol("x")
        self.a = float(a)
        self.b = float(b)
        self.points = int(points)

    @staticmethod
    def convert_graph_to_b64(fig):
        graph_string = io.BytesIO()
        fig.savefig(graph_string, format="jpg")
        graph_string.seek(0)
        return base64.b64encode(graph_string.read())

    def get_diffs(self):
        return diff(self.f, self.x).simplify()

    def run(self):
        x = np.linspace(self.a, self.b, self.points)
        y = [self.f.subs(self.x, _x) for _x in x]
        fig, ax = plt.subplots()
        ax.plot(x, y)
        ax.set(title=f"{self.f}")
        ax.grid()

        result = {
            "graph_image": self.convert_graph_to_b64(fig).decode(),
            "derivative_function": str(diff(self.f, self.x).simplify()),
        }

        _simplify = str(self.f.simplify())
        result["simplified_function"] = _simplify if str(self.f) != _simplify else ''

        return result

if __name__ == "__main__":
    f = "x**2 - x**3 + x + x + 1"
    G = Grapher(f)
    print(G.run())


from numpy import array, append, cross, sign, seterr, around, array_equal
from numpy.linalg import det, solve
seterr(all='raise')

def dist(p,q):
    """ Calculates the distance between the points p and q.

    Parameters
    ---------
    p : Point
        the first point

    q : Point
        the second point

    Returns
    ----------
    Float
        The distance between the points p and q.
    """
    return (sum((p.array()-q.array())**2))**0.5

def orient(*points):
    """ Performs the ccw test to determine the orientation of the points.

    Parameters:
    -------------
    points : Points
        An arbitrary number of Points to determine the orientation of

    Returns
    -------
    Integer
        Returns 1 if the points are ordered in ccw order. Returns -1 if the points are ordered in clockwise order.
        Returns 0 if the points are colinear.
    """
    points = [p.array() for p in points]
    d = det(array(points))
    if d > 0:
        return 1
    elif d < 0:
        return -1
    else:
        return 0

def incircle(a,b,c,d):
    """ Determines if the Point d is inside of the circle formed by the traingle a,b,c.

    Parameters
    ------------
    a : Point
        The first point of the triangle

    b : Point
        the second point on the triangle

    c : Point
        The third point on the triangle

    Returns
    -------------
    Integer
        Returns 0 if the point d is on the circle. Returns -1 if the point d lies outside the circle
        Returns 1 if the point d lies inside the circle.
    """

    zero = Point(0,0)
    _a = [a[0], a[1], dist(a, zero)**2, 1]
    _b = [b[0], b[1], dist(b, zero)**2, 1]
    _c = [c[0], c[1], dist(c, zero)**2, 1]
    _d = [d[0], d[1], dist(d, zero)**2, 1]
    A = array([_a, _b, _c, _d])
    d = around(det(A),decimals=6)
    if d == 0:
      return 0
    return sign(d)*orient(a,b,c)

def circumcenter(a,b,c):
    """Finds the circumcenter of a triangle.

    Parameters
    ------------
    a : Point
        The first point of the triangle

    b : Point
        the second point of the triangle

    c : Point
        the third point of the triangle

    Returns
    -------------
    Point
        The circumcenter of the triangle
    """
    zero = Point(0,0)
    d = 2*((a[0]*(b[1]-c[1])) + (b[0]*(c[1]-a[1])) + (c[0]*(a[1]-b[1])))
    x = ((dist(a, zero)**2)*(b[1]-c[1]) + (dist(b, zero)**2)*(c[1]-a[1]) + (dist(c, zero)**2)*(a[1]-b[1])) / d
    y = ((dist(a, zero)**2)*(c[0]-b[0]) + (dist(b, zero)**2)*(a[0]-c[0]) + (dist(c, zero)**2)*(b[0]-a[0])) / d
    return Point(x,y)

# Slope from a to b
def slope(a,b):
    """ Gets the slope between two points.

    Parameters
    -------------
    a : Point
        The first point in the slope calculation.

    b : Point
        The second point is the slope calculation.

    Returns
    ------------
    Float
        The slope between the two points. If we have the case where we divide by zero, if b[1] > a[1], then
        we return float('inf'). Otherwise, returns float('-inf').
    """
    try:
        return (b[1]-a[1]) / (b[0]-a[0])
    except (ZeroDivisionError, RuntimeWarning, FloatingPointError) as e:
        if (b[1] > a[1]):
            return float('inf')
        else:
            return float('-inf')

class Point:
    """This class represents a point that has been lifted up to some z plane."""

    def __init__(self, *coordinates, z = 1):
        """
        Parameters
        --------------

        coordinates : Integers or Floats
            An arbitrary number of Intergers and Floats that correspond to the coordinates of the point to be created.

        z : Integer
            The z plane for the coordinate to be lifted up to. Defaults to z=1.
        """

        self._p = append(coordinates, z)

    def array(self):
        """Gets an array of the coordinates of the point.

        Returns
        ----------
        numpy.Array
            An array containing the coordinates of the point.
        """
        return self._p

    def __eq__(self, other):
        return array_equal(self._p, other._p)

    def __lt__(self, other):
        if (self._p[0] != other._p[0]):
            return self._p[0] < other._p[0]
        else:
            return self._p[1] < other._p[1]

    def __str__(self):
        return str(self._p[:2])

    def __getitem__(self, index):
        return self._p[index]


import numpy as np

def minmax(x):
	'''
	x m*1
	'''
	min_ = np.min(x)
	max_ = np.max(x)
	x_ = np.zeros_like(x,dtype=float)
	for i in range(x.shape[0]):
		x_[i] = (x[i] - min_) / (max_ - min_)
	return x_

if __name__ == "__main__":
	X = np.array([0, 15, -9, 7, 12, 3, -21])
	print(minmax(X))


'''

    A collection of functions to perform portfolio analysis.

    Max Gosselin, 2019

'''
import numpy as np
import pandas as pd
from scipy import optimize

def portfolio_metrics(weights, avg_xs_returns, covariance_matrix):
    ''' Compute basic portfolio metrics: return, stdv, sharpe ratio '''

    portfolio_return = np.sum(weights * avg_xs_returns)
    portfolio_stdv = np.sqrt(np.dot(weights.T, np.dot(weights, covariance_matrix)))

    portfolio_sharpe = portfolio_return / portfolio_stdv

    tickers = covariance_matrix.columns

    metrics = {
        'return': portfolio_return,
        'stdv': portfolio_stdv,
        'sharpe': portfolio_sharpe,
        'weights': weights
        }
    metrics.update(dict([(ticker, weight) for ticker, weight in zip(tickers, weights)]).items())

    return metrics

def simulate_portfolios(iters, xs_stats, covariance_matrix):
    ''' What we want here is to randomly generate portfolios that will sit
        inside the efficiency frontier for illustrative purposes '''

    # Set up an empty array to store our generated portfolios
    simulations = []

    while iters > 1:

        weights = np.random.random(len(xs_stats.columns))
        weights /= np.sum(weights)

        simulations.append(portfolio_metrics(weights, xs_stats.loc['Avg'], covariance_matrix))

        iters -= 1

    return simulations

def solve_minvar(xs_avg, covariance_matrix):
    ''' Solve for the weights of the minimum variance portfolio

        Constraints:
            sum of weights = 1,
            weights bound by [0, 0.2],

        Returns the weights and the jacobian used to generate the solution.

        '''

    def __minvar(weights, xs_avg, covariance_matrix):
        ''' Anonymous function to compute stdv '''
        return np.sqrt(np.dot(weights.T, np.dot(weights, covariance_matrix)))

    p_size = len(xs_avg)
    args = (xs_avg, covariance_matrix)
    constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}]
    bounds = [(0, 0.2)] * p_size

    minimized_weights = optimize.minimize(__minvar, np.zeros(p_size), args=args,
                        method='SLSQP', bounds=bounds, constraints=constraints, options={'maxiter':1000})

    return minimized_weights

def solve_maxsharpe(xs_avg, covariance_matrix):
    ''' Solve for the weights of the maximum Sharpe ratio portfolio

        Constraints:
            sum of weights = 1,
            weights bound by [0, 0.2],

        Returns the weights and the jacobian used to generate the solution.

        '''
    def __max_by_min_sharpe(weights, xs_avg, covariance_matrix):
        ''' Anonymous function to compute sharpe ratio, note that since scipy only minimizes we go negative. '''
        pm = portfolio_metrics(weights, xs_avg, covariance_matrix)
        return -pm['return'] / pm['stdv']

    p_size = len(xs_avg)
    args = (xs_avg, covariance_matrix)
    constraints = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}]
    bounds = [(0, 0.2)] * p_size

    minimized_weights = optimize.minimize(__max_by_min_sharpe, ((1/p_size) * np.ones(p_size)), args=args,
                        method='SLSQP', bounds=bounds, constraints=constraints, options={'maxiter':1000})

    return minimized_weights

def solve_for_target_return(xs_avg, covariance_matrix, target):
    ''' Solve for the weights of the minimum variance portfolio which has
        a specific targeted return.

        Constraints:
            sum of weights = 1,
            weights bound by [0, 0.2],
            portfolio return = target return,

        Returns the weights and the jacobian used to generate the solution.

        '''

    def __minvar(weights, xs_avg, covariance_matrix):
        ''' Anonymous function to compute stdv '''
        return np.sqrt(np.dot(weights.T, np.dot(weights, covariance_matrix)))

    def __match_target(weights):
        ''' Anonymous function to check equality with the target return '''
        return np.sum(weights * xs_avg)

    p_size = len(xs_avg)
    args = (xs_avg, covariance_matrix)
    constraints = [
        {'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
        {'type': 'eq', 'fun': lambda x: __match_target(x) - target},
    ]
    bounds = [(0, 0.2)] * p_size

    minimized_weights = optimize.minimize(__minvar, ((1/p_size) * np.ones(p_size)), args=args,
                        method='SLSQP', bounds=bounds, constraints=constraints, options={'maxiter':1000})

    return minimized_weights

def generate_efficient_frontier(targets, xs_avg, covariance_matrix):

    portfolios = []

    for target in targets:

        p_weights = solve_for_target_return(xs_avg, covariance_matrix, target)
        portfolios.append(portfolio_metrics(p_weights['x'], xs_avg, covariance_matrix))

    return portfolios


from __future__ import annotations

import numpy as np
from numpy.linalg import inv

class UnivariateGaussian:
    """
    Class for univariate Gaussian Distribution Estimator
    """

    def __init__(self, biased_var: bool = False):  # -> UnivariateGaussian
        """
        Estimator for univariate Gaussian mean and variance parameters

        Parameters
        ----------
        biased_var : bool, default=False
            Should fitted estimator of variance be a biased or unbiased estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `UnivariateGaussian.fit` function.

        mu_: float
            Estimated expectation initialized as None. To be set in `UnivariateGaussian.fit`
            function.

        var_: float
            Estimated variance initialized as None. To be set in `UnivariateGaussian.fit`
            function.
        """
        self.biased_ = biased_var
        self.fitted_, self.mu_, self.var_ = False, None, None

    @staticmethod
    def gaussian_density(val, mu, var):
        return np.exp((-1) * ((val - mu) ** 2) / 2 * var) / np.sqrt((2 * np.pi * var))

    def fit(self, X: np.ndarray) -> UnivariateGaussian:
        """
        Estimate Gaussian expectation and variance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Training data

        Returns
        -------
        self : returns an instance of self.

        Notes
        -----
        Sets `self.mu_`, `self.var_` attributes according to calculated estimation (where
        estimator is either biased or unbiased). Then sets `self.fitted_` attribute to `True`
        """
        self.mu_ = np.mean(X)
        s = 0
        for x_i in X:
            s += (x_i - self.mu_) ** 2

        # Dividing by n-1 or n according to bias of estimator
        self.var_ = s / (len(X) - 1) if not self.biased_ else s / (len(X))

        self.fitted_ = True
        return self

    def pdf(self, X: np.ndarray) -> np.ndarray:
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, var_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")
        # Gaussian density function
        # return np.exp((-1) * ((X - self.mu_) ** 2) / 2 * self.var_) / np.sqrt((2 * np.pi * self.var_))
        return self.gaussian_density(X, self.mu_, self.var_)

    @staticmethod
    def log_likelihood(mu: float, sigma: float, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : float
            Expectation of Gaussian
        sigma : float
            Variance of Gaussian
        X : ndarray of shape (n_samples, )
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated
        """
        # Likelihood of X under (mu, sigma) is defined as the density function (mu, sigma) of X
        exp = np.sum((X - mu) ** 2) / (-2 * sigma)
        return exp - (len(X) / 2) * np.log(2 * np.pi * sigma)

class MultivariateGaussian:
    """
    Class for multivariate Gaussian Distribution Estimator
    """

    def __init__(self):
        """
        Initialize an instance of multivariate Gaussian estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `MultivariateGaussian.fit` function.

        mu_: ndarray of shape (n_features,)
            Estimated expectation initialized as None. To be set in `MultivariateGaussian.fit`
            function.

        cov_: ndarray of shape (n_features, n_features)
            Estimated covariance initialized as None. To be set in `MultivariateGaussian.fit`
            function.
        """
        self.mu_, self.cov_ = None, None
        self.fitted_ = False

    def fit(self, X: np.ndarray) -> MultivariateGaussian:
        """
        Estimate Gaussian expectation and covariance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Training data

        Returns
        -------
        self : returns an instance of self

        Notes
        -----
        Sets `self.mu_`, `self.cov_` attributes according to calculated estimation.
        Then sets `self.fitted_` attribute to `True`
        """
        self.mu_ = np.mean(X, axis=0)  # mu_[j] = mean of the j'th column of X
        X_centered = X - np.tile(self.mu_, (X.shape[0], 1))  # removing the mean from every column
        self.cov_ = np.dot(X_centered.transpose(), X_centered) / (X.shape[0] - 1)  # formula for the covariance matrix

        self.fitted_ = True
        return self

    @staticmethod
    def mult_gaussian_density(X: np.ndarray, mu: np.ndarray, sigma: np.ndarray) -> float:
        """
        @param X: 1d Vector of dimension D
        @param mu: Gaussian expectation
        @param sigma: Gaussian variance
        @return: Density value of a single Gaussian Multivariate vector X
        """
        X_centered = X - mu
        expo = np.exp((-0.5) * X_centered @ np.linalg.inv(sigma) @ X_centered.transpose())
        d = X.shape[0]
        return expo / np.sqrt((2 * np.pi) ** d * np.linalg.det(sigma))

    def pdf(self, X: np.ndarray):
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, cov_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")
        res = np.zeros(X.shape[0])
        for i in range(X.shape[0]):
            res[i] = self.mult_gaussian_density(X[i, :], self.mu_, self.cov_)

        return res

    @staticmethod
    def log_likelihood(mu: np.ndarray, cov: np.ndarray, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : ndarray of shape (n_features,)
            Expectation of Gaussian
        cov : ndarray of shape (n_features, n_features)
            covariance matrix of Gaussian
        X : ndarray of shape (n_samples, n_features)
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated over all input data and under given parameters of Gaussian
        """

        # Using the formula I calculated in q9:
        d = X.shape[1]  # number of features
        m = X.shape[0]  # number of samples
        sum = 0
        for x_i in X:
            sum += (x_i - mu).transpose() @ np.linalg.inv(cov) @ (x_i - mu)
        l = -0.5 * m * np.log(np.power(2 * np.pi, d) * np.linalg.det(cov))
        return l - 0.5 * sum


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.4'
#       jupytext_version: 1.1.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # S_LognormPrincipalBasis [<img src="https://www.arpm.co/lab/icons/icon_permalink.png" width=30 height=30 style="display: inline;">](https://www.arpm.co/lab/redirect.php?code=S_LognormPrincipalBasis&codeLang=Python)
# For details, see [here](https://www.arpm.co/lab/redirect.php?permalink=EBRandGeomLogN2).

# ## Prepare the environment

# +
import os.path as path
import sys

sys.path.append(path.abspath('../../functions-legacy'))

import numpy as np
from numpy import array, ones, diag, exp

import matplotlib.pyplot as plt

plt.style.use('seaborn')

from pcacov import pcacov
# -

# ## input parameters

m = [0.17, 0.06]  # (normal) expectation
svec = [0.24, 0.14]  # (normal) standard deviation
rho = 0.15  # (normal) correlation

# ## Compute lognormal expectation and covariance

c2_ = array([[1, rho], [rho, 1]])  # (normal) correlation matrix
s2 = np.diagflat(svec)@c2_@np.diagflat(svec)  # (normal) covariance matrix
mu = exp(m + 0.5*diag(s2))  # expectation
sig2 = np.diagflat(mu)@(exp(s2) - ones((2, 1)))@np.diagflat(mu)  # covariance matrix

# ## Principal Component Analysis of the covariance matrix

[e, lambda2] = pcacov(sig2)

# ## Verify that the principal basis is orthogonal both in the euclidean and in the statistical sense

euclidean_inner_prod = e.T@e
statistical_inner_prod = e.T@sig2@e


import numpy as np
import math
import logging

logging.basicConfig(level=logging.DEBUG, format="%(message)s")
logger = logging.getLogger("Neural Network")

# The neural network weight and biases
layers = []

# Activation functions
def sigmoid(x):
  return 1 / (1 + math.exp(-x))

def rectifier(x):
    return np.maximum(x, 0)

def softmax(x):
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum(axis=0)
# End activation functions

# Create neural network. First integer in the list is the input layer. Last number is the output layer
def init_neural_network(layers_size):

    weights = []
    biases = []

    logger.info("Starting network initialization: {}\n".format(layers_size))

    # Create all weights
    current_position = 0
    for layer_size in layers_size[:-1]:

        # Initialize random weights and biases between -1 and 1
        layer_weights = (np.random.random((layers_size[current_position+1], layer_size)) * 2) - 1
        layer_biases = (np.random.random((layers_size[current_position+1]))  * 2) - 1
        weights.append(layer_weights)
        biases.append(layer_biases)

        logger.debug("Weights between Layer {} ({} nodes) and Layer {} ({} nodes):\n{}\n".format(
            current_position,
            layer_size,
            current_position + 1,
            layers_size[current_position+1],
            layer_weights))

        logger.debug("Biases between Layer {} ({} nodes) and Layer {} ({} nodes):\n{}\n".format(
            current_position,
            layer_size,
            current_position + 1,
            layers_size[current_position+1],
            layer_biases))

        current_position += 1

    return (weights, biases)

# Layers and input vector
def forward_pass(layers, inputs):

    # Check if the input vector is the same size as the input layer
    if (len(inputs) != layers[0][0].shape[1]):
        logger.error("Input does not match neural network")
        return

    logger.debug("-----------------------------------------\n Starting forward pass \n")
    logger.debug("Input vector: {}".format(inputs))

    weights = layers[0]
    biases = layers[1]

    activation_function = np.vectorize(rectifier)

    result_vector = inputs
    for layer_weights, layer_biases in zip(weights[:-1], biases[:-1]):
        result_vector = activation_function(np.dot(layer_weights, result_vector) + layer_biases)

        logger.debug("Hidden layer vector: {}".format(result_vector))

    # Output layer with softmax activation function
    result_vector = softmax(np.dot(weights[-1], result_vector) + biases[-1])
    logger.debug("Output vector: {}".format(result_vector))

    return result_vector

def main():
    xor =  np.array([[1, 1], [0, 1], [1, 0], [0, 0]])

    layers = init_neural_network([2, 2, 2, 1])

    # TODO: Learn

    # Check if the Neural Network passes the XOR test
    test_succes = True

    logger.info("Starting neural network XOR test")
    # Start predicting the XOR output values
    for input in xor:
        output_vector = forward_pass(layers, input)

        if output_vector == np.array([bool(input[0]) ^ bool(input[1])]):
            logger.debug("Wrong prediction.\n")
            test_succes = False
        else:
            logger.debug("Correct prediction.\n")

    logger.info("XOR test succes: {}".format(test_succes))

if __name__ == "__main__":
    main()


#! /usr/bin/env python

"""
File: sinsum1.py
Copyright (c) 2016 Austin Ayers
License: MIT

Course: PHYS227
Assignment: 3.15
Date: Feb 11, 2016
Email: ayers111@mail.chapman.edu
Name: Austin Ayers
Description: Compares two functions, one to approximate the other
"""

import numpy as np

def S(t, n, T):
    """
    Returns the value of function S(t;n)
    """
    sum = []
    for i in range(1,n+1):
        sum.append(((2 * (i + 1) - 1)**-1) * np.sin((2 * (2 * (i + 1) - 1) \
        * np.pi * t) / float(T)))
    return np.sum(sum) * 4 / np.pi

def f(t, T):
    """
    Returns the value of the piecewise function f(t)
    """
    if(t < T/2.0):
        return 1
    elif(t == T/2.0):
        return 0
    return -1

def outputTable():
    """
    Outputs a table of errors of ranging values of n and t
    """
    table = [["\nn", "alpha", "\tValue of S", "\tValue of f", "Value of f - S\n"]]
    T = 2 * np.pi
    n = [1, 3, 5, 10, 30, 100]
    t = [0.01 * T, 0.25 * T, 0.49 * T]

    for elem_n in n:
        for elem_t in t:
            S_val = S(elem_t, elem_n, T)
            f_val = f(elem_t, T)
            table.append([elem_n, elem_t/T, S_val, f_val, f_val - S_val])
    print(str(table[0][0]) + "\t" + str(table[0][1]) + "\t" + str(table[0][2]) + "\t" + str(table[0][3]) + "\t" + str(table[0][4]))
    for num, j in enumerate(table):
        if(num == 0):
            continue
        if(num%3 == 1):
            print("\n")
        print(str(j[0]) + "\t" + str(j[1]) + "\t" + str(j[2]) + "\t\t" + str(j[3]) + "\t\t" + str(j[4]))

def test_sum():
    sum = S(.49, 1, 2 * np.pi)
    assert(sum - (4/np.pi)*(1)*np.sin((.49*2*np.pi)/(2*np.pi)) < .001)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: Alejandro Alonso Membrilla
"""

"""
Funciones de uso general
"""
import math
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

def normalizar_matriz(im):
  minimo = min(im.ravel())
  maximo = max(im.ravel())
  rango = maximo-minimo
  return (im-minimo)/rango

def pintaI(im, figsize=(5,5)):
  im_norm = normalizar_matriz(im)
  cmap = 'gray' if len(im.shape) == 2 else None
  plt.figure(figsize=figsize)
  plt.axis('off')
  plt.imshow( im_norm, cmap=cmap, vmin=0.0, vmax=1.0 )
  plt.show(block=False)

#Normaliza y pinta en pantalla un conjunto de imágenes con sus
# respectivos títulos
def pintaMIE(vim, titulos, ncol = 2, figsize=(12,12), winname=None):
  import matplotlib.pyplot as plt
  from math import ceil
  fig = plt.figure(num=winname, figsize=figsize)
  for k, im in enumerate(vim):
    sub = fig.add_subplot( ceil(len(vim)/ncol), ncol, k + 1 )
    plt.axis('off')
    plt.title(titulos[k])
    cmap = 'gray' if len(im.shape) == 2 else None
    sub.imshow(normalizar_matriz(im), cmap=cmap)
  plt.show(block=False)

#Establece un punto de parada
def esperar():
  input("Pulsa una tecla para continuar...")
  plt.close('all')

"""
Funciones para el ejercicio 1A
"""
#Cuando se calcula una máscara gaussiana o sus derivadas a partir de un tamaño
# de máscara dado, sirve para calcular el valor de la desviación típica a partir
# de dicho tamaño
def calc_sigma(diam):
  return ((diam-1)//2 )/3

#La máscara es la gaussiana discretizada
def masc_gaussiana(sigma=None, diam=None):
  if sigma == None:
    sigma = calc_sigma(diam)
    lim = diam//2
  else:
    lim = math.ceil(3*sigma)  #radio de la máscara
  rango = np.arange(-lim, lim+1)
  masc = np.asarray( [math.exp(-(x**2)/(2*sigma**2)) for x in rango] )
  return masc/sum(masc)

#La máscara es la derivada de la gaussiana discretizada
def masc_derivada1_gauss(sigma=None, diam=None):
  if sigma == None:
    sigma = calc_sigma(diam)
    lim = diam//2
  else:
    lim = math.ceil(3*sigma)
  rango = np.arange(-lim, lim+1)
  masc = np.asarray( [math.exp(-(x**2)/(2*sigma**2)) for x in rango] )
  masc *= -rango/(sigma**2)
  return masc   #En este caso no normalizamos la máscara porque su suma debe valer 0

#La máscara es la segunda derivada de la gaussiana discretizada
def masc_derivada2_gauss(sigma=None, diam=None):
  if sigma == None:
    sigma = calc_sigma(diam)
    lim = diam//2
  else:
    lim = math.ceil(3*sigma)
  rango = np.arange(-lim, lim+1)
  sig2 = sigma*sigma
  masc = np.asarray( [math.exp(-(x**2)/(2*sig2)) for x in rango] )
  masc *= (rango**2-sig2)/(sig2*sig2)
  return masc   #En este caso no normalizamos la máscara porque su suma debe valer 0

def ejercicio1A():
  #Mostramos gráficas de la gaussiana y sus derivadas discretizadas
  sigma = 3.0
  plt.plot(masc_gaussiana(sigma))
  plt.plot(masc_derivada1_gauss(sigma))
  plt.plot(masc_derivada2_gauss(sigma))
  plt.axis("off")
  plt.legend(["Gaussiana", "Derivada 1ª", "Derivada 2ª"])
  plt.show(block=False)
  esperar()

"""
Funciones para el ejercicio 1B
"""
#El método asume que el padding solo debe hacerse en los márgenes laterales,
#porque únicamente se utiliza en correlaciones 1D con máscaras aplicadas por filas
#modo indica el tipo de padding:
#    1. "negro" añade márgenes negros
#    2. "ext" añade márgenes replicados
#    3. "ciclo" añade márgenes en bucle (mosaico)
#El proceso consiste en generar el marco a la izquierda y a la derecha, y a
# concatenarlos con la imagen original
def pad_image( image, pad_size, modo="ciclo" ):
  nfilas = len(image)

  if modo == "negro":
    #Padding a izquierda y derecha
    padding = np.zeros((nfilas, pad_size))
    padded_image = np.concatenate( (padding, image, padding), axis=1 )

  elif modo == "ext":
    #Padding a izquierda y derecha
    padding_izda = np.reshape( np.repeat(image[:,0], pad_size), (nfilas, pad_size) )
    padding_dcha = np.reshape( np.repeat(image[:,-1], pad_size), (nfilas, pad_size) )
    padded_image = np.concatenate( (padding_izda, image, padding_dcha), axis=1 )

  elif modo == "ciclo":
    #Padding a izquierda y derecha
    padding_izda = np.reshape( image[:,-pad_size-1:-1], (nfilas, pad_size) )
    padding_dcha = np.reshape( image[:,0:pad_size], (nfilas, pad_size) )
    padded_image = np.concatenate( (padding_izda, image, padding_dcha), axis=1 )

  return padded_image

#Aplica la correlación en todos los elementos de una columna
def corr_en_columna(image, mask, i, radio_mask):
  return np.sum(mask*image[:,i-(radio_mask+1):i+radio_mask], axis=1 )

#Aplica la correlación 1D por filas en todos los elementos de una matriz
def correlation1D(image, mask, modo="ciclo"):
  radio_mask = math.floor( len(mask)/2 )
  padded_image = pad_image(image, radio_mask, modo=modo)
  image_ncol = np.size(padded_image, axis=1)
  correlacion = np.asarray(
      [ corr_en_columna(padded_image, mask, i, radio_mask) for i in range(radio_mask+1, image_ncol-radio_mask+1) ]
    ).T
  #Trasponemos porque np.asarray convierte los elementos de la lista en filas,
  # pero son columnas
  return correlacion

#Aplica correlación 2D separable (una máscara horizontal y luego otra vertical)
def correlation2D(image, hmask, vmask, modo="ciclo"):
  #Si un filtro es separable, basta con aplicar la correlación unidimensional 2 veces:
  #Una por filas
  correlation_filas = correlation1D(image, hmask, modo=modo)
  #Y otra por columnas
  correlation = correlation1D(correlation_filas.T, vmask, modo=modo).T
  return correlation

#Devuelve una convolución 2D mediante una correlación con la máscara invertida
def convolution2D(image, hmask, vmask, modo="ciclo"):
  return correlation2D(image, hmask[::-1], vmask[::-1], modo=modo)

#NOTA: esta función incluye código usado únicamente en el Bonus
# (el bool monobanda indica que la imagen está en escala de grises)
def suavizar(img, sigma, modo="ciclo"):
  masc_gauss = masc_gaussiana(sigma)
  monobanda = len(img.shape) == 2
  if monobanda:
    return convolution2D(img, masc_gauss, masc_gauss, modo=modo)
  else:
    #Calculamos la convolución 2D de cada banda y le añadimos una nueva dimensión...
    banda_b = convolution2D(img[:,:,0], masc_gauss, masc_gauss, modo=modo)[:,:,np.newaxis]
    banda_g = convolution2D(img[:,:,1], masc_gauss, masc_gauss, modo=modo)[:,:,np.newaxis]
    banda_r = convolution2D(img[:,:,2], masc_gauss, masc_gauss, modo=modo)[:,:,np.newaxis]
    #...para poder concatenar las bandas sin problemas
    return np.concatenate((banda_b, banda_g, banda_r), axis=2)

def ejercicio1B(sigma=3.0):
  img = cv.imread('imagenes/cat.bmp', 0)
  blurcv = cv.GaussianBlur(img, (0,0), sigmaX=sigma)
  blur = suavizar(img, sigma)
  pintaMIE([img, blurcv, blur], ["Original", "Suavizada con OpenCV", "Suavizado propio"], ncol=3)
  esperar()

"""
Funciones del apartado 1C
"""
#Devuelve el array arr centrado en otro array de tamaño size
def centrar(arr, size):
  if size == arr.size:
    return arr
  diff = size - arr.size
  diff_half = int(diff/2)
  if diff > 0:
    zeros = np.zeros(diff_half)
    return np.concatenate( (zeros, arr, zeros) )
  else:
    return np.array(arr[-diff_half:diff_half])

def ejercicio1C():
  #Mostramos gráficas comparando la derivada de la gaussiana discretizada con
  # el resultado de getDerivKernels, para varios sigmas y tamaños de bloque
  for orden in [1,2]:
    for bsize in [5,7]:
      fig = plt.figure(figsize=(13,4))
      for sigma in [1,1.2]:
        #OpenCV da el kernel de la 1ª derivada invertido, y en forma de array
        # multidimensional, de ahí viene la siguiente indexación
        deriv = np.transpose(cv.getDerivKernels(orden,orden,5)[0])[0][::-1]
        masc = [masc_derivada1_gauss, masc_derivada2_gauss][orden-1](sigma=1)
        fig.add_subplot(1, 2, 1 if sigma == 1 else 2)
        plt.plot(np.arange(masc.size), masc, centrar(deriv, masc.size))
        plt.title("Derivada de orden {}. Tamaño de bloque {}. Sigma={}.".format(orden, bsize, sigma))
        plt.legend(["Derivada Gaussiana", "getDerivKernels"])
      plt.show(block=False)

  esperar()

  #Escalamos las máscaras y volvemos a compararlas con los kernels
  fig = plt.figure(figsize=(13,8))

  deriv = np.transpose(cv.getDerivKernels(1,1,5)[0])[0][::-1]
  masc = masc_derivada1_gauss(sigma=1)*3.3
  fig.add_subplot(2, 2, 1)
  plt.plot(np.arange(masc.size), masc, centrar(deriv, masc.size))
  plt.title("Derivada de orden 1. Tamaño de bloque 5. Sigma=1.")
  plt.legend(["Derivada Gaussiana", "getDerivKernels"])

  deriv = np.transpose(cv.getDerivKernels(1,1,7)[0])[0][::-1]
  masc = masc_derivada1_gauss(sigma=1.2)*11
  fig.add_subplot(2, 2, 2)
  plt.plot(np.arange(masc.size), masc, centrar(deriv, masc.size))
  plt.title("Derivada de orden 1. Tamaño de bloque 7. Sigma=1.2.")
  plt.legend(["Derivada Gaussiana", "getDerivKernels"])

  deriv = np.transpose(cv.getDerivKernels(2,2,5)[0])[0][::-1]
  masc = masc_derivada2_gauss(sigma=1)*2
  fig.add_subplot(2, 2, 3)
  plt.plot(np.arange(masc.size), masc, centrar(deriv, masc.size))
  plt.title("Derivada de orden 2. Tamaño de bloque 5. Sigma=1.")
  plt.legend(["Derivada Gaussiana", "getDerivKernels"])

  deriv = np.transpose(cv.getDerivKernels(2,2,7)[0])[0][::-1]
  masc = masc_derivada2_gauss(sigma=1.2)*6
  fig.add_subplot(2, 2, 4)
  plt.plot(np.arange(masc.size), masc, centrar(deriv, masc.size))
  plt.title("Derivada de orden 2. Tamaño de bloque 7. Sigma=1.2.")
  plt.legend(["Derivada Gaussiana", "getDerivKernels"])

  plt.show(block=False)

  esperar()

"""Funciones del ejercicio 1D"""
def LoG(image, sigma, modo="ext"):
  #Calculamos ambas máscaras a utilizar
  m_gauss = masc_gaussiana(sigma)
  m_deriv2_gauss = masc_derivada2_gauss(sigma)
  #Hacemos las convoluciones para obtener las derivadas
  img_deriv2_x = convolution2D(image, m_deriv2_gauss, m_gauss, modo=modo)
  img_deriv2_y = convolution2D(image, m_gauss, m_deriv2_gauss, modo=modo)

  #Obtenemos el resultado
  lapl_of_gauss = img_deriv2_x + img_deriv2_y
  norm_LoG = sigma**2*lapl_of_gauss
  #norm_LoG = normalizar_matriz(sigma**2*lapl_of_gauss)
  return norm_LoG

#Ejemplo de cálculo de LoG (se muestran las derivadas parciales y las máscaras)
def ejemplo_LoG(sigma, modo):
  img = cv.imread('imagenes/cat.bmp', 0)

  img_deriv2_x = np.copy(img)
  img_deriv2_y = np.copy(img)

  #Calculamos ambas máscaras a utilizar
  m_gauss = masc_gaussiana(sigma)
  m_deriv2_gauss = masc_derivada2_gauss(sigma)
  #Hacemos las convoluciones para obtener las derivadas
  img_deriv2_x = convolution2D(img_deriv2_x, m_deriv2_gauss, m_gauss, modo=modo)
  img_deriv2_y = convolution2D(img_deriv2_y, m_gauss, m_deriv2_gauss, modo=modo)

  #Pintamos las derivadas...
  pintaMIE( [img_deriv2_x,img_deriv2_y], ['Derivada con respecto de x', 'Derivada con respecto de y'], 2 )
  #...y las máscaras
  mascaras = [ np.outer(m_deriv2_gauss, m_gauss), np.outer(m_gauss, m_deriv2_gauss) ]
  pintaMIE ( mascaras, ['Máscara gaussiana', 'Derivada segunda de la gaussiana'] )

  #Podríamos haber utilizado los cálculos anteriores en vez de llamar a la
  # función LoG, pero de esta forma vemos que esta funciona correctamente
  pintaMIE( [LoG(img, 1, modo='negro')], ["Laplaciana de la gaussiana"], ncol=1 )
  esperar()

def ejercicio1D():
  ###Ejemplo de cálculo de LoG (se muestran las derivadas parciales y las máscaras)
  ejemplo_LoG(1, "negro")
  ejemplo_LoG(3, "ext")
  ejemplo_LoG(1, "negro")
  ejemplo_LoG(3, "ext")

"""
Funciones para el ejercicio 2A
"""
#Devuelve otro array con las filas y columnas pares de la imagen original
def downsample_v0(img):
  return img[::2,::2]

def pyrDown_v0(img, sigma, modo='ciclo'):
  return downsample_v0( suavizar(img, sigma, modo=modo) )

#Suaviza y reduce el tamaño de la imagen a la mitad
def pyrDown(img, sigma, modo='ciclo'):
  return cv.resize(suavizar(img, sigma, modo), (img.shape[1]//2, img.shape[0]//2))

#Devuelve la pirámide gaussiana de una imagen dada
def piramide_gaussiana(img, sigma=1.0, altura=4, modo="ciclo"):
  piramide_suavizada = [img]
  for i in range(altura):
    piramide_suavizada.append( pyrDown(piramide_suavizada[i], sigma, modo) )
  return piramide_suavizada

def ejercicio2A():
  img = cv.imread('imagenes/cat.bmp', 0)
  #Sin suavizar
  piramide = [img]
  for i in range(4):
    piramide.append( downsample_v0(piramide[i]) )

  titulos = [ "Nivel {}".format(i) for i in range(5) ]
  pintaMIE(piramide, titulos, ncol=5, figsize=(20,20), winname="Pirámide sin suavizar")

  #Suavizadas
  for sigma in [0.9, 1.0, 1.1, 1.2]:
    piramide_suavizada = piramide_gaussiana(img, sigma, altura=4, modo="ext")
    pintaMIE(piramide_suavizada, titulos, ncol=5, figsize=(20,20), winname="Pirámide suavizada. Sigma={}".format(sigma))

  esperar()

  #No muestran mucha diferencia entre ellas. Probamos con desviaciones típicas más diferenciadas
  for sigma in [0.5, 1, 2, 3]:
    piramide_suavizada = piramide_gaussiana(img, sigma, altura=4, modo="ext")
    pintaMIE(piramide_suavizada, titulos, ncol=5, figsize=(20,20), winname="Pirámide suavizada. Sigma={}".format(sigma))

  esperar()

"""
Funciones para el ejercicio 2B
"""
#Inserta ceros entre cada dos columnas y dos filas
def upsample_v0(img):
  upsam = np.zeros(shape=(np.size(img,0)*2, np.size(img,1)*2))
  upsam[::2,::2] = img
  return upsam

#Suaviza la imagen aumentada con una máscara incrementada. Equivale a suavizar y
# multiplicar la imagen por 4 (análogo al método utilizado por OpenCV)
def pyrUp_v0(img, sigma, modo="ciclo"):
  masc = masc_gaussiana(sigma)*2
  return convolution2D(upsample_v0(img), masc, masc, modo)

#Aumenta la imagen usando cv.resize con interpolación bilineal (por defecto)
def upsample(img):
  return cv.resize(img, (np.size(img,1)*2, np.size(img,0)*2))

#Aumenta la imagen y la suaviza
def pyrUp(img, sigma, modo="ciclo"):
  return suavizar(upsample(img), sigma, modo)

#Si son del mismo tamaño, las resta. Si no, cambia de tamaño (interpolación bilineal) la segunda
# al tamaño de la primera y entonces las resta
def restar_imgs(img1, img2):
  if img1.shape == img2.shape:
    return img1-img2
  else:
    #El parámetro dsize de cv.resize tiene la orientación contraria al atributo shape de numpy
    new_shape = (img1.shape[1], img1.shape[0])
    return img1-cv.resize( img2, new_shape )

def piramide_laplaciana(img, sigma=1.0, altura=4, modo="ciclo"):
  piramide_lapl = []
  pir_gauss = piramide_gaussiana(img, sigma, altura, modo)
  for i in range(1,altura+1):
    piramide_lapl.append( restar_imgs( pir_gauss[i-1], pyrUp(pir_gauss[i], sigma, modo) ) )
    #piramide_lapl.append( -LoG(pir_gauss[i-1], sigma=sigma) )
  return piramide_lapl

#Calcula la pirámide laplaciana usando la función pyrUp de OpenCV
def piramide_laplaciana_cv(img, sigma=1.0, altura=4, modo="ciclo"):
  piramide_lapl = []
  pir_gauss = piramide_gaussiana(img, sigma, altura, modo)
  for i in range(1,altura+1):
    piramide_lapl.append( restar_imgs( pir_gauss[i-1], cv.pyrUp(pir_gauss[i]) ) )
  return piramide_lapl

def ejercicio2B():
  img = cv.imread('imagenes/cat.bmp', 0)
  imagenes = [pyrUp_v0(img, 1.0, "ext"), pyrUp(img, 1.0, "ext"), cv.pyrUp(img)]
  titulos = ["PyrUp primera versión", "Segunda versión", "Versión de OpenCV"]
  pintaMIE(imagenes, titulos, ncol=3, winname="Comparación de PyrUps")

  esperar()

  titulos = [ "Nivel {}".format(i) for i in range(1, 5) ]
  img2 = cv.imread('imagenes/marilyn.bmp', 0)

  ##Comparamos nuestras pirámides laplacianas con las conseguidas con cv.pyrUp
  #Pirámides laplacianas con pyrUp propio
  pintaMIE( piramide_laplaciana(img, sigma=1.0, modo="ext"), titulos, 4, (15,15), "Pirámide cat.bmp" )
  pintaMIE( piramide_laplaciana(img2, sigma=1.0, modo="ext"), titulos, 4, (15,15), "Pirámide marilyn.bmp")
  #Pirámides laplacianas con cv.pyrUp
  pintaMIE( piramide_laplaciana_cv(img, sigma=1.0, modo="ext"), titulos, 4, (15,15), "Pirámide cat.bmp con cv.pyrUp" )
  pintaMIE( piramide_laplaciana_cv(img2, sigma=1.0, modo="ext"), titulos, 4, (15,15), "Pirámide marilyn.bmp con cv.pyrUp" )

  esperar()

  #Ejemplo de uso de la pirámide laplaciana: reconstrucción de imágenes
  sigma = 1.0
  im = piramide_gaussiana(img, altura=4)[4]
  pir_laplaciana = piramide_laplaciana(img, altura=4, modo='ext')
  for i in range(4,0,-1):
    shape = pir_laplaciana[i-1].shape
    #Aumentamos la imagen y la suavizamos. El cv.resize exterior se debe a que
    # la creación de pirámides gaussianas y laplacianas asume que las dimensiones
    # de la imagen son potencia de 2, pero estamos manipulando imágenes con
    # número de filas/columnas impar
    im_up = cv.resize( pyrUp(im, sigma=sigma), (shape[1], shape[0]) )
    im = normalizar_matriz(pir_laplaciana[i-1]) + normalizar_matriz(im_up)
    pintaMIE([pir_laplaciana[i-1], im_up, im], ["Laplaciana", "Reconstrucción anterior", "Imagen reconstruida"], ncol=3, winname="Reconstrucción de x{}".format(i))
  pintaMIE([img, im], ["Original", "Reconstruida"], winname="Comparación entre la original y la reconstrucción")

  esperar()

"""
Funciones para el ejercicio 3A
"""
"""
El 3A se ha hecho de forma conjunta con el 3C, mostrando varias parejas y sus
imágenes híbridas para varios valores de sigmas y eligiendo los que obtenían mejores resultados.
"""
def ejercicio3A():
  pass

"""
Funciones para el ejercicio 3B
"""
def high_img(img, sigma, modo='ciclo'):
  #k = 1.5
  return img-suavizar(img, sigma, modo)
  #Alternativamente
  #return LoG(img, sigma, modo)

def low_img(img, sigma, modo='ciclo'):
  return suavizar(img, sigma, modo)

def hybrid(himg, limg, hsigma, lsigma, modo='ciclo'):
  return high_img(himg, hsigma, modo=modo) + low_img(limg, lsigma, modo=modo)

def pintaHybrid(himg, limg, hsigma, lsigma, modo='ciclo', figsize=(9,9)):
  high = high_img(himg, hsigma, modo)
  low = low_img(limg, lsigma, modo)
  imgs = [ high, low, high + low ]
  titulos = [ "Frecuencias altas", "Frecuencias bajas", "Imagen híbrida" ]
  pintaMIE(imgs, titulos, ncol=3)

def ejercicio3B():
  hsigma = 0.8
  lsigma = 7.0
  modo='ciclo'
  himg = cv.imread('imagenes/einstein.bmp', 0)
  limg = cv.imread('imagenes/marilyn.bmp', 0)
  pintaHybrid(himg, limg, hsigma, lsigma, modo)
  esperar()

"""Funciones para el ejercicio 3C"""
#Mostramos por pantalla las imágenes híbridas para cada pareja,
# comparando los resultados para diversos valores de sigma (ejercicio 3A)
def comp_sigmas_pajaro_avion():
  hsigmas = (1.0, 1.5, 2.0)
  lsigmas = (5.0, 8.0, 11.0)
  modo='ciclo'
  himg = cv.imread('imagenes/bird.bmp', 0)
  limg = cv.imread('imagenes/plane.bmp', 0)

  for hsigma, lsigma in zip(hsigmas, lsigmas):
    pintaHybrid(himg, limg, hsigma, lsigma, modo)

  esperar()

def comp_sigmas_bici_moto():
  hsigmas = (0.6, 0.8, 1.4)
  lsigmas = (7.0, 9.0, 11.0)
  modo='ciclo'
  himg = cv.imread('imagenes/bicycle.bmp', 0)
  limg = cv.imread('imagenes/motorcycle.bmp', 0)

  for hsigma, lsigma in zip(hsigmas, lsigmas):
    pintaHybrid(himg, limg, hsigma, lsigma, modo)

  esperar()

def comp_sigmas_perro_gato():
  hsigmas = (2.0, 2.8, 3.6)
  lsigmas = (10.0, 12.0, 14.0)
  modo='ciclo'
  himg = cv.imread('imagenes/dog.bmp', 0)
  limg = cv.imread('imagenes/cat.bmp', 0)

  for hsigma, lsigma in zip(hsigmas, lsigmas):
    pintaHybrid(himg, limg, hsigma, lsigma, modo)

  esperar()

def comp_sigmas_pez_submarino():
  hsigmas = (0.4, 0.7, 1.2)
  lsigmas = (5.0, 7.0, 9.0)
  modo='ciclo'
  himg = cv.imread('imagenes/fish.bmp', 0)
  limg = cv.imread('imagenes/submarine.bmp', 0)

  for hsigma, lsigma in zip(hsigmas, lsigmas):
    pintaHybrid(himg, limg, hsigma, lsigma, modo)

  esperar()

def comp_sigmas_einstein_marilyn():
  hsigmas = (0.4, 0.8, 1.3)
  lsigmas = (5.0, 7.0, 9.0)
  modo='ciclo'
  himg = cv.imread('imagenes/einstein.bmp', 0)
  limg = cv.imread('imagenes/marilyn.bmp', 0)

  for hsigma, lsigma in zip(hsigmas, lsigmas):
    pintaHybrid(himg, limg, hsigma, lsigma, modo)

  esperar()

def ejercicio3C():
  comp_sigmas_pajaro_avion()
  comp_sigmas_bici_moto()
  comp_sigmas_perro_gato()
  comp_sigmas_pez_submarino()
  comp_sigmas_einstein_marilyn()

"""
Funciones para el ejercicio 3D
"""
#Tomando los sigmas considerados óptimos para cada pareja (ejercicio 3A/3C),
# mostramos la pirámide gaussiana de la hibridación de cada pareja
def ejercicio3D():
  titulos = [ "Nivel {}".format(i) for i in range(6) ]
  modo = 'ciclo'

  #Pirámide para el híbrido pájaro-avión
  hsigma = 1.5
  lsigma = 8.0
  himg = cv.imread('imagenes/bird.bmp', 0)
  limg = cv.imread('imagenes/plane.bmp', 0)
  hyb = hybrid(himg, limg, hsigma, lsigma, modo)
  pyr_gauss = piramide_gaussiana(hyb, sigma=1.0, altura=4, modo=modo)
  pintaMIE(pyr_gauss, titulos, ncol=3, figsize=(12,8))
  esperar()

  #Pirámide para el híbrido bici-moto
  hsigma = 0.8
  lsigma = 9.0
  himg = cv.imread('imagenes/bicycle.bmp', 0)
  limg = cv.imread('imagenes/motorcycle.bmp', 0)
  hyb = hybrid(himg, limg, hsigma, lsigma, modo)
  pyr_gauss = piramide_gaussiana(hyb, sigma=1.0, altura=4, modo=modo)
  pintaMIE(pyr_gauss, titulos, ncol=3, figsize=(12,8))
  esperar()

  #Pirámide para el híbrido einstein-marilyn
  hsigma = 0.8
  lsigma = 7.0
  himg = cv.imread('imagenes/einstein.bmp', 0)
  limg = cv.imread('imagenes/marilyn.bmp', 0)
  hyb = hybrid(himg, limg, hsigma, lsigma, modo)
  pyr_gauss = piramide_gaussiana(hyb, sigma=1.0, altura=4, modo=modo)
  pintaMIE(pyr_gauss, titulos, ncol=3, figsize=(12,8))
  esperar()

"""
Funciones para el Bonus 1
"""
def color_hyb_pajaro_avion(modo='ciclo'):
  hsigma = 2.0
  lsigma = 9.0
  himg = cv.imread('imagenes/bird.bmp', 1)[:,:,::-1]
  limg = cv.imread('imagenes/plane.bmp', 1)[:,:,::-1]
  pintaHybrid(himg, limg, hsigma, lsigma, modo=modo, figsize=())

def color_hyb_bici_moto(modo='ciclo'):
  hsigma = 1.0
  lsigma = 9.0
  himg = cv.imread('imagenes/bicycle.bmp', 1)[:,:,::-1]
  limg = cv.imread('imagenes/motorcycle.bmp', 1)[:,:,::-1]
  pintaHybrid(himg, limg, hsigma, lsigma, modo=modo)

def color_hyb_perro_gato(modo='ciclo'):
  hsigma = 3.5
  lsigma = 12.0
  himg = cv.imread('imagenes/dog.bmp', 1)[:,:,::-1]
  limg = cv.imread('imagenes/cat.bmp', 1)[:,:,::-1]
  pintaHybrid(himg, limg, hsigma, lsigma, modo=modo)

def color_hyb_pez_submarino(modo='ciclo'):
  hsigma = 1.8
  lsigma = 9.0
  himg = cv.imread('imagenes/fish.bmp', 1)[:,:,::-1]
  limg = cv.imread('imagenes/submarine.bmp', 1)[:,:,::-1]
  pintaHybrid(himg, limg, hsigma, lsigma, modo=modo)

def color_hyb_einstein_marilyn(modo='ciclo'):
  hsigma = 0.9
  lsigma = 6.0
  himg = cv.imread('imagenes/einstein.bmp', 1)[:,:,::-1]
  limg = cv.imread('imagenes/marilyn.bmp', 1)[:,:,::-1]
  pintaHybrid(himg, limg, hsigma, lsigma, modo=modo)

def bonus1():
  color_hyb_pajaro_avion()
  color_hyb_bici_moto()
  color_hyb_perro_gato()
  color_hyb_pez_submarino()
  color_hyb_einstein_marilyn()
  esperar()

"""
Funciones para el Bonus 2
"""
def color_hyb_erizo_bola(modo='ciclo'):
  hsigma = 2.9
  lsigma = 26.0
  modo='ciclo'
  limg = cv.imread('imagenes/bolabolos.jpeg', 1)[:,:,::-1]
  himg = cv.imread('imagenes/erizo.jpg', 1)[:,:,::-1]
  himg = cv.resize(himg, (limg.shape[1], limg.shape[0]))
  pintaHybrid(himg, limg, hsigma, lsigma, modo=modo, figsize=(15,15))

def bonus2():
  color_hyb_erizo_bola()
  esperar()

def main():
  ejercicio1A()
  ejercicio1B()
  ejercicio1C()
  ejercicio1D()

  ejercicio2A()
  ejercicio2B()

  ejercicio3A()
  ejercicio3B()
  ejercicio3C()
  ejercicio3D()

  bonus1()
  bonus2()

main()


import numpy as np
import cv2
from matplotlib import pyplot as plt

def std_filter(I, ksize):
    F = np.ones((ksize,ksize), dtype=np.float) / (ksize*ksize);

    MI = cv2.filter2D(I,-1,F) # apply mean filter on I

    I2 = I * I; # I squared
    MI2 = cv2.filter2D(I2,-1,F) # apply mean filter on I2

    return np.sqrt(MI2 - MI * MI)

def zero_crossing(I):
    """Finds locations at which zero-crossing occurs, used for
    Laplacian edge detector"""

    Ishrx = I.copy();
    Ishrx[:,1:] = Ishrx[:,:-1]

    Ishdy = I.copy();
    Ishdy[1:,:] = Ishdy[:-1,:]

    ZC = (I==0) | (I * Ishrx < 0) | (I * Ishdy < 0); # zero crossing locations

    SI = std_filter(I, 3) / I.max()

    Mask =  ZC & (SI > .1)

    E = Mask.astype(np.uint8) * 255 # the edges

    return E

I = cv2.imread("agha-bozorg.jpg", cv2.IMREAD_GRAYSCALE)

# set the sigma for Gaussian Blurring
sigma = 7

# Sobel magnitude of gradient
thresh = 90 # threshold
Ib = cv2.GaussianBlur(I, (sigma,sigma), 0); # blur the image
Ix = cv2.Sobel(Ib,cv2.CV_64F,1,0)
Iy = cv2.Sobel(Ib,cv2.CV_64F,0,1)
Es = np.sqrt(Ix*Ix + Iy*Iy)
Es = np.uint8(Es > thresh)*255 # threshold the gradients

# Laplacian of Gaussian
# Here, we first apply a Gaussian filter and then apply
# the Laplacian operator (instead of apply the LoG filter)
Ib = cv2.GaussianBlur(I, (sigma,sigma), 0);
El = cv2.Laplacian(Ib,cv2.CV_64F,ksize=5)
El = zero_crossing(El);

# Canny Edge detector
lth = 50   # low threshold
hth = 120  # high threshold
Ib = cv2.GaussianBlur(I, (sigma,sigma), 0); # blur the image
Ec = cv2.Canny(Ib,lth, hth)

f, axes = plt.subplots(2, 2)

axes[0,0].imshow(I,cmap = 'gray')
axes[0,0].set_title("Original Image")

axes[0,1].imshow(Es,cmap = 'gray')
axes[0,1].set_title("Sobel")

axes[1,0].imshow(El,cmap = 'gray')
axes[1,0].set_title("Laplacian")

axes[1,1].imshow(Ec,cmap = 'gray')
axes[1,1].set_title("Canny")

# Notice that imshow in matplotlib considers the minimums value of I
# as black and the maximum value as white (this is different from
# the behaviour in cv2.imshow)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Mon Jul 15 13:59:59 2019

@author: Thiago
"""

import numpy as np
import pylab as pl
import statistics as st
from matplotlib.pyplot import figure

#%%

#Configuração

N_Int = [100, 1000, 10000, 100000, 1000000]
numIteracoes = 10
res = np.zeros((5,numIteracoes), dtype=np.float64)

#intervalo de integração
a, b = 0, 1.0

#maximo
M = 5

#%%

#função para integração
def f(x):
    return (np.cos(50.0*x) + np.sin(20.0*x)) ** 2

#valor exato para o intervalo [0, 1]
exato = 0.965200936050146

#cria os vetores x e y da solução da função no intervalo [0, 1]
num_pontos_sol_exata = 1000
sol_exata_x = np.zeros((num_pontos_sol_exata,), dtype=np.float64)
sol_exata_y = np.zeros((num_pontos_sol_exata,), dtype=np.float64)
x=0
delta = (b-a)/(num_pontos_sol_exata-1)
for i in range(num_pontos_sol_exata):
    sol_exata_x[i] = x
    sol_exata_y[i] = f(x)
    x += delta

#%%
#Calculo da area

for n in range(5):
    for it in range(numIteracoes):
        #quantidade de pontos
        N0 = N_Int[n]
        k=0
        #print("Executando o método de Monte Carlo com",N0,"pontos.")
        x = np.random.uniform(a, b, size=int(N0))
        y = np.random.uniform(0, M, size=int(N0))

        #listas que vão armazenar os pontos para geração do gráfico
        xd = []
        yd = []
        xf = []
        yf = []

        #contabiliza os pontos abaixo da curva e monta as listas de pontos
        for i in range(int(N0)):
            if(y[i] < f(x[i])):
                k += 1
                xd.append(x[i])
                yd.append(y[i])
            else:
                xf.append(x[i])
                yf.append(y[i])

        #calcula a area abaixo do gráfico
        res[n][it] = M*(b-a)*k/N0

        #gera um grafico para cada numero de N testado
        if(it == 0):
            #define o tamanho dos graficos
            figure(num=None, figsize=(6, 4), dpi=72, facecolor='w', edgecolor='k')

            #pontos
            pl.scatter(xf, yf, c='b', label=u'Pontos acima da curva')
            pl.scatter(xd, yd, c='g', label=u'Pontos abaixo da curva')

            #solução exata
            pl.plot(sol_exata_x, sol_exata_y, c='k', label=u'Solução Exata')

            #marca o maximo
            pl.axhline(y=M, linewidth=2, color='r', label=u'Máximo')

            #configura o gráfico
            pl.legend(loc='upper right')
            pl.title(u'Método de Monte Carlo com '+str(N_Int[n])+' pontos')
            pl.axis((0,1,0,6))
            pl.savefig(fname='mc_'+str(N_Int[n])+'.png', format='png')
            pl.show()

    print("Resultado com",N_Int[n],"pontos e",numIteracoes,"iterações")
    print(u'Média', st.mean(res[n]))
    print(u'Desvio Padrão', st.stdev(res[n]))


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

import numpy as np, tensorflow as tf, arviz as az, matplotlib.pyplot as plt, tensorflow_probability as tfp
tfd, tfb = tfp.distributions, tfp.bijectors
tf.config.optimizer.set_jit(True)

count_data = tf.constant([
    13, 24,  8, 24,  7, 35, 14, 11, 15, 11, 22, 22, 11, 57, 11, 19, 29,  6, 19, 12, 22, 12, 18, 72, 32,  9,  7, 13,
    19, 23, 27, 20,  6, 17, 13, 10, 14,  6, 16, 15,  7,  2, 15, 15, 19, 70, 49,  7, 53, 22, 21, 31, 19, 11, 18, 20,
    12, 35, 17, 23, 17,  4,  2, 31, 30, 13, 27,  0, 39, 37,  5, 14, 13, 22,
], dtype = "float32")

n = tf.size(count_data, out_type = tf.float32)

var_name = ["lambda1", "lambda2", "tau"]

@tfd.JointDistributionCoroutineAutoBatched
def mdl_batch():
	lamb1 = yield tfd.Gamma(concentration=8., rate=0.3, name="lambda1")
	lamb2 = yield tfd.Gamma(concentration=8., rate=0.3, name="lambda2")
	tau = yield tfd.Uniform(low=0., high=1., name="tau")
	rate = tf.gather([lamb1, lamb2], indices = tf.cast(tau*n <= tf.range(n), dtype="int32")) # vectorize ifelse
	Y = yield tfd.Poisson(rate=rate,name="Y")

# a helper function in McMC chain
def trace_fn(current_state, kernel_results):
	mdr = kernel_results.inner_results.inner_results
	return mdr.target_log_prob, mdr.leapfrogs_taken, mdr.has_divergence, mdr.energy, mdr.log_accept_ratio

@tf.function(autograph = False, experimental_compile = True) # speed up a lot the McMC sampling
def run_mcmc( # pass numeric arguments as Tensors whenever possible
	init_state, unconstraining_bijectors,
	num_steps = 50000, burnin = 10000,
	num_leapfrog_steps = 3, step_size = .5
):
	kernel0 = tfp.mcmc.NoUTurnSampler(
		target_log_prob_fn = lambda *args: mdl_batch.log_prob(Y=count_data, *args),
		step_size = step_size
	)
	kernel1 = tfp.mcmc.TransformedTransitionKernel(
		inner_kernel= kernel0,
		bijector = unconstraining_bijectors
	)
	kernel2 = tfp.mcmc.DualAveragingStepSizeAdaptation( # pkr = previous kernel results
		inner_kernel = kernel1,
		num_adaptation_steps = int(0.8*burnin),
		step_size_setter_fn = lambda pkr, new_step_size: pkr._replace(inner_results = pkr.inner_results._replace(step_size=new_step_size)),
		step_size_getter_fn = lambda pkr: pkr.inner_results.step_size,
		log_accept_prob_getter_fn = lambda pkr: pkr.inner_results.log_accept_ratio
	)
	# tf.get_logger().setLevel("ERROR") # multiple chains
	return tfp.mcmc.sample_chain( # ATTENTION: 2 values to unpack
		num_results = num_steps,
		num_burnin_steps = burnin,
		current_state = init_state,
		kernel = kernel2,
		trace_fn = trace_fn
	)

nchain = 4
init_state = [mdl_batch.sample(nchain)._asdict()[_] for _ in var_name]
unconstraining_bijectors = [tfb.Exp()]*2 + [tfb.Sigmoid()]
samples, sampler_stat = run_mcmc(init_state, unconstraining_bijectors)

#%% using the pymc3 naming convention, with log_likelihood instead of lp so that ArviZ can compute loo and waic
sample_stats_name = ['log_likelihood', 'tree_size', 'diverging', 'energy', 'mean_tree_accept']

sample_stats = {k: v.numpy().T for k, v in zip(sample_stats_name, sampler_stat)}
posterior = {k: np.swapaxes(v.numpy(), 1, 0) for k, v in zip(var_name, samples)}
az_trace = az.from_dict(posterior=posterior, sample_stats=sample_stats)

az.summary(az_trace)
az.plot_trace(az_trace)


#{{{ Imports

import numpy as np

#}}}

#{{{ Index of refractions

def sellmeier(wl, B1, B2, B3, C1, C2, C3):
    '''
    Calculate index of refraction using Sellmeiers equation

    n^2 = 1+B1*wl^2/(wl^2 - C1) + B2*wl^2/(wl^2 - C2) + B3*wl^2/(wl^2 - C3)

    See below for the coefficients for specific materials.
    http://www.cvimellesgriot.com/products/Documents/Catalog/Dispersion_Equations.pdf
    '''

    n = np.sqrt(1+B1*wl**2/(wl**2 - C1) + B2*wl**2/(wl**2 - C2) + B3*wl**2/(wl**2 - C3))
    return n

def n_fused_silica(wl):
    '''
    Calculate the index of refraction of fused silica for a given wavelength.
    '''
    #Convert the wavelength to the unit of um.
    wl = wl*1e6

    B1 = 6.961663e-1
    B2 = 4.079426e-1
    B3 = 8.974794e-1
    C1 = 4.67914826e-3
    C2 = 1.35120631e-2
    C3 = 9.79340025e1

    return sellmeier(wl, B1, B2, B3, C1, C2, C3)

def n_sapphire_ordinary(wl):
    '''
    Calculate the index of refraction of Sapphire ordinary axis for a given wavelength.
    '''

    #Convert the wavelength to the unit of um.
    wl = wl*1e6

    B1 = 1.4313493
    B2 = 6.5054713e-1
    B3 = 5.3414021
    C1 = 5.2799261e-3
    C2 = 1.42382647e-2
    C3 = 3.25017834e2

    return sellmeier(wl, B1, B2, B3, C1, C2, C3)

def n_sapphire_extraordinary(wl):
    '''
    Calculate the index of refraction of Sapphire extraordinary axis for a given wavelength.
    '''

    #Convert the wavelength to the unit of um.
    wl = wl*1e6

    B1 = 1.5039759
    B2 = 5.5069141e-1
    B3 = 6.5927379
    C1 = 5.48041129e-3
    C2 = 1.47994281e-2
    C3 = 4.0289514e2

    return sellmeier(wl, B1, B2, B3, C1, C2, C3)

n_fused_silica_532nm = 1.46071
n_fused_silica_1064nm = 1.45
n_fused_silica_1550nm = 1.444

no_sapphire_532nm = 1.77170
ne_sapphire_532nm = 1.76355

no_sapphire_1064nm = 1.75449
ne_sapphire_1064nm = 1.74663

no_sapphire_1550nm = 1.74618
ne_sapphire_1550nm = 1.73838

#}}}


import numpy as np

def expectedPortfolioRet(returns, weight):
    return np.sum(returns.mean() * weight) * 252

def expectedPortfolioStd(returns, weight):
    return np.sqrt(np.dot(weight, np.dot(returns.cov() * 252, weight)))

def expectedSharpeRatio(returns, weight, rf=0):
    return (expectedPortfolioRet(returns, weight) - rf) / (
        expectedPortfolioStd(returns, weight)
    )

def sharpe(returns, std, rf=0):
    return (returns - rf) / std

def randomWeight(length):
    w = np.random.random(length)
    w /= np.sum(w)
    return w


from math import sqrt
from scipy.stats import t

class TTests(object):

    @classmethod
    def t_statistic(cls, population_mean, sample_mean, sample_std_deviation, sample_length):
        """Gives the t_statistics

        :param population_mean: (float)
        :param sample_mean: (float)
        :param sample_std_deviation: (float)
        :param sample_length: (int)
        :return: (int) t_statistic
        """

        # Numerator is difference of sample mean and population mean
        t_num = float(sample_mean) - float(population_mean)

        # Denominator is sample's standard deviation divided by square root of sample length
        t_denom = float(sample_std_deviation)/sqrt(float(sample_length))

        return t_num/t_denom

    @classmethod
    def t_distribution(cls, sample_length, alpha):
        """

        :param sample_length: (int)
        :param alpha: (float)
        :return: (float, float) r_min, r_max
        """
        df = sample_length - 1
        half_alpha = alpha/float(2)

        # T-distribution minimum value
        r_min = t.ppf(half_alpha, df)

        # T-distribution maximum value
        r_max = t.ppf(1-half_alpha, df)

        return r_min, r_max

class Statistics(object):

    def __init__(self, data_list):

        self.data_list = data_list

    @classmethod
    def sum(cls, data_list):
        return sum(data_list)

    @classmethod
    def mean(cls, summation, list_length):
        """

        :return: (float) mean value
        """
        return summation/float(list_length)

    @classmethod
    def variance(cls, data_list, mean):
        """

        :return: (float) variance value
        """

        square_diff = [(x-mean)**2 for x in data_list]
        return sum(square_diff)/float(len(data_list))

    @classmethod
    def standard_dev(cls, variance):
        """

        :return: (float) standard deviation
        """
        return sqrt(variance)

    def compute_all(self):
        self.data_list_len_f = len(self.data_list)
        self.data_list_sum = self.sum(self.data_list)
        self.data_list_mean = self.mean(self.data_list_sum, self.data_list_len_f)
        self.data_list_variance = self.variance(self.data_list, self.data_list_mean)
        self.data_list_standard_dev = self.standard_dev(self.data_list_variance)


# # from scipy.stats.stats import pearsonr
# import numpy as np
#
import math
physics_scores = [int(x) for x in input().strip().split(' ')]
history_scores = [int(x) for x in input().strip().split(' ')]
# print(physics_scores, history_scores)
# print(len(physics_scores), len(history_scores))
# # print("%.3f" %pearsonr(physics_scores,history_scores))
# print(np.corrcoef(physics_scores, history_scores))

def average(l):
    n = len(l)
    total = sum(l)
    return total//n

def pearson_corr(l1,l2):
    n = len(l1)# should be equal to len(l2)
    x = average(l1)
    y = average(l2)
    xdiff = 0
    ydiff = 0
    diffprod = 0
    for i in range(n):
        xdiff_temp = l1[i] - x
        ydiff_temp = l2[i] - y
        diffprod += xdiff_temp * ydiff_temp
        xdiff += xdiff_temp ** 2
        ydiff += ydiff_temp ** 2
    # print(diffprod)
    # print(xdiff, ydiff)
    return diffprod / math.sqrt(xdiff * ydiff)

print(pearson_corr(physics_scores, history_scores))


'''
area_curves.py

Find the area enclosed by two curves between two points
'''

from sympy import Integral, Symbol, SympifyError, sympify

def find_area(f1x, f2x, var, a, b):
    a = Integral(f1x-f2x, (var, a, b)).doit()
    return a

if __name__ == '__main__':
    f1x = input('Enter the upper function in one variable: ')
    f2x = input('Enter the lower upper function in one variable: ')
    var = input('Enter the variable: ')
    l = float(input('Enter the lower bound of the enclosed region: '))
    u = float(input('Enter the upper bound of the enclosed region: '))

    try:
        f1x = sympify(f1x)
        f2x = sympify(f2x)
    except SympifyError:
        print('One of the functions entered is invalid')
    else:
        var = Symbol(var)
        print('Area enclosed by {0} and {1} is: {2} '.format(f1x, f2x, find_area(f1x, f2x, var, l, u)))


import numpy as np
# from __future__ import annotations

# softmax = exp(a) / sum(exp(a))

def softmax(vector):
    exp_vec = np.exp(vector)
    exp_sum_vec = np.sum(exp_vec)
    return exp_vec / exp_sum_vec

if __name__ == "__main__":
    # return max or 0 depending on value
    print(softmax(np.array([5, 1])))


# Code made for : Sergio Andrés Díaz Ariza
# Specialization--> Statistics in Python Coursera
# 19 Abril, 2021
# License MIT

# Replicate Cartwheel example

import numpy as np
import statsmodels.api as sm
import pandas as pd

t_star = 1.96  # Number of t from tabbles
p = .85  # Proportion of population
n = 659  # Number of observations

se = np.sqrt((p * (1 - p)) / n)  # Standard Error
print("\nStandard Error:\t{}".format(se))

# Confidence Interval
lcb = p - t_star * se
ucb = p + t_star * se
print("\nConfidence Interval:\t({}, {})".format(lcb, ucb))

# Library that calculate the intervals

ci = sm.stats.proportion_confint(n * p,
                                 n)  # Here we calculate the confidence interval, with the proportion and population
print("\nConfidence interval with stats library: {}\n".format(ci))

# With pandas and data basis
df = pd.read_csv(r"C:\Users\HP\PycharmProjects\Python_Statistics\Understanding_and_Visualizing_Data_With_Python"
                 r"\Univariate Data\Python for Analysis of Univariate Data\Cartwheeldata.csv")

print(df.head())

mean = df["CWDistance"].mean()
sd = df["CWDistance"].std()
n = len(df)

tstar = 2.064
se = sd / np.sqrt(n)

print("\nStandard Error of population:\t {}".format(se))

lcb2 = mean - tstar * se
ucb2 = mean + tstar * se
print("\nConfidence Interval Cartwheel:\t({}, {})".format(lcb2, ucb2))

# Using the library stats

ci2 = sm.stats.DescrStatsW(df["CWDistance"]).zconfint_mean()
print("\nConfidence interval with stats library: {}\n".format(ci2))

Inferential Satatistics Analysis/Introduction Confidence Intervals/Introduction.py


import numpy as np

def compute_cost(X, y, theta):
    # Initialize some useful values
    m = y.size
    cost = 0

    # ===================== Your Code Here =====================
    # Instructions : Compute the cost of a particular choice of theta.
    #                You should set the variable "cost" to the correct value.

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

    # cost of linear regression
    # J = (X*theta-y)'*(x*theta-y)/(2*m)
    # or J = (x*theta-y)**2/(2*m)

    #cost = sum((X.dot(theta)-y)**2)/(2*m)
    #cost = (X.dot(theta)-y).transpose().dot(X.dot(theta)-y)/(2*m)
    diff = (np.dot(X, theta)-y)
    cost = np.dot(diff, diff.T)/(2*m)
    return cost


from math import sqrt, log10, pow
import json, os, yaml
import numpy as np

class FallVelocity_solver(object):
    """
    FallVelocity

    Python version of Gary Parker's 1D Sediment Transport Morphodynamics e-book,
    originally in Visual Basic and converted to C by Andrew Leman (2009)

    MC Perignon
    Oct 2015

    ----------------------------------------------

    Computes the settling velocity, vs, of a particle with the formulation of Dietrich (1982).

    Only valid for Reynold's numbers less than or equal to 2.5 x 10**6. If Re is greater than
    this upper limit, warns and exits.

    Dietrich, E. W., 1982, Settling velocity of natural particles,
    Water Resources Research, 18(6), 1626-1982.

    ----------------------------------------------
    Input:
    D: Sediment particle size (mm)
    nu: Kinematic viscosity of the liquid (m**2/s)
    g: Acceleration due to gravity (m/s**2)
    rho_w: Density of liquid (Kg/m**3)
    rho_s: Density of sediment(Kg/m**3)

    Output:
    vs: Particle settling velocity (m/s)
    Re: Particle Reynolds number
    Rf: Dimensionless fall velocity of the particle

    Equations:
    R = (rho_s - rho_w) / rho_w

    Re = sqrt(R*g*D)*D / nu

    Rf = vs / sqrt(R*g*D)
    """

    def __init__(self, params = None):

        if params is None:

            filename='input_files/FallVelocity.yaml'

            with open(filename, 'r') as file_obj:
                params = yaml.load(file_obj)

            default_params = {
                  'particle__diameter': 0.1,
                  'fluid__kinematic_viscosity': 1e-6,
                  'gravitational_acceleration': 9.81,
                  'fluid__density': 1000.,
                  'particle__density': 2650.,
                  'output_filename': 'output/FallVelocity_output.json'}

            # sets missing parameters to default values
            for key,value in default_params.items():
                params[key] = params.get(key, default_params[key])

            for key,value in params.items():
                if (value is None) or (value == 'None'):
                    params[key] = default_params[key]

        self._grain_size = np.array([float(params['particle_diameter'])]) # mm
        self._nu = np.array([float(params['fluid__kinematic_viscosity'])])
        self._g = np.array([float(params['gravitational_acceleration'])])
        self._rho_w = np.array([float(params['fluid__density'])])
        self._rho_s = np.array([float(params['particle__density'])])
        self._output_filename = str(params['output_filename'])

        # Outputs
        self._vs = np.empty((1,))
        self._Re = np.empty((1,))
        self._Rf = np.empty((1,))

    @property
    def grain_size(self):
        """Grain size in mm."""

        return self._grain_size

    @grain_size.setter
    def grain_size(self, new_D):
        """Set the grain size.

        Parameters
        ----------
        new_D : float
            New grain size in mm.
        """
        self._grain_size[:] = new_D

    @property
    def kinematic_viscosity(self):
        """Kinematic viscosity of the fluid in m2/s"""
        return self._nu

    @kinematic_viscosity.setter
    def kinematic_viscosity(self, new_nu):
        """Set the kinematic viscosity.

        Parameters
        ----------
        new_nu : float
            New kinematic viscosity in m2/s.
        """

        self._nu[:] = new_nu

    @property
    def gravitational_acceleration(self):
        """Gravitational acceleration in m/s2"""
        return self._g

    @gravitational_acceleration.setter
    def gravitational_acceleration(self, new_g):
        """Set the gravitational acceleration.

        Parameters
        ----------
        new_g : float
            New gravitational acceleration in m/s2.
        """

        self._g[:] = new_g

    @property
    def density_of_fluid(self):
        """Density of the fluid in Kg/m3"""
        return self._rho_w

    @density_of_fluid.setter
    def density_of_fluid(self, new_rhow):
        """Set the density of the fluid.

        Parameters
        ----------
        new_rhow : float
            New density of the fluid in Kg/m3.
        """

        self._rho_w[:] = new_rhow

    @property
    def density_of_particle(self):
        """Density of the particle in Kg/m3"""
        return self._rho_s

    @density_of_particle.setter
    def density_of_particle(self, new_rhos):
        """Set the density of the particle.

        Parameters
        ----------
        new_rhos : float
            New density of the particle in Kg/m3.
        """

        self._rho_s[:] = new_rhos

    @property
    def settling_velocity(self):
        """Settling velocity in m/s"""

        return self._vs

    @property
    def Reynolds_number(self):
        """Reynolds number of the particle"""

        return self._Re

    @property
    def dimensionless_fall_velocity(self):
        """Dimensionless fall velocity of the particle"""

        return self._Rf

    def run(self):

        self._R = (self._rho_s - self._rho_w) / self._rho_w

        self._Re[:] = self.calculateReynoldsnumber()
        self._Rf[:] = self.calculate_dimensionless_fall_velocity()
        self._vs[:] = self.calculate_settling_velocity()

    def calculateReynoldsnumber(self):

        _Re = sqrt(self._R * self._g * (self._grain_size/1000.)) * \
                    (self._grain_size/1000.) / self._nu

        failMessage  = "This equation is only valid for Reynolds numbers "\
                        "below 2.6e+06. The calculated Reynolds number for "\
                        "this particle is %.2g" % _Re
        assert _Re <= 2.6e6, failMessage

        return _Re

    def calculate_dimensionless_fall_velocity(self):

        x = log10(self._Re**2)
        y = (-3.76715) + (1.92944*x) - (0.09815*x*x) - \
            (0.00575*x*x*x) + (0.00056*x*x*x*x)

        _Rf = pow(pow(10,y) / self._Re, 1./3)

        return _Rf

    def calculate_settling_velocity(self):

        _vs = self._Rf * sqrt(self._R * self._g * (self._grain_size/1000.))

        return _vs

    def _assure_path_exists(self, path):
        """Helper function to create output directories"""

        dir = os.path.dirname(path)
        if not os.path.exists(dir):
                os.makedirs(dir)

    def finalize(self):

        self._assure_path_exists(self._output_filename)

        output_dict = {
            'Grain_size_mm' : list(self._grain_size),
            'Kinematic_viscosity' : list(self._nu),
            'Gravitational_acceleration' : list(self._g),
            'Density_of_fluid' : list(self._rho_w),
            'Density_of_sediment' : list(self._rho_s),
            'Reynolds_number' : list(self._Re),
            'Dimensionless_fall_velocity' : list(self._Rf),
            'Settling_velocity' : list(self._vs)
        }

        with open(self._output_filename, 'w') as f:
            json.dump(output_dict, f, indent=4)


from hsi_toolkit.util import img_det
import numpy as np

def spsmf_detector(hsi_img, tgt_sig, mask = None, mu = None, sig_inv = None):
	"""
	Subpixel Spectral Matched Filter
	 matched filter derived from a subpixel mixing model
	 H0: x = b
	 H1: x = alpha*s + beta*b
	 Ref: formulation from Eismann's book, pp 664

	Inputs:
	 hsi_image - n_row x n_col x n_band hyperspectral image
	 tgt_sig - target signature (n_band x 1 - column vector)
	 mask - binary image limiting detector operation to pixels where mask is true
	        if not present or empty, no mask restrictions are used
	 mu - background mean (n_band x 1 column vector)
	 siginv - background inverse covariance (n_band x n_band matrix)

	Outputs:
	 spsmf_out - detector image

	8/8/2012 - Taylor C. Glenn
	6/2/2018 - Edited by Alina Zare
	12/2018 - Python Implementation by Yutai Zhou
	"""
	if tgt_sig.ndim == 1:
		tgt_sig = tgt_sig[:, np.newaxis]

	spsmf_out, kwargsout = img_det(spsmf_helper, hsi_img, tgt_sig, mask, mu = mu, sig_inv = sig_inv)

	return spsmf_out

def spsmf_helper(hsi_data, tgt_sig, kwargs):
	mu = np.mean(hsi_data, axis = 1) if kwargs['mu'] is None else kwargs['mu']
	mu = mu[:, np.newaxis]
	sig_inv = np.linalg.pinv(np.cov(hsi_data.T, rowvar = False)) if kwargs['sig_inv'] is None else kwargs['sig_inv']

	n_band, n_pixel = hsi_data.shape
	s = tgt_sig # 72 x 1
	st_sig_inv = s.T @ sig_inv # 1 x 72
	st_sig_inv_s = s.T @ sig_inv @ s # 1 x 1
	K = n_band

	spsmf_data = np.zeros(n_pixel)

	for i in range(n_pixel):
		x = hsi_data[:,i][:, np.newaxis] # 72x1
		st_sig_inv_x = st_sig_inv @ x # 1 x 1
		a0 = (x.T @ sig_inv @ x) * st_sig_inv_s - st_sig_inv_x ** 2
		a1 = st_sig_inv_x * (s.T @ sig_inv @ mu) - st_sig_inv_s * (mu.T @ sig_inv @ x)
		a2 = -K * st_sig_inv_s

		beta = (-a1 + np.sqrt(a1 ** 2 - 4 * a2 * a0)) / (2 * a2)
		alpha = st_sig_inv @ (x - beta * mu) / st_sig_inv_s
		z1 = x - mu
		z2 = x - alpha * s - beta * mu

		spsmf_data[i] = z1.T @ sig_inv @ z1 - (z2.T @ sig_inv @ z2) / (beta ** 2) - 2 * K * np.log(np.abs(beta))

	return spsmf_data, {}


import numpy as np
import pandas as pd
from pandas import DataFrame as df
pd.set_option('display.width', 320)
pd.set_option('display.max_rows', 100)
pd.options.display.float_format = '{:,.2f}'.format
from scipy.stats import norm
import matplotlib.pyplot as plt

def BlackScholes(tau, S, K, sigma):
    d1=np.log(S/K)/sigma/np.sqrt(tau)+0.5*sigma*np.sqrt(tau)
    d2=d1-sigma*np.sqrt(tau)
    npv=(S*norm.cdf(d1)-K*norm.cdf(d2))
    delta=norm.cdf(d1)
    gamma=norm.pdf(d1)/(S*sigma*np.sqrt(tau))
    vega=S*norm.pdf(d1)*np.sqrt(tau)
    theta=-.5*S*norm.pdf(d1)*sigma/np.sqrt(tau)
    return {'npv':npv,'delta':delta,'gamma':gamma,'vega':vega,'theta':theta}

class Call(object):
    def __init__(self,start,T,K,N):
        self.T=T
        self.K=K
        self.start=start  #day to sell   option
        self.N=N

    def calc(self,today,vol,S):
        if today<self.start:
            return {'delta':0,'npv':0,'vega':0,'gamma':0,'theta':0,'intrinsic':0}
        if today>self.T:
            return {'delta':0,'npv':0,'vega':0,'gamma':0,'theta':0,'intrinsic':0}
        if today==self.T:
            return {'delta':0,'npv':0,'vega':0,'gamma':0,'theta':0,'intrinsic':self.N*max(0,S-self.K)}
        tau=(self.T-today)/250.
        call=BlackScholes(tau, S, self.K, vol)
        return {'delta':self.N*call['delta'],'npv':self.N*call['npv'],'vega':self.N*call['vega'],'gamma':self.N*call['gamma'],'theta':self.N*call['theta'],'intrinsic':self.N*max(0,S-self.K)}

def deltahedge(Ndays,Sdynamics="S*=(1.0+vol*np.sqrt(dt)*np.random.randn())",volDynamics="vol=.30"):
    S=90.0
    Strike=100.0
    vol=.30
    columns=('spot','vol','shares','cash','option','npv','vega','gamma','theta','pnlPredict')
    df = pd.DataFrame([[S,vol,0,0,0,0,0,0,0,0]],columns=columns)
    dt=1/250.
    cash=0
    dayToSellCall=1
    maturityCall=Ndays-1
    call=Call(dayToSellCall,maturityCall,Strike,-10)# sell one call on dayToSellCall day
    for day in range(1,Ndays+1):
        exec(Sdynamics)
        exec(volDynamics)

        if day==dayToSellCall: #sell call
          callValue=call.calc(day,vol,S)
          cash-=callValue['npv']

        #delta hedge
        callValue=call.calc(day,vol,S)
        delta=callValue['delta']
        currentNumberShares=df.iloc[day-1].shares
        sharesBuy=-currentNumberShares-delta
        cash-=sharesBuy*S
        if day==maturityCall:
            cash+=call.calc(day,vol,S)['intrinsic'] #settle call

        gamma=callValue['gamma']
        theta=callValue['theta']
        dS=S-df.iloc[day-1].spot
        pnlPredict=0.5*gamma*dS*dS+theta*dt
        dfnew=pd.DataFrame([[S,vol,-delta,cash,-callValue['npv'],cash+callValue['npv']-delta*S,callValue['vega'],gamma,theta/250.,pnlPredict]],columns=columns)
        df=df.append(dfnew,ignore_index=True)

    df['pnl'] = df['npv'] - df['npv'].shift(1)
    df['vol']=100.0*df['vol']
    df['error']=df['pnl']-df['pnlPredict']
    df.set_value(dayToSellCall, "error", 0)
    #df.loc[:,['vol','spot']].plot(title='Spot and implied Volatility')
    df.loc[:,['npv','spot','option']].plot(title='-Call+delta*S+cash vs Spot {0} {1}'.format(Sdynamics,volDynamics))
    df.loc[:,['theta']].plot(title='Theta {0} {1}'.format(Sdynamics,volDynamics))
    df.loc[:,['pnl']].hist(bins=50)
    #df.loc[:,['error']].hist(bins=50)
    print(df.loc[:,['pnl']].describe())
    #print df

deltahedge(1000)#constant vol
deltahedge(1000,volDynamics="vol*=(1.0+0.5*np.sqrt(dt)*np.random.randn())")#stochastic vol
deltahedge(1000,Sdynamics="S=90") #consant OTM
deltahedge(10000,Sdynamics="S=100") #consant ATM
deltahedge(10000,Sdynamics="S=110")#consant ITM
deltahedge(10000,Sdynamics="S+=S*dt") #growing stock
plt.show()


import numpy as np

class LossFunction:
    @staticmethod
    def calculate_cost(expected_value, predicted):
        raise NotImplementedError("Should have implemented this!")

    @staticmethod
    def calculate_cost_gradient(expected_value, outputs, derivative_outputs):
        raise NotImplementedError("Should have implemented this!")

class MeanSquaredError(LossFunction):
    @staticmethod
    def calculate_cost(expected_value, outputs):
        return 0.5 * np.power(np.linalg.norm(expected_value - outputs), 2)

    @staticmethod
    def calculate_cost_gradient(expected_value, outputs, derivative_outputs):
        return (outputs - expected_value) * derivative_outputs


import numpy as np
import matplotlib.pyplot as plt

# O(n) Mathod:

def Fibonacci_Matrix_Calc (i):

    # Define an intitial list to store the result.
    F_list = [0, 1]

    # Define matrix operator.
    Matrix_Op = np.matrix('1 1; 1 0', dtype = np.longlong)
    # Define initial vector (1, 0).
    Matrix_0 = np.matrix('1; 0', dtype = np.longlong)

    # Matrix operation.
    for n in range (1, i-1):
        Matrix_N = Matrix_Op * Matrix_0
        Matrix_0 = Matrix_N

        # Insert the number into the list.
        Matrix_to_Array = np.array(Matrix_N).ravel()
        F_list.append(Matrix_to_Array[0])

    return (F_list)

#Driver:
F = Fibonacci_Matrix_Calc(10)
print (F)
#Notice: Numpy matrix defalt data type is int32... so after the 47th number it will overflow...

# Plot:
F_plot_val = Fibonacci_Matrix_Calc(93)
# x axis
def axis_x():
    list_axis_x = []
    l = 0
    for l in range (1, len(F_plot_val)+1):
        list_axis_x.append(l)
        l += l
    return (list_axis_x)
a_x = axis_x()
plt.plot(a_x, F_plot_val, 'o')
plt.show()

# O(logn) Mathod:

# Define the "A" Matrix.
A = [[1, 1], [1, 0]]

# Difine Initial Vector.
V = [1, 0]

# Matrices miltiplication (the matrices are expected in the form of a list of 2x2 size).
def Multiply_Matrices(Matrix_1, Matrix_2):
    # Calc Matrix x Vector.
    if np.ndim(Matrix_2) == 1:
        New_Metrix_c1_r1 = Matrix_1[0][0] * Matrix_2[0] + Matrix_1[0][1] * Matrix_2[1]
        New_Metrix_c1_r2 = Matrix_1[0][0] * Matrix_2[0] + Matrix_1[0][1] * Matrix_2[1]
        New_Vector = [New_Metrix_c1_r1, New_Metrix_c1_r2]
        return New_Vector
    # Calc Matrix x Matrix.
    New_Metrix_c1_r1 = Matrix_1[0][0] * Matrix_2[0][0] + Matrix_1[0][1] * Matrix_2[1][0]
    New_Metrix_c1_r2 = Matrix_1[0][0] * Matrix_2[0][1] + Matrix_1[0][1] * Matrix_2[1][1]
    New_Metrix_c2_r1 = Matrix_1[1][0] * Matrix_2[0][0] + Matrix_1[1][1] * Matrix_2[1][0]
    New_Metrix_c2_r2 = Matrix_1[1][0] * Matrix_2[0][1] + Matrix_1[1][1] * Matrix_2[1][1]
    # Calc Result.
    New_Metrix = [[New_Metrix_c1_r1, New_Metrix_c1_r2], [New_Metrix_c2_r1, New_Metrix_c2_r2]]
    return New_Metrix

# Matrix Exponentiation Calc.
def Matrix_Exponentiation(M, p):
    if p == 1:
        return M
    elif p % 2 == 0:
        return Matrix_Exponentiation(Multiply_Matrices(M, M), p//2)
    else:
        # If devided by 2 remain 1, then need to multiply with "A" one more time.
        return Multiply_Matrices(M, Matrix_Exponentiation(Multiply_Matrices(M, M), p//2))

# Output the Nth Fibonacci Number.
Nth_Fib = 21
print(Multiply_Matrices((Matrix_Exponentiation(A, Nth_Fib - 1)), V)[0])

# O(1) Time Complexcity..

# Nth Fibonacci Number.
N = 21

# Golden Ratio Phi.
phi = (1 + np.sqrt(5))/2
#Conjugate of Golden Ratio Phi.
phi_con = (1 - np.sqrt(5))/2

# Initial Vector.
V = np.matrix('1, 0')

# Eigenbasis S and S-Inverse Matrices.
S_mtx = np.matrix([[phi, 1], [phi_con, 1]])
S_inv = (1/np.sqrt(5)) * np.matrix([[1, -1], [-phi_con, phi]])

# A Matrix and A^n Matrix.
A_mtx = np.matrix([[phi, 0],[0, phi_con]])
A_to_the_n_mtx = np.matrix([[phi ** (N-1), 0], [0, phi_con ** (N-1)]])

# Calc [Fn, Fn-1] = S x A^n x S^-1 x V
Fn_Fn_m1 = V * S_inv * A_to_the_n_mtx * S_mtx
FN = np.array(Fn_Fn_m1).ravel()

print (FN[0])

# Create Fibonacci Numbers Function

def Fibonacci_calc(i):

    # Innitializing first 2 numbers (basic cases) in the Fibonacci number set.
    Fn_group = [1, 1]

    # Case 1: only one number.
    if i == 1:
        return (Fn_group.pop(Fn_group[-1]))

    # Case 2: only 2 numbers.
    if i == 2:
        return (Fn_group)

    # Calc and return the required Fibonacci number array.
    for x in range (1, i):
        Fn_group.append(Fn_group[-1] + Fn_group[-2])
    return (Fn_group)

print(Fibonacci_calc(21)


import numpy as np
from scipy import optimize

data = np.loadtxt('../data/ex2data1.txt', delimiter=',')

features = data[:,:2]
y = data[:,2]

m, n = features.shape
X = np.column_stack((np.ones((m, 1)), features))
initial_theta = np.zeros((n+1))
# this is the hypothsis function h(X)
def sigmoid(z):
    return 1/(1+np.exp(-z))

def costFunction(initial_theta, X, y):
    # J = (-1/m) * sum(     y .* log(sigmoid(X*theta))     +     (1-y) .* log(1 - sigmoid(X*theta))     );
    h_of_X = sigmoid(np.dot(X, initial_theta[:,np.newaxis]))
    J = np.sum(-y[:, np.newaxis] * np.log(h_of_X) -
                  (1-y[:, np.newaxis]) * np.log(1-h_of_X))/X.shape[0]
    return J

def gradient(theta, X, y):
    # grad = (1/m) *  ( (sigmoid(X*theta) - y)'  * X );
    h_of_X = sigmoid(np.dot(X, theta[:,np.newaxis]))
    grad = np.dot(X.T, h_of_X - y[:, np.newaxis]) / X.shape[0]
    #print grad.flatten()

    return grad.flatten()

print(gradient(initial_theta, X, y))

res = optimize.minimize(fun=costFunction, x0 =initial_theta, args=(X,y), method='TNC', jac=gradient)
print(res)