# Problem: https://projecteuler.net/problem=227

"""
. Define distance as the clockwise number of people between the dice.
. Use the distance between the dices as a state.
. Use Markov chain to track the probabilities.
. T[distance1][distance2] is the probability of transitioning from distance 1 to distance 2

Solve for the expected number of steps from T:
    . Let E(X->Y) indicate the expected number of steps of going from state X to state Y.
    . We have this relatation:
        E(X->Y) = sum_{all next_state}(P(X->next_state) * [E(next_state->Y) + 1])
                = 1 + sum_{all next_state}(P(X->next_state) * E(next_state->Y))
                = P(X->X-2)  * E(X-2->Y // for this problem
                + P(X->X-1)  * E(X-1->Y)
                + P(X->X)    * E(X->Y)
                + P(X->X+1)  * E(X+1->Y)
                + P(X->X+2)  * E(X+2->Y)
                + 1

    . We have, for example,
        E(30->0) = 1/36  * [E(28->0) + 1]
                 + 8/36  * [E(29->0) + 1]
                 + 18/36 * [E(30->0) + 1]
                 + 8/36  * [E(31->0) + 1]
                 + 1/36  * [E(32->0) + 1]

    . Construct a matrix A from the above relations, i.e.:
        A * E = y
     where E[X] = E(X->0),
           y[X] = 0 if X is the final state, y[X] = 1 otherwise
           A = (I - T)

    . Solve for E.
"""

import numpy as np

N = 100

if __name__ == "__main__":
    ans = 0.0

    T = np.zeros((N, N), dtype = np.double)
    S = np.zeros((N), dtype = np.double)

    S[50] = 1.0

    #T[0][0] = 1.0

    for delta in range(1, N):
        # player 1 rolls 1, player 2 rolls 1: delta doesn't change
        # player 1 rolls 1, player 2 rolls [2..5]
        T[delta][(delta-1)%N] += 4/36
        # player 1 rolls 1, player 2 rolls 6
        T[delta][(delta-2)%N] += 1/36

        # player 1 rolls [2..5], player 2 rolls 1
        T[delta][(delta+1)%N] += 4/36

        # player 1 rolls [2..5], player 2 rolls 6
        T[delta][(delta-1)%N] += 4/36

        # player 1 rolls 6, player 2 rolls 1
        T[delta][(delta+2)%N] += 1/36
        # player 1 rolls 6, player 2 rolls [2..5]
        T[delta][(delta+1)%N] += 4/36
        # player 1 rolls 6, player 2 rolls 6: delta doesn't change

        T[delta][delta] = 18/36

    y = np.ones((N, 1), dtype = np.double)
    y[0,0] = 0

    x = np.linalg.solve(np.eye(N) - T, y)
    print(x[50][0])


import numpy as np
import matplotlib.pyplot as plt

np.random.seed(1)

def autocorr(x,k):
  '''returns the lag-k autocorrelation of vector x'''
  return np.corrcoef(x[:len(x)-k], x[k:])[0,1]

def thomasfiering(x,N):
  '''Lag-1 model. use historical data x
  to generate a synthetic sequence of N timesteps.
  Assumes x is lognormally distributed.'''
  x = np.log(x) # log-space avoids negative values
  mu = x.mean()
  sigma = x.std()
  rho = autocorr(x,1)
  Q = np.zeros(N) # initialize
  Q[0] = np.random.normal(mu,sigma,1)

  for i in range(1,N):
    Z = np.random.standard_normal()
    Q[i] = mu + rho*(Q[i-1] - mu) + Z*sigma*np.sqrt(1-rho**2)

  return np.exp(Q)

# assume annual flow data is lognormally distributed
# this will avoid negative flows
Q = np.loadtxt('data/folsom-annual-flow.csv', delimiter=',', skiprows=1, usecols=[1])

Q_synthetic = thomasfiering(Q, N=125)

print('Means: %f, %f' % (Q.mean(),Q_synthetic.mean()))
print('Stdev: %f, %f' % (Q.std(),Q_synthetic.std()))
print('Rho: %f, %f' % (autocorr(Q,1), autocorr(Q_synthetic,1)))
# # note "retransformation bias" in rho

plt.plot(Q_synthetic)
plt.plot(Q, color='red')
plt.plot([0,125], [Q.mean(), Q.mean()], color='k', linewidth=2)
plt.legend(['Synthetic', 'Observed', 'Mean'])
plt.show()


import math
import numpy.linalg
import numpy as np

def chernoff_distance(s, means, variances, univariate = False):
    """ Returns the Chernoff Distance, as defined in (3.150), p.98 of
        Fukunaga, 1990, Introduction to Statistical Pattern Recognition, 2nd Edition
    """

    from math import log
    from math import sqrt
    from math import pow

    from numpy.linalg import det
    from numpy import transpose
    from numpy.linalg import inv
    from numpy import array

    if univariate:
        part1 = 0.25 * log(0.25 * (variances[0] / variances[1] + variances[1] / variances[0]) + 0.5)
        part2 = 0.25 * pow(means[0] - means[1], 2) / (variances[0] + variances[1])
        return part1 + part2

    mean_diff = array(means[0]) - array(means[1])
    var_avg = (array(variances[0]) + array(variances[1])) * 0.5
    ln_coeff = 0.5 * log(det(var_avg) / sqrt(det(array(variances[0])) * det(array(variances[1]))))

    #return 0.125 * transpose(mean_diff) * inv(var_avg) * mean_diff + ln_coeff
    return 0.125 * np.matmul(np.matmul(mean_diff.T, inv(var_avg)), mean_diff) + ln_coeff

if __name__ == '__main__':
    #import doctest
    #doctest.testmod()

    mean1 = (1, 2)
    cov1 = [[1, 0], [0, 1]]
    mean2 = (3, 4)
    cov2 = [[2, 0], [0, 2]]

    s = 0.5

    distance = chernoff_distance(s, [mean1, mean2], [cov1, cov2])

    print('chernoff_distance(', end='')
    print(s, str([mean1, mean2]), str([cov1, cov2]), end='')
    print('):')
    print(distance)

    from numpy import exp
    print('Error', exp(-distance))

    distance2 = chernoff_distance(s, [mean1, mean1], [cov1, cov1])

    print('chernoff_distance(', end='')
    print(s, str([mean1, mean1]), str([cov1, cov1]), end='')
    print('):')
    print(distance2)

    from numpy import exp
    print('Error', exp(-distance2))


import math as math
import numpy as np

# MAPE Mean Absolute Percentage Error
def mape(y_true, y_pred):
    total = 0
    for index in range(len(y_true)):
        total = total + abs((y_true[index] - y_pred[index])/(y_true[index]))
    return total/len(y_true)

# RMSE Root Mean Squared Error
def rmse(y_true, y_pred):
    total = 0
    for index in range(len(y_true)):
        total = total + pow((y_true[index] - y_pred[index]),2)
    total = total / len(y_true)
    return math.sqrt(total)

# SSR Sum Squared Residuals
def ssr(y_true, y_pred):
    return np.sum(np.square(y_true - y_pred))


import numpy as np

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))

if __name__ == "__main__":
    t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

    y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
    r1 = cross_entropy_error(np.array(y), np.array(t))
    print(f'r1:{r1}')   # r1:0.510825457099338

    y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
    r2 = cross_entropy_error(np.array(y), np.array(t))
    print(f'r2:{r2}')   # r2:2.302584092994546


# Implement Bayes Classifier here!
import numpy as np
def sep_class(X,y):
    train={}
    for i in range(len(y)):
        x=X[i]
        label=y[i]
        if(label not in train):
            train[label]=[]
        train[label].append(x)
    return train
def class_estimates(train):
    mean={}
    var={}
    for label in train:
        #calculate sample mean
        mean[label]=np.mean(np.asarray(train[label]),axis=0)
        #calculate sample variance. ddof set(unless only one example is there for label)
        #because we have to divide by n-1 to get sample variance
        dof=1
        if(len(train[label])==1):
            dof=0
        var[label]=np.var(np.asarray(train[label]),axis=0,ddof=dof)
    return mean,var
def prior_prob(train):
    p_class={}
    n=sum([len(v) for v in train.values()])
    for label in train:
        p_class[label]=len(train[label])/n
    return p_class
def likelihood(x,mean,var):
    p=0.0
    for i in range(len(x)):
        if(var[i]==0):
            if(x[i]==mean[i]):
                return 1.0
            else:
                return 0.0
        else:
            p=p-0.5*np.log(2*np.pi*var[i])-((x[i]-mean[i])**2)/(2*var[i])
    return (p)
def posterior(x,train):
    post={}
    mean,var=class_estimates(train)
    p_class=prior_prob(train)
    for label in train:
        post[label]=likelihood(x,mean[label],var[label])+np.log(p_class[label])
    return post
def predict(X_test,X_train,y_train):
    n_test=X_test.shape[0]
    train=sep_class(X_train,y_train)
    pred_label=np.zeros(n_test)
    for i in range(n_test):
        post=posterior(X_test[i,:],train)
        pred_label[i]=max(post,key=lambda k:post[k])
    return pred_label


from sympy.core.sympify import _sympify
from sympy.matrices.expressions import MatrixExpr
from sympy.core.numbers import I
from sympy.core.singleton import S
from sympy.functions.elementary.exponential import exp
from sympy.functions.elementary.miscellaneous import sqrt

class DFT(MatrixExpr):
    r"""
    Returns a discrete Fourier transform matrix. The matrix is scaled
    with :math:`\frac{1}{\sqrt{n}}` so that it is unitary.

    Parameters
    ==========

    n : integer or Symbol
        Size of the transform.

    Examples
    ========

    >>> from sympy.abc import n
    >>> from sympy.matrices.expressions.fourier import DFT
    >>> DFT(3)
    DFT(3)
    >>> DFT(3).as_explicit()
    Matrix([
    [sqrt(3)/3,                sqrt(3)/3,                sqrt(3)/3],
    [sqrt(3)/3, sqrt(3)*exp(-2*I*pi/3)/3,  sqrt(3)*exp(2*I*pi/3)/3],
    [sqrt(3)/3,  sqrt(3)*exp(2*I*pi/3)/3, sqrt(3)*exp(-2*I*pi/3)/3]])
    >>> DFT(n).shape
    (n, n)

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/DFT_matrix

    """

    def __new__(cls, n):
        n = _sympify(n)
        cls._check_dim(n)

        obj = super().__new__(cls, n)
        return obj

    n = property(lambda self: self.args[0])  # type: ignore
    shape = property(lambda self: (self.n, self.n))  # type: ignore

    def _entry(self, i, j, **kwargs):
        w = exp(-2*S.Pi*I/self.n)
        return w**(i*j) / sqrt(self.n)

    def _eval_inverse(self):
        return IDFT(self.n)

class IDFT(DFT):
    r"""
    Returns an inverse discrete Fourier transform matrix. The matrix is scaled
    with :math:`\frac{1}{\sqrt{n}}` so that it is unitary.

    Parameters
    ==========

    n : integer or Symbol
        Size of the transform

    Examples
    ========

    >>> from sympy.matrices.expressions.fourier import DFT, IDFT
    >>> IDFT(3)
    IDFT(3)
    >>> IDFT(4)*DFT(4)
    I

    See Also
    ========

    DFT

    """
    def _entry(self, i, j, **kwargs):
        w = exp(-2*S.Pi*I/self.n)
        return w**(-i*j) / sqrt(self.n)

    def _eval_inverse(self):
        return DFT(self.n)


import numpy as np
from matplotlib import pyplot as plt

def rk4(f, y0, T, N):
    # f(t_n, y_n) -> y'_n
    # y0 = Initial value
    # T = Final value of t, t varies from 0 to T inclusive
    # N = number of steps
    y = np.array(y0, dtype=float)
    h = float(T)/N
    ts = np.linspace(0, T, N+1)

    out = np.empty(shape=(N+1,y.shape[0]), dtype=float)

    for i, t in enumerate(ts[:-1]):
        out[i] = y
        k1 = f(t, y)
        k2 = f(t+0.5*h, y + 0.5*h*k1)
        k3 = f(t+0.5*h, y + 0.5*h*k2)
        k4 = f(t+h, y + h*k3)
        y += h* 1/6.0 * (k1 + k2+k2 + k3+k3 + k4)

    out[N] = y

    return out

def p3a():
    f = lambda t,y: y
    y0 = [1]
    T = 1

    return (np.e - rk4(f,y0,T,10)[10,0])/(np.e - rk4(f,y0,T,20)[20,0])

def p3b():
    f = lambda t,y: y
    y0 = [1]
    T = 1

    for n in range(1,100):
        approx = rk4(f, y0, T, n)[n,0]
        if str(approx)[:7] == '2.71828':
            return f'Step size N = {n} returns y_N = {approx}'

beta = 0.5 # S->I growth
gamma = 0.25 # I->R growth
rho1 = 0.01 # Group 1->2 growth
rho2 = 0.01 # Group 2->1 growth

y0 = np.array([.95,.05,0,1,0,0])
T = 60

def f(t,Phi): # f(t,y) = y'
    S1, I1, R1, S2, I2, R2 = Phi # Extract pre-step values
    f = np.zeros(6) # Output array
    f[0] = -beta*S1*I1-rho1*S1+rho2*S2          # New S1
    f[1] = beta*S1*I1-gamma*I1-rho1*I1+rho2*I2  # New I1
    f[2] = gamma*I1-rho1*R1+rho2*R2             # New R1
    f[3] = -beta*S2*I2-rho2*S2+rho1*S1          # New S2
    f[4] = beta*S2*I2-gamma*I2-rho2*I2+rho1*I1  # New I2
    f[5] = gamma*I2-rho2*R2+rho1*R1             # New R2
    return f   # Return new values

def p5d():
    N25 = rk4(f, y0, T, 25)[25,:]
    N50 = rk4(f, y0, T, 50)[50,:]
    N100 = rk4(f, y0, T, 100)[100,:]

    for i, label in enumerate(['S_1','I_1','R_1','S_2','I_2','R_2']):
        print(f'Ratio for {label} = {(N50[i] - N25[i])/(N100[i] - N50[i])}')

def p5efgh(r1, r2):
    global rho1, rho2

    rho1 = r1
    rho2 = r2

    arr = rk4(f, y0, T, 100)
    ts = np.linspace(0,T,101) # All t values

    plt.figure()  # Start a plot
    plt.plot(ts,arr[:,0],'-',linewidth=1)   # Plot S1
    plt.plot(ts,arr[:,1],'-',linewidth=1)   # Plot I1
    plt.plot(ts,arr[:,2],'-',linewidth=1)   # Plot R1
    plt.plot(ts,arr[:,3],'--',linewidth=1)   # Plot S2
    plt.plot(ts,arr[:,4],'--',linewidth=1)   # Plot I2
    plt.plot(ts,arr[:,5],'--',linewidth=1)   # Plot R2
    plt.legend(['S_1','I_1','R_1','S_2','I_2','R_2'])  # Label each plot accordingly
    plt.xlabel('t') # Label x axis
    plt.show() # Finish and show the plot

def ab2(f, y0, y1, T, N):
    # f(t_n, y_n) -> y'_n
    # y0,y1 = Initial values
    # T = Final value of t, t varies from 0 to T inclusive
    # N = number of steps
    h = float(T)/N
    ts = np.linspace(0, T, N+1)

    out = np.empty(shape=(N+1,y0.shape[0]), dtype=float)
    out[0] = np.array(y0, dtype=float)
    out[1] = np.array(y1, dtype=float)

    for i in range(2,N+1):
        tm1 = ts[i-1]
        tm2 = ts[i-2]
        ym1 = out[i-1]
        ym2 = out[i-2]

        out[i] = ym1 + h*(1.5*f(tm1,ym1) - 0.5*f(tm2, ym2))

    return out

def p5i():
    y1 = y0
    N25 = ab2(f, y0, y1, T, 25)[25,:]
    N50 = ab2(f, y0, y1, T, 50)[50,:]
    N100 = ab2(f, y0, y1, T, 100)[100,:]

    for i, label in enumerate(['S_1','I_1','R_1','S_2','I_2','R_2']):
        print(f'Ratio for {label} = {(N50[i] - N25[i])/(N100[i] - N50[i])}')


"""
    2D trapezoidal rule to integrate Gauss's law for a dipole

    Solution to Problem Set 2, Problem 3
    To run:
        python trap_rule_2_3.py

"""

import matplotlib.pyplot as plt
import numpy as np

def trap_2D(f, x_min, x_max, y_min, y_max, n, m,*P):
    hx = (x_max-x_min)/n
    hy = (y_max-y_min)/m

# contributions from rectangle corners
    I = (f(x_min,y_min,*P)+f(x_min,y_max,*P)+f(x_max,y_min,*P)+f(x_max,y_max,*P))/4

# contributions from rectangle sides
    I_x = 0

    for i in range(1,n):
        xi = x_min+i*hx
        I_x += (f(xi,y_min,*P)+f(xi,y_max,*P))/2;

    I_y = 0

    for j in range(1,m):
        yj = y_min+j*hy
        I_y += (f(x_min,yj,*P)+f(x_max,yj,*P))/2

# contributions from inside of rectangle
    I_xy = 0

    for i in range(1,n):
        for j in range(1,m):
            xi = x_min+i*hx
            yj = y_min+j*hy

            I_xy += f(xi,yj,*P)

    return (I+I_x+I_y+I_xy)*hx*hy

#function to be integrated, divided by q
def Er_R2_overQ(theta,phi, d,R):
    return 1/(4.*np.pi) * (2+d/R*np.sin(theta)*np.cos(phi))*np.sin(theta)

int_analytic = 2.0
#integration boundaries
theta_min = 0
theta_max = np.pi
phi_min = 0
phi_max = 2.*np.pi
#choose some values for (d,R) such that d/R = 0.1
d = 0.1
R = 1
P =(0.1,1)
N = np.array([50, 100, 200, 400])
rel_err = np.zeros(4)
for i in range(N.shape[0]):
    int_trap = trap_2D(Er_R2_overQ, theta_min,theta_max,phi_min,phi_max, N[i], N[i],*P)
    rel_err[i] = np.abs(int_trap - int_analytic) / int_analytic

#fit a line to log(N), log(rel_err) to get slope/convergence rate
m, b = np.polyfit(np.log(N), np.log(rel_err), 1)
print("convergence rate 2D trapezoidal rule %.3f" % m)
plt.xlabel("log(number of steps)")
plt.ylabel("log(relative Error)")
plt.yscale('log')
plt.xscale('log')
#plot relative error
plt.plot(N, rel_err, marker ='o',label='2D Trapezoidal Rule',color ='b')
# and best fit
plt.plot(N,np.exp(np.log(N)*m+b),linestyle = "--",color ='b')
plt.legend()
plt.show()

Problem Sets/Problem Set 2/Solutions/trap2D_ps2_3.py


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
print(tf.__version__)

"""## We want to try some non linear curvy datasets"""

n = 10000
std = 6
X = np.random.random((n,2)) * (2*std) - (std)
y = np.cos(X[:,0]) + np.sin(X[:,1])

# TRAIN TEST SPLIT
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)

print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)

# Visualising the dataset
plt.scatter(X_train[:,0], y_train, color = 'red')
plt.title('X1 Plot')
plt.xlabel('X1')
plt.ylabel('y')
plt.show()

# Visualising the dataset
plt.scatter(X_train[:,1], y_train, color = 'red')
plt.title('X2 Plot')
plt.xlabel('X2')
plt.ylabel('y')
plt.show()

# Visualising the dataset with the target variable
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plt.scatter(X_train[:,0], X_train[:,1], y_train)
plt.title('Scatter Plot')
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()

N, D = X_train.shape

model = tf.keras.models.Sequential([
                                    tf.keras.layers.Flatten(input_shape = (D,)),
                                    tf.keras.layers.Dense(40, activation='relu'),
                                    tf.keras.layers.Dense(1)
])

#custom_opt = tf.keras.optimizers.Adam(0.01)
model.compile(
    optimizer='adam',
    loss='mse')

report = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs = 100)

plt.plot(report.history['loss'], label="loss")
plt.plot(report.history['val_loss'], label="validation_loss")
plt.legend()

print("Train eval: ", model.evaluate(X_train, y_train))
print("Test eval: ", model.evaluate(X_test, y_test))

y_pred = model.predict(X_test)[:,0]

print(y_test.shape)
print(y_pred.shape)

plt.scatter(y_test, y_pred, color = 'b')
plt.title('y_test vs y_pred')
plt.xlabel('y_test')
plt.ylabel('y_pred')
plt.show()

# Visualising the dataset
plt.scatter(X_test[:,0], y_test, color = 'red')
plt.scatter(X_test[:,0], y_pred, color = 'b')
plt.title('Scatter Plot PCA')
plt.xlabel('PCA 1')
plt.ylabel('y')
plt.show()

# Visualising the dataset
plt.scatter(X_test[:,1], y_test, color = 'red')
plt.scatter(X_test[:,1], y_pred, color = 'b')
plt.title('Scatter Plot PCA')
plt.xlabel('PCA 2')
plt.ylabel('y')
plt.show()

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X[:,0], X[:,1], y)

y_pred = model.predict(X).flatten()
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X[:,0], X[:,1], y_pred)
plt.show()


# Problem: https://projecteuler.net/problem=201

"""
    . Let state encode the size of subset and the curr_sum of a subset as follows,
        state = curr_sum * (MAX_SUBSET_SIZE + 1) + subset_size
    . This way of encoding states allow us to easily generate the next states.
      For example, if we want to add a new element of value V to the state, we have
      new_state = (curr_sum + V) * (MAX_SUBSET_SIZE + 1) + subset_size + 1
                = curr_sum * (MAX_SUBSET_SIZE + 1) + subset_size + V * (MAX_SUBSET_SIZE+1) + 1
                = state + V * (MAX_SUBSET_SIZE+1) + 1
    . Let DP[i][state] keep the number of combinations of subsets of size j and of sum k where state = k * (MAX_SUBSET_SIZE + 1) + j and the subset consists of elements from the first i elements from the superset.
        . DP[i][state] = 0 if there are no such a combination
        . DP[i][state] = 1 if there's one such a combination
        . DP[i][state] = 2 if there are more than one such a combination
    . We have that,
        DP[i+1][state] = DP[i][state] + DP[i][prev_state]
            where prev_state = (curr_sum - element[i+1]) * (MAX_SUBSET_SIZE + 1) + subset_size - 1
                for each corresponding state where state = curr_sum * (MAX_SUBSET_SIZE + 1) + subset_size
        This is correct as += DP[i][state] means that we decide to not to add the elements[i+1]
                    and    += DP[i][prev_state] means that we decide to add the elements[i+1].
"""

import numpy as np
from scipy.ndimage.interpolation import shift

N = 100
M = 50

elements = [k*k for k in range(1, N+1)]
max_sum = sum(sorted(elements)[-M:])

def encode(subset_size, curr_sum):
    return curr_sum * (M+1) + subset_size
def decode(h):
    return h % (M+1), h // (M+1)

n_states = (M+1) * (max_sum+1)

if __name__ == "__main__":
    ans = 0

    prev_DP = None
    curr_DP = np.zeros(n_states+1, dtype = np.int16)

    curr_DP[encode(0, 0)] = 1

    for i, element in enumerate(elements):
        prev_DP, curr_DP = curr_DP, prev_DP
        curr_DP = prev_DP + shift(prev_DP, element*(M+1) + 1, cval=0)
        #                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        #                   generate the next states from the previous states
        curr_DP[np.where(curr_DP > 1)] = 2

    size_M_index = np.arange(max_sum+1) * (M+1) + M
    result_index = curr_DP[size_M_index]
    ans = np.sum(np.where(result_index == 1))
    print(ans)


import numpy as np

def radius_of_curvature(p0, p1, p2):
    """
    Calculates the radius of curvurature between three points. Based on https://stackoverflow.com/questions/1211212/how-to-calculate-an-angle-from-three-points and https://en.wikipedia.org/wiki/Degree_of_curvature.
    """
    a = np.array([p1[0] - p2[0], p1[1] - p2[1]])
    b = np.array([p1[0] - p0[0], p1[1] - p0[1]])
    c = np.array([p0[0] - p2[0], p0[1] - p2[1]])
    Dc = np.arccos(a.dot(b) / (np.linalg.norm(a) * np.linalg.norm(b)))
    radius_of_curvature = np.linalg.norm(c) / (2 * np.sin(Dc / 2))
    return radius_of_curvature


import numpy as np

'''
Compute cost function
@param x matrix of size (set_len, input_len)
@param y vector of size (set_len) - expected values
@param w vector of size (input_len) - weights
@param b vector of size (1) - bias
@return cost value
'''
def cost_function(X, y, w, b):
    y_hat = np.dot(X, w) + b
    res = 0.5 * np.sum((y - y_hat) ** 2)
    return res

def evaluate(X, y, w, b):
    print('Cost: ' + str(cost_function(X, y, w, b)))

'''
Apply stochastic gradient descent on the whole training set of size set_len
@param x matrix of size (set_len, input_len)
@param y vector of size (set_len) - expected values
@param w vector of size (input_len) - weights
@param b vector of size (1) - bias
@param lr - learning rate
@return vector (input_len) updated weights, updated biais
'''
def sgd(X, y, w, b, lr):
    y_hat = np.dot(X, w) + b
    dW = np.dot(X.T, y_hat - y)
    dB = sum(y_hat - y)
    w = w - lr * dW
    b = b - lr * dB
    return w, b

'''
@param x matrix of size (set_len, input_len)
@param y vector of size (set_len) - expected values
@param epochs - number of epochs of learning
@param lr - learning rate

Run the training for several epochs.
After each epochs, the wieghts are tested on the training set
'''
def train(X, y, epochs, lr):

    w = np.random.randn(X.shape[1])
    b = np.random.randn(1)

    #Training
    for i in range(1, epochs + 1):
        print('Epoch :' + str(i))
        w, b = sgd(X, y, w, b, lr)
        evaluate(X, y, w, b)

    return w, b


'''
the basic implementation of rotation matrix, Axis-angle, Euler angle and Quaternion.
'''
import numpy as np

def cross_product():
    # study cross product and skew-symmetric
    a = np.random.rand(3)
    b = np.random.rand(3)
    print('a cross product b:', np.cross(a, b))
    print('using skew-symmetric:')

    def skew_symmetric(a):
        return np.array([
            [0, -a[2], a[1]],
            [a[2], 0, -a[0]],
            [-a[1], a[0], 0]
        ])

    print('cross by using skew_symmetric:', skew_symmetric(a) @ b)

def rotation_matrix():
    # create two coordinate which rotate by z axis: angle: 45 degree.
    e_1 = np.array([[1], [0], [0]])
    e_2 = np.array([[0], [1], [0]])
    e_3 = np.array([[0], [0], [1]])
    E = np.hstack((e_1, e_2, e_3))
    e_1p = np.array([[1.], [1.], [0.]]) / np.sqrt(2)
    e_2p = np.array([[-1.], [1.], [0.]]) / np.sqrt(2)
    e_3p = np.array([[0], [0], [1]])
    Ep = np.hstack((e_1p, e_2p, e_3p))
    R = E.T @ Ep
    print('rotation matrix:R=\n', R)
    print('R inv:\n', np.linalg.inv(R))
    print('R.T:\n', R.T)
    print('we can find R.T == inv(R)')
    print('check orthogonal property:', np.linalg.norm(R @ R.T - np.eye(3)))

    # using homogeneous representation
    # create two translation vector.
    t1 = np.array([[1], [2], [3]])
    t2 = np.array([[3], [2], [1]])
    R1 = R
    R2 = R1 @ R1 @ R1
    p = np.array([[2], [3], [5]])
    print('after transformation:')
    res1 = R2 @ (R1 @ p + t1) + t2
    print('res=', res1.ravel())
    print('using Homogeneous coordinates:')
    p1 = np.vstack((p, 1))
    T1 = np.hstack((R1, t1))
    tmp = np.array([[0, 0, 0, 1]])
    T1 = np.vstack((T1, tmp))
    print('Transform matrix:T1=\n', T1)

    T2 = np.hstack((R2, t2))
    tmp = np.array([[0, 0, 0, 1]])
    T2 = np.vstack((T2, tmp))
    print('Transform matrix:T2=\n', T2)
    res2 = T2 @ T1 @ p1
    print('res=', res2.ravel()[:3])

if __name__ == '__main__':
    rotation_matrix()


# Copyright 2021 Neuron-AI GitHub Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
from numpy import array, sqrt
from warnings import warn

def euclidean_distance(x, y, **kwargs):
	x = array(x).reshape(1, -1)
	y = array(y).reshape(1, -1)

	if x.shape != y.shape and kwargs.get("suppress_warnings") != True:
		warn(UserWarning("using sequences which do not contain equivalent numbers of items can result in unexpected results."))

	return sqrt(sum((x - y)**2 for x, y in zip(x[0], y[0])))

def manhattan_distance(x, y, **kwargs):
	x = array(x).reshape(1, -1)
	y = array(y).reshape(1, -1)

	if x.shape != y.shape and kwargs.get("suppress_warnings") != True:
		warn(UserWarning("using sequences which do not contain equivalent numbers of items can result in unexpected results."))

	return sum(abs((x - y)) for x, y in zip(x[0], y[0]))

distance_functions = {
    "euclidean": euclidean_distance,
    "manhattan": manhattan_distance
}


"""Measures of dispersion of datasets"""

import numpy as np

__all__ = [
    'interquartile_range', 'maximum_absolute_value', 'standard_deviation', 'total_range',
]

def interquartile_range(X):
    """
    Distance between the first and the third quartile; range of the central half of the data.

    Parameters
    ----------
    X : np.array
        Dataset. Should be a two-dimensional array.

    Returns
    -------
    a: np.array
        One-dimensional array that contains the interquartile range for each feature.
    """
    quartiles = np.nanpercentile(X, [25, 75], axis=0)
    return quartiles[1, :] - quartiles[0, :]

def maximum_absolute_value(X):
    """
    Maximum distance from 0.

    Parameters
    ----------
    X : np.array
        Dataset. Should be a two-dimensional array.

    Returns
    -------
    a: np.array
        One-dimensional array that contains the maximum absolute value for each feature.
    """
    return np.nanmax(np.abs(X), axis=0)

def standard_deviation(X):
    """
    Square root of the sum of the squared distances to the mean.

    Parameters
    ----------
    X : np.array
        Dataset. Should be a two-dimensional array.

    Returns
    -------
    a: np.array
        One-dimensional array that contains the standard deviation for each feature.
    """
    return np.nanstd(X, axis=0)

def total_range(X):
    """
    Distance between the smallest and largest value.

    Parameters
    ----------
    X : np.array
        Dataset. Should be a two-dimensional array.

    Returns
    -------
    a: np.array
        One-dimensional array that contains the ranges for each feature.

    Notes
    -----
    This function is not called `range` so as not to overwrite the built-in function `range`.
    """
    return np.nanmax(X, axis=0) - np.nanmin(X, axis=0)


"""Fourier matrix."""
import numpy as np

def fourier(dim: int) -> np.ndarray:
    r"""
    Generate the Fourier transform matrix [WikDFT]_.

    Generates the `dim`-by-`dim` unitary matrix that implements the quantum
    Fourier transform.

    The Fourier matrix is defined as:

    .. math::
        W_N = \frac{1}{N}
        \begin{pmatrix}
            1 & 1 & 1 & 1 & \ldots & 1 \\
            1 & \omega & \omega^2 & \omega^3 & \ldots & \omega^{N-1} \\
            1 & \omega^2 & \omega^4 & \omega^6 & \ldots & \omega^{2(N-1)} \\
            1 & \omega^3 & \omega^6 & \omega^9 & \ldots & \omega^{3(N-1)} \\
            \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \\
            1 & \omega^{N-1} & \omega^{2(N-1)} & \omega^{3(N-1)} &
            \ldots & \omega^{3(N-1)}
        \end{pmatrix}

    Examples
    ==========

    The Fourier matrix generated from :math:`d = 3` yields the following
    matrix:

    .. math::
        W_3 = \frac{1}{3}
        \begin{pmatrix}
            1 & 1 & 1 \\
            0 & \omega & \omega^2 \\
            1 & \omega^2 & \omega^4
        \end{pmatrix}

    >>> from toqito.matrices import fourier
    >>> fourier(3)
    [[ 0.57735027+0.j ,  0.57735027+0.j ,  0.57735027+0.j ],
     [ 0.57735027+0.j , -0.28867513+0.5j, -0.28867513-0.5j],
     [ 0.57735027+0.j , -0.28867513-0.5j, -0.28867513+0.5j]]

    References
    ==========
    .. [WikDFT] Wikipedia: DFT matrix,
        https://en.wikipedia.org/wiki/DFT_matrix

    :param dim: The size of the Fourier matrix.
    :return: The Fourier matrix of dimension `dim`.
    """
    # Primitive root of unity.
    root_unity = np.exp(2 * 1j * np.pi / dim)
    entry_1 = np.arange(0, dim)[:, None]
    entry_2 = np.arange(0, dim)
    return np.power(root_unity, entry_1 * entry_2) / np.sqrt(dim)


from __future__ import annotations
import numpy as np
from numpy.linalg import inv, det, slogdet
from collections.abc import Callable

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

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

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

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

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

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

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

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

        Notes
        -----
        Sets `self.mu_`, `self.var_` attributes according to calculated estimation (where
        estimator is either biased or unbiased). Then sets `self.fitted_` attribute to `True`
        """
        m = X.shape[0]
        self.mu_ = np.sum(X) / m
        var_denom = np.sum((X - self.mu_)**2)
        if self.biased_:
            self.var_ = var_denom / m
        else:
            self.var_ = var_denom / (m - 1)

        self.fitted_ = True
        return self

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

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

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

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

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

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

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated
        """
        m = X.shape[0]
        return (-0.5)*(m*np.log(2*np.pi) + \
                       m*np.log(sigma) + \
                       (1/sigma)*np.sum((X-mu)**2))

class MultivariateGaussian:
    """
    Class for multivariate Gaussian Distribution Estimator
    """
    def __init__(self):
        """
        Initialize an instance of multivariate Gaussian estimator

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

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

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

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

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

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

        Notes
        -----
        Sets `self.mu_`, `self.cov_` attributes according to calculated estimation.
        Then sets `self.fitted_` attribute to `True`
        """
        self.fitted_ = True
        m = X.shape[0]
        self.mu_ = np.sum(X,axis=0) / m
        centered_X = X - self.mu_.T
        self.cov_ = (centered_X.T @ centered_X) / (m - 1)
        return self

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

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

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

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")
        d = self.mu_.shape[0]
        normalization_factor = 1/np.sqrt(((2*np.pi)**d)*det(self.cov_))
        centered_X = X - self.mu_.T
        Ax = np.einsum('ij,nj->ni',inv(self.cov_),centered_X)
        xAx = np.einsum('ni,ni->n', centered_X, Ax)
        exponential_factor = (-0.5)*xAx
        return normalization_factor * np.exp(exponential_factor)

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

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

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated over all input data and under given parameters of Gaussian
        """
        m,d = X.shape
        centered_X = X - mu.T
        Ax = np.einsum('ij,nj->ni',inv(cov),centered_X)
        sum_xAx = np.einsum('ni,ni->', centered_X, Ax)
        return (-0.5)*(m*d*np.log(2*np.pi) + m*slogdet(cov)[1] + np.sum(sum_xAx))


"""
  Name     : c14_10_correlated_random_numbers.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""

import scipy as sp
sp.random.seed(123)
n=1000
rho=0.3
x1=sp.random.normal(size=n)
x2=sp.random.normal(size=n)
y1=x1
y2=rho*x1+sp.sqrt(1-rho**2)*x2
print(sp.corrcoef(y1,y2))


#
#	root-secant.py
#	Finding root using secant method
#
#	Sparisoma Viridi | https://butiran.github.io/
#
#	Execute: py root-secant.js
#
#	20210103
#	1042 Start this program from root-newton-raphton.py.
# 1351 Remove unecessary semicolons.
#	20210210
#	0527 Typo node --> py.
#

# Import necessary libraries
import numpy as np

# Define a test function
def test_function(x):
	y3 = 0.01 * x * x * x
	y2 = -0.2192 * x * x
	y1 = 0.3056 * x
	y0 = 1.568
	y = y3 + y2 + y1 + y0
	return y

# Define input
f = test_function
xinit1 = 2
xinit2 = 3
eps = 1E-10
n = 0
maxstep = 40

# Define default message and parameter
xroot = "not found"
SHOW_PROGRESS = False

# Do iteration
Nstep = 0
x = []
x.append(xinit1)
x.append(xinit2)
froot = np.abs(f(x[n+1]))

while froot > eps and n < maxstep - 1:
	x.append(x[n+1] - f(x[n+1]) * (x[n+1] - x[n])  / (f(x[n+1]) - f(x[n])))

	froot = np.abs(f(x[n+2]))
	if froot < eps:
		xroot = x[n+2]

	if SHOW_PROGRESS:
		if n == 0:
			fn = f(x[n])
			print("n\tx\tf(x)")
			print(n, x[n], f(x[n]), sep="\t")
			print(n+1, x[n+1], f(x[n+1]), sep="\t")
		print(n+2, x[n+2], f(x[n+2]), sep="\t")

	n += 1

Nstep = n+2

if SHOW_PROGRESS:
	print()

# Display result
print("f(x)  0.01x^3 - 0.2192x^2 + 0.3056x + 1.568");
print("x1    ", xinit1, sep="")
print("x2    ", xinit2, sep="")
print("ε     ", eps, sep="")
print("Nstep ", Nstep, sep="")
print("xroot ", xroot, sep="")


#!/usr/bin/env python3

from datetime import date
import math
import numpy as np
import sys
from math import *

#Binomial Tree model
def tree(stock_price, strike_price, rf, volatility, div_yield, steps, c_or_p):
	#Inputs
    Dt = 1 / steps #set time to expiration equal to 1
    r_Dt = rf * Dt
    exp_r_Dt = math.exp(r_Dt)
    exp_neg_r_Dt = math.exp(-r_Dt)
    exp_div_Dt = math.exp(div_yield * Dt)
    exp_neg_div_Dt = math.exp(-div_yield * Dt)
    sig_sqrt_Dt = volatility * math.sqrt(Dt)
    exp_sig_sqrt_Dt = math.exp(sig_sqrt_Dt)

	#probability of step up & down
    u = exp_sig_sqrt_Dt
    d = 1/u
    q = (exp_r_Dt * exp_neg_div_Dt - d) / (u -d)
    p = 1 - q

	#Step by step matrix calculation with estimated price of the underlying
    sv = np.zeros((steps + 1,steps +1), dtype = np.float64)
    sv[0,0] = stock_price
    z1 = 0
    for i in range(1,steps + 1, 1):
        z1 = z1 + 1
        for n in range(z1 + 1):
            sv[n,i] = sv[0,0] * (u ** (i - n)) * (d ** n)

	#Matrix with the final payoff depending on put or call
    iv = np.zeros((steps + 1,steps +1), dtype = np.float64)
    z2 = 0
    for i in range(1,steps + 1, 1):
        find = False
        for n in range(z2 + 2):
            if c_or_p == "C":
                iv[n,i] = max(sv[n,i] - strike_price, 0)
            elif c_or_p == "P":
                iv[n,i] = max(-1 * (sv[n,i] - strike_price), 0)
            else:
                print("fravor has to be 'C' for call and 'P' for put")
                find = True
                break
        if find:
            break
        z2 = z2 + 1

    pv = np.zeros((steps + 1,steps +1), dtype = np.float64)
    pv[:, steps] = iv[:, steps]
    z3 = steps + 1
    for i in range(steps -1, -1, -1):
        z3 = z3 - 1
        for n in range(z3):
            pv[n,i] = (q * pv[n, i + 1] + p * pv[n + 1, i + 1]) * exp_neg_r_Dt
    return(pv[0,0])

#Black-Scholes model
def phi(x):
    #Cumulative distribution function for the standard normal distribution
    return (1.0 + erf(x / sqrt(2.0))) / 2.0

def black_scholes_call(stock_price, strike_price, rf, volatility, div_yield, c_or_p):
	#Black Scholes formula for call and put options
	d1 = (math.log(stock_price/strike_price) + rf - div_yield + volatility**2/2)/volatility
	d2 = d1 - volatility
	#Call price
	call_bs = stock_price * phi(d1) - exp(-rf) * strike_price * phi(d2)
	#Put price
	put_bs = exp(-rf) * strike_price * phi(-d2) - stock_price * phi(-d1)
	if c_or_p == 'C':
		return(call_bs)
	elif c_or_p == 'P':
		return(put_bs)
	else:
		print("You have to select your option to be either a call('C') or a put('P')")

if __name__ == "__main__":
	#inputs for Black Scholes model
	stock_price = float(sys.argv[1])
	strike_price = float(sys.argv[2])
	rf = float(sys.argv[3])
	volatility = float(sys.argv[4])
	div_yield = float(sys.argv[5])
	c_or_p = sys.argv[6]

	#inputs for Binomial Tree model
	steps = int(sys.argv[7])

	#calculate the difference between two methods
	diff = tree(stock_price, strike_price, rf, volatility, div_yield, steps, c_or_p) - black_scholes_call(stock_price, strike_price, rf, volatility, div_yield, c_or_p)
	print('Price of this option using Binomial Tree model is ', tree(stock_price, strike_price, rf, volatility, div_yield, steps, c_or_p))
	print('Price of this option using Black-Scholes model is ', black_scholes_call(stock_price, strike_price, rf, volatility, div_yield, c_or_p))
	print('Price difference between these two methods is', diff)


# Undergraduate Student: Arturo Burgos
# Professor: João Rodrigo Andrade
# Federal University of Uberlândia - UFU, Fluid Mechanics Laboratory - MFLab, Block 5P, Uberlândia, MG, Brazil

# Third exercise: Fibonacci sequence - by a common loop

import numpy as np
import time
from numba import jit

n = int(input("Enter the n indices: "))
Fbn=0 # Value of the first box
A = 0 # Values of the second
B = 1 # Value of the third box

##############################
# Variable iterative fibonacci  --> Better performance, why?
##############################

@jit(nopython=True)
def varloop_fibonacci(k,F1,F2,S):

    for i in range(0,k):
        F1=S
        S = F1+F2
        F2 = F1

    return S

t_initial1 = time.time()
Fbn = varloop_fibonacci(n,A,B,Fbn)

print('\n')
print("--- %s seconds ---\n" % (time.time() - t_initial1))
print("The correspond indices number is: ",Fbn)
print('\n')
Fbn=0
t_initial = time.time()  # I set here the initial time.
Fbn = varloop_fibonacci(n,A,B,Fbn)
print("--- %s seconds after compilation---\n" % (time.time() - t_initial1))
print("The correspond indices number is: ",Fbn)


import numpy as np

def entropy(distribution, unit=2):
    """Finds the entropy of a distribution.
        Its default unit is 'bit'.

    Args:
        distribution ([FrequencyTable]): A FrequencyTable or any of
                                         its sub-classes
        unit (int, optional): Unit of the entropy. Defaults to 2 (bits).

    Returns:
        [float]: Entropy of the distribution
    """
    frequencies = distribution.frequencies(normalised=True)
    # check to see if it is a deterministic case (all but one are zero)
    zeros_size = frequencies[frequencies == 0].size
    if zeros_size + 1 == frequencies.size:
        return 0
    else:
        return np.sum(-frequencies * np.log2(frequencies) / np.log2(unit))


import numpy as np
import math  as m

def getAlpha(k, N):
    if k == 0:
        a = N
    else:
        a = N/2
    return a

# dct1D
def dct1(v):
    N = len(v)
    c = np.zeros(N)
    for k in range(0, N):
        ck = 0
        for i in range(0, N):
            ck = ck + m.cos(m.pi * k * (2*(i+1) - 1) / (2*N)) * v[i]
        ck = ck / m.sqrt(getAlpha(k, N))
        c[k] = ck
    return c

# dct2D with 2 x dct1D
def dct2V1(Mat):
    N,M = np.shape(Mat)

    C = np.zeros((N, M))

    for h in range(0, N):
        v = Mat[h,:]
        C[h] = dct1(v)

    Mat = np.copy(C)

    for h in range(0, M):
        v = Mat[:, h]
        C[:,h] = dct1(v)
    return C

# dct2D
def dct2V2(Mat):
    N,M = np.shape(Mat)

    C = np.zeros((N, M))

    for k in range(0, N):
        for l in range(0, M):
            ckl = 0
            for i in range(0, N):
                for j in range(0, M):
                    ckl = ckl + m.cos(m.pi * k * (2*(i+1) - 1) / (2 * N)) * m.cos(m.pi * l * (2*(j+1) - 1) / (2 * M)) * Mat[i, j]
            C[k, l] = ckl / m.sqrt(getAlpha(k,N) * getAlpha(l,M));
    return C


from sympy import *
x, y, z = symbols('x y z')
init_printing(use_unicode=True)

#print simplify(sin(x)**2 + cos(x)**2)

#print simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1))

#print simplify(gamma(x)/gamma(x - 2))

print simplify((x + 1)**2)
print expand((x + 1)**2)


"""TensorFlow-related utilities."""
from __future__ import absolute_import
from __future__ import division
from __future__ import generator_stop
from __future__ import print_function

import numpy as np
import tensorflow as tf

def tf_log(tensor, base=np.exp(1.)):
    r"""TensorFlow logarithmic operation.

    Parameters
    ----------
    tensor : tensorflow.Tensor
        Tensor for logarithmic operation.
    base : int or float, default np.exp(1.)
        Base of logarithmic operation.

    Returns
    -------
    new_tensor : tensorflow.Tensor
        Tensor after logarithmic operation.

    Notes
    -----
    Logarithmic identity
    .. math:: \log_a{b}=\frac{\log_c{b}}{\log_c{a}}

    References
    ----------
    .. [1] https://github.com/tensorflow/tensorflow/issues/1666#issuecomment-202453841
    .. [2] https://en.wikipedia.org/wiki/List_of_logarithmic_identities
    """
    numerator = tf.math.log(tensor)
    denominator = tf.math.log(tf.constant(base, dtype=numerator.dtype))
    new_tensor = numerator / denominator
    return new_tensor


import numpy as np

def cov(arr : np.ndarray):
    """Compute the sample covariance matrix for
    a matrix arr, assumed to be given in (samples x features)
    form for data fitting applications.

    Parameters
    ----------
    arr : np.ndarray
        matrix in (samples x features) form
    """
    n, _ = arr.shape
    return arr.T@arr / (n - 1)

def center(arr: np.ndarray):
    """Center a data matrix, assumedly given in (samples x features)
    form for data fitting applications.

    Parameters
    ----------
    arr : np.ndarray
        matrix in (samples x features) form
    """
    return arr - np.mean(arr, axis=0)


from numpy import *
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib import rc
from numpy.linalg import eigh
from numpy.linalg import norm

# Load the "gatlin" image data
X = loadtxt('gatlin.csv', delimiter=',')

#================= ADD YOUR CODE HERE ====================================
# Perform SVD decomposition
## TODO: Perform SVD on the X matrix
# Instructions: Perform SVD decomposition of matrix X. Save the
#               three factors in variables U, S and V
#

w, U = eigh(dot(X, transpose(X)))
index_w = max(argsort(w)) - argsort(w)
w = w[index_w]
U = U[:, index_w]

w2, V = eigh(dot(transpose(X),X))
index_w2 = max(argsort(w2)) - argsort(w2)
w2 = w2[index_w2]
V = V[:, index_w2]

Sdiag = diag(sqrt(absolute(w)))
S = zeros(X.shape)
min_size = min(X.shape)
S[:min_size,:min_size] = Sdiag

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

# Plot the original image
plt.figure(1)
plt.imshow(X,cmap = cm.Greys_r)
plt.title('Original image (rank 480)')
plt.axis('off')
plt.draw()

#================= ADD YOUR CODE HERE ====================================
# Matrix reconstruction using the top k = [10, 20, 50, 100, 200] singular values
## TODO: Create four matrices X10, X20, X50, X100, X200 for each low rank approximation
## using the top k = [10, 20, 50, 100, 200] singlular values
#

Ks = [10, 20, 50, 100, 200]
Xs = []

for k in Ks:
	Sk = S[:k, :k]
	Uk = U[:, :k]
	Vk = V[:, :k]
	Xs.append(dot(dot(Uk,Sk),transpose(Vk)))

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

#================= ADD YOUR CODE HERE ====================================
# Error of approximation
## TODO: Compute and print the error of each low rank approximation of the matrix
# The Frobenius error can be computed as |X - X_k| / |X|
#

error = []
for x in Xs:
	error.append((norm(X-x)/norm(X)))

plt.figure(3)
plt.plot(Ks, error)
plt.show()

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

Xgen = (x for x in Xs)

# Plot the optimal rank-k approximation for various values of k)
# Create a figure with 6 subfigures
plt.figure(2)

# Rank 10 approximation
plt.subplot(321)
plt.imshow(Xgen.next(),cmap = cm.Greys_r)
plt.title('Best rank' + str(5) + ' approximation')
plt.axis('off')

# Rank 20 approximation
plt.subplot(322)
plt.imshow(Xgen.next(),cmap = cm.Greys_r)
plt.title('Best rank' + str(20) + ' approximation')
plt.axis('off')

# Rank 50 approximation
plt.subplot(323)
plt.imshow(Xgen.next(),cmap = cm.Greys_r)
plt.title('Best rank' + str(50) + ' approximation')
plt.axis('off')

# Rank 100 approximation
plt.subplot(324)
plt.imshow(Xgen.next(),cmap = cm.Greys_r)
plt.title('Best rank' + str(100) + ' approximation')
plt.axis('off')

# Rank 200 approximation
plt.subplot(325)
plt.imshow(Xgen.next(),cmap = cm.Greys_r)
plt.title('Best rank' + str(200) + ' approximation')
plt.axis('off')

# Original
plt.subplot(326)
plt.imshow(X,cmap = cm.Greys_r)
plt.title('Original image (Rank 480)')
plt.axis('off')

plt.draw()

#================= ADD YOUR CODE HERE ====================================
# Plot the singular values of the original matrix
## TODO: Plot the singular values of X versus their rank k

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

plt.show()


import numpy as np
import numpy.random as rd
import scipy.stats as st
import matplotlib.pyplot as plt
import pickle as pk
import sys

#Definir les funcions per a fer test
def f_norm(x):
    return st.norm.pdf(x,0,1)

def f_skew(x):
    return st.skewnorm.pdf(x,7,loc=-1.32,scale=1.65)

def f_bimodal_asym(x):
    std1 = 0.41
    std2 = 0.71
    sample = 0.4*st.norm.pdf(x,-0.82-0.16,std1)+0.6*st.norm.pdf(x,0.82-0.16,std2)
    return sample

def f_bimodal_sym(x):
    std1 = 0.5
    std2 = 0.5
    sample = 0.5*st.norm.pdf(x,-0.9,std1)+0.5*st.norm.pdf(x,0.9,std2)
    return sample

def f_trimodal_asym(x):
    std1 = 0.65
    std2 = 0.35
    std3 = 0.35
    mu1 = -1.7
    mu2 = 0
    mu3 = 1.3
    sample = 0.2*st.norm.pdf(x,mu1+0.08,std1)+0.6*st.norm.pdf(x,mu2+0.08,std2)+0.2*st.norm.pdf(x,mu3+0.08,std3)
    return sample

def f_trimodal_sym(x):
    std1 = 0.65
    std2 = 0.15
    std3 = std1
    mu1 = -1.3
    mu2 = 0
    mu3 = -mu1
    sample = 0.25*st.norm.pdf(x,mu1,std1)+0.5*st.norm.pdf(x,mu2,std2)+0.25*st.norm.pdf(x,mu3,std3)
    return sample

def f_student(x):
    return st.t.pdf(x,3,scale=0.585)

def f_laplace(x):
    return st.laplace.pdf(x,loc=0,scale=0.72)

def f_laplace_sharp(x):
    std1 = 0.2
    std2 = 1
    sample = 0.5*st.laplace.pdf(x,0,std1)+0.5*st.laplace.pdf(x,0,std2)
    return sample

def f_test1(x):
    std1 = 0.5
    std2 = 0.5
    sample = 0.5*st.norm.pdf(x,-0.9,std1)+0.5*st.norm.pdf(x,0.9,std2)
    return sample

def f_test2(x):
    std1 = 0.6
    std2 = 0.6
    sample = 0.8*st.norm.pdf(x,-1.05+0.62,std1)+ 0.2*st.norm.pdf(x,1.05+0.62,std2)
    return sample


import numpy as np

# Q3
f = np.matrix([[25,50,1,4],[255,52,2,5],[255,100,0,3],[255,100,3,120]])
w1 = np.matrix([[1,1,1],[1,-8,1],[1,1,1]])
w2 = np.matrix([[0,1/10,0],[1/10,6/10,1/10],[0,1/10,0]])

def conv_point(f, w, x, y):
  n,m = w.shape
  a = int((n - 1) / 2)
  b = int((m - 1) / 2)
  sub_f = f[x-a : x+a+1, y-b : y+b+1]
  w = np.flip(np.flip(w, 0), 1)
  value = np.sum(np.multiply(sub_f, w))
  return int(value)

print('w1 of g(1, 1) =', conv_point(f, w1, 1, 1))
print('w1 of g(2, 2) =', conv_point(f, w1, 2, 2))
print('w2 of g(1, 2) =', conv_point(f, w2, 1, 2))
print()

# Q4
wm = np.matrix([[1,1,1],[1,1,1],[1,1,1]])

def conv_point_median(f, w, x, y):
  n,m = w.shape
  a = int((n - 1) / 2)
  b = int((m - 1) / 2)
  sub_f = f[x-a : x+a+1, y-b : y+b+1]
  sub_f = np.delete(sub_f, [1,1])
  r = np.sort(np.asarray(sub_f).reshape(-1))
  return int(np.median(r))

def conv_point_max(f, w, x, y):
  n,m = w.shape
  a = int((n - 1) / 2)
  b = int((m - 1) / 2)
  sub_f = f[x-a : x+a+1, y-b : y+b+1]
  return int(np.max(sub_f))

def conv_point_midpoint(f, w, x, y):
  n,m = w.shape
  a = int((n - 1) / 2)
  b = int((m - 1) / 2)
  sub_f = f[x-a : x+a+1, y-b : y+b+1]
  max = np.max(sub_f)
  min = np.min(sub_f)
  return int((max + min) / 2)

print('median of g(1, 2) =', conv_point_median(f, wm, 1, 2))
print('max of g(2, 2) =', conv_point_max(f, wm, 2, 2))
print('midpoint of g(2, 1) =', conv_point_midpoint(f, wm, 2, 1))


import math
from numba import jit, prange
import time
import numpy as np

@jit('i8(i8,i8)', nopython=True)
def gcd(a, b):
    """
    Reference:
    https://qiita.com/wrist/items/889696a507fbc8b392e4
    http://swdrsker.hatenablog.com/entry/2017/12/20/235201
    https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.remainder.html
    """
    while b != 0:
        #a, b = b, a % b
        a, b = b, np.remainder(a, b)
    return a

@jit('f8(i8)', nopython=True)
def calc_pi_by_gcd(N):
    s = 0
    for a in range(1, N+1):
        for b in range(1, N+1):
            if gcd(a, b) == 1:
                s += 1
    pi = math.sqrt(6*N**2/s)
    return pi

@jit('f8(i8)', nopython=True, parallel=True)
def calc_pi_by_gcd_parallel(N):
    s = 0
    for a in prange(1, N+1):
        for b in range(1, N+1):
            if gcd(a, b) == 1:
                s += 1
    pi = math.sqrt(6*N**2/s)
    return pi

def main():
    start = time.time()
    pi = calc_pi_by_gcd(10000)
    end = time.time()
    print(pi)
    print("Elapsed time=", end-start)

    start = time.time()
    pi = calc_pi_by_gcd_parallel(10000)
    end = time.time()
    print(pi)
    print("Elapsed time=", end-start)

if __name__ == '__main__':
    main()


import numpy as np
import innerproduct

def l2distance(X,Z=None):

    """
    function D=l2distance(X,Z)

    Computes the Euclidean distance matrix.
    Syntax:
    D=l2distance(X,Z)
    Input:
    X: nxd data matrix with n vectors (rows) of dimensionality d
    Z: mxd data matrix with m vectors (rows) of dimensionality d

    Output:
    Matrix D of size nxm
    D(i,j) is the Euclidean distance of X(i,:) and Z(j,:)

    call with only one input:
    l2distance(X)=l2distance(X,X)

    """

    if Z is None:
        Z = X

    n, d = X.shape
    m = Z.shape[0]

    G = innerproduct(X, Z)
    S = np.broadcast_to(innerproduct(X).diagonal().reshape(n, 1), (n,m))
    R = np.broadcast_to(innerproduct(Z).diagonal().reshape(m, 1).T, (n,m))

    D = np.sqrt(S - (2 * G) + R)

    assert(D.shape == (n, m))

    return D


import numpy as np

def sphere(x_positions):
    return np.array([x ** 2 for x in x_positions]).sum()

def f2(x_positions):
    return ((x_positions - np.arange(len(x_positions)))**2).sum()

def rosenbrock(x_positions):
    return (100 * (x_positions[1::] - x_positions[:-1] ** 2)**2 + (x_positions[:-1] - 1)**2).sum()

def rastrigin(x_positions):
    return (x_positions ** 2 - 10 * np.cos(2*np.pi*x_positions) + 10).sum()

def griewank(x_positions):
    return ((x_positions ** 2).sum()) / 4000 - np.prod(np.cos(x_positions / np.sqrt(np.arange(1, len(x_positions)+1)))) + 1

def ackley(x_positions):
    result = -20 * np.exp(-0.2 * np.sqrt((x_positions ** 2).sum()) / len(x_positions)) - \
             np.exp((np.cos(2 * np.pi * x_positions)).sum() / len(x_positions)) + 20 + np.e
    return result

def easom(x_positions):
    return -np.cos(x_positions[0]) * np.cos(x_positions[1]) * np.exp(-((x_positions[0] - np.pi)**2) - ((x_positions[1] - np.pi)**2))

def brown(x_positions):
    return (((x_positions[:-1] ** 2) ** (x_positions[1::] ** 2 + 1)) + ((x_positions[1::] ** 2) ** (x_positions[:-1] ** 2 + 1))).sum()

def schwefel(x_positions):
    return ((np.absolute(x_positions))**2).sum() + np.prod(np.absolute(x_positions))

def zakharov(x_positions):
    return -np.sum(x_positions) + (x_positions * np.arange(len(x_positions)) / 2).sum() ** 2 + (x_positions * np.arange(len(x_positions)) / 2).sum() ** 4

def schaffersf6(x_positions):
    return 0.5 + ((np.sin(np.sqrt(x_positions[0] ** 2 + x_positions[1] ** 2))) ** 2 - 0.5) / ((1 + 0.001 * (x_positions[0] ** 2 + x_positions[1] ** 2)) ** 2)

def leeyao_2004(x):
    result = np.pi / len(x) * (10 * ((np.sin(np.pi * x[0])) ** 2)
                               + (((x[:-1] - 1)**2) * (1 + 10 * ((np.sin(np.pi * x[1::]))**2))).sum()
                               + (x - 1)**2) + _leeyao_u(x)
    print(result)
    return result

def _leeyao_u(x_positions):
    a, k, m = 10, 100, 4
    result = []
    for z in x_positions:
        if z > a:
            result.append(k * ((z - a) ** m))
        elif -a <= z <= a:
            result.append(0)
        elif z < -a:
            result.append(k * ((-z-a) ** m))
    return np.array(result).sum()


import numpy as np

class Activate:
    @staticmethod
    def sigmoid(x):
        return 1/(1 + np.exp(-x))

    @staticmethod
    def dsigmoid(x):
        s = Activate.sigmoid(x)
        return s*(1-s)

    @staticmethod
    def tanh(x):
        return 2/(1 + np.exp(-2*x)) - 1

    @staticmethod
    def dtanh(x):
        return 1 - Activate.tanh(x)**2


#!/usr/bin/env python3

# FILE CONTENTS: special functions used in other files

import numpy as np

import scipy
from scipy import linalg

from sympy.functions.combinatorial.numbers import stirling as sympy_stirling

# factorial and its logarithm
def factorial(nn, vals = {}):
    if vals.get(nn) == None:
        vals[nn] = scipy.special.factorial(nn, exact = (type(nn) == int))
    return vals[nn]
def ln_factorial(nn, vals = {}):
    if vals.get(nn) == None:
        vals[nn] = scipy.special.gammaln(nn+1)
    return vals[nn]

# falling factorial and its logarithm
def poch(nn, kk, vals = {}):
    if vals.get((nn,kk)) == None:
        vals[nn,kk] = np.prod([ nn-cc for cc in range(kk) ], dtype = int)
    return vals[nn,kk]
def ln_poch(nn, kk, vals = {}):
    if vals.get((nn,kk)) == None:
        vals[nn,kk] = np.sum([ np.log(nn-cc) for cc in range(kk) ])
    return vals[nn,kk]

# binomial coefficient and its logarithm
def binom(nn, kk, vals = {}):
    if vals.get((nn,kk)) == None:
        integer_inputs = ( type(nn) is int ) and ( type(kk) is int )
        vals[nn,kk] = scipy.special.comb(nn, kk, exact = integer_inputs)
    return vals[nn,kk]

# unsigned stirling number of the first kind
def stirling(nn, kk, vals = {}):
    if vals.get((nn,kk)) == None:
        vals[nn,kk] = int(sympy_stirling(nn, kk, kind = 1, signed = False))
    return vals[nn,kk]

# coefficient for computing a product of spin operators
def zeta(mm, nn, pp, qq, vals = {}):
    if vals.get((mm,nn,pp,qq)) == None:
        val = (-1)**pp * 2**qq * np.sum([ stirling(pp,ss) * binom(ss,qq)
                                          * (mm+nn-2*pp)**(ss-qq)
                                          for ss in range(qq,pp+1) ], dtype = int)
        vals[mm,nn,pp,qq] = val
        vals[nn,mm,pp,qq] = val
    return vals[mm,nn,pp,qq]

# return vector in (z,x,y) format along an axis specified by text
def axis_str(text):
    sign, axis = text
    axis = axis.lower()
    assert(sign in [ "+", "-" ])
    assert(axis in [ "z", "x", "y" ])
    if axis == "z": vec = np.array([ 1, 0, 0 ])
    if axis == "x": vec = np.array([ 0, 1, 0 ])
    if axis == "y": vec = np.array([ 0, 0, 1 ])
    if sign == "-": vec = -vec
    return vec

# get polar and azimulthal angles of a vector (v_z, v_x, v_y)
def vec_theta_phi(vec):
    return np.array([ np.arccos(vec[0]/scipy.linalg.norm(vec)),
                      np.arctan2(vec[2],vec[1]) ])

# trig functions that check for "special" values
def my_cos(phi):
    if phi == 0 or abs(phi) == 2*np.pi: return 1
    if abs(phi) in [ np.pi/2, 3*np.pi/2 ]: return 0
    if abs(phi) == np.pi: return -1
    return np.cos(phi)
def my_sin(phi):
    if phi == 0 or abs(phi) in [ np.pi, 2*np.pi ]: return 0
    if abs(phi) == np.pi/2: return np.sign(phi)
    if abs(phi) == 3*np.pi/2: return -np.sign(phi)
    return np.sin(phi)
def my_expi(phi):
    return my_cos(phi) + 1j*my_sin(phi)

import numpy,import scipy,from scipy,from sympy


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

# Diferenças finitas:

# Exemplo:
def sen(x):
    return np.sin(x)

'Problema calcular o valor de Df para x in [0, pi] aproximar. '

def d_sen(x, n):
    h = np.pi / n
    return (sen(x + h) - sen(x)) / h

# -----------------------------------------------------------------------
# Método de Euler:

# Solução para o problema y' = y, com y'(t) = x.

# Condições de valor inicial:
# Ponto incial:
to = 0
# Valor incial:
yo = 1

b = 10
n = 1000

h = int(abs(b - to)) / n

x = np.linspace(to, b, n + 1)

F = np.zeros(n + 1)
F[0] = yo

for i in range(1, n + 1):
    F[i] = (h + 1) * F[i - 1]
# Plot solução real:

plt.plot(x, np.exp(x))
# Plot solução aprox.
plt.plot(x, F)

plt.show()

# ---------------------------------------------------------------------------
# Método de Monte Carlo.

# Pontos sorteados:
m = 10000
# Verificação dos pontos

Circulo = 0.0
to = time.time()
for k in range(m):
    x = np.random.rand() * 2
    y = np.random.rand() * 2
    if (x - 1) ** 2 + (y - 1) ** 2 <= 1:
        Circulo += 1
t = time.time() - to
pi = 4 * (Circulo / m)
Erro = abs(np.pi - pi)
# print('Valor aprox. : {}, Valor de Erro: {} e tempo de execução: {}'.format(pi, Erro, t))

def f(x, y):
    return x ** 2 + y ** 2


import numpy as np
import sys

from collections import deque

class PacLinearRegression():
    """
    A class used to fit a linear regression to a dataset, given features X and
    label y provided that both are in the form of numpy arrays.

    ...

    Attributes
    ----------
    learning_rate : float
        learning rate of the linear regression for gradient descent
    cost_function : str
        type of cost function to be used (mae or mse)
    max_iter : int
        number of gradient descent iteration (10x higher for mae)
    X : array of float/int
        the feature array of the dataset
    y : array of float/int
        the label array of the dataset
    theta0 : arrray of float
        slope parameter(s) of linear regression
    theta1 : float
        intercept/bias parameter of linear regression
    m : int
        number of data points
    cost_hist: list of float
        the historical cost function values
    coeff_: list of float
        the list representation of theta0 and theta1, respectively

    Methods
    -------
    fit(X, y):
        Fit a linear regression to a data by calling gradientDescent method.
    computeCostFunction(y_pred):
        Return cost value per specified cost function.
    gradientCalculation(y_pred):
        Calculate gradients for theta0 and theta1 provided the type of cost
        function.
    gradientDescent():
        Create a loop of gradient descent method, appending cost_hist per iter.
    predict(X):
        Predict the label given feature X.
    score(y_val, y_pred):
        Return the R-squared metric of the model.
    """

    def __init__(self, max_iter=1e4, learning_rate=1e-4,
                cost_function='mse', early_stop=False):
        """
        Constructs necessary attributes for the PacLinearRegression object.
        Attributes are introduced within fit method for a more concise
        structure.

        Parameters
        ----------
            learning_rate : float
                learning rate of the linear regression for gradient descent
            cost_function : str
                type of cost function to be used (mae or mse)
            max_iter : int
                number of gradient descent iteration (10x higher for mae)
        """

        self.learning_rate = learning_rate
        self.cost_function = cost_function
        if cost_function == 'mae':
            self.max_iter = int(max_iter * 10)
        else:
            self.max_iter = int(max_iter)
        self.early_stop = early_stop

    def fit(self, X, y):
        """
        Fit a linear regression to a data by calling gradientDescent method.

        Parameters
        ----------
            X : array of float/int
                the feature array of the dataset
            y : array of float/int
                the label array of the dataset
            theta0 : arrray of float
                slope parameter(s) of linear regression
            theta1 : float
                intercept/bias parameter of linear regression
            m : int
                number of data points
            cost_hist: list of float
                the historical cost function values
            coeff_: list of float
                the list representation of theta0 and theta1, respectively

        Returns
        -------
            self : fitted PacLinearRegression class
        """

        try:
            self.X = X.reshape(len(X), X.shape[1])
            self.theta0 = np.zeros(X.shape[1]).reshape(X.shape[1], -1)
        except:
            self.X = X.reshape(len(X), 1)
            self.theta0 = 0
        self.y = y.reshape(len(y), 1)
        self.theta1 = 0
        self.m = len(X)
        self.cost_hist = self.gradientDescent()
        self.coeff_ = [i[0] for i in self.theta0] + [self.theta1]
        return self

    def computeCostFunction(self, y_pred):
        """Return cost value per specified cost function."""
        if self.cost_function == 'mse':
            return (1/self.m) * sum((self.y - y_pred)**2)
        elif self.cost_function == 'mae':
            return (1/self.m) * sum(abs((self.y - y_pred)))
        else:
            print('Error: cost_function is not valid.')
            sys.exit()

    def gradientCalculation(self, y_pred):
        """
        Calculate gradients for theta0 and theta1 provided the type of cost
        function.

        Parameters
        ----------
            y_pred : array of float/int
                the predicted label of current iteration.

        Returns
        -------
            None
        """

        if self.cost_function == 'mse':
            dtheta0 = (-1/self.m) * self.X.T.dot(self.y - y_pred)
            dtheta1 = (-1/self.m) * np.sum(self.y - y_pred)
        elif self.cost_function == 'mae':
            y_adj = self.y - y_pred
            y_adj[y_adj==0] = 0
            y_adj[y_adj<0] = -1
            y_adj[y_adj>0] = 1
            dtheta0 = (-1/self.m) * np.sum(y_adj)
            dtheta1 = (-1/self.m) * np.sum(y_adj)
        self.theta0 -= self.learning_rate * dtheta0
        self.theta1 -= self.learning_rate * dtheta1

    def gradientDescent(self):
        """
        Create a loop of gradient descent method, appending cost_hist per iter.
        An early stopping criterion is also implemented using relative error of
        average cost function per 100 appended values. The value set is 0.005%.

        Parameters
        ----------
            None

        Returns
        -------
            cost_hist: list of float
                the historical cost function values
        """

        cost_hist, cost_threshold = [], []
        cost_mean = deque(maxlen=100)
        threshold = 100
        for i in range(self.max_iter):
            y_pred = self.predict(self.X)
            cost_hist.append(self.computeCostFunction(y_pred))
            cost_mean.append(self.computeCostFunction(y_pred))
            if (i+1 >= threshold) and self.early_stop:
                cost_threshold.append(np.mean(cost_mean))
                try:
                    cut_off = abs((cost_threshold[-1] - cost_threshold[-2]) \
                                * 100 / cost_threshold[-2])
                    if cut_off < 0.001:
                        self.max_iter = len(cost_hist)
                        return cost_hist
                except:
                    continue
            self.gradientCalculation(y_pred)
        return cost_hist

    def predict(self, X):
        """Predict the label given feature X."""
        return X.dot(self.theta0) + self.theta1

    def score(self, y_val, y_pred):
        """Return the R-squared metric of the model."""
        y_pred = [i[0] for i in y_pred]
        y_bar = np.mean(y_val)
        denom = np.sum((y_bar - y_val)**2)
        numer = (denom - np.sum((y_pred - y_val)**2))
        R_squared = numer / denom
        return R_squared


def golden_section(f, a, b, tol=1e-5, **kwargs):
    """
    Golden section search.

    Stolen from https://en.wikipedia.org/wiki/Golden-section_search

    Given a function f with a single local minimum in
    the interval [a,b], gss returns a subset interval
    [c,d] that contains the minimum with d-c <= tol.

    example:
    >>> f = lambda x: (x-2)**2
    >>> a = 1
    >>> b = 5
    >>> tol = 1e-5
    >>> (c,d) = golden_section(f, a, b, tol)
    >>> print (c,d)
    (1.9999959837979107, 2.0000050911830893)
    """
    import numpy as np

    # 1/phi
    invphi = (np.sqrt(5) - 1) / 2
    # 1/phi^2
    invphi2 = (3 - np.sqrt(5)) / 2

    (a, b) = (min(a, b), max(a, b))
    h = b - a
    if h <= tol:
        return (a, b)

    # required steps to achieve tolerance
    n = int(np.ceil(np.log(tol / h) / np.log(invphi)))

    c = a + invphi2 * h
    d = a + invphi * h
    yc = f(c, **kwargs)
    yd = f(d, **kwargs)

    for k in range(n - 1):
        if yc < yd:
            b = d
            d = c
            yd = yc
            h = invphi * h
            c = a + invphi2 * h
            yc = f(c, **kwargs)
        else:
            a = c
            c = d
            yc = yd
            h = invphi * h
            d = a + invphi * h
            yd = f(d, **kwargs)

    if yc < yd:
        return (a, d)
    else:
        return (c, b)


import math as mt
def velocity(t,g=9.8,m=78.5,c=12.5):
    return (g*m*(1-mt.exp(-(c/m)*t)))/c
ti=[]
t0=0 # Waktu Awal
t1=12 # Waktu Akhir
n=12000
h=(t1-t0)/n
fa=velocity(t0)
fn=velocity(t1)
## pengisian matriks
while t0<t1+h:
    ti.append(t0)
    t0=t0+h
print('nilai t=',ti)
panjang=len(ti)
####
velo=[]
for i in range (0, panjang):
    v=velocity(ti[i])
    velo.append(v)
print('Nilai hasil fungsi =',velo)
###
length=len(velo)
sigma=0
for i in range(1,length-1):
    sigma=sigma+velo[i]
print("sigma =",sigma)
## METODE TRAPEZOID
hasil=(fa+2*sigma+fn)*h/2
print("Hasil Trapezoid =", hasil)
####
sigmaGanjil=0
sigmaGenap=0
#METODE SIMPSON
for i in range(1,length-1):
    if i%2!=0 :
        sigmaGanjil=sigmaGanjil+velo[i]
    else:
        sigmaGenap=sigmaGenap+velo[i]
print("hasil sigma ganjil = ",sigmaGanjil,", hasil sigma genap =",sigmaGenap)
simpson=(fa+4*sigmaGanjil+2*sigmaGenap+fn)*h/3
print("Nilai Hasil Simpson =",simpson)

from sympy import *
t=Symbol('t')
g=9.8
m=78.5
c=12.5
fd=(g*m*(1-exp(-(c/m)*t)))/c
integral=fd.integrate(t)
print("Hasil Integral=",integral)

from scipy import integrate
hasilIntegral=integrate.quad(velocity,0,12)
print("Hasil Integral Eksak",hasilIntegral)

er_simson=abs(simpson-hasilIntegral[0])
er_trape=abs(hasil-hasilIntegral[0])
print("Hasil Eror Sympson = ", er_simson)
print("Hasil Eror Trapezoid = ", er_trape)

Chapter 9/Tugas Nomor 1 Integrasi Numerik.py


__author__ = "Nadimozzaman Pappo"
__github__ = "http://github.com/mnpappo"

"""
This scripts simulates(by 2d ploting) various activation functions used in ANN.
"""

import theano
import theano.tensor as T
import matplotlib.pyplot as plt
import numpy as np

# sigmoid/logistic function
def sigmoid(a):
    x = T.dmatrix('x')
    s = 1/(1+T.exp(-x))
    logistic = theano.function([x], s)  # performs elementwise operations
    result = logistic([a])  # we got the logistic value for every element in i
    return result

# sample input
a = np.arange(-10, 10, .2)
result = sigmoid(a)
# A plot of the logistic function, with a on the x-axis and sigmoid(a) on
# the y-axis
# our logistic theano func retuns a 2d array/matrix thats why we neeed to
# flatten it to plot
plt.plot(a, np.array(result).flatten())
plt.title('logistic/sigmoid function')
plt.grid(True)
plt.xlabel("input", color='red')
plt.ylabel("logistic output", color='red')
plt.show()

# tanh function
def tanh(a):
    x = T.dmatrix('x')
    p, q = T.exp(x)-T.exp(-x), T.exp(x)+T.exp(-x)
    t = p/q
    tanhfunc = theano.function([x], t)
    result = tanhfunc([a])
    return result

# sample input
a = np.arange(-10, 10, .2)
result = tanh(a)

plt.plot(a, np.array(result).flatten())
plt.title('tanh function')
plt.grid(True)
plt.xlabel("input", color='red')
plt.ylabel("tanh output", color='red')
plt.show()


import numpy as np

def normalize_data(x):
    """Function to normalize data using standard score / z-score.
    Args:
        x            (numpy array): Matrix features of size N x D.
    Returns:
        normalized_x (numpy array): Matrix features of size N x D, with its data normalized.
    """

    # Normalization using Standard Score
    # Standardize data by substracting with mean and dividing with standard deviation
    mean         = np.mean(x, axis = 0)
    std_dev      = np.std(x, axis = 0)
    normalized_x = (x - mean) / std_dev

    return normalized_x

def clean_data(x):
    """Function to clean outliers (-999 value) in matrix features and replace them with median.
    Args:
        x (numpy array): Matrix features of size N x D.
    Returns:
        x (numpy array): Matrix features of size N x D, with outliers replaced by median of each feature.
    """

    # locate useful values and outliers (-999)
    mask     = (x != -999)
    outliers = (~mask)

    # Replacing outliers (-999) with median (per feature) of the rest of the data
    x[outliers] = (x[outliers] * 0) + np.median(x[mask], axis = 0)

    return x

def log_basis(x):
    """Function to generate logarithmic basis function for matrix features of x.
    Args:
        x              (numpy array): Matrix features of size N x D.
    Returns:
        log_features_x (numpy array): Matrix log features of size N x 2D.
    """

    # Find minimum value per feature
    min_               = np.min(x, axis=0)

    # Generate log feature of ( 1 / (1 + log(1+x-min)) )
    log_x_1            = 1 / (1 + np.log(1 + x - min_))

    # normalized log feature 1
    normalized_log_x_1 = normalize_data(log_x_1)

    # Generate log feature of ( log(1+x-min) )
    log_x_2            = np.log(1 + x - min_)

    # normalized log feature 2
    normalized_log_x_2 = normalize_data(log_x_2)

    # combine log feature 1 and log feature 2 as one single numpy array
    log_features_x = np.concatenate((normalized_log_x_1, normalized_log_x_2), 1)

    return log_features_x

def polynomial_basis(x, degree):
    """Function to generate polynomial basis function for matrix features of x.
    Args:
        x               (numpy array)        : Matrix features of size N x D.
        degree          (int, scalar)        : Degree of polynomial basis function.
    Returns:
        poly_features_x (list of numpy array): Matrices polynomial features with each size of N x ((degree-1) * D).
    """

    # locate useful values and outliers (-999)
    mask     = (x != -999)
    outliers = (~mask)

    # for power>= 2, store each polynomial basis features into list
    poly_features_x = []

    # iterate through each degree, 2 <= degree < power+1
    for power in range(2, degree + 1):
        # Generate matrix power of features
        poly_x           = np.power(x, power)

        # Find mean of non-outlier values
        mean_poly_x      = np.sum( poly_x * mask, axis=0)/ np.sum(mask, axis=0)

        # Find standard deviation of non-outlier values
        std_dev_poly_x   = np.sqrt( np.sum( ((poly_x - mean_poly_x) * mask)**2, axis=0)/np.sum(mask, axis=0))

        # Normalization using Standard Score, apply to non-outlier values only
        # Standardize data by substracting with mean and dividing with standard deviation
        poly_x           = (poly_x * mask - mean_poly_x)/std_dev_poly_x

        # Replace outliers by zero
        poly_x[outliers] = 0

        # add to list
        poly_features_x.append(poly_x)

    return poly_features_x

def cross_terms_basis(x):
    """Function to generate cross term function for matrix features of x.
    Args:
        x              (numpy array): Matrix features of size N x D.
    Returns:
        log_features_x (numpy array): Matrix cross term features of size N x (D-2)!.
    """

    # store each cross-term basis features into list
    cross_features_x = []

    # Iterate through features and create the cross-term
    for i in range(x.shape[1]):
        for j in range(i+1, x.shape[1]):
            cross_term = (x[:, i] * x[:, j])
            cross_term = normalize_data(cross_term)
            cross_features_x.append(cross_term)

    cross_features_x = np.array(cross_features_x).squeeze().T

    return cross_features_x

def sqrt_basis(x):
    """Function to generate square root basis function for matrix features of x.
    Args:
        x      (numpy array): Matrix features of size N x D.
    Returns:
        sqrt_x (numpy array): Matrix square root features of size N x D.
    """

    # Find minimum value per feature
    min_ = np.min(x, axis=0)

    # generate square root term
    sqrt_x = np.sqrt(x - min_)

    # normalize square root features using standard score
    sqrt_x = normalize_data(sqrt_x)

    return sqrt_x

def generate_features(x, degree, with_ones = True, with_log = False, with_sqrt = False, cross_terms = False):
    """Function to standardize data input and generate features.
    Args:
        x              (numpy array)            : Matrix features of size N x D.
        degree         (int, scalar)            : Degree of polynomial basis function.
        with_ones      (boolean, default true)  : Boolean value to indicate either adding feature column with ones value or not.
        with_logs      (boolean, default false) : Boolean value to indicate either generating features with log basis function or not.
        with_sqrt      (boolean, default false) : Boolean value to indicate either generating features with square root basis function or not.
        cross_terms    (boolean, default false) : Boolean value to indicate either generating features with cross terms (Xi*Xj) basis function or not.
    Returns:
        preprocessed_x (numpy array)            : Final matrix features to be processed using ML methods. Minimal size of N x D.
    """

    # Remove outliers from matrix features
    x_without_outliers = clean_data(x)

    # normalize matrix features (without outliers) using standard score (z-score)
    normalized_x       = normalize_data(x_without_outliers)

    # Form matrix features at least with normalized features
    preprocessed_x     = normalized_x

    # if generating with logarithmic basis function
    if with_log:
        # generate logarithmic basis function for matrix input x
        log_features_x = log_basis(x)

        # combine logarithmic basis features into final matrix
        preprocessed_x = np.concatenate((preprocessed_x, log_features_x), axis = 1)

    # generate polynomial basis function for matrix input x
    poly_features_x    = polynomial_basis(x, degree)

    # iterate through the polynomial features of x (through degree)
    for poly_x in poly_features_x:
        preprocessed_x = np.concatenate((preprocessed_x, poly_x), axis=1)

    # if adding feature column with ones value.
    if with_ones:
        # ---------- Add ones to the matrix --------
        tmp       = np.ones([preprocessed_x.shape[0], preprocessed_x.shape[1] + 1])
        tmp[:,1:] = preprocessed_x
        preprocessed_x   = tmp

    # if generating with cross term basis function
    if cross_terms:
        # generate cross term basis function for matrix input x
        cross_term_features = cross_terms_basis(x)

        # combine cross term basis features into final matrix
        preprocessed_x = np.concatenate((preprocessed_x, cross_term_features), axis = 1)

    # if generating with square root basis function
    if with_sqrt:
        # generate square root basis function for matrix input x
        sqrt_features = sqrt_basis(x)

        # combine square root basis features into final matrix
        preprocessed_x = np.concatenate((preprocessed_x, sqrt_features), axis = 1)

    return preprocessed_x


%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

def func(x):
    a = -2
    b = 10
    return np.exp(a*x) * np.cos(b*x)

def func_integral(x):
    a = -2
    b = 10
    c = 5
    d = 52
    return np.exp(a*x) * (c* np.sin(b*x) - np.cos(b*x)) * (1/d)

def trapezoid_core(f,x,h):
    return 0.5 * h * (f(x + h) + f(x))

def trapezoid_method(f,a,b,N):
    x = np.linspace(a,b,N)
    h = x[1] - x[0]

    fint = 0.0

    for i in range(0,len(x) - 1, 1):
        fint += trapezoid_core(f,x[i],h)

    print("Iterations = " + str(i))
    return(fint)

def simpson_core(f,x,h):
    return (h/3)* (f(x) + 4*f(x+h) + f(x+2*h))

def simpsons_method(f,a,b,N):
    x = np.linspace(a,b,N)
    h = x[1] - x[0]

    Fint = 0.0

    for i in range (0, len(x) - 2, 2):
        Fint += simpson_core(f,x[i],h)

    if (N%2 == 0):
        Fint += simpson_core(f,x[-2], 0.5 * h)

    print("Iterations = " + str(i))
    return Fint

def romberg_core(f,a,b,i):
    h = b - a
    dh = h/2.**i
    K = h/2.**(i+1)

    M = 0.0
    for j in range(2**i):
        M += f(a + 0.5*dh + j*dh)

    return(K*M)

def romberg_integration(f,a,b,tol):

    i = 0

    imax = 1000

    delta = 100.0*np.fabs(tol)

    I = np.zeros(imax,dtype=float)

    I[0] = 0.5*(b-a)*(f(a) + f(b))

    i +=1

    while(delta>tol):

        I[i] = 0.5*I[i-1] + romberg_core(f,a,b,i)

        delta = np.fabs( (I[i] - I[i-1]) / I[i])

        print(i,I[i],I[i-1],delta)

        if (delta>tol):
            i += 1

            if (i > imax):
                print('Max iterations reached.')
                raiseStopIteration('Stopping iterations after ', i)

    print("Iterations = " + str(i))
    return(I[i])

print("function = e^(-2x) * cos(10x)")
answer = func_integral(np.pi) - func_integral(0)
print("Integral = " + str(answer))
print(" ")
print('Trapezoid method')
print(trapezoid_method(func,0,np.pi,10))
print(" ")
print("Simpson's method")
print(simpsons_method(func,0,np.pi,10))
print(" ")
print("Romberg's method")
tolerance = 1e-6
RI = romberg_integration(func,0,np.pi,tolerance)
print(RI, (RI - answer)/answer, tolerance


import numpy as np

def bisection(func, a=0, b=1, tol=1e-6, maxits=1000, which="min"):
    """Implements a basic bisection search on the function f over the interval [a,b].

    Args:
        f (func): the function to optimize.
        a (float): left end-point to optimize over.
        b (float): the right end-point to optimize over.
        tol (float): relative tolerance for when to stop iterating.
        maxits (int): maximum number of iterations.
    """
    if which == "max":
        func = lambda x: func(-x)

    if func(a)*func(b) >= 0:
        raise ValueError("Bisection failed... f(a) and f(b) must be of oposite sign!")

    if func(a) == 0:
        return a

    if func(b) == 0:
        return b

    for i in range(maxits):
        midpoint = (b+a)/2
        f_mid = func(midpoint)
        f_a = func(a)
        f_b = func(b)

        if f_a*f_mid < 0:
            b = midpoint
        elif f_b*f_mid < 0:
            a = midpoint
        elif f_mid == 0:
            return midpoint

        if np.abs((b+a)/2 - midpoint) < tol:
            return (b+a)/2
        else:
            pass

def newton(f, df, x0, epsilon, maxiter):
    x = x0
    for n in range(0, maxiter):
        f_x = f(x)
        if abs(f_x) < epsilon:
            return x
        df_x = df(x)
        if df_x == 0:
            print("Error: derivative is zero")
        x -= f_x/df_x
    return x


import numpy as np
import time
from inspect import signature

def nparams(f):
	return len(signature(f).parameters)

def sign(x):
	if x >= 0:
		return 1.0
	else:
		return -1.0

def FindMinima(fn, gradfn, eta=0.1, threshold=1e-3):
	# Determine the number of arguments to the gradient.
	n_params = len(signature(gradfn).parameters)

	# Create a randomized starting position.
	position = (np.random.rand(n_params) - 0.5) * 20
	gradient = np.ones(n_params)

	while True:
		# Calculate the gradient.
		gradient = gradfn(*position)
		#print(gradient)

		# Check for zero gradient.
		closeness = np.abs(gradient).max()
		if closeness < threshold:
			break

		# Move each coordinate based on the gradient.
		cofactor  = ((closeness**4) / (1 + closeness**4)) + (eta / 10)
		for c in range(n_params):
			position[c] -= eta * sign(gradient[c]) * cofactor
			#position[c] -= eta * gradient[c] * cofactor

	# We may have reached a minimum. Return this position.
	return position

def Minimize(fn, gradfn, std_threshold=0.01, n_best=10):
	# Determine the number of arguments to the gradient.
	n_params = len(signature(gradfn).parameters)

	positions = []
	minima    = []
	# Find enough minima to start looking for convergence.
	for i in range(n_best):
		pos = FindMinima(fn, gradfn)
		val = fn(*pos)[0]
		minima.append(val)
		positions.append(pos)

	minima.sort()
	while np.array(minima[:n_best]).std() > std_threshold:
		print(len(minima))

		pos = FindMinima(fn, gradfn)
		val = fn(*pos)[0]
		minima.append(val)
		print(val)
		positions.append(pos)
		minima.sort()

	minidx  = np.array(minima).argmin()
	minimum = minima[minidx]
	minpos  = positions[minidx]
	return minimum, minpos

def f(x, y, z):
	return [-np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*(np.sin(x) + np.sin(y) + np.sin(z))]

def gradf(x, y, z):
	return [
		-np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*np.cos(x) + (2 / 25)*x*np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*(np.sin(x) + np.sin(y) + np.sin(z)),
		-np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*np.cos(y) + (2 / 25)*y*np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*(np.sin(x) + np.sin(y) + np.sin(z)),
		-np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*np.cos(z) + (2 / 25)*z*np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*(np.sin(x) + np.sin(y) + np.sin(z))
	]

if __name__ == '__main__':
	start = time.time_ns()
	minimum, minpos = Minimize(f, gradf)
	stop = time.time_ns()
	print("Minimum: %f"%minimum)
	print("Pos:     %s"%str(minpos))
	print("Time:    %fs"%((stop - start) / 1e9))


import math as mt
def poisel(z,L=91.44,u=0.01,d=0.1,p=1,g=9.81,R=10):
    a=64*L*u/(p*d**2)
    return (8*R**2)/(d**2)*(-a+mt.sqrt(a**2+8*g*(z+L)))
ti=[]
t0=5 # Waktu Awal
t1=50 # Waktu Akhir
n=5000
h=(t1-t0)/n
fa=poisel(t0)
fn=poisel(t1)
## pengisian matriks
while t0<t1+h:
    ti.append(t0)
    t0=t0+h
print('nilai t=',ti)
panjang=len(ti)
####
pois=[]
for i in range (0, panjang):
    v=poisel(ti[i])
    pois.append(v)
print('Nilai hasil fungsi =',pois)
###
length=len(pois)
sigma=0
for i in range(1,length-1):
    sigma=sigma+pois[i]
print("sigma =",sigma)
## METODE TRAPEZOID
hasil=(fa+2*sigma+fn)*h/2
print("Hasil Trapezoid =", hasil)
####
sigmaGanjil=0
sigmaGenap=0
#METODE SIMPSON
for i in range(1,length-1):
    if i%2!=0 :
        sigmaGanjil=sigmaGanjil+pois[i]
    else:
        sigmaGenap=sigmaGenap+pois[i]
print("hasil sigma ganjil = ",sigmaGanjil,", hasil sigma genap =",sigmaGenap)
simpson=(fa+4*sigmaGanjil+2*sigmaGenap+fn)*h/3
print("Nilai Hasil Simpson =",simpson)

from sympy import *
z=Symbol('z')
L=91.44
u=0.01
d=0.1
p=1
g=9.81
R=10
a=64*L*u/(p*d**2)
fd=(8*R**2)/(d**2)*(-a+sqrt(a**2+8*g*(z+L)))
integral=fd.integrate(z)
print("Hasil Integral=",integral)

from scipy import integrate
hasilIntegral=integrate.quad(poisel,5,50)
print("Hasil Integral Eksak",hasilIntegral)

er_simson=abs(simpson-hasilIntegral[0])
er_trape=abs(hasil-hasilIntegral[0])
print("Hasil Eror Sympson = ", er_simson)
print("Hasil Eror Trapezoid = ", er_trape)

Chapter 9/Tugas Nomor 2 Integrasi Numerik.py


# coding:utf8
print("一.一维矩阵的加，减,平方，三角函数")
import  numpy as np
a=np.array([10,20,30,40])
b=np.arange(4)#0,1,2,3
c=b**2
d=np.sin(a)
e=np.cos(a)
f=np.tan(a)
print(a+b)
print(a-b)
print(c)
print(d)
print(e)
print(f)
print(b<3)#返回Ture或者False,bool类型的矩阵

print("二.多维矩阵的乘法")
import  numpy as np
a=np.array([[1,1],
            [0,1]])
b=np.arange(4).reshape((2,2))
c=a*b#两个同型矩阵对应元素的乘积
c_dot=np.dot(a,b)#矩阵的乘法运算
c_dot_2=a.dot(b) #矩阵ab的乘积
print(c)
print(c_dot)
print(c_dot_2)

print("三.多维矩阵行列运算")
import  numpy as np
a=np.array([[1,2,3],[2,3,4]])#shape=2x4
print(a)
print(np.sum(a)) #15
print(np.max(a)) #4
print(np.min(a)) #1
print(np.sum(a,axis=1)) #行求和[6,9]
print(np.sum(a,axis=0)) #列求和[3,5,7]
print(np.max(a,axis=0)) #列最大[2,3,4]
print(np.min(a,axis=1)) #行最小[1,2]

print("四.矩阵的运算 ")
import  numpy as np
A=np.arange(2,14).reshape(3,4)
print(A)
print(np.argmin(A))  #0
print(np.argmax(A))  #11
print(np.mean(A))  # 均值7.5
print(A.mean())    #均值 7.5
print(np.average(A))# 均值7.5
print(np.median(A))  #中位数7.5
print(np.cumsum(A)) # 累加[ 2  5  9 14 20 27 35 44 54 65 77 90]
print(np.diff(A))#后一个元素减去前一个元素
#将所有非零元素的行与列坐标分割开，重构成两个分别关于行和列的矩阵
print(np.nonzero(A))

print("五.矩阵的排序 ")
import  numpy as np
A=np.arange(14,2,-1).reshape(3,4)#从14到3，步长为-1
print(A)
print(np.sort(A))  #每一行排序
print(np.sort(A, axis=0))  #每一列排序

print("六.矩阵的转置有两种表示方法")

import  numpy as np
A=np.arange(1,10).reshape(3,3)
print(A)
print(np.transpose(A))
print(A.T)

print("七.矩阵截取clip")
import numpy as np
a=np.arange(1,13).reshape((3,4))
print(a)
print(np.clip(a,5,9))#最小5，最大9，小于5的都成了5，大于9的都成了9


import numpy as np
import matplotlib.pyplot as plt

def cost_function(X, y, theta, m):
    a = np.dot(X, theta) # h(X)
    b = a - y[:][np.newaxis].T
    return np.dot(b.T, b)[0][0] / 2 / m

def gradient_descent(X, y, iteration=1500, alpha=0.01):
    m = len(X) # num of samples
    theta = np.zeros((1, 2)).T # initiate theta = [0, 0]
    X = np.concatenate((np.ones((m, 1)), X[np.newaxis][:].T), axis=1) # add a colum of one to X

    for i in range(iteration):
        theta_temp = theta
        a = np.dot(X, theta_temp) # h(X)
        b = a - y[:][np.newaxis].T
        theta[0] = theta_temp[0] - alpha * np.dot(b.T, X[:,0]) / m
        theta[1] = theta_temp[1] - alpha * np.dot(b.T, X[:,1]) / m

    return theta

def main():
    data = np.loadtxt('./ex1/ex1/ex1data1.txt', delimiter=',', )
    X = data[:, 0]
    y = data[:, 1]

    # plot the data
    plt.scatter(X, y, marker='x', c='red')
    plt.xlabel('Population of City in 10,000s')
    plt.ylabel('Profit in $10,000s')

    theta = gradient_descent(X, y)
    print('Profile in areas of 35,000 is: %.2f' % np.dot(np.array([1, 3.5]), theta))
    print('Profile in areas of 70,000 is: %.2f' % np.dot(np.array([1, 7.0]), theta))

    # plot the prediction
    temp_x = np.linspace(5, 25, 20)
    temp_y = theta[0] + theta[1] * temp_x
    plt.plot(temp_x, temp_y)

    plt.legend(['Linear regression', 'Training data'], loc='lower right')
    plt.show()

if __name__ == '__main__':
    main()


import numpy as np

def wtd_mean(values, weights):
    '''
    Calculates the weighted mean of a set of values
    :param values: The values to take the weighted mean of
    :param weights: The weights
    :return:
    '''
    values = np.array(values)
    weights = np.array(weights)
    return np.dot(values, weights) / weights.sum()

def lin_interp(x, x1, y1, x2, y2):
    '''
    Performs linear interpolation between (x1,y1) and (x2,y2) to the point x.
    If x is not between x1 and x2, no worries, we just follow the linear map onwards.
    The map is f(x) = m (x-x1) + y1, where
    m=(y2-y1)/(x2-x1)
    However, if x1==x2, our function is the constant function
    f(x) = (y1+y2)/2
    :param x: The input value to interpolate to
    :param x1: The x coordinate of our first point
    :param y1: The y coordinate of our first point
    :param x2: The x coordinate of our second point
    :param y2: The y coordinate of our second point
    :return: The linearly interpolated value as described above
    '''
    if x1 == x2:
        return (y2+y1)/2
    m = (y2-y1)/(x2-x1)
    #print("x="+str(x)+"interp ("+str(x1)+","+str(y1)+") to ("+str(x2)+","+str(y2)+")")
    return m*(x-x1)+y1

def approx_equal(a, b, per_diff=1e-15) -> bool:
    '''
    Checks if 2 values are approximately equal, but checking
    | a - b | / (avg(|a|, |b|) < per_diff
    note: if a==b==0 then we return True, and do not divide by zero
    :param a: The first value to check
    :param b: The second value to check
    :param per_diff: The maximum percent difference to allow.
    :return: True/False
    '''
    if (a == 0) and (b == 0):
        return True
    return np.abs(b-a)/((np.abs(a)+np.abs(b))/2) <= per_diff

def wtd_median(values, weights):
    '''
    Calculates the weighted median of a set of values.
    :param values: The values to calculated the weighted median of
    :param weights: The weights of the values
    :return: The weighted median
    '''
    pairs = list(zip(values, weights))
    sorted_pairs = sorted(pairs, key=lambda x: x[0])
    #print("Sorted Pairs="+str(sorted_pairs))
    total_weight = np.sum(weights)
    half_way = total_weight/2
    if len(values) == 0:
        # Nothing to do
        return 0
    elif len(values) == 1:
        # We have only 1 value
        return values[0]
    #Now we have at least 2 values, we start with first value
    prev_area = 0
    xs_with_cumulative_area_equal = []
    for i in range(len(sorted_pairs)):
        (x, weight) = sorted_pairs[i]
        area = prev_area+weight
        #print("area="+str(area)+" prev_area="+str(prev_area)+" str")
        if approx_equal(area, half_way) and len(xs_with_cumulative_area_equal) == 0:
            # Okay we are equal
            next_x = sorted_pairs[i+1][0]
            xs_with_cumulative_area_equal.append(x+0.5)
        elif area > half_way:
            # We have gone from being below to being above, the median is here
            # Unless we have other places with area equal
            if len(xs_with_cumulative_area_equal) > 0:
                # Okay we need to average the previous equal areas
                xs_with_cumulative_area_equal.append(x-0.5)
                return np.mean(xs_with_cumulative_area_equal)
            # Alright nothing was previously equal, do this
            (prev_x, prev_weight) = sorted_pairs[i-1]
            if i!=(len(sorted_pairs)-1):
                next_x = sorted_pairs[i+1][0]
            else:
                next_x = (x - prev_x) + x
            return lin_interp(half_way, prev_area, x-0.5, area, x+0.5)
        prev_area = area
    return None


from torch import nn, tensor, max
import numpy as np

# Cross entropy example
# One hot
# 0: 1 0 0
# 1: 0 1 0
# 2: 0 0 1
Y = np.array([1, 0, 0])
Y_pred1 = np.array([0.7, 0.2, 0.1])
Y_pred2 = np.array([0.1, 0.3, 0.6])
print(f'Loss1: {np.sum(-Y * np.log(Y_pred1)):.4f}')
print(f'Loss2: {np.sum(-Y * np.log(Y_pred2)):.4f}')

# Softmax + CrossEntropy (logSoftmax + NLLLoss)
loss = nn.CrossEntropyLoss()

# target is of size nBatch
# each element in target has to have 0 <= value < nClasses (0-2)
# Input is class, not one-hot
Y = tensor([0], requires_grad=False)

# input is of size nBatch x nClasses = 1 x 4
# Y_pred are logits (not softmax)
Y_pred1 = tensor([[2.0, 1.0, 0.1]])
Y_pred2 = tensor([[0.5, 2.0, 0.3]])

l1 = loss(Y_pred1, Y)
l2 = loss(Y_pred2, Y)

print(f'PyTorch Loss1: {l1.item():.4f} \nPyTorch Loss2: {l2.item():.4f}')
print(f'Y_pred1: {max(Y_pred1.data, 1)[1].item()}')
print(f'Y_pred2: {max(Y_pred2.data, 1)[1].item()}')

# target is of size nBatch
# each element in target has to have 0 <= value < nClasses (0-2)
# Input is class, not one-hot
Y = tensor([2, 0, 1], requires_grad=False)

# input is of size nBatch x nClasses = 2 x 4
# Y_pred are logits (not softmax)
Y_pred1 = tensor([[0.1, 0.2, 0.9],
                  [1.1, 0.1, 0.2],
                  [0.2, 2.1, 0.1]])

Y_pred2 = tensor([[0.8, 0.2, 0.3],
                  [0.2, 0.3, 0.5],
                  [0.2, 0.2, 0.5]])

l1 = loss(Y_pred1, Y)
l2 = loss(Y_pred2, Y)
print(f'Batch Loss1:  {l1.item():.4f} \nBatch Loss2: {l2.data:.4f}')


"""
SiEPIC Photonics Package

Author:     Mustafa Hammood
            Mustafa@siepic.com

            https://github.com/SiEPIC-Kits/SiEPIC_Photonics_Package

Solvers and simulators: Mach-Zehnder Interferometer simulator using a simple transfer function of an MZI

Based on L. Chrostowski MATLAB implementation
"""
#%% dependent packages
import matplotlib.pyplot as plt
import numpy as np

#%% user input
# the wavelength range of interest.
wavelength_start = 1500e-9
wavelength_stop = 1600e-9
resolution = 0.001

# Effective index:
# - as a Taylor expansion around the central wavelength, lam0
lam0 = 1.55
n1=2.4
n2=-1.0
n3=0.0  # these are constants from the waveguide model.

# plot, and check if this is as expected:
L1=100
L2=110  # Units [µm, microns], variable

# Complex propagation constant
alpha = 1e-3   # propagation loss [micron^-1]; constant

#%% analysis

lambda_0 = np.linspace(wavelength_start, wavelength_stop, round((wavelength_stop-wavelength_start)*1e9/resolution))*1e6

def neff(lam):
    return n1 + n2*(lam-lam0) + n3*(lam-lam0)**2

def beta(lam):
    return	2*np.pi*neff(lam)/lam - 1j*alpha/2*np.ones(np.size(lam))

# MZI transfer function
def T_MZI(L1, L2, lam):
    return 0.25*np.abs(np.exp(-1j*beta(lam)*L1) + np.exp(-1j*beta(lam)*L2))**2

#%% application

plt.figure()
plt.plot(lambda_0, neff(lambda_0),linewidth=2.0)
plt.xlabel ('Wavelength [$\mu$m]')
plt.ylabel ('Effective index')
plt.autoscale(enable=True, axis='x', tight=True)
plt.autoscale(enable=True, axis='y', tight=True)
plt.title('Effective index model')

plt.figure()
plt.plot(lambda_0, T_MZI(L1, L2, lambda_0), linewidth=3)
plt.xlabel ('Wavelength [$\mu$m]')
plt.ylabel ('Transmission')
plt.autoscale(enable=True, axis='x', tight=True)
plt.autoscale(enable=True, axis='y', tight=True)
plt.title('MZI transfer function')

plt.figure()
T_MZI_dB = 10*np.log10(T_MZI(L1, L2, lambda_0))
plt.plot(lambda_0, T_MZI_dB, linewidth=3)
plt.xlabel ('Wavelength [$\mu$m]')
plt.ylabel ('Transmission [dB]')
plt.autoscale(enable=True, axis='x', tight=True)
plt.autoscale(enable=True, axis='y', tight=True)
plt.title ('MZI transfer function')