from model import graph_generator
from scipy.stats import norm
import math

class BlackScholes:
    __oneDay = 1/365
    # *********************** D1 & D2 **********************
    @classmethod
    def __d1(cls,S,K,R,T,Vol):
        """Class method so that the method has the access to the other methods inside the class without being forced
            to call BlackScholes.My_function() but with cls.My_function()"""
        return (math.log(S/K)+(R+(Vol**2)/2)*T)/(Vol*math.sqrt(T))

    @classmethod
    def __d2(cls,S,K,R,T,Vol):
        return cls.__d1(S,K,R,T,Vol)-Vol*math.sqrt(T)

    # *********************** PRICE FORMULAS **********************
    @classmethod
    def call_price(cls,S,K,R,T,Vol):
        """Return the price of a vanilla call"""
        price = S*norm.cdf(cls.__d1(S,K,R,T,Vol))-K*math.exp(-R*T)*norm.cdf(cls.__d2(S,K,R,T,Vol))
        return round(price,3)

    @classmethod
    def call_spread_price(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.call_price(S,K1,R,T,Vol)-cls.call_price(S,K2,R,T,Vol)
        return -cls.call_price(S, K1, R, T, Vol) + cls.call_price(S, K2, R, T, Vol)

    @classmethod
    def put_spread_price(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return -cls.put_price(S,K2,R,T,Vol)+cls.put_price(S,K1,R,T,Vol)
        return cls.put_price(S,K2,R,T,Vol)-cls.put_price(S,K1,R,T,Vol)

    @classmethod
    def put_price(cls,S,K,R,T,Vol):
        """Return the price of a vanilla put"""
        price = -S*norm.cdf(-cls.__d1(S,K,R,T,Vol))+K*math.exp(-R*T)*norm.cdf(-cls.__d2(S,K,R,T,Vol))
        return round(price,3)

    @classmethod
    def call_digital_price(cls,S,K,R,T,Vol):
        return round(math.exp(-R*T)*norm.cdf(cls.__d2(S,K,R,T,Vol)),3)

    @classmethod
    def put_digital_price(cls,S,K,R,T,Vol):
        return round(math.exp(-R*T)*norm.cdf(-cls.__d2(S,K,R,T,Vol)),3)

    @classmethod
    def straddle_price(cls,S,K,R,T,Vol):
        return cls.call_price(S,K,R,T,Vol)+cls.put_price(S,K,R,T,Vol)

    @classmethod
    def strangle_price(cls,S,K1,K2,R,T,Vol):
        return cls.put_price(S,K1,R,T,Vol)+cls.call_price(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_price(cls,S,K1,K2,R,T,Vol):
        return -cls.put_price(S,K1,R,T,Vol)+cls.call_price(S,K2,R,T,Vol)

    @classmethod
    def calendar_price(cls,S,K,R,T1,T2,Vol):
        return -cls.call_price(S,K,R,T1,Vol)+cls.call_price(S,K,R,T2,Vol)

    # *********************** DELTA FORMULAS **********************
    @classmethod
    def call_delta(cls,S,K,R,T,Vol):
        delta = norm.cdf(cls.__d1(S,K,R,T,Vol))
        return round(delta,3)

    @classmethod
    def call_spread_delta(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.call_delta(S,K1,R,T,Vol) - cls.call_delta(S,K2,R,T,Vol)
        return -cls.call_delta(S,K1,R,T,Vol) + cls.call_delta(S,K2,R,T,Vol)

    @classmethod
    def put_delta(cls,S,K,R,T,Vol):
        delta = norm.cdf(cls.__d1(S,K,R,T,Vol))-1
        return round(delta,3)

    @classmethod
    def put_spread_delta(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return - cls.put_delta(S,K2,R,T,Vol) + cls.put_delta(S,K1,R,T,Vol)
        return cls.put_delta(S, K2, R, T, Vol) - cls.put_delta(S, K1, R, T, Vol)

    @classmethod
    def call_digital_delta(cls,S,K,R,T,Vol):
        return round((norm.pdf(cls.__d2(S,K,R,T,Vol))/(S*Vol*math.sqrt(T)))*math.exp(-R*T),3)

    @classmethod
    def put_digital_delta(cls,S,K,R,T,Vol):
        return round(-cls.call_digital_delta(S,K,R,T,Vol),3)

    @classmethod
    def straddle_delta(cls,S,K,R,T,Vol):
        return cls.call_delta(S,K,R,T,Vol)+cls.put_delta(S,K,R,T,Vol)

    @classmethod
    def strangle_delta(cls,S,K1,K2,R,T,Vol):
        return cls.put_delta(S,K1,R,T,Vol)+cls.call_delta(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_delta(cls,S,K1,K2,R,T,Vol):
        return -cls.put_delta(S, K1, R, T, Vol) + cls.call_delta(S, K2, R, T, Vol)

    @classmethod
    def calendar_delta(cls,S,K,R,T1,T2,Vol):
        return -cls.call_delta(S,K,R,T1,Vol)+cls.call_delta(S,K,R,T2,Vol)

    # *********************** GAMMA FORMULAS **********************
    @classmethod
    def gamma(cls,S,K,R,T,Vol):
        gamma = (1/(S*Vol*math.sqrt(T)))*norm.pdf(cls.__d1(S,K,R,T,Vol))
        return round(gamma,3)

    @classmethod
    def call_spread_gamma(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.gamma(S,K1,R,T,Vol)-cls.gamma(S,K2,R,T,Vol)
        return -cls.gamma(S,K1,R,T,Vol)+cls.gamma(S,K2,R,T,Vol)

    @classmethod
    def put_spread_gamma(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return - cls.gamma(S,K2,R,T,Vol) + cls.gamma(S,K1,R,T,Vol)
        return cls.gamma(S,K2,R,T,Vol) - cls.gamma(S,K1,R,T,Vol)

    @classmethod
    def call_digital_gamma(cls,S,K,R,T,Vol):
        return round((math.exp(-R*T)*norm.pdf(cls.__d2(S,K,R,T,Vol))*cls.__d1(S,K,R,T,Vol))/(S**2*Vol**2*T),3)

    @classmethod
    def put_digital_gamma(cls,S,K,R,T,Vol):
        return round(-cls.call_digital_gamma(S,K,R,T,Vol),3)

    @classmethod
    def straddle_gamma(cls,S,K,R,T,Vol):
        return 2*cls.gamma(S,K,R,T,Vol)

    @classmethod
    def strangle_gamma(cls,S,K1,K2,R,T,Vol):
        return cls.gamma(S,K1,R,T,Vol)+cls.gamma(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_gamma(cls,S,K1,K2,R,T,Vol):
        return -cls.gamma(S, K1, R, T, Vol) + cls.gamma(S, K2, R, T, Vol)

    @classmethod
    def calendar_gamma(cls, S, K, R, T1, T2, Vol):
        return -cls.gamma(S, K, R, T1, Vol) + cls.gamma(S, K, R, T2, Vol)

    # *********************** VEGA FORMULAS **********************
    @classmethod
    def vega(cls,S,K,R,T,Vol):
        vega = S*math.sqrt(Vol)*norm.pdf(cls.__d1(S,K,R,T,Vol))
        return round(vega,3)

    @classmethod
    def call_spread_vega(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.vega(S,K1,R,T,Vol) - cls.vega(S,K2,R,T,Vol)
        return - cls.vega(S,K1,R,T,Vol) + cls.vega(S,K2,R,T,Vol)

    @classmethod
    def call_digital_vega(cls,S,K,R,T,Vol):
        return round(-math.exp(-R*T)*cls.__d1(S,K,R,T,Vol)*norm.pdf(cls.__d2(S,K,R,T,Vol))/Vol,3)

    @classmethod
    def put_spread_vega(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return - cls.vega(S,K2,R,T,Vol) + cls.vega(S,K1,R,T,Vol)
        return cls.vega(S,K2,R,T,Vol) - cls.vega(S,K1,R,T,Vol)

    @classmethod
    def put_digital_vega(cls,S,K,R,T,Vol):
        return round(-cls.call_digital_vega(S,K,R,T,Vol),3)

    @classmethod
    def straddle_vega(cls,S,K,R,T,Vol):
        return 2*cls.vega(S,K,R,T,Vol)

    @classmethod
    def strangle_vega(cls,S,K1,K2,R,T,Vol):
        return cls.vega(S,K1,R,T,Vol)+cls.vega(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_vega(cls, S, K1, K2, R, T, Vol):
        return -cls.vega(S, K1, R, T, Vol) + cls.vega(S, K2, R, T, Vol)

    @classmethod
    def calendar_vega(cls, S, K, R, T1, T2, Vol):
        return -cls.vega(S, K, R, T1, Vol) + cls.vega(S, K, R, T2, Vol)

    # *********************** THETA FORMULAS **********************
    @classmethod
    def call_theta(cls,S,K,R,T,Vol):
        d2 = cls.__d2(S,K,R,T,Vol)
        theta = -(K*Vol*norm.pdf(d2)*math.exp(-R*T))/(2*math.sqrt(T)) - R*K*norm.cdf(d2)*math.exp(-R*T)
        return round(theta,3)

    @classmethod
    def call_spread_theta(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.call_theta(S,K1,R,T,Vol) - cls.call_theta(S,K2,R,T,Vol)
        return - cls.call_theta(S,K1,R,T,Vol) + cls.call_theta(S,K2,R,T,Vol)

    @classmethod
    def call_digital_theta(cls,S,K,R,T,Vol):
        disc_factor = math.exp(-R*T)
        d1 = cls.__d1(S,K,R,T,Vol)
        d2 = cls.__d2(S,K,R,T,Vol)
        return round(R*disc_factor*norm.cdf(d2)+disc_factor*norm.pdf(d2)*((d1/(2*T)) - (R/(Vol*math.sqrt(T)))),3)

    @classmethod
    def put_theta(cls,S,K,R,T,Vol):
        d1 = cls.__d1(S, K, R, T, Vol)
        d2 = cls.__d2(S, K, R, T, Vol)
        theta = R*K*math.exp(-R*T)*norm.cdf(-d2) - S*norm.pdf(d1)*Vol/(2*math.sqrt(T))
        return round(theta,3)

    @classmethod
    def put_spread_theta(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return -cls.put_theta(S,K2,R,T,Vol) + cls.put_theta(S,K1,R,T,Vol)
        return cls.put_theta(S,K2,R,T,Vol) - cls.put_theta(S,K1,R,T,Vol)

    @classmethod
    def put_digital_theta(cls,S,K,R,T,Vol):
        return round(-R*math.exp(-R*T) - cls.call_digital_theta(S,K,R,T,Vol),3)

    @classmethod
    def straddle_theta(cls,S,K,R,T,Vol):
        return cls.call_theta(S,K,R,T,Vol)+cls.put_theta(S,K,R,T,Vol)

    @classmethod
    def strangle_theta(cls,S,K1,K2,R,T,Vol):
        return cls.put_theta(S,K1,R,T,Vol)+cls.call_theta(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_theta(cls, S, K1, K2, R, T, Vol):
        return -cls.put_theta(S, K1, R, T, Vol) + cls.call_theta(S, K2, R, T, Vol)

    @classmethod
    def calendar_theta(cls, S, K, R, T1, T2, Vol):
        return -cls.call_theta(S, K, R, T1, Vol) + cls.call_theta(S, K, R, T2, Vol)

    # *********************** PAYOFF GRAPH CALLERS **********************
    @classmethod
    def payoff_lists(cls, S, K, R, T, Vol, call_price, put_price):
        """Return a tuple : (list of strikes, list of Y curve , list of title)"""
        return graph_generator.Plotter.get_payoff_lists((cls.call_price, cls.put_price)
                                                        , K, R, T, Vol,cls.__oneDay,
                                                        ["Call", "Put"],
                                                        (call_price, put_price))

    @classmethod
    def payoff_call_spread(cls,K1,K2,R,T,Vol,bull_price,bear_price):
        return graph_generator.Plotter.get_payoff_strategy((cls.call_spread_price,cls.call_spread_price,cls.call_spread_price,cls.call_spread_price),
                                                                  ((K1,K2,R,T,Vol,True),(K1,K2,R,cls.__oneDay,Vol,True),(K1,K2,R,T,Vol,False),(K1,K2,R,cls.__oneDay,Vol,False)),
                                                                  ["Bull Sp.","Bear Sp."],
                                                                  (bull_price,bull_price, bear_price,bear_price),
                                                                  (K1+K2)/2)

    @classmethod
    def payoff_put_spread(cls, K1, K2, R, T, Vol, bull_price, bear_price):
        return graph_generator.Plotter.get_payoff_strategy((cls.put_spread_price, cls.put_spread_price,cls.put_spread_price, cls.put_spread_price),
                                                                  ((K1, K2, R, T, Vol, True),(K1, K2, R, cls.__oneDay, Vol, True),
                                                                   (K1, K2, R, T, Vol, False),(K1, K2, R, cls.__oneDay, Vol, False)),
                                                                  ["Bull Spread","Bear Spread"],
                                                                 (bull_price,bull_price, bear_price,bear_price),
                                                                  (K1 + K2) / 2)

    @classmethod
    def payoff_digital_graph(cls, S, K, R, T, Vol, call_price, put_price):
        return graph_generator.Plotter.get_payoff_lists((cls.call_digital_price, cls.put_digital_price)
                                                         , K, R, T, Vol,cls.__oneDay,
                                                         ["Digital Call", "Digital Put"],
                                                         (call_price, put_price))

    @classmethod
    def straddle_payoff_graph(cls,S,K,R,T,Vol,straddle_price):
        return graph_generator.Plotter.get_payoff_lists((cls.straddle_price,)
                                                         , K, R, T, Vol,cls.__oneDay,
                                                         ["Straddle"],
                                                         (straddle_price,))

    @classmethod
    def strangle_payoff_graph(cls,S,K1,K2,R,T,Vol,price):
        return graph_generator.Plotter.get_payoff_strategy([cls.strangle_price,cls.strangle_price]
                                                         , [[K1,K2, R, T, Vol],[K1,K2, R, cls.__oneDay, Vol]],
                                                         ["Strangle"],
                                                         [price,price],(K1+K2)/2)

    @classmethod
    def risk_rev_payoff_graph(cls, S, K1, K2, R, T, Vol,price):
         return graph_generator.Plotter.get_payoff_strategy([cls.risk_rev_price,cls.risk_rev_price]
                                                                  , [[K1, K2, R, T, Vol],[K1, K2, R, cls.__oneDay, Vol]],
                                                                  ["Risk Reversal"],
                                                                  [price,price], (K1 + K2) / 2)

    @classmethod
    def calendar_payoff_graph(cls, S, K, R, T1, T2, Vol,price):
        return graph_generator.Plotter.get_payoff_strategy([cls.calendar_price,cls.calendar_price], [[K, R, T1, T2, Vol],[K, R, cls.__oneDay,  T2-T1+cls.__oneDay, Vol]],
                                                           ["Calendar Spread"],
                                                           [price,price], K)

    # *********************** GREEKS GRAPH CALLERS **********************
    @classmethod
    def greeks_graph(cls,S,K,R,T,Vol):
        list_greeks = (cls.gamma,cls.gamma,cls.call_delta,cls.put_delta,cls.call_theta,cls.put_theta,cls.vega,cls.vega)
        list_title = ["Call Gamma","Put Gamma","Call Delta","Put Delta","Call Theta","Put Theta","Call Vega","Put Vega"]
        return graph_generator.Plotter.get_graph_greeks(list_greeks,K,R,T,Vol,cls.__oneDay,list_title)

    @classmethod
    def greeks_digital_graph(cls,S,K,R,T,Vol):
        list_greeks = (
        cls.call_digital_gamma, cls.put_digital_gamma, cls.call_digital_delta, cls.put_digital_delta, cls.call_digital_theta, cls.put_digital_theta, cls.call_digital_vega, cls.put_digital_vega)
        list_title = [
        "Digital Call Gamma", "Digital Put Gamma", "Digital Call Delta", "Digital Put Delta", "Digital Call Theta", "Digital Put Theta","Digital Call Vega", "Digital Put Vega"]
        return graph_generator.Plotter.get_graph_greeks(list_greeks, K, R, T, Vol,cls.__oneDay, list_title)

    @classmethod
    def call_spread_greeks_graph(cls,K1,K2,R,T,Vol):
        list_greeks = (
        cls.call_spread_gamma, cls.call_spread_gamma, cls.call_spread_delta, cls.call_spread_delta, cls.call_spread_theta, cls.call_spread_theta, cls.call_spread_vega, cls.call_spread_vega)
        list_title = [
        "Bull Gamma", "Bear Gamma", "Bull Delta", "Bear Delta", "Bull Theta", "Bear Theta" , "Bull Vega", "Bear Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks(list_greeks, ((K1,K2,R,T,Vol,True),(K1,K2,R,cls.__oneDay,Vol,True),(K1,K2,R,T,Vol,False),(K1,K2,R,cls.__oneDay,Vol,False)), list_title,(K1+K2)/2)

    @classmethod
    def put_spread_greeks_graph(cls,K1,K2,R,T,Vol):
        list_greeks = (
            cls.put_spread_delta, cls.put_spread_delta, cls.put_spread_gamma, cls.put_spread_gamma,
            cls.put_spread_vega, cls.put_spread_vega, cls.put_spread_theta, cls.put_spread_theta)
        list_title = [
            "Bull Gamma", "Bear Gamma", "Bull Delta", "Bear Delta", "Bull Theta",
            "Bear Theta" ,"Bull Vega", "Bear Vega"]

        return graph_generator.Plotter.get_graph_strategy_greeks(list_greeks, (
        (K1, K2, R, T, Vol, True),(K1, K2, R, cls.__oneDay, Vol, True), (K1, K2, R, T, Vol, False),(K1, K2, R, cls.__oneDay, Vol, False)), list_title, (K1 + K2) / 2)

    @classmethod
    def straddle_greeks_graph(cls, S, K, R, T, Vol):
        list_greeks = (cls.straddle_gamma, cls.straddle_delta, cls.straddle_theta, cls.straddle_vega)
        list_title = ["Gamma","Delta", "Theta", "Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks_one_leg(list_greeks, ((K, R, T, Vol),(K, R, cls.__oneDay, Vol)), list_title, K)

    @classmethod
    def strangle_greeks_graph(cls, S, K1, K2, R, T, Vol):
        list_greeks = (cls.strangle_gamma,cls.strangle_delta, cls.strangle_theta, cls.strangle_vega)
        list_title = ["Gamma", "Delta", "Theta", "Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks_one_leg(list_greeks, ((K1, K2, R, T, Vol),(K1, K2, R, cls.__oneDay, Vol)), list_title,
                                                                          (K1 + K2) / 2)

    @classmethod
    def risk_rev_greeks_graph(cls, S, K1, K2, R, T, Vol):
        list_greeks = (cls.risk_rev_gamma,cls.risk_rev_delta, cls.risk_rev_theta,cls.risk_rev_vega)
        list_title = ["Gamma", "Delta","Theta","Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks_one_leg(list_greeks, ((K1, K2, R, T, Vol),(K1, K2, R, cls.__oneDay, Vol)), list_title,
                                                                          (K1 + K2) / 2)

    @classmethod
    def calendar_greeks_graph(cls, S, K, R, T1, T2, Vol):
        list_greeks = (cls.calendar_gamma, cls.calendar_delta, cls.calendar_theta, cls.calendar_vega)
        list_title = ["Gamma", "Delta", "Theta", "Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks_one_leg(list_greeks, ((K, R, T1, T2, Vol),(K, R, cls.__oneDay, T2-T1+cls.__oneDay, Vol)), list_title,K)


import numpy as np
from math import cos, sin, atan2, pi
import matplotlib.pyplot as plt

def circular_mean(X, I):
    """Compute the circular mean of the given set of values X in I.

    Args:
        X (list[float]): The set of values.
        I (tuple[float,float]): The range these values are in.

    Returns:
        float: The circular mean.
    """
    assert I[0] < I[1]
    lenI = I[1] - I[0]
    pulse = 2 * pi / lenI
    sum_cos = 0.
    sum_sin = 0.
    for x in X:
        sum_cos += cos(pulse * x)
        sum_sin += sin(pulse * x)
    return atan2(sum_sin, sum_cos) / pulse

if __name__ == "__main__":
    vs = np.array([np.array([-5, 4]), np.array([4, -5])])
    mean_v = np.array([circular_mean(vs[:, 0], (-5, 5)),
                       circular_mean(vs[:, 1], (-5, 5))])
    print(f"vs: {vs}")
    print(f"mean: {mean_v}")

    for v in vs:
        plt.plot(v[0], v[1], '.k')
    plt.plot(mean_v[0], mean_v[1], '.g')
    plt.show()


import scipy as sc
import numpy as np
from matplotlib import pyplot as plt

# https://en.wikipedia.org/wiki/Gaussian_function
def Gaussian(x,a,b,c):
    up = ((x-b)**2.0)/(2.0*c**2.0)
    return a*np.exp(-up)

# https://en.wikipedia.org/wiki/Relativistic_Breit-Wigner_distribution
def BreitWigner(e,a,m,g):
    gamma = np.sqrt(m**2.0*(m**2.0 + g**2.0))
    k = (2.0*np.sqrt(2.0)*m*g*gamma)/(np.pi*np.sqrt(m**2.0 + gamma))
    lower = (e**2.0 - m**2.0)**2.0 + m**2.0*g**2.0
    return a*k/lower

def exponential(x,a,b,c):
    return a*np.exp(-b*x) + c

def linear(x,m,b):
    return m*x + b

def poly2(x, a,b,c):
    return a + b*(x) + c*(x**2.0)

def poly3(x, a,b,c,d):
    return a + b*(x) + c*(x**2.0) + d*(x**3.0)

def poly4(x, a,b,c,d,e):
    return a + b*(x) + c*(x**2.0) + d*(x**3.0) + e*(x**4.0)

class FittingResult(object):
    def __init__(self,func,xdata,popt,pcov,label="Result"):
        self.func = func
        self.xdata = xdata
        self.popt = popt
        self.pcov = pcov
        self.label = label
        self.errs = np.sqrt(np.diag(self.pcov))

    def __repr__(self):
        return "Parameters:\n{}".format(
            "\n".join(["\tx{} = {:.3f} +- {:.3f}".format(i,x,self.errs[i]) for i,x in enumerate(self.popt)])
        )

    def _repr_html_(self):
        return "
Parameters:

{}
".format(
            "\n".join(["
x{} = {:.3f} +- {:.3f}
".format(i,x,self.errs[i]) for i,x in enumerate(self.popt)])
        )

    def plot(self,show=False,error=False):
        if show:
            plt.figure()
        ydata = [self.func(x, *self.popt) for x in self.xdata]
        plt.plot(self.xdata,ydata,label=self.label+".Fit")
        plt.legend()
        if error:
            peydata = [self.func(x, *(o+e for o,e in zip(self.popt,self.errs))) for x in self.xdata]
            neydata = [self.func(x, *(o-e for o,e in zip(self.popt,self.errs))) for x in self.xdata]
            plt.fill_between(self.xdata,peydata,neydata,alpha=0.6)
        if show:
            plt.show()


import numpy as np
import math as m
# matrix construction ########################################################

def identity():
  return np.array([[1., 0., 0., 0.],
	           [0., 1., 0., 0.],
	           [0., 0., 1., 0.],
	           [0., 0., 0., 1.]])

def scale(sx=1., sy=1., sz=1.):
  return np.array([[sx, 0., 0., 0.],
	           [0., sy, 0., 0.],
	           [0., 0., sz, 0.],
	           [0., 0., 0., 1.]])

def translate(tx=0., ty=0., tz=0.):
  return np.array([[1., 0., 0., tx],
	           [0., 1., 0., ty],
	           [0., 0., 1., tz],
	           [0., 0., 0., 1.]])

# rotation with angle a in radians about axis (x,y,z)
def rotate(a=0., x=0., y=0., z=1.):
  s, c = m.sin(a), m.cos(a)
  h = m.sqrt(x*x + y*y + z*z)
  x, y, z = x/h, y/h, z/h
  sx, sy, sz = s*x, s*y, s*z
  oc = 1.-c
  return np.array([[oc*x*x+c,  oc*x*y-sz, oc*x*z+sy, 0.],
	           [oc*x*y+sz, oc*y*y+c,  oc*y*z-sx, 0.],
	           [oc*x*z-sy, oc*y*z+sx, oc*z*z+c,  0.],
	           [0.,        0.,        0.,        1.]])
# orthogonal projection
def ortho(l, r, b, t, n, f):
  w, h, d = r-l, t-b, f-n
  return np.array([[2./w, 0.,    0.,   -(r+l)/w],
	           [0.,   2./h,  0.,   -(t+b)/h],
	           [0.,   0.,   -2./d, -(f+n)/d],
	           [0.,   0.,    0.,    1.     ]])

# perspective projection, map frustum to unit cube
def frustum(l, r, b, t, n, f):
  w, h, d = r-l, t-b, f-n
  return np.array([[2.*n/w, 0.,      (r+l)/w,  0.      ],
	           [0.,     2.*n/h,  (t+b)/h,  0.      ],
	           [0.,     0.,     -(f+n)/d, -2.*f*n/d],
	           [0.,     0.,     -1.,       0.      ]])

# manipulation ###############################################################
def matrix(n, m, f=lambda i, j: 0.):
  I, J = range(n), range(m)
  return np.array([[f(i, j) for j in J] for i in I])

def size(A):
  return A.shape

def transpose(A):
  return np.transpose(A)

# opengl format
def column_major(A):
  return [float(a) for line in transpose(A) for a in line]

def exclude(A, i, j):
  return [R[:j]+R[j+1:] for R in A[:i]+A[i+1:]]

def top_left(A):
  n, m = size(A)
  return exclude(A, n-1, m-1)

# sum ########################################################################
def add(A, B):
  n, p = size(A)
  q, m = size(B)
  assert (n, p) == (q, m)
  return matrix(n, m, lambda i, j: A[i][j]+B[i][j])

def sub(A, B):
  n, p = size(A)
  q, m = size(B)
  assert (n, p) == (q, m)
  return matrix(n, m, lambda i, j: A[i][j]-B[i][j])
# product ####################################################################
def scalar(s, A):
  n, m = size(A)
  return matrix(n, m, lambda i, j: s*A[i][j])
def mul(A, B):
  return np.dot(A,B)
def product(A, *Bs):
  for B in Bs:
    A = np.dot(A,B)
  return A
def vapply(A,v):
  return np.dot(A,v)
# inverse ####################################################################
def det(A):
  return np.linalg.det(A)
def inverse(A):
  return np.linalg.inv(A)
# vector #####################################################################

# construction from list or tuples
def point(tpl):
  return np.array(tpl)
# vector from p1 to p0
def vector(p0, p1):
  return p0-p1

# homogenous vector from point p with weight w
def hom(p,w):
  return np.array([c*w for c in p] + [w]) if w else np.array([c for c in p] + [w])
# projection of homogenous vector
def proj(h):
  return np.array([h[i]/h[-1] if h[-1] else h[i] for i in range(0,len(h)-1)])
def cut(h):
  return np.array([h[i] for i in range(0,len(h)-1)])

# vector from coords
def coords(*args,dtype=float):
  return np.array(args,dtype=dtype)

# dot product of u and v: norm(u)*norm(v)*cos(angle between u and v)
def dot(u, v):
  return np.dot(u,v)
# squared norm of v
def norm2(v):
  return dot(v, v)
# norm of v, length of v
def norm(v):
  return m.sqrt(norm2(v))
# return unit vector in direction of v and length of v
def unit_length(v):
  n = norm(v)
  return point(v)/n, n
# return unit vector in direction of v
def unit(v):
  n = norm(v)
  #import pdb ; pdb.set_trace()
  return v/n
# square matrix filled with v in each row
def vmatrix(v):
  return np.array([[vi] for vi in v])

# parallel part of x to y: the projection of x onto y
def para(x,y):
  return dot(x,y)/norm2(y) * y
# perpendicular part of x to y:
# x == para(x,y)+perp(x,y), dot(para(x,y),perp(x,y)) == 0
def perp(x,y):
  return x - para(x,y)
# reflection of x about y
def refl(x,y):
  return -x + 2*para(x,y)

# perpendicular vector to 2d vector x, counterclockwise
def perp2ccw(x):
  return coords(-x[1],x[0])

def perp2cw(x):
  return coords(x[1],-x[0])


"""
polynomial_dissimilarity.py
--------------

From
----
Donnat, Claire, and Susan Holmes. "Tracking
network dynamics: A survey of distances
and similarity metrics." arXiv
preprint arXiv:1801.07351 (2018).

author: Jessica T. Davis
email:
Submitted as part of the 2019 NetSI Collabathon.

"""
import numpy as np
import networkx as nx
import scipy.sparse as ss
from .base import BaseDistance

class PolynomialDissimilarity(BaseDistance):
    def dist(self, G1, G2,k=5,alpha=1):
        """
        The function calculates the distance between two graphs by
        comparing the polynomials of the eigenvalue decomposition of
        their adjacency matrices, where $A^k$ is the adjacency matrix
        raised to the kth power and the the elements correspond to
        the number of paths of length k between nodes i and j.

        Params
        ------

        G1, G2 (nx.Graph): two networkx graphs to be compared.
        k (float): maximum degree of the polynomial
        alpha (float): weighting factor

        Returns
        -------
        dist (float): Polynomial Dissimilarity
        """
        A1 = nx.to_numpy_array(G1)
        A2 = nx.to_numpy_array(G2)

        P_A1 = calculate_polynomial(A1,k,alpha)
        P_A2 = calculate_polynomial(A2,k,alpha)

        dist = np.linalg.norm(P_A1-P_A2,ord='fro')/A1.shape[0]**2

    return dist

def calculate_polynomial(A,k,alpha):
    eig_vals,Q = np.linalg.eig(A)

    n = A.shape[0]
    W = np.diag(sum(list(map(lambda kp:eig_vals**kp/(n-1)**(alpha*kp-1), range(1,k+1)))))

    P_A  = np.dot(np.dot(Q,W),Q.T)

return P_A

import numpy,import scipy,import networkx


# -*- coding: utf-8 -*-
"""
Created on Thu Mar 29 11:39:05 2018

@author: garvit
"""

import scipy.stats as ss
from decimal import Decimal
import math as m
import numpy as np
from datetime import date,timedelta
from mibian import BS

def call_bsm (S0,K,r,T,Otype,sig):
    d1 = Decimal(m.log(S0/K)) + (r+ (sig*sig)/2)*T/(sig*Decimal(m.sqrt(T)))
    d2 = d1 - sig*Decimal(m.sqrt(T))
    if (Otype == "Call"):
        price = S0*Decimal(ss.norm.cdf(np.float(d1))) \
        - K*Decimal(m.exp(-r*T))*Decimal(ss.norm.cdf(np.float(d2)))
        return (price)
    elif (Otype == "Put"):
        price  = -S0*Decimal(ss.norm.cdf(np.float(-d1)))\
        + K*Decimal(m.exp(-r*T))*Decimal(ss.norm.cdf(np.float(-d2)))
        return (price)

def vega (S0,K,r,T,sig):
    d1 = Decimal(m.log(S0/K))/(sig*Decimal(m.sqrt(T))) + Decimal((r+ (sig*sig)/2)*T/(sig*Decimal(m.sqrt(T))))
    vega = S0*Decimal(ss.norm.pdf(np.float(d1)))*Decimal(m.sqrt(T))
    return(vega)

def imp_vol(S0, K, T, r, market,flag):
    e = 10e-15; x0 = Decimal(1);
    def newtons_method(S0, K, T, r, market,flag,x0, e):
        delta = call_bsm (S0,K,r,T,flag,x0) - market
        while delta > e:
            x0 = Decimal(x0 - (call_bsm (S0,K,r,T,flag,x0) - market)/vega (S0,K,r,T,x0))
            delta = abs(call_bsm (S0,K,r,T,flag,x0) - market)
        return(Decimal(x0))
    sig =  newtons_method(S0, K, T, r, market,flag,x0 , e)
    return(sig*100)

fromdate = date(2018,3,30)
todate = date(2018,9,21)
daygenerator = (fromdate + timedelta(x + 1) for x in range((todate - fromdate).days))
days = sum(1 for day in daygenerator if day.weekday())
T = Decimal(days)/Decimal(360)
S0 = Decimal(109.97)
K = Decimal(100)
market = Decimal(13.725)
flag = 'Call'
r = Decimal(0.0225)
iv = imp_vol(S0, K, T, r, market,flag)
print("")
print("")
print("############################ Newton Raphson Method ####################")
print("")
print('The implied volatility from my calculation is',iv,"%")
cp_iv = call_bsm(S0,K,r,T,Otype="Call",sig=Decimal(iv/100))
print('Newton Raphson implied volatility gives call price',cp_iv)
print("Error in obtained call price", '{:.20f}'.format(np.float(market - cp_iv)))
# Mibian Library
c = BS([S0, K, r, days], callPrice=market)
print("")
print("")
print("############################ Bisection Method #########################")
print("")
print("The implied volatility using Mibian Library is", c.impliedVolatility,"%")
cp_mbian = call_bsm(S0,K,r,T,Otype="Call",sig=Decimal(c.impliedVolatility/100))
print('Mibian implied volatility gives call price',cp_mbian)
print("Error in obtained call price", market - cp_mbian)


import numpy as np

def evaluate(y_pred: np.array, y_true: np.array, precision=4):
    print('MSE:', round(MSE(y_pred, y_true), precision))
    print('RMSE:', round(RMSE(y_pred, y_true), precision))
    print('MAE:', round(MAE(y_pred, y_true), precision))
    print('MAPE:', round(MAPE(y_pred, y_true)*100, precision), '%')
    print('PCC:', round(PCC(y_pred, y_true), precision))
    return MSE(y_pred, y_true), RMSE(y_pred, y_true), MAE(y_pred, y_true), MAPE(y_pred, y_true)

def MSE(y_pred: np.array, y_true: np.array):
    return np.mean(np.square(y_pred - y_true))

def RMSE(y_pred:np.array, y_true:np.array):
    return np.sqrt(MSE(y_pred, y_true))

def MAE(y_pred:np.array, y_true:np.array):
    return np.mean(np.abs(y_pred - y_true))

def MAPE(y_pred:np.array, y_true:np.array, epsilon=1e-0):       # avoid zero division
    return np.mean(np.abs(y_pred - y_true) / (y_true + epsilon))

def PCC(y_pred:np.array, y_true:np.array):      # Pearson Correlation Coefficient
    return np.corrcoef(y_pred.flatten(), y_true.flatten())[0,1]


import numpy as np

def relaxation(A, b, omega, eps, maxIterations):
  size = len(A)
  x = np.zeros(size)

  for k in range(maxIterations):
    xPrev = np.copy(x)
    for j in range(size):
      d = b[j]

      for i in range(size):
        if j != i:
          d -= A[j][i] * x[i]
      x[j] = (1 - omega) * x[j] + (omega / A[j][j]) * d

    print(f"{k}-th iteration:")
    print(x)
    if np.linalg.norm(np.dot(A, x) - b) <= eps * np.linalg.norm(b):
      print("^^^^^^^^^^^^^^")
      print(">> Малость невязки << Solution found")
      return
    if np.linalg.norm(x - xPrev) <= eps * np.linalg.norm(xPrev):
      print("^^^^^^^^^^^^^^")
      print(">> Малость нормы приближений << Solution found")
      return

  print("Number of iterations exceeded")


import pandas as pd
import numpy as np
import tsplib95

def distance(a, b):
    """Euclidean distance of two points.

    Args:
        a (numpy.array or pd.Series)
        b (numpy.array or pd.Series)

    Returns:
        [float]
    """
    return np.sqrt(np.sum(np.power((a-b), 2)))

def get_dist_matrix(path):
    """Given a csv of rows each containing
    index, x, y coordinates, return the distance matrix,
    for the group of points.
    """
    df = pd.read_csv(path,
                     sep=' ',
                     names=['x', 'y'])

    full_matrix = []
    for i in df.index:
        vec_distance = []
        for j in df.index:
            vec_distance.append(distance(df.iloc[i,].to_numpy(),
                                    df.iloc[j,].to_numpy()))
        vec_distance = np.array(vec_distance)
        full_matrix.append(vec_distance)
    full_matrix = np.array(full_matrix)
    return full_matrix

def tsplib_distance_matrix(tsplib_file: str) -> np.ndarray:
    """Distance matrix from a TSPLIB file
    Parameters
    ----------
    tsplib_file
        A string with the complete path of the TSPLIB file (or just its name if
        it is the in current path)
    Returns
    -------
    distance_matrix
        A ND-array with the equivalent distance matrix of the input file
    Notes
    -----
    This function can handle any file supported by the `tsplib95` lib.
    """

    tsp_problem = tsplib95.load(tsplib_file)
    distance_matrix_flattened = np.array([
        tsp_problem.get_weight(*edge) for edge in tsp_problem.get_edges()
    ])
    distance_matrix = np.reshape(
        distance_matrix_flattened,
        (tsp_problem.dimension, tsp_problem.dimension),
    )

    # Some problems with EXPLICIT matrix have a large number in the distance
    # from a node to itself, which makes no sense for our problems. Thus,
    # always ensure a diagonal filled with zeros
    np.fill_diagonal(distance_matrix, 0)
    return distance_matrix


def SWHeart(u, v, alpha):
# SWHEART Calculate the heart of the Wilson function.
# SWHeart(u, v, alpha) calculates the matrix H (Heart of the Wilson
# function) for maturities specified by vectors u and v. The formula is
# taken from the EIOPA technical specifications paragraph 132.
#
# Arguments:
#    u =     n_1 x 1 vector of maturities. Ex. u = [1; 3]
#    v =     n_2 x 1 vector of maturities. Ex. v = [1; 2; 3; 5]
#    alpha = 1 x 1 floating number representing the convergence speed parameter alpha. Ex. alpha = 0.05
#
# Returns:
#    n_1 x n_2 matrix representing the Heart of the Wilson function for selected maturities and parameter alpha. H is calculated as in the paragraph 132 of the EIOPA documentation.
#
# For more information see https://www.eiopa.europa.eu/sites/default/files/risk_free_interest_rate/12092019-technical_documentation.pdf
    import numpy as np

    u_Mat = np.tile(u, [v.size, 1]).transpose()
    v_Mat = np.tile(v, [u.size, 1])
    return 0.5 * (alpha * (u_Mat + v_Mat) + np.exp(-alpha * (u_Mat + v_Mat)) - alpha * np.absolute(u_Mat-v_Mat) - np.exp(-alpha * np.absolute(u_Mat-v_Mat))); # Heart of the Wilson function from paragraph 132


# RESPUESTA 2
import numpy as np
from scipy import stats
from math import log

import plotly.plotly as py
import plotly.graph_objs as go

from funciones import gcl_uniforme
import constante

# Paso 1: Generamos muestras de la variable uniforme U
x_n = constante.SEMILLA
u = []  # array de uniformes
x = []  # array de inversas

for _ in range(constante.CANT_EXPERIMENTOS):
    x_n = gcl_uniforme(x_n)
    u.append(x_n)

#  Paso 2: Aplicar la transformacion inversa
paramLambda = float(1)/float(15)
for i in range(len(u)):
    x.append(-log(1-u[i])/paramLambda)  # Transformacion inversa

# Mostramos histograma del resultado
data = [go.Histogram(x=x)]
py.plot(data, filename='histograma-inversa-exponencial')

# Mostramos media, varianza y moda muestrales y teoricos
media = np.mean(x)
varianza = np.var(x)
moda = max(set(x), key=x.count)

print("Media muestral: {0} Varianza muestral: {1} Moda muestral: {2}".format(media, varianza, moda))
print("Media teorica:  {0} Varianza teorica:  {1} Moda teorica:  {2}".format(15, 15*15, 0))


''' principal_component_analysis.py
Principal Component Analysis reduces the dimensionality of the feature set by
sorting the features by the explained variance.  It does this by
1) computing a covariance matrix for the features
2) finding the eigenvectors and eigenvalues of the matrix, principal components.
3) computing explained variance for the components and sorting them

Always standardize because PCA is sensitive to scaling

Created on Jul 2, 2016

from Python Machine Learning by Sebastian Raschka under the following license

The MIT License (MIT)

Copyright (c) 2015, 2016 SEBASTIAN RASCHKA (mail@sebastianraschka.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@author: richard lyman
'''
n_components = 10 # of of pca components to use for final accuracy

import numpy as np
import ocr_utils
import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA
from sklearn.decomposition import KernelPCA

chars_to_train = range(48,58)
columnsXY=range(0,20)
column_str = 'column_sum{}'.format(list(columnsXY))

input_filters_dict = {'m_label': chars_to_train, 'font': 'E13B'}

# output  the character label and the image and column sums
output_feature_list = ['m_label','image',column_str]

# read the complete image (20x20) = 400 pixels for each character
ds = ocr_utils.read_data(input_filters_dict=input_filters_dict,
                            output_feature_list=output_feature_list,
                            test_size=.2,
                            random_state=0)
windows_limit = 5000 # uses too much memory for my 32 bit windows computer so limit size of sample
y_train = ds.train.features[0][:windows_limit]
X_train_image = ds.train.features[1][:windows_limit]
X_train = ds.train.features[2][:windows_limit]

y_test = ds.test.features[0]
X_test_image = ds.test.features[1]
X_test = ds.test.features[2]

cov_mat = np.cov(X_train_image.T)
eigen_vals, eigen_vecs = np.linalg.eig(cov_mat)

print('\nEigenvalues \n%s' % eigen_vals[:2*n_components])

tot = sum(eigen_vals)
var_exp = [(i / tot) for i in sorted(eigen_vals, reverse=True)]
cum_var_exp = np.cumsum(var_exp)
var_exp = var_exp[:20]
cum_var_exp = cum_var_exp[:2*n_components]
title='explained variance'
plt.bar(range(1, len(var_exp)+1), var_exp, alpha=0.5, align='center',   label='individual explained variance')
plt.step(range(1, len(cum_var_exp)+1), cum_var_exp, where='mid',
         label='cumulative explained variance')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal components')
plt.legend(loc='best')
plt.tight_layout()
plt.title(title)
ocr_utils.show_figures(plt,title)

# Make a list of (eigenvalue, eigenvector) tuples
eigen_pairs = [(np.abs(eigen_vals[i]), eigen_vecs[:,i]) for i in range(len(eigen_vals))]

# Sort the (eigenvalue, eigenvector) tuples from high to low
eigen_pairs.sort(reverse=True)

# The eigenpairs with the highest explained variance
w = np.hstack((eigen_pairs[0][1][:, np.newaxis],
               eigen_pairs[1][1][:, np.newaxis]))
print('Matrix W:\n', w[:2*n_components,:])

X_train_pca = X_train_image.dot(w)
print ('projection of first dataset sample on first 2 eignvectors {}'.format(X_train_image[0].dot(w)))

markers = ('o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan','orange','green','brown','lightblue','pink')

for l, c, m in zip(np.unique(y_train), colors, markers):
    plt.scatter(X_train_pca[y_train==l, 0],
                X_train_pca[y_train==l, 1],
                c=c, label=l, marker=m)

plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
title='features mapped to two principal components'
plt.title(title)
ocr_utils.show_figures(plt,title)

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

pca = PCA(n_components=2)

X_train_pca = pca.fit_transform(X_train_image)
X_test_pca = pca.transform(X_test_image)

lr = LogisticRegression(solver='liblinear',multi_class='auto')
logistic_fitted =lr.fit(X_train_pca, y_train)

print('\nPCA Train Accuracy: {:4.6f}, n_components={}'.format(accuracy_score(y_train, logistic_fitted.predict(X_train_pca)),pca.n_components))
print('PCA Test Accuracy: {:4.6f}, n_components={}'.format(accuracy_score(y_test, logistic_fitted.predict(X_test_pca)),pca.n_components))

title = 'train pc1 versus pc2'
ocr_utils.plot_decision_regions(X=X_train_pca, y=y_train, classifier=lr, labels=['pc1','pc2'], title=title)

title = 'test pc1 versus pc2'
ocr_utils.plot_decision_regions(X=X_test_pca, y=y_test, classifier=lr, labels=['pc1','pc2'], title=title)
X_train_pca = pca.fit_transform(X_train_image)
X_test_pca = pca.transform(X_test_image)

########################################################################################
pca = PCA(n_components=n_components)
X_train_pca = pca.fit_transform(X_train_image)
X_test_pca = pca.transform(X_test_image)

lr = LogisticRegression(solver='liblinear',multi_class='auto')
logistic_fitted = lr.fit(X_train_pca, y_train)

y_train_pred = logistic_fitted.predict(X_train_pca)
y_test_pred = logistic_fitted.predict(X_test_pca)

print('\nPCA Train Accuracy: {:4.6f}, n_components={}'.format(accuracy_score(y_train, y_train_pred),pca.n_components))
print('PCA Test Accuracy: {:4.6f}, n_components={}'.format(accuracy_score(y_test, y_test_pred),pca.n_components))

X_errors_image = X_test_image[y_test!=y_test_pred]
y_errors = y_test[y_test!=y_test_pred]
X_errors_pca = X_test_pca[y_test!=y_test_pred]

X_orig = X_train_image[:500]
title = 'originals'
X2D=np.reshape(X_orig, (X_orig.shape[0], ds.train.num_rows, ds.train.num_columns))
ocr_utils.montage(X2D,title=title)

X_orig = X_train_pca[:500]
title = 'inverse original'
X_inverse = pca.inverse_transform(X_orig)
X2D = np.reshape(X_inverse, (X_inverse.shape[0], ds.train.num_rows, ds.train.num_columns))
X2D = X2D - np.min(X2D)
ocr_utils.montage(X2D,title=title)

# change to a 2D shape
X_errors2D=np.reshape(X_errors_image, (X_errors_image.shape[0], ds.train.num_rows, ds.train.num_columns))
ocr_utils.montage(X_errors2D,title='PCA Error Characters, components={}'.format (n_components))

title = 'inverse transform errors'
X_inverse = pca.inverse_transform(X_errors_pca)
X2D=np.reshape(X_inverse, (X_inverse.shape[0], ds.train.num_rows, ds.train.num_columns))
X2D = X2D - np.min(X2D)
ocr_utils.montage(X2D,title=title)

########################################################################################
kernel='rbf'  # really slow
pca = KernelPCA(n_components=2,kernel=kernel, gamma=15)

X_train_pca = pca.fit_transform(X_train_image)
X_test_pca = pca.transform(X_test_image)

lr = LogisticRegression(solver='liblinear',multi_class='auto')
logistic_fitted=lr.fit(X_train_pca, y_train)
y_train_pred = logistic_fitted.predict(X_train_pca)
y_test_pred = logistic_fitted.predict(X_test_pca)

print('\nKernel PCA Train Accuracy: {:4.6f}, n_components={}, kernel={}'.format(accuracy_score(y_train, y_train_pred), pca.n_components,kernel))
print('Kernel PCA Test Accuracy: {:4.6f}, n_components={}, kernel={}'.format(accuracy_score(y_test, y_test_pred),pca.n_components,kernel))

title = 'train kernel {} pc1 versus pc2'.format(kernel)
ocr_utils.plot_decision_regions(X=X_train_pca, y=y_train, classifier=lr, labels=['pc1','pc2'], title=title)

title = 'test kernel {} pc1 versus pc2'.format(kernel)
ocr_utils.plot_decision_regions(X=X_test_pca, y=y_test, classifier=lr, labels=['pc1','pc2'], title=title)

########################################################################################
# too slow on my computer

# pca = KernelPCA(n_components=n_components,kernel=kernel, gamma = 15)
#
# X_train_pca = pca.fit_transform(X_train_image)
# X_test_pca = pca.transform(X_test_image)
#
# print ('n_components={}'.format(pca.n_components))
#
# lr = LogisticRegression()
# logistic_fitted = lr.fit(X_train_pca, y_train)
#
# y_pred = logistic_fitted.predict(X_test_pca)
# print('\nKKernelPCA Train Accuracy: {:4.6f}, n_components={}, kernel={}'.format(accuracy_score(y_train, logistic_fitted.predict(X_train_pca)), pca.n_components, kernel))
# print('KernelPCA Test Accuracy: {:4.6f}, n_components={}, kernel={}'.format(accuracy_score(y_test, y_pred), pca.n_components, kernel))
#
# X_errors_image = X_test_image[y_test!=y_pred]
# y_errors = y_test[y_test!=y_pred]
#
# error_images = X_errors_image.shape[0]
#
# # change to a 2D shape
# X_errors2D=np.reshape(X_errors_image, (error_images, ds.train.num_rows, ds.train.num_columns))
# ocr_utils.montage(X_errors2D,title='Kernel {} KernelPCA Errors Character,components={}s'.format(kernel,n_components))

print ('\n########################### No Errors ####################################')


from sympy import *
from tait_bryan_R_utils import *
from rodrigues_R_utils import *
from quaternion_R_utils import *
from plucker_line_utils import *

fx,fy,cx,cy = symbols('fx fy cx cy');
px, py, pz = symbols('px py pz')
om, fi, ka = symbols('om fi ka')
#sx, sy, sz = symbols('sx sy sz')
#q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
mx_w, my_w, mz_w, lx_w, ly_w, lz_w = symbols('mx_w my_w mz_w lx_w ly_w lz_w')
u_s,v_s = symbols('u_s v_s')
u_e,v_e = symbols('u_e v_e')

position_symbols = [px, py, pz]
orientation_symbols = [om, fi, ka]
#orientation_symbols = [sx, sy, sz]
#orientation_symbols = [q0, q1, q2, q3]
plucker_line_symbols = [mx_w, my_w, mz_w]
all_symbols = position_symbols + orientation_symbols + plucker_line_symbols

RT_wc = matrix44FromTaitBryan(px, py, pz, om, fi, ka)
#RT_wc = matrix44FromRodrigues(px, py, pz, sx, sy, sz)
#RT_wc = matrix44FromQuaternion(px, py, pz, q0, q1, q2, q3)
K=plucker_line_K(fx, fy, cx, cy)
mm_cw = plucker_line_motion_matrix_cw(RT_wc)
l_w = Matrix([[mx_w],[my_w],[mz_w],[lx_w],[ly_w],[lz_w]])
l_c = mm_cw*l_w
m_c=Matrix([[l_c[0,0]],[l_c[1,0]],[l_c[2,0]]])
l=K*m_c

x_s=Matrix([[u_s,v_s,1]])
x_e=Matrix([[u_e,v_e,1]])

target_value = Matrix([0,0]).vec()
model_function = Matrix([(x_s*l)/sqrt(l[0,0]*l[0,0] + l[1,0]*l[1,0]),(x_e*l)/sqrt(l[0,0]*l[0,0] + l[1,0]*l[1,0])]).vec()
obs_eq = target_value - model_function
obs_eq_jacobian = obs_eq.jacobian(all_symbols)

print(obs_eq)
print(obs_eq_jacobian)

with open("perspective_camera_plucker_line_tait_bryan_wc_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void observation_equation_perspective_camera_plucker_line_tait_bryan_wc(Eigen::Matrix<double, 2, 1> &delta, double fx, double fy, double cx, double cy, double px, double py, double pz, double om, double fi, double ka, double mx_w, double my_w, double mz_w, double lx_w, double ly_w, double lz_w, double u_s, double v_s, double u_e, double v_e)\n")
    f_cpp.write("{")
    f_cpp.write("delta.coeffRef(0,0) = %s;\n"%(ccode(obs_eq[0,0])))
    f_cpp.write("delta.coeffRef(1,0) = %s;\n"%(ccode(obs_eq[1,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void observation_equation_perspective_camera_plucker_line_tait_bryan_wc_jacobian(Eigen::Matrix<double, 2, 6, Eigen::RowMajor> &j, double fx, double fy, double cx, double cy, double px, double py, double pz, double om, double fi, double ka, double mx_w, double my_w, double mz_w, double lx_w, double ly_w, double lz_w, double u_s, double v_s, double u_e, double v_e)\n")
    f_cpp.write("{")
    for i in range (2):
        for j in range (6):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(obs_eq_jacobian[i,j])))
    f_cpp.write("}")


from __future__ import division
from past.utils import old_div
import numpy as np

def angular_distance_fast(ra1, dec1, ra2, dec2):
    """
    Compute angular distance using the Haversine formula. Use this one when you know you will never ask for points at
    their antipodes. If this is not the case, use the angular_distance function which is slower, but works also for
    antipodes.

    :param lon1:
    :param lat1:
    :param lon2:
    :param lat2:
    :return:
    """

    lon1 = np.deg2rad(ra1)
    lat1 = np.deg2rad(dec1)
    lon2 = np.deg2rad(ra2)
    lat2 = np.deg2rad(dec2)
    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = np.sin(old_div(dlat,2.0))**2 + np.cos(lat1) * np.cos(lat2) * np.sin(old_div(dlon,2.0))**2
    c = 2 * np.arcsin(np.sqrt(a))
    return np.rad2deg(c)

def angular_distance(ra1, dec1, ra2, dec2):
    """
    Returns the angular distance between two points, two sets of points, or a set of points and one point.

    :param ra1: array or float, longitude of first point(s)
    :param dec1: array or float, latitude of first point(s)
    :param ra2: array or float, longitude of second point(s)
    :param dec2: array or float, latitude of second point(s)
    :return: angular distance(s) in degrees
    """

    # Vincenty formula, slower than the Haversine formula in some cases, but stable also at antipodes

    lon1 = np.deg2rad(ra1)
    lat1 = np.deg2rad(dec1)
    lon2 = np.deg2rad(ra2)
    lat2 = np.deg2rad(dec2)

    sdlon = np.sin(lon2 - lon1)
    cdlon = np.cos(lon2 - lon1)
    slat1 = np.sin(lat1)
    slat2 = np.sin(lat2)
    clat1 = np.cos(lat1)
    clat2 = np.cos(lat2)

    num1 = clat2 * sdlon
    num2 = clat1 * slat2 - slat1 * clat2 * cdlon
    denominator = slat1 * slat2 + clat1 * clat2 * cdlon

    return np.rad2deg(np.arctan2(np.sqrt(num1 ** 2 + num2 ** 2), denominator))

def spherical_angle( ra0, dec0, ra1, dec1, ra2, dec2 ):
    """
    Returns the spherical angle distance between two sets of great circles defined by (ra0, dec0), (ra1, dec1) and (ra0, dec0), (ra2, dec2)

    :param ra0: array or float, longitude of intersection point(s)
    :param dec0: array or float, latitude of intersection point(s)
    :param ra1: array or float, longitude of first point(s)
    :param dec1: array or float, latitude of first point(s)
    :param ra2: array or float, longitude of second point(s)
    :param dec2: array or float, latitude of second point(s)
    :return: spherical angle in degrees
    """

    a = np.deg2rad( angular_distance(ra0, dec0, ra1, dec1))
    b = np.deg2rad( angular_distance(ra0, dec0, ra2, dec2))
    c = np.deg2rad( angular_distance(ra2, dec2, ra1, dec1))

    #use the spherical law of cosines: https://en.wikipedia.org/wiki/Spherical_law_of_cosines#Rearrangements

    numerator = np.atleast_1d( np.cos(c) - np.cos(a) * np.cos(b) )
    denominator = np.atleast_1d( np.sin(a)*np.sin(b) )

    return np.where( denominator == 0 , np.zeros( len(denominator)), np.rad2deg( np.arccos( old_div(numerator,denominator))) )


"""
Encrypt and decrypt using Hill cipher for English alphabet
"""
import sys
import argparse
import numpy as np
from numpy import matrix

ap = argparse.ArgumentParser()
ap.add_argument("-k", "--k", help="enter a key of length 4 or 9")
ap.add_argument("-e", "--e", help="enter string to be encrypted")
ap.add_argument("-d", "--d", help="enter string to be decrypted")

if len(sys.argv) > 1:
    args = ap.parse_args()
    print(args)
    k = args.k.lower()
    lk = len(k)
    s = int(lk**0.5)
    if not (lk == 4 or lk == 9):
        print('k needs to be a key of length 4 or 9')
        quit()
    if args.e:
        istr = args.e.lower()
        ci = istr.replace(' ', '') # concatenated input string
        if len(ci) % s != 0:
            ci += 'z'*(s - (len(ci)%s)) # Pad with z
        km = matrix(np.array([ord(x) - ord('a') for x in k]).reshape(s, s))
        print('Key matrix: ', km.getA1())
        o = ''
        for i in range(len(ci)//s):
            m = matrix(np.array([ord(x) - ord('a') for x in ci[i*s:i*s+s]]).reshape(s,1))
            o += ''.join([chr(x + ord('a')) for x in ((km*m)%26).getA1()])
        x = 0
        for i in istr.split():
            print(o[x:x+len(i)].upper(), end=' ')
            x += len(i)
        print('')
    elif args.d:
        print('Decrypt')
    else:
        print('Enter a string to encrypt or decrypt')
        ap.print_help()
else:
    ap.print_help()


"""
Linear Regression with TensorFlow
sungjoon.choi@cpslab.snu.ac.kr
"""
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

# Generate training data
np.random.seed(1)
def f(x,a,b):
    n    = train_X.size
    vals = np.zeros((1, n))
    for i in range(0, n):
        ax  = np.multiply(a, x.item(i))
        val = np.add(ax, b)
        vals[0, i] = val
    return vals

Wref = 0.7
bref = -1.
n    = 20
noise_var = 0.001
train_X   = np.random.random((1, n))
ref_Y     = f(train_X, Wref, bref)
train_Y   = ref_Y + np.sqrt(noise_var)*np.random.randn(1, n)
n_samples = train_X.size # <= Just for using size operator
print ("")
print (" Type of 'train_X' is ", type(train_X))
print (" Shape of 'train_X' is %s" % (train_X.shape,))
print (" Type of 'train_Y' is ", type(train_Y))
print (" Shape of 'train_Y' is %s" % (train_Y.shape,))

# Plot
# plt.figure(1)
# plt.plot(train_X[0, :], ref_Y[0, :], 'ro', label='Original data')
# plt.plot(train_X[0, :], train_Y[0, :], 'bo', label='Training data')
# plt.axis('equal')
# plt.legend(loc='lower right')

# Prepare for Linear Regression

# Parameters
training_epochs = 2000
display_step    = 50

# Set TensorFlow Graph
X = tf.placeholder(tf.float32, name="input")
Y = tf.placeholder(tf.float32, name="output")
W = tf.Variable(np.random.randn(), name="weight")
b = tf.Variable(np.random.randn(), name="bias")

# Construct a Model
activation = tf.add(tf.multiply(X, W), b)

# Define Error Measure and Optimizer
learning_rate   = 0.01
cost = tf.reduce_mean(tf.pow(activation-Y, 2))
# learning_rate   = 0.001
# cost = tf.sqrt(tf.reduce_sum(tf.pow(activation-Y, 2)))
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost) #Gradient descent

"""
 tf.reduce_sum()
 tf.reduce_mean()
 _____

 tf.pow(Yhat, Y, 2)
 tf.nn.softmax_cross_entropy_with_logits(Yhat, Y)
 _____

 tf.train.GradientDescentOptimizer(0.05).minimize(cost)
 tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
"""

# Initializer
init = tf.global_variables_initializer()

# Run!
sess = tf.Session()

# Initialize
sess.run(init)
for epoch in range(training_epochs):
    for (x, y) in zip(train_X[0, :], train_Y[0, :]):
        # print "x: ", x, " y: ", y
        sess.run(optimizer, feed_dict={X:x, Y:y})

    # Check cost
    if epoch % display_step == 0:
        costval = sess.run(cost, feed_dict={X: train_X, Y:train_Y})
        print ("Epoch:", "%04d"%(epoch+1), "cost=", "{:.5f}".format(costval))
        Wtemp = sess.run(W)
        btemp = sess.run(b)
        print (" Wtemp is", "{:.4f}".format(Wtemp), "btemp is", "{:.4f}".format(btemp))
        print (" Wref is", "{:.4f}".format(Wref), "bref is", "{:.4f}".format(bref))

# Final W and b
Wopt = sess.run(W)
bopt = sess.run(b)
fopt = f(train_X, Wopt, bopt)

# Plot Results
plt.figure(2)
plt.plot(train_X[0, :], ref_Y[0, :], 'ro', label='Original data')
plt.plot(train_X[0, :], train_Y[0, :], 'bo', label='Training data')
plt.plot(train_X[0, :], fopt[0, :], 'k-', label='Fitted Line')
plt.axis('equal')
plt.legend(loc='lower right')
plt.show()


__author__ = 'jhaux'

import cv2
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
import os

import image_operations as imop
import ltm_analysis as ltm
import jimlib as jim

from numpy import linspace , arange , reshape ,zeros
from scipy.fftpack import fft2 , fftfreq
from cmath import pi

# create some arbitrary data
some_data = arange(0.0 , 16384.0 , dtype = complex)

# reshape it to be a 128x128 2d grid
some_data_grid = reshape(some_data , (128 , 128) )

# assign some real spatial co-ordinates to the grid points
# first define the edge values
x_min = -250.0
x_max = 250.0
y_min = -250.0
y_max = 250

# then create some empty 2d arrays to hold the individual cell values
x_array = zeros( (128,128) , dtype = float )
y_array = zeros( (128,128) , dtype = float )

# now fill the arrays with the associated values
for row , y_value in enumerate(linspace (y_min , y_max , num = 128) ):

  for column , x_value in enumerate(linspace (x_min , x_max , num = 128) ):

    x_array[row][column] = x_value
    y_array[row][column] = y_value

# now for any row,column pair the x_array and y_array hold the spatial domain
# co-ordinates of the associated point in some_data_grid

# now use the fft to transform the data to the wavenumber domain
some_data_wavedomain = fft2(some_data_grid)

# now we can use fftfreq to give us a base for the wavenumber co-ords
# this returns [0.0 , 1.0 , 2.0 , ... , 62.0 , 63.0 , -64.0 , -63.0 , ... , -2.0 , -1.0 ]
n_value = fftfreq( 128 , (1.0 / 128.0 ) )

# now we can initialize some arrays to hold the wavenumber co-ordinates of each cell
kx_array = zeros( (128,128) , dtype = float )
ky_array = zeros( (128,128) , dtype = float )

# before we can calculate the wavenumbers we need to know the total length of the spatial
# domain data in x and y. This assumes that the spatial domain units are metres and
# will result in wavenumber domain units of radians / metre.
x_length = x_max - x_min
y_length = y_max - y_min

# now the loops to calculate the wavenumbers
for row in xrange(128):

  for column in xrange(128):

    kx_array[row][column] = ( 2.0 * pi * n_value[column] ) / x_length
    ky_array[row][column] = ( 2.0 * pi * n_value[row] ) / y_length

# now for any row,column pair kx_array , and ky_array will hold the wavedomain coordinates
# of the correspoing point in some_data_wavedomain


"""
Benchmark Laplace equation solving.

From the Numpy benchmark suite, original code at
https://github.com/yarikoptic/numpy-vbench/commit/a192bfd43043d413cc5d27526a9b28ad343b2499
"""

import numpy as np

dx = 0.1
dy = 0.1
dx2 = (dx * dx)
dy2 = (dy * dy)

def setup():
    from numba import jit

    global laplace

    @jit(nopython=True)
    def laplace(N, Niter):
        u = np.zeros((N, N))
        u[0] = 1
        for i in range(Niter):
            u[1:(-1), 1:(-1)] = ((((u[2:, 1:(-1)] + u[:(-2), 1:(-1)]) * dy2) +
                                ((u[1:(-1), 2:] + u[1:(-1), :(-2)]) * dx2))
                                / (2 * (dx2 + dy2)))
        return u

class Laplace:
    N = 150
    Niter = 200

    def setup(self):
        # Warm up
        self.run_laplace(10, 10)

    def run_laplace(self, N, Niter):
        u = laplace(N, Niter)

    def time_laplace(self):
        self.run_laplace(self.N, self.Niter)


import numpy as np

def get_coeffs(order: int):
    # Coefficients of order r=2
    # On smooth solutions this should converge with order r=3
    Cs = []
    As = []
    Sigs = []

    Cs.append(np.array([1, 2]) / 3)
    As.append(
        np.array(
            [
                [3, -1],
                [1, 1],
            ]
        )
        / 2
    )
    Sigs.append(np.array([[[1, 0], [-2, 1]], [[1, 0], [-2, 1]]], dtype=np.float32))

    # Coefficients of order r=3
    # On smooth solutions this should converge with order r=5
    Cs.append(np.array([1, 6, 3]) / 10)
    As.append(
        np.array(
            [
                [11, -7, 2],
                [2, 5, -1],
                [-1, 5, 2],
            ]
        )
        / 6
    )
    const = 3.0
    s3 = (
        np.array(
            [
                [[10, 0, 0], [-31, 25, 0], [11, -19, 4]],
                [[4, 0, 0], [-13, 13, 0], [5, -13, 4]],
                [[4, 0, 0], [-19, 25, 0], [11, -31, 10]],
            ],
            dtype=np.float32,
        )
        / 3.0
    )
    s3 = s3.astype(np.float32)
    Sigs.append(s3)

    C = Cs[order - 2]
    a = As[order - 2]
    sigma = Sigs[order - 2]

    return C, a, sigma

def reconstruct(order, q):
    """
    Do WENO reconstruction

    Parameters
    ----------

    order : int
        The stencil width
    q : numpy array
        Scalar data to reconstruct

    Returns
    -------

    qL : numpy array
        Reconstructed data - boundary points are zero
    """

    C, a, sigma = get_coeffs(order)

    qL = np.zeros_like(q)
    beta = np.zeros((order, len(q)))
    w = np.zeros_like(order)
    npt = len(q) - 2 * order
    epsilon = 1e-16
    for i in range(order, npt + order):
        q_stencils = np.zeros(order)
        alpha = np.zeros(order)
        for k in range(order):
            for l in range(order):
                for m in range(l + 1):
                    beta[k, i] += sigma[k, l, m] * q[i + k - l] * q[i + k - m]
            alpha[k] = C[k] / (epsilon + beta[k, i] ** 2)
            for l in range(order):
                q_stencils[k] += a[k, l] * q[i + k - l]
        w = alpha / np.sum(alpha)
        qL[i] = np.dot(w, q_stencils)

    return qL


import scipy.linalg as linalg
import numpy as np
import contextlib

A = np.zeros((10,10))
for i in range(10):
    for j in range(10):
        if (i==j) :
            A[i][j] = 1
        if (abs(i-j) == 1):
            A[i][j] = 0.2

inv_A = np.linalg.inv(A)
print("inv_A=")
np.set_printoptions(formatter={'float': '{: 0.3f}'.format})
print(inv_A)

P,L,U = linalg.lu(A)
print("L=")
print(L)
print("U=")
print(U)


import sklearn
import numpy as np
from AbstractDataset import AbstractDataset
from sklearn.datasets import make_swiss_roll

# shape is defined by args in get
# default is (1000,3)

class SwissRollDataset(AbstractDataset):

	def __init__(self):
		super().__init__()

	def get(self,n_samples = 1000, noise = 0.1, **kwargs) -> np.ndarray:
		try:
			X, y = make_swiss_roll(n_samples = n_samples, noise = noise)
			return X,y
		except:
			RuntimeError

	@property
	def is_sparse(self) -> bool:
		return False


# -*- coding: utf-8 -*-
"""
Created on Wed Apr  8 15:05:29 2020

@author: Wallace

Examples based on https://realpython.com/linear-regression-in-python/
"""

# Step 1: Import packages
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
import matplotlib.pyplot as plt

# Step 2a: Provide data
x = [[0, 1], [5, 1], [15, 2], [25, 5], [35, 11], [45, 15], [55, 34], [60, 35]]
y = [4, 5, 20, 14, 32, 22, 38, 43]
x, y = np.array(x), np.array(y)

# Step 2b: Transform input data
x_ = PolynomialFeatures(degree=2, include_bias=False).fit_transform(x)

# Step 3: Create a model and fit it
model = LinearRegression().fit(x_, y)

# Step 4: Get results
r_sq = model.score(x_, y)
intercept, coefficients = model.intercept_, model.coef_

# Step 5: Predict
y_pred = model.predict(x_)

print('coefficient of determination:', r_sq)
print('intercept:', intercept)
print('coefficients:', coefficients, sep='\n')
print('predicted response:', y_pred, sep='\n')

#
# Step 6: Plot data
#

fig, ax = plt.subplots()
ax.plot(y, y_pred, linewidth=0, marker='s', label = "Predicted")
ax.plot(y, y, label=' Exact, y_pred = y')
ax.set_xlabel('Actual Response, y')
ax.set_ylabel('Predicted Response, y_pred')
ax.legend(facecolor='white')
plt.show()


import numpy as np

def wrapToPi(angle):
    return (angle + np.pi) % (2*np.pi) - np.pi

def rotmat2d(angle):
    return np.array([[np.cos(angle), -np.sin(angle)],
                     [np.sin(angle),  np.cos(angle)]])


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

# Simple Amortization Table
def amoritization(loan, APR, payment, referenceDate=None):
    '''
    Calculates an amoritization shedule assuming monthly payments.
    Returns Pandas DataFrame of schdule.

    Parameters
    ----------
    loan: float
        Amount of loan
    APR: float
        APR in decimal
    payment: float
        the monthly payment
    referenceDate: string
        reference date for DateTime Index
    Returns
    -------
    amoritizationTable: DataFrame
        DataFrame with DateTime Index
    '''
    # Initialize values
    period = 0
    balance = loan
    # Initialize arrays to append results to
    periods= np.array(period)
    interestPaid = np.zeros(1)
    principalPaid= np.zeros(1)
    principal= loan
    # Rate per month
    rper = APR/12
    # Iterate while the loan is not paid off
    while balance > 0:
        # Increace Period
        period += 1
        # Calculate interest in the period
        intamt = rper * balance
        # Calculate payment towards principal
        paidamt = payment - intamt
        # Reduce the balance
        balance = balance - paidamt
        # Append periods, interest, payment, & balance to arrays
        periods       = np.append(periods      , period)
        interestPaid  = np.append(interestPaid , intamt)
        principalPaid = np.append(principalPaid, paidamt)
        principal     = np.append(principal    , balance)
        # Check if the balance is less than the payment
        if balance  < payment:
            payment = balance + (rper*balance)
    # Create a DateTime of the results
    time = pd.date_range(start=pd.to_datetime('today').date(),
                         periods=period+1, freq='M')
    # Dictionary of results
    results = {
                'time'      : time,
                'periods'   : periods,
                'interest'  : interestPaid,
                'principal' : principalPaid,
                'balance'   : principal
              }
    # Create DataFrame from Dictionary
    amoritizationTable = pd.DataFrame.from_records(results, index=['time'])
    return amoritizationTable

def loanNpv(loanAmt, APR, loan):
    '''
    Gives the NPV of the loan given loan amount and amoritization schedule
    Parameters
    ----------
    loanAmt: float
        Original loan amount
    loan: DataFrame
        AMoritizaion schedule
    Returns
    -------
    NPV: Float
        Net present value
    '''
    # Calculate the cash flows (monthly payment)
    pmt = -(loan.interest + loan.principal).values
    # Set time 0 to inflow of loanAmount
    pmt[0] = loanAmt
    # Calculate NPV
    NPV = np.npv(APR, pmt)
    return NPV


from numpy import *

def compute_error_for_given_points(b,m,points):
	totalError = 0
	for i in range(0,len(points)):
		x = points[i,0]
		y = points[i,1]
		totalError += (y-(m * x + b)) ** 2
		return totalError / float(len(points))

def step_gradient(b_current,m_current,points,learning_rate):
	#gradient descent
	b_gradient = 0
	m_gradient = 0
	N = float(len(points))
	for i in range(0,len(points)):
		x = points[i,0]
		y = points[i,1]
		b_gradient += -(2/N) * (y - ((m_current * x) + b_current))
		m_gradient += -(2/N) * x * (y - ((m_current * x) + b_current))
	new_b = b_current - (learning_rate * b_gradient)
	new_m = m_current - (learning_rate * m_gradient)
	return (new_b,new_m)

def gradient_descent_runner(points,starting_b,starting_m,learning_rate,num_iterations):
	b = starting_b
	m = starting_m

	for i in range(num_iterations):
		b,m = step_gradient(b,m.array(points),learning_rate)
	return [b,m]

def run():
	points = genfromtext('dataset/data.csv',delimiter = ',')
	#hyperparameters
	learning_rate = 0.0001
	#y = mx + b
	initial_b = 0
	initial_m = 0
	num_iterations = 1000
	[b. m] = gradient_descent_runner(points, initial_b, initial_m, learning_rate, num_iterations)
	print(b)
	print(m)

if __name__ == '__main__':
	run()


import os
import sys
import cv2
import numpy as np

def xyz2lonlat(xyz):
    atan2 = np.arctan2
    asin = np.arcsin

    norm = np.linalg.norm(xyz, axis=-1, keepdims=True)
    xyz_norm = xyz / norm
    x = xyz_norm[..., 0:1]
    y = xyz_norm[..., 1:2]
    z = xyz_norm[..., 2:]

    lon = atan2(x, z)
    lat = asin(y)
    lst = [lon, lat]

    out = np.concatenate(lst, axis=-1)
    return out

def lonlat2XY(lonlat, shape):
    X = (lonlat[..., 0:1] / (2 * np.pi) + 0.5) * (shape[1] - 1)
    Y = (lonlat[..., 1:] / (np.pi) + 0.5) * (shape[0] - 1)
    lst = [X, Y]
    out = np.concatenate(lst, axis=-1)

    return out

class Equirectangular:
    def __init__(self, img_name):
        self._img = cv2.imread(img_name, cv2.IMREAD_COLOR)
        [self._height, self._width, _] = self._img.shape
        #cp = self._img.copy()
        #w = self._width
        #self._img[:, :w/8, :] = cp[:, 7*w/8:, :]
        #self._img[:, w/8:, :] = cp[:, :7*w/8, :]

    def GetPerspective(self, FOV, THETA, PHI, height, width):
        #
        # THETA is left/right angle, PHI is up/down angle, both in degree
        #

        f = 0.5 * width * 1 / np.tan(0.5 * FOV / 180.0 * np.pi)
        cx = (width - 1) / 2.0
        cy = (height - 1) / 2.0
        K = np.array([
                [f, 0, cx],
                [0, f, cy],
                [0, 0,  1],
            ], np.float32)
        K_inv = np.linalg.inv(K)

        x = np.arange(width)
        y = np.arange(height)
        x, y = np.meshgrid(x, y)
        z = np.ones_like(x)
        xyz = np.concatenate([x[..., None], y[..., None], z[..., None]], axis=-1)
        xyz = xyz @ K_inv.T

        y_axis = np.array([0.0, 1.0, 0.0], np.float32)
        x_axis = np.array([1.0, 0.0, 0.0], np.float32)
        R1, _ = cv2.Rodrigues(y_axis * np.radians(THETA))
        R2, _ = cv2.Rodrigues(np.dot(R1, x_axis) * np.radians(PHI))
        R = R2 @ R1
        xyz = xyz @ R.T
        lonlat = xyz2lonlat(xyz)
        XY = lonlat2XY(lonlat, shape=self._img.shape).astype(np.float32)
        persp = cv2.remap(self._img, XY[..., 0], XY[..., 1], cv2.INTER_CUBIC, borderMode=cv2.BORDER_WRAP)

        return persp


from numpy.linalg import eig, inv, svd
from math import atan2
import numpy as np

def __fit_ellipse(x, y):
    x, y = x[:, np.newaxis], y[:, np.newaxis]
    D = np.hstack((x * x, x * y, y * y, x, y, np.ones_like(x)))
    S, C = np.dot(D.T, D), np.zeros([6, 6])
    C[0, 2], C[2, 0], C[1, 1] = 2, 2, -1
    U, s, V = svd(np.dot(inv(S), C))
    a = U[:, 0]
    return a

def ellipse_center(a):
    b, c, d, f, g, a = a[1] / 2, a[2], a[3] / 2, a[4] / 2, a[5], a[0]
    num = b * b - a * c
    x0 = (c * d - b * f) / num
    y0 = (a * f - b * d) / num
    return np.array([x0, y0])

def ellipse_axis_length(a):
    b, c, d, f, g, a = a[1] / 2, a[2], a[3] / 2, a[4] / 2, a[5], a[0]
    up = 2 * (a * f * f + c * d * d + g * b * b - 2 * b * d * f - a * c * g)
    down1 = (b * b - a * c) * (
        (c - a) * np.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a)
    )
    down2 = (b * b - a * c) * (
        (a - c) * np.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a)
    )
    res1 = np.sqrt(up / down1)
    res2 = np.sqrt(up / down2)
    return np.array([res1, res2])

def ellipse_angle_of_rotation(a):
    b, c, d, f, g, a = a[1] / 2, a[2], a[3] / 2, a[4] / 2, a[5], a[0]
    return atan2(2 * b, (a - c)) / 2

def fit_ellipse(x, y):
    """@brief fit an ellipse to supplied data points: the 5 params
        returned are:

        M - major axis length
        m - minor axis length
        cx - ellipse centre (x coord.)
        cy - ellipse centre (y coord.)
        phi - rotation angle of ellipse bounding box

    @param x first coordinate of points to fit (array)
    @param y second coord. of points to fit (array)
    """
    a = __fit_ellipse(x, y)
    centre = ellipse_center(a)
    phi = ellipse_angle_of_rotation(a)
    M, m = ellipse_axis_length(a)
    # assert that the major axix M > minor axis m
    if m > M:
        M, m = m, M
    # ensure the angle is betwen 0 and 2*pi
    phi -= 2 * np.pi * int(phi / (2 * np.pi))
    return [M, m, centre[0], centre[1], phi]


## module lamRange
''' r = lamRange(d,c,N).
    Returns the sequence {r[0],r[1],...,r[N]} that
    separates the N lowest eigenvalues of the tridiagonal
    matrix; that is, r[i] < lam[i] < r[i+1].
'''
import numpy as np
from sturmSeq import *
from gerschgorin import *

def lamRange(d,c,N):
    lamMin,lamMax = gerschgorin(d,c)
    r = np.ones(N+1)
    r[0] = lamMin
  # Search for eigenvalues in descending order
    for k in range(N,0,-1):
      # First bisection of interval(lamMin,lamMax)
        lam = (lamMax + lamMin)/2.0
        h = (lamMax - lamMin)/2.0
        for i in range(1000):
          # Find number of eigenvalues less than lam
            p = sturmSeq(d,c,lam)
            numLam = numLambdas(p)
          # Bisect again & find the half containing lam
            h = h/2.0
            if numLam < k: lam = lam + h
            elif numLam > k: lam = lam - h
            else: break
      # If eigenvalue located, change the upper limit
      # of search and record it in [r]
        lamMax = lam
        r[k] = lam
    return r


import numpy as np

def trimf (x, params):

    if not isinstance(params, list):
        raise TypeError ("Params should be a list with three values.")
    if len(params) != 3:
        raise TypeError ("Number of parameters are incorrect. Expected 3 values in the list.")
    if not (isinstance(params[0], float) and isinstance(params[1], float) and isinstance(params[2], float)):
        raise TypeError ("Parameters should be in float.")
    if not(params[0] <= params[1]):
        raise TypeError("First value in list should be smaller than second one.")
    if not (params[1] <= params[2]):
        raise TypeError("Second value in list should be smaller than third one.")

    a = params[0]
    b = params[1]
    c = params[2]

    result = 0.0

    if x <= a:
        result = 0.0

    elif a <= x <= b:
        result = (x-a)/(b-a)

    elif b <= x <= c:
        result = (c-x)/(c-b)

    elif c <= x:
        result = 0

    return result


'''

do some calculations on a open channel flow inside an inclined pipe (like injector A11, Peregrino)

for now, only pipes (TYPE_PIPE) are supported.

the angle theta is like this (the pointed angle between dotted lines):

           %%%    %%%
      %%%              %%%

  %%%                      %%%

 %%%                         %%%
               .
 %%%          . .            %%%
             .   .
 %%%        .     .         %%%
           .       .
    %%%   .         .    %%%
         .           .
          %%%     %%%
'''

from scipy.optimize import fsolve
import pylab as pl

TYPE_PIPE = 'pipe'

def _flowarea(theta, r):
    return r**2/2. * (theta-pl.sin(theta))

def afa(theta, r):
    '''
    this is A*f(A)
    '''
    A = _flowarea(theta, r)
    return A**(5/3.) / (r*theta)**(2/3.)

def _manning_vmean(rh, n, s, k=1.):
    '''
    https://en.wikipedia.org/wiki/Manning_formula
    rh: hydraulic radius
    n: manning-roughness
    s: slope
    k: conversion factor. 1 for Si
    return: mean velocity [m/s]
    '''
    return k*rh**(2/3.)*pl.sqrt(s) / n

def _manning_func(q, n, s, k=1.):
    '''
    https://en.wikipedia.org/wiki/Manning_formula, after some manipulation
    q: flowrate
    n: manning-roughness
    s: slope
    k: conversion factor. 1 for Si
    return: value
    '''
    return q*n / (k*pl.sqrt(s))

def _hydr_rad(theta, r):
    p = r*theta      # wetted perimeter
    return _flowarea(theta, r)/p

def flow_characteristics(gtype, geom, inclin, q, n=0.022, k=1., full_output=False):
    '''
    given channel type, channel goemetry, flow-rate and inclination, it will
    calculate mean flow velocity and some geometric characteristics for the flow.
    # input
      gtype  : geometry type, f.ex. TYPE_PIPE
      geom   : struct with geometry details
      for TYPE_PIPE:
          geom.diam
      inclin : inclination of channel [deg]. 0 means horizontal
      q      : flowrate [m3/s]
      n      : manning friction factor. default is corrugated steel
      k      : unit conversion. 1. for Si
      full_output: use this one to get some info on the numerical solution (converged or not?)
    # output
      v      : mean velocity
      for TYPE_PIPE:
          theta  : angle discribing fluid channel (see above)
          w      : width of fluid channel (upper)
          h      : height of fluid channel
      ans : if full_output, this will be the full output from fsolve
    '''
    #
    s = pl.sin(inclin*pl.pi/180.)            # slope
    #
    if gtype == TYPE_PIPE:
        r = geom.diam/2.
        func = lambda theta: _flowarea(theta, r)*_manning_vmean(_hydr_rad(theta, r), n, s, k) - q
        theta0 = pl.pi/4.              # initial guess (0 got stuck)
        ans = fsolve(func, theta0, full_output=full_output)
        theta = ans[0][0] if full_output else ans[0]
        v = _manning_vmean(_hydr_rad(theta, r), n, s, k)
        h = r - r*pl.cos(theta/2.)
        w = 2*r*pl.sin(theta/2.)
        if full_output:
            return v, theta, w, h, ans   # angle, width, height, output from fsolve
        else:
            return v, theta, w, h        # angle, width, height


import numpy as np

def r2_score(y_true, y_pred):
    corr_matrix = np.corrcoef(y_true, y_pred)
    corr = corr_matrix[0, 1]
    return corr ** 2

class LinearRegression:
    def __init__(self, learning_rate=0.001, n_iters=1000):
        self.lr = learning_rate
        self.n_iters = n_iters
        self.weights = None
        self.bias = None

    def fit(self, X, y):
        n_samples, n_features = X.shape

        # init parameters
        self.weights = np.zeros(n_features)
        self.bias = 0

        # gradient descent
        for _ in range(self.n_iters):
            y_predicted = np.dot(X, self.weights) + self.bias
            # compute gradients
            dw = (1 / n_samples) * np.dot(X.T, (y_predicted - y))
            db = (1 / n_samples) * np.sum(y_predicted - y)

            # update parameters
            self.weights -= self.lr * dw
            self.bias -= self.lr * db

    def predict(self, X):
        y_approximated = np.dot(X, self.weights) + self.bias
        return y_approximated

# Testing
if __name__ == "__main__":
    # Imports
    import matplotlib.pyplot as plt
    from sklearn.model_selection import train_test_split
    from sklearn import datasets

    def mean_squared_error(y_true, y_pred):
        return np.mean((y_true - y_pred) ** 2)

    X, y = datasets.make_regression(
        n_samples=100, n_features=1, noise=20, random_state=4
    )

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=1234
    )

    regressor = LinearRegression(learning_rate=0.01, n_iters=1000)
    regressor.fit(X_train, y_train)
    predictions = regressor.predict(X_test)

    mse = mean_squared_error(y_test, predictions)
    print("MSE:", mse)

    accu = r2_score(y_test, predictions)
    print("Accuracy:", accu)

    y_pred_line = regressor.predict(X)
    cmap = plt.get_cmap("viridis")
    fig = plt.figure(figsize=(8, 6))
    m1 = plt.scatter(X_train, y_train, color=cmap(0.9), s=10)
    m2 = plt.scatter(X_test, y_test, color=cmap(0.5), s=10)
    plt.plot(X, y_pred_line, color="black", linewidth=2, label="Prediction")
    plt.show()


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

# Function to minmize
def f(x):
	return 8 * (x-10) * np.sin(0.5*x-5) + 200

# Derivative of the function
def fd(x):
	return 4 * (x - 10) * np.cos(0.5*x - 5) + 8 * np.sin(0.5*x-5)

x_min = -30
x_max = 30
x = np.linspace(x_min, x_max, 200)
y = f(x)
r = 0.005  # Learning rate
x_est = 25  # Starting point
y_est = f(x_est)

def animate(i):
	global x_est
	global y_est

	# Gradient descent
	x_est = x_est - fd(x_est) * r
	y_est = f(x_est)

	# Update the plot
	scat.set_offsets([[x_est,y_est]])
	text.set_text("Value : %.2f" % y_est)
	line.set_data(x, y)
	return line, scat, text

def init():
	line.set_data([], [])
	return line,

# Visualization Stuff
fig, ax = plt.subplots()
ax.set_xlim([x_min, x_max])
ax.set_ylim([-5, 500])
ax.set_xlabel("x")
ax.set_ylabel("f(x)")
plt.title("Gradient Descent Local Minima")
line, = ax.plot([], [])
scat = ax.scatter([], [], c="red")
text = ax.text(-25,450,"")

ani = animation.FuncAnimation(fig, animate, 40,
	init_func=init, interval=100, blit=True)

plt.show()


from math import pi, e, sqrt
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import quad
from numpy import trapz
from matplotlib.patches import Rectangle

mean = 0
variance = 1
sigma = sqrt(variance)
Z = 1.28
alternate_mean = 5

# formula for Gaussian distribution, proof of God
def get_y(x, mean, variance, sigma):
	return (e**((-(x-mean)**2)/2*variance))/(sigma * sqrt(2*pi))

# suggestion from stack overflow, returns the index of the nearest matching value
def get_x_index(xs, Z):
	return min(range(len(xs)), key=lambda i: abs(xs[i]-Z))

def graph_normal(mean, variance):
	fig = plt.figure()
	xs = list(np.linspace(-4, 4, 1000))
	ys = [get_y(x, mean, variance, sigma) for x in xs]
	plt.plot(xs, ys)
	plt.vlines(0, ymin=0, ymax=max(ys))
	plt.xlabel("Mean Values")
	plt.title("Standard Gaussian")
	return fig, xs, ys

def fill_graph(fig, xs, ys, Z, outside=False, inside=False):
	idx = get_x_index(xs, Z)
	y_max =  ys[idx]
	outside_range = 0 < ys < y_max
	inside_range = 0 < ys > y_max
	if outside:
		plt.fill_between(xs, 0, ys, where=outside_range, facecolor="blue")
		area = trapz(ys[0:-idx], dx=0.0001) + trapz(ys[idx:], dx=0.0001)
	if inside:
		plt.fill_between(xs, 0, ys, where=inside_range, facecolor="blue")
		area = trapz(ys[-idx:idx], dx=0.0001)
	proportion = round(area / trapz(ys, dx=0.0001), 2)
	patch = Rectangle((0,0), 1, 1, facecolor="blue")
	plt.legend([patch], ["Area under the curve {}".format(proportion)])
	return fig

def add_gaussian(fig, alternate_mean, variance):
	sigma = sqrt(variance)
	lb = alternate_mean - 4
	ub = alternate_mean + 4
	xs = list(np.linspace(lb, ub, 1000))
	ys = [get_y(i, alternate_mean, variance, sigma) for i in xs]
	plt.vlines(alternate_mean, ymin=0, ymax=max(ys))
	plt.plot(xs, ys)
	return fig

fig, xs, ys = graph_normal(mean, variance)
fig = add_gaussian(fig, alternate_mean, variance)
fig = fill_graph(fig, xs, ys, Z, inside=True)
#fig.savefig("Graphs/Shaded_Inside.png")


import matplotlib.pyplot as plt
import numpy as np
import numpy
def linearCombo(a, b, c):
    '''This function is for visualizing linear combination of standard basis in 3D.
    Function syntax: linearCombo(a, b, c), where a, b, c are the scalar multiplier,
    also the elements of the vector.
    '''
    fig = plt.figure(figsize = (10,10))
    ax = fig.add_subplot(projection='3d')

    ######################## Standard basis and Scalar Multiplid Vectors#########################
    vec = np.array([[[0, 0, 0, 1, 0, 0]], # e1
                    [[0, 0, 0, 0, 1, 0]], # e2
                    [[0, 0, 0, 0, 0, 1]], # e3
                    [[0, 0, 0, a, 0, 0]], # a* e1
                    [[0, 0, 0, 0, b, 0]], # b* e2
                    [[0, 0, 0, 0, 0, c]], # c* e3
                    [[0, 0, 0, a, b, c]]]) # ae1 + be2 + ce3
    colors = ['b','b','b','r','r','r','g']
    for i in range(vec.shape[0]):
        X, Y, Z, U, V, W = zip(*vec[i,:,:])
        ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False,
                  color = colors[i] ,arrow_length_ratio = .08, pivot = 'tail',
                  linestyles = 'solid',linewidths = 3, alpha =.6)

    #################################Plot Rectangle Boxes##############################
    dlines = np.array([[[a, 0, 0],[a, b, 0]],
                      [[0, b, 0],[a, b, 0]],
                      [[0, 0, c],[a, b, c]],
                      [[0, 0, c],[a, 0, c]],
                      [[a, 0, c],[a, b, c]],
                      [[0, 0, c],[0, b, c]],
                      [[0, b, c],[a, b, c]],
                      [[a, 0, 0],[a, 0, c]],
                      [[0, b, 0],[0, b, c]],
                      [[a, b, 0],[a, b, c]]])
    colors = ['k','k','g','k','k','k','k','k','k']
    for i in range(dlines.shape[0]):
        ax.plot(dlines[i,:,0], dlines[i,:,1], dlines[i,:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    #################################Annotation########################################
    ax.text(x = a, y = b, z = c, s= ' $(%0.d, %0.d, %.0d)$'% (a, b, c), size = 18)
    ax.text(x = a, y = 0, z = 0, s= ' $%0.d e_1 = (%0.d, 0, 0)$'% (a, a), size = 15)
    ax.text(x = 0, y = b, z = 0, s= ' $%0.d e_2 = (0, %0.d, 0)$'% (b, b), size = 15)
    ax.text(x = 0, y = 0, z = c, s= ' $%0.d e_3 = (0, 0, %0.d)$' %(c, c), size = 15)

    #################################Axis Setting######################################
    ax.grid()
    ax.set_xlim([0, a+1])
    ax.set_ylim([0, b+1])
    ax.set_zlim([0, c+1])

    ax.set_xlabel('x-axis', size = 18)
    ax.set_ylabel('y-axis', size = 18)
    ax.set_zlabel('z-axis', size = 18)

    ax.set_title('Vector $(%0.d, %0.d, %.0d)$ Visualization' %(a, b, c), size = 20)

    ax.view_init(elev=20., azim=15)

if __name__ == '__main__':
    a = 7
    b = 4
    c = 9
    linearCombo(a, b, c)

def linearComboNonStd(a, b, c, vec1, vec2, vec3):
    '''This function is for visualizing linear combination of non-standard basis in 3D.
    Function syntax: linearCombo(a, b, c, vec1, vec2, vec3), where a, b, c are the scalar multiplier,
    ve1, vec2 and vec3 are the basis.
    '''
    fig = plt.figure(figsize = (10,10))
    ax = fig.add_subplot(projection='3d')
    ########################Plot basis##############################
    vec1 = np.array([[0, 0, 0, vec1[0], vec1[1], vec1[2]]])
    X, Y, Z, U, V, W = zip(*vec1)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue',arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    vec2 = np.array([[0, 0, 0, vec2[0], vec2[1], vec2[2]]])
    X, Y, Z, U, V, W = zip(*vec2)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue',arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    vec3 = np.array([[0, 0, 0, vec3[0], vec3[1], vec3[2]]])
    X, Y, Z, U, V, W = zip(*vec3)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue',arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    ###########################Plot Scalar Muliplied Vectors####################
    avec1 = a * vec1
    X, Y, Z, U, V, W = zip(*avec1)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = a/100, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    bvec2 = b * vec2
    X, Y, Z, U, V, W = zip(*bvec2)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = b/100, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    cvec3 = c * vec3
    X, Y, Z, U, V, W = zip(*cvec3)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = c/100, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    combo = avec1 + bvec2 + cvec3
    X, Y, Z, U, V, W = zip(*combo)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'green', alpha = .7,arrow_length_ratio = np.linalg.norm(combo)/300, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    #################################Plot Rectangle Boxes##############################
    point1 = [avec1[0, 3], avec1[0, 4], avec1[0, 5]]
    point2 = [avec1[0, 3]+bvec2[0, 3], avec1[0, 4]+bvec2[0, 4], avec1[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [bvec2[0, 3], bvec2[0, 4], bvec2[0, 5]]
    point2 = [avec1[0, 3]+bvec2[0, 3], avec1[0, 4]+bvec2[0, 4], avec1[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [bvec2[0, 3], bvec2[0, 4], bvec2[0, 5]]
    point2 = [cvec3[0, 3]+bvec2[0, 3], cvec3[0, 4]+bvec2[0, 4], cvec3[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [cvec3[0, 3], cvec3[0, 4], cvec3[0, 5]]
    point2 = [cvec3[0, 3]+bvec2[0, 3], cvec3[0, 4]+bvec2[0, 4], cvec3[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [cvec3[0, 3], cvec3[0, 4], cvec3[0, 5]]
    point2 = [cvec3[0, 3]+avec1[0, 3], cvec3[0, 4]+avec1[0, 4], cvec3[0, 5]+avec1[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [avec1[0, 3], avec1[0, 4], avec1[0, 5]]
    point2 = [cvec3[0, 3]+avec1[0, 3], cvec3[0, 4]+avec1[0, 4], cvec3[0, 5]+avec1[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    ##
    point1 = [avec1[0, 3]+bvec2[0, 3]+cvec3[0, 3],
              avec1[0, 4]+bvec2[0, 4]+cvec3[0, 4],
              avec1[0, 5]+bvec2[0, 5]+cvec3[0, 5]]
    point2 = [cvec3[0, 3]+avec1[0, 3],
              cvec3[0, 4]+avec1[0, 4],
              cvec3[0, 5]+avec1[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    ##
    point1 = [avec1[0, 3]+bvec2[0, 3]+cvec3[0, 3],
              avec1[0, 4]+bvec2[0, 4]+cvec3[0, 4],
              avec1[0, 5]+bvec2[0, 5]+cvec3[0, 5]]
    point2 = [cvec3[0, 3]+bvec2[0, 3],
              cvec3[0, 4]+bvec2[0, 4],
              cvec3[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    ##
    point1 = [avec1[0, 3]+bvec2[0, 3]+cvec3[0, 3],
              avec1[0, 4]+bvec2[0, 4]+cvec3[0, 4],
              avec1[0, 5]+bvec2[0, 5]+cvec3[0, 5]]
    point2 = [bvec2[0, 3]+avec1[0, 3],
              bvec2[0, 4]+avec1[0, 4],
              bvec2[0, 5]+avec1[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)
    #################################Annotation########################################
    ax.text(x = vec1[0,3], y = vec1[0,4], z = vec1[0,5], s= ' $v_1 =(%0.d, %0.d, %.0d)$'% (vec1[0,3], vec1[0,4], vec1[0,4]), size = 8)
    ax.text(x = vec2[0,3], y = vec2[0,4], z = vec2[0,5], s= ' $v_2 =(%0.d, %0.d, %.0d)$'% (vec2[0,3], vec2[0,4], vec2[0,4]), size = 8)
    ax.text(x = vec3[0,3], y = vec3[0,4], z = vec3[0,5], s= ' $v_3= (%0.d, %0.d, %.0d)$'% (vec3[0,3], vec3[0,4], vec3[0,4]), size = 8)

    ax.text(x = avec1[0,3], y = avec1[0,4], z = avec1[0,5], s= ' $%.0d v_1 =(%0.d, %0.d, %.0d)$'% (a, avec1[0,3], avec1[0,4], avec1[0,4]), size = 8)
    ax.text(x = bvec2[0,3], y = bvec2[0,4], z = bvec2[0,5], s= ' $%.0d v_2 =(%0.d, %0.d, %.0d)$'% (b, bvec2[0,3], bvec2[0,4], bvec2[0,4]), size = 8)
    ax.text(x = cvec3[0,3], y = cvec3[0,4], z = cvec3[0,5], s= ' $%.0d v_3= (%0.d, %0.d, %.0d)$'% (c, cvec3[0,3], cvec3[0,4], cvec3[0,4]), size = 8)
#     ax.text(x = 0, y = b, z = 0, s= ' $%0.d e_2 = (0, %0.d, 0)$'% (b, b), size = 15)
#     ax.text(x = 0, y = 0, z = c, s= ' $%0.d e_3 = (0, 0, %0.d)$' %(c, c), size = 15)

    #################################Axis Setting######################################
    ax.grid()
    ax.set_xlim([0, 15])
    ax.set_ylim([0, 15])
    ax.set_zlim([0, 15])

    ax.set_xlabel('x-axis', size = 18)
    ax.set_ylabel('y-axis', size = 18)
    ax.set_zlabel('z-axis', size = 18)

    #ax.set_title('Vector $(%0.d, %0.d, %.0d)$ Visualization' %(a, b, c), size = 20)

    ax.view_init(elev=20., azim=15)

if __name__ == '__main__':
    a = 2
    b = 3
    c = 4
    vec1 = np.array([2,1,0])
    vec2 = np.array([0,3,1])
    vec3 = np.array([1,2,3])
    linearComboNonStd(a, b, c, vec1,vec2,vec3)


import numpy as np

class KMeans:
    def __init__(self, cluster_num, sample_num_all, max_iter=100):
        self.cluster_num = cluster_num
        self.max_iter = max_iter
        self.sample_num_all = sample_num_all
        self.cluster_centers = None

    def generate_init_center(self, data):
        idx = np.random.choice(self.sample_num_all, self.cluster_num, replace=False)
        self.cluster_centers = np.vstack([data[i] for i in idx])

    def find_nearest_center(self, point):
        min_dist = float('inf')
        label = 0
        for i in range(self.cluster_num):
            dist = np.linalg.norm(point-self.cluster_centers[i])
            if dist < min_dist:
                min_dist = dist
                label = i
        return label

    def fit(self, data):
        self.generate_init_center(data)
        label_all = []
        for it in range(self.max_iter):
            label_all = []
            for id in range(self.sample_num_all):
                label = self.find_nearest_center(data[id])
                label_all.append(label)
            label_all = np.array(label_all)
            for i in range(self.cluster_num):
                idx = np.argwhere(label_all == i)
                points = np.vstack([data[idx]])
                self.cluster_centers[i] = np.mean(points, axis=0)
        return label_all, self.cluster_centers


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
from scipy import linalg
from scipy.stats import multivariate_normal
import pyprobml_utils as pml

mu_1 = np.array([[0.22], [0.45]])
mu_2 = np.array([[0.5], [0.5]])
mu_3 = np.array([[0.77], [0.55]])
Mu = np.array([mu_1, mu_2, mu_3])

Sigma1 = np.array([[0.011, -0.01], [-0.01, 0.018]])
Sigma2 = np.array([[0.018, 0.01],  [0.01, 0.011]])
Sigma3 = Sigma1
Sigma = np.array([Sigma1, Sigma2, Sigma3])
mixmat = np.array([[0.5], [0.3], [0.2]]);

def sigmaEllipse2D(mu, Sigma, level=3, npoints=128):
    '''
    SIGMAELLIPSE2D generates x,y-points which lie on the ellipse describing
    a sigma level in the Gaussian density defined by mean and covariance.

    Input:
        MU          [2 x 1] Mean of the Gaussian density
        SIGMA       [2 x 2] Covariance matrix of the Gaussian density
        LEVEL       Which sigma level curve to plot. Can take any positive value,
                    but common choices are 1, 2 or 3. Default = 3.
        NPOINTS     Number of points on the ellipse to generate. Default = 32.

    Output:
        XY          [2 x npoints] matrix. First row holds x-coordinates, second
                    row holds the y-coordinates. First and last columns should
                    be the same point, to create a closed curve.
    '''
    phi = np.linspace(0, 2*np.pi, npoints)
    x = np.cos(phi)
    y = np.sin(phi)
    z = level*np.vstack((x, y))
    xy = mu + linalg.sqrtm(Sigma).dot(z)
    return xy

def plot_sigma_levels(mu, P):
    xy_1 = sigmaEllipse2D(mu, P, 0.25)
    xy_2 = sigmaEllipse2D(mu, P, 0.5)
    xy_3 = sigmaEllipse2D(mu, P, 0.75)
    xy_4 = sigmaEllipse2D(mu, P, 1)
    xy_5 = sigmaEllipse2D(mu, P, 1.25)
    xy_6 = sigmaEllipse2D(mu, P, 1.5)

    plt.plot(xy_1[0], xy_1[1])
    plt.plot(xy_2[0], xy_2[1])
    plt.plot(xy_3[0], xy_3[1])
    plt.plot(xy_4[0], xy_4[1])
    plt.plot(xy_5[0], xy_5[1])
    plt.plot(xy_6[0], xy_6[1])
    plt.plot(mu[0], mu[1], 'ro')

def plot_sigma_vector(Mu, Sigma):
    n = len(Mu)
    plt.figure(figsize=(12, 7))
    for i in range(n):
        plot_sigma_levels(Mu[i], Sigma[i])
    plt.tight_layout()
    pml.savefig('mixgaussSurface.pdf')
    plt.show()

plot_sigma_vector(Mu, Sigma)

def plot_gaussian_mixture(Mu, Sigma, weights=None, x = None, y = None):
    if x == None:
        x = np.arange(0, 1, 0.01)
    if y == None:
        y = np.arange(-0.5, 1.2, 0.01)

    if len(Mu) == len(Sigma) == len(weights):
        pass
    else:
        print("Error: Mu, Sigma and weights must have the same dimension")
        return

    X, Y = np.meshgrid(x, y)
    Pos = np.dstack((X, Y))
    Z = 0

    for i in range(len(Mu)):
        Z = Z + weights[i] * multivariate_normal(Mu[i].ravel(), Sigma[i]).pdf(Pos)

    fig = plt.figure(figsize=(12, 7))
    ax = fig.gca(projection='3d')
    ax.plot_surface(X, Y, Z, cmap="copper", lw=0.5, rstride=1, cstride=1)
    ax.set_xlabel('X axis')
    ax.set_ylabel('Y axis')
    ax.set_zlabel('Z axis')
    plt.tight_layout()
    pml.savefig('mixgaussSurface.pdf')
    plt.show()

weights = [0.5, 0.3, 0.2]
plot_gaussian_mixture(Mu, Sigma, weights=weights)


"""
http://www.johnwittenauer.net/machine-learning-exercises-in-python-part-3/
"""

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import classification_report

import os

# def predict(x, theta):
#     prob = sigmoid(x @ theta)
#     return (prob >= 0.5).astype(int)

def predict(theta, X):
    probability = sigmoid(X * theta.T)
    return [1 if x >= 0.5 else 0 for x in probability]

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

def gradient(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        term = np.multiply(error, X[:, i])
        grad[i] = np.sum(term) / len(X)

    return grad

def cost(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    return np.sum(first - second) / (len(X))

if __name__ == '__main__':
    path = "D:\git\Coursera-ML\johnwittenauer\data\ex2data1.txt"
    data = pd.read_csv(path, header=None, names=['Exam 1', 'Exam 2', 'Admitted'])
    print(data.head())
    positive = data[data['Admitted'].isin([1])]
    negative = data[data['Admitted'].isin([0])]

    fig, ax = plt.subplots(figsize=(12, 8))
    ax.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Admitted')
    ax.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Not Admitted')
    ax.legend()
    ax.set_xlabel('Exam 1 Score')
    ax.set_ylabel('Exam 2 Score')

    # plt.show()

    nums = np.arange(-10, 10, step=0.1)

    fig, ax = plt.subplots(figsize=(12, 8))
    ax.plot(nums, sigmoid(nums), 'r')

    # add a ones column - this makes the matrix multiplication work out easier
    data.insert(0, 'Ones', 1)

    # set X (training data) and y (target variable)
    cols = data.shape[1]
    X = data.iloc[:, 0:cols - 1]
    y = data.iloc[:, cols - 1:cols]

    # convert to numpy arrays and initalize the parameter array theta
    X = np.array(X.values)
    y = np.array(y.values)
    theta = np.zeros(3)
    print(X.shape, theta.shape, y.shape)

    print(cost(theta, X, y))
    # plt.show()
    import scipy.optimize as opt

    result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(X, y))
    final_theta = result[0]
    print("final theta:", final_theta)
    print("cost:", cost(result[0], X, y))

    #report
    theta_min = np.matrix(final_theta)
    predictions = predict(theta_min, X)
    # print(predictions)

    print(classification_report(y, predictions))

    # theta_min = np.matrix(result[0])
    # predictions = predict(theta_min, X)
    # correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
    # accuracy = (sum(map(int, correct)) % len(correct))
    # print ('accuracy = {0}%'.format(accuracy))