# -*- coding: utf-8 -*-
"""
Quadratic solver and plotter example

"""

import math
import scipy.optimize as sp
import matplotlib.pyplot as plt
import numpy

def GetRoots(a,b,c):
    # term inside sqrt
    term = b**2 - 4*a*c

    #handle the complex roots
    factor = 1.0
    if term < 0:
        factor = 1j
        term = -term

    rootMin = (-b - factor*math.sqrt(term))/(2.0*a)
    rootMax = (-b + factor*math.sqrt(term))/(2.0*a)

    return [rootMin,rootMax]

# quadratic coefficients and equation
a = 1
b = -50
c = -1
f = lambda x: a*x**2 + b*x + c
g = lambda b: b**2/4.0

#rootsUsingBrent = sp.brentq(f,0.0,Rt*2)

#get roots
roots = GetRoots(a,b,c)

# min and max X values
minX = -100
maxX = 100

# linear interpolation for plots resolution
resolution = 100

# vectorize function
x = numpy.linspace(minX,maxX,resolution)
y = numpy.vectorize(f)(x)

b = numpy.linspace(minX,maxX,resolution)
c = numpy.vectorize(g)(x)

# plot quadratic function
plt.plot(b,c)
ax = plt.gca()

ax.set_title('a = 1')
ax.set_xlabel('b - coefficient')
ax.set_ylabel('c - coefficient')

ax.fill_between(b, c,2500, facecolor='green', interpolate=True)
ax.fill_between(b, 0,c, facecolor='red', interpolate=True)
ax.text(-50, 2000, 'Complex', style='normal',color='green', fontsize=15,
        bbox={'facecolor':'white', 'alpha':1, 'pad':1.2})
ax.text(-90, 100, 'Real', style='normal',color='red', fontsize=15,
        bbox={'facecolor':'white', 'alpha':1, 'pad':1.2})
ax.text(35, 650, 'Repeated', style='normal',color='blue', fontsize=15,
        bbox={'facecolor':'white', 'alpha':1, 'pad':1.2})
#xmin, xmax = ax.get_xlim()
#ymin, ymax = ax.get_ylim()

#plot roots
#plt.plot([xmin,xmax],[0.0,0.0],'r--')
#plt.plot([roots[0],roots[0]],[ymin,ymax],'g')
#plt.plot([roots[1],roots[1]],[ymin,ymax],'g')

#plt.savefig("root_regions.pdf")
plt.show()

print("Roots are: ", roots[0], " and ", roots[1])


import numpy as np
from sklearn.linear_model import LinearRegression

def ols_sklearn(row):
    """Solve OLS using scikit-learn's LinearRegression"""
    est = LinearRegression()
    X = np.arange(row.shape[0]).reshape(-1, 1) # shape (14, 1)
    # note that the intercept is built inside LinearRegression
    est.fit(X, row.values)
    m = est.coef_[0] # note c is in est.intercept_
    return m

def ols_lstsq(row):
    """Solve OLS using numpy.linalg.lstsq"""
    # build X values for [0, 13]
    X = np.arange(row.shape[0]) # shape (14,)
    ones = np.ones(row.shape[0]) # constant used to build intercept
    A = np.vstack((X, ones)).T # shape(14, 2)
    # lstsq returns the coefficient and intercept as the first result
    # followed by the residuals and other items
    m, c = np.linalg.lstsq(A, row.values, rcond=-1)[0]
    return m

def ols_lstsq_raw(row):
    """Variant of `ols_lstsq` where row is a numpy array (not a Series)"""
    X = np.arange(row.shape[0])
    ones = np.ones(row.shape[0])
    A = np.vstack((X, ones)).T
    m, c = np.linalg.lstsq(A, row, rcond=-1)[0]
    return m

def ols_sm(row):
    # by default statsmodels fit uses
    # https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse (pinv)
    # QR method is the alternative (this add 3s to execution in 50k rows test)
    sm_X = sm.add_constant(row.index)
    model = sm.OLS(row.values, sm_X)
    results = model.fit()
    #results.params # 2 params, C followed by m
    return results.params[1]


# -*- coding: utf-8 -*-
"""
Created on Wed Nov 18 17:38:32 2020

@author: chris
"""
import time
import sympy
import numpy as np
xx,yy = sympy.symbols('x[0],x[1]')  #I replaced the symbolics to x[0] and x[1]
#so I can directly copy past this into the jacobian function below without
#having to change the variable names

from sympy import symbols, Matrix,Function, simplify, exp, hessian, solve

ka=9
kb=2
La=10
Lb=10
F1=2
F2=4

X = Matrix([xx,yy])
g = Matrix([0.5*(ka*((xx**2+(La-yy)**2)**0.5 - La)**2)+0.5*(kb*((xx**2+(Lb+yy)**2)**0.5 - Lb)**2)-F1*xx-F2*yy])

gradf = simplify(g.jacobian(X))
print('Jacobian g', gradf)

hessianf = simplify(hessian(g, X))
print('Hessian g', hessianf)

from scipy import optimize
import numpy as np

def f(x): # The Spring Energy Minimisation Equation
    return 0.5*(ka*((x[0]**2+(La-x[1])**2)**0.5 - La)**2)+0.5*(kb*((x[0]**2+(Lb+x[1])**2)**0.5 - Lb)**2)-F1*x[0]-F2*x[1]

def jacobian(x):
    return np.array((-90.0*x[0]*(x[0]**2 + (x[1] - 10)**2)**(-0.5) - 20.0*x[0]*(x[0]**2 + (x[1] + 10)**2)**(-0.5) + 11.0*x[0] - 2, -90.0*x[1]*(x[0]**2 + (x[1] - 10)**2)**(-0.5) - 20.0*x[1]*(x[0]**2 + (x[1] + 10)**2)**(-0.5) + 11.0*x[1] + 900.0*(x[0]**2 + (x[1] - 10)**2)**(-0.5) - 200.0*(x[0]**2 + (x[1] + 10)**2)**(-0.5) - 74.0))

start=time.time()

for i in range(10000):
    solution = optimize.minimize(f, [1,1], method="Newton-CG",jac=jacobian)
#print(solution,'\n','\n')
end=time.time()

print(solution,'\n',f"Runtime of the program is {end - start}",'\n')

def hessian(x):
    return np.array(((90.0*x[0]**2*(x[0]**2 + x[1]**2 - 20*x[1] + 100)**(-1.5) + 20.0*x[0]**2*(x[0]**2 + x[1]**2 + 20*x[1] + 100)**(-1.5) - 90.0*(x[0]**2 + x[1]**2 - 20*x[1] + 100)**(-0.5) - 20.0*(x[0]**2 + x[1]**2 + 20*x[1] + 100)**(-0.5) + 11.0, 90.0*x[0]*x[1]*(x[0]**2 + x[1]**2 - 20*x[1] + 100)**(-1.5) + 20.0*x[0]*x[1]*(x[0]**2 + x[1]**2 + 20*x[1] + 100)**(-1.5) - 900.0*x[0]*(x[0]**2 + x[1]**2 - 20*x[1] + 100)**(-1.5) + 200.0*x[0]*(x[0]**2 + x[1]**2 + 20*x[1] + 100)**(-1.5)), (90.0*x[0]*x[1]*(x[0]**2 + x[1]**2 - 20*x[1] + 100)**(-1.5) + 20.0*x[0]*x[1]*(x[0]**2 + x[1]**2 + 20*x[1] + 100)**(-1.5) - 900.0*x[0]*(x[0]**2 + x[1]**2 - 20*x[1] + 100)**(-1.5) + 200.0*x[0]*(x[0]**2 + x[1]**2 + 20*x[1] + 100)**(-1.5), 9000.0*(x[0]**2 + (x[1] - 10)**2)**(-1.5)*(0.1*x[1] - 1)**2 - 90.0*(x[0]**2 + (x[1] - 10)**2)**(-0.5) + 2000.0*(x[0]**2 + (x[1] + 10)**2)**(-1.5)*(0.1*x[1] + 1)**2 - 20.0*(x[0]**2 + (x[1] + 10)**2)**(-0.5) + 11.0)))

start=time.time()
for i in range(10000):
    solution1=optimize.minimize(f, [1,1], method="Newton-CG",jac=jacobian,hess=hessian)
end=time.time()
print(solution1, '\n',f"Runtime of the program is {end - start}",'\n')

#%% Other option with CG method
start=time.time()
for i in range(10000):
    solution2 = optimize.minimize(f, [1,1], method="CG")
end=time.time()
print(solution2,'\n',f"Runtime of the program is {end - start}",'\n')

start=time.time()
for i in range(10000):
    solution3 = optimize.minimize(f, [1,1], method="CG",jac=jacobian)
end=time.time()
print(solution3,'\n',f"Runtime of the program is {end - start}",'\n')

import numpy,from scipy,import sympy,from sympy


''' compute VaR (value at risk) of a stock from a monte carlo simulation '''
import numpy as np
import math
from scipy.special import ndtri
from random import random

# based on https://www.youtube.com/watch?v=e79OtCamxD0
# ln(S/S-1)= drift + rand * vol
# drift = expected return for the stock
def walk(n=1000, annual_drift=.1, annual_volatility=.4, init_price=100,
         verbose=False):
    drift = float(annual_drift)/252
    vol = float(annual_volatility)/np.sqrt(252)
    drift_mean = drift-0.5*math.pow(vol,2)
    prices = np.zeros(n)
    if verbose:
        print "drift:", drift
        print "volatility:", vol
        print "drift mean:", drift_mean
    previous_price = init_price
    for i in range(n):
        r = random()
        z = ndtri(r)
        log_return = drift+vol*z
        price = previous_price*math.exp(log_return)
        if verbose:
            print "#%i r=%.2f z=%.2f ret=%.2f%% price=%.2f$" %(i, r, z, log_return*100, price)
        previous_price = price
        prices[i]=price

    return prices

if __name__=="__main__":
    n = 1000
    confidence = 99.0
    prices = np.array([walk()[-1] for i in range(n)])
    prices.sort()
    Si = np.percentile(prices, 100-confidence)
    VaR = Si - prices[0]
    print "Si=%.2f Lowest=%.2f VaR=%.2f" %(Si, prices[0], VaR)


import numpy as np
import math

import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot

print('Exponent of a large number', math.exp(-3439) )

'''
Requirment: Decide whether to attend festival at home town ot not

Criteria 1: Weather rainy or not
Criteria 2: Can I go by train?
Criteria 3: Will I get leave in office?

Bias: If leave has more bias value then, others not taking any priority
Leave  = 6

Bias : If All have equal priority, then bias will be less
'''

inputs = [0, 0, 1] # inputs[weather, train, leave]
weights = [2, 3, 5] # weights[weather, train, leave]

def sigmoid(inputs, weights, bias):
    productOfInputsWeight = (inputs * weights)
    print ('Product of Array:', productOfInputsWeight, ', Length: ', productOfInputsWeight.ndim)
    sum = productOfInputsWeight.sum()
    print('Sum of Array', sum)

    z = sum + (-bias)
    print('Perceptron output:', z)

    # Sigmoid value should not impact the netwowrk as it alwys greater than 1.
    sigmoid = 1 / 1 + np.exp(-z)
    return sigmoid

# print('Am I going to Festival:', sigmoid(np.array(inputs), np.array(weights), 5))

# Excercise 1: Sigmoid neurons simulating perceptrons
'''
Suppose we take all the weights and biases in a network of perceptrons, and multiply them by a positive constant, c>0.
Show that the behaviour of the network doesn't change
'''
def simulateSigmoidAsPerceptron(inputs, weights, bias):

    outputs = []
    global sigmoid

    if inputs.ndim == 2:
        for index in range(len(inputs)):
            print('index:', index)
            input = inputs[index]
            weight = weights[index]
            sigmoidValue = sigmoid(inputs, weights, bias[index])
            print('sigmoid Output:', sigmoidValue)
            outputs.append(sigmoidValue)
        return outputs

    # If number of dimensions is 1

    # Weight should positive number > 0
    return sigmoid(inputs, outputs, bias)

sigmoidSimulationInput = np.array([
    [[0, 2], [0, 3], [1, 5]],
    [[1, 3], [1, 3], [0, 5]],
    [[0, 4], [1, 2], [1, 4]]
])

print ('sigmoidSimulationInput Array Shape:', sigmoidSimulationInput.shape)

inputsList = sigmoidSimulationInput[:, :, 0]
weightsList = sigmoidSimulationInput[:, :, 1]
biasList = [4, 5, 6]

# print ('Get Inputs from sigmoidSimulationInput', sigmoidSimulationInput[:, :, 0])

sigmoidOutputs =  simulateSigmoidAsPerceptron(np.array(inputsList), np.array(weightsList), biasList)
print('simulateSigmoidAsPerceptron:', sigmoidOutputs)

pyplot.plot(sigmoidOutputs, biasList)
pyplot.xlabel('Sigmoid output')
pyplot.ylabel('Bias value')
pyplot.show()


from numpy import *
from numpy.fft import *
import pyfftw

class shift_fftw:
      """Shift function via FFT"""
      def __init__(self, dk_x, dk_y, N):
          kx = linspace(-floor(N/2.0),ceil(N/2.0)-1,N)
          kx = ifftshift(kx)
          [self.kX, self.kY] = meshgrid(kx,kx)
          self.kX = self.kX*dk_x
          self.kY = self.kY*dk_y
          self.f = pyfftw.empty_aligned((N,N),dtype='complex128',n=16)
          self.r = pyfftw.empty_aligned((N,N),dtype='complex128',n=16)
          self.N_tot = N*N
          self.fft_forward = pyfftw.FFTW(self.r, self.f, axes=(0,1))
          self.fft_inverse = pyfftw.FFTW(self.f, self.r, direction='FFTW_BACKWARD', axes=(0,1))

      def shift(self, func, px, py):
          self.r[:,:] = ifftshift(func)
          self.fft_forward.update_arrays(self.r, self.f)
          self.fft_forward.execute()
          self.f = self.f*exp(-2*pi*1j*px*self.kX)*exp(-2*pi*1j*py*self.kY)
          self.fft_inverse.update_arrays(self.f, self.r)
          self.fft_inverse.execute();
          return fftshift(self.r) / self.N_tot #fix normalization

Tutorial 6 - Ptychography/ePIE_code/ptycho_recon/fft_shift.py


import sympy

def para_bhat():
    tht = sympy.symbols('theta', real=True)
    xhat = 2*sympy.cos(tht)**2 - sympy.sin(tht)**2
    yhat = 3*sympy.cos(tht)* sympy.sin(tht)
    sympy.pprint(xhat)
    xhat = xhat.simplify()
    yhat = yhat.simplify()
    print('simpler xhat, yhat')
    sympy.pprint([xhat, yhat])
    r2 = xhat**2+yhat**2
    sympy.pprint(r2)
    r2 = r2.simplify()
    print('simplified rhat')
    sympy.pprint(r2)
    # sympy.pprint(sympy.sqrt(r2).simplify())
    # r2g = (2+sympy.sqrt(3)*sympy.sin(tht))*(2-sympy.sqrt(3)*sympy.sin(tht))
    # sympy.pprint(r2 - r2g.expand())
    # arg = yhat/sympy.sqrt(r2)
    # sympy.pprint(sympy.asin(arg))
    # sympy.pprint(sympy.asin(arg.expand()))
    # sympy.pprint(sympy.asin(arg.expand().simplify()))
    # norming
    phix = sympy.acos(xhat/sympy.sqrt(r2))
    phiy = sympy.asin(yhat/sympy.sqrt(r2))
    #angle phi_of_theta
    phit = sympy.atan2(yhat, xhat)
    # sympy.pprint(phix.subs(tht, sympy.pi/3).evalf())
    # sympy.pprint(phiy.subs(tht, sympy.pi/3).evalf())
    print('phi of theta')
    # sympy.pprint(phit.expand().simplify())
    sympy.pprint(phit.subs(tht, sympy.pi/3).evalf())

    sympy.pprint((phit.subs(tht, sympy.pi/3)/sympy.pi).evalf())
    ratio = (phit.subs(tht, sympy.pi/3)/sympy.pi).evalf()
    # print('pi guess')
    # sympy.pprint(10*ratio**2)
    # sympy.pprint((10*ratio**2-sympy.pi).evalf())
    # sympy.pprint(phit.subs(tht, 2*sympy.pi/3).evalf())
    # sympy.pprint(phit.subs(tht, 3*sympy.pi/3))
    tht2rat = phit.subs(tht, sympy.pi/3)/sympy.pi
    print('phi_2 in units of pi')
    sympy.pprint(tht2rat)
    sympy.pprint(tht2rat.expand().simplify())
    sympy.pprint(tht2rat.expand().simplify().evalf())
    # sympy.pprint(10*(tht2rat**2).expand().simplify())
    # sympy.pprint(10*(tht2rat**2).expand().simplify().evalf())
    sympy.pprint(((10*tht2rat)**2).expand().simplify().evalf())
    sympy.pprint((
        phit.subs(tht, sympy.pi/3).evalf() - (.1*sympy.pi)**2
        ).evalf())

def limit_phis():
    tht = sympy.symbols('theta', real=True)
    xhat = 2*sympy.cos(tht)**2 - sympy.sin(tht)**2
    yhat = 3*sympy.cos(tht)* sympy.sin(tht)
    xhat = xhat.simplify()
    yhat = yhat.simplify()
    phit = sympy.atan2(yhat, xhat)
    return [0, 0,
        phit.subs(tht, sympy.pi/3),phit.subs(tht, 2*sympy.pi/3),
        0,
        -phit.subs(tht, 2*sympy.pi/3), -phit.subs(tht, sympy.pi/3)
        ]

def dipole_limit():
    alpha = sympy.symbols('alpha', real=True)
    phis = limit_phis()
    mu_x = alpha
    mu_y = 0
    for phi in phis[1:]:
        mu_x += sympy.cos(phi)
        mu_y += sympy.sin(phi)
    sympy.pprint([mu_x, mu_y])
    mag2 = mu_x**2 + mu_y**2
    sympy.pprint(mag2)
    sympy.pprint(mag2.expand())
    sympy.pprint(mag2.expand().simplify())
    sympy.pprint(mag2.expand().evalf())
    mag = sympy.sqrt(mag2.evalf())
    sympy.pprint(mag)
    sympy.pprint(mag.evalf())

if __name__ == '__main__':
    para_bhat()
    # sympy.pprint(limit_phis())
    # dipole_limit()


from statsmodels.stats.proportion import proportions_ztest

# can we assume anything from our sample
significance = 0.05

# our sample - 82% are good
sample_success = 410
sample_size = 500

# our Ho is  80%
null_hypothesis = 0.80

# check our sample against Ho for Ha > Ho
# for Ha < Ho use alternative='smaller'
# for Ha != Ho use alternative='two-sided'
stat, p_value = proportions_ztest(count=sample_success, nobs=sample_size, value=null_hypothesis, alternative='larger')

# report
print('z_stat: %0.3f, p_value: %0.3f' % (stat, p_value))

if p_value > significance:
    print("Fail to reject the null hypothesis - we have nothing else to say")
else:
    print("Reject the null hypothesis - suggest the alternative hypothesis is true")


from math import exp
from ..test_shape import wrapper
import numpy as np
def sigmoid(X,prime=False):
	"""an activation function which outputs the value between (0,1)"""
	if prime:
		return sigmoid(X) * (1.0 - sigmoid(X))
	else:
		return 1.0/(1.0+np.exp(-X))
def sigmoidDerivative(X):
	"""  input : array of features
	     output : Sigmoid derivative of the input  """

	return stable_sigmoid(X) * (1.0 - stable_sigmoid(X))

def tanh(X):
	"""an activation function which outputs the value between (-1,1)"""
	if isinstance(X,np.ndarray):
		return (2.0/(1.0+np.exp(-(2*X))))-1
	else:
		X=np.array(X)
		return tanh(X)
def relu(X,prime=False):
	if prime:
		X = np.array(X)
		grads= 1.0*(X>0)
		grads[grads==0]=1e-1
		return grads
	if isinstance(X,np.ndarray):
		return np.maximum(X,0)
	else:
		X = np.array(X)
		return relu(X)

def reluDerivative(X,ep=1e-1):
	X = np.array(X)
	grads= 1.0*(X>0)
	grads[grads==0]=1e-1
	return grads
def stable_sigmoid(X,prime=False):
	if prime:
		return stable_sigmoid(X) * (1.0 - stable_sigmoid(X))
	if isinstance(X,(list,np.ndarray)):
		res=[]
		for i in X:
			#for now remove the condition
			if i>=0:
				z=exp(-i)
				res.append(z)
			else:
				z=exp(i)
				res.append(z/(1.0+z))
		return np.asarray(res)
	else:
		if X>=0:
				z=exp(-X)
				return 1.0/(1.0+z)
		else:
				z=exp(X)
				return z/(1.0+z)
def softmax(X,prime=False):
	if prime:
		y=X.reshape(-1,1)
		return np.diagflat(y) - np.dot(y, y.T)
	y = X - np.max(X)
	exp = np.exp(y)
	return exp/np.sum(exp)
def identity():pass


from numba import njit, stencil
from numpy import array

@njit(fastmath=True, parallel=True)
def central_difference_3(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (f[1] - f[-1]) / (2 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def central_difference_5(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (f[-2] - 8 * f[-1] + 8 * f[1] - f[2]) / (12 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def central_difference_7(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (-f[-3] + 9 * f[-2] - 45 * f[-1] + 45 * f[1] - 9 * f[2] + f[3]) / (60 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def central_difference_9(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (3 * f[-4] - 32 * f[-3] + 168 * f[-2] - 672 * f[-1] + 672 * f[1] - 168 * f[2] + 32 * f[3] - 3 * f[4]) / (840 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def lanczos_5(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (f[1] - f[-1] + 2 * (f[2] - f[-2])) / (10 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def lanczos_7(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (f[1] - f[-1] + 2 * (f[2] - f[-2]) + 3 * (f[3] - f[-3])) / (28 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def lanczos_9(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (f[1] - f[-1] + 2 * (f[2] - f[-2]) + 3 * (f[3] - f[-3]) + 4 * (f[4] - f[-4])) / (60 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def lanczos_11(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (f[1] - f[-1] + 2 * (f[2] - f[-2]) + 3 * (f[3] - f[-3]) + 4 * (f[4] - f[-4]) + 5 * (f[5] - f[-5])) / (110 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def lanczos_13(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (f[1] - f[-1] + 2 * (f[2] - f[-2]) + 3 * (f[3] - f[-3]) + 4 * (f[4] - f[-4]) + 5 * (f[5] - f[-5])) + 6 * (f[6] - f[-6]) / (182 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def super_lanczos_7(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (58 * (f[1] - f[-1]) + 67 * (f[2] - f[-2]) - 22 * (f[3] - f[-3])) / (252 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def super_lanczos_9(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (126 * (f[1] - f[-1]) + 193 * (f[2] - f[-2]) + 142 * (f[3] - f[-3]) - 86 * (f[4] - f[-4])) / (1188 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def super_lanczos_11(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (296 * (f[1] - f[-1]) + 503 * (f[2] - f[-2]) + 532 * (f[3] - f[-3]) + 294 * (f[4] - f[-4]) - 300 * (f[5] - f[-5])) / (5148 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def smooth_noise_robust_5(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (2 * (f[1] - f[-1]) + f[2] - f[-2]) / (8 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def smooth_noise_robust_7(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (5 * (f[1] - f[-1]) + 4 * (f[2] - f[-2]) + f[3] - f[-3]) / (32 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def smooth_noise_robust_9(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (14 * (f[1] - f[-1]) + 14 * (f[2] - f[-2]) + 6 * (f[3] - f[-3]) + f[4] - f[-4]) / (128 * h)
    )(data, step)

@njit(fastmath=True, parallel=True)
def smooth_noise_robust_11(data: array, step: float) -> array:
    return stencil(
        lambda f, h: (42 * (f[1] - f[-1]) + 48 * (f[2] - f[-2]) + 27 * (f[3] - f[-3]) + 8 * (f[4] - f[-4]) + f[5] - f[-5]) / (512 * h)
    )(data, step)

METHODS = {
    'cd3': central_difference_3,
    'cd5': central_difference_5,
    'cd7': central_difference_7,
    'cd9': central_difference_9,
    'l5': lanczos_5,
    'l7': lanczos_7,
    'l9': lanczos_9,
    'l11': lanczos_11,
    'l13': lanczos_13,
    'sl7': super_lanczos_7,
    'sl9': super_lanczos_9,
    'sl11': super_lanczos_11,
    'snr5': smooth_noise_robust_5,
    'snr7': smooth_noise_robust_7,
    'snr9': smooth_noise_robust_9,
    'snr11': smooth_noise_robust_11,
}

def differentiate(data: array, step: float, method: str='l11') -> array:
    return METHODS[method](data, step)


from scipy.stats import beta

class Prior:
    def __init__(self, name, size, par, random_state):
        ''' Constructor for this class. '''

        """
        :type name: str
        :type size: int
        :type par: list
        :rtype: list
        """

        self.name = name # assign the name of distribution ["Beta", "Dirichlet"]
        self.size = size
        self.par = par
        self.random_state = random_state

    def Prior_MCsim(self):
        print('Generate ' + str(self.size) + " samples for " + self.name + ' distribution')
        if self.name == 'Beta':
            return beta.rvs(a = self.par[0], b = self.par[1], size = self.size, random_state = self.random_state)
        else :
            print("Not Available")
            return []

if __name__ == '__main__':
    p = Prior(name = 'Beta', size = 1000, par = [1, 1], random_state = 1234)
    print(p.Prior_MCsim())


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Feb 11 11:14:22 2019

@author: alankar
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.special.orthogonal import p_roots #Legendre Polynomial roots
#from scipy import constants

"""
def gauss_quad(func,a,b,n,*args):#Legendre
    [x,w] = p_roots(n+1)
    I_G = 0.5*(b-a)*np.sum(w*func(0.5*(b-a)*x+0.5*(b+a),*args))
    return I_G

def gauss_quad_2d(func,a,b,n,*args):#Legendre
    I_G = gauss_quad(lambda y:gauss_quad(func,a[0],b[0],n[0],y,*args),a[1],b[1],n[1])
    return I_G
"""

def gauss_quad_2d(func,a,b,n,*args):#Legendre
    [x,wx] = p_roots(n[0]+1)
    [y,wy] = p_roots(n[1]+1)
    x,y = np.meshgrid(x,y)
    wx,wy = np.meshgrid(wx,wy)
    I_G = 0.5*(b[0]-a[0])*0.5*(b[1]-a[1])*np.sum(wx*wy*\
              func(0.5*(b[0]-a[0])*x+0.5*(b[0]+a[0]),0.5*(b[1]-a[1])*y+0.5*(b[1]+a[1]),*args))
    return I_G

I = lambda x,y,z: np.nan_to_num(z/(x**2+y**2+z**2)**1.5) #To remove singularity at (0,0,0)

L = 10
N = 100

z = np.linspace(0.,10,30)
#G sigma = 1

Fz = np.array([gauss_quad_2d(I,[-L/2.,-L/2.],[L/2.,L/2.],[N,N],Z) for Z in z])

plt.figure(figsize=(13,10))
plt.plot(z,Fz,c='r')
plt.xlabel(r'$z$',size=22)
plt.ylabel(r'$F_z$',size=22)
plt.title('Gravitational pull',size=25)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.grid()
plt.savefig('14_1.png')
plt.show()

N = 800
z = np.linspace(0.,10,400)
Fz = np.array([gauss_quad_2d(I,[-L/2.,-L/2.],[L/2.,L/2.],[N,N],Z) for Z in z])

plt.figure(figsize=(13,10))
plt.plot(z,Fz,c='r')
plt.xlabel(r'$z$',size=22)
plt.ylabel(r'$F_z$',size=22)
plt.title('Gravitational pull',size=25)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.grid()
plt.savefig('14_2.png')
plt.show()


# Import the necessary modules.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# In this script, we will perform a simulation of constitutive expression from
# a promoter where the mRNA is allowed to decay. We are assuming that the mRNA
# is transcribed a constant rate. We'll begin by defining some parameters of
# our simulation.
prod_rate = 20  # Production rate of mRNA in units of 1/min
gamma = 1 / 3  # Degradation rate of mRNA in units of 1/min
dt = 0.01  # Time step in units of minutes.
total_time = 30  # Total length of the simulation in units of minutes.
time = np.arange(0, total_time, dt)  # Time vector

# We can do a simple euler integration of the mRNA transcription dynamics.
m_theo = np.zeros(len(time))  # A storage vector for the theoretical mean.
for i in range(1, len(time)):
    m_theo[i] = m_theo[i-1] + prod_rate * dt - gamma * dt * m_theo[i-1]

# For the gillespie simulation, we will determine whether an mRNA is
# transcribed based on the probabilities of production, decay, or no change at
# all at each point in time.
n_simulations = 100  # Total number of simulations to perform
max_it = 1000  # Maximum number of algorithm iterations.
m_sim = np.zeros((n_simulations, max_it))
tau = np.zeros((n_simulations, max_it))

for i in range(n_simulations):
    for j in range(1, max_it):
        degradation = gamma * m_sim[i, j-1]
        prob_sum = prod_rate + degradation

        # Determine the time to the next reaction (tau). We can pull this from
        # a Poisson distribution by relating it to a uniform distribution as
        # follows
        tau[i, j] = 1 / prob_sum * np.log(1 / np.random.rand())

        # Now we'll flip a coin and decide which step to take.
        flip = np.random.rand()
        if flip <= prod_rate / prob_sum:
            m_sim[i, j] = m_sim[i, j-1] + 1
        else:
            m_sim[i, j] = m_sim[i, j-1] - 1

# And our simulation is done! We need to now relate teh tau vector to physical
# time. We can do this simply by summing up each step of the tau.
plt.figure()

# Plot each simulation
for i in range(n_simulations):
    t = np.empty_like(tau[i])
    for j in range(len(tau[i])):
        t[j] = np.sum(tau[i, :j])

    # Now plot the simulation vs time.
    plt.plot(t, m_sim[i, :])

# Plot the thoeretical prediction.
plt.plot(time, m_theo, 'k-')

# Set the labels.
plt.xlabel('time (min)')
plt.ylabel('number of mRNAs')
plt.ylim([0, 100])
plt.show()


#!/Users/robertpoenaru/.pyenv/shims/python

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

N_COILS = 3  # INTEGER NUMBER
RADIUS = 3  # CENTIMETERS
CURRENT = 5  # AMPERES
B_FIELD = 2.5  # TESLA

# ANGLE BETWEEN THE MAGNETIC MOMENT OF THE LOOP AND THE MAGNETIC FIELD B
THETA = 30.0  # RADIANS

def Rad(angle):
    return float(angle * np.pi / 180.0)

def Area(radius):
    return float(np.power(radius, 2) * np.pi)

# Compute the magnetic moment (magnetic dipole) of the current carrying loop

def MU(PARAMS):
    # magnetic moment is directly proportional to the product between the current running through the loop and the enclosing area
    r = PARAMS[1] * 0.01
    I = PARAMS[2]
    N = PARAMS[0]

    # the magnetic moment of a single loop
    mu_0 = Area(r) * I

    # the magnetic moment of the entire coil
    mu = N * mu_0

    print(f'The magnetic moment is: µ= {mu}')
    return mu

# Calculate the torque that is exerted by the magnetic field on the magnetic moment of the loop.
# Torque will start to align the magnetic moment of the loop with the field itself
def T(PARAMS):
    B = float(PARAMS[3])
    theta = float(Rad(PARAMS[4]))
    T = MU(PARAMS) * B * np.sin(theta)
    print(f'The torque applied on the current carrying loop is T= {T}')
    return T

# Compute the radius of the trajectory of a charged particle moving inside a magnetic field with constant magnitude
def R(MASS, CHARGE, SPEED, B_FIELD):
    R = (MASS * SPEED) / (CHARGE * B_FIELD)
    print(f'The radius of the particle trajectory is R= {R}')
    return R

PARAM_SET = [N_COILS, RADIUS, CURRENT, B_FIELD, THETA]

# print(MU(PARAM_SET))
# print(T(PARAM_SET))
# print(R(1, 1, 1, 1))

interval = np.arange(-120, 120, 2.5)

alphas = list(map(lambda x: x * np.pi / 180.0, interval))

sines = np.sin(alphas)

currents = rd.uniform(1, 5, len(interval))
# areas = list(map(lambda x: 0.30 * x, currents))

AREA = 3.44
dipoles = list(map(lambda I: round(I * AREA, 3), currents))

B_Field = 2.5

torques = [-x * sines[30] * B_Field for x in dipoles]
print(torques)

plt.plot(dipoles, torques, '-r', label='torques')
plt.savefig('torques.pdf', dpi=500, bbox_inches='tight')
plt.close()

angled_torques = [-B_Field * dipoles[0] * sine for sine in sines]
print(angled_torques)

plt.plot(sines, angled_torques, '-r', label='angled-torques')
plt.savefig('angled_torques.pdf', dpi=500, bbox_inches='tight')
plt.close()


import math
from scipy.stats import norm

parameterCount = 5

def BSPrice(St, sigma, K, tau, r, isCall):
    omega = 1 if isCall else -1
    sigmahat = sigma*math.sqrt(tau)
    d1 = 1/sigmahat*(math.log(St/K)+(r+sigma**2/2)*tau)
    d2 = d1-sigmahat
    n1 = norm.cdf(omega*d1)
    n2 = norm.cdf(omega*d2)
    rhat = math.exp(-r*tau)
    price = omega*(n1*St - n2*K*rhat)
    return price

def BSPrice_Analytical(St, sigma, K, tau, r, isCall):
    omega = 1 if isCall else -1
    sigmahat = sigma * math.sqrt(tau)
    d1 = 1 / sigmahat * (math.log(St / K) + (r + sigma ** 2 / 2) * tau)
    d2 = d1 - sigmahat
    n1 = norm.cdf(omega * d1)
    n2 = norm.cdf(omega * d2)
    rhat = math.exp(-r * tau)
    price = omega * (n1 * St - n2 * K * rhat)

    derivatives = [0] * parameterCount
    if isCall:
        derivatives[0] = n1
        derivatives[1] = St*norm.pdf(d1)*math.sqrt(tau)
        derivatives[3] = -St*norm.pdf(d1)*sigma/(2*math.sqrt(tau))-r*K*math.exp(-r*tau)*n2
        derivatives[4] = K*tau*math.exp(-r*tau)*n2

    else:
        derivatives[0] = -n1
        derivatives[1] = St*norm.pdf(d1)*math.sqrt(tau)
        derivatives[3] = -St*norm.pdf(d1)*sigma/(2*math.sqrt(tau))+r*K*math.exp(-r*tau)*n2
        derivatives[4] = -K*tau*math.exp(-r*tau)*n2

    return {'price': price, 'derivatives': derivatives}

def BSPrice_SAD(St, sigma, K, tau, r, isCall):
    omega = 1 if isCall else -1
    sigmahat = sigma*math.sqrt(tau)
    d1 = 1/sigmahat*(math.log(St/K)+(r+sigma**2/2)*tau)
    d2 = d1-sigmahat
    n1 = norm.cdf(omega*d1)
    n2 = norm.cdf(omega*d2)
    rhat = math.exp(-r*tau)
    price = omega*(n1*St - n2*K*rhat)

    sigmahatdot = [0]*parameterCount
    sigmahatdot[1] += math.sqrt(tau)
    sigmahatdot[3] += 1/2 * sigma / math.sqrt(tau)

    d1dot = [0]*parameterCount
    for i in range(0, parameterCount):
        d1dot[i] += (-1/sigmahat**2 * (math.log(St/K) + (r+sigma**2)*tau)) * sigmahatdot[i]
    d1dot[0] += 1/(St*sigmahat)
    d1dot[1] += 1/sigmahat*sigma*tau
    d1dot[2] += -1 / (K*sigmahat)
    d1dot[3] += sigma**2/sigmahat
    d1dot[4] += tau/sigmahat

    d2dot = [0]*parameterCount
    for i in range(0, parameterCount):
        d2dot[i] += -1 * sigmahatdot[i]
    for i in range(0, parameterCount):
        d2dot[i] += 1 * d1dot[i]

    n1dot = [0]*parameterCount
    for i in range(0, parameterCount):
        n1dot[i] += omega*norm.pdf(omega*d1) * d1dot[i]

    n2dot = [0]*parameterCount
    for i in range(0, parameterCount):
        n2dot[i] += omega*norm.pdf(omega*d2) * d2dot[i]

    rhatdot = [0]*parameterCount
    rhatdot[3] += -r * rhat
    rhatdot[4] += -tau * rhat

    pricedot = [0]*parameterCount
    for i in range(0, parameterCount):
        pricedot[i] += omega*St * n1dot[i]
    for i in range(0, parameterCount):
        pricedot[i] += -omega*K*rhat * n2dot[i]
    for i in range(0, parameterCount):
        pricedot[i] += -omega*n2*K * rhatdot[i]
    pricedot[0] += omega*n1
    pricedot[2] += -omega*n2*rhat

    return {'price': price, 'derivatives': pricedot}
#Assignment count for derivative calculation: 57

def BSPrice_AAD(St, sigma, K, tau, r, isCall):
    omega = 1 if isCall else -1
    sigmahat = sigma * math.sqrt(tau)
    d1 = 1 / sigmahat * (math.log(St / K) + (r + sigma ** 2 / 2) * tau)
    d2 = d1 - sigmahat
    n1 = norm.cdf(omega * d1)
    n2 = norm.cdf(omega * d2)
    rhat = math.exp(-r * tau)
    price = omega * (n1 * St - n2 * K * rhat)

    Pbar = 1

    rhatbar = -n2*K*omega * Pbar

    n2bar = -K*rhat*omega * Pbar

    n1bar = omega*St * Pbar

    d2bar = norm.pdf(omega*d2)*omega * n2bar

    d1bar = norm.pdf(omega*d1)*omega * n1bar
    d1bar += 1 * d2bar

    sigmahatbar = -1 *d2bar
    sigmahatbar += -1/(sigmahat**2)*(math.log(St/K)+(r+(sigma**2)/2)*tau) * d1bar

    rbar = -tau*rhat * rhatbar
    rbar += sigmahat*tau * d1bar

    taubar = -r*rhat * rhatbar
    taubar += (1/sigmahat*r + sigma**2/(sigmahat*2)) * d1bar
    taubar += 1/2*sigma/math.sqrt(tau) * sigmahatbar

    Kbar = -omega*n2*rhat * Pbar
    Kbar += -1/(sigmahat*K) * d1bar

    sigmabar = sigmahat*sigma*tau * d1bar
    sigmabar += math.sqrt(tau) * sigmahatbar

    Stbar = omega*n1 * Pbar
    Stbar += 1/(sigmahat*St) * d1bar

    derivatives = [Stbar, sigmabar, Kbar, taubar, rbar]

    return {'price': price, 'derivatives': derivatives}
#Assignment count for derivative calculation: 20

def BSPrice_AAAD(St, sigma, K, tau, r, isCall):
    omega = 1 if isCall else -1
    sigmahat = sigma*tau.sqrt()
    d1 = 1/sigmahat*((St/K).log()+(r+sigma**2/2)*tau)
    d2 = d1-sigmahat
    n1 = (omega*d1).normalcdf()
    n2 = (omega*d2).normalcdf()
    rhat = (-r*tau).exp()
    price = omega*(n1*St - n2*K*rhat)
    return price


import numpy as np
from scipy.stats import multivariate_normal
from itertools import islice

scenario = {
    "CPH:NDA-DK": 67.63,
    "CPH:DANSKE": 106.1,
    "CPH:NDA-DK-volatility": 1,
    "EONIA": -0.0049,
    "DKKLIBOR-1W": -0.00002,
}

class MonteCarloScenarios:
    def __init__(self, mean_scenario, covariance, keys=None, seed=123): # Always have seed
        if keys is None:
            self.keys = sorted(mean_scenario.keys())
        else:
            missing = [k for k in keys if k not in mean_scenario]
            if missing:
                raise Exception(f"Missing keys in mean scenario: {missing}")
            self.keys = keys
        self.mean_scenario = mean_scenario
        self.mean = np.array([self.mean_scenario[key] for key in self.keys])
        self.covariance = covariance
        self.random = np.random.RandomState(seed)
    def __iter__(self):
        return self
    def __next__(self):
        return dict(zip(self.keys, multivariate_normal.rvs(self.mean, self.covariance, random_state=self.random)))

def monte_carlo_scenarios(mean_scenario, covariance, keys=None, seed=123):
    if keys is None:
        keys = sorted(mean_scenario.keys())
    else:
        missing = [k for k in keys if k not in mean_scenario]
        if missing:
            raise Exception(f"Missing keys in mean scenario: {missing}")
        keys = keys
    mean = np.array([mean_scenario[key] for key in keys])
    random = np.random.RandomState(seed)
    while True:
        yield dict(zip(keys, multivariate_normal.rvs(mean, covariance, random_state=random)))

# Problem: np -> dict

class Asset:
    pass

class Equity(Asset):
    def __init__(self, name):
        self.name = name

    def price(self, scenario):
        return scenario[self.name]

def price_scenarios(portfolio, scenarios):
    return [portfolio.price(scenario) for scenario in scenarios]

def report(portfolio, scenarios):
    prices = price_scenarios(portfolio, scenarios)
    print("Mean price:         ", np.mean(prices))
    print("Minimum price:      ", np.min(prices))
    print("Maximum price:      ", np.max(prices))
    print("Standard deviation: ", np.std(prices))

infinite_scenarios = MonteCarloScenarios(scenario, [[2.0,0.5],[0.5,1.0]], ["CPH:NDA-DK", "CPH:DANSKE"])
print (next(infinite_scenarios))

scenarios = list(islice(infinite_scenarios,0,5))
for i,x in enumerate(scenarios):
    print(i,x)

scenarios = list(islice(infinite_scenarios,0,1000))

report(Equity("CPH:NDA-DK"), scenarios)

report(Equity("CPH:NDA-DK"), islice(monte_carlo_scenarios(scenario, [[2.0,0.5],[0.5,1.0]], ["CPH:NDA-DK", "CPH:DANSKE"]),0,1000))


import itertools
from unittest import TestCase

import numpy as np
from numpy.testing import assert_almost_equal

from bayesian_decision_tree.utils import hypercube_to_hypersphere_surface

class UtilsTest(TestCase):
    def test_hypercube_to_hypersphere_surface_2D_full_single_point(self):
        hc = np.array([0.2, 0.9])
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=False)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 1)
        self.assertEqual(hs.shape, (3,))
        assert_almost_equal(np.linalg.norm(hs), 1)

    def test_hypercube_to_hypersphere_surface_1D_full(self):
        n_points = 11
        hc = np.linspace(0, 1, n_points).reshape(-1, 1)
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=False)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 2)
        self.assertEqual(hs.shape, (n_points, 2))
        assert_almost_equal(np.linalg.norm(hs, axis=1), 1)

        # check uniformity
        expected_cos = np.dot(hs[0], hs[1])
        for i in range(1, n_points):
            cos = np.dot(hs[i-1], hs[i])
            assert_almost_equal(cos, expected_cos)

        cos = np.dot(hs[0], hs[-2])
        assert_almost_equal(cos, expected_cos)

        cos = np.dot(hs[0], hs[-1])
        assert_almost_equal(cos, 1.0)

    def test_hypercube_to_hypersphere_surface_1D_half(self):
        n_points = 11
        hc = np.linspace(0, 1, n_points).reshape(-1, 1)
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=True)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 2)
        self.assertEqual(hs.shape, (n_points, 2))
        assert_almost_equal(np.linalg.norm(hs, axis=1), 1)

        # check uniformity
        expected_cos = np.dot(hs[0], hs[1])
        for i in range(1, n_points):
            cos = np.dot(hs[i-1], hs[i])
            assert_almost_equal(cos, expected_cos)

        cos = np.dot(hs[0], hs[-2])
        assert_almost_equal(cos, -expected_cos)

        cos = np.dot(hs[0], hs[-1])
        assert_almost_equal(cos, -1.0)

    def test_hypercube_to_hypersphere_surface_2D_full(self):
        n_points_per_dim = 1000
        n_points = n_points_per_dim**2
        grid = np.linspace(0, 1, n_points_per_dim)
        x, y = np.meshgrid(grid, grid)
        hc = np.array([x.flatten(), y.flatten()]).T
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=False)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 2)
        self.assertEqual(hs.shape, (n_points, 2+1))
        assert_almost_equal(np.linalg.norm(hs, axis=1), 1)

        # make sure all quadrants contain approximately the same number of data points
        tolerance_fraction = 0.01
        for quadrant_signs in itertools.product([-1, 1], [-1, 1], [-1, 1]):
            in_quadrant = np.all(hs * quadrant_signs > 0, axis=1).sum()
            min = n_points / 2**(2+1) * (1-tolerance_fraction)
            max = n_points / 2**(2+1) * (1+tolerance_fraction)
            msg = f'Expected a value between {min:.0f} and {max:.0f}, but was {in_quadrant}'
            self.assertTrue(min <= np.sum(in_quadrant) <= max, msg=msg)

    def test_hypercube_to_hypersphere_surface_2D_half(self):
        n_points_per_dim = 1000
        n_points = n_points_per_dim**2
        grid = np.linspace(0, 1, n_points_per_dim)
        x, y = np.meshgrid(grid, grid)
        hc = np.array([x.flatten(), y.flatten()]).T
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=True)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 2)
        self.assertEqual(hs.shape, (n_points, 2+1))
        assert_almost_equal(np.linalg.norm(hs, axis=1), 1)

        # make sure all quadrants contain approximately the same number of data points
        tolerance_fraction = 0.01
        for quadrant_signs in itertools.product([-1, 1], [-1, 1], [-1, 1]):
            in_quadrant = np.all(hs * quadrant_signs > 0, axis=1).sum()
            if quadrant_signs[0] == -1:
                self.assertEqual(np.sum(in_quadrant), 0)
            else:
                min = n_points / 2**2 * (1-tolerance_fraction)
                max = n_points / 2**2 * (1+tolerance_fraction)
                msg = f'Expected a value between {min:.0f} and {max:.0f} in quadrant {quadrant_signs}, but was {in_quadrant}'
                self.assertTrue(min <= np.sum(in_quadrant) <= max, msg)

    def test_hypercube_to_hypersphere_surface_5D_full(self):
        n_points = 1_000_000
        np.random.seed(666)
        hc = np.random.uniform(0, 1, (n_points, 5))
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=False)
        # hs = np.random.normal(0, 1, hs.shape)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 2)
        self.assertEqual(hs.shape, (n_points, 5+1))
        assert_almost_equal(np.linalg.norm(hs, axis=1), 1)

        # make sure all quadrants contain approximately the same number of data points
        tolerance_fraction = 0.02
        for quadrant_signs in itertools.product(*list(np.tile([-1, 1], (5+1, 1)))):
            in_quadrant = np.all(hs * quadrant_signs > 0, axis=1).sum()
            min = n_points / 2**(5+1) * (1-tolerance_fraction)
            max = n_points / 2**(5+1) * (1+tolerance_fraction)
            msg = f'Expected a value between {min:.0f} and {max:.0f}, but was {in_quadrant}'
            self.assertTrue(min <= np.sum(in_quadrant) <= max, msg=msg)

    def test_hypercube_to_hypersphere_surface_5D_half(self):
        n_points = 1_000_000
        np.random.seed(666)
        hc = np.random.uniform(0, 1, (n_points, 5))
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=True)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 2)
        self.assertEqual(hs.shape, (n_points, 5+1))
        assert_almost_equal(np.linalg.norm(hs, axis=1), 1)

        # make sure all quadrants contain approximately the same number of data points
        tolerance_fraction = 0.01
        for quadrant_signs in itertools.product(*list(np.tile([-1, 1], (5+1, 1)))):
            in_quadrant = np.all(hs * quadrant_signs > 0, axis=1).sum()
            if quadrant_signs[0] == -1:
                self.assertEqual(np.sum(in_quadrant), 0)
            else:
                min = n_points / 2**5 * (1-tolerance_fraction)
                max = n_points / 2**5 * (1+tolerance_fraction)
                msg = f'Expected a value between {min:.0f} and {max:.0f} in quadrant {quadrant_signs}, but was {in_quadrant}'
                self.assertTrue(min <= np.sum(in_quadrant) <= max, msg)

    def test_hypercube_to_hypersphere_surface_6D_full(self):
        n_points = 1_000_000
        np.random.seed(666)
        hc = np.random.uniform(0, 1, (n_points, 6))
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=False)
        # hs = np.random.normal(0, 1, hs.shape)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 2)
        self.assertEqual(hs.shape, (n_points, 6+1))
        assert_almost_equal(np.linalg.norm(hs, axis=1), 1)

        # make sure all quadrants contain approximately the same number of data points
        tolerance_fraction = 0.03
        for quadrant_signs in itertools.product(*list(np.tile([-1, 1], (6+1, 1)))):
            in_quadrant = np.all(hs * quadrant_signs > 0, axis=1).sum()
            min = n_points / 2**(6+1) * (1-tolerance_fraction)
            max = n_points / 2**(6+1) * (1+tolerance_fraction)
            msg = f'Expected a value between {min:.0f} and {max:.0f}, but was {in_quadrant}'
            self.assertTrue(min <= np.sum(in_quadrant) <= max, msg=msg)

    def test_hypercube_to_hypersphere_surface_6D_half(self):
        n_points = 1_000_000
        np.random.seed(666)
        hc = np.random.uniform(0, 1, (n_points, 6))
        hs = hypercube_to_hypersphere_surface(hc, half_hypersphere=True)

        # check dimensionality and norms
        self.assertEqual(hs.ndim, 2)
        self.assertEqual(hs.shape, (n_points, 6+1))
        assert_almost_equal(np.linalg.norm(hs, axis=1), 1)

        # make sure all quadrants contain approximately the same number of data points
        tolerance_fraction = 0.03
        for quadrant_signs in itertools.product(*list(np.tile([-1, 1], (6+1, 1)))):
            in_quadrant = np.all(hs * quadrant_signs > 0, axis=1).sum()
            if quadrant_signs[0] == -1:
                self.assertEqual(np.sum(in_quadrant), 0)
            else:
                min = n_points / 2**6 * (1-tolerance_fraction)
                max = n_points / 2**6 * (1+tolerance_fraction)
                msg = f'Expected a value between {min:.0f} and {max:.0f} in quadrant {quadrant_signs}, but was {in_quadrant}'
                self.assertTrue(min <= np.sum(in_quadrant) <= max, msg)


import numpy as np

def logistic(u):
    """
    Compute the logistic function on a set of inputs

    Parameters
    ----------
    u: ndarray(N)
        A set of inputs to the logistic function

    Returns
    -------
    ndarray(N): logistic function outputs
    """
    return 1/(1+np.exp(-u))

def logistic_deriv(u):
    """
    Compute the logistic function's derivative on a set of inputs

    Parameters
    ----------
    u: ndarray(N)
        A set of inputs to the logistic function

    Returns
    -------
    ndarray(N): Derivatives of logistic function at these input
    """
    res = logistic(u)
    return res*(1-res)

def leaky_relu(u):
    """
    Compute the leaky ReLU on a set of inputs

    Parameters
    ----------
    u: ndarray(N)
        A set of inputs to the Leaky ReLU function

    Returns
    -------
    ndarray(N): Leaky ReLU outputs
    """
    res = np.zeros_like(u)
    res[u > 0] = u[u > 0]
    ## TODO: Finish this
    return res

def leaky_relu_deriv(u):
    """
    Compute the leaky ReLU's derivative on a set of inputs

    Parameters
    ----------
    u: ndarray(N)
        A set of inputs to the Leaky ReLU

    Returns
    -------
    ndarray(N): Derivatives of Leaky ReLU at these inputs
    """
    res = np.zeros_like(u)
    ## TODO: Finish this
    return res ## TODO: This is a dummy value

def softmax(u):
    """
    Compute the softmax of an input

    Parameters
    ----------
    u: ndarray(N)
        Inputs to softmax

    Returns
    -------
    ndarray(N)
        The softmax of the input at every dimension
    """
    ## TODO: Fill this in with a numerically stable
    ## version of softmax
    return 0*u ## TODO: this is a dummy value


from scipy.linalg import svd
import numpy as np
import logging

from mla.base import BaseEstimator

np.random.seed(1000)

class PCA(BaseEstimator):
    y_required = False

    def __init__(self, n_components, solver="svd"):
        """Principal component analysis (PCA) implementation.

        Transforms a dataset of possibly correlated values into n linearly
        uncorrelated components. The components are ordered such that the first
        has the largest possible variance and each following component as the
        largest possible variance given the previous components. This causes
        the early components to contain most of the variability in the dataset.

        Parameters
        ----------
        n_components : int
        solver : str, default 'svd'
            {'svd', 'eigen'}
        """
        self.solver = solver
        self.n_components = n_components
        self.components = None
        self.mean = None

    def fit(self, X, y=None):
        self.mean = np.mean(X, axis=0)
        self._decompose(X)

    def _decompose(self, X):
        # Mean centering
        X = X.copy()
        X -= self.mean

        if self.solver == "svd":
            _, s, Vh = svd(X, full_matrices=True)
        elif self.solver == "eigen":
            s, Vh = np.linalg.eig(np.cov(X.T))
            Vh = Vh.T

        s_squared = s ** 2
        variance_ratio = s_squared / (s_squared).sum()
        logging.info("Explained variance ratio: %s" % (variance_ratio[0 : self.n_components]))
        self.components = Vh[0 : self.n_components]

    def transform(self, X):
        X = X.copy()
        X -= self.mean
        return np.dot(X, self.components.T)

    def _predict(self, X=None):
        return self.transform(X)


#!/usr/bin/env python3
"""
Create bifurcation diagrams.

Basis for solving exercises 10.2.3 - 10.2.8 in Strogatz'
`Nonlinear Dynamics and Chaos`.

There several implemented maps which can be selected by setting the
command-line flag `--map` which accepts an integer:
0: logisitc map
    f(x) = r*x*(1-x)
1: standard period doubling route to chaos
    f(x) = x * exp(-r * (1 - x))
2: one period-doubling bifurcation and the show is over
    f(x) = exp(-r * x)
3: period-doubling and chaos galore
    f(x) = r * cos(x)
4: nasty mess
    f(x) = r * tan(x)
5: attractors sometimes come in symmetric pairs
    f(x) = r * x - x^3
"""
import argparse as ap
import math
import matplotlib.pyplot as plt
import multiprocessing as mp
import numpy as np

from matplotlib.colors import LinearSegmentedColormap

def setup_cli():
    """Setup the command-line interface."""
    parser = ap.ArgumentParser(description=__doc__, formatter_class=ap.RawTextHelpFormatter)
    parser.add_argument('-0', '--x0', type=float, default=0.5,
                        help="The initial value of the map.")
    parser.add_argument('--r-min', type=float, default=2.8,
                        help="The minimal value of the bifurcation parameter.")
    parser.add_argument('--r-max', type=float, default=4.0,
                        help="The maximal value of the bifurcation parameter.")
    parser.add_argument('--r-points', type=int, default=2000,
                        help="The number of bifurcation parameter points to sample between "
                        "the minimal and maximal values.")
    parser.add_argument('--skip', type=int, default=600,
                        help="The number of iterations to skip to let the map reach its final state.")
    parser.add_argument('-n', '--points-to-draw-after-skip', type=int, default=200,
                        help="The number of points to draw after the skipping period.")
    parser.add_argument('--dpi', type=int, default=350, help='The image resolution in dpi.')
    parser.add_argument('--n-cpus', type=int, default=1, help="The number of CPUs to use.")
    parser.add_argument('--marker-size', type=float, default=0.02, help="The marker-size.")
    parser.add_argument('--marker', type=str, default='o', help="The marker type.")
    parser.add_argument('--map', type=int, default=0, help="An integer indicating which map to use [Default: logistic map].")

    return parser.parse_args()

def logistic_map(x, r):
    return r * x * (1.0 - x)

def std_period_doubling_route_to_chaos(x, r):
    return x * math.exp(-r * (1-x))

def one_period_doubling_bifurcation_and_show_is_over(x, r):
    return math.exp(-r * x)

def period_doubling_and_chaos_galore(x, r):
    return r * math.cos(x)

def nasty_mess(r, x):
    return r * math.tan(x)

def symmetric_pairs(r, x):
    return r * x - x**3

def get_points_to_draw(f, x0: float, skip: int, n: int, r: float) -> list:
    """
    Get the points to draw in the bifurcation diagram.

    Parameters
    ----------
    f: Callable
        The map function
    x0: float
        The initial conidition of the map.
    skip: int
        The number of iteration values to skip after the initial condition
        to let the map reach its final state.
    n: int
        The number of points to generate (and to return for plotting) after
        reaching the final state configuation.
    r: float
        The bifurcation parameter.

    Returns
    -------
    list
        A list of point pairs to plot in the bifurcation diagram for the given value `r`
        of the bifurcation parameters.
    """

    x = x0
    # skip the initial values
    for _ in range(skip):
        x = f(x, r)

    # store the values that should be plotted
    output = []
    for _  in range(n):
        x = f(x, r)
        output.append((r, x))

    return output

class PoolFunc:
    """
    A callable with a set of internal parameters that can be used by the thread pool.
    """
    def __init__(self, f, x0, skip, n):
        self._f = f
        self._x0 = x0
        self._skip = skip
        self._n = n

    def __call__(self, r):
        """
        On a given thread, generate the list of points to plot for a specified bifurcation parameter value `r`.
        """
        # Delegate the work to the function `get_points_to_draw`.
        return get_points_to_draw(self._f, self._x0, self._skip, self._n, r)

if __name__ == "__main__":
    # create the colormap for the attractor
    colors = ['#5e81ab', '#81a1c0', '#88c0d1', '#81a1c0']
    cmap = LinearSegmentedColormap.from_list('blueish', colors)

    # define the mapping functions
    mappings = {
        0: logistic_map,
        1: std_period_doubling_route_to_chaos,
        2: one_period_doubling_bifurcation_and_show_is_over,
        3: period_doubling_and_chaos_galore,
        4: nasty_mess,
        5: symmetric_pairs
    }

    # parse the command-line arguments
    args = setup_cli()

    # create the r-values
    r_values = np.linspace(args.r_min, args.r_max, args.r_points)

    values = []
    func = PoolFunc(mappings[args.map], args.x0, args.skip, args.points_to_draw_after_skip)
    with mp.Pool(args.n_cpus) as pool:
        results = pool.map(func, r_values)

    for r in results:
        values += r

    print("Computed values.")

    plt.rcParams["figure.facecolor"] = '#2e3440'
    fig, ax = plt.subplots(figsize=(16, 9), dpi=args.dpi)
    # ax.scatter([v[0] for v in values], [v[1] for v in values], c='#88c0d0', marker=args.marker, s=args.marker_size)
    ax.scatter([v[0] for v in values], [v[1] for v in values], c=[v[1] for v in values], cmap=cmap, marker=args.marker, s=args.marker_size)
    plt.axis('off')
    plt.tight_layout()
    plt.savefig('bifurcation.jpg', bbox_inches='tight', transparent=False, dpi=args.dpi, pad_inches=0)


from abc import abstractmethod
from typing import Iterable

import numpy as np
from sklearn.decomposition import PCA

class _EOF_base():

    def __init__(
        self,
        X: Iterable[np.ndarray],
        n_modes=None,
        norm=False
    ):

        if norm:
            X /= X.std(axis=0)

        self.n_samples = X.shape[0]
        self.n_features = X.shape[1]

        self.n_modes = n_modes
        if n_modes is None:
            self.n_modes = min(self.n_samples, self.n_features)

        # TODO: weights

        self.X = X

    def solve(self) -> None:
        '''
        Perform the EOF analysis.

        To boost performance, the standard solver is based on
        the PCA implementation of scikit-learn [1]_ which uses different algorithms
        to perform the decomposition based on the data matrix size.

        Naive approaches using singular value decomposition of the
        data matrix ``X (n x p)`` or the covariance matrix ``C (p x p)``
        quickly become infeasable computationally when the number of
        samples :math:`n` or features :math:`p` increase (computational power increases
        by :math:`O(n^2p)` and :math:`O(p^3)`, respectively.)

        References
        ----------
        .. [1] https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html

        '''

        pca = PCA(n_components=self.n_modes)
        self._pcs = pca.fit_transform(self.X)
        self._singular_values = pca.singular_values_
        self._explained_variance = pca.explained_variance_
        self._explained_variance_ratio  = pca.explained_variance_ratio_
        self._eofs = pca.components_.T

        # Consistent signs for deterministic output
        maxidx = [abs(self._eofs).argmax(axis=0)]
        flip_signs = np.sign(self._eofs[maxidx, range(self._eofs.shape[1])])
        self._eofs *= flip_signs
        self._pcs *= flip_signs

    def singular_values(self) -> np.ndarray:
        '''Get the singular values.

        The `i` th singular value :math:`\sigma_i` is defined by

        .. math::
           \sigma_i = \sqrt{n \lambda_i}

        where :math:`\lambda_i` and :math:`n` are the associated eigenvalues
        and the number of samples, respectively.

        '''

        return self._singular_values

    def explained_variance(self):
        '''Get the explained variance.

        The explained variance is simply given by the individual eigenvalues
        of the covariance matrix.

        '''

        return self._explained_variance

    def explained_variance_ratio(self):
        '''Get the explained variance ratio.

        The explained variance ratio is the fraction of total variance
        explained by a given mode and is calculated by :math:`\lambda_i / \sum_i^m \lambda_i`
        where `m` is the total number of modes.

        '''

        return self._explained_variance_ratio

    def eofs(self):
        '''Get the EOFs.

        The empirical orthogonal functions (EOFs) are equivalent to the eigenvectors
        of the covariance matrix of `X`.

        '''

        return self._eofs

    def pcs(self):
        '''Get the PCs.

        The principal components (PCs), also known as PC scores, are computed
        by projecting the data matrix `X` onto the eigenvectors.

        '''

        return self._pcs


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from IPython.display import HTML
plt.rcParams['figure.figsize'] = [8, 8]
plt.rcParams.update({'font.size': 18})
#plt.rcParams['animation.html'] = 'jshtml'

dx = 0.01
L = 10
x = np.arange(0, L+ dx, dx)
n = len(x)
nquart = int(np.floor(n/4))

f = np.zeros_like(x)
f[nquart:3*nquart] = 1

A0 = np.sum(f * np.ones_like(x)) * dx * 2 / L
fFS = A0/2 * np.ones_like(f)

fig, ax = plt.subplots()
plt.plot(x, f, color='k', LineWidth=2)
fFS_plot, = plt.plot([], [], color='r', LineWidth=2)

all_fFS = np.zeros((len(fFS), 101))
all_fFS[:,0] = fFS

for k in range(1, 101):
    Ak = np.sum(f * np.cos(2 * np.pi * k * x / L)) * dx * 2 / L
    Bk = np.sum(f * np.sin(2 * np.pi * k * x / L)) * dx * 2 / L
    fFS = fFS + Ak*np.cos(2 * k * np.pi * x / L) + Bk *np.sin(2 * k * np.pi * x / L)
    all_fFS[:, k] = fFS

def init():
    ax.set_xlim(x[0], x[-1])
    ax.set_ylim(-0.2, 1.2)
    return fFS

def animate(iter):
    fFS_plot.set_data(x, all_fFS[:, iter])
    return fFS_plot

anim = animation.FuncAnimation(fig, animate, init_func=init, frames=101, interval=100, repeat=False)
#HTML(anim.to_jshtml())
plt.show()
anim.save('gibbs-phenomenon.gif', writer='imagemagick')


import scipy.linalg
import numpy as np
import numexpr as ne

def sym_decorrelation_ne(W):
    """ Symmetric decorrelation """
    K = np.dot(W, W.T)
    s, u = np.linalg.eigh(K)
    return (u @ np.diag(1.0/np.sqrt(s)) @ u.T) @ W
# logcosh
def g_logcosh_ne(wx,alpha):
    """derivatives of logcosh"""
    return ne.evaluate('tanh(alpha * wx)')
def gprime_logcosh_ne(wx,alpha):
    """second derivatives of logcosh"""
    return alpha * (1-ne.evaluate('tanh(alpha*wx)**2'))
# exp
def g_exp_ne(wx,alpha):
    """derivatives of exp"""
    return ne.evaluate('wx * exp(-wx**2/2)')
def gprime_exp_ne(wx,alpha):
    """second derivatives of exp"""
    return (1-np.square(wx)) * ne.evaluate('exp(-wx**2/2)')

def fastICA_ne(X, f,alpha=None,n_comp=None,maxit=200, tol=1e-04):
    n,p = X.shape
    #check if n_comp is valid
    if n_comp is None:
        n_comp = min(n,p)
    elif n_comp > min(n,p):
        print("n_comp is too large")
        n_comp = min(n,p)

    #centering
    #by subtracting the mean of each column of X (array).
    X = X - X.mean(axis=0)[None,:]
    X = X.T

    #whitening
    s = np.linalg.svd(X @ (X.T) / n)
    D = np.diag(1/np.sqrt(s[1]))
    k = D @ (s[0].T)
    k = k[:n_comp,:]
    X1 = k @ X

    # initial random weght vector
    w_init = np.random.normal(size=(n_comp, n_comp))
    W = sym_decorrelation_ne(w_init)

    lim = 1
    it = 0

    # The FastICA algorithm
    while lim > tol and it < maxit :
        wx = W @ X1
        if f =="logcosh":
            gwx = g_logcosh_ne(wx,alpha)
            g_wx = gprime_logcosh_ne(wx,alpha)
        elif f =="exp":
            gwx = g_exp_ne(wx,alpha)
            g_wx = gprimeg_exp_ne(wx,alpha)
        else:
            print("doesn't support this approximation negentropy function")
        W1 = np.dot(gwx,X1.T)/X1.shape[1] - np.dot(np.diag(g_wx.mean(axis=1)),W)
        W1 = sym_decorrelation_ne(W1)
        it = it +1
        lim = np.max(np.abs(np.abs(np.diag(W1 @ W.T))) - 1.0)
        W = W1

    S = W @ X1
    A = scipy.linalg.pinv2(W @ k)
    return{'X':X1.T,'A':A.T,'S':S.T}

import numpy,import scipy,import numexpr


import numpy as np

def identity_function(x):
    return x

def leaky_relu(x):
    return np.max(0.1 * x, x)

def relu(x):
    return np.max(0, x)

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

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

def step_function(x):
    return np.array(x > 0, dtype=np.int)

def softmax(x):
    if x.ndim == 2:
        x = x.T
        x -= np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T

    x -= np.max(x)  # 오버플로 대책
    return np.exp(x) / np.sum(np.exp(x))


# coding=utf-8
from collections import Counter
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix

report_columns = ['Class', 'TP', 'TN', 'FP', 'FN', 'Support',
                  'Precision', 'Recall', 'F1score', 'Accuracy']

def accuracy(tp, tn, fp, fn):
    return tp / (tp + fn)

def precision(tp, tn, fp, fn):
    return tp / (tp + fp)

def recall(tp, tn, fp, fn):
    return tp / (tp + fn)

def fb1(precision, recall):
    return 2 * precision * recall / (precision + recall)

def overall_acc(tp, tn, fp, fn):
    return np.sum(tp) / np.sum(tp + fn)

def a_p_r_f(tp, tn, fp, fn):
    acc = accuracy(tp, tn, fp, fn)
    prec =  np.nan_to_num(precision(tp, tn, fp, fn))
    rec = np.nan_to_num(recall(tp, tn, fp, fn))
    fscore =  np.nan_to_num(fb1(prec, rec))
    return acc, prec, rec, fscore

def classification_report(y_true, y_pred, label_list, drop_false=False,
                         micro=True, macro=True):
    #search false label position.
    if drop_false:
        for i, l in enumerate(label_list):
            if 'false' in l:
                false_id = i

    supports = Counter(y_true)
    supports = np.array([supports.get(i, 0) for i in range(len(set(y_true) | set(y_pred)))])

    cm = confusion_matrix(y_true, y_pred)

    tps = np.diag(cm) # TP
    fps = []
    fns = []
    for i, tp in enumerate(tps):
        fps.append(cm[:, i].sum() - tp) # FP
        fns.append(cm[i].sum() - tp) # FN
    fps = np.array(fps)
    fns = np.array(fns)

    df = pd.DataFrame({
                'Class': 0,
                'TP': tps,
                'TN': len(y_true) - tps - fps - fns,
                'FP': fps,
                'FN': fns,
                'Support': supports}).astype(int)

    df['Class'] = label_list
    if macro:
        macro_metrics = []
        acc, prec, rec, fscore = a_p_r_f(df.TP, df.TN, df.FP, df.FN)
        row = ['macro_include_false'] + [''] * 5
        row += [np.average(t) for t in [prec, rec, fscore, acc]]
        macro_metrics.append(pd.Series(row, index=report_columns))
        if drop_false:
            df_d = df.drop(false_id, axis=0)
            acc, prec, rec, fscore = a_p_r_f(df_d.TP, df_d.TN, df_d.FP, df_d.FN)
            row = ['macro_drop_false'] + [''] * 5
            row += [np.average(t) for t in [prec, rec, fscore, acc]]
            macro_metrics.append(pd.Series(row, index=report_columns))

    if micro:
        micro_metrics = []
        micro_include_false = df.sum()
        micro_include_false['Class'] = 'micro_include_false'
        micro_metrics.append(micro_include_false)
        if drop_false:
            micro_drop_false = df.drop(false_id, axis=0).sum()
            micro_drop_false['Class'] = 'micro_drop_false'
            micro_metrics.append(micro_drop_false)

        df = df.append(micro_metrics)

    # compute micro_metrics
    acc, prec, rec, fscore = a_p_r_f(df.TP, df.TN, df.FP, df.FN)

    df['Precision'] = prec
    df['Recall'] = rec
    df['F1score'] = fscore
    df['Accuracy'] = acc

    if macro:
        df = df.append(macro_metrics)

    return df.reset_index(drop=True)


import numpy as np
from functools import partial

from problem import (
    generate_objective_function,
    generate_objective_gradient_function,
)
from constraints import (
    generate_constraints_function,
    generate_constraint_gradients_function,
)
from methods import BFGS, MaximumIterationError, gradient_descent
from plotting import path_figure

def generate_quadratically_penalized_objective(robot_arm):
    '''
    Given a RobotArm object with a valid joint lenghts and destinations
    this function returns a quadratically penalized objective function
    taking in two parameters: thetas and mu
    Function: R^(ns) x R --> R
    '''
    n = robot_arm.n
    s = robot_arm.s
    objective = generate_objective_function(robot_arm)
    constraints_func = generate_constraints_function(robot_arm)

    def quadratically_penalized_objective(thetas, mu):
        if not thetas.shape == (n * s,):
            raise ValueError('Thetas is not given as 1D-vector, but as: ' + \
                             str(thetas.shape))

        return objective(thetas) + \
               0.5 * mu * np.sum(constraints_func(thetas) ** 2)

    return quadratically_penalized_objective

def generate_quadratically_penalized_objective_gradient(robot_arm):
    n = robot_arm.n
    s = robot_arm.s

    objective_gradient = generate_objective_gradient_function(robot_arm)

    constraints_func = generate_constraints_function(robot_arm)
    constraint_gradients_func = generate_constraint_gradients_function(robot_arm)

    def quadratic_constraint_gradients(thetas):
        if not thetas.shape == (n * s,):
            raise ValueError('Thetas is not given as 1D-vector, but as: ' + \
                             str(thetas.shape))

        constraint_gradients = constraint_gradients_func(thetas)
        constraints = constraints_func(thetas)
        assert constraint_gradients.shape == (n*s, 2*s,)
        assert constraints.shape == (2*s,)
        return constraints.reshape((1, 2*s,)) * constraint_gradients

    def quadratically_penalized_objective_gradient(thetas, mu):
        if not thetas.shape == (n * s,):
            raise ValueError('Thetas is not given as 1D-vector, but as: ' + \
                             str(thetas.shape))

        grad = objective_gradient(thetas)
        constraint_gradients = 0.5 * mu * quadratic_constraint_gradients(thetas)
        return grad + 0.5 * mu * np.sum(constraint_gradients, axis=1)

    return quadratically_penalized_objective_gradient

def quadratic_penalty_method(robot_arm):
    n = robot_arm.n
    s = robot_arm.s

    Q = generate_quadratically_penalized_objective(robot_arm)
    grad_Q = generate_quadratically_penalized_objective_gradient(robot_arm)

    thetas = robot_arm.generate_initial_guess(show=False)

    mu = 1
    tau = 1e-4

    def get_f(mu):
        return partial(Q, mu=mu)

    def get_grad_f(mu):
        return partial(grad_Q, mu=mu)

    print('Starting quadratic penalty method')
    for yolo in range(1000):
        f = get_f(mu)
        grad_f = get_grad_f(mu)
        try:
            thetas = BFGS(thetas, f, grad_f, tau)
        except MaximumIterationError:
            print('Reached MaximumIterationError in loop number {}'.format(yolo))
            break
        mu = 1.5 * mu
        tau = 0.5 * tau

    return thetas


"""
Decibel
=======

Provides
  1. Basic calculations with levels.

"""
import numpy as np

def dbsum(levels, axis=None):
    """Summation of levels.

    Parameters
    ----------

    Notes
    -----
    .. math:: L_{sum} = 10 \\log_{10}{\\sum_{i=0}^n{10^{L/10}}}
    """

    levels = np.asanyarray(levels)
    return 10.0 * np.log10((10.0**(levels / 10.0)).sum(axis=None))

def dbmean(levels, axis=None):
    """Energetic average of levels.

    Parameters
    ----------
    levels : list, nparray
        Sequence of levels.

    Notes
    -----
    .. math:: L_{mean} = 10 \\log_{10}{\\frac{1}{n}\\sum_{i=0}^n{10^{L/10}}}
    """

    levels = np.asanyarray(levels)
    return 10.0 * np.log10((10.0**(levels / 10.0)).mean(axis=axis))

def dbsub(a, b):
    """Energitally subtract level b from level a.

    Parameters
    ----------
    a : float
        Level.
    b : float
        Level.

    Notes
    -----
    .. math:: L_{a-b} = 10 \\log_{10}{10^{L_a/10}-10^{L_b/10}}
    Energitally subtract b from a.
    """
    a = np.asanyarray(a)
    b = np.asanyarray(b)
    return 10.0 * np.log10(10.0**(a / 10.0) - 10.0**(b / 10.0))

def dbadd(a, b):
    """Energetic addition of levels.

    Parameters
    ----------
    a : float
        Single level or sequence of levels.
    b : float
        Single level or sequence of levels.

    Notes
    -----
    .. math:: L_{a+b} = 10 \\log_{10}{10^{L_b/10}+10^{L_a/10}}
    Energetically adds b to a.
    """
    a = np.asanyarray(a)
    b = np.asanyarray(b)
    return 10.0 * np.log10(10.0**(a / 10.0) + 10.0**(b / 10.0))


""" Class defintion for the Franklin Reiter related message attack

    Much of the code in this file is derived from the writeup
    located at: ahack.ru/write-ups/ructf-quals-14.htm
"""
sage_present = True
try:
    from sage.all_cmdline import *
except ImportError:
    sage_present = False

from RSAExploits.exploits.exploit import Exploit

class Franklin_Reiter(Exploit):
    """ Class providing a run interface to Franklin Reiter's attack """

    def run(self, rsadata_list, info_dict = None):
        """ Recovers plaintext message using Franklin-Reiter's attack

            This attack should work as long as the provided parameters
            are correct. However, if the public exponent e is not small,
            it may run for a long time.

            Args:
                rsadata_list: A list of RSA_Data objects on which to attempt the
                              boneh durfee exploit
                info_dict: Specifies the relationship between the two plaintext
                           messages as a polynomial under the key "Franklin_Reiter"

            Side Effects:
                If a message is recovered, the corresponding field in the
                RSA_Data objects will be set

            Return:
                True if the attack completes
        """

        global sage_present
        success = False

        if not sage_present:
            print "Franklin Reiter: Sage required, skipping exploit"
            return success

        if len(rsadata_list) != 2:
            print "Franklin Reiter: Exactly 2 RSA_Data objects must be provided"
            return success

        if info_dict == None or "Franklin_Reiter" not in info_dict:
            print "Franklin Reiter: Polynomial relationship between messages required"
            return success

        print ("Franklin Reiter: Running Attack...")
        success = True
        n = rsadata_list[0].get_n()
        e = rsadata_list[0].get_e()
        c1 = rsadata_list[0].get_c()
        c2 = rsadata_list[1].get_c()
        poly = info_dict["Franklin_Reiter"]
        msg = Franklin_Reiter.franklin_reiter(n, e, c1, c2, poly)
        rsadata_list[0].set_m(msg)
        rsadata_list[1].set_m(msg)
        print ("Franklin Reiter: Message recovered")
        return success

    @staticmethod
    def franklin_reiter(n, e, c1, c2, poly):
        """ Recover the plaintext message using franklin-reiter attack """
        x = PolynomialRing(ZZ.quo(n*ZZ), 'x').gen()
        poly1 = poly**e - c1
        poly2 = x**e-c2
        coefs = Franklin_Reiter.poly_gcd(poly1, poly2)
        msg = int(-pow(coefs[1], -1, n) * coefs[0])
        return msg

    @staticmethod
    def poly_gcd(poly1, poly2):
        """ Compute the gcd of two polynomials """
        a = poly1
        b = poly2
        i = 0

        while True:
            r = a % b
            if r == 0:
                c = rp.coeffs()
                return c
            rp = r
            a, b = b, r
            i += 1


from scipy.optimize import minimize
from scipy.optimize import LinearConstraint
from matplotlib import pyplot as plt
import numpy as np
import datetime

class CHFitter(object):
    '''

    Description
    -----------------------------------------

    This class provides methods to estimate the CH function as presented in the following paper:

    Title: "Forecasting life expectancy: Evidence from a new survival function."
    By: Wong, Chi Heem, and Albert K. Tsui.
    Journal: Insurance: Mathematics and Economics 65 (2015): 208-226.

    Please cite the paper if you used this code in your work!

    @version: 2021-Jan-08
    @author: Chi Heem, Wong

    ...

    Methods
    -----------------------------------------
    - set_params(alpha1, beta1, gamma1, alpha2, beta2, gamma2)
        Manually set parameters to the CH function

    - fit(df, sx_col, age_col, x0=None, plot_fit=False)
         Estimate the parameters of the CH function from data

    - predict(x)
        Predicts the S(x) given by the CH function

    - print_summary
        Prints a summary of the estimation (if model was fitted) and
        the parameters of CH function

    Example usage
    --------
    .. code:: python
        from CHFitter import CHFitter
        chf = CHFitter()
        chf.fit(df, 'sx', 'age', plot_fit=True)
        chf.print_summary()

    '''
    def __init__(self, ):
        '''
        Constructor
        '''
        # Function parameters
        self.params = None

        # Diagnostics info
        self._params_estimated = False
        self._params_set = False
        self._time_fit_was_called = None
        self._time_taken = None
        self._Sx_col = None
        self._age_col = None
        self._n = None

        self._r_squared = None # R-squred
        self._mse = None # mean squared error
        self._rss = None # sum of squared residuals
        self._mape = None # mean absolute percentage error

    def fit(self, df, sx_col, age_col, x0=None, plot_fit=False):
        '''
        Estimate the parameters of the CH function from data

        Parameters
        -------------------
        df: DataFrame
            a Pandas DataFrame with necessary columns `sx_col` and
            `age_col` (see below). `sx_col` refers to the survival probabilities (y-axis).
            `age_col` refers to the age axis (x-axis).

        sx_col: string
            the name of the column in DataFrame that contains the survival probabilities.

        age_col: string
            the name of the column in DataFrame that contains the ages.

        x0 (optional): list
            the initial conditions of minimization. must be of the format "[alpha1, beta1, gamma1, alpha2, beta2, gamma2]"

        plot_fit (optional): Bool
            option to plot the estimated CH function against data points (default = False)

        Returns
        -------
        params: Numpy array
            estimated parameters of the CH function

        '''
        if x0 is None:
            self.params = [0.4, 65, 4, 2, 80, 4]
        else:
            self.params = x0

        self._time_fit_was_called = datetime.datetime.utcnow()
        self._Sx_col = sx_col
        self._age_col = age_col

        sx = df[sx_col]; ages=df[age_col]
        self._n = len(sx)
        x0 = self.params

        # Set constraints
        LB = np.ones(7)*1e-6
        UB = np.ones(7)*100
        X = np.eye(7,6)
        X[-1,:] = [-1, 0, 0, 1, 0, 0]
        linear_constraint = LinearConstraint(X, LB, UB)

        # Minimize
        def loss(params, ages, sx):
            sx_het = params[0]*np.exp(-np.exp(np.power(ages/params[1],params[2]))) + params[3]*np.exp(-np.cosh(np.power(ages/params[4],params[5])))
            diff = sx_het-sx
            return np.matmul(np.transpose(diff), diff)/len(sx)

        res = minimize(loss,
                     x0,
                     method='trust-constr',
                     args=(ages, sx),
                     #jac=self.jac,
                     constraints=linear_constraint,
                     )
        self._params_estimated = True
        self._time_taken = datetime.datetime.utcnow() - self._time_fit_was_called

        # Store estimated parameters
        self.params= res.x

        # Obtain and store statistics
        sx_het = self.predict(ages)
        err = sx - sx_het
        self._rss = np.sum(np.power(err, 2))
        ape = np.abs(err/sx)
        n = np.sum(np.where(ape==np.inf, 0, 1))
        ape[ape == np.inf] = 0 # Handle cases where sx=0
        self._mape = np.nansum(np.abs(ape))/n
        self._r_squared = 1- self._rss/np.sum(np.power(sx-np.mean(sx),2))
        self._mse = res.fun

        if plot_fit==True:
            plt.scatter(ages, sx)
            plt.plot(ages, sx_het)
            plt.legend(['Data points', 'Fitted CH function'])
            plt.show()
        return self.params

    def predict(self, x):
        '''
        Predicts the S(x) given by the CH function given a Numpy array, x

        Parameters
        -------------------
        x: Numpy array
            points that require predictions

        Returns
        -------
        S_x: Numpy array
            values from the CH function
        '''
        if (self._params_estimated or self._params_estimated):
            return self.params[0]*np.exp(-np.exp(np.power(x/self.params[1],self.params[2]))) \
                    + self.params[3]*np.exp(-np.cosh(np.power(x/self.params[4],self.params[5])))
        else:
            raise Exception('Parameters for the model are not set. Please set parameters or fit model to data.')

    def set_params(self, alpha1, beta1, gamma1, alpha2, beta2, gamma2):
        '''
        Manually set parameters to the CH function. Useful if you know the
        parameters and want to do predictions.

        Parameters
        -------------------
        alpha1: float
            alpha1 in the CH function
        beta1: float
            beta1 in the CH function
        gamma1: float
            gamma1 in the CH function
        alpha2: float
            alpha2 in the CH function
        beta2: float
            beta2 in the CH function
        gamma2: float
            gamma2 in the CH function
        '''
        self.params = [alpha1, beta1, gamma1, alpha2, beta2, gamma2]
        self._params_estimated = True

    def print_summary(self,):
        '''
        Prints a summary of the estimation (if model was fitted) and
        the parameters of CH function
        '''
        if self._params_estimated:
            print(''.join([''.join('-') for _ in range(50)]))
            print('Fitted on %s in %.3f seconds' %((self._time_fit_was_called.strftime("%Y-%m-%d %H:%M:%S") + " UTC"), self._time_taken.total_seconds()))
            print('Age variable : "%s"'%self._age_col)
            print('Sx variable  : "%s"' %self._Sx_col)
            print('N            : %d' %self._n)
            print(''.join([''.join('-') for _ in range(50)]))
            print('R-squared                             : %f' %self._r_squared)
            print('Mean squared error (MSE)              : %f' %self._mse)
            print('Residual sum of squares (RSS)         : %f' %self._rss)
            print('Mean absolute percentage error (MAPE) : %f' %self._mape)
            print(''.join([''.join('-') for _ in range(50)]))
        print('Parameters')
        print('alpha1 : %.5f' %self.params[0])
        print('beta1  : %.5f' %self.params[1])
        print('gamma1 : %.5f' %self.params[2])
        print('alpha1 : %.5f' %self.params[3])
        print('beta2  : %.5f' %self.params[4])
        print('gamma2 : %.5f' %self.params[5])
        print(''.join([''.join('-') for _ in range(50)]))


# coding=utf-8
# uliontse

import numpy as np
import pandas as pd
from sympy import Symbol,diff
import matplotlib.pyplot as plt

def generate_1d(expr_or_poly1d,init_x=-0.5,step=0.01,num_iters=1e4,showPlot=False,xlimArr=None,ylimArr=None):
    '''
    :param expr_or_poly1d: str or array1d_like,
    :param init_x: float,
    :param step: float,
    :param num_iters: int or float,
    :param showPlot: boolean,
    :param xlimArr: tuple, affect plot_x:
    :param ylimArr: tuple,
    :return: dict,
    '''
    cur_x = np.array([init_x])[0] # avoid Overflow Error.
    step = np.array([step])[0]
    num_iters = int(num_iters)

    if isinstance(expr_or_poly1d,str):
        expr = expr_or_poly1d
        x = Symbol('x')
        diff_expr = str(diff(eval(expr),x))
        gradient = lambda x: np.array([eval(diff_expr)])[0]
        y = lambda x: np.array([eval(expr)])[0]

    else: # follow np.poly1d Error.
        L = expr_or_poly1d
        y = np.poly1d(L)
        gradient = np.poly1d.deriv(y)

    if xlimArr:
        plot_x = np.linspace(init_x-abs(xlimArr[0]),init_x+abs(xlimArr[1]),201)
    else:
        plot_x = np.linspace(init_x -5, init_x + 5, 201)
    plot_y = y(plot_x)
    # plt.ion()

    cur_num = 0
    for i in range(num_iters):
        cur_num += 1
        prev_x = cur_x
        cur_x -= step * gradient(prev_x)
        if abs(y(cur_x) - y(prev_x)) < np.array([1e-8])[0]:
            break
        if showPlot and i % 2 == 0:
            # plt.cla()
            plt.plot(plot_x, plot_y)
            plt.scatter(cur_x, y(cur_x), color='red')
            if xlimArr: plt.xlim(xlimArr)
            if ylimArr: plt.ylim(ylimArr)
            plt.pause(0.1)
    plt.ioff()
    plt.show()
    return {
        'X': cur_x,
        'Y': y(cur_x),
        'Gradient': gradient(cur_x),
        'Numloop': cur_num
    }

def generate_2d(dataFrame:pd.DataFrame,step=0.1,num_iters=1e4):
    '''
    :param dataFrame: pandas.DataFrame, like train_data with labels.
    :param step: float,
    :param num_iters: int or float,
    :return: dict,
    '''

    arr = np.hstack([np.ones((len(dataFrame),1)),dataFrame])
    X = pd.DataFrame(arr) # 列名未知
    Y = X.pop(X.shape[1]-1)

    init_theta = np.zeros(X.shape[1])
    cur_theta = init_theta
    num_iters = int(num_iters)

    def y(theta):
        try:
            return np.sum((Y - X.dot(theta)) ** 2) / len(Y)
        except:
            return float('inf')

    def gradient(theta):
        try:
            return X.T.dot(X.dot(theta) - Y) * 2 / len(X)
        except:
            return float('inf')

    cur_num = 0
    for i in range(num_iters):
        cur_num += 1
        prev_theta = cur_theta
        cur_theta -= step * gradient(prev_theta)
        if abs(y(cur_theta) - y(prev_theta)) < np.array([1e-8])[0]:
            break

    return {
        'theta': cur_theta.values,
        'Y': y(cur_theta),
        'Gradient': gradient(cur_theta).values,
        'Numloop': cur_num,
        'Linear_Intercept': cur_theta[0],
        'Linear_Coef': cur_theta[1:].values,
    }


#!/usr/bin/python

#-------------------------------------------------------------------------------
#License GPL v3.0
#Author: Alexandre Manhaes Savio <alexsavio@gmail.com>
#Grupo de Inteligencia Computational <www.ehu.es/ccwintco>
#Universidad del Pais Vasco UPV/EHU
#Use this at your own risk!
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#README:
#Transforms a NxN matrix volume (N^2 volumes in 4th dimension) into other measure maps.
#You can make a list of measures and they will be applied in order.
#A list of the implemented measures are listed below.

#Geodesic anisotropy equation was extracted from
#P. G. Batchelor et al. - A Rigorous Framework for Diffusion Tensor Calculus - Magnetic Resonance in Medicine 53:221-225 (2005)

# What is tensor denoising?
#Log-Euclidean tensor denoising was used to eliminate singular, negative definite, or rank-deficient tensors
#-------------------------------------------------------------------------------

#from IPython.core.debugger import Tracer; debug_here = Tracer()

import argparse, os, sys

from time import clock
import nibabel as nib
import numpy as np
from scipy.linalg import logm
from scipy.linalg.matfuncs import sqrtm
from numpy.linalg import det
from numpy.linalg import eigvals
from numpy.linalg import eigvalsh

#-------------------------------------------------------------------------------
#definining measure functions
def mylogm (v):
   return np.reshape(logm(v.reshape(N,N)), [1,N*N])
#-------------------------------------------------------------------------------

def mydet (v):
   return det(v.reshape(N,N))
#-------------------------------------------------------------------------------

def mytrace (v):
   return np.trace(v.reshape(N,N))
#-------------------------------------------------------------------------------

def myeigvals (v):
   return eigvals(v.reshape(N,N)).flatten()
#-------------------------------------------------------------------------------

def mymaxeigvals (v):
   return max (myeigvals(v))
#-------------------------------------------------------------------------------

def myeigvalsh (v):
   return eigvalsh(v.reshape(N,N)).flatten()
#-------------------------------------------------------------------------------

def mymaxeigvalsh (v):
   return max (myeigvalsh(v))
#-------------------------------------------------------------------------------

def mydeftensor (v):
   j = v.reshape([N,N])
   s = sqrtm(j.transpose()*j)
   return S.reshape([1,N*N])
#-------------------------------------------------------------------------------

def mygeodan (v):
   s = logm(v.reshape(N,N))
   return np.sqrt(np.trace(np.square(s - np.trace(s)/N * np.eye(N))))
#-------------------------------------------------------------------------------

def calculate_measures (funcs, data, odims):
   for i in range(len(funcs)):
      measure = funcs[i]
      odim    = odims[i]
      data    = measure(data)

   return data
#-------------------------------------------------------------------------------

def set_parser():
   parser = argparse.ArgumentParser(description='Transforms a NxN matrix volume (N^2 volumes in 4th dimension) into other measure maps. \n You can make a list of measures and they will be applied in order. \n A list of the implemented measures are listed below.', prefix_chars='-')
   parser.add_argument('-i', '--in', dest='infile', required=True,
                      help='Jacobian matrix volume (4DVolume with 9 volumes)')
   parser.add_argument('-m', '--mask', dest='maskfile', required=False,
                      help='Mask file')
   parser.add_argument('-o', '--out', dest='outfile', required=True,
                      help='Output file name')
   parser.add_argument('-N', '--dims', dest='dims', required=False, default=3, type=int,
                      help='Order of the matrices in the volume')
   parser.add_argument('--matlog', dest='funcs', action='append_const', const='matlog',
                        help='Matrix logarithm')
   parser.add_argument('--deftensor', dest='funcs', action='append_const', const='deftensor',
                        help='Deformation tensor S=sqrtm(J`*J)')
   parser.add_argument('--det', dest='funcs', action='append_const', const='det',
                        help='Determinant')
   parser.add_argument('--trace', dest='funcs', action='append_const', const='trace',
                        help='Trace')
   parser.add_argument('--eigvals', dest='funcs', action='append_const', const='eigvals',
                        help='Eigenvalues of a general matrix')
   parser.add_argument('--maxeigvals', dest='funcs', action='append_const', const='maxeigvals',
                        help='Maximum eigenvalue of a general matrix')
   parser.add_argument('--eigvalsh', dest='funcs', action='append_const', const='eigvalsh',
                        help='Eigenvalues of a Hermitian or real symmetric matrix')
   parser.add_argument('--maxeigvalsh', dest='funcs', action='append_const', const='maxeigvalsh',
                        help='Maximum eigenvalue of a Hermitian or real symmetric matrix')
   parser.add_argument('--geodan', dest='funcs', action='append_const', const='geodan',
                        help='Geodesic anisotropy: sqrt(trace(matlog(S) - (trace(matlog(S))/N)*eye(N))^2, where N==3 ')
   return parser

#Geodesic anisotropy from:
#COMPARISON OF FRACTIONAL AND GEODESIC ANISOTROPY IN DIFFUSION TENSOR IMAGES OF 90 MONOZYGOTIC AND DIZYGOTIC TWINS
#Agatha D. Lee1, Natasha Lepore1, Marina Barysheva1, Yi-Yu Chou1, Caroline Brun1, Sarah K. Madsen1, Katie L. McMahon2, 1 Greig I. de Zubicaray2, Matthew Meredith2, Margaret J. Wright3, Arthur W. Toga1, Paul M. Thompson
#http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.142.3274

#-------------------------------------------------------------------------------
## START MATRIX TRANSFORMATIONS
#-------------------------------------------------------------------------------
def main():

   #parsing arguments
   parser = set_parser()

   #parsing arguments
   try:
      args     = parser.parse_args  ()
   except argparse.ArgumentError, exc:
      print (exc.message + '\n' + exc.argument)
      parser.error(str(msg))
      return -1

   ifile = args.infile.strip()
   ofile = args.outfile.strip()
   maskf = args.maskfile.strip()
   funcs = args.funcs

   #setting the global variable that indicates the order of the matrices
   global N
   N     = args.dims

   #loading file and preprocessing
   iinfo  = nib.load(ifile)
   affine = iinfo.get_affine()

   minfo = nib.load(maskf)

   if len(iinfo.shape) != 4:
      err = 'File ' + ifile + ' should be a 4D volume'
      print(err)
      return -1

   #global variable N (for the nested functions)
   N = np.sqrt(iinfo.shape[3])

   if not N % 1 == 0:
      err = 'File ' + ifile + ' should have N volumes along its 4th dimension, where N is an exponent of 2.'
      print(err)
      return -1

   try:
      #deciding what function to use
      # and indicating size of 4th dimension of output
      myfuncs = {}
      odims   = np.empty(len(funcs), dtype=int)

      for i in range(len(funcs)):
         if funcs  [i] == 'matlog':
            myfuncs[i] = mylogm
            odims  [i] = N
         elif funcs[i] == 'det':
            myfuncs[i] = mydet
            odims  [i] = 1
         elif funcs[i] == 'trace':
            myfuncs[i] = mytrace
            odims  [i] = 1
         elif funcs[i] == 'deftensor':
            myfuncs[i] = mydeftensor
            odims  [i] = N
         elif funcs[i] == 'eigvalsh':
            myfuncs[i] = myeigvalsh
            odims  [i] = 3
         elif funcs[i] == 'eigvals':
            myfuncs[i] = myeigvals
            odims  [i] = 3
         elif funcs[i] == 'maxeigvalsh':
            myfuncs[i] = myeigvalsh
            odims  [i] = 1
         elif funcs[i] == 'maxeigvals':
            myfuncs[i] = myeigvals
            odims  [i] = 1
         elif funcs[i] == 'geodan':
            myfuncs[i] = mygeodan
            odims  [i] = 1

      #reading input data
      img  = iinfo.get_data()
      mask = minfo.get_data()

      sx = img.shape[0]
      sy = img.shape[1]
      sz = img.shape[2]

      nvox = sx*sy*sz
      im   = img.reshape(nvox,9)
      msk  = mask.flatten()
      idx  = np.where(msk > 0)[0]

      tic = clock();

      #processing
      lm      = np.zeros([nvox, odims[-1]])
      for i in idx:
         lm[i,:] = calculate_measures (myfuncs, im[i,:], odims)
         #lm[i,:] = meafun(im[i,:])

      toc = clock() - tic
      print ('Time spent: ' + str(toc))

      #saving output
      lm = lm.reshape([sx, sy, sz, odims[-1]])
      lm = lm.squeeze()

   #   debug_here()
      new_image = nib.Nifti1Image(lm, affine)
      nib.save(new_image, ofile)

   except:
      print ('Ooops! Error processing file ' + ifile)
      print 'Unexpected error: ', sys.exc_info()
      return -1

if __name__ == "__main__":
    sys.exit(main())

#Testing multiprocessing. Not implemented. Leaving for patience to solve.
#for i in range(len(im)/7000):
#   p.apply_async(mylogm, args=(im[i,:],i))

##determining multiprocessing stuff
#if nthreads > 1:
#   from multiprocessing.pool import Pool

#ncpus = multiprocessing.cpu_count()
#if nthreads > ncpus:
#   nthreads = ncpus - 1

#if nthreads > 1:
#   p = ThreadPool(nthreads)
#   print ('Using ' + nthreads + ' threads for execution')

#import nibabel as nib
#import numpy as np
#from time import clock
#from multiprocessing import Pool
#ifile='patient.M.90..5.OAS1_0247_MR1_mpr_n4_anon_111_t88_masked_gfc_spline_jacmat.nii.gz'
#meta = nib.load(ifile)
#img = meta.get_data()
#sx = img.shape[0]
#sy = img.shape[1]
#sz = img.shape[2]
#im = img.reshape(sx*sy*sz,9)
#p = Pool(4)
#p.map(mylogm, im)

#from time import clock
#for l in range(3):
#   ti = clock();
#   p = ThreadPool(4)
#   lm = im[np.arange(len(im)/500),:]
#   lm = np.zeros(lm.shape)
#   lm = p.map(mylogm, im)
##   for i in range(len(im)/500):
##      v = p.apply_async(mylogm, args=(im[i,:]))
##   p.close()
##   p.join()
#   tf = clock()-ti
#   print tf

#for l in range(3):
#   lm = np.empty(im.shape)
#   ti = clock();
#   for i in range(len(im)/500):
#      lm[i,:] = mylogm(im[i,:])
#   tf = clock()-ti
#   print tf


import numpy as np
from sklearn import metrics

def nrmse(y_actual, y_pred):
    # normalized root mean square error
    value = round(np.sqrt(metrics.mean_squared_error(y_actual, y_pred)) / np.mean(y_actual), 3)
    passed = "✔" if value < 0.15 else "❌"
    return value, passed


import numpy as np

def vertices(sides, radius, center, is_top):
    """Returns a NumPy matrix for a polygon in 3D space with homogeneous coordinates.
    The polygon is created based on the given number of sides, being circunscribed
    by a circle defined by the given parameters."""
    if is_top:
        vertices = (np.zeros((2,sides))+np.arange(sides))*np.radians(360/sides)
    else:
        vertices = (np.zeros((2,sides))+np.arange(sides)[::-1])*np.radians(360/sides)
    vertices[0,:] = np.cos(vertices[0,:])
    vertices[1,:] = np.sin(vertices[1,:])
    return np.concatenate((np.concatenate((vertices,np.zeros((1,sides))),axis=0)*radius + center,np.ones((1,sides))),axis=0)

def create_prism(x,y,z,h,r_bottom,r_top,sides):
    """Uses the coordinates of the center of the bottom circle.
    Returns a NumPy array for a 3D-prism with homogeneous coordinates.
    The first "sides" vertices define the polygon at the bottom of it,
    while the next "sides" vertices define the polygon on top of it.
    The last vertix is actually the geometric center of the object, stored for ease of use."""
    return np.concatenate((vertices(sides, r_top, np.array([[x],[y],[z+h]]),True),vertices(sides, r_bottom, np.array([[x],[y],[z]]),False)),axis=1)


# CuPy and Numba on GPU
# Author: Shaurya

import numpy as np
import cupy as cp
from time import perf_counter

# similar to numpy arrays
# cupy was created by the people behind chainer for their GPU based DL
arr=cp.arange(10)
repr(arr)
arr.dtype
arr.shape
arr.strides
arr.device

# we can move data between CPU and the GPU
arr_cpu=np.arange(10)
arr_gpu=cp.asarray(arr_cpu)
arr_gpu.device
# when we print contents of gpu array, it loads array back into cpu to show results

# numpy to cupy
cp.asarray(arr_cpu)

# cupy to numpy
cp.asnumpy(arr_gpu)

# mathematical functions
arr_gpu*2
cp.exp(-0.5*arr_gpu)
cp.linalg.norm(arr_gpu)

#! Numba on the GPU
# ufuncs and gfuncs
from numba import vectorize

@vectorize(['int64(int64,int64)'],target='cuda')
def add_ufunc(x,y):
    return x+y

a=np.array([1,2,3,4,6],dtype='int64')
b=np.array([10,20,30,40,60],dtype='int64')
b_col=b[:,np.newaxis]
c=np.arange(4*4).reshape((4,4))

# with gpu cupy
start=perf_counter()
print(f'a+b= {add_ufunc(a,b)}')
print(f'Time: {perf_counter()-start}s')

# with CPU numpy
start=perf_counter()
print(f'a+b={a+b}')
print(f'Time: {perf_counter()-start}s')
# 10 times faster than GPU because there is no arithmetic intensity

import math
sqrt_2pi=np.float32((2*math.pi)**0.5)

# for gpu
@vectorize(['float32(float32,float32,float32)'],target='cuda')
def gaussian_pdf(x,mean,sigma):
    return math.exp(-0.5*((x-mean)/sigma)**2)/(sigma*sqrt_2pi)

# for CPU
import scipy.stats

# evaluate gaussian prob distribution function million times
x=np.random.uniform(-3,3,size=1000000).astype(np.float32)
mean=np.float32(0.0)
sigma=np.float32(1.0)

# calc time taken cpu
start=perf_counter()
scipy.stats.norm.pdf(x,mean,sigma)
print(repr(perf_counter()-start)+'s')
# 0.065 seconds

# calc time gpu
start=perf_counter()
gaussian_pdf(x,mean,sigma)
print(repr(perf_counter()-start)+'s')

# CUDA device functions
from numba import cuda

@cuda.jit(device=True)
def polar_to_cartesian(rho,theta):
    x=rho*math.cos(theta)
    y=rho*math.sin(theta)
    return x,y

@timeit
@vectorize(['float32(float32,float32,float32,float32)'],target='cuda')
def polar_distance(rho1,theta1,rho2,theta2):
    x1,y1=polar_to_cartesian(rho1,theta1)
    x2,y2=polar_to_cartesian(rho2,theta2)
    return ((x1-x2)**2+(y1-y2)**2)**0.5

n=1000000
rho1=np.random.uniform(0.5,1.5,size=n).astype(np.float32)
theta1=np.random.uniform(-np.pi,np.pi,size=n).astype(np.float32)
rho2=np.random.uniform(0.5,1.5,size=n).astype(np.float32)
theta2=np.random.uniform(-np.pi,np.pi,size=n).astype(np.float32)

start=perf_counter()
polar_distance(rho1,theta1,rho2,theta2)
print(repr(perf_counter()-start)+'s')

# CPU version of above
import numba
@numba.jit
def polar_to_cartesian_cpu(rho,theta):
    x=rho*math.cos(theta)
    y=rho*math.sin(theta)
    return x,y

# no device specified in vectorize; defaults to CPU
@timeit
@vectorize(['float32(float32,float32,float32,float32)'])
def polar_distance_cpu(rho1,theta1,rho2,theta2):
    x1,y1=polar_to_cartesian_cpu(rho1,theta1)
    x2,y2=polar_to_cartesian_cpu(rho2,theta2)
    return ((x1-x2)**2+(y1-y2)**2)**0.5

np.testing.assert_allclose(polar_distance(rho1,theta1,rho2,theta2),
                            polar_distance(rho1,theta1,rho2,theta2),
                            rtol=1e-7,atol=5e-7)

# timeit
def timeit(f):
    def decorate(*args,**kwargs):
        start=perf_counter()
        result=f(*args,**kwargs)
        end=perf_counter()
        try:
            print(f'Time taken to execute {f.__name__}: {end-start}s')
        except AttributeError as e:
            print(e)
            print('GPU bound probably...')
            print(f'Time taken to execute: {end-start}s')
        return result
    return decorate

# check timings for polar distance on cpu and gpu
polar_distance_cpu(rho1,theta1,rho2,theta2)
polar_distance(rho1,theta1,rho2,theta2)

import numpy,import scipy,import numba,from numba,import cupy