#!/usr/bin/env python

import numpy as np
import matplotlib.pyplot as plt

def circle_formula(x, r):
	y = np.sqrt(r**2 - x**2) # rearranged from x^2 + y^2 = r^2
	return y

def vect_dist(v1, v2):
	dx = v1[0] - v2[0] # change in x
	dy = v1[1] - v2[1] # change in y
	dist = np.sqrt(np.power(dx,2) + np.power(dy, 2)) # pythagoras
	return dist

def find_pi(steps):

	r = 1 # Radius shouldn't matter so long as it is consistent
	x = np.linspace(0, r, steps) # x ranging from origin to radius
	y = circle_formula(x, r) # calculate corresponding y values

	vect_list = np.vstack((x,y)).T # stack x and y values to make next step easier

	# Now going to add up each segment to get the length of the arc
	curve_len = 0. # initialise curve length
	for i in range(len(vect_list)-1):
		curve_len += vect_dist(vect_list[i], vect_list[i+1]) # add each segment of the curve

	c = 4*curve_len # we only measured 1/4 of a circle
	d = 2*r # diameter is two times radius

	pi = c/d # from definition pi*d = circumfrence

	return pi

if __name__ == "__main__":

	steps = [10, 100, 1000, 10000, 100000, 1000000] # number of circle points in calculation
	pis = [] # empty list to house calculated values of pi

	for step in steps:
		pis.append(find_pi(step)) # find pi using varying numbers of steps

	# Print difference between my value and numpy value of pi
	print(f"There is a difference of {np.abs(np.pi-pis[-1])} at {steps[-1]} steps.")

	# Plot values
	plt.plot([min(steps), max(steps)], [np.pi, np.pi]) # plot actual value of pi
	plt.semilogx(steps, pis) # plot my values of pi
	plt.show() # show graph
	# Whole script runs in 12.4s on my PC, if taking too lower max nr of steps


# The sales of a new high-tech item (in thousands) are given by:

# S(t) = 108 - 90e^-0.4t

# where t represents time in years. Find the rate of change of sales at each time.

from sympy import *
import math
import mpmath as mp

init_printing()

def disp_fun( f ):
	pprint( '\n{0}\n\n'.format( pretty( f ) ) )

t = symbols( 't' )
S = 110 - ( 90 * exp( -0.4*t ) )
dS = diff( S, t )

disp_fun( S )
disp_fun( dS )

# a.) After 1 year. (Round to three decimal places as needed.)
round( dS.subs( { t: 1 } ), 3 )

# b.) After 5 years.
round( dS.subs( { t: 5 } ), 3 )

# c.) What is happening to the rate of change of sales as time goes on?

print( 'It always decreases' )

# d.) Does the rate of change of sales ever equal zero?
print( 'No' )


import numpy as np

def powermod(k,i,n):
    if i==0: return 1
    if i%2==0: return powermod(k, i/2, n)**2 % n
    if i%2==1: return (k*powermod(k, i-1, n)) % n

def factorization(n):
    d = dict()
    for i in range(2, int(np.sqrt(n))+1):
        if n % i == 0: d[i] = 0
        while n % i == 0:
            n = n / i
            d[i] += 1
    if n > 1:
        if n in d: d[n] += 1
        else: d[n] = 1
    return d

def bmg(p, start=None, skip=0):
    """
    Biggest Modular Generator
    """
    count_gen=0
    a = p - 2 if start is None else min(p - 2, start)
    while a > 1:
        if isprimroot(p, a):
            if count_gen == skip:
                return a
            count_gen += 1
        a -= 1
    return None

def isprimroot(p, m):
    for f in factorization(p-1).keys():
        if powermod(m, (p-1)/f, p) == 1:
            return False
    return True

def RabinMiller(n, k):
    """
    Input1: n, an integer to be tested for primality;
    Input2: k, a parameter that determines the accuracy of the test
    Output: False if n is composite, otherwise True if probably prime
    """
    if n<2: return False
    if n in [2,3]: return True
    if n%2==0: return False

    m = n-1
    r=0
    while(m%2==0):
        r+=1
        m = m/2
    d = (n-1)/2**r

    for _ in range(k): #WitnessLoop
        a = np.random.randint(2, n-2)
        x = powermod(a, d, n)
        if x == 1 or x == n - 1: continue

        continue_WitnessLoop = False
        for _ in range(r-1):
            x = (x**2) % n
            if x == 1:
                return False
            if x == n - 1:
                continue_WitnessLoop = True
                continue
        if continue_WitnessLoop: continue
        return False
    return True

def next_prime(k=16, start=0, skip=0):
    if start in [0,1]: m = 2
    elif start%2==0: m = start+1
    else: m = start
    while not RabinMiller(m, k): m += 2
    return m if skip == 0 else next_prime(k=k, start=m, skip=skip-1)


import pandas as pd
import numpy as np
import numpy.random as npr
import statsmodels as sm
import statsmodels.tsa.stattools as ts
import math
import scipy
import matplotlib.pyplot as plt
from scipy import log,exp,sqrt,stats
import sys
import os
import logging
from brd_mod.brdstats import *
from brd_mod.brdgeo import *

def discrete_future_value(x,r,n):
	'''
	Discrete Future Value of Money over Time
	x= initial capital
	r= rate (0.n format)
	n= time increment
	'''
	return x*(1+r)**n

def discrete_present_value(x,r,n):
	'''
	Discrete Present Value of Money from Time
	x= future capital
	r= rate (0.n format)
	n= time increment
	'''
	return x*(1+r)**-n

def continuous_future_value(x,r,t):
	'''
	Continuous Future Value of Money over Time
	x= initial capital
	r= rate (0.n format)
	n= time increment
	'''
	return x*math.exp(r*t)

def continuous_present_value(x,r,t):
	'''
	Continuous Present Value of Money from Time
	x= future capital
	r= rate (0.n format)
	n= time increment
	'''
	return x*math.exp(-r*t)

def value_at_risk(position, c, mu, sigma):
	'''
	Calculate Basic Value at Risk (VAR)
	position= total value of investment
	c= confidence level
	mu= mean return
	sigma= sd of return
	'''
	alpha=stats.norm.ppf(1-c)
	var = position*(mu-sigma*alpha)
	return var

def value_at_risk_long(S, c, mu, sigma, n):
	'''
	Calculate Basic Value at Risk (VAR) into the Future
	S= total value of investment
	c= confidence level
	mu= mean return
	sigma= sd of return
	n= time increment
	'''
	alpha=stats.norm.ppf(1-c)
	var = S*(mu*n-sigma*alpha*np.sqrt(n))
	return var

def imperial_returns(data):
	'''
	Calculate Basic Returns of a Price Series
	Uses Formula: (data(t)-data(t-1))-1
	'''
	daily_returns = (data/data.shift(1))-1
	return daily_returns;

def log_returns(data):
	'''
	Calculate Logarithmic Returns of a Price Series
	Uses Formula: ln(data(t)/data(t-1))
	'''
	returns = np.log(data/data.shift(1))
	return returns;

def rolling_volatility(data, n=10):
	'''
	Generate Rolling Standard Deviation of a Series
	n= window size
	'''
	return data.rolling(window=n).std()

def show_data_plot(data):
	'''
	Basic Way to Plot and Show Data
	'''
	data.plot(figsize=(10,5))
	plt.show()

def brownian_motion(mu=0, dt=0.1, N=1000):
	'''
	Generate x-y Brownian Motion Series (Wiener Process)
	mu= mean of distribution
	dt= standard deviation of distribution
	N= size of sample
	'''
	W = scipy.zeros(N+1)
	t = scipy.linspace(0, N, N+1);
	W[1:N+1] = scipy.cumsum(scipy.random.normal(mu,dt,N))
	return t,W

def plot_brownian_motion(t,W):
	'''
	Plots a Wiener Process (Brownian Motion) with Labels and Title
	t= x values (time)
	W= y values (Wiener process)
	'''
	plt.plot(t,W)
	plt.xlabel('Time(t)')
	plt.ylabel('Wiener-process W(t)')
	plt.title('Wiener-process')
	plt.show()

def blackscholes_call(S,E,T,rf,sigma):
	'''
	Prices European Call Option
	S= stock price at current time
	E= strike price in future
	T= expiry in years
	rf= risk free rate (0.n format)
	sigma= volatility of underlying stock
	'''
	d1=(log(S/E)+(rf+sigma*sigma/2.0)*T)/(sigma*sqrt(T))
	d2 = d1-sigma*sqrt(T)
	return S*stats.norm.cdf(d1)-E*exp(-rf*T)*stats.norm.cdf(d2)

def blackscholes_put(S,E,T,rf,sigma):
	'''
	Prices European Put Option
	S= stock price at current time
	E= strike price in future
	T= expiry in years
	rf= risk free rate (0.n format)
	sigma= volatility of underlying stock
	'''
	d1=(log(S/E)+(rf+sigma*sigma/2.0)*T)/(sigma*sqrt(T))
	d2 = d1-sigma*sqrt(T)
	return -S*stats.norm.cdf(-d1)+E*exp(-rf*T)*stats.norm.cdf(-d2)

def zero_bond_price(par_value,market_rate,n):
	'''
	Prices a Zero-Coupon Bond
	par_value= bond's par value (base)
	market_rate= market return rate (0.n format)
	n= years into future
	'''
	return par_value/(1+market_rate)**n

def bond_price(par_value,coupon,market_rate,n):
	'''
	Prices a Zero-Coupon Bond
	par_value= bond's par value (base)
	coupon= bond yield (0.n format)
	market_rate= market return rate (0.n format)
	n= years into future
	'''
	c = par_value*coupon
	return c/market_rate*(1-(1/(1+market_rate)**n))+par_value/(1+market_rate)**n

def array_to_series(data):
	'''
	Converts numpy.array into pandas.series
	'''
	return pd.Series(data)

def series_to_array(data):
	'''
	Converts pandas.series into numpy.array
	NOTE: Deprecated for Python 3+
	'''
	return data.as_matrix()

def kelly_leverage(returns, rf=0):
	'''
	Calculate Optimal Leverage According to Kelly Formula:
	(meanReturn-rf)/(stdReturn)^2
	returns= returns series
	rf= risk-free rate (0.n format)
	'''
	mean= returns.mean()
	std= returns.std()
	return (mean-rf)/(std**2)

def sharpe_ratio(returns, rf=0):
	'''
	Calculate Basic Sharpe Ratio According to Formula:
	(meanReturn-rf)/(stdReturn)
	returns= returns series
	rf= risk-free rate (0.n format)
	'''
	mean= returns.mean()
	std= returns.std()
	return (mean-rf)/(std)

def kelly_criterion(W=0.5, R=1):
	'''
	Calculate Optimal Portfolio Weight According to Kelly Criterion:
	W= winning probability
	R= win/loss ratio
	'''
	return W- ((1-W)/R)

def ols(Y, X, show_print=True):
	'''
	Runs and fits an Ordinary Least Squares
	regression on y from x's; including
	show_print willprint the summary of the regression
	'''
	model= sm.OLS(Y, X)
	results= model.fit()
	if show_print:
		logging.info(results.summary())

	return results

import numpy,import scipy,from scipy,import statsmodels


import numpy as np
from scipy import stats

def Grubbs(data, type = "Two Sided", alpha = 0.05, verbose = False):
    '''
    Performs the Grub's Outlier test on data.
    Set verbose to true to see the grubbs value and threshold
    Returns NaN if no outlier is present
    Else returns the index of the outlier
    '''
    data = np.array(data)
    mean = np.nanmean(data)
    std = np.std(data)
    dof = data.shape[0] -1
    N = data.shape[0]

    if type.lower() == "two sided":
        t = stats.t.ppf(q = 1 - alpha / (2*N), df = dof)
        G = np.max(np.abs(data - mean)) / std
        threshold = (N-1) / N**.5 * (t**2 / (N-2 + t**2))**.5
        if verbose:
            print(f"G = {G:.4f}\nGrubbs Threshold = {threshold:.4f}")
        if G > threshold:
            return np.argmax(np.abs(data - mean))
        else:
            return np.nan

    elif type.lower() == "greater":
        t = stats.t.ppf(q = 1 - alpha / (N), df = dof)
        G = (np.max(data) - mean) / std
        threshold = (N-1) / N**.5 * (t**2 / (N-2 + t**2))**.5
        if verbose:
            print(f"G = {G:.4f}\nGrubbs Threshold = {threshold:.4f}")
        if G > threshold:
            return np.argmax(data)
        else:
            return np.nan

    elif type.lower() == "lesser":
        t = stats.t.ppf(q = 1 - alpha / (N), df = dof)
        G = (mean - np.min(data)) / std
        threshold = (N-1) / N**.5 * (t**2 / (N-2 + t**2))**.5
        if verbose:
            print(f"G = {G:.4f}\nGrubbs Threshold = {threshold:.4f}")
        if G > threshold:
            return np.argmin(data)
        else:
            return np.nan


import numpy as np
from scipy.special import comb

#Based on code from JohnDeJesus22: https://github.com/JohnDeJesus22/DataScienceMathFunctions/blob/master/hypergeometricfunctions.py

def hypergeom_pmf(N, A, n, x):

    '''
    Probability Mass Function for Hypergeometric Distribution
    :param N: population size
    :param A: total number of desired items in N
    :param n: number of draws made from N
    :param x: number of desired items in our draw of n items
    :returns: PMF computed at x
    '''
    Achoosex = comb(A,x)
    NAchoosenx = comb(N-A, n-x)
    Nchoosen = comb(N,n)

    return (Achoosex)*NAchoosenx/Nchoosen

def hypergeom_cdf(N, A, n, t, min_value = None, max_value = None):

    '''
    Cumulative Density Funtion for Hypergeometric Distribution
    :param N: population size
    :param A: total number of desired items in N
    :param n: number of draws made from N
    :param t: number of desired items in our draw of n items up to t
    :returns: CDF computed up to t
    '''
    if min_value:
        if(max_value):
            return np.sum([hypergeom_pmf(N, A, n, x) for x in range(min_value, max_value+1)])
        else:
            return np.sum([hypergeom_pmf(N, A, n, x) for x in range(min_value, t+1)])

    return np.sum([hypergeom_pmf(N, A, n, x) for x in range(t+1)])


import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import numpy as np
import random
import torch

print('目标函数 f(x) = x*x')

def gd(eta):
    x = 10
    results = [x]
    for i in range(10):
        x -= eta * 2 * x # 导数
        results.append(x)
    print('epoch 10, x:', x)
    return results

print('进行一维梯度下降')
res = gd(0.2)
print(res)
print('绘制迭代轨迹')

def show_trace(res):
    n = max(abs(min(res)), abs(max(res)), 10)
    f_line = np.arange(-n, n, 0.1)
    plt.plot(f_line, [x * x for x in f_line])
    plt.plot(res, [x * x for x in res], '-o')
    plt.xlabel('x')
    plt.ylabel('f(x)')
    plt.show()

show_trace(res)

print('尝试不同学习率，0.05')
show_trace(gd(0.05))

print('尝试不同学习率，1.1')
show_trace(gd(1.1))

print('进行二维梯度下降')

def train_2d(trainer):
    x1, x2, s1, s2 = -5, -2, 0, 0
    results = [(x1, x2)]
    for i in range(20):
        x1, x2, s1, s2 = trainer(x1, x2, s1, s2)
        results.append((x1, x2))
    print('epoch %d, x1 %f, x2 %f' % (i+1, x1, x2))
    return results

def show_trace_2d(f, results):
    plt.plot(*zip(*results), '-o', color='#ff7f0e')
    x1, x2 = np.meshgrid(np.arange(-5.5, 1.0, 0.1), np.arange(-3.0, 1.0, 0.1))
    plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')
    plt.xlabel('x1')
    plt.ylabel('x2')
    plt.show()

eta = 0.1

def f_2d(x1, x2):
    return x1 ** 2 + 2 * x2 ** 2

def gd_2d(x1, x2, s1, s2):
    return (x1 - eta * 2 * x1, x2 - eta * 4 * x2, 0, 0)

show_trace_2d(f_2d, train_2d(gd_2d))

print('进行随机梯度下降')

def sgd_2d(x1, x2, s1, s2):
    return (x1 - eta * (2 * x1 + np.random.normal(0.1)),
            x2 - eta * (4 * x2 + np.random.normal(0.1)), 0, 0)

show_trace_2d(f_2d, train_2d(sgd_2d))


import numpy as np
from scipy.stats import binom
from scipy.stats import mode
import pymc3 as pm
import matplotlib.pyplot as plt
import seaborn as sns

p_grid = np.linspace(0.,1.,1000)
prior = np.repeat(1.,1000)
likehood = binom.pmf(6,9,p_grid)
posterior = likehood*prior
posterior = posterior/posterior.sum()

plt.plot(p_grid, posterior)
plt.show()

sample_size = int(10000)
samples = np.random.choice(p_grid,p=posterior,size=sample_size,replace=True)
sns.kdeplot(samples)
plt.show()

print('bellow 0.2:'+str(sum(samples<0.2)/sample_size))
print('above 0.8:'+str(sum(samples>0.8)/sample_size))
print('between 0.2 and 0.8:'+str(sum((samples > 0.2) & (samples < 0.8))/sample_size))
print('20% of posterior probability lies under which p-value?'+str(np.percentile(samples,20)))
print('20% of posterior probability lies above which p-value?'+str(np.percentile(samples,80)))
print('which values of p contain the narrowest interval equal to 66% of posterior?'+str(pm.hpd(samples,alpha=0.66)))
print('which values of p contain the 66% posterior, assuming equal posterior prob bellow and above the interval?')

#it's 17,83 - but lets confirm it
low = 0
up = 66
while low < 35 and up < 100:
    probs = np.percentile(samples,[low,up])
    llow = sum(samples<probs[0])/sample_size
    uup = sum(samples>probs[1])/sample_size
    if np.abs(llow - uup) > 0.001:
        low += 1
        up += 1
        continue
    print('-----')
    print('<%d,%d>'%(low,up))
    print(str(probs))
    print('how much posterior prob is bellow %f:%s '%(probs[0],str(llow)))
    print('how much posterior prob is above %f:%s'%(probs[1],str(uup)))
    break


import numpy as np
from numpy import dtype, linalg

def jacobi(A, b, x0, tol, N):
    """
    Jacobi method: solve Ax = b given an initial approximation x0
    Parameters:
        a: Matrix A from system Ax=b
        b: Array containing b values
        x0: Initial approximation of solution
        tol: Tolerance
        iter_max: Maximum number of iterations
    Returns:
        x: Solution of linear system
    """
    A = A.astype('double')
    b = b.astype('double')
    x0 = x0.astype('double')

    n = np.shape(A)[0]
    x = np.zeros(n)
    it = 0

    while (it < N):
        it += 1

        for i in np.arange(n):
            x[i] = b[i]
            for j in np.concatenate((np.arange(0, i), np.arange(i + 1, n))):
                x[i] -= A[i, j] * x0[j]
            x[i] /= A[i, i]

        new_epsilon = np.linalg.norm(x - x0, np.inf) / np.linalg.norm(x, np.inf)

        if (new_epsilon < tol):
            return x

        x0 = np.copy(x)

    raise NameError('Max. iterations exceeded')

def gauss_seidel(A, b, x0, tol, N):
    """
    Gauss-Seidel method: solve Ax = b given an initial approximation x0
    Parameters:
        a: Matrix A from system Ax=b
        b: Array containing b values
        x0: Initial approximation of solution
        tol: Tolerance
        iter_max: Maximum number of iterations
    Returns:
        x: Solution of linear system
    """
    A = A.astype('double')
    b = b.astype('double')
    x0 = x0.astype('double')

    n = np.shape(A)[0]
    x = np.copy(x0)
    it = 0

    while (it < N):
        it += 1

        for i in np.arange(n):
            x[i] = b[i]
            for j in np.concatenate((np.arange(0,i),np.arange(i + 1, n))):
                x[i] -= A[i, j] * x[j]
            x[i] /= A[i, i]

        new_epsilon = np.linalg.norm(x - x0, np.inf) / np.linalg.norm(x, np.inf)

        if (new_epsilon < tol):
            return x

        x0 = np.copy(x)
    return x


"""
Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable
numbers.

For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284.
The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.

Evaluate the sum of all the amicable numbers under 10000.
"""

import numpy as np
N = 10000

def d(n):
    divisors = set()
    divisors.add(1)

    less_half = np.math.floor(n/2)

    for i in range(2, less_half + 1):

        if n % i == 0:
            divisors.add(i)

    return sum(divisors)

def is_amicable(m):
    a = d(m)
    b = d(a)

    if (m == b) and (a != b):
        return m
    else:
        return False

amicable_pairs = set()

for j in range(1, N):
    check = is_amicable(j)

    if check:
        amicable_pairs.add(j)

print(f'Sum of all the amicable numbers under {N} is {sum(amicable_pairs)}')

Solutions/Problem 21 - Amicable numbers.py


import numpy as np

# Generate some random	data
data = np.random.randn(2, 3)

print(data)

print(data * 10)

print(data + data)

print(data.shape)

print(data.dtype)

data1 = [6, 7.5, 8, 0, 1]

arr1 = np.array(data1)

print(arr1)

data2 = [[1,2,3,4],[5,6,7,8]]
arr2 = np.array(data2)

print(arr2)

print(arr2.ndim)
print(arr2.shape)

print(arr1.dtype)
print(arr2.dtype)

print(np.zeros(10))
print(np.zeros((3,6)))

print(np.empty((2,3,2)))

print(np.arange(15))

arr1 = np.array([1,2,3], dtype=np.float64)

print(arr1)

arr3d =	np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
print(arr3d)
print(arr3d[0])

# 基本的索引和切片
arr = np.arange(10)
print(arr)

print(arr[5])

print(arr[5:8])
arr[5:8] = 12
print(arr)

# 切片索引
arr2d = [[1,2,3],[4,5,6],[7,8,9]]
print(arr2d[:2])
print(arr2d[1:])

# 花式索引
arr = np.empty((8, 4))
for i in range(8):
    arr[i] = i

print(arr)

print(arr[[4, 3, 0, 6]])

arr = np.random.randn(6, 3)
print(arr)

print(np.dot(arr.T, arr))

# 通用函数
arr = np.arange(10)
print(arr)

print(np.sqrt(arr))

print(np.exp(arr))

x = np.random.randn(8)

y = np.random.randn(8)

print(x)
print(y)

print(np.maximum(x, y))

arr = np.random.randn(7) * 5
print(arr)

remainder, whole_part =	np.modf(arr)
print(remainder)
print(whole_part)

points = np.arange(-5, 5, 0.01)

xs, ys = np.meshgrid(points, points)

print(ys)

z = np.sqrt(xs ** 2 + ys ** 2)
print(z)

import matplotlib.pyplot as plt

plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
plt.title("Image	plot	of	$\sqrt{x^2	+	y^2}$	for	a	grid	of	values")


#%%
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
plt.style.use("bmh")

# Las ecuaciones diferenciales del modelo SIR
def deriv(y, t, N, beta, gamma):
    S, I, R = y
    dSdt = -beta * S * I / N
    dIdt = beta * S * I / N - gamma * I
    dRdt = gamma * I
    return dSdt, dIdt, dRdt

def plot(S, I, R, t, divide_by=1):
    # Dibujamos los datos de S(t), I(t) y R(t)
    fig, ax = plt.subplots()
    ax.plot(t, S / divide_by, 'b', alpha=0.5, lw=2, label='Susceptible')
    ax.plot(t, I / divide_by, 'r', alpha=0.5, lw=2, label='Infectado')
    ax.plot(t, R / divide_by, 'g', alpha=0.5, lw=2, label='Recuperado con inmunidad')
    ax.set_xlabel('Tiempo /días')
    ax.set_ylabel(f'Número (dividido por {divide_by:,})')
    legend = ax.legend()

def plot_with_death_rate(S, I, R, t, divide_by=1, death_rate=0.05):
    # Dibujamos los datos de S(t), I(t) y R(t)
    fig, ax = plt.subplots()
    ax.plot(t, S / divide_by, 'b', alpha=0.5, lw=2, label='Susceptible')
    ax.plot(t, I / divide_by, 'r', alpha=0.5, lw=2, label='Infectado')
    RR = R * (1 - death_rate)
    DD = R - RR
    ax.plot(t, RR / divide_by, 'g', alpha=0.5, lw=2, label='Recuperado con inmunidad')
    ax.plot(t, DD / divide_by, 'k', alpha=0.5, lw=2, label='No recuperado')
    ax.set_xlabel('Tiempo /días')
    ax.set_ylabel(f'Número (dividido por {divide_by:,})')
    legend = ax.legend()

# población inicial, N.
N = 779_853 # poblaciçon de un país como España

# Número inicial de infectados y recuperados, I0 and R0.
I0 = 2/N
R0 = 0

# El resto, casi todo N, es susceptible de infectarse
S0 = 779_853

# Tasas de contagio y recuperación.
beta = 0.06 # contagio
gamma = 0.021 # recuperación

# Pasos temporales (en días)
t = np.linspace(0, 652, 652)

# condiciones iniciales
y0 = S0, I0, R0

# Integrate the SIR equations over the time grid, t.
ret = odeint(deriv, y0, t, args=(N, beta, gamma))
S, I, R = ret.T

plot(S, I, R, t) # Datos sin normalizar
plot(S, I, R, t, divide_by=N) # Datos normalizados

plot_with_death_rate(S, I, R, t, divide_by=N, death_rate=0.05)
# %%


"""This file contains code used in "Think Stats",
by Allen B. Downey, available from greenteapress.com

Copyright 2014 Allen B. Downey
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html
"""

from __future__ import print_function, division

import thinkstats2
import thinkplot

import math
import random
import numpy as np

from scipy import stats
from estimation import RMSE, MeanError

"""This file contains a solution to exercises in Think Stats:

Exercise 8.1

In this chapter we used $\xbar$ and median to estimate $\mu$, and
found that $\xbar$  yields lower MSE.
Also, we used $S^2$ and $S_{n-1}^2$ to estimate $\sigma$, and found that
$S^2$ is biased and $S_{n-1}^2$ unbiased.

Run similar experiments to see if $\xbar$ and median are biased estimates
of $\mu$.
Also check whether $S^2$ or $S_{n-1}^2$ yields a lower MSE.

My conclusions:

1) xbar and median yield lower mean error as m increases, so neither
one is obviously biased, as far as we can tell from the experiment.

2) The biased estimator of variance yields lower RMSE than the unbiased
estimator, by about 10%.  And the difference holds up as m increases.

Exercise 8.2

Suppose you draw a sample with size $n=10$ from a population
with an exponential disrtribution with $\lambda=2$.  Simulate
this experiment 1000 times and plot the sampling distribution of
the estimate $\lamhat$.  Compute the standard error of the estimate
and the 90\% confidence interval.

Repeat the experiment with a few different values of $n$ and make
a plot of standard error versus $n$.

1) With sample size 10:

standard error 0.896717911545
confidence interval (1.2901330772324622, 3.8692334892427911)

2) As sample size increases, standard error and the width of
the CI decrease:

10      0.90    (1.3, 3.9)
100     0.21    (1.7, 2.4)
1000    0.06    (1.9, 2.1)

All three confidence intervals contain the actual value, 2.

Exercise 8.3

In games like hockey and soccer, the time between goals is
roughly exponential.  So you could estimate a team's goal-scoring rate
by observing the number of goals they score in a game.  This
estimation process is a little different from sampling the time
between goals, so let's see how it works.

Write a function that takes a goal-scoring rate, {\tt lam}, in goals
per game, and simulates a game by generating the time between goals
until the total time exceeds 1 game, then returns the number of goals
scored.

Write another function that simulates many games, stores the
estimates of {\tt lam}, then computes their mean error and RMSE.

Is this way of making an estimate biased?  Plot the sampling
distribution of the estimates and the 90\% confidence interval.  What
is the standard error?  What happens to sampling error for increasing
values of {\tt lam}?

My conclusions:

1) RMSE for this way of estimating lambda is 1.4

2) The mean error is small and decreases with m, so this estimator
appears to be unbiased.

One note: If the time between goals is exponential, the distribution
of goals scored in a game is Poisson.

See https://en.wikipedia.org/wiki/Poisson_distribution

"""

def Estimate1(n=7, m=100000):
    """Mean error for xbar and median as estimators of population mean.

    n: sample size
    m: number of iterations
    """
    mu = 0
    sigma = 1

    means = []
    medians = []
    for _ in range(m):
        xs = [random.gauss(mu, sigma) for i in range(n)]
        xbar = np.mean(xs)
        median = np.median(xs)
        means.append(xbar)
        medians.append(median)

    print('Experiment 1')
    print('mean error xbar', MeanError(means, mu))
    print('mean error median', MeanError(medians, mu))

def Estimate2(n=7, m=100000):
    """RMSE for biased and unbiased estimators of population variance.

    n: sample size
    m: number of iterations
    """
    mu = 0
    sigma = 1

    estimates1 = []
    estimates2 = []
    for _ in range(m):
        xs = [random.gauss(mu, sigma) for i in range(n)]
        biased = np.var(xs)
        unbiased = np.var(xs, ddof=1)
        estimates1.append(biased)
        estimates2.append(unbiased)

    print('Experiment 2')
    print('RMSE biased', RMSE(estimates1, sigma**2))
    print('RMSE unbiased', RMSE(estimates2, sigma**2))

def SimulateSample(lam=2, n=10, m=1000):
    """Sampling distribution of L as an estimator of exponential parameter.

    lam: parameter of an exponential distribution
    n: sample size
    m: number of iterations
    """
    def VertLine(x, y=1):
        thinkplot.Plot([x, x], [0, y], color='0.8', linewidth=3)

    estimates = []
    for j in range(m):
        xs = np.random.exponential(1/lam, n)
        lamhat = 1/np.mean(xs)
        estimates.append(lamhat)

    stderr = RMSE(estimates, lam)
    print('standard error', stderr)

    cdf = thinkstats2.Cdf(estimates)
    ci = cdf.Percentile(5), cdf.Percentile(95)
    print('confidence interval', ci)
    VertLine(ci[0])
    VertLine(ci[1])

    # plot the CDF
    thinkplot.Cdf(cdf)
    thinkplot.Save(root='estimation2',
                   xlabel='estimate',
                   ylabel='CDF',
                   title='Sampling distribution')

    return stderr

def SimulateGame(lam):
    """Simulates a game and returns the estimated goal-scoring rate.

    lam: actual goal scoring rate in goals per game
    """
    goals = 0
    t = 0
    while True:
        time_between_goals = random.expovariate(lam)
        t += time_between_goals
        if t > 1:
            break
        goals += 1

    # estimated goal-scoring rate is the actual number of goals scored
    L = goals
    return L

def Estimate4(lam=2, m=1000000):

    estimates = []
    for i in range(m):
        L = SimulateGame(lam)
        estimates.append(L)

    print('Experiment 4')
    print('rmse L', RMSE(estimates, lam))
    print('mean error L', MeanError(estimates, lam))

    pmf = thinkstats2.Pmf(estimates)

    thinkplot.Hist(pmf)
    thinkplot.Show()

def main():
    thinkstats2.RandomSeed(17)

    Estimate1()
    Estimate2()

    print('Experiment 3')
    for n in [10, 100, 1000]:
        stderr = SimulateSample(n=n)
        print(n, stderr)

    Estimate4()

if __name__ == '__main__':
    main()

DSC 530 - Data Exploration and Analysis/ThinkStats2/code/chap08soln.py


# -*- coding: utf-8 -*-
"""
Created on Tue Nov  9 07:52:36 2021

@author: Ezra
"""

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

def f(t,y):
    return y+np.cos(t)-t*t

def fa(t,y):
    return (1/2)*(4-np.exp(t)+4*t+2*t*t-np.cos(t)+np.sin(t))

def fp(t,y):
    return f(t,y)-np.sin(t)-2*t

def fdp(t,y):
    return fp(t,y)-np.cos(t)-2

def ftp(t,y):
    return fdp(t,y)+np.sin(t)

def fqp(t,y):
    return ftp(t,y)+np.cos(t)

def euler(n,a,b,alpha):
    '''
    Parameters
    ----------
    n : integer
        number of steps.
    a : float
        starting value for iVar.
    b : tuple
        ending value for iVar.
    alpha : float
        intial condition.

    Returns
    -------
    t : float
        time.
    w : float
        dVar.
    '''
    h=(b-a)/n
    t=a
    w=alpha
    for i in range(n):
        w=w+h*f(t,w)
        t=t+h
    return t,w

def taylor2(n,a,b,alpha):
    '''
    Parameters
    ----------
    n : integer
        number of steps.
    a : float
        starting value for iVar.
    b : tuple
        ending value for iVar.
    alpha : float
        intial condition.

    Returns
    -------
    t : float
        time.
    w : float
        dVar.
    '''
    h=(b-a)/n
    t=a
    w=alpha
    for i in range(n):
        w=w+h*f(t,w) + (h*h/2) * fp(t,w)
        t = t+h
    return t,w

def taylor4(n,a,b,alpha):
    '''
     See taylor 2
    '''
    h=(b-a)/n
    t=a
    w=alpha
    for i in range(n):
        w=w+h*f(t,w) + (h*h/2) * fp(t,w)+(h**3/6)*fdp(t,w)+(h**4/24)*ftp(t,w)
        t = t+h
    return t,w

def taylor6(n,a,b,alpha):
     '''
      See taylor 2
     '''
     h=(b-a)/n
     t=a
     w=alpha
     for i in range(n):
        w=w+h*f(t,w) + (h*h/2) * fp(t,w)+(h**3/6)*fdp(t,w)+(h**4/24)*ftp(t,w)+(h**5/120)*fqp(t,w)
        t = t+h
     return t,w

def err(x,y):
    '''
    Parameters
    ----------
    x : approximate solution
    y : exact solution

    Returns
    -------
    relative error between x and y
    '''
    return abs((y-x)/y)

yEval= 2
anaSoln = fa(yEval, 34.2343)
t,eulerSoln = euler(20,0,yEval,1)

n = np.array([10,20,50,100,200,1000])
eulerErr = []
t2Err = []
t4Err=[]
t6Err=[]

y1=np.linspace(0,4,20)
t,esol=euler(20,0,y1,1)
t,t2=taylor2(20,0,y1,1)
t,t4=taylor4(20,0,y1,1)
t,t6=taylor6(20,0,y1,1)
ana=fa(y1,34.2343)

plt.figure(0)
plt.grid(True)
plt.title("Plot of approximate solution of y=f(t,y)")
plt.xlabel("t")
plt.ylabel("y(t)")
plt.plot(y1,esol,label="euler")
plt.plot(y1,t2,label="taylor2")
plt.plot(y1,t4,label="taylor4")
plt.plot(y1,t6,label="taylor6")
plt.plot(y1,ana,label="analytic")
plt.legend()
plt.show()

for i in range(len(n)):
    t,eulerSoln = euler(n[i],0,yEval,1)
    eulerErr.append(err(eulerSoln,anaSoln))
    t,t2Soln = taylor2(n[i],0,yEval,1)
    t2Err.append(err(t2Soln,anaSoln))
    t,t4Soln = taylor4(n[i],0,yEval,1)
    t4Err.append(err(t4Soln,anaSoln))
    t,t6Soln = taylor6(n[i],0,yEval,1)
    t6Err.append(err(t6Soln,anaSoln))

error=pd.DataFrame({'eulerErr': eulerErr,'t2Err':t2Err,'t4Err':t4Err,
                        't6Err':t6Err})
file_name='techlab4error.xlsx'
error.to_excel(file_name)

print("Analytic Soln:", anaSoln, "Euler Soln:", eulerSoln, "Difference",
      anaSoln-eulerSoln, "taylor2:", t2Soln, "taylor4:", t4Soln)

plt.figure(1)
plt.grid(True)
plt.title("Plot of Relative Error at t=2")
plt.xlabel("n")
plt.ylabel("Relative Error")
plt.semilogy(n,eulerErr,label="euler")
plt.semilogy(n,t2Err,label="taylor2")
plt.semilogy(n,t4Err,label="taylor4")
plt.semilogy(n,t6Err,label="taylor6")
plt.legend()
plt.show()

print(eulerErr, "test")

'''
Solution to the Gompertz differential equation using eulers method
'''

z=.0439
K=12000.0
def G(t,N):
    return z*np.log(K/N)*N

# def Gp(t,N):
#     return z*K+z*np.log(K/N)*G(t,N)

def euler2(n,a,b,alpha):
    h=(b-a)/n
    t=a
    w=alpha
    for i in range(n):
        w=w+h*G(t,w)
        t=t+h
    return t, w

# def taylor22(n,a,b,alpha):
#     h=(b-a)/n
#     t=a
#     w=alpha
#     for i in range(n):
#         w=w+h*G(t,w) + (h*h/2) * Gp(t,w)
#         t = t+h
#     return t,w

x1=np.linspace(0, 100, 1000, endpoint=True)
t, eulerSoln = euler2(20,0,x1,4000)
# t,t23=taylor22(20,0,x1,4000)

#print(t, eulerSoln, "test")

plt.figure(2)
plt.grid(True)
plt.title("Gompertz solution plot")
plt.xlabel("t")
plt.ylabel("cells")
plt.plot(t,eulerSoln,label="euler")
# plt.plot(t,t23,label="taylor2")
plt.legend
plt.show()

def Iter(numIter,p0,tol):
    '''

    Parameters
    ----------
    numIter : integer
        how many cells in the tuple you want it to check.
    p0 : float
        value you are looking for.
    tol : float
        how close you need the value to be for it to return the cell.

    Returns
    -------
    p : float
        the value that it found within the tol.
    i : float
        the value of the cell.
    '''
    i=1
    while i <= numIter:
        p=eulerSoln[i]
        if abs(p-p0) < tol:
            return p,i
        i = i+1

p,m=Iter(1000,11000,10)

print(p,m)


# The dynamic programming algorithm for the knapsack 0-1 problem.
# The item values and weights are provided through input lists.
# The algorithm has pseudo-polynomial time complexity in the number of items: O(nC)

import numpy as np

def knapsack_0_1_dp(v, w, C):
	n = len(v)
	cmax = C
	# Subproblem solutions (two-dimensional array (n + 1)x(C + 1))
	opt = [[0] * (cmax + 1) for _ in range(n + 1)]
	# Base case (i = 0)
	for c in range(0, cmax + 1):
		opt[0][c] = 0
	# Systematically solve all subproblems
	for i in range(0, n):
		for c in range(0, cmax + 1):
			# Use recursion formula
			if w[i] > c:
				# Case 1
				opt[i][c] = opt[i - 1][c]
			else:
				# max(Case 1, Case 2)
				opt[i][c] = max(opt[i - 1][c], v[i] + opt[i - 1][c - w[i]])
	# Solution to largest subproblem
	return opt

def reconstruct(v, w, C, opt):
	n = len(v)
	# Remaining capacity
	cmax = C
	# Items included/excluded
	opt_sol = [None] * n
	# Trace back through the two-dimensional array
	i = n - 1
	while i >= 0:
		if (w[i] <= cmax) and (v[i] + opt[i - 1][cmax - w[i]] >= opt[i - 1][cmax]):
			# Case 2, include item i
			opt_sol[i] = True
			# Reserve space for included item
			cmax = cmax - w[i]
		else:
			# Exclude item i, capacity unchanged
			opt_sol[i] = False
		i = i - 1
	# Process optimal solution
	print_solution(opt_sol, opt[n - 1][C], v, w, C)

def print_solution(opt_sol, opt_val, v, w, C):
	n = len(opt_sol)
	k = 0
	while k < n and opt_sol[k] == 0:
		k = k + 1

	total_weight = 0
	if k < n:
		print ('(', k, ',', w[k], ',', v[k], ')', sep='', end='')
		total_weight = total_weight + w[k]
		for i in range(k + 1, n):
			if opt_sol[i] == 1:
				total_weight = total_weight + w[i]
				print (' + ', sep='', end='')
				print ('(', i, ',', w[i], ',', v[i], ')', sep='', end='')

	print(' => ', '(', total_weight, ',', opt_val, ')', sep='')

# List of item values
v = [7, 2, 10, 4]
# List of item weights
w = [3, 6, 9, 5]
# Knapsack capacity
C = 15
# opt = knapsack_0_1_dp(v, w, C)
# reconstruct(v, w, C, opt)
v, w = np.loadtxt("knapsack_dataset.txt", dtype=int, unpack=True)
opt = knapsack_0_1_dp(v[1:], w[1:], v[0])
reconstruct(v[1:], w[1:], v[0], opt)


# Copyright 2018 - Jonathan Alcantara e Osmar Fernandes

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np

tolerance = 0.00000001
iterations_limit = 10000

def multi_newton(x0_vector, f_vector, jacobian_vector):

    x_vector = x0_vector
    for iteration in range(iterations_limit):

        jacobian_current_values = jacobian_vector(x_vector)
        function_current_values = f_vector(x_vector)

        delta_x = -1*(np.linalg.inv(jacobian_current_values)
                      @ function_current_values)

        x_vector = x_vector + delta_x

        if(np.linalg.norm(delta_x)/np.linalg.norm(x_vector) \
            < tolerance):
            return x_vector

    return "Convergence not reached"

def f_vector(x_vector):
    return np.array([[x_vector[0][0] + 2*x_vector[1][0] - 2.0],
            [pow(x_vector[0][0], 2) + 4*pow(x_vector[1][0], 2) - 4]])

def jacobian_vector(x_vector):
    return np.array([[1, 2], [2*x_vector[0][0], 8*x_vector[1][0]]])

print(multi_newton(np.array([[2], [3]]), f_vector, jacobian_vector))


from sympy import *
import sys
sys.path.insert(1, '..')
from rodrigues_R_utils import *

x_src_l, y_src_l, z_src_l = symbols('x_src_l y_src_l z_src_l')
x_trg_g, y_trg_g, z_trg_g = symbols('x_trg_g y_trg_g z_trg_g')
x_trg_ln, y_trg_ln, z_trg_ln = symbols('x_trg_ln y_trg_ln z_trg_ln')
px, py, pz = symbols('px py pz')
sx, sy, sz = symbols('sx sy sz')

position_symbols = [px, py, pz]
rodrigues_symbols = [sx, sy, sz]
all_symbols = position_symbols + rodrigues_symbols

point_source_local = Matrix([x_src_l, y_src_l, z_src_l,1]).vec()
RT_wc = matrix44FromRodrigues(px, py, pz, sx, sy, sz)[:-1,:]
point_source_global = RT_wc * point_source_local
point_on_line_target_global = Matrix([x_trg_g, y_trg_g, z_trg_g])

a=point_source_global-point_on_line_target_global
b=Matrix([x_trg_ln, y_trg_ln, z_trg_ln]).vec()

p_proj = point_on_line_target_global + (a.dot(b)/b.dot(b))*b

delta = Matrix([0,0,0]).vec()-(point_source_global - p_proj)
delta_jacobian=delta.jacobian(all_symbols)

print(delta)
print(delta_jacobian)

with open("point_to_projection_onto_line_rodrigues_wc_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void point_to_projection_onto_line_rodrigues_wc(Eigen::Matrix<double, 3, 1> &delta, double px, double py, double pz, double sx, double sy, double sz, double x_src_l, double y_src_l, double z_src_l, double x_trg_g, double y_trg_g, double z_trg_g, double x_trg_ln, double y_trg_ln, double z_trg_ln)\n")
    f_cpp.write("{")
    f_cpp.write("delta.coeffRef(0,0) = %s;\n"%(ccode(delta[0,0])))
    f_cpp.write("delta.coeffRef(1,0) = %s;\n"%(ccode(delta[1,0])))
    f_cpp.write("delta.coeffRef(2,0) = %s;\n"%(ccode(delta[2,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void point_to_projection_onto_line_rodrigues_wc_jacobian(Eigen::Matrix<double, 3, 6> &j, double px, double py, double pz, double sx, double sy, double sz, double x_src_l, double y_src_l, double z_src_l, double x_trg_g, double y_trg_g, double z_trg_g, double x_trg_ln, double y_trg_ln, double z_trg_ln)\n")
    f_cpp.write("{")
    for i in range (3):
        for j in range (6):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(delta_jacobian[i,j])))
    f_cpp.write("}")


import numpy as np

def extendedEuclideanAlgorithm(x1, x2):
    # base case
    if x1 == 0:
        return x2, 0, 1
    # recursive call
    gcd, x_, y_ = extendedEuclideanAlgorithm(x2 % x1, x1)
    x = y_ - (x2 // x1) * x_
    y = x_
    return gcd, x, y

# modular multiplicative inverse
def inverse(n, p):
    if n == 0:
        raise ZeroDivisionError('Stahp dividing by zero you!!!')
    if n < 0:
        # k ** -1 = p - (-k) ** -1  (mod p)
        return p - inverse(-n, p)
    gcd, x, _ = extendedEuclideanAlgorithm(n, p)
    assert gcd == 1
    assert np.mod(n*x, p) == 1
    return np.mod(x, p)


# https://github.com/llSourcell/Second_Order_Optimization_Newtons_Method/blob/master/newtons_method_optimization.py

from sympy import *
from sympy.parsing import sympy_parser as spp

import numpy as np
import matplotlib.pyplot as plt

init_printing(use_unicode=True)

# Plot range
plot_from, plot_to, plot_step = -7.0, 7.0, 0.1
# Precision for iterative methods
target_precision = 0.3

m = Matrix(symbols('x1 x2'))

def dfdx(x, g):
    # Gradient in multi dimension
    return [float(g[i].subs(m[0], x[0]).subs(m[1], x[1])) for i in range(len(g))]

def sd(alpha=0.0002):
    """
    Steepest Descent - 1st order optimization
    :return:
    """
    print "STEEPEST DESCENT: start"
    # gradient
    g = [diff(obj, i) for i in m]
    # Initialize xs
    xs = [[0.0, 0.0]]
    xs[0] = x_start
    # Get gradient at start location (df/dx or grad(f))
    iter_s = 0
    while np.linalg.norm(xs[-1] - x_result) > target_precision:
        # print "STEEPEST DESCENT: distance:", np.linalg.norm(xs[-1] - x_result)
        gs = dfdx(xs[iter_s], g)
        # Compute search direction and magnitude (dx)
        #  with dx = - grad but no line searching
        xs.append(xs[iter_s] - np.dot(alpha, gs))
        # print xs[-1]
        iter_s += 1
        if iter_s > 10000:
            break
    print "STEEPEST DESCENT: result distance:", np.linalg.norm(xs[-1] - x_result)
    xs = np.array(xs)
    plt.plot(xs[:, 0], xs[:, 1], 'g-o')

def nm():
    """
    Newton's method - 2nd order optimization
    :return:
    """
    print "NEWTON METHOD: start"
    # gradient
    g = [diff(obj, i) for i in m]
    # Hessian matrix
    H = Matrix([[diff(g[j], m[i]) for i in range(len(m))] for j in range(len(g))])
    H_inv = H.inv()

    xn = [[0, 0]]  # Newton method result global for comparison
    xn[0] = x_start

    iter_n = 0
    while np.linalg.norm(xn[-1] - x_result) > target_precision:
        # print "NEWTON METHOD: distance:", np.linalg.norm(xn[-1] - x_result)
        gn = Matrix(dfdx(xn[iter_n], g))
        delta_xn = -H_inv * gn
        delta_xn = delta_xn.subs(m[0], xn[iter_n][0]).subs(m[1], xn[iter_n][1])
        xn.append(Matrix(xn[iter_n]) + delta_xn)
        iter_n += 1
    print "NEWTON METHOD: result distance:", np.linalg.norm(xn[-1] - x_result)

    xn = np.array(xn)
    plt.plot(xn[:, 0], xn[:, 1], 'k-o')

if __name__ == '__main__':

    ####################
    # Quadratic function
    ####################
    # Start location
    x_start = [-4.0, 6.0]

    # obj = spp.parse_expr('x1**2 - x2 * x1 - x1 + 4 * x2**2')
    # x_result = np.array([16/15, 2/15])
    obj = spp.parse_expr('x1**2 - 2 * x1 * x2 + 4 * x2**2')
    x_result = np.array([0, 0])

    # Design variables at mesh points
    i1 = np.arange(plot_from, plot_to, plot_step)
    i2 = np.arange(plot_from, plot_to, plot_step)
    x1_mesh, x2_mesh = np.meshgrid(i1, i2)
    f_str = obj.__str__().replace('x1', 'x1_mesh').replace('x2', 'x2_mesh')
    f_mesh = eval(f_str)

    # Create a contour plot
    plt.figure()

    plt.imshow(f_mesh, cmap='Paired', origin='lower',
               extent=[plot_from - 20, plot_to + 20, plot_from - 20, plot_to + 20])
    plt.colorbar()

    # Add some text to the plot
    plt.title('f(x) = ' + str(obj))
    plt.xlabel('x1')
    plt.ylabel('x2')
    nm()
    sd(alpha=0.05)
    plt.show()

    #####################
    # Rosenbrock function
    #####################
    # Start location
    x_start = [-4.0, -5.0]

    obj = spp.parse_expr('(1 - x1)**2 + 100 * (x2 - x1**2)**2')
    x_result = np.array([1, 1])

    # Design variables at mesh points
    i1 = np.arange(plot_from, plot_to, plot_step)
    i2 = np.arange(plot_from, plot_to, plot_step)
    x1_mesh, x2_mesh = np.meshgrid(i1, i2)
    f_str = obj.__str__().replace('x1', 'x1_mesh').replace('x2', 'x2_mesh')
    f_mesh = eval(f_str)

    # Create a contour plot
    plt.figure()

    plt.imshow(f_mesh, cmap='Paired', origin='lower',
               extent=[plot_from - 20, plot_to + 20, plot_from - 20, plot_to + 20])
    plt.colorbar()

    # Add some text to the plot
    plt.title('f(x) = ' + str(obj))
    plt.xlabel('x1')
    plt.ylabel('x2')
    nm()
    sd(alpha=0.0002)
    plt.show()

    # import timeit
    # print(timeit.timeit("nm()", setup="from __main__ import nm", number=10))
    # print(timeit.timeit("sd()", setup="from __main__ import sd", number=10))


# -*- coding: utf-8 -*-
"""
Created on Wed Jun 16 12:56:17 2021

@author: Luigi
"""
import numpy as np
import scipy as sci
import sympy as sym
import matplotlib.pyplot as plt

def Lagrange(xnodi, i):
    if i == 0:
        xzeri = xnodi[1:]
    else:
        xzeri = np.append(xnodi[:i], xnodi[i + 1 :])
    num = np.poly(xzeri)
    den = np.polyval(num, xnodi[i])
    return num / den

def Interpol(x, y, xx):
    m = x.size
    n = xx.size
    L = np.zeros((m,n))
    for k in range(m):
        L[k, :] = np.polyval(Lagrange(x, k), xx)
    return np.dot(y, L)

def simpsonComp(f, a, b, n):
    h = (b - a) / (2 * n)
    interv = np.arange(a, b + h, h)
    fnodi = f(interv)
    I = h * (fnodi[0] + 2 * np.sum(fnodi[2 : 2*n : 2]) + 4 * np.sum(fnodi[1 : 2*n : 2]) + fnodi[2*n]) /3
    return I

def simpsonTol(f, a, b, tol):
    N = 1
    nMax = 2048
    err = 1
    In = simpsonComp(f, a, b, N)
    while err >= tol and N < nMax:
        N *= 2
        I2n = simpsonComp(f, a, b, N)
        err = np.abs(I2n - In) / 15
        In = I2n
    return In, N

f = lambda x : x - np.sqrt(x - 1)
a = 1
b = 3
x = np.linspace(a, b, 4)
xx = np.linspace(a, b, 100)
y = f(x)
yy = Interpol(x, y, xx)

plt.plot(xx, yy, xx, f(xx), x, y, "o")
plt.legend(["Polinomio di grado 3", "Funzione", "Nodi di interpolazione"])
plt.show()

n = 4
p = lambda nodi : Interpol(x, y, nodi)
I1, N1 = simpsonTol(f, a, b, 10**-5)
print(f"Sono necessare {N1} iterazioni per I1")
I2, N2 = simpsonTol(p, a, b, 10**-5)
print(f"Sono necessare {N2} iterazioni per I2")
I1es = 2.114381916835873
I2es = 2.168048769926493
err1 = abs(I1es - I1)
err2 = abs(I2es - I2)
print("ErrRel I1 = ", err1)
print("ErrRel I2 = ", err2)


import numpy as np
import matplotlib.pyplot as plt
import sympy as sp

def func(exp):
	"""
	Function to convert the expression to the Pythonic format to make mathematical calculations.

	Parameters:
	exp:	inputted expression by the user to be lambdified
	"""

	x = sp.symbols('x')
	return sp.utilities.lambdify(x, exp, "math")

def bisection(exp, a, b, tol, points, count):
	"""
	Function to calculate the root of the expression through the bisection method.

	Parameters:
	exp:	inputted expression by the user whose root needs to be found
	a:		lower limit of the range
	b:		upper limit of the range
	tol:	maximum permissible error between the calculated root and the true solution
	points:	a list to save the roots calculated through the iterations
	count:	it ensures that checking for the permissible error of the root cannot be done in the first iteration which has only one root
	"""

	function = func(exp)

	# Checking to ensure the range given by the user is correct and a root to the expression lies between the range
	if function(a) * function(b) > 0:

		print(f"Invalid range of {a} and {b}. Ensure range is between a root by checking if both signs are different.")
		return

	c = (a + b) / 2
	points.append(c)

	if count > 1:

		if abs(points[-1] - points[-2]) < tol:

			return points

	if function(a) * function(c) < 0:

		count += 1
		return bisection(exp, a, c, tol, points, count)

	elif function(b) * function(c) < 0:

		count += 1
		return bisection(exp, b, c, tol, points, count)

def plot_func(exp, array, a, b):
	"""
	A function to plot the expression and all the calculated roots, while highlighting the final correct root

	Parameters:
	exp:	inputted expression by the user which needs to be plotted
	a:		lower limit of the range
	b:		upper limit of the range
	array:	array of roots to be plotted
	"""

	function = func(exp)

	# Plotting the function by creating an array of Xs and Ys
	x = np.linspace(a, b, 20)
	y = []

	for i in x:
		y.append(function(i))

	# An array of zeros the same length as the number of roots in arrays, so as to plot the roots
	array_y = np.zeros(len(array))

	fig = plt.figure(figsize=(8,7))

	ax1 = fig.add_axes([0.05, 0.05, 0.9, 0.9])

	ax1.plot(x, y, label="Function: %s"% exp)
	ax1.axhline(0, color='red', ls='--', alpha=0.5)
	ax1.scatter(array[:-1], array_y[:-1], color='black', s=10, alpha=0.8, edgecolor='black', label="Roots")
	ax1.scatter(array[-1], 0, color="green", s=15, label="Final Root: %s"% str(round(array[-1], 3)))

	ax1.set_title("Finding Roots: Bisection Method")
	ax1.legend()

	plt.show()

roots = []
count = 1

expr = input("Enter a continuous function in x: ")
a, b = map(int, input("Enter the range with a space in between the two numbers: ").split())
error = float(input("Enter the max allowable tolerance: "))

# Example input values:

# expr = "x^3 - x - 2"
# a, b = 1, 2
# error = 0.001

final = bisection(expr, a, b, error, roots, count)

plot_func(expr, final, a, b)

print("The root by bisection method: ", round(final[-1], 3))


import numpy as np
import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')
import texttable as tt
tab = tt.Texttable()
def euler(f,a,b,n,yinit):
    h=(b-a)/(n)
    xs = a+np.arange(n)*h
    ys=np.zeros(n)
    y = yinit
    for j,x in enumerate(xs):
        ys[j]=y
        y+=h*f(x,y)
    return xs, ys
def rk2(f,a,b,n,yinit):
    h=(b-a)/(n)
    xs = a+np.arange(n)*h
    ys=np.zeros(n)
    y = yinit
    for j,x in enumerate(xs):
        ys[j]=y
        k0 = h*f(x,y)
        y+=h*f(x+h/2,y+k0/2)
    return xs, ys
def rk4(f,a,b,n,yinit):
    h=(b-a)/(n)
    xs = a+np.arange(n)*h
    ys=np.zeros(n)
    y = yinit
    for j,x in enumerate(xs):
        ys[j]=y
        k0 = h*f(x,y)
        k1 = h*f(x+h/2,y+k0/2)
        k2 = h*f(x+h/2,y+k1/2)
        k3 = h*f(x+h,y+k2)
        y+=(k0+2*k1+2*k2+k3)/6
    return xs, ys
def Analytic(yinit,a,b,h,tau):
    xs,ys=[],[]
    p=np.arange(a,b,h)
    for t in p:
        D=yinit*np.exp(-1*t/tau)
        ys.append(D)
        xs.append(t)
    return xs,ys
def graph(xs_1,ys_1,xs_2,ys_2,xs_3,ys_3,xs_4,ys_4,xs_5,ys_5,xs_6,ys_6,xs_7,ys_7,xs_8,ys_8,xs_9,ys_9,xs_10,ys_10,title):
    fig,axs=plt.subplots(3,2,figsize=(15,15))
    fig.suptitle(title, fontsize=30)
    ax11,ax12,ax21,ax22,ax31,ax32=axs[0][0],axs[0][1],axs[1][0],axs[1][1],axs[2][0],axs[2][1]
    ax11.plot(xs_1,ys_1,'^', color='green',label="euler"),ax11.plot(xs_4,ys_4,'-', color='black',label="rk2")
    ax11.plot(xs_7,ys_7,'>',color='black',label="rk4"),ax11.plot(xs_7,ys_7,'*',color='brown',label="Analytic")
    ax11.set_title("Analytic v/s Euler v/s rk2 v/s rk4"),ax11.set_ylabel("N"),ax11.set_xlabel("Time")
    ax12.plot(xs_1,ys_1,'^', color='green',label="h ={}".format(xs_1[1]-xs_1[0])),ax12.plot(xs_2,ys_2,'-', color='black',label="h ={}".format(xs_2[1]-xs_2[0]))
    ax12.plot(xs_3,ys_3,'>',color='black',label="h ={}".format(xs_3[1]-xs_3[0])),ax12.plot(xs_7,ys_7,'*',color='brown',label="Analytic")
    ax12.set_title("Euler for different stepsize(h)"),ax12.set_ylabel("N"),ax12.set_xlabel("Time")
    ax21.plot(xs_4,ys_4,'^', color='green',label="h ={}".format(xs_4[1]-xs_4[0])),ax21.plot(xs_5,ys_5,'-', color='black',label="h ={}".format(xs_5[1]-xs_5[0]))
    ax21.plot(xs_6,ys_6,'>',color='black',label="h ={}".format(xs_6[1]-xs_6[0])),ax21.plot(xs_7,ys_7,'*',color='brown',label="Analytic")
    ax21.set_title("rk2 for different stepsize(h)"),ax21.set_ylabel("N"),ax21.set_xlabel("Time")
    ax22.plot(xs_7,ys_7,'^', color='green',label="h ={}".format(xs_7[1]-xs_7[0])),ax22.plot(xs_8,ys_8,'-', color='black',label="h ={}".format(xs_8[1]-xs_8[0]))
    ax22.plot(xs_9,ys_9,'>',color='black',label="h ={}".format(xs_9[1]-xs_9[0])),ax22.plot(xs_10,ys_10,'*',color='brown',label="Analytic")
    ax22.set_title("rk4 for different stepsize(h)"),ax22.set_ylabel("N"),ax22.set_xlabel("Time")
    ax31.plot(xs_1,(ys_10-ys_1)/ys_10,'.', color='green',label="euler"),ax31.plot(xs_1,(ys_10-ys_4)/ys_10,'.', color='black',label="rk2")
    ax31.plot(xs_1,(ys_10-ys_7)/ys_10,'.', color='red',label="rk4")
    ax31.set_title("Error Plot at h = 0.4"),ax31.set_ylabel("Absolute Error"),ax31.set_xlabel("Time")
    ax32.plot(xs_1,(ys_1-ys_4)/ys_1,'.', color='green',label="euler-rk2"),ax32.plot(xs_1,(ys_7-ys_4)/ys_7,'.', color='black',label="rk4-rk2")
    ax32.plot(xs_1,(ys_1-ys_7)/ys_1,'.', color='red',label="euler-rk4")
    ax32.set_title("Comparative Error Plot at h = 0.4"),ax32.set_ylabel("Absolute Error"),ax32.set_xlabel("Time")
    ax11.legend(),ax11.grid(True),ax12.legend(),ax12.grid(True),ax21.legend(),ax21.grid(True),ax22.legend(),ax22.grid(True)
    ax31.legend(),ax31.grid(True),ax32.legend(),ax32.grid(True)
    plt.show()
def q3_a(a,yinit,t_half):
    b = 5*t_half
    tau=t_half/np.log(2)
    h = t_half/10
    n = int((b-a)/h)
    decay = lambda x, y: -1*y/tau
    xs_1, ys_1 = euler(decay,a,b,n,yinit)
    xs_2, ys_2 = euler(decay,a,b,2*n,yinit)
    xs_3, ys_3 = euler(decay,a,b,4*n,yinit)
    xs_4, ys_4 = rk2(decay,a,b,n,yinit)
    xs_5, ys_5 = rk2(decay,a,b,2*n,yinit)
    xs_6, ys_6 = rk2(decay,a,b,4*n,yinit)
    xs_7, ys_7 = rk4(decay,a,b,n,yinit)
    xs_8, ys_8 = rk4(decay,a,b,2*n,yinit)
    xs_9, ys_9 = rk4(decay,a,b,4*n,yinit)
    xs_10, ys_10 = Analytic(yinit,a,b,h,tau)
    print("Radioactive Decay", "h =", h)
    headings_1 = ["t" ,"Analytic","euler","rk2","rk4","Ab_error euler","Ab_error rk2","Ab_error rk4"]
    tab.header(headings_1)
    for row in zip(xs_1,ys_10,ys_1,ys_4,ys_7,(ys_10-ys_1)/ys_10,(ys_10-ys_4)/ys_10,(ys_10-ys_7)/ys_10):
        tab.add_row(row)
        tab.set_max_width(0)
        tab.set_precision(6)
    s = tab.draw()
    print(s)
    tab.reset()

    graph(xs_1,ys_1,xs_2,ys_2,xs_3,ys_3,xs_4,ys_4,xs_5,ys_5,xs_6,ys_6,xs_7,ys_7,xs_8,ys_8,xs_9,ys_9,xs_10,ys_10,"Radioactive Decay")

def q3_b(a,yinit,R,C):
    b = 5*R*C
    tau=R*C
    h = tau/10
    n = int((b-a)/h)
    rc = lambda x, y: -1*y/tau
    xs_1, ys_1 = euler(rc,a,b,n,yinit)
    xs_2, ys_2 = euler(rc,a,b,2*n,yinit)
    xs_3, ys_3 = euler(rc,a,b,4*n,yinit)

    xs_4, ys_4 = rk2(rc,a,b,n,yinit)
    xs_5, ys_5 = rk2(rc,a,b,2*n,yinit)
    xs_6, ys_6 = rk2(rc,a,b,4*n,yinit)

    xs_7, ys_7 = rk4(rc,a,b,n,yinit)
    xs_8, ys_8 = rk4(rc,a,b,2*n,yinit)
    xs_9, ys_9 = rk4(rc,a,b,4*n,yinit)
    xs_10, ys_10 = Analytic(yinit,a,b,h,tau)
    print("RC Circuit", "h =", h)
    headings_1 = ["t" ,"Analytic","euler","rk2","rk4","$\delta$","Ab_error rk2","Ab_error rk4"]
    tab.header(headings_1)
    for row in zip(xs_1,ys_10,ys_1,ys_4,ys_7,(ys_10-ys_1)/ys_10,(ys_10-ys_4)/ys_10,(ys_10-ys_7)/ys_10):
        tab.add_row(row)
        tab.set_max_width(0)
        tab.set_precision(6)
    s = tab.draw()
    print(s)
    tab.reset()

    graph(xs_1,ys_1,xs_2,ys_2,xs_3,ys_3,xs_4,ys_4,xs_5,ys_5,xs_6,ys_6,xs_7,ys_7,xs_8,ys_8,xs_9,ys_9,xs_10,ys_10,"RC Circuit")

def q3_c(a,yinit,eta,rad,m):
    tau=m/((np.pi)*6*rad*eta)
    b = 5*tau
    h = tau/10
    n = int((b-a)/h)
    stokes = lambda x, y: -1*y/tau
    xs_1, ys_1 = euler(stokes,a,b,n,yinit)
    xs_2, ys_2 = euler(stokes,a,b,2*n,yinit)
    xs_3, ys_3 = euler(stokes,a,b,4*n,yinit)
    xs_4, ys_4 = rk2(stokes,a,b,n,yinit)
    xs_5, ys_5 = rk2(stokes,a,b,2*n,yinit)
    xs_6, ys_6 = rk2(stokes,a,b,4*n,yinit)
    xs_7, ys_7 = rk4(stokes,a,b,n,yinit)
    xs_8, ys_8 = rk4(stokes,a,b,2*n,yinit)
    xs_9, ys_9 = rk4(stokes,a,b,4*n,yinit)
    xs_10, ys_10 = Analytic(yinit,a,b,h,tau)
    xs_10.pop(int(xs_10[-1]))
    ys_10.pop(int(ys_10[-1]))
    print("Stokes Law", "h =", h)
    headings_1 = ["t" ,"Analytic","euler","rk2","rk4","Ab_error euler","Ab_error rk2","Ab_error rk4"]
    tab.header(headings_1)
    for row in zip(xs_1,ys_10,ys_1,ys_4,ys_7,(ys_10-ys_1)/ys_10,(ys_10-ys_4)/ys_10,(ys_10-ys_7)/ys_10):
        tab.add_row(row)
        tab.set_max_width(0)
        tab.set_precision(6)
    s = tab.draw()
    print(s)
    tab.reset()
    graph(xs_1,ys_1,xs_2,ys_2,xs_3,ys_3,xs_4,ys_4,xs_5,ys_5,xs_6,ys_6,xs_7,ys_7,xs_8,ys_8,xs_9,ys_9,xs_10,ys_10,"Stokes Law")
if __name__ == "__main__":
    q3_a(0,20000,4)
    q3_b(0,10,1e3,1e-6)
    q3_c(0,10,10,0.2,200)


import scipy as sp
import numpy as np
#From https://www.stat.auckland.ac.nz/~fewster/325/notes/ch9.pdf
#Python soln = https://stackoverflow.com/questions/33385763/find-markov-steady-state-with-left-eigenvalues-using-numpy-or-scipy
#Starting values for t=0; any state equally likely.
start = np.array([[1/4,1/4,1/4,1/4]])
print(start)
#Specify the matrix, P
P = np.array([[0,.9,.1,0],[.8,.1,0,.1],[0,.5,.3,.2],[.1,0,0,.9]])
#We want to find the left matrix pi which produces pi.P = pi.
#By definition, this is the stationary matrix.
#Calculate the left eigenvector, which is the solution to the problem pi(P - I) = 0.
eigenvalue, eigenvector = sp.sparse.linalg.eigs(P.T, k=1,which ='LM')
print(eigenvalue)
print('Un-normalised eigenvector: ',eigenvector)
evect_norm = (eigenvector/eigenvector.sum()).real
print('Normalised eigenvector: ',evect_norm)
print('Check that pi*P = pi: ',np.dot(evect_norm.T,P).T.real)
print('Estimate using P^n: ',np.linalg.matrix_power(P,100)[0,:])


"""
Problem
=======
Given a sorted array, return the index of the given key. Return -1 if
the given key is not found in the given array.

"""

import numpy as np

# Time: O(log n)
# Space: O(1)
def binary_search(a, key):
    lo, hi = 0, len(a)
    while lo < hi:
        mi = (lo + hi) // 2
        if a[mi] < key:
            lo = mi + 1
        elif a[mi] > key:
            hi = mi
        else:
            return mi
    return -1

# Implementation from Standard library:
# bisect.bisect_left(a, x, lo=0, hi=len(a))
# bisect.bisect_right(a, x, lo=0, hi=len(a))
# bisect.bisect(a, x, lo=0, hi=len(a)) # same as bisect_right

## Testing ##

def generate_random_sorted_array(maxlen=10, maxent=20):
    n = np.random.randint(0, maxlen + 1)
    arr = np.random.randint(1, maxent, n)
    return np.sort(arr)

def random_tests(seed=None, maxlen=10, maxent=20):
    np.random.seed(seed)
    n = 0
    try:
        while True:
            flag = False
            arr = generate_random_sorted_array(maxlen, maxent)
            for key in range(0, maxent + 1):
                n += 1
                i = binary_search(arr, key)
                if ((-1 < i < len(arr) and arr[i] != key) or (i == -1 and key in arr)):
                    print(f'Test #{n}')
                    print('arr: ', arr)
                    print('key: ', key)
                    print('i: ', i)
                    flag = True
                    break
            if flag:
                break
    except KeyboardInterrupt:
        print(f'\nPassed {n} tests.')

if __name__ == '__main__':
    print('Performing random tests...')
    print('Press ^C to stop.')
    random_tests(seed=42, maxlen=100, maxent=200)


#Author: marejak023
#Contact: marejak023@gmail.com, marejak023.wz.cz
#Date: 06/02/2021

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

#code for computing Leibniz series
k = 1 # denominator
s = 0 # sum

# x and y array for storing plot values
y = np.array([])
x = np.array([])
PI_CONST_LINE = np.array([]) # pi contstant for y reference value (set for 15f points from math package)

sum_range = int(input("\nEnter a range: ")) # range for the series

for i in range(sum_range):
    # even numbers have + sign
    if i % 2 == 0:
        s += 4/k
    else: # changes + to - sign
        s -= 4/k
    k += 2
    y = np.append(y, s) # adds current value of sum of the series to the y array
    PI_CONST_LINE = np.append(PI_CONST_LINE, math.pi) # append math.pi values to PI_CONST_LINE array, so pi y reference value is for every n (sum_range) = math.pi

# stores value in x array (sum_range = n [number of steps])
for i in range(sum_range):
    x = np.append(x, i)

error = ((s-math.pi)/math.pi)*100 # Percentage value of error

# printing out computed values
print("\nApproximate value of pi using Liebniz series is: ", "{:.15f}".format(s))
print("Real value using math python constant math.pi is: ", math.pi)
print("Value of error in % is: ", abs(error), "%")
print("\nApproximate value is limited to 15 float digits, so it matches the internal float value of math.pi")

# making plot
# titles & labels
plt.figure().canvas.set_window_title("Pi approximation using Leibniz series")
plt.title(r"$\pi$ approximation using Leibniz series $\frac{\pi}{4}=\sum_{n=0}^{\infty}\frac{(-1)^n}{2n+1}$", y = 1.05) # y is for title positioning on y axis
plt.xlabel(r"n", fontsize = 20) # r is for using mathmode ($$ for entering mathmode, same as LaTeX notation)
plt.ylabel(r"$S_n$", fontsize = 20)

plt.plot(x, y, marker = 'X', mec = "#0085E7", mfc = "#FFF", c = "#0085E7", ls = '--') # plots Leibniz series, mec = marker edge color, mfc = marker face color c = linecolor, ls = linestyle
plt.plot(x, PI_CONST_LINE, ls = '-.', c = "#000") # plots constant line with y value = pi (from math.pi, 15f points) & x value = n (sum_range)
plt.grid(linestyle = '--')
plt.show() # display plot


import numpy as np

def linear_solve(A, b):
  AAT = A@(A.T)
  # w = np.linalg.solve(AAT, b)
  w = np.linalg.lstsq(AAT, b)[0]
  x = (A.T)@w
  return x

class PolyModel:
  '''
  H(n) : h(x): R-> R, h(x)= a_0 + a_1*x + a_2*x^2 + ... + a_{n-1}*x^n-1
  '''

  def __init__(self, n):
      self.n = n
      self.a = None

  def generate_features(self, X):
      X = X.flatten()
      arrays = [X**i for i in range(self.n)]
      A = np.stack(arrays, axis = 1)
      return A

  def fit(self, X, Y, refit=False):
      '''
      X: (d,1)
      Y: (d,1)
      '''
      if self.a and (not refit):
         raise ValueError("Re-Fitting")
      A = self.generate_features(X)
      # self.a = linear_solve(A, Y)
      self.a = np.linalg.lstsq(A, Y)[0]
      return self

  def predict(self, X, A=None):
      if (not A):
        A = self.generate_features(X)
      return A@self.a

  def score(self, X, y):
      '''
        RMSE
      '''
      y_pred = self.predict(X=X)
      return np.linalg.norm(y-y_pred, ord=2)/np.sqrt(X.shape[0])


## 1. Vectors ##

vector1 = np.asarray([4, 5, 7, 10])
vector2 = np.asarray([8, 6, 3, 2])
vector3 = np.asarray([10, 4, 6, -1])
vector1_2 = vector1 + vector2
vector3_1 = vector3 + vector1

## 2. Vectors and scalars ##

vector = np.asarray([4, -1, 7])
vector_7 = vector * 7
vector_8 = vector / 8

## 4. Plotting vectors ##

import numpy as np
import matplotlib.pyplot as plt

# We're going to plot 2 vectors
# The first will start at origin 0,0 , then go over 1 and up 2.
# The second will start at origin 1,2 then go over 3 and up 2.
X = [0,1]
Y = [0,2]
U = [1,3]
V = [2,2]
# Actually make the plot.
plt.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1)
# Set the x axis limits
plt.xlim([0,6])
# Set the y axis limits
plt.ylim([0,6])
# Show the plot.
plt.show()
plt.quiver([0,1,0], [0,2,0], [1,3,4], [2,2,4], angles='xy', scale_units='xy', scale=1)
plt.xlim([0,6])
plt.ylim([0,6])
plt.show()

## 5. Vector length ##

# We're going to plot 3 vectors
# The first will start at origin 0,0 , then go over 2 (this represents the bottom of the triangle)
# The second will start at origin 2,2, and go up 3 (this is the right side of the triangle)
# The third will start at origin 0,0, and go over 2 and up 3 (this is our vector, and is the hypotenuse of the triangle)
X = [0,2,0]
Y = [0,0,0]
U = [2,0,2]
V = [0,3,3]
# Actually make the plot.
plt.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1)
plt.xlim([0,6])
plt.ylim([0,6])
plt.show()
vector_length = (4 + 9) ** .5

## 6. Dot product ##

# These two vectors are orthogonal
X = [0,0]
Y = [0,0]
U = [1,-1]
V = [1,1]
plt.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1)
plt.xlim([-2,2])
plt.ylim([-2,2])
plt.show()
dot = 3 * 5 + 4 * 6 + 5 * 7 + 6 * 8

## 7. Making predictions ##

# Slope and intercept are defined, and nba is loaded in
predictions = slope * nba["fga"] + intercept

## 9. Multiplying a matrix by a vector ##

import numpy as np
# Set up the coefficients as a column vector
coefs = np.asarray([[3], [-1]])
# Setup the rows we're using to make predictions
rows = np.asarray([[2,1], [5,1], [-1,1]])

# We can use np.dot to do matrix multiplication.  This multiplies rows by coefficients -- the order is important.
np.dot(rows, coefs)

nba_coefs = np.asarray([[slope], [intercept]])
nba_rows = np.vstack([nba["fga"], np.ones(nba.shape[0])]).T
predictions = np.dot(nba_rows, nba_coefs)

## 11. Applying matrix multiplication ##

A = np.asarray([[5,2], [3,5], [6,5]])
B = np.asarray([[3,1], [4,2]])
C = np.dot(A, B)

Linear Algebra/Working with vectors-80.py


import numpy as np

ITERATION_LIMIT = 1000

# initialize the matrix
A = np.array([[10., -1., 2., 0.],
              [-1., 11., -1., 3.],
              [2., -1., 10., -1.],
              [0., 3., -1., 8.]])
# initialize the RHS vector
b = np.array([6., 25., -11., 15.])

print("System of equations:")
for i in range(A.shape[0]):
    row = ["{0:3g}*x{1}".format(A[i, j], j + 1) for j in range(A.shape[1])]
    print("[{0}] = [{1:3g}]".format(" + ".join(row), b[i]))

x = np.zeros_like(b)
for it_count in range(1, ITERATION_LIMIT):
    x_new = np.zeros_like(x)
    print("Iteration {0}: {1}".format(it_count, x))
    for i in range(A.shape[0]):
        s1 = np.dot(A[i, :i], x_new[:i])
        s2 = np.dot(A[i, i + 1:], x[i + 1:])
        x_new[i] = (b[i] - s1 - s2) / A[i, i]
    if np.allclose(x, x_new, rtol=1e-8):
        break
    x = x_new

print("Solution: {0}".format(x))
error = np.dot(A, x) - b
print("Error: {0}".format(error))


"""
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from matplotlib import cm
import scipy as sp
from scipy.sparse import csr_matrix, linalg
from scipy.stats import linregress
from scipy import linalg, optimize

################### Problema 1.4
## Parte 1
# Escriba dos funciones que calculen A_h y b_h. La entrada debe ser N,f,g

def calcula_A(N):
    h = 1/ (N-1)
    down = np.ones(N-2)
    center = np.ones(N-1)
    upper = np.ones(N-2)
    d1 = -down
    d2 = 4*center
    d3 = -1*upper
    d = np.array([d1, d2, d3])
    offset = [-1, 0, 1]
    L4 = sp.sparse.diags(d, offset)

    dd1 = -down
    dd2 = np.zeros(N-1)
    dd3 = -upper
    dd = np.array([dd1, dd2, dd3])
    A1 = sp.sparse.diags(dd, offset)

    I = np.identity(N-1)

    L = sp.sparse.kron(A1, I)
    R = sp.sparse.kron(I, L4)

    A = (L + R) / h**2
    return A

def g(x,y):
    if y==1 and x<1 and x>0:
        return np.sin(2*np.pi*x)
    else:
        return 0

f = lambda x,y: 8*(np.pi**2)*np.sin(2*np.pi*x)*np.sin(2*np.pi*y)

def calcula_b(N, f, g):
    f_h=np.zeros(N**2)
    g_h=np.zeros(N**2)
    x=np.linspace(0,1,num=N)
    y=np.linspace(0,1,num=N)

    for j in range(len(x)):
        for k in range(len(y)):
            f_h[(k-1)*(N-1)+j]=f(x[j], y[k])

    g_h[1]=N**2*(g(x[1],0)+g(0,y[1]))
    g_h[N-1]=N**2*(g(x[N-1],0)+g(1,y[1]))
    g_h[(N-1)**2-(N-2)]=N**2*(g(x[1],1)+g(0,y[N-1]))
    g_h[(N-1)**2]=N**2*(g(x[N-1],1)+g(1,y[N-1]))
    for j in range(2,N-1):#esto es para j in {2,...,N-2}
        g_h[j]=N**2*g(x[j],0)
        g_h[(N-1)*(N-2)+j]=N**2*g(x[j],1)
        g_h[j*(N-1)+1]=N**2*(g(0,y[j]))
        g_h[j*(N-1)]=N**2*g(1,y[j])

    b_h=f_h+g_h
    return b_h

## Parte 2
# Para N en {4,16}, grafique la solución numérica y la solución única de
# la ecuación.

N = [4, 16]

for i in range(0,len(N)):
    u = sp.sparse.linalg.spsolve(calcula_A(N[i]), calcula_b(N[i]-1, f, g))
    U = np.zeros((N[i], N[i]))
    counter = 0
    for j in range(N[i]-1):
        for k in range(N[i]-1):
            U[k][j] = u[k + j*(N[i]-1)]

    x = np.linspace(0,1, N[i])

    X, Y = np.meshgrid(x, x)
    fig = plt.figure(i)
    fig.clf()
    ax = fig.add_subplot(111, projection='3d', elev=30, azim=10)
    ax.plot_surface(X, Y, np.transpose(U)) #, rstride=2, cstride=2, cmap=cm.plasma
    #ax.dist = 1
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('u')
    fig.show()

'''
# Parte 3
#
# para N en {4,8,16,32,64} =, calcule el error en norma L2.

arreglo_N = [ 2**(i) for i in range(2,7)]

# Grafique los respectivos valores en función de h, en escala logarítmica
# usando log log ¿qué puede observar?

""" arreglo_N = [ 2**(i) for i in range(2,7)]
arreglo_condicion_A_h = []

for N in arreglo_N:
    # cambiar por matriz A_h
    A_h = np.identity( (N-1)**2 )
    condicion_A_h = np.linalg.cond(A_h, p = 2)
    arreglo_condicion_A_h.append(condicion_A_h)

arreglo_h   = np.divide(1,arreglo_N)
arreglo_N_2 = np.multiply(arreglo_N,arreglo_N)

condicion_fig,condicion_ax = plt.subplots(2)
condicion_h  = condicion_ax[0]
condicion_n2 = condicion_ax[1]

condicion_h.loglog(arreglo_h,arreglo_condicion_A_h)
condicion_n2.loglog(arreglo_N_2,arreglo_condicion_A_h)

plt.show() """

# Calcule el orden de error experimental, es decir, estime mediante
# regresión lineal el valor de p tal que e_h sea de orden O(h^p)

# Parte 4
#
# recuerdo, A_h es matriz de (N-1)^2 x (N-1)^2
#
# Para N = {4,8,16,32,64}, **calcule el número de condición de A_h**
# en la nroma 2. Compare esto h y N^2.
# indicación, sea 1<=p <= infinityo. A matriz cuadrada de n por n.
# se define la norma inducida p de A como
# ||A||_p = sup |A_x|_p / |x|_p

# def: si A es invertible, el número de condición en norma inducida p como
# cond_p(A) = ||A||_p ||A^-1||_P
# hint: para las observaciones : usar el teorema que aparece en la pregunta
#
# en python, numpy.linalg.cond
'''

################ Problema 2.4
'''
contexto: Ecuación de calor en estado no estacionario

d_dt u - d2_dt2 u = 0 ;   (t,x) en (0,infinito)x(0,1)

u(t,x+1) = u(t,x)     ;

u(0,x)   = u_(x)

N_T entero, un paso tempora dt > 0,
N   entero, paso espacial   dx = 1/N

x_j = j dx, j en {0,...,N}
t_n = n dt, n en {0,...,N_T}

puntos frontera:

x_0 = 0
x_n = 1

puntos interiores:
x1,...,x_N-1

finalmente, sea u_j^n una aproximación de u(t_n,x_j). Usando la discretización
centrada, es posible deducir que

du_dt (t,x_j) = () u(t,x_j+1) - 2u(t,x_j) + u(t,x_j-1) )/(dx**2)  + O(dx**2)

integrando en el intervalo [t_n,t_n+dt]

u_j^n+1 - u_j^n approx dt/2 * (corcho)

donde se cumple que u_j^0 = u_0(xj). Esquema de Crank-Nicolson

'''
## Parte 1

u_0 = lambda x: np.sin(4*np.pi*x)

""" def resolucion(dt,N_T,N,u_0):
    #u = u(t,x)
    u = np.zeros([N_t+1,N+1],)
 """
## Parte 2


# -*- coding: utf-8 -*-
"""Serie_de_Taylor_en_Python.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1-k9zbpEj8QcH3ncAcQAePCHC28Kuz46w

# Serie de Taylor en Python

$$e^x = \sum_{k=0}^n \frac{x^k}{k!}$$
"""

import math

math.factorial(3)

x = 2
e_2 = x**0/math.factorial(0)+x**1/math.factorial(1)+x**2/math.factorial(2)+x**3/math.factorial(3)+x**4/math.factorial(4)
print(e_2)

print(math.exp(2))

def func_e(x, n):
  e_value = 0
  for k in range(n): #0, 1, 2, 3, 4
    e_value += x**k/math.factorial(k)
  return e_value

func_e(x = 2, n = 10)

func_e(5, 7)

x = 5
eps = 1e-6
for i in range(1, 200):
  e_app = func_e(x, i)
  e_exa = math.exp(x)
  e_err = abs(e_app - e_exa)
  if e_err < eps:
    break

print(f"Término {i}: Valor de Serie de Taylor = {e_app}, Valor real = {e_exa}, Error = {e_err}")

"""$$\cos(x) = \sum_{k=0}^n (-1)^k \frac{x^{2k}}{(2k)!}$$"""

def func_cos(x, n):
  cos_value = 0
  for k in range(n):
    coef = (-1)**k
    num = x**(2*k)
    den = math.factorial(2*k)
    cos_value += coef*num/den
  return cos_value

angle = math.radians(45)
print(func_cos(angle, 7))

math.cos(angle)

# Commented out IPython magic to ensure Python compatibility.
import numpy as np
import matplotlib.pyplot as plt
# %matplotlib inline

angles = np.arange(-2*np.pi, 2*np.pi, 0.1)
p_cos = np.cos(angles)

fig, ax = plt.subplots()
ax.plot(angles, p_cos)

for n in range(1, 7):
  t_cos = [func_cos(angle, n) for angle in angles]
  ax.plot(angles, t_cos)

fig.set_size_inches(10,6)

ax.set_ylim([-7,5])

legend_list = ["Función cos(x)"]
for n in range(1, 7):
  legend_list.append(f"Pol. Taylor de grado {n}")
ax.legend(legend_list, loc = 3)

plt.show()


import numpy as np
import matplotlib.pyplot as plt

N=101
initial=-np.pi
final=np.pi
x=np.linspace(initial,final,N)
h=(final-initial)/(N-1)

#analytic:
y_ana=np.sin(x)
dy_ana=np.cos(x)

#acclaim the list to store the data
dy_2=[]
dy_3=[]
dy_5=[]

for i in range(0,N-1):
    dy_2.append((y_ana[i+1]-y_ana[i])/h)

x2=np.delete(x,[N-1])

for i in range(1,N-1):
    dy_3.append((y_ana[i+1]-y_ana[i-1])/h/2)

x3=np.delete(x,[0,N-1])

for i in range(2,N-2):
    dy_5.append((y_ana[i-2]-8*y_ana[i-1]+8*y_ana[i+1]-y_ana[i+2])/12/h)

x5=np.delete(x,[0,1,N-2,N-1])

plt.plot(x,dy_ana,label="analytic")
plt.plot(x2,dy_2,'r.',label="two points")
plt.plot(x3,dy_3,'k.',label="three points")
plt.plot(x5,dy_5,'g.',label="five points")
plt.legend()
plt.show()

#error calculation
error2=(abs(dy_2-dy_ana[0:N-1]))
error3=(abs(dy_3-dy_ana[1:N-1]))
error5=(abs(dy_5-dy_ana[2:N-2]))

plt.plot(x2,error2,label="two points")
plt.plot(x3,error3,label="three points")
plt.plot(x5,error5,label="five points")
plt.title("error by using different number of points")
plt.legend()
plt.show()


import numpy as np

class PCA:
    """:class:`PCA` is a class for dimensionality reduction
    """

    def __init__(self, n_components: int, whiten: bool = False):
        self.n_components = n_components
        self.whiten = whiten
        self.e_values = None
        self.w = None

    def fit(self, x_mat: np.ndarray):
        x_mat = x_mat - x_mat.mean(axis=0)
        cov = np.cov(x_mat.T) / x_mat.shape[0]
        e_values, e_vectors = np.linalg.eig(cov)
        idx = e_values.argsort()[::-1]
        e_values = e_values[idx]
        e_vectors = e_vectors[:, idx]
        self.w = e_vectors
        self.e_values = e_values

    def transform(self, x_mat: np.ndarray) -> np.ndarray:
        if not self.w:
            return
        x_mat_projected = x_mat.dot(self.w[:, : self.n_components])
        if self.whiten:
            return x_mat_projected / np.sqrt(self.e_values[0 : self.n_components])
        else:
            return x_mat_projected

    def fit_transform(self, x_mat: np.ndarray) -> np.ndarray:
        self.fit(x_mat)
        return self.transform(x_mat)


def cells():
    # setup SymPy
    from sympy import *
    x, y, z, t = symbols('x y z t')
    init_printing()

    # setup plotting
    %matplotlib notebook
    import matplotlib.pyplot as mpl
    from util.plot_helpers import plot_vec, plot_vecs, autoscale_arrows

    '''
    '''

    '''
    ## SymPy Matrix objects
    '''

    '''
    '''

    v = Matrix([1,2,3])
    v

    '''
    '''

    # define symbolically
    v_1, v_2, v_3 = symbols('v_1 v_2 v_3')
    v = Matrix([v_1,v_2,v_3])

    '''
    '''

    v

    '''
    '''

    v.T

    '''
    '''

    A = Matrix(
        [   [1,7],
            [2,8],
            [3,9]   ])
    A

    '''
    '''

    # define symbolically
    a_11, a_12, a_21, a_22, a_31, a_32 = symbols('a_11 a_12 a_21 a_22 a_31 a_32')
    A = Matrix([
            [a_11, a_12],
            [a_21, a_22],
            [a_31, a_32]])

    '''
    '''

    A

    '''
    '''

    '''
    ## Vector operations
    '''

    '''
    '''

    u_1, u_2, u_3 = symbols('u_1 u_2 u_3')
    u = Matrix([u_1,u_2,u_3])
    v_1, v_2, v_3 = symbols('v_1 v_2 v_3')
    v = Matrix([v_1,v_2,v_3])
    alpha = symbols('alpha')

    u

    '''
    '''

    alpha*u

    '''
    '''

    u+v

    '''
    '''

    u.norm()

    '''
    '''

    uhat = u/u.norm()
    uhat

    '''
    '''

    u = Matrix([1.5,1])
    w = 2*u
    uhat = u/u.norm()

    fig = mpl.figure()
    plot_vecs(u, w, uhat)
    autoscale_arrows()

    '''
    '''

    '''
    ### Dot product
    '''

    '''
    '''

    u = Matrix([u_1,u_2,u_3])
    v = Matrix([v_1,v_2,v_3])

    u.dot(v)

    '''
    '''

    fig = mpl.figure()
    u = Matrix([1,1])
    v = Matrix([3,0])
    plot_vecs(u,v)
    autoscale_arrows()

    u_dot_v = u.dot(v)
    u_dot_v

    '''
    '''

    phi = acos( u.dot(v)/(u.norm()*v.norm()) )
    print('angle between u and v is', phi)
    u.norm()*v.norm()*cos(phi)

    '''
    '''

    '''
    ### Cross product
    '''

    '''
    '''

    u = Matrix([u_1,u_2,u_3])
    v = Matrix([v_1,v_2,v_3])

    u.cross(v)

    '''
    '''

    u = Matrix([1,0,0])
    v = Matrix([1,1,0])
    w = u.cross(v)      # a vector perpendicular to both u and v

    mpl.figure()
    plot_vecs(u, v, u.cross(v))

    '''
    '''

    print('length of cross product', w.norm())

    phi = acos( u.dot(v)/(u.norm()*v.norm()) )

    w.norm() == u.norm()*v.norm()*sin(phi)

    '''
    '''

    '''
    ## Projection operation
    '''

    '''
    '''

    def proj(vec, d):
        """Computes the projection of vector `vec` onto vector `d`."""
        return d.dot(vec)/d.norm() * d/d.norm()

    '''
    '''

    fig = mpl.figure()
    u = Matrix([1,1])
    v = Matrix([3,0])

    pu_on_v = proj(u,v)

    plot_vecs(u, v, pu_on_v)

    # autoscale_arrows()
    ax = mpl.gca()
    ax.set_xlim([-1,3])
    ax.set_ylim([-1,3])

    '''
    '''

    '''
    # Matrix operations
    '''

    '''
    '''

    a_11, a_12, a_21, a_22, a_31, a_32 = symbols('a_11 a_12 a_21 a_22 a_31 a_32')
    A = Matrix([
            [a_11, a_12],
            [a_21, a_22],
            [a_31, a_32]])
    b_11, b_12, b_21, b_22, b_31, b_32 = symbols('b_11 b_12 b_21 b_22 b_31 b_32')
    B = Matrix([
            [b_11, b_12],
            [b_21, b_22],
            [b_31, b_32]])
    alpha = symbols('alpha')

    '''
    '''

    A

    '''
    '''

    A + B

    '''
    '''

    alpha*A

    '''
    '''

    v_1, v_2 = symbols('v_1 v_2')
    v = Matrix([v_1,v_2])

    A*v

    '''
    '''

    A[:,0]*v[0] + A[:,1]*v[1]

    '''
    '''

    A = Matrix([
            [a_11,a_12],
            [a_21, a_22],
            [a_31, a_32]])
    B = Matrix([
            [b_11,b_12],
            [b_21, b_22]])

    A*B

    '''
    '''

    A.T

    '''
    '''

    print('the shape of v is ', v.shape)
    v

    '''
    '''

    print('the shape of v.T is ', v.T.shape)
    v.T

    '''
    '''

    u = Matrix([u_1,u_2,u_3])
    v = Matrix([v_1,v_2,v_3])

    u.T*v

    '''
    '''

    u * v.T

    '''
    '''

    A = Matrix([
      [3,       3],
      [2,  S(3)/2]
    ])
    A

    '''
    '''

    A.inv()

    '''
    '''

    A * A.inv()

    '''
    '''

    A.inv() * A

    '''
    '''

    B = Matrix([
            [b_11,b_12],
            [b_21, b_22]])
    B

    '''
    '''

    B.trace()

    '''
    '''

    B.det()

    '''
    '''

    '''
    '''


#%% Imports
import numpy as np

# np.where() evalutes before condition
np.seterr(divide='ignore',invalid='ignore')

#%% Calculate the Entropy

def entropy(x,nbins=20):
    count, bin_edges  = np.histogram(x,nbins)
    p = count/len(x)
    H = -np.sum(np.where(p>0,p*np.log2(p),0))
    return H

# # Entropy for X:
# Hx = entropy(x)

#%% Calculate the joint entropy for X and Y:

def joint_entropy(x,y,nbins=20):
        count_xy, xedges, yedges = np.histogram2d(x,y,nbins)
        p_xy = count_xy/len(y)

        tmp = np.where(p_xy>0, p_xy*np.log2(p_xy),0)
        Hxy = -np.sum(np.sum(tmp))
        return Hxy

# # Example:
# Hxy = joint_entropy(x,y)

#%% Calculate the conditional Entropies

def conditional_entropy(x,y,nbins=20):
    count_xy,xedges,yedges = np.histogram2d(x,y,nbins)
    # x-values are rows
    # y-values are columns

    p_xy = count_xy/len(x)

    # Sum across all columns for each row
    p_x = np.sum(p_xy,axis=1)

    # Sum across all rows for each column
    p_y = np.sum(p_xy,axis=0)

    # Conditional Entropy for Rows:
    p_x = np.tile(p_x.reshape(-1,1), (1,nbins))
    tmp = np.where(p_xy>0,p_xy*np.log2(p_xy/p_x),0)
    Hyx_conditional = -np.sum(np.sum(tmp))

    # Conditional Entropy for Columns:
    p_y = np.tile(p_y, (nbins,1))
    tmp = np.where(p_xy>0,p_xy*np.log2(p_xy/p_y),0)
    Hxy_conditional = -np.sum(np.sum(tmp))

    return Hyx_conditional, Hxy_conditional

# Example:
# Hyx_conditional, Hxy_conditional = conditional_entropy(x,y)

#%% Old Functions

# def joint_entropy(x,y,nbins=20):
#         count_xy, xedges, yedges = np.histogram2d(x,y,nbins)
#         p_xy = count_xy/len(y)

#         tmp = np.empty((nbins,nbins))

#         for i in range(nbins):
#             for j in range(nbins):
#                 tmp[i,j]=p_xy[i,j]*np.log2(p_xy[i,j])
#         Hxy = -np.nansum(np.nansum(tmp))
#         return Hxy

# def conditional_entropy(x,y,nbins=20):
#     count_xy,xedges,yedges = np.histogram2d(x,y,nbins)
#     # x-values are rows
#     # y-values are columns

#     p_xy = count_xy/len(x)

#     # Sum across all columns for each row
#     p_x = np.sum(p_xy,axis=1)

#     # Sum across all rows for each column
#     p_y = np.sum(p_xy,axis=0)

#     # Conditional Entropy for Rows:
#     tmp = np.empty((nbins,nbins))

#     for i in range(nbins):
#         for j in range(nbins):
#             tmp[i,j] = p_xy[i,j]*np.log2(p_xy[i,j]/p_x[i])
#     Hyx_conditional = -np.nansum(np.nansum(tmp))

#     # Conditional Entropy for Columns:
#     tmp = np.empty((nbins,nbins))
#     for j in range(nbins):
#         for i in range(nbins):
#             tmp[i,j] = p_xy[i,j]*np.log2(p_xy[i,j]/p_y[j])
#     Hxy_conditional = -np.nansum(np.nansum(tmp))

#     return Hyx_conditional, Hxy_conditional


# Matrices chain multiplication optimization
# Author: Douglas Canevarollo

# That optimization problem involves matrices multiplication. That is, considering and array chain
# <A1, A2, ..., An>, which of the order of multiplications of this chain will return the least number
# of scalar multiplications?

# That dynamic implementation has a O(n^3) time complexity class when the recursive has a O(2^n).
from numpy import zeros
from sys import maxsize  # Max value of int64 size

# Helper class to store the information of the read matrices.
class Matrix:
    def __init__(self, lines, columns):
        self.lines = int(lines)
        self.columns = int(columns)

# Method that prints the optimal matrices chain.
def print_chain(brackets, i, j):
    if i == j:
        print("A" + str(i), end=' ')
        return None

    print("( ", end='')

    print_chain(brackets, i, brackets[i][j])
    print_chain(brackets, brackets[i][j] + 1, j)

    print(") ", end='')

# dimensions = <d0, d1, d2, d3..., dn>, where the dimension of the Ai matrix is di-1,di. That is,
# dimensions is actually the dimensions array of our matrices on the chain.
# For instance, dimension of A3 is d2,d3.
def matrix_chain_order(dimensions, dim_size):
    # The 'm' array stores the minimum number of scalar multiplications needed.
    # We initialize it with zeros to simplify the code.
    m = zeros((dim_size, dim_size), dtype='int64')
    # 'brackets' array stores the chain position that contains parentheses.
    brackets = zeros((dim_size, dim_size), dtype=int)

    # Cost is zero when multiplying one matrix.
    if dim_size == 1:
        raise RuntimeError

    if dim_size == 2:
        m[1][-1] = dimensions[0]*dimensions[1]

    # 'l' variable used to run through the dimensions array
    for l in range(2, dim_size):
        for i in range(1, dim_size - l + 1):
            j = i+l-1

            m[i][j] = maxsize  # Similar to m <- infinite
            for k in range(i, j):
                temp = m[i][k] + m[k+1][j] + dimensions[i - 1] * dimensions[k] * dimensions[j]
                if temp < m[i][j]:
                    m[i][j] = temp
                    # 'k' is the optimal break point of the multiplication interval <Ai...j>
                    brackets[i][j] = k

    # Returns the minimum number of scalar multiplications needed and the brackets indexes array.
    return m[1][-1], brackets

if __name__ == "__main__":
    matrices = []
    dimensions = []

    try:
        i = 0
        chain_size = int(input("Number of matrices in the chain\n> "))
        while i < chain_size:
            print("\nMatrix " + str(i))
            matrices.append(Matrix(input("Lines\n> "), input("Columns\n> ")))
            # Remember that to a matrices multiplication be possible the lines number of the 1st matrix
            # should be equal to the columns number of the 2nd.
            # Thus, we just need to store in 'p' array the matrices lines.
            if i != 0 and matrices[i-1].columns != matrices[i].lines:
                print("\nERROR!\nThe number of lines of this matrix should be equal to the previous columns.")
                matrices.pop(-1)
                # Repeat the insertion on the index i
                continue

            dimensions.append(matrices[i].lines)
            i += 1

        dim_size = len(dimensions)
        min_mult, brackets_array = matrix_chain_order(dimensions, dim_size)

        print("\nOptimal parenthesization\n> ", end='')
        print_chain(brackets_array, 0, dim_size-1)
        print("\n%i scalars multiplications needed." % min_mult)
    except ValueError:
        print("\nJust integers, please. Try again.")
    except RuntimeError:
        print("\nYou cannot multiply just one matrix.")
    finally:
        exit(0)


import math
import numpy as np

def calc_angle(a, b, c):
    """
    :param a: a[x, y]
    :param b: b[x, y]
    :param c: c[x, y]
    :return: angle between ab and bc
    """
    if b in (a, c):
        raise ValueError("Undefined angle, two identical points", (a, b, c))

    ang = math.degrees(
        math.atan2(a[1] - b[1], a[0] - b[0]) - math.atan2(c[1] - b[1], c[0] - b[0]))
    return ang + 360 if ang < 0 else ang

def calc_m_and_b(point1, point2):
    """
    calculate the slope intercept form of the line from Point 1 to Point 2.
    meaning, finding the m and b, in y=mx+b.
    :param point1: point 1
    :param point2: point 2
    :return: m, b
    """
    points = [point1, point2]
    x_coords, y_coords = zip(*points)
    A = np.vstack([x_coords, np.ones(len(x_coords))]).T

    return np.linalg.lstsq(A, y_coords, rcond=None)[0]

def y_from_m_b_x(m, b, x):
    """
    get y from y=mx+b
    :param m: slope (m)
    :param b: b
    :param x: x
    :return: y from y=mx+b
    """
    return m * x + b

def x_from_m_b_y(m, b, y):
    """
    get x from y=mx+b
    :param m: slope (m)
    :param b: b
    :param y: y
    :return: get x from y=mx+b
    """
    return (y - b) / m


from numpy import loadtxt, zeros, ones, array, linspace, logspace
from pylab import scatter, show, title, xlabel, ylabel, plot, contour

#Evaluate the linear regression
def compute_cost(X, y, theta):
    '''
    Comput cost for linear regression
    '''
    #Number of training samples
    m = y.size

    predictions = X.dot(theta).flatten()

    sqErrors = (predictions - y) ** 2

    J = (1.0 / (2 * m)) * sqErrors.sum()

    return J

def gradient_descent(X, y, theta, alpha, num_iters):
    '''
    Performs gradient descent to learn theta
    by taking num_items gradient steps with learning
    rate alpha
    '''
    m = y.size
    J_history = zeros(shape=(num_iters, 1))

    for i in range(num_iters):

        predictions = X.dot(theta).flatten()

        errors_x1 = (predictions - y) * X[:, 0]
        errors_x2 = (predictions - y) * X[:, 1]

        theta[0][0] = theta[0][0] - alpha * (1.0 / m) * errors_x1.sum()
        theta[1][0] = theta[1][0] - alpha * (1.0 / m) * errors_x2.sum()

        J_history[i, 0] = compute_cost(X, y, theta)

    return theta, J_history

#Load the dataset
data = loadtxt('ex1data1.txt', delimiter=',')

#Plot the data
scatter(data[:, 0], data[:, 1], marker='o', c='b')
title('Profits distribution')
xlabel('Population of City in 10,000s')
ylabel('Profit in $10,000s')
#show()

X = data[:, 0]
y = data[:, 1]

#number of training samples
m = y.size

#Add a column of ones to X (interception data)
it = ones(shape=(m, 2))
it[:, 1] = X

#Initialize theta parameters
theta = zeros(shape=(2, 1))

#Some gradient descent settings
iterations = 1500
alpha = 0.01

#compute and display initial cost
print compute_cost(it, y, theta)

theta, J_history = gradient_descent(it, y, theta, alpha, iterations)

print theta
#Predict values for population sizes of 35,000 and 70,000
predict1 = array([1, 3.5]).dot(theta).flatten()
print 'For population = 35,000, we predict a profit of %f' % (predict1 * 10000)
predict2 = array([1, 7.0]).dot(theta).flatten()
print 'For population = 70,000, we predict a profit of %f' % (predict2 * 10000)

#Plot the results
result = it.dot(theta).flatten()
plot(data[:, 0], result)
show()

#Grid over which we will calculate J
theta0_vals = linspace(-10, 10, 100)
theta1_vals = linspace(-1, 4, 100)

#initialize J_vals to a matrix of 0's
J_vals = zeros(shape=(theta0_vals.size, theta1_vals.size))

#Fill out J_vals
for t1, element in enumerate(theta0_vals):
    for t2, element2 in enumerate(theta1_vals):
        thetaT = zeros(shape=(2, 1))
        thetaT[0][0] = element
        thetaT[1][0] = element2
        J_vals[t1, t2] = compute_cost(it, y, thetaT)

#Contour plot
J_vals = J_vals.T
#Plot J_vals as 15 contours spaced logarithmically between 0.01 and 100
contour(theta0_vals, theta1_vals, J_vals, logspace(-2, 3, 20))
xlabel('theta_0')
ylabel('theta_1')
scatter(theta[0][0], theta[1][0])
show()


# finding LCM(Lowest Common Multiple)

# The lowest common multiple is the least number that is common multiple of both of the numbers

# find the lcms of the following two numbers

import numpy as np

num1 = 4
num2 = 6

x = np.lcm(num1, num2)

print(x)
# return 12

# finding lcm in arrays
# to find the lowest common multiple of all values in an array
# you can use the reduce() method

# the reduce() uses the ufunc, lcm() on each element and reduce the array by one dimension

# import numpy as np

arr = np.array([3, 6, 9])

x = np.lcm.reduce(arr)

print(x)

# find the lcm of all an array where the array contains all integers from 1 to 10

# import numpy as np

arr = np.arange(1, 11)

x = np.lcm.reduce(arr)

print(x)


import numpy as np
from matplotlib import pyplot as plt
plt.ion()

def simulateParticles_loop( n=50, n_gen=100, showPlots=True):
    ''' simulate gaussian motion of n particles for n_gen steps each
    '''
    # store the value of each particle at every step
    f_all = np.zeros((n,n_gen))

    for i in range(n):
        y = 0
        f_all[i,0] = y
        for j in range(1,n_gen):
            y += np.random.randn()
            f_all[i,j] = y

    x_grid, y_grid = np.mgrid[0:n, 0:n_gen]

    if showPlots:
        plt.figure()
        plt.plot(y_grid.T, f_all.T, alpha=.2)

        plt.figure()
        plt.hist2d(y_grid.ravel(), f_all.ravel(), bins=(n_gen, 50))

        plt.show()

    return y_grid, f_all

def simulateParticles_loop_onAry( ary ):
    ''' simulate gaussian motion of n particles for n_gen steps each
    '''
    for i in range(ary.shape[0]):
        y = 0
        for j in range(1, ary.shape[1]):
            y += np.random.randn()
            ary[i,j] = y
    return ary

def simulateParticles_vec( n=50, n_gen=100, showPlots=True):
    ''' simulate gaussian motion of n particles for n_gen steps each
    '''
    f_0 = np.zeros(n)
    f_diff = np.random.randn(n, n_gen)
    f_diff[:,0] = 0
    f_all = f_0[:,np.newaxis] + f_diff.cumsum(1)

    x_grid, y_grid = np.mgrid[0:n, 0:n_gen]

    if showPlots:
        plt.figure()
        plt.plot(y_grid.T, f_all.T, alpha=.2)

        plt.figure()
        plt.hist2d(y_grid.ravel(), f_all.ravel(), bins=(n_gen,50))

        plt.show()

    return y_grid, f_all

def rotateParticles(n=50, n_gen=100):
    ''' simulate gaussian motion of n particles for n_gen steps each
        and then rotate all the particles paths
    '''
    y_grid, f_all = simulateParticles_vec(n=n, n_gen=n_gen)

    ang = 0./180.*np.pi
    xy = np.array([y_grid.ravel(), f_all.ravel()])
    R = np.array( [[np.cos(ang), -np.sin(ang)],[np.sin(ang), np.cos(ang)]])
    xy_rot = R.dot(xy)
    fig = plt.figure()
    lines = plt.plot(xy_rot[0].reshape(f_all.shape).T,
                     xy_rot[1].reshape(f_all.shape).T, alpha=.2)

    for ang in np.linspace(-np.pi/4,np.pi/4., 45):
        R = np.array( [[np.cos(ang), -np.sin(ang)], [np.sin(ang), np.cos(ang)]])
        xy_rot = R.dot(xy)

        xy_2d = xy_rot.reshape((2, n, n_gen))

        for i in range(n):
            lines[i].set_data( xy_2d[0,i], xy_2d[1,i])

        fig.canvas.draw()


import numpy as np
from numpy.linalg import norm

def numerical_grad(func,x):
    '''
    Function to calculate gradients
    of a function with a single parameter numerically

    f(x+h) - f(x-h)
    ---------------
           2h

    '''

    h = 1e-7

    grad_vec = []

    #assuming x is a (num,shape) matrix
    #for example (2,3,3) matrix
    #then we need to compute the gradient one at a time

    f_x_plus_h = func(x+h)
    f_x_min_h = func(x-h)

    grad_vec = (f_x_plus_h-f_x_min_h)/(2*h)

    return grad_vec

def compare(num_grads,grads):
    diff = abs(num_grads-grads)
    bools = diff < 1e-8
    if np.sum(bools) == bools.size:
        print('The derivated gradients are correct')
    else:
        print('The derivated gradients are wrong.')
    return diff

def numerical_grad_layer(layer,lossfn,x,y,param_name):
    '''
    Function to calculate gradients
    of a layer with respect to a parameter numerically

    layer(x,param+h) - layer(x,param-h)
    -----------------------------------
                     2*h

    the numerical gradients are calculated differently in that
    every value in the parameter is tweaked and then we measure the
    effect on the whole layer.
    The loss is then calculated when we add and when we subtract

    So it would be like this:
    assuming:
    `param` = [1,2,3]
    `x` = [3,4,5]
    `y` = [6,7,8]
    loss(p,y) = sum(p-y)
    `h` = 1e-4
    net(param) = x+param

    The numerical grad of param would be calculated as

    [
        (loss(net([1+h,2,3]),y) - loss(net([1-h,2,3]),y)) / 2*h,

        (loss(net([1,2+h,3]),y) - loss(net([1,2-h,3]),y)) / 2*h,

        (loss(net([1,2,3+h]),y) - loss(net([1,2,3-h]),y)) / 2*h
    ]

    The result is [0.9999999999976694, 0.9999999999976694, 0.9999999999976694]

    which is pretty close to [1,1,1]

    '''

    h = 1e-7

    orig_param = layer.params[param_name].copy()
    h_vec = np.zeros(np.prod(orig_param.shape))
    n_grad = np.zeros_like(h_vec)

    for idx in range(np.prod(orig_param.shape)):
        h_vec[idx] = h

        layer.params[param_name] = orig_param + h_vec.reshape(orig_param.shape)
        l1 = lossfn(layer(x),y)

        layer.params[param_name] = orig_param - h_vec.reshape(orig_param.shape)
        l2 = lossfn(layer(x),y)

        n_grad[idx] = (l1-l2)/(2*h)

        h_vec[idx] = 0

    n_grad = n_grad.reshape(orig_param.shape)

    return n_grad


import numpy as np
from matplotlib import pyplot as plt

def polygon(n):
    print("Initializing x and y array of size "+str(n)+" for x and y co-ordinates")
    x=[None]*n
    y=[None]*n

    numberofleftturn=0
    numberofrightturn=0

    print("Getting Vertices:")

    for i in range (n):
        x[i]=int(input("Enter x co-ordinate of Vertex "+str(i+1)+": "))
        y[i]=int(input("Enter y co-ordinate of Vertex "+str(i+1)+": "))
        plt.scatter(x[i],y[i])

    print("***************************************************")
    print("List of Vertices Before Sorting:")
    print("***************************************************")
    for i in range(n):
        print("("+str(x[i])+","+str(y[i])+")")

    print("Sorting All the Vertices in Counter Clockwise manner")
    center_point = [np.sum(x)/n, np.sum(y)/n]
    angles = np.arctan2(x-center_point[0],y-center_point[1])
    sort_tups = sorted([(i,j,k) for i,j,k in zip(x,y,angles)], key = lambda t: t[2],reverse=True)
    if len(sort_tups) != len(set(sort_tups)):
        raise Exception('You mistakenly input two equal vertices')
    x,y,angles = zip(*sort_tups)

    print("***************************************************")
    print("List of Vertices after Sorting:")
    print("***************************************************")
    for i in range(n):
        print("("+str(x[i])+","+str(y[i])+")")

    print("***************************************************")
    print("Performing Turn Test:")
    print("***************************************************")

    print("Appending First Two Coordinate to the list of vertices")
    x = list(x)
    y = list(y)

    x.append(x[0])
    y.append(y[0])

    x.append(x[1])
    y.append(y[1])

    for i in range(n):
        print("At Point("+str(x[i+1])+","+str(y[i+1])+")")
        print("Points to consider: ("+str(x[i])+","+str(y[i])+")","("+str(x[i+1])+","+str(y[i+1])+")","("+str(x[i+2])+","+str(y[i+2])+")")

        turntest=(x[i+1]-x[i])*(y[i+2]-y[i])-(y[i+1]-y[i])*(x[i+2]-x[i])
        if(turntest>0):
            print("Left Turn")
            numberofleftturn=numberofleftturn+1
        elif(turntest<0):
            print("Right Turn")
            numberofrightturn=numberofrightturn+1
        else:
            print("Collinear")

    print("***************************************************")
    print("Checking Convexity:")
    print("***************************************************")
    if(numberofrightturn==0):
        if(numberofleftturn==0):
            print("All Points are Collinear")
        else:
            print("Polygon is a Convex Polygon")

    else:
        print("Polygon is a Concave Polygon")

    print("Plotting Co-ordinates")
    plt.plot(x,y,'-')

    print("Showing Graph")
    plt.show()

print("Implementation of Polygon, Turn Test, and Convexity")
print("***************************************************")
n=int(input("Enter no of Vertices:"))
polygon(n)


#!/usr/bin/env python
# coding: utf-8
# In[1]:
import numpy as np
import pandas as pd
from pandas_datareader import data as web
from scipy.stats import norm
import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')
# In[2]:
ticker = 'PG'
data = pd.DataFrame()
data[ticker] = web.DataReader(ticker, data_source='yahoo', start='2007-1-1', end='2017-3-21')['Adj Close']
# In[3]:
log_returns = np.log(1 + data.pct_change())
#

# $$
# {\LARGE S_t = S_{t-1} \mathbin{\cdot} e^{((r - \frac{1}{2} \cdot stdev^2) \mathbin{\cdot} \delta_t + stdev \mathbin{\cdot} \sqrt{\delta_t} \mathbin{\cdot} Z_t)}  }
# $$
#

# In[4]:
r = 0.025
# In[5]:
stdev = log_returns.std() * 250 ** 0.5
stdev
# In[6]:
type(stdev)
# In[7]:
stdev = stdev.values
stdev
# In[8]:
T = 1.0
t_intervals = 250
delta_t = T / t_intervals
iterations = 10000
# In[9]:
Z = np.random.standard_normal((t_intervals + 1, iterations))
S = np.zeros_like(Z)
S0 = data.iloc[-1]
S[0] = S0
#

# $$
# {\LARGE S_t = S_{t-1} \mathbin{\cdot} e^{((r - \frac{1}{2} \cdot stdev^2) \mathbin{\cdot} \delta_t + stdev \mathbin{\cdot} \sqrt{\delta_t} \mathbin{\cdot} Z_t)}  }
# $$
#

# In[10]:
for t in range(1, t_intervals + 1):
    S[t] = S[t-1] * np.exp((r - 0.5 * stdev ** 2) * delta_t + stdev * delta_t ** 0.5 * Z[t])
# In[11]:
S
# In[12]:
S.shape
# In[13]:
plt.figure(figsize=(10, 6))
plt.plot(S[:, :10]);