import numpy as np
import math
from typing import Tuple, Set

def conj_grad(A: np.matrix, b: np.ndarray, x_0: np.ndarray):
    k = 0
    r = {}; r[0] = b - A @ x_0
    x = {}; x[0] = x_0
    p = {}
    tau = {}
    mu = {}

    while not math.isclose(np.linalg.norm(r[k], ord=2), 0):
        k += 1
        if k == 1:
            p[k] = r[0]
        else:
            tau[k-1] = (r[k-1].transpose() @ r[k-1]) / (r[k-2].transpose() @ r[k-2])
            p[k] = r[k-1] + tau[k-1] * p[k-1]
        mu[k] = (r[k-1].transpose() @ r[k-1]) / (p[k].transpose() @ A @ p[k])
        x[k] = x[k-1] + mu[k] * p[k]
        r[k] = r[k-1] - mu[k] * (A @ p[k])

    x_star = x[k]
    return x_star, k

def lu_solve(L: np.matrix, R: np.matrix, b: np.array) -> np.array:
    y = np.zeros(b.size)
    for m in range(0, b.size):
        y[m] = b[m] - sum(
            L[m][i] * y[i] for i in range(0, m)
        )
        y[m] /= L[m][m]

    x = np.zeros(b.size)
    for k in reversed(range(0, b.size)):
        x[k] = y[k] - sum(
                R[k][i] * x[i] for i in range(k + 1, b.size)
        )
        x[k] /= R[k][k]

    return x

def conj_grad_precond(A: np.matrix, b: np.ndarray, x_0: np.ndarray, precond_func):
    k = 0
    r = {}; r[0] = b - A @ x_0
    x = {}; x[0] = x_0
    z = {}
    p = {}

    L, U = precond_func(A)
    z[0] = lu_solve(L, U, r[0])

    while not math.isclose(np.linalg.norm(r[k], ord=2), 0):
        k += 1
        if k == 1:
            p[k] = z[0]
        else:
            tau = (r[k-1].transpose() @ z[k-1]) / (r[k-2].transpose() @ z[k-2])
            p[k] = z[k-1] + tau * p[k-1]
        mu = (r[k-1].transpose() @ z[k-1]) / (p[k].transpose() @ A @ p[k])
        x[k] = x[k-1] + mu * p[k]
        r[k] = r[k-1] - mu * (A @ p[k])
        z[k] = lu_solve(L, U, r[k])

    x_star = x[k]
    return x_star, k

def matrix_portrait(A: np.matrix, e: float = None) -> Set[Tuple[int, int]]:
    if e is None:
        Omega = set()
        n = A.shape[0]
        for i in range(0, n):
            for j in range(0, n):
                if not math.isclose(A[i, j], 0):
                    Omega.add((i, j))

        return Omega
    else:
        Omega = set()
        n = A.shape[0]
        for i in range(0, n):
            for j in range(0, n):
                if abs(A[i, j]) > e or i==j:
                    Omega.add((i, j))

        return Omega

def incomplete_lu(A: np.matrix, Omega: Set[Tuple[int, int]], modified: bool = False) -> Tuple[np.matrix, np.matrix]:
    A = A.copy()
    n = A.shape[0]
    L = np.eye(n, dtype=float)
    R = np.zeros(A.shape, dtype=float)

    for k in range(0, n):
        for i in range(k, n):
            if (k, i) in Omega:
                R[k, i] = A[k, i]
            elif modified:
                R[k, k] -= A[k, i]
                R[k, i] = 0
        for j in range(k + 1, n):
            L[j, k] = A[j, k] / A[k, k] if (j, k) in Omega else 0
        for p in range(k + 1, n):
            for q in range(k + 1, n):
                    A[p, q] -= L[p, k]*R[k, q]

    return L, R

def ilu_k(A: np.matrix, k: int, modified: bool = False, e: float = None) -> Tuple[np.matrix, np.matrix]:
    Omega = matrix_portrait(A, e)
    for i in range(0, k+1):
        L, R = incomplete_lu(A, Omega, modified)
        T = L @ R - A
        Omega |= matrix_portrait(T, e) # | is set union

    return L, R


# Import required libraries:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model

# Read the CSV file :
data = pd.read_csv("https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/FuelConsumptionCo2.csv")
data.head()

# Let's select some features to explore more :
data = data[["ENGINESIZE","CO2EMISSIONS"]]

# ENGINESIZE vs CO2EMISSIONS:
plt.scatter(data["ENGINESIZE"] , data["CO2EMISSIONS"] , color="blue")
plt.xlabel("ENGINESIZE")
plt.ylabel("CO2EMISSIONS")
plt.show()

# Generating training and testing data from our data:
# We are using 80% data for training.
train = data[:(int((len(data)*0.8)))]
test = data[(int((len(data)*0.8))):]

# Modeling:

# Using sklearn package to model data :
regr = linear_model.LinearRegression()
train_x = np.array(train[["ENGINESIZE"]])
train_y = np.array(train[["CO2EMISSIONS"]])
regr.fit(train_x,train_y)

# The coefficients:
print ("coefficients : ",regr.coef_)       #Slope
print ("Intercept : ",regr.intercept_)     #Intercept

# Plotting the regression line:
plt.scatter(train["ENGINESIZE"], train["CO2EMISSIONS"], color='blue')
plt.plot(train_x, regr.coef_*train_x + regr.intercept_, '-r')
plt.xlabel("Engine size")
plt.ylabel("Emission")

# Predicting values:

# Function for predicting future values :
def get_regression_predictions(input_features,intercept,slope):
 predicted_values = input_features*slope + intercept
 return predicted_values

# Predicting emission for future car:
my_engine_size = 3.5
estimatd_emission = get_regression_predictions(my_engine_size,regr.intercept_[0],regr.coef_[0][0])
print ("Estimated Emission :",estimatd_emission)

# Checking various accuracy:
from sklearn.metrics import r2_score
test_x = np.array(test[['ENGINESIZE']])
test_y = np.array(test[['CO2EMISSIONS']])
test_y_ = regr.predict(test_x)
print("Mean absolute error: %.2f" % np.mean(np.absolute(test_y_ - test_y)))
print("Mean sum of squares (MSE): %.2f" % np.mean((test_y_ - test_y) ** 2))
print("R2-score: %.2f" % r2_score(test_y_ , test_y) )


import numpy as np
# For plotting
import matplotlib.pyplot as plt
fontsize = 16
import pandas as pd
import scipy.integrate as integrate

def PI_Approx(N):
    np.random.seed(seed=42)
    inside = 0
    PI_approx = []
    rand = []
    rand_2 = []
    for i in range(1,N):
        r = np.random.uniform(low=-1,high=1)
        rand.append(r)
        r2 = np.random.uniform(low=-1,high=1)
        rand_2.append(r2)
        if (r*r + r2*r2 < 1):
            inside = inside + 1
        PI_approx.append(4*inside/i)

        if i == 100 or i == 1000 or i == 10000:
            circle_1 = plt.Circle((0,0),1, color = 'black', fill = False, linewidth = 2, alpha = 0.5)
            fig, ax = plt.subplots(figsize = (7,7))
            color = np.array(rand)*np.array(rand) + np.array(rand_2)*np.array(rand_2) < 1
            plt.scatter(rand, rand_2, c=color, cmap= 'bwr', alpha=0.5)
            ax.add_patch(circle_1)
            plt.title(r'$ N = %s , n = %s \rightarrow \pi \approx \frac{4n}{N} \approx %.2f$' %(i, inside, 4*inside/i), fontsize=fontsize)
            plt.xlim(-1,1)
            plt.ylim(-1,1)
            plt.xlabel('x', fontsize=fontsize)
            plt.ylabel('y', fontsize=fontsize)
            plt.savefig('monte_carlo/MC_Circle_N=%s.jpg' %i, bbox_inches='tight', dpi=300)
    PI_approx = pd.DataFrame(PI_approx, columns=['Estimated PI'])
    PI_approx['Real PI'] = np.pi
    PI_approx['Error'] = np.pi - PI_approx['Estimated PI']

    return PI_approx

df_PI = PI_Approx(N=100000)

plt.figure()
df_PI[['Estimated PI', 'Real PI']].plot(logx=True)
plt.xlabel('N iterations', fontsize=fontsize)
plt.ylabel(r'$ \pi_{est} $', fontsize=fontsize)
plt.savefig('monte_carlo/Real_vs_Estimated_PI.jpg', bbox_inches='tight', dpi=300)

plt.figure()
df_PI['N_scaling']=1/np.sqrt(df_PI.index.values)
df_PI['Error'].abs().plot(logy=True, logx=True)
df_PI['N_scaling'].plot(logy=True, logx=True, c='r')
plt.text(1000, 0.2, r'$ \propto \frac{1}{\sqrt{N}}$', fontsize=fontsize)
plt.ylabel('Error', fontsize=fontsize)
plt.xlabel('N iterations', fontsize=fontsize)
plt.savefig('monte_carlo/Real_vs_Estimated_PI_Scaling.jpg', bbox_inches='tight', dpi=300)


# !pip install tf-nightly tfp-nightly seaborn
import matplotlib
from matplotlib import pyplot as plt

import seaborn

import numpy as np
from scipy.stats import itemfreq

import tensorflow as tf
import tensorflow_probability as tfp
tfd = tfp.distributions
tf.enable_eager_execution()

# ===========================================================================
# How do we specify the distribution in tensorflow probability
# ===========================================================================
boxes = tfd.Categorical(probs=[0.2, # p(r)
                               0.2, # p(b)
                               0.6], # p(g)
                        name="DistributionOfBoxes")
fruits = tfd.Mixture(
    cat=boxes,
    components=[
        # in order, the probabilities of: apple, orange, lime
        tfd.Categorical(probs=[0.3, 0.4, 0.3], name="RedBox"),
        tfd.Categorical(probs=[0.5, 0.5, 0.0], name="BlueBox"),
        tfd.Categorical(probs=[0.3, 0.3, 0.4], name="GreenBox"),
    ],
    name="DistributionOfFruits")

print("p(apple)  =", tf.exp(fruits.log_prob(0)))
print("p(orange) =", tf.exp(fruits.log_prob(1)))
print("p(lime)   =", tf.exp(fruits.log_prob(2)))

# ===========================================================================
# Draw sample and plot histogram
# ===========================================================================
n_samples = 10000
samples = fruits.sample(n_samples)
distribution = itemfreq(samples)
plt.bar(distribution[:, 0], distribution[:, 1])
plt.title("Distribution of fruits after picking %d times" % n_samples)
plt.xticks(np.arange(0, 3), fontsize=12)

# ===========================================================================
# Calculate the log-likelihood
# ===========================================================================
samples1 = fruits.sample(10)
samples2 = [0, 1, 2, 2, 2, 2, 2, 2, 2, 2]

llk1 = tf.reduce_sum(fruits.log_prob(samples1))
print("log[p(sample1|model)] =", llk1)

llk2 = tf.reduce_sum(fruits.log_prob(samples2))
print("log[p(sample2|model)] =", llk2)

if llk1 > llk2:
  print('Samples-1 are more likely to be generated from our model')
elif llk1 == llk2:
  print('Both samples are equal likely to be generated from our model')
else:
  print('Samples-2 are more likely to be generated from our model')

# ===========================================================================
# Could we replicate the same results in numpy using sampling
# ===========================================================================
# ====== configuration ====== #
n_samples = 100
np.random.seed(5218)

# ====== Sampling process ====== #
boxes = ['red', 'blue', 'green']
# this might be slow but it is intuitive
chose_boxes = [np.random.choice(boxes, size=None, p=[0.2, 0.2, 0.6])
               for i in range(n_samples)]


import numpy as np

def cgsolve(A, b, tau):
    """
    Given A nd b, it solves the linear system Ax=b using the CF-method.
    The matrix A must be symmetric and postive definite.

    The CG-steps get smaller after every iteration.
    For an nxn matrix the algorithm performs at most n iterations before terminating.
    """

    if A.shape[0] != A.shape[1]:
        raise ValueError("Matrix A is not symmetric.")
    for eig in np.linalg.eigvals(A):
        if eig <= 0.:
            raise ValueError("Matrix A is not positive definite")

    x = np.ones(A.shape[0])    # arbitrary initial guess
    r = b - np.matmul(A, x)
    d = r

    while True:
        r_prev = r
        alpha = r.dot(r)/d.dot(np.matmul(A, d))
        x = x + alpha*d
        r = r - alpha*np.matmul(A, d)
        if np.linalg.norm(r) <= tau:
            break
        d = r + (r.dot(r)/r_prev.dot(r_prev))*d

    return x

def main():
    # 2x2 matrx
    A = np.array([[1, 0],[0, 1]])
    b = np.array([23, 34])
    tau = 10e-8

    x = cgsolve(A, b, tau)
    print("### Test 2x2")
    print("x:", x)
    print("Approx.:", np.matmul(A, x))
    print("Actual:", b)

    # 6x6 matrix
    A = np.array([[7, -2, -3, 0, -1, 0],
                  [-2, 8, 0, 0, -1, 0],
                  [-3, 0, 4, -1, 0, 0],
                  [0, 0, -1, 5, 0, -2],
                  [-1, -1, 0, 0, 4, 0],
                  [0, 0, 0, -2, 0, 6]])
    b = np.array([1, 2, 3, 4, 5, 6])
    tau = 10e-8

    x = cgsolve(A, b, tau)
    print("\n### Test 6x6")
    print("x:", x)
    print("Approx.:", np.matmul(A, x))
    print("Actual:", b)

if __name__ == "__main__":
    main()


import os
import numpy as np
path = os.path.dirname(os.path.realpath(__file__))

def consumption(lines:list):
    data = [[int(i) for i in list(line.strip())] for line in lines]
    arr = np.array(data, dtype='int32')
    l = len(arr)
    w = len(arr[0])

    gamma_rate = []
    epsilon_rate = []
    for i in range(w):
        t = 1 if sum(arr[:,i]) >=l/2 else 0
        gamma_rate.append(t)
        epsilon_rate.append(1-t)

    gamma_val = int(''.join(map(lambda x: str(int(x)), gamma_rate)), 2)
    epsilon_val = int(''.join(map(lambda x: str(int(x)), epsilon_rate)), 2)

    return gamma_val * epsilon_val

def life_support_ratig(lines:list):

    def count(l,i,c):
        # Counts occurrence of value 'c' in index 'i' of elements in the list 'l' (expected to be strings of same length)
        res = 0
        for k in range(len(l)):
            if l[k][i] == c:
                res += 1
        return res

    def clean(l,i,c):
        # Cleans elements in the list 'l' (expected to be strings of same length)  where value in index 'i' is equal to 'c'
        k = 0
        while k < len(l):
            if l[k][i] != c:
                l.pop(k)
                continue
            k += 1
        return l

    oxygen = lines.copy()
    co2 = lines.copy()

    i = 0
    while len(oxygen)>1 or len(co2)>1:
        if len(oxygen)>1:
            c = count(oxygen,i,'1')
            t = '1' if c >= len(oxygen)/2 else '0'
            oxygen = clean(oxygen,i,t)

        if len(co2)>1:
            c = count(co2,i,'1')
            t = '0' if c >= len(co2)/2 else '1'
            co2 = clean(co2,i,t)

        i += 1

    return int(oxygen[0], 2) * int(co2[0], 2)

test_lines = [
    '00100',
    '11110',
    '10110',
    '10111',
    '10101',
    '01111',
    '00111',
    '11100',
    '10000',
    '11001',
    '00010',
    '01010',
]

lines = []
with open(path+"/input.txt") as file:
    lines = file.readlines()
    lines = [line.strip() for line in lines]

assert consumption(test_lines) == 198, "Function is wrong"

print("Part A:", consumption(lines))

assert life_support_ratig(test_lines) == 230, "Function is wrong"

print("Part B:", life_support_ratig(lines))


import numpy as np

def softmax_(predictions):
    '''
    Computes probabilities from scores

    Arguments:
      predictions, np array, shape is either (N) or (batch_size, N) -
        classifier output

    Returns:
      probs, np array of the same shape as predictions -
        probability for every class, 0..1
    '''
    s_pred =predictions-np.max(predictions)
    probs = np.exp(s_pred)/np.sum(np.exp(s_pred))
    # pp('soft max =', probs)
    return probs
    # TODO implement softmax_
    # Your final implementation shouldn't have any loops

def cross_entropy_loss_(probs, target_index):
    '''
    Computes cross-entropy loss

    Arguments:
      probs, np array, shape is either (N) or (batch_size, N) -
        probabilities for every class
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)

    Returns:
      loss: single value
    '''

    log =   np.sum(- np.log(probs[np.arange(probs.shape[0]),target_index.flatten()]))
    return log
    # TODO implement cross-entropy
    # Your final implementation shouldn't have any loops

def l2_regularization_(W, reg_strength):
    """
      Computes L2 regularization loss on weights and its gradient

      Arguments:
        W, np array - weights
        reg_strength - float value

      Returns:
        loss, single value - l2 regularization loss
        gradient, np.array same shape as W - gradient of weight by l2 loss
    """
    n_classes = W.value.shape[1]

    loss = reg_strength * np.sum(W.value**2)
    loss/=n_classes
    grad = reg_strength*W.value
    # pp('L2 loss, grad = ',loss,grad)
    return loss, grad

    # TODO: Copy from the previous assignment

def softmax__with_cross_entropy_(predictions, target_index):
    """
      Computes softmax_ and cross-entropy loss for model predictions,
      including the gradient

      Arguments:
        predictions, np array, shape is either (N) or (batch_size, N) -
          classifier output
        target_index: np array of int, shape is (1) or (batch_size) -
          index of the true class for given sample(s)

      Returns:
        loss, single value - cross-entropy loss
        dprediction, np array same shape as predictions - gradient of predictions by loss value
    """
    n_samples = predictions.shape[0]
    n_featerus = predictions.shape[1] if len(predictions.shape)>1 else 0
    # pp('enter of the function = ',predictions, target_index)

    softmax__ = softmax_(predictions)

    loss = cross_entropy_loss_(softmax__,target_index)
    loss = loss/n_samples
    prediction = softmax__.copy()
    prediction[np.arange(n_samples),target_index.ravel()] -= 1/n_samples

    # pp('=N_SUMPLES=',n_samples)
    # prediction/=n_samples
    # pp('loss , grand (prediction) = ',loss, prediction)
    return loss, prediction

    # TODO: Copy from the previous assignment


"""
An implementation on spherical harmonics in python becasue scipy.special.sph_harm in scipy<=0.13 is very slow

Originally written by Jozef Vesely
https://github.com/scipy/scipy/issues/1280
"""

import numpy as np

def xfact(m):
    # computes (2m-1)!!/sqrt((2m)!)
    res = 1.
    for i in xrange(1, 2*m+1):
        if i % 2: res *= i # (2m-1)!!
        res /= np.sqrt(i) # sqrt((2m)!)
    return res

def lplm_n(l, m, x):
    # associated legendre polynomials normalized as in Ylm, from Numerical Recipes 6.7
    l,m = int(l),int(m)
    assert 0<=m<=l and np.all(np.abs(x)<=1.)

    norm = np.sqrt(2. * l + 1.) / np.sqrt(4. * np.pi)
    if m == 0:
        pmm = norm * np.ones_like(x)
    else:
        pmm = (-1.)**m * norm * xfact(m) * (1.-x**2.)**(m/2.)
    if l == m:
        return pmm
    pmmp1 = x * pmm * np.sqrt(2.*m+1.)
    if l == m+1:
        return pmmp1
    for ll in xrange(m+2, l+1):
        pll = (x*(2.*ll-1.)*pmmp1 - np.sqrt( (ll-1.)**2. - m**2.)*pmm)/np.sqrt(ll**2.-m**2.)
        pmm = pmmp1
        pmmp1 = pll
    return pll

def Ylm(l, m, phi, theta):
    # spherical harmonics
    # theta is from 0 to pi with pi/2 on equator
    l,m = int(l),int(m)
    assert 0 <= np.abs(m) <=l
    if m > 0:
        return lplm_n(l, m, np.cos(theta)) * np.exp(1J * m * phi)
    elif m < 0:
        return (-1.)**m * lplm_n(l, -m, np.cos(theta)) * np.exp(1J * m * phi)
    return lplm_n(l, m, np.cos(theta)) * np.ones_like(phi)

def Ylmr(l, m, phi, theta):
    # real spherical harmonics
    # theta is from 0 to pi with pi/2 on equator
    l,m = int(l),int(m)
    assert 0 <= np.abs(m) <=l
    if m > 0:
        return lplm_n(l, m, np.cos(theta)) * np.cos(m * phi) * np.sqrt(2.)
    elif m < 0:
        return (-1.)**m * lplm_n(l, -m, np.cos(theta)) * np.sin(-m * phi) * np.sqrt(2.)
    return lplm_n(l, m, np.cos(theta)) * np.ones_like(phi)

if __name__ == "__main__":
    from scipy.special import sph_harm
    from scipy.misc import factorial2, factorial
    from timeit import Timer

    def ref_xfact(m):
        return factorial2(2*m-1)/np.sqrt(factorial(2*m))

    print "Time: xfact(10)", Timer("xfact(10)",
        "from __main__ import xfact, ref_xfact").timeit(100)
    print "Time: ref_xfact(10)", Timer("ref_xfact(10)",
        "from __main__ import xfact, ref_xfact").timeit(100)
    print "Time: xfact(80)", Timer("xfact(80)",
        "from __main__ import xfact, ref_xfact").timeit(100)
    print "Time: ref_xfact(80)", Timer("ref_xfact(80)",
        "from __main__ import xfact, ref_xfact").timeit(100)

    print "m", "xfact", "ref_xfact"
    for m in range(10) + range(80,90):
        a = xfact(m)
        b = ref_xfact(m)
        print m, a, b

    phi, theta = np.ogrid[0:2*np.pi:10j,-np.pi/2:np.pi/2:10j]

    print "Time: Ylm(1,1,phi,theta)", Timer("Ylm(1,1,phi,theta)",
        "from __main__ import Ylm, sph_harm, phi, theta").timeit(10)
    print "Time: sph_harm(1,1,phi,theta)", Timer("sph_harm(1,1,phi,theta)",
        "from __main__ import Ylm, sph_harm, phi, theta").timeit(10)

    print "l", "m", "max|Ylm-sph_harm|"
    for l in xrange(0,10):
        for m in xrange(-l,l+1):
            a = Ylm(l,m,phi,theta)
            b = sph_harm(m,l,phi,theta)
            print l,m, np.amax(np.abs(a-b))


'''
The Normal CDF
100xp
Now that you have a feel for how the Normal PDF looks, let's consider its CDF. Using the
samples you generated in the last exercise (in your namespace as samples_std1, samples_std3,
and samples_std10), generate and plot the CDFs.

Instructions
-Use your ecdf() function to generate x and y values for CDFs: x_std1, y_std1, x_std3, y_std3
and x_std10, y_std10, respectively.
-Plot all three CDFs as dots (do not forget the marker and linestyle keyword arguments!).
-Make a 2% margin in your plot.
-Hit submit to make a legend, showing which standard deviations you used, and to show your plot.
There is no need to label the axes because we have not defined what is being described by the Normal distribution; we are just looking at shapes of CDFs.
'''
import numpy as np
import matplotlib.pyplot as plt

def ecdf(data):
    """Compute ECDF for a one-dimensional array of measurements."""

    # Number of data points: n
    n = len(data)

    # x-data for the ECDF: x
    x = np.sort(data)

    # y-data for the ECDF: y
    y = np.arange(1, n+1) / n

    return x, y

# Seed random number generator
np.random.seed(42)

# Draw 100000 samples from Normal distribution with stds of interest: samples_std1, samples_std3,
#   samples_std10
samples_std1 = np.random.normal(20, 1, size=100000)
samples_std3 = np.random.normal(20, 3, size=100000)
samples_std10 = np.random.normal(20, 10, size=100000)

# Generate CDFs
x_std1, y_std1 = ecdf(samples_std1)
x_std3, y_std3 = ecdf(samples_std3)
x_std10, y_std10 = ecdf(samples_std10)

# Plot CDFs
_ = plt.plot(x_std1, y_std1, marker='.', linestyle='none')
_ = plt.plot(x_std3, y_std3, marker='.', linestyle='none')
_ = plt.plot(x_std10, y_std10, marker='.', linestyle='none')

# Make 2% margin
plt.margins(0.02)

# Make a legend and show the plot
_ = plt.legend(('std = 1', 'std = 3', 'std = 10'), loc='lower right')
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Thu Jun  8 13:36:50 2017

@author: picku
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
style.use('ggplot')

def generatePolyPoints(start, stop, num_points, coeff, noiseLevel = 1, plot = 1):
    """
    Generate evenly spaced x and y coordinates in Euclidean space from a polynomial
    with gaussian noise

    Parameters
    ------------
    start : float,
          Minimum value for the generated x-coordinates

    stop  : float
          Maximum value for the generated x-coordinates

    num_points : int
               Number of coordinates to generate

    coeff : 1-d array,

    noiseLevel : float, optional
              Indicates amount of gaussian noise to add to the y-coordinates
              generated from the polynomial characterized by coeff array.
              Zero indicates no noise, while each integer increment results in
              ten-fold noise increase

    plot : int, values = 1|~1 , optional
        plot == 1 returns a matplotlib plot of the generated coordinates

    Returns
    -----------
    x_pts : 1-d array, shape = [num_points,]
    y_pts : 1-d array, shape = [num_points,]

    """
    x_pts = np.arange(start, stop, (stop - start)/num_points)
    line = coeff[0]

    for i in np.arange(1, len(coeff)):
        line += coeff[i] * x_pts ** i

    if noiseLevel > 0:
        y_pts = np.random.normal(-(10 ** noiseLevel), 10 ** noiseLevel, len(x_pts)) + line
    else:
        y_pts = line

    if plot == 1: #Plot option
        plt.figure()
        plt.scatter(x_pts, y_pts)
        plt.xlabel('x')
        plt.ylabel('y')

    return x_pts, y_pts


#!/usr/bin/env python

import numpy as np
import random

from q1_softmax import softmax
from q2_sigmoid import sigmoid, sigmoid_grad
from q2_gradcheck import gradcheck_naive

def cross_entropy(labels, y_hat):
    assert labels.shape == y_hat.shape, "Predictions must match labels shape."
    log_y_hat = np.log(y_hat)
    x_ent = np.sum(labels * log_y_hat, axis=-1)
    return x_ent

def forward_backward_prop(data, labels, params, dimensions):
    """
    Forward and backward propagation for a two-layer sigmoidal network

    Compute the forward propagation and for the cross entropy cost,
    and backward propagation for the gradients for all parameters.

    Arguments:
    data -- M x Dx matrix, where each row is a training example.
    labels -- M x Dy matrix, where each row is a one-hot vector.
    params -- Model parameters, these are unpacked for you.
    dimensions -- A tuple of input dimension, number of hidden units
                  and output dimension
    """

    ### Unpack network parameters (do not modify)
    ofs = 0
    Dx, H, Dy = (dimensions[0], dimensions[1], dimensions[2])

    W1 = np.reshape(params[ofs:ofs+ Dx * H], (Dx, H))
    ofs += Dx * H
    b1 = np.reshape(params[ofs:ofs + H], (1, H))
    ofs += H
    W2 = np.reshape(params[ofs:ofs + H * Dy], (H, Dy))
    ofs += H * Dy
    b2 = np.reshape(params[ofs:ofs + Dy], (1, Dy))

    z1 = np.matmul(data, W1) + b1
    h = sigmoid(z1)
    z2 = np.matmul(h, W2) + b2
    y_hat = softmax(z2)
    cost = cross_entropy(labels, y_hat)

    dJ_dz2 = y_hat - labels  # (1x10)
    gradW2 = np.matmul(h.T, dJ_dz2)  # (5x10)
    dz2_dh = W2.T  # (10x5)
    dh_dz1 = np.diag(h * (1-h))  # (5x5)
    dJ_dz1 = np.matmul(np.matmul(dJ_dz2, dz2_dh), dh_dz1)  # (1x5)
    gradW1 = np.matmul(data.T, dJ_dz1)  # (10x5)
    gradb2 = dJ_dz2
    gradb1 = dJ_dz1

    ### Stack gradients (do not modify)
    grad = np.concatenate((gradW1.flatten(), gradb1.flatten(),
        gradW2.flatten(), gradb2.flatten()))

    return cost, grad

def sanity_check():
    """
    Set up fake data and parameters for the neural network, and test using
    gradcheck.
    """
    print "Running sanity check..."

    N = 20
    dimensions = [10, 5, 10]
    data = np.random.randn(N, dimensions[0])   # each row will be a datum
    labels = np.zeros((N, dimensions[2]))
    for i in xrange(N):
        labels[i, random.randint(0,dimensions[2]-1)] = 1

    params = np.random.randn((dimensions[0] + 1) * dimensions[1] + (
        dimensions[1] + 1) * dimensions[2], )

    gradcheck_naive(lambda params:
        forward_backward_prop(data, labels, params, dimensions), params)

def your_sanity_checks():
    """
    Use this space add any additional sanity checks by running:
        python q2_neural.py
    This function will not be called by the autograder, nor will
    your additional tests be graded.
    """
    print "Running your sanity checks..."

if __name__ == "__main__":
    sanity_check()
    your_sanity_checks()


# Visualization with hierarchical clustering and t-SNE
# In this chapter, you'll learn about two unsupervised learning techniques for data visualization, hierarchical clustering and t-SNE. Hierarchical clustering merges the data samples into ever-coarser clusters, yielding a tree visualization of the resulting cluster hierarchy. t-SNE maps the data samples into 2d space so that the proximity of the samples to one another can be visualized.

# Hierarchical clustering of the grain data
# In the video, you learned that the SciPy linkage() function performs hierarchical clustering on an array of samples. Use the linkage() function to obtain a hierarchical clustering of the grain samples, and use dendrogram() to visualize the result. A sample of the grain measurements is provided in the array samples, while the variety of each grain sample is given by the list varieties.

# Perform the necessary imports
from scipy.cluster.hierarchy import linkage, dendrogram
import matplotlib.pyplot as plt

# Calculate the linkage: mergings
mergings = linkage(samples, method ='complete')

# Plot the dendrogram, using varieties as labels
dendrogram(mergings,
           labels=varieties,
           leaf_rotation=90,
           leaf_font_size=6,
)
plt.show()

# Hierarchies of stocks
# In chapter 1, you used k-means clustering to cluster companies according to their stock price movements. Now, you'll perform hierarchical clustering of the companies. You are given a NumPy array of price movements movements, where the rows correspond to companies, and a list of the company names companies. SciPy hierarchical clustering doesn't fit into a sklearn pipeline, so you'll need to use the normalize() function from sklearn.preprocessing instead of Normalizer.

# linkage and dendrogram have already been imported from scipy.cluster.hierarchy, and PyPlot has been imported as plt.

# Import normalize
from sklearn.preprocessing import normalize

# Normalize the movements: normalized_movements
normalized_movements = normalize(movements)

# Calculate the linkage: mergings
mergings = linkage(normalized_movements, method ='complete')

# Plot the dendrogram
dendrogram(mergings, labels = companies, leaf_rotation= 90, leaf_font_size=6)
plt.show()

# Different linkage, different hierarchical clustering!
# In the video, you saw a hierarchical clustering of the voting countries at the Eurovision song contest using 'complete' linkage. Now, perform a hierarchical clustering of the voting countries with 'single' linkage, and compare the resulting dendrogram with the one in the video. Different linkage, different hierarchical clustering!

# You are given an array samples. Each row corresponds to a voting country, and each column corresponds to a performance that was voted for. The list country_names gives the name of each voting country. This dataset was obtained from Eurovision.

# Perform the necessary imports
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import linkage, dendrogram

# Calculate the linkage: mergings
mergings = linkage(samples, method ='single')

# Plot the dendrogram
dendrogram(mergings, labels = country_names, leaf_rotation =90, leaf_font_size =6)
plt.show()

# Extracting the cluster labels
# In the previous exercise, you saw that the intermediate clustering of the grain samples at height 6 has 3 clusters. Now, use the fcluster() function to extract the cluster labels for this intermediate clustering, and compare the labels with the grain varieties using a cross-tabulation.

# The hierarchical clustering has already been performed and mergings is the result of the linkage() function. The list varieties gives the variety of each grain sample.

# Perform the necessary imports
import pandas as pd
from scipy.cluster.hierarchy import fcluster

# Use fcluster to extract labels: labels
labels = fcluster(mergings, 6, criterion ='distance')

# Create a DataFrame with labels and varieties as columns: df
df = pd.DataFrame({'labels': labels, 'varieties': varieties})

# Create crosstab: ct
ct = pd.crosstab(df['labels'], df['varieties'])

# Display ct
print(ct)

# t-SNE visualization of grain dataset
# In the video, you saw t-SNE applied to the iris dataset. In this exercise, you'll apply t-SNE to the grain samples data and inspect the resulting t-SNE features using a scatter plot. You are given an array samples of grain samples and a list variety_numbers giving the variety number of each grain sample

# Import TSNE
from sklearn.manifold import TSNE

# Create a TSNE instance: model
model = TSNE(learning_rate =200)

# Apply fit_transform to samples: tsne_features
tsne_features = model.fit_transform(samples)

# Select the 0th feature: xs
xs = tsne_features[:,0]

# Select the 1st feature: ys
ys = tsne_features[:,1]

# Scatter plot, coloring by variety_numbers
plt.scatter(xs, ys, c=variety_numbers)
plt.show()

# A t-SNE map of the stock market
# t-SNE provides great visualizations when the individual samples can be labeled. In this exercise, you'll apply t-SNE to the company stock price data. A scatter plot of the resulting t-SNE features, labeled by the company names, gives you a map of the stock market! The stock price movements for each company are available as the array normalized_movements (these have already been normalized for you). The list companies gives the name of each company. PyPlot (plt) has been imported for you.

# Import TSNE
from sklearn.manifold import TSNE

# Create a TSNE instance: model
model = TSNE(learning_rate =50)

# Apply fit_transform to normalized_movements: tsne_features
tsne_features = model.fit_transform(normalized_movements)

# Select the 0th feature: xs
xs = tsne_features[:, 0]

# Select the 1th feature: ys
ys = tsne_features[:,1]

# Scatter plot
plt.scatter(xs, ys, alpha = 0.5)

# Annotate the points
for x, y, company in zip(xs, ys, companies):
    plt.annotate(company, (x, y), fontsize=5, alpha=0.75)
plt.show()

Unsupervised Learning in Python/Visualization_with_hierarchical_clustering_and_t-SNE.py


import numpy as np
import matplotlib.pyplot as plt
import os.path

#Reading the output
flag = 0
if os.path.exists('../../build/output/simpson_integration/simpson_approximation_errors_v1.txt'):
    flag = 1
    N, deviation = np.loadtxt('../../build/output/simpson_integration/simpson_approximation_errors_v1.txt', skiprows = 1, unpack = True)
elif os.path.exists('output/simpson_integration/simpson_approximation_errors_v1.txt'):
    flag = 2
    N, deviation = np.loadtxt('output/simpson_integration/simpson_approximation_errors_v1.txt', skiprows = 1, unpack = True)
elif os.path.exists('../../build/output/simpson_integration/simpson_approximation_errors.txt'):
    flag = 3
    N, deviation = np.loadtxt('../../build/output/simpson_integration/simpson_approximation_errors.txt', skiprows = 1, unpack = True)
elif os.path.exists('output/simpson_integration/simpson_approximation_errors.txt'):
    flag = 4
    N, deviation = np.loadtxt('output/simpson_integration/simpson_approximation_errors.txt', skiprows = 1, unpack = True)
else:
    print("No output file found")
    exit()

#Fixed quantities
a = 0
b = 2
third_derivative_a = 0
third_derivative_b = 48

#Error computed as I_simpson - I_exact
log_deviation = np.log10(abs(deviation))

#Error from theory
log_theoretical_error = np.log10(1/90*(third_derivative_b-third_derivative_a)*((b-a)/(2**N))**4)

#Error computed trhough the difference between I(N) and I(2N)
log_approximated_error= [None] * (deviation.size-1)
for i in range(0,deviation.size-1):
    log_approximated_error[i] = np.log10(1/15*(abs(deviation[i+1]-deviation[i])))

#Definition of useful quantities
N = 2**N
logN = np.log10(N)

#Plots
fig = plt.figure(figsize=(4.5*np.sqrt(2),4.5))
plt.plot(logN,log_deviation,'o',markersize=5, label = r'$\left| \widetilde{I} - I \right|$')
plt.plot(logN,log_theoretical_error,'o', markersize=5, color = 'red', label = 'Theoretical Error')
plt.plot(logN[1:],log_approximated_error,'o', markersize=5, label = 'Numerical error' )
plt.title("Error analysis for Simpson integration method", fontsize = 14)
plt.xlabel('Log(N)' , fontsize = 12)
plt.ylabel('Log(value)', fontsize = 12)
plt.legend( fontsize = 12)

fit_deviation = np.polyfit(logN[0:10], log_deviation[0:10], 1)
fit_theoretical_error = np.polyfit(logN[0:10], log_theoretical_error[0:10], 1)
fit_approximated_error = np.polyfit(logN[0:10], log_approximated_error[0:10], 1)

print('**********************')
print('PYTHON SCRIPT RESULTS:')
print(f'Deviation slope = {fit_deviation[0]}')
print(f'Theoretical slope = {fit_theoretical_error[0]}')
print(f'Approximated slope = {fit_approximated_error[0]}')
print('**********************')

if(flag == 1 or flag == 3):
    plt.savefig('simpson_approximation_errors.pdf')
else:
    plt.savefig('../pyplots/simpson_integration/simpson_approximation_errors.pdf')

print("\nOutput saved in pyplots/simpson_approximation_errors.pdf")


import numpy as np
import scipy.linalg as la

def qr_algorithm(A, eps=0.01, max_steps=1000):
    for i in range(max_steps):
        Q, R = la.qr(A)
        new_A = Q.T @ A @ Q
        diag_delta = la.norm(np.diag(A) - np.diag(new_A))
        if diag_delta < eps:
            return np.diag(A)
        A = new_A
    raise ValueError("failed to converge" + str(diag_delta))

A = np.array([[0,2,-1],[2,-1,1],[2,-1,3]])

print("eigenvalues of:\n", A)
print("scipy:", sorted(la.eigvals(A).real))
print("own qr algorithm:", sorted(qr_algorithm(A).real))


import numpy as np
import matplotlib.pyplot as plt

#############
# Equations #
#############

def dS_dt(beta, I, S, N):
    return (-beta * I * S) / N

def dI_dt(beta, I, S, N, gamma):
    return (beta * I * S) / N - gamma * I

def dR_dt(gamma, I):
    return gamma * I

###############
# Runge-Kutta #
###############

def runge_kutta(h, n, beta, I, S, N, gamma, R):
    '''
    Classical 4th Order Runge-Kutta Method

    y' = f(x, y) with initial conditions y(x0) = y0 and step size h

    k1 = h * f(x, y)
    k2 = h * f(x + h / 2, y + k1 / 2)
    k3 = h * f(x + h / 2, y + k2 / 2)
    k4 = h * f(x + h, y + k3)

    x = x + h
    y = y + (k1 + 2 * k2 + 2 * k3 + k4) / 6
    '''

    j = 0
    s, i, r = np.zeros(n + 1), np.zeros(n + 1), np.zeros(n + 1)
    s[0], i[0], r[0] = S, I, R
    while j < n:
        S_k1 = h * dS_dt(beta, i[j], s[j], N)
        I_k1 = h * dI_dt(beta, i[j], s[j], N, gamma)
        R_k1 = h * dR_dt(gamma, i[j])

        S_k2 = h * dS_dt(beta, i[j] + h / 2, s[j] + S_k1 / 2, N)
        I_k2 = h * dI_dt(beta, i[j] + I_k1 / 2, s[j] + h / 2, N, gamma)
        R_k2 = h * dR_dt(gamma, i[j] + h / 2)

        S_k3 = h * dS_dt(beta, i[j] + h / 2, s[j] + S_k2 / 2, N)
        I_k3 = h * dI_dt(beta, i[j] + I_k2 / 2, s[j] + h / 2, N, gamma)
        R_k3 = h * dR_dt(gamma, i[j] + h / 2)

        S_k4 = h * dS_dt(beta, i[j] + h, s[j] + S_k3, N)
        I_k4 = h * dI_dt(beta, i[j] + I_k3, s[j] + h, N, gamma)
        R_k4 = h * dR_dt(gamma, i[j] + h)

        s[j+1] = s[j] + (S_k1 + 2 * S_k2 + 2 * S_k3 + S_k4) / 6
        i[j+1] = i[j] + (I_k1 + 2 * I_k2 + 2 * I_k3 + I_k4) / 6
        r[j+1] = r[j] + (R_k1 + 2 * R_k2 + 2 * R_k3 + R_k4) / 6

        j += 1

    return s, i, r

h = 0.05
n = 5000
# Step-size of 0.05, 5000 iterations, 0.2 contact rate, 1 infected,
# 9999 susceptible, 10000 population, 0.042 transition rate, 0 recovered
s, i, r = runge_kutta(h, n, 1 / 5, 1, 9999, 10000, 1 / 24, 0)

# Plot values
t = np.linspace(0, h * n, len(s))
plt.plot(t, s, 'g-')
plt.plot(t, i, 'r-')
plt.plot(t, r, 'b-')
plt.legend(['Susceptible', 'Infected', 'Recovered'])
plt.xlabel('Time (Days)')
plt.ylabel('Number of People')
plt.title(r'SIR Model: $\beta=0.2, \gamma=0.042$')
plt.show()


from sympy import *
from sympy.matrices import *

class Transform:
    def __init__(self, phi, alpha, a, d):
        self.phi = phi
        self.alpha = alpha
        self.a = a
        self.d = d
        self.DHMatrix()

    def DHMatrix(self):
        # phi = rad(self.phi)
        phi = self.phi
        alpha = rad(self.alpha)
        a = self.a
        d = self.d

        dh = Matrix([[cos(phi), -sin(phi)*cos(alpha)  ,sin(phi)*sin(alpha), a*cos(phi)],
                     [sin(phi),  cos(phi)*cos(alpha), -cos(phi)*sin(alpha), a*sin(phi)],
                     [       0,           sin(alpha),           cos(alpha),          d],
                     [       0,                    0,                    0,          1]])
        self.A = dh

        self.R = dh[:3, :3]
        self.vecA = dh[:3, 3]

# Let's do an analysis with the SSRMS with the shoulder roll joint locked at an angle beta
beta, theta1, theta2, theta3, theta4, theta5, theta6, theta7 = symbols("beta theta1 theta2 theta3 theta4 theta5 theta6 theta7")
d1, d2, d3, d6, d7, a3, a4 = symbols('d1 d2 d3 d6 d7 a3 a4')

# SSRMS
configs = [[theta1, 90,  0, d1],
           [theta2, 90,  0, d2],
           [theta3,  0, a3, d3],
           [theta4,  0, a4,  0],
           [theta5, 90,  0,  0],
           [theta6, 90,  0, d6],
           [theta7, 90,  0, d7]]

# Stanford Arm Example from Class
# configs = [[theta1, -90, 0, d1],
#            [theta2,  90, 0, d2],
#            [   -90,   0, 0, d3],
#            [theta4, -90, 0,  0],
#            [theta5,  90, 0,  0],
#            [theta6,   0, 0,  0]]

# Build our robot
T = []
for config in configs:
    T.append(Transform(*config))

def KJ():
  # Kinematic Jacobian
  # The z_i's need to be WRT the base coordinate system
  z = Matrix([[0], [0], [1]])

  # Calculate all our z vectors
  for i, _ in enumerate(T):
      rot = Identity(3)
      for j in range(i):
          rot *= T[j].R
      T[i].z = rot * z

  # Loop through backwards to calculate the r vectors
  for i, _ in enumerate(T):
      i = len(T) - (i+1)
      rot = Identity(3)
      for j in range(i):
          rot *= T[j].R
      rot *= T[i].vecA
      if (i+1) < len(T):
          rot += T[i+1].r
      T[i].r = rot

  # Calculate the z skew matrices and compute cross products
  for i, _ in enumerate(T):
      T[i].z_skew = Matrix([[         0, -T[i].z[2],  T[i].z[1]],
                            [ T[i].z[2],          0, -T[i].z[0]],
                            [-T[i].z[1],  T[i].z[0],         0]])
      T[i].crossed = T[i].z_skew * T[i].r

  # Combine elements to form Jacobian
  top, bottom = Matrix(), Matrix()
  for i, _ in enumerate(T):
      top = top.row_join(T[i].crossed)
      bottom = bottom.row_join(T[i].z)
  J = top.col_join(bottom)

  return J

def GJ():
  # Geometric Jacobian
  Q_rev = Matrix([[0, -1, 0, 0],
                  [1,  0, 0, 0],
                  [0,  0, 0, 0],
                  [0,  0, 0, 0]])

  for i, _ in enumerate(T):
    print('i ', i)
    if i > 0:
      T[i].Ti = T[i-1].Ti * T[i-1].A
    if i == 0:
      T[i].Ti = Matrix(Identity(4))
    T[i].Ti.simplify()

    T[i].D = simplify(T[i].Ti * Q_rev) * simplify(T[i].Ti.inv())

  Jg = Matrix([[T[0].D[0, 3], T[1].D[0, 3], T[2].D[0, 3], T[3].D[0, 3], T[4].D[0, 3], T[5].D[0, 3], T[6].D[0, 3]],
               [T[0].D[1, 3], T[1].D[1, 3], T[2].D[1, 3], T[3].D[1, 3], T[4].D[1, 3], T[5].D[1, 3], T[6].D[1, 3]],
               [T[0].D[2, 3], T[1].D[2, 3], T[2].D[2, 3], T[3].D[2, 3], T[4].D[2, 3], T[5].D[2, 3], T[6].D[2, 3]],
               [T[0].D[2, 1], T[1].D[2, 1], T[2].D[2, 1], T[3].D[2, 1], T[4].D[2, 1], T[5].D[2, 1], T[6].D[2, 1]],
               [T[0].D[0, 2], T[1].D[0, 2], T[2].D[0, 2], T[3].D[0, 2], T[4].D[0, 2], T[5].D[0, 2], T[6].D[0, 2]],
               [T[0].D[1, 0], T[1].D[1, 0], T[2].D[1, 0], T[3].D[1, 0], T[4].D[1, 0], T[5].D[1, 0], T[6].D[1, 0]]])
  return Jg

# Inverse Kinematic Solution
# T07 = T[0].A * T[1].A * T[2].A * T[3].A * T[4].A * T[5].A * T[6].A

# We know that
# u_x, u_y, u_z, v_x, v_y, v_z, w_x, w_y, w_z, p_x, p_y, p_z = symbols('u_x u_y u_z v_x v_y v_z w_x w_y w_z p_x p_y p_z')
# T07 = Matrix([[u_x, v_x, w_x, p_x],
#               [u_y, v_y, w_y, p_y],
#               [u_z, v_z, w_z, p_z],
#               [  0,   0,   0,   1]])

# And setting T01^-1 * T07 = T12 T23 T34 T45 T56 T67
# print(latex(T[0].A.inv() * T07))
# print(latex(T[1].A * T[2].A * T[3].A * T[4].A * T[5].A * T[6].A))

def IK(SHOULDER=1, WRIST=1, ELBOW=1,
       a3=2.3, a4=2.3, d1=0.65, d2=0.3,
       d3=0.9, d6=0.3, d7=0.65,
       beta=rad(60),
       u_x=0.8021, u_y=-0.5859, u_z=-0.1154,
       v_x=0.1217, v_y= 0.3495, v_z=-0.9290,
       w_x=0.5846, w_y= 0.7311, w_z=0.3517,
       p_x=2.4790, p_y=-2.4734, p_z=-0.4927):
  '''
  Return the IK for a given pose, initial locked angle, and arm characteristics.
  '''

  theta1 = beta

  h1 = -v_z * d7 - d1 + p_z
  q1 = (v_x * d7 - p_x)*cos(beta) + (v_y * d7 - p_y)*sin(beta)

  theta2 = SHOULDER * acos(d3/(sqrt(h1**2 + q1**2))) + atan2(q1, h1) - pi

  theta6 = WRIST * acos(v_z*cos(theta2)-(v_x*cos(theta1) + v_y*sin(theta1))*sin(theta2))

  theta7 = -atan2(((u_x*cos(theta1) + u_y*sin(theta1))*sin(theta2) - u_z*cos(theta2))/sin(theta6),
                  ((w_x*cos(theta1) + w_y*sin(theta1))*sin(theta2) - w_z*cos(theta2))/sin(theta6)) + pi/2

  X = d6 * ((w_z * sin(theta2) + cos(theta2) * (w_x * cos(theta1) + w_y * sin(theta1))) * cos(theta7) - (u_z * sin(theta2) + cos(theta2) * (u_x * cos(theta1) + u_y * sin(theta1))) * sin(theta7)) - d7 * (v_z * sin(theta2) + cos(theta2) * (v_x * cos(theta1) + v_y * sin(theta1))) + (-d1 + p_z) * sin(theta2) + cos(theta2) * (p_x * cos(theta1) + p_y * sin(theta1))
  Y = -d2 + d6 * ((w_x * sin(theta1) - w_y * cos(theta1)) * cos(theta7) - (u_x * sin(theta1) - u_y * cos(theta1)) * sin(theta7)) - d7 * (v_x * sin(theta1) - v_y * cos(theta1)) + p_x * sin(theta1) - p_y*cos(theta1)

  theta4 = ELBOW * acos((X**2 + Y**2 - a3**2 - a4**2)/(2*a3*a4))

  theta3 = atan2(Y*(a3 + a4*cos(theta4)) - X*a4*sin(theta4),
                 X*(a3 + a4*cos(theta4)) + Y*a4*sin(theta4))

  theta5 = atan2((v_x*sin(theta1)-v_y*cos(theta1))/(sin(theta6)),
                 (w_x*sin(theta1)-w_y*cos(theta1))*cos(theta7)-(u_x*sin(theta1)-u_y*cos(theta1))*sin(theta7)) - (theta3 + theta4)

  thetas = []
  for theta in [theta1, theta2, theta3, theta4, theta5, theta6, theta7]:
    thetas.append(deg(theta).evalf())

  thetas = Matrix(thetas)
  return thetas

def IK_sols():
  '''
  Enumerate through the eight possible solutions for a given pose.
  '''

  c = [[ 1,  1,  1],
       [ 1,  1, -1],
       [ 1, -1,  1],
       [-1,  1,  1],
       [ 1, -1, -1],
       [-1,  1, -1],
       [-1, -1,  1],
       [-1, -1, -1]]

  res = []
  for S, E, W in c:
      print(S, E, W)
      t = IK(SHOULDER=S, ELBOW=E, WRIST=W)
      res.append(t)

  stacked = np.zeros((7, 1))
  for row in res:
      stacked = np.hstack((stacked, np.array(row)))

  return stacked[:, 1:].T


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

def principleStresses(stressTensor,method='eigen'):
    principle_stresses = []
    if method=='eigen':
        principle_stresses = np.linalg.eigvals(stressTensor)
    elif method=='invariants':
        I = stressInvariants(stressTensor)
        principle_stresses = np.roots([1,-I[0],I[1],-I[2]])
    elif method=='Mohr':
        # Requires plane stress assumption on the XY-plane
        print('Making plane stress assumption for XY plane...')
        retDict = generateMohrsCircle(stressTensor)
        principle_stresses = [retDict['sigmaP1'],retDict['sigmaP2'],stressTensor[2][2]]
    quickSort(principle_stresses,0,len(principle_stresses)-1)
    return principle_stresses

def stressInvariants(stressTensor):
    I_1 = np.trace(stressTensor)
    I_2 = np.linalg.det([[stressTensor[0][0], stressTensor[0][1]],
                         [stressTensor[0][1], stressTensor[1][1]]]) + \
          np.linalg.det([[stressTensor[0][0], stressTensor[0][2]],
                         [stressTensor[0][2], stressTensor[2][2]]]) + \
          np.linalg.det([[stressTensor[1][1], stressTensor[1][2]],
                         [stressTensor[1][2], stressTensor[2][2]]])
    I_3 = np.linalg.det(stressTensor)
    I = [I_1, I_2, I_3]
    return I

def octohedralStresses(stressTensor):
    prin_stresses = principleStresses(stressTensor)

    sigma_oct = 1/3*sum(prin_stresses)
    tau_oct = 1/3*( (prin_stresses[0] - prin_stresses[1])**2 + (prin_stresses[0] - prin_stresses[2])**2 + (prin_stresses[1] - prin_stresses[2])**2 )**0.5
    return [sigma_oct, tau_oct]

def partition(arr, low, high):
    i = (low-1)
    pivot = arr[high]

    for j in range(low, high):
        if arr[j] >= pivot:
            i = i+1
            arr[i], arr[j] = arr[j], arr[i]

    arr[i+1], arr[high] = arr[high], arr[i+1]
    return (i+1)

def quickSort(arr, low, high):
    if len(arr) == 1:
        return arr
    if low < high:

        # pi is partitioning index, arr[p] is now
        # at right place
        pi = partition(arr, low, high)

        # Separately sort elements before
        # partition and after partition
        quickSort(arr, low, pi-1)
        quickSort(arr, pi+1, high)

def generateMohrsCircle(stressTensor,filename=None,thetaCutPlane=None,show=[0]):
    # Arguments: stressTensor - the stress tensor describing the stress state; MUST OBEY PLANE STRESS ASSUMPTIONS
    #            filename - name to save file as for HW formatting; do not include file extension; disregard
    #            thetaCutPlane - angle of the cut plane in degrees; OPTIONAL - default is no angle
    #            show - the figures to show; OPTIONAL - default is Mohr's Circle only

    # Calculate values necessary for constructing Mohr's Circle
    sigmaXX = stressTensor[0][0]
    sigmaYY = stressTensor[1][1]
    tauXY = stressTensor[0][1]
    center = (sigmaXX+sigmaYY)/2
    radius = math.hypot(sigmaYY-center,tauXY)
    sigmaP1,sigmaP2 = center-radius,center+radius
    thetaP1 = 1/2*math.asin(tauXY/radius)
    thetaP2 = thetaP1+math.pi

    # Calculate angle necessary for maximum in-plane shear stress
    phi = math.pi/4 + thetaP1

    # Calculate values necessary for a cut plane if angle for said plane is provided
    alpha,sigma1,sigma2,tau1=None,None,None,None
    if thetaCutPlane != None:
        alpha = -(2*math.radians(thetaCutPlane)+2*thetaP1)
        sigma1 = center + radius*math.cos(alpha)
        sigma2 = center - radius*math.cos(alpha)
        tau1 = radius*math.sin(alpha)

    # A function designed to draw the figures. Workings are irrelevant to actual assignment
    plotMohrsCircle(filename,sigmaYY,sigmaXX,tauXY,center,radius,sigmaP1,sigmaP2,thetaP1,phi,alpha,sigma1,sigma2,tau1,thetaCutPlane,show)

    # Constructing an object to return. You can ignore this as well
    keys = ['center','radius','sigmaP1','sigmaP2','thetaP1','phi']
    values = [center,radius,sigmaP1,sigmaP2,math.degrees(thetaP1),math.degrees(phi)]
    if thetaCutPlane != None:
        keys = keys + ['alpha','sigma1','sigma2','tau1']
        values = values + [math.degrees(alpha),sigma1,sigma2,tau1]
    meDict = dict(zip(keys,values))

    return meDict

def plotMohrsCircle(filename,sigmaXX,sigmaYY,tauXy,center,radius,sigmaP1,sigmaP2,thetaP1,phi=None,alpha=None,sigma1=None,sigma2=None,tau1=None,thetaCutPlane=None,show=[0],units='MPa'):
    fig,axes = plt.subplots(nrows=1,ncols=len(show))
    if len(show)==1:
        axes = [axes]
    for i in range(len(axes)):
        axes[i].set_aspect('equal')

    axesIndex = 0

    if 0 in show:
        circ = plt.Circle((center,0),radius,edgecolor='#0000FF',facecolor='#FFFFFF')
        axes[axesIndex].add_artist(circ)
        axes[axesIndex].set_xlim(center-(radius+0.5*radius),center+(radius+0.75*radius))
        axes[axesIndex].set_ylim(-(radius+0.3*radius),(radius+0.3*radius))
        axes[axesIndex].plot([center],[0],'ko',[sigmaXX],[tauXY],'ro',[sigmaYY],[-tauXY],'ro',[sigmaXX,sigmaYY],[tauXY,-tauXY],'k--')
        axes[axesIndex].plot([sigmaP1],[0],'bo',[sigmaP2],[0],'bo')
        axes[axesIndex].plot([center-(radius+0.5*radius),center+(radius+0.75*radius)],[0,0],'k')
        axes[axesIndex].text(sigmaXX + 0.1*radius,tauXY,"({},{})".format(sigmaXX,tauXY),fontsize=12)
        axes[axesIndex].text(sigmaYY + 0.1*radius, -tauXY-0.25*radius, "({},{})".format(sigmaYY,-tauXY),fontsize=12)
        axes[axesIndex].text(center+0.25*radius,0.1*radius,"{}°".format(round(thetaP1*180/math.pi,3)),fontsize=12)
        axes[axesIndex].text(center+0.1*radius,-0.25*radius, "{}".format(round(center,3)),fontsize=12)
        axes[axesIndex].text(sigmaP1+0.1*radius,-0.25*radius,"s_P1",fontsize=12)
        axes[axesIndex].text(sigmaP2+0.1*radius,-0.25*radius,"s_P2",fontsize=12)

        axesIndex+=1

    if 1 in show:
        corners = [[2,4,4,2],
                   [2,2,4,4]]
        lines = [[1.5,3,3,4.5,0,3,3,6,0,3,3,6],
                 [3,1.5,4.5,3,3,0,6,3,3,0,6,3]]
        ends = [[0,3,3,6,0.25,3.25,3.25,5.75,0.25,2.75,2.75,5.75],
                [3,0,6,3,3.25,0.25,5.75,3.25,2.75,0.25,5.75,2.75]]
        R = [[math.cos(thetaP1),-math.sin(thetaP1)],
             [math.sin(thetaP1),math.cos(thetaP1)]]
        corners = [list(line) for line in np.matmul(np.array(R),np.array(corners))]
        lines = [list(line) for line in np.matmul(np.array(R),np.array(lines))]
        ends = [list(line) for line in np.matmul(np.array(R),np.array(ends))]
        corners[0].append(corners[0][0])
        corners[1].append(corners[1][0])
        axes[axesIndex].plot(corners[0],corners[1])
        for i in range(len(lines[0])):
            axes[axesIndex].plot([lines[0][i],ends[0][i]],[lines[1][i],ends[1][i]],'k')
        axes[axesIndex].plot([corners[0][1],corners[0][1]],[min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5],'k--')
        axes[axesIndex].text(ends[0][2]+0.1,ends[1][2]+0.1,"{} {}".format(round(sigmaP1,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][3]+0.1,ends[1][3]+0.1,"{} {}".format(round(sigmaP2,3),units),fontsize=12)
        axes[axesIndex].set_xlim(min(lines[0]+ends[0])-0.5,max(lines[0]+ends[0])+2.25)
        axes[axesIndex].set_ylim(min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5)

        axesIndex+=1

    if 2 in show:
        corners = [[2,4,4,2],
                   [2,2,4,4]]
        lines = [[1.5,3.0,3.0,4.5,   0.00,3.00,3.00,6.00,   0.00,3.00,3.00,6.00,   1.75,2.00,4.25,2.00,   1.75,2.00,4.00,4.25,   4.25,1.75],
                 [3.0,1.5,4.5,3.0,   3.00,0.00,6.00,3.00,   3.00,0.00,6.00,3.00,   2.00,1.75,2.00,4.25,   4.00,4.25,1.75,2.00,   1.75,4.25]]
        ends =  [[0.0,3.0,3.0,6.0,   0.25,3.25,3.25,5.75,   0.25,2.75,2.75,5.75,   1.75,4.00,4.25,4.00,   1.50,2.25,3.75,4.50,   4.25,1.75],
                 [3.0,0.0,6.0,3.0,   3.25,0.25,5.75,3.25,   2.75,0.25,5.75,2.75,   4.00,1.75,4.00,4.25,   3.75,4.50,1.50,2.25,   1.75,4.25]]
        R = [[math.cos(phi),-math.sin(phi)],
             [math.sin(phi),math.cos(phi)]]
        corners = [list(line) for line in np.matmul(np.array(R),np.array(corners))]
        lines = [list(line) for line in np.matmul(np.array(R),np.array(lines))]
        ends = [list(line) for line in np.matmul(np.array(R),np.array(ends))]
        corners[0].append(corners[0][0])
        corners[1].append(corners[1][0])
        axes[axesIndex].plot(corners[0],corners[1])
        for i in range(len(lines[0])):
            axes[axesIndex].plot([lines[0][i],ends[0][i]],[lines[1][i],ends[1][i]],'k')
        axes[axesIndex].plot([corners[0][1],corners[0][1]],[min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5],'k--')
        axes[axesIndex].text(ends[0][2]+0.1,ends[1][2]+0.1,"{} {}".format(round(center,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][3]+0.1,ends[1][3]+0.1,"{} {}".format(round(center,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][-1]-2.5,lines[1][-1]-0.1,"{} {}".format(round(radius,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][-2]+0.1,lines[1][-2]-0.1,"{} {}".format(round(radius,3),units),fontsize=12)
        axes[axesIndex].set_xlim(min(lines[0]+ends[0])-1.5,max(lines[0]+ends[0])+2.25)
        axes[axesIndex].set_ylim(min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5)

        axesIndex+=1

    if 3 in show:
        corners = [[2,4,4,2],
                   [2,2,4,4]]
        lines = [[1.5,3.0,3.0,4.5,   0.00,3.00,3.00,6.00,   0.00,3.00,3.00,6.00,   1.75,2.00,4.25,2.00,   1.75,2.00,4.00,4.25,   4.25,1.75],
                 [3.0,1.5,4.5,3.0,   3.00,0.00,6.00,3.00,   3.00,0.00,6.00,3.00,   2.00,1.75,2.00,4.25,   4.00,4.25,1.75,2.00,   1.75,4.25]]
        ends =  [[0.0,3.0,3.0,6.0,   0.25,3.25,3.25,5.75,   0.25,2.75,2.75,5.75,   1.75,4.00,4.25,4.00,   1.50,2.25,3.75,4.50,   4.25,1.75],
                 [3.0,0.0,6.0,3.0,   3.25,0.25,5.75,3.25,   2.75,0.25,5.75,2.75,   4.00,1.75,4.00,4.25,   3.75,4.50,1.50,2.25,   1.75,4.25]]
        R = [[math.cos(alpha),-math.sin(alpha)],
             [math.sin(alpha),math.cos(alpha)]]
        corners = [list(line) for line in np.matmul(np.array(R),np.array(corners))]
        lines = [list(line) for line in np.matmul(np.array(R),np.array(lines))]
        ends = [list(line) for line in np.matmul(np.array(R),np.array(ends))]
        corners[0].append(corners[0][0])
        corners[1].append(corners[1][0])
        axes[axesIndex].plot(corners[0],corners[1])
        for i in range(len(lines[0])):
            axes[axesIndex].plot([lines[0][i],ends[0][i]],[lines[1][i],ends[1][i]],'k')
        axes[axesIndex].plot([corners[0][1],corners[0][1]],[min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5],'k--')
        axes[axesIndex].text(ends[0][2]+0.1,ends[1][2]+0.1,"{} {}".format(round(sigma1,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][3]+0.1,ends[1][3]+0.1,"{} {}".format(round(sigma2,3),units),fontsize=12)
        axes[axesIndex].text(ends[0][-2]+0.1,lines[1][-2]-0.1,"{} {}".format(round(tau1,3),units),fontsize=12)
        axes[axesIndex].set_xlim(min(lines[0]+ends[0])-1.5,max(lines[0]+ends[0])+2.25)
        axes[axesIndex].set_ylim(min(lines[1]+ends[1])-0.5,max(lines[1]+ends[1])+0.5)

    fig.set_figheight(4)
    fig.set_figwidth(4*len(axes))
    fig.tight_layout()

    if filename!=None:
        fig.savefig(filename+'.png')


import numpy as np
import scipy.stats
import matplotlib.pyplot as plt

# Problem 1
## Question b)
s = 1
sigma = 0.5
X = np.array([-1.41, -0.57, 1.12, 3.05])
Y = np.array([1.69, 0.51, -1.08, -2.44])
## Posterior parameters estimation.
Sigma = ((1 / sigma ** 2) * np.dot(X, X) + (1 / s ** 2)) ** (-1)
M_pos = (1 / sigma ** 2) * Sigma * np.dot(X, Y)

xnew = 2.18
### MonteCarlo to estimate mu_new
n = 1000
beta_simul = np.random.normal(loc=M_pos, scale=Sigma ** (0.5), size=n)
MC_estimate = np.mean(x_new * beta_simul)
std_simul = np.std(beta_simul * xnew)
### CI
alpha = 0.05
q = scipy.stats.norm.ppf(1 - alpha / 2)
sup_bound = (q * std_simul) / np.sqrt(n) + MC_estimate
inf_bound = - (q * std_simul) / np.sqrt(n) + MC_estimate
print("MC estimate : {}".format(MC_estimate))
print("{}% CI : [{},{}]".format((1 - alpha) * 100, inf_bound, sup_bound))

## Question c)
n = 100000
error_simul = np.random.normal(loc=0, scale=0.5, size=n)
Y_new_simul = error_simul + xnew * np.random.normal(loc=M_pos, scale=Sigma ** (0.5), size=n)
count = 0
for i in range(len(Y_new_simul)):
    if Y_new_simul[i] < -2.1:
        count += 1
print("Estimated P(Y_new < -2.1) = {}".format(count / n))

# Problem 3
P = np.array([[0, 1 / 3, 1 / 3, 1 / 3, 0],
              [1 / 3, 0, 1 / 3, 0, 1 / 3],
              [1 / 2, 1 / 2, 0, 0, 0],
              [1 / 2, 0, 0, 0, 1 / 2],
              [0, 1 / 2, 0, 1 / 2, 0]])
n = np.shape(P)[0]
## Question a)
print("Stationary Distribution : {}".format(solveStationary(P)))

## Question b)

## Question d)
b = [0, -1, 0, 0, 0]
P_mod = P.copy()
P_mod[:, 0] = [0] * n
print("mu_c = {}".format(np.linalg.solve(P.T - np.eye(n), b)[2]))

## Question e)
P_mod2 = np.array([[0, 0, 1 / 3],
                   [0, 0, 0.5],
                   [1 / 2, 1 / 2, 0]])
b = [-1 / 3, -1 / 2, 0]

# np.linalg.solve(P_mod2 - np.eye(n-2),b)
print("p_B = {}".format(np.linalg.solve(P_mod2 - np.eye(n - 2), b)[0]))

## Question f)
b = [-1] * n
np.linalg.solve(P_mod - np.eye(n), b)
print("Expected number of steps starting from C before returning to A : {}".format(
    np.linalg.solve(P_mod - np.eye(n), b)[2]))

def solveStationary(A):
    """ x = xA where x is the answer
    x - xA = 0
    x( I - A ) = 0 and sum(x) = 1
    """
    n = A.shape[0]
    a = np.eye(n) - A
    a = np.vstack((a.T, np.ones(n)))
    b = np.matrix([0] * n + [1]).T
    return np.linalg.lstsq(a, b)[0]

## Problem 5)
def kernel_1(x, y, sigma, r=0.5):
    return (sigma ** 2) * np.exp(-r * (x - y) ** 2)

### Estimation of sigma.
def estimate_sigma(Y):
    sigma_estimates = [i ** 2 for i in Y]
    return np.mean(sigma_estimates) ** 0.5

## Question b)
X = np.array([-1, 0, 2])
Y = np.array([2.4, 3.9, -2.8])
plt.scatter(X, Y)
plt.show()

sigma = 1 / 2
print(estimate_sigma(Y))

def mu_new(x_new, r=0.5):
    K33 = np.zeros((3, 3))
    for i in range(3):
        for j in range(3):
            X_i = X[i]
            X_j = X[j]
            K33[i, j] = kernel_1(X_i, X_j, sigma, r)
    K_3new = np.array([kernel_1(x_new, x, sigma, r) for x in X])
    return np.matmul(K_3new, np.matmul(np.linalg.inv(K33), Y))

r_val = [0.5, 1, 2]
for r in r_val:
    X_new = np.linspace(-3, 4, 50)
    Y_new_expected_values = np.array([mu_new(x_new, r) for x_new in X_new])
    print("mu_new(1) = {}".format(mu_new(1, r)))
    plt.scatter(X_new, Y_new_expected_values, label="r = {}".format(r), alpha=0.5)

plt.scatter(X, Y)
plt.legend()
plt.grid()
plt.show()

## Question d)
def covariance2(r, x, y):
    return -2 * r * (x - y) * np.exp(-r * (x - y) ** 2)

def covariance3(r, x, y):
    return 2 * r * np.exp(-r * (x - y) ** 2) * (1 - 2 * r * (x - y) ** 2)

Y = np.array([2.4, 3.9, -2.8, 6.4, -2.0, -0.8])

def mu_new_2(xnew, r=0.5):
    K_6new = [kernel_1(i, xnew, sigma, r) for i in X] \
             + [covariance2(r, i, xnew) for i in X]

    K66 = np.zeros((6, 6))
    for i in range(0, 6):
        if i < 3:
            tab = [kernel_1(j, X[i], sigma, r) for j in X] \
                  + [covariance2(r, j, X[i]) for j in X]
        if i >= 3:
        tab = [covariance2(r, X[i - 3], j) for j in X] \
              + [covariance3(r, X[i - 3], j) for j in X]
        K66[i] = tab
    # print(K66)
    return np.matmul(K_6new, np.matmul(np.linalg.inv(K66), Y))

# Plots
r_val = [0.5, 1, 2]
for r in r_val:
    X_new = np.linspace(-3, 3, 50)
    Y_new_expected_values = np.array([mu_new_2(x_new, r) for x_new in X_new])
    print("mu_new_2(1) = {}".format(mu_new_2(1, r)))
    plt.scatter(X_new, Y_new_expected_values, label="r = {}".format(r), alpha=0.5)

plt.scatter(X, Y[0:3], alpha= 1)
plt.legend()
plt.grid()
plt.show()


import numpy as np
def rotate_to_north_pole(v, angle=None):
    """
    Rotate a unit vector v to the north pole of a unit sphere

    Return the rotation matrix
    See Rodrigues' rotation formula: https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula#Matrix_notation
    Input: 1D array (i.e., a point on a unit sphere)
    """
    d = len(v) # dimension of the feature space

    ## normalize vector v
    v = v / np.linalg.norm(v)
    ## north pole coordinates d-dimension [0, ..., 0, 1]
    north_pole = [0] * (d - 1)
    north_pole.append(1)
    north_pole = np.asarray(north_pole)

    inner_prod = np.inner(north_pole, v)
    if not angle:
        angle = np.arccos(inner_prod)
    if np.abs(inner_prod - 1) < 1e-15:
        return np.eye(d)
    elif np.abs(inner_prod + 1) < 1e-15:
        return -np.eye(d)
    c = v - north_pole * inner_prod
    c = c / np.linalg.norm(c)
    A = np.outer(north_pole, c) - np.outer(c, north_pole)

    rot = np.eye(d) + np.sin(angle)*A + (np.cos(angle) - 1)*(np.outer(north_pole, north_pole)\
                                                             + np.outer(c, c))
    return rot
def log_north_pole(x):
    """
    LOGNP Riemannian log map at North pole of S^k
        LogNP(x) returns k x n matrix where each column is a point on tangent
        space at north pole and the input x is (k+1) x n matrix where each column
        is a point on a sphere.
    Input: d x n matrix w.r.t. the extrinsic coords system
    Output: (d-1) x n matrix w.r.t. the coords system (tangent space) origined at the NP
    """
    d, n = x.shape
    scale = np.arccos(x[-1, :]) / np.sqrt(1-x[-1, :]**2)
    scale[np.isnan(scale)] = 1
    log_px = scale * x[:-1, :]
    return log_px
def exp_north_pole(x):
    """
    EXPNP Riemannian exponential map at North pole of S^k
    returns (k+1) x n matrix where each column is a point on a
    sphere and the input v is k x n matrix where each column
    is a point on tangent  space at north pole.

    Input: d x n matrix
    """
    d, n = x.shape
    nv = np.sqrt(np.sum(x ** 2, axis=0))
    tmp = np.sin(nv) / (nv + 1e-15)
    exp_px = np.vstack((tmp * x, np.cos(nv)))
    exp_px[:, nv < 1e-16] = np.repeat(np.vstack((np.zeros((d, 1)), 1)), np.sum(nv<1e-16), axis=1)
    return exp_px


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jul 10 17:23:06 2021

@author: alessandro
"""
import numpy as np
import scipy.linalg as spl
import matplotlib.pyplot as plt

def usolve(U, b):
    m, n = U.shape
    x = np.zeros((n, 1))
    for i in range(n - 1, -1, -1):
        s = np.dot(U[i, i+1:], x[i+1:])
        x[i] = (b[i] - s) / U[i, i]
    return x

def qr(x, y, n):
    H = np.vander(x, n + 1)
    Q, R = spl.qr(H)
    X = np.dot(Q.T, y)
    pol = usolve(R[:n+1, :], X[:n+1])
    return pol

dati = np.array([1900, 76, 1910, 92, 1920, 106, 1930, 123, 1940, 132, 1950, 151, 1960, 179, 1970, 203, 1980, 226, 1990, 249, 2000, 281, 2010, 305], dtype=float)

x = dati[0::2]
y = dati[1::2]

p1 = qr(x, y, 1)
p2 = qr(x, y, 2)
p3 = qr(x, y, 3)
p4 = qr(x, y, 4)

xx = np.linspace(1900, 2010, 100)
y1 = np.polyval(p1, xx)
y2 = np.polyval(p2, xx)
y3 = np.polyval(p3, xx)

plt.plot(x, y, 'o')
plt.plot(xx, y1, label="p1(x)")
plt.plot(xx, y2, label="p2(x)")
plt.plot(xx, y3, label="p3(x)")
plt.legend()
plt.show()

y1 = np.polyval(p1, x)
y2 = np.polyval(p2, x)
y3 = np.polyval(p3, x)
e1, e2, e3 = 0, 0, 0

for i in range(12):
    e1 += (y1[i] - y[i]) ** 2
    e2 += (y2[i] - y[i]) ** 2
    e3 += (y3[i] - y[i]) ** 2

print(f"e1 = {e1}")
print(f"e2 = {e2}")
print(f"e3 = {e3}")


import numpy as np

def compute_cost(AL, Y):
        """
        Implement the cost function defined by equation (7).

        Parameters
        ----------
        AL : numpy array
            Probability vector corresponding to your label predictions
            with shape (1, number of examples)
        Y : numpy array
            True "label" vector (for example: containing 0 if non-cat, 1 if cat)
            with shape (1, number of examples)

        Returns
        -------
        total_cost : float
            cross-entropy cost
        """
        # Compute loss from aL and y.
        log_cost = np.multiply(-np.log(AL), Y) + np.multiply(-np.log(1 - AL), 1 - Y)
        total_cost = np.nansum(log_cost)
        assert(total_cost.shape == ())

        return total_cost

def regularized_cost(AL, Y, parameters, lambd):
    """
    Implement the cost function with L2 regularization.

    Parameters
    ----------
    AL : numpy array
        post-activation, output of forward propagation,
        of shape (output size, number of examples)
    Y : numpy array
        "true" labels vector, of shape (output size, number of examples)
    parameters : dict
        python dictionary containing parameters of the model

    Returns
    -------
    total_regularized_cost :
        value of the regularized loss function (formula (2))
    """
    cost_cross_entropy = compute_cost(AL, Y) # This gives you the cross-entropy part of the cost
    cost_L2 = (lambd / 2) * np.nansum(list(map(lambda x: np.nansum(np.square(x)), parameters.values())))
    total_regularized_cost = cost_cross_entropy + cost_L2

    return total_regularized_cost


import numpy as np

def generate_data():
    '''
    Generates fake data to use for calculating lane curvature.
    In your own project, you'll ignore this function and instead
    feed in the output of your lane detection algorithm to
    the lane curvature calculation.
    '''
    # Set random seed number so results are consistent for grader
    # Comment this out if you'd like to see results on different random data!
    np.random.seed(0)
    # Generate some fake data to represent lane-line pixels
    ploty = np.linspace(0, 719, num=720)  # to cover same y-range as image
    quadratic_coeff = 3e-4  # arbitrary quadratic coefficient
    # For each y position generate random x position within +/-50 pix
    # of the line base position in each case (x=200 for left, and x=900 for right)
    leftx = np.array([200 + (y ** 2) * quadratic_coeff + np.random.randint(-50, high=51)
                      for y in ploty])
    rightx = np.array([900 + (y ** 2) * quadratic_coeff + np.random.randint(-50, high=51)
                       for y in ploty])

    leftx = leftx[::-1]  # Reverse to match top-to-bottom in y
    rightx = rightx[::-1]  # Reverse to match top-to-bottom in y

    # Fit a second order polynomial to pixel positions in each fake lane line
    left_fit = np.polyfit(ploty, leftx, 2)
    right_fit = np.polyfit(ploty, rightx, 2)

    return ploty, left_fit, right_fit

def measure_curvature_pixels():
    '''
    Calculates the curvature of polynomial functions in pixels.
    '''
    # Start by generating our fake example data
    # Make sure to feed in your real data instead in your project!
    ploty, left_fit, right_fit = generate_data()

    # Define y-value where we want radius of curvature
    # We'll choose the maximum y-value, corresponding to the bottom of the image
    y_eval = np.max(ploty)

    ##### TO-DO: Implement the calculation of R_curve (radius of curvature) #####
    ## Implement the calculation of the left line here
    left_curverad = ((1 + (2 * left_fit[0] * y_eval + left_fit[1]) ** 2) ** (3 / 2)) / np.abs(2 * left_fit[0])
    ## Implement the calculation of the right line here
    right_curverad = ((1 + (2 * right_fit[0] * y_eval + right_fit[1]) ** 2) ** (3 / 2)) / np.abs(2 * right_fit[0])

    return left_curverad, right_curverad

# Calculate the radius of curvature in pixels for both lane lines
left_curverad, right_curverad = measure_curvature_pixels()

print(left_curverad, right_curverad)
# Should see values of 1625.06 and 1976.30 here, if using
# the default `generate_data` function with given seed number


# -*- coding: utf-8 -*-
"""
Created on Thu Sep 30 08:41:19 2021

@author: Ezra
"""

import numpy as np
import time
import pandas as pd

def f(x):
    return -x*np.sin(x)-x*x/4*np.cos(x)

def fPrime(x):
    return x**2/4*np.sin(x)-3*x/2*np.cos(x)-np.sin(x)

def fwdDiff(x0,h):
    dfdx=(f(x0+h)-f(x0))/h
    return dfdx

def bacDiff(x0,h):
    dfdx=(f(x0)-f(x0-h))/h
    return dfdx

def cenDiff(x0,h):
    dfdx=(f(x0+h)-f(x0-h))/(2*h)
    return dfdx

def fourthOrdDiff(x0,h):
    dfdx = (4*cenDiff(x0, h/2)-cenDiff(x0,h))/3
    return dfdx

def sixthOrdDiff(x0,h):
    dfdx=(16*(fourthOrdDiff(x0, h/2))-fourthOrdDiff(x0, h))/15
    return dfdx

x0=np.array([-3,-2.5,-2,-1.5,-1,-.5,0,.5,1,1.5,2,2.5,3])

t1 = time.perf_counter()
fde=fwdDiff(x0,.1)
tfwd  = time.perf_counter()-t1
print("time to run fwdDiff", tfwd)

t2 = time.perf_counter()
bde=bacDiff(x0,.1)
tbc  = time.perf_counter()-t2
print("time to run bacDiff", tbc)

t3 = time.perf_counter()
cde=cenDiff(x0,.1)
tcen  = time.perf_counter()-t3
print("time to run cendiff", tcen)

t4 = time.perf_counter()
foe=fourthOrdDiff(x0, 0.1)
tfourth  = time.perf_counter()-t4
print("time to run fourthOrdDiff", tfourth)

t5 = time.perf_counter()
soe=sixthOrdDiff(x0,.1)
tsix  = time.perf_counter()-t5
print("time to run sixthOrdDiff", tsix)

act=fPrime(x0)

print("Forward Difference Estimate=", fde)
print("Backwards Difference Estimate=", bde)
print("Center Difference Estimate=", cde)
print("Fourth Order Difference Estimate=", foe)
print("Sixth Order Difference Estimate=", soe)
print("Analytic Value of Derivative=", act)

def err(x,y):

    return abs((y-x)/y)

h=np.array([.5,.5**2,.5**3,.5**4,.5**5])

x0=1
analytical= fPrime(x0)
errFwd=[]
errCen=[]
errBac=[]
errFourthOrder=[]
errSixthOrder=[]
dfdxFwd=[]
dfdxBac=[]
dfdxCen=[]
dfdxFourth=[]
dfdxSixth=[]

for i in range(len(h)):
    dfdxFwd.append(fwdDiff(x0,h[i]))
    dfdxBac.append(bacDiff(x0,h[i]))
    dfdxCen.append(cenDiff(x0,h[i]))
    dfdxFourth.append(fourthOrdDiff(x0,h[i]))
    dfdxSixth.append(sixthOrdDiff(x0, h[i]))
    errFwd.append(err(dfdxFwd[i],analytical))
    errBac.append(err(dfdxBac[i],analytical))
    errCen.append(err(dfdxCen[i],analytical))
    errFourthOrder.append(err(dfdxFourth[i],analytical))
    errSixthOrder.append(err(dfdxSixth[i],analytical))

import matplotlib.pyplot as plt

plt. figure(0)
plt.semilogy(h,errFwd,label="FWD")
plt.semilogy(h,errBac,label="BAC")
plt.semilogy(h,errCen,label="CEN")
plt.semilogy(h,errFourthOrder,label="FTH")
plt.semilogy(h,errSixthOrder,label="SIX")
plt.title("Error of methods of various orders")
plt.xlabel("h")
plt.ylabel("Relative error")
plt.grid(True)
plt.legend()
plt.show()

x=np.linspace(-5,5,100)
z=np.linspace(-5,5,20)

plt.figure(1)
plt.plot(x,fPrime(x),label="Analytic")
plt.scatter(z,fwdDiff(z, .1),color='red',label= 'fwdDiff')
plt.scatter(z,bacDiff(z, .1),color='green',label='bacDiff')
#plt.scatter(z,cenDiff(z, .1),color='orange',label="cenDiff")
plt.scatter(z,fourthOrdDiff(z, .1),color='pink',label="4thOrdDiff")
plt.title("graph of function using cenDiff and analytic solution")
plt.xlabel("x")
plt.ylabel("y")
plt.grid(True)
plt.legend()

dfdxFwd=tuple(dfdxFwd)
dfdxBac=tuple(dfdxBac)
dfdxCen=tuple(dfdxCen)
dfdxFourth=tuple(dfdxFourth)
dfdxSixth=tuple(dfdxSixth)
errFwd=tuple(errFwd)
errBac=tuple(errBac)
errCen=tuple(errCen)
errFourthOrder=tuple(errFourthOrder)
errSixthOrder=tuple(errSixthOrder)

Estimates=pd.DataFrame({'FwdDiff': dfdxFwd,'BacDiff':dfdxBac,'CenDiff':dfdxCen,
                        'FourthOrdDiff':dfdxFourth,'SixthOrdDiff':dfdxSixth,
                        'errFwd':errFwd,'errBac':errBac,'errCen':errCen,
                        'errFourthOrder':errFourthOrder,'errSixthOrder':errSixthOrder})
file_name='dfdx.xlsx'
Estimates.to_excel(file_name)


import numpy as np
import matplotlib.pyplot as plt

def serieFourierCuadrada(s, r, n):
    #s = float(input("Ingresa el valor de s: "))
    #r = float(input("Ingresa el valor de r: "))
    #n = int(input("Ingresa el valor de n: "))

    impares = [x for x in range(0,n+1) if x%2 == 1]
    #print(impares)

    t = np.arange(-8*s,8*s+0.01, 0.01)

    #print(t)

    #Calcula la serie.
    for i in range(0, len(impares)):

        if i == 0:
            ft = np.sin((impares[i]*np.pi*t)/s)
            ft*=(r/impares[i])
        else:
            ftAux = np.sin((impares[i]*np.pi*t)/s)
            ftAux*=(r/impares[i])
            ft+=ftAux

    ft*=(-4/np.pi)
    #print("Total: ",ft)

    line, = plt.plot(t, ft, lw=2)

    #Muestra los ejes xy.
    ax = plt.subplot()
    ax.spines['left'].set_position('zero')

    ax.spines['right'].set_color('none')
    ax.yaxis.tick_left()

    ax.spines['bottom'].set_position('zero')

    ax.spines['top'].set_color('none')
    ax.xaxis.tick_bottom()

    #Añade estilo a la representación de la gráfica.
    plt.ylim(-r-1, r+1)
    plt.grid(True)
    plt.axis('equal')
    #plt.title(r'f(t) = $\frac{-4}{\pi} \sum_{i=1}^\infty \frac{%.2f}{n}sin(\frac{nt}{%.2f})$' % (r, s))
    plt.title(r'f(t) = $\frac{-4}{\pi} ( \frac{%.2f}{1}sin(\frac{πt}{%.2f}) + \frac{%.2f}{3}sin(\frac{3πt}{%.2f}) + \frac{%.2f}{5}sin(\frac{5πt}{%.2f}) + \frac{%.2f}{7}sin(\frac{7πt}{%.2f}) + \frac{%.2f}{9}sin(\frac{9πt}{%.2f}) + \frac{%.2f}{11}sin(\frac{11πt}{%.2f}) + ... )$ ' % (r, s, r, s, r, s, r, s, r, s, r, s))
    plt.show()

def serieFourierTriangular(s, r, n):
    #s = float(input("Ingresa el valor de s: "))
    #r = float(input("Ingresa el valor de r: "))
    #n = int(input("Ingresa el valor de n: "))

    impares = [x for x in range(0,n+1) if x%2 == 1]
    #print(impares)

    t = np.arange(-8*s,8*s+0.01, 0.01)

    #print(t)

    #Calcula la serie.
    for i in range(0, len(impares)):

        if i == 0:
            ft = np.cos((impares[i]*np.pi*t)/s)
            ft*=(1/impares[i]**2)
        else:
            ftAux = np.cos((impares[i]*np.pi*t)/s)
            ftAux*=(1/impares[i]**2)
            ft+=ftAux

    ft*=(-4*r/np.pi**2)
    ft+=r/2
    #print("Total: ",ft)

    line, = plt.plot(t, ft, lw=2)

    #Muestra los ejes xy.
    ax = plt.subplot()
    ax.spines['left'].set_position('zero')

    ax.spines['right'].set_color('none')
    ax.yaxis.tick_left()

    ax.spines['bottom'].set_position('zero')

    ax.spines['top'].set_color('none')
    ax.xaxis.tick_bottom()

    #Añade estilo a la representación de la gráfica.
    plt.ylim(-r-1, r+1)
    plt.grid(True)
    plt.axis('equal')
    plt.title(r'f(t) = $ \frac{%.2f}{2} + \frac{-4(%.2f)}{π^{2}} ( \frac{1}{1^{2}}cos(\frac{πt}{%.2f}) + \frac{1}{3^{2}}cos(\frac{3πt}{%.2f}) + \frac{1}{5^{2}}cos(\frac{5πt}{%.2f}) + \frac{1}{7^{2}}cos(\frac{7πt}{%.2f}) + \frac{1}{9^{2}}cos(\frac{9πt}{%.2f}) + \frac{1}{11^{2}}sin(\frac{11πt}{%.2f}) + ... )$ ' % (r, r, s, s, s, s, s, s))
    plt.show()

#print("1.-Serie Fourier funcion Cuadrada")
#print("2.-Serie Fourier funcion Triangular")
#opcion = int(input("Opcion: "))

#if(opcion == 1):
#    SerieFourierCuadrada()
#else:
#    SerieFourierTriangular()


"""
  Name     : c9_32_matrix_multiplication.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 numpy as np
ret=np.matrix(np.array([[0.1,0.2],[0.10,0.1071],[-0.02,0.25],[0.012,0.028],[0.06,0.262],[0.14,0.115]]))
covar=np.dot(ret.T,ret)
print("covar")
print(covar)


"""

The point of this code is to prove that the descending interest method
is not always the optimal method.

The code computes the grand total cost of using the "gradient descent"
method and the "descending interest" method to pay 2 loans, assuming there
are $100 to apply above the loans' minimum payment.

The starting principal of loan X varies. It varies from being equal to the
other loan's starting principal to + $10000 greater. All other parameters
are held constant: the interests, the length (term), the extra money (a).
NOTE though, that the changing the starting principal changes the minimum
payment as per the "present value" equation used in creating amortized loans.

Each variation of loan X's principal yeilds a different total cost.
The total cost as a function of difference in starting principal is plotted
to compare methods.

"""

import numpy as np
import matplotlib.pyplot as plt
from loan_functions import *

# Interests
IA = 0.35/12.0
IB = 0.10/12.0

# Term of loans (# of timesteps)
# B loan's min payment will be computed within loop
N = 100.0

x = 10000.0
nx = 800
max_iters = 9000
a = 100.0
L0A = 500.0
L0B = L0A + np.linspace(0.0, x, nx)
dL0 = L0B - L0A

mp1 = compute_monthly_loan_payment(L0A,IA,N)

Tgd = []
Tdi = []
for LB in L0B:

    L = np.array( [L0A, LB] )
    I = np.array( [IA, IB] )
    mpB = compute_monthly_loan_payment(LB, IB, N)
    mp = np.array( [mp1, mpB] )

    w, n, grand_total_paid = gradient_descent_algo1(L, I, mp, a, max_iters, 0.01)
    grand_total, tsteps = descending_interest_method(L, I, mp, a, 2)
    Tgd.append(grand_total_paid)
    Tdi.append(grand_total)

Tgd = np.array(Tgd)
Tdi = np.array(Tdi)

fig, ax = plt.subplots(2,1,figsize=(9,9),sharex=True)
ax[0].plot(dL0,Tdi,color='green',label='descending interest')
ax[0].plot(dL0,Tgd,color='orange',label='gradient descent')
ax[0].grid()
ax[0].legend()
#ax[0].set_xlabel('difference between loan principals ($)')
ax[0].set_ylabel('total amount paid ($)')
ax[1].plot(dL0,(Tdi-Tgd),color='blue')
ax[1].set_xlabel('difference between loan starting principals ($)')
ax[1].set_ylabel('difference in total amount paid ($)\n descending interest minus gradient descent')
ax[1].grid()
fig.suptitle('Comparison of methods using 2-loan scenario, varying only the starting\n principal of one of the loans.')
plt.show()


#!/usr/bin/env python
# coding: utf-8
import numpy as np
#This is a implementation and library for binary classification, a.k.a logistic regression

'''
In order to implement logistic regression, we need to initialize parameters w and b, note that when using logistic regression,
we have only one computing unit in the neural network. Therefore parameters can be initialized to all zeros.
However, if we are using multiple computing units(e.g. sigmoid or relu), it is required to initialize the parameter w randomly, while
b can be set to all zeros.
'''
def logistic_parameter_initialize(x_dim):
    # x_dim indicates the dimensions of input feature,a bias unit b is defaultly set.
    w = np.zeros((1,x_dim))
    b = 0

    parameter = {'w':w,'b':b}
    return parameter

#implementation of sigmoid function
def sigmoid(z):
    value = 1/(1+np.exp(-z))
    return value

#note the dimensions of vectors:
# w - (1,x_dims)
# b - (1,x_dims) *broadcasted*
# X - (x_dims,m) *m=number of samples

# Forward propagation step: compute the predicted y's label
def forward_prop(w,b,X):
    z = np.dot(w,X)+b
    a = sigmoid(z)
    return z,a

# Compute cost function: used to check convergence
def compute_cost(a,y):
    m = a.shape[1]
    cost = -np.sum(y*np.log(a)+(1-y)*np.log(1-a))/m
    return cost

# Back propagation step: compute partial derivatives of each parameter respectively
def back_prop(X,a,y):
    m = a.shape[1]
    dz = a - y
    dw = np.dot(X,dz.T).T/m
    db= np.sum(dz)/m
    # Note: dw should have the same dimension as w have.Therefore back_prop return dw.T
    return dw,db

# The overall implementation of trainning a logistic regression
def train_logistic_regression(X,y,number_of_iteration = 1000,learning_rate = 0.03,print_cost = True,plot_cost = True):
    # Dimension convert: make sure all vectors are in proper shapes.
    y = y.reshape(1,-1)   # y is a row vector
    m = y.shape[1]  #  m = total number of trainning examples
    if(X.shape[1] != m):
        X=X.T       #=====> Note that array.reshape and array.T are different!
    x_dim = X.shape[0]
    # Initialize parameters
    params = logistic_parameter_initialize(x_dim)
    w = params['w']
    b = params['b']

    if(plot_cost == True):
        i_curve = []
        cost_curve = []
        plt.figure(figsize=(5,5))
        plt.title('Cross entrophy of regression')

    for i in range(1,number_of_iteration+1):
        z,a = forward_prop(w,b,X)
        dw,db = back_prop(X,a,y)
        w = w - learning_rate*dw
        b = b - learning_rate*db
        cost = compute_cost(a,y)
        # Visualize the process of regression
        if(i%100 == 0 and print_cost == True):
            print('number of iterations:{}, cost = {}'.format(i,cost))
        if(i%100 == 0 and plot_cost == True):
            i_curve.append(i)
            cost_curve.append(cost)
    if(plot_cost==True):
        i_curve = np.reshape(i_curve,(1,-1))
        cost_curve = np.reshape(cost_curve,(1,-1))
        plt.scatter(i_curve,cost_curve)

    return w,b

#After training the unit, we can now use it to make predictions.
def logistic_predict(w,b,X,y=0,evaluate = True):
    if(not w.shape[1] == X.shape[0]):
        X = X.T
    yhat = np.dot(w,X)+b
    yhat = yhat>0.5
    #Codes below is used to evaluate the performance of logistic regression on given dataset X with label y
    #You can just ignore this part
    if(evaluate == True):
        y=y.reshape(1,-1)
        train_accuracy = np.sum(yhat==y)/y.shape[1]
        print('accuracy = %.2f\n'%train_accuracy)
    return yhat


import numpy as np
import sys

def matrix_chain_order(p):
	n = len(p) - 1
	m = np.zeros((n, n)).astype(int)
	s = np.zeros((n, n)).astype(int)

	for l in range(2, n+1):
		for i in range(0, n-l+1):
			j = i + l - 1
			m[i, j] = sys.maxint
			for k in range(i, j):
				q = m[i, k] + m[k+1, j] + p[i] * p[k+1] * p[j+1]
				if q < m[i, j]:
					m[i, j] = q
					s[i, j] = k

	return (m, s)

def print_optimal_parens(s, i, j):
	if i == j:
		sys.stdout.write("A" + str(i+1))
	else:
		sys.stdout.write("(")
		print_optimal_parens(s,i,s[i, j])
		print_optimal_parens(s, s[i, j] + 1, j)
		sys.stdout.write(")")

def main():
	#p = [30, 35, 15, 5, 10, 20, 25]
	p = [5, 10, 3, 12, 5, 50, 6]
	(m, s) = matrix_chain_order(p)

	print "m =\n", m
	print "s =\n", s

	print_optimal_parens(s, 0, 5)
	print

if __name__ == "__main__":
	main()


import numpy as np
from numpy import linalg as la
from matplotlib import pyplot as plt
import bisection as bis
class Levenberg():

    def descent(self, X = np.array([[0.0],[0.0]]), precision = 10**-6):
        self.Xs = []       # Candidates
        self.Xs.append(X)  # First Candidate (x0)
        self.precision = precision

        while la.norm(self.gradF(self.Xs[-1])) > self.precision:
            X = self.Xs[-1]
            grT = self.buildGradientR().T
            H = self.hessiana(X)
            d = np.dot(np.dot(la.inv(H), grT), self.r(X))
            X = X - 1*d  # TODO: calculate optimal alpha
            self.Xs.append(X)

        return self.Xs[-1]

    def r(self, X):
        x1 = X[0][0]
        x2 = X[1][0]

        return np.array([[4*(x1 - 2)], [4*(x2 - 3)]])

    ''' Builds the matrix of gradient of r(x)'''
    def buildGradientR(self):   # TODO: Transform function in CONSTANT
        return np.array([[4, 0],
                         [0, 4]])

    def gradF(self, X):
        grT = self.buildGradientR().T
        r = self.r(X)

        return np.dot(grT, r)

    ''' Returns Gradient of r(x)T * Gradient of r(x)'''
    def hessiana(self, X):
        grT = self.buildGradientR().T
        gr = self.buildGradientR()

        return np.dot(grT, gr)

    def plot(self, title, xlabel, ylabel):
        x1s = []
        x2s = []

        for X in self.Xs:
            x1s.append(X[0][0])
            x2s.append(X[1][0])

        plt.plot(x1s, x2s, 'ro')
        plt.title(title)
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        plt.show()


from mpi4py import MPI
import numpy

def paralelo_pi(n):
    count = 0
    for x, y in n:
        if x**2 + y**2 <= 1:
            count += 1
    pi = 4*float(count)/len(n)
    return pi

comm = MPI.COMM_WORLD
processadores = comm.Get_size()
oRank = comm.Get_rank()

if oRank == 0:
    N = 100000
    n = numpy.random.random((processadores, N, 2))
else:
    n = None
n = comm.scatter(n, root=0)

piBuffer = paralelo_pi(n)

somaPi = comm.reduce(piBuffer, root=0)

if oRank == 0:
    pi = somaPi / processadores
    error = abs(pi - numpy.pi)
    print("pi: %.16f, erro: %.16f" % (pi, error))


# -*- coding: utf-8 -*-
'''This module provides functions related to Digital Signal Processing and Fourier Analysis.'''
import numpy as np
import matplotlib.pyplot as plt

def make_sinusoidal_signal(
    mean: float=0,
    amp: float=1.0,
    freq: int=1,
    points: int=100,
    noise_mean: float=0.0,
    noise_var: float=0.1):
    """
    Generate a synthetic signal.

    mean: Signal baseline or average.
    amp: Signal amplitude
    freq: number of times you repeat one cycle
    points: number of points (samples) within each cycle
    noise_mean: mean of the white noise signal
    noise_var: variance of the white noise signal. Note that this impacts your signal/noise ratio
    """
    one_cycle = amp * np.sin(np.linspace(0, 2*np.pi, num=points))
    full_signal = np.tile(one_cycle, freq) + mean
    total_length = points * freq
    noise = np.random.normal(noise_mean, noise_var, size=total_length)
    return full_signal + noise

def dft_report(signal):
    # compute dft
    fft = np.fft.fft(signal)

    # get real components (index is freq as k/N of 2pi, value is the energy)
    real = np.abs(np.real(fft))

    # keep only the first half
    midpoint = int(signal.size / 2)
    real = real[:midpoint] / signal.size

    top_freq_index = np.argmax(real[1:]) + 1
    top_freq_amp = real[top_freq_index] * 2
    print(f'Average = {real[0]:.2f}', )
    print(f'Index of most prominent freq = {top_freq_index}')
    print(f'Top Freq Amplitude = {top_freq_amp:.4f}')
    print(f'''Period of the most prominent freq = {int(signal.size/top_freq_index)}''')
    plt.stem(2*real[1:]);


#===================================================================================
#
#   Code (đã cái tiến) cho PP chia đôi.
#       * Input: f(x) trong pt f(x) = 0; khoảng cách li ban đầu (a, b); sai số epsilon
#       * Output: Nghiệm PT f(x) = 0;
#       * Hạn chế: Chưa có gói tìm khoảng cách ly nghiệm
#       * Cải tiến: Giảm khối lượng tính toán - tính f(a) 1 lần + thêm SymPy
#
#===================================================================================
from sympy import *
from math import *
import sys

#===================================================================================
# Phần thuật toán chính
class bisection_oop:
#{
    def __init__(self, a_0, b_0, eps, expr):
    #{
        self.symf = sympify(expr);
        self.f = lambdify(symbols("x"), self.symf, "math");
        self.a_0 = a_0;
        self.b_0 = b_0;
        self.eps = eps;

    def __checkInputValidity(self):
    #{
        L = self.a_0
        R = self.b_0

        # Corner case: f(L) = 0 or f(R) = 0
        if(self.f(L) == 0 or self.f(R) == 0): return 1

        # Check if a < b
        if(L > R or (L == R and self.f(L) != 0)): return 0

        # Check if f(a) * f(b) < 0
        if(self.f(L) * self.f(R) >= 0): return 0

        return 1
    #}
    def __bisectionMethod(self):
    #{
        # Internal function
        # Return root of f(x) = 0 which f(x), eps and range [a_0, b_0] are given.
        # Assign [a, b] and eps
        nIterations = 0
        left    = self.a_0
        right   = self.b_0
        epsilon = self.eps

        # Special case: f(a) = 0 or f(b) = 0
        if(self.f(left) == 0): return left
        if(self.f(right) == 0): return right

        # Evaluation phase
        mid   = (left + right) / 2
        lft_sign = 1 if self.f(left) >= 0 else -1
        while abs(right - left) >= epsilon:
        #{
            mid = (left + right) / 2
            val = self.f(mid)

            # print(left, mid, right, sep=',', file=sys.stderr)

            if(val == 0): return mid
            if(val * lft_sign < 0):
                right = mid
            else:
                left = mid

            nIterations = nIterations + 1
        #}

        # print(left, mid, right, sep=',', file=sys.stderr)
        print(f"Phương pháp chia đôi kết thúc sau {nIterations} lần lặp")
        return mid
    #}

    def Solve(self):
    #{
        if(self.__checkInputValidity() == 0):
        #{
            print("Invalid input. The program will now exit", file=sys.stderr)
            return float("NaN")
        #}
        return self.__bisectionMethod()
    #}
#}

#===================================================================================
# Chương trình ví dụ
# uu = bisection_oop(L, R, eps, input_expr)
# print(uu.Solve())
#uu = bisection_oop(0.008, 0.08, 1e-10, "1/sqrt(x)+2*log(0.0000015/(3.7*0.005)+ 2.51/(13.743*sqrt(x)))/log(10)")
print(f" Nghiệm của PT f(x) = 0 theo PP chia đôi là: x = {uu.Solve()}")

Topic 1 - Find solution for single-variable functions/02.Chia_doi/bisection.py


import numpy as np

from pySDC.core.Collocation import CollBase
from pySDC.core.Errors import CollocationError

class CollGaussLegendre(CollBase):
    """
    Implements Gauss-Legendre Quadrature

    Attributes:
        order (int): order of the quadrature
        num_nodes (int): number of collocation nodes
        tleft (float): left interval point
        tright (float): right interval point
        nodes (numpy.ndarray): array of quadrature nodes
        weights (numpy.ndarray): array of quadrature weights for the full interval
        Qmat (numpy.ndarray): matrix containing the weights for tleft to node
        Smat (numpy.ndarray): matrix containing the weights for node to node
        delta_m (numpy.ndarray): array of distances between nodes
        right_is_node (bool): flag to indicate whether right point is collocation node
        left_is_node (bool): flag to indicate whether left point is collocation node
    """

    def __init__(self, num_nodes, tleft, tright):
        """
        Initialization

        Args:
            num_nodes (int): number of nodes
            tleft (float): left interval boundary (usually 0)
            tright (float): right interval boundary (usually 1)
        """
        super(CollGaussLegendre, self).__init__(num_nodes, tleft, tright)
        if num_nodes < 1:
            raise CollocationError("Number of nodes should be at least 1 for Gauss-Legendre, but is %d" % num_nodes)
        self.order = 2 * self.num_nodes
        self.nodes = self._getNodes
        self.weights = self._getWeights(tleft, tright)
        self.Qmat = self._gen_Qmatrix
        self.Smat = self._gen_Smatrix
        self.delta_m = self._gen_deltas
        self.left_is_node = False
        self.right_is_node = False

    @property
    def _getNodes(self):
        """
        Computes nodes for the Gauss-Legendre quadrature

        Python version by Dieter Moser, 2014

        Returns:
            np.ndarray: array of Gauss-Legendre nodes
        """
        M = self.num_nodes
        a = self.tleft
        b = self.tright

        # Building the companion matrix comp_mat with det(nodes*I-comp_mat)=P_n(nodes), where P_n is the
        # Legendre polynomial under consideration. comp_mat will be constructed in such a way that it is symmetric.
        linspace = np.linspace(1, M - 1, M - 1)
        v = [linspace[i] / np.sqrt(4.0 * linspace[i] ** 2 - 1.0) for i in range(len(linspace))]
        comp_mat = np.diag(v, 1) + np.diag(v, -1)

        # Determining the abscissas (nodes) - since det(nodesI-comp_mat)=P_n(nodes), the abscissas are the roots
        # of the characteristic polynomial, i.e. the eigenvalues of comp_mat
        [eig_vals, _] = np.linalg.eig(comp_mat)
        indizes = np.argsort(eig_vals)
        nodes = eig_vals[indizes]

        # take real part and shift from [-1,1] to [a,b]
        nodes = nodes.real
        nodes = (a * (1 - nodes) + b * (1 + nodes)) / 2

        return nodes


import numpy as np

__all__ = [
    "prop",
    "gini_impurity",
    "entropy",
    "info_gain",
]

def prop(x, s):
    """Returns the proportion of `x` in `s`.
    """
    return list(s).count(x)/len(s)

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

def gini_impurity(s):
    """Calculate the Gini Impurity for a list of samples.

    See:
    https://en.wikipedia.org/wiki/Decision_tree_learning#Gini_impurity
    """
    return 1 - sum(prop(s[i], s)**2 for i in range(len(s)))

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

def entropy(s):
    """Calculate the Entropy Impurity for a list of samples.
    """
    return -sum(
        p*np.log(p) for i in range(len(s)) for p in [prop(s[i], s)]
    )

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

def info_gain(current_impurity, true_branch, false_branch, criterion):
    """Information Gain.

    The uncertainty of the starting node, minus the weighted impurity of
    two child nodes.
    """

    measure_impurity = gini_impurity if criterion == "gini" else entropy
    p = float(len(true_branch)) / (len(true_branch) + len(false_branch))

    return current_impurity - p * measure_impurity(true_branch) - (1 - p) * measure_impurity(false_branch)

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


import numpy as np

# Write a function that takes as input two lists Y, P,
# and returns the float corresponding to their cross-entropy.

def cross_entropy(Y, P):
    return -1 * sum(
        [
            (Y[i] * np.log(P[i]) + (1 - Y[i]) * np.log(1 - P[i]))
            for i in range(len(Y))
        ]
    )

if __name__ == "__main__":
    Y = [1, 0, 1, 1]
    P = [0.4, 0.6, 0.1, 0.5]
    print(cross_entropy(Y, P))
    print('should be: 4.8283137373')


from numpy import array, pi, sin
from NIM.benchmarks.benchmark import Benchmark

class Levy13(Benchmark):
    """dim: 2"""
    def __init__(self, lower=-10, upper=10, dimension=2):
        super(Levy13, self).__init__(lower, upper, dimension)

    def get_optimum(self):
        return array([[1, 1]]), 1.3497838043956716e-31

    @staticmethod
    def eval(sol):
        temp1 = (sin(3 * pi * sol[0]) ** 2)
        temp2 = (sol[0] - 1) ** 2 * (1 + (sin(3 * pi * sol[1])) ** 2)
        temp3 = (sol[1] - 1) ** 2 * (1 + (sin(2 * pi * sol[1])) ** 2)

        return temp1 + temp2 + temp3


import numpy as np

def hyperbolic_gammas(gamma_max, k, num_heads):
    # Formula (27) from https://arxiv.org/pdf/1902.06865.pdf
    b = np.exp(np.log(1 - gamma_max**(1 / k)) / num_heads)
    gammas = (1 - b**(np.arange(num_heads) + 1))**k
    return gammas


def midpoint_double1(f, a, b, c, d, nx, ny):
    hx = (b - a)/nx
    hy = (d - c)/ny
    I = 0
    for i in range(nx):
        for j in range(ny):
            xi = a + hx/2 + i*hx
            yj = c + hy/2 + j*hy
            I = I + hx*hy*f(xi, yj)
    return I

def midpoint(f, a, b, n):
    h = (b-a)/n
    f_sum = 0
    for i in range(0, n, 1):
        x = (a + h/2.0) + i*h
        f_sum = f_sum + f(x)
    return h*f_sum

def midpoint_double2(f, a, b, c, d, nx, ny):
    def g(x):
        return midpoint(lambda y: f(x, y), c, d, ny)

    return midpoint(g, a, b, nx)

def test_midpoint_double():
    """Test that a linear function is integrated exactly."""
    def f(x, y):
        return 2*x + y

    a = 0;  b = 2;  c = 2;  d = 3
    import sympy
    x, y = sympy.symbols('x  y')
    I_expected = sympy.integrate(f(x, y), (x, a, b), (y, c, d))
    # Test three cases: nx < ny, nx = ny, nx > ny
    for nx, ny in (3, 5), (4, 4), (5, 3):
        I_computed1 = midpoint_double1(f, a, b, c, d, nx, ny)
        I_computed2 = midpoint_double2(f, a, b, c, d, nx, ny)
        tol = 1E-14
        #print I_expected, I_computed1, I_computed2
        assert abs(I_computed1 - I_expected) < tol
        assert abs(I_computed2 - I_expected) < tol

if __name__ == '__main__':
    test_midpoint_double()


import numpy as np
import scipy.linalg as la

#######################################################################
# This function is an implementation of Newton's method for nonlinear #
# systems of equations.                                               #
#                                                                     #
#   PARAMETERS:                                                       #
#       F:  np column matrix of equations f1, f2, ... fn.             #
#       DF: Jacobian of F.                                            #
#       X:  Initial guess for solution set.                           #
#       n:  Number of iterations.                                     #
#                                                                     #
# Currently prints solution iteratively, but can be easily changed to #
# return the final value of X.                                        #
#                                                                     #
#       Colin McKinley | September 2018                               #
#######################################################################

def multNewton(F, Df, X, n):
    #Evaluation of lambdas must be done on 1d array
    #Arrays are flattened and reshaped
    DfShape = np.shape(Df)
    FShape = np.shape(F)
    XShape = np.shape(X)

    #Create arrays to hold values
    DfNums = np.zeros((np.size(Df)))
    FNums = np.zeros((np.size(F)))
    for i in range(n):
        print i
        print "="*25
        #Fill Number arrays with values from lambdas
        for j, f in enumerate(F.flatten()):
            FNums[j] = f(X[0])

        for k, df in enumerate(Df.flatten()):
            DfNums[k] = df(X[0])

        #Reshape for matrix operations
        FNums = FNums.reshape(FShape)
        DfNums = DfNums.reshape(DfShape)

        #Solve system
        Dx = la.solve(-DfNums, FNums)

        #Transpose X to column
        X = X.T
        X += Dx

        #Flatten back to 1d arrays
        FNums = FNums.flatten()
        DfNums = DfNums.flatten()
        X = X.reshape(XShape)
        print X
    return "EXIT_SUCCESS"

#Simple test case with f1 = x0^3 + x1 - 1 & f2 = x1^3 -
if __name__ == "__main__":
    #Functions for f
    F = np.array([[lambda x: x[0]**3 + x[1] - 1],
                  [lambda x: x[1]**3 - x[0] + 1]])

    #Partial derivatives
    Df11 = lambda x: 3*(x[0]**2) + x[1] - 1
    Df12 = lambda x: 1
    Df21 = lambda x: -1
    Df22 = lambda x: 3*(x[0]**2) + x[1] - 1
    Df = np.array([[Df11, Df12], [Df21, Df22]])
    X = np.array([.5, .5])[np.newaxis]
    n = 100
    print(multNewton(F, Df, X, n))