#!/usr/bin/env python
# Author: Andrew Jewett (jewett.aij at g mail)
# License: MIT License  (See LICENSE.md)
# Copyright (c) 2020, Scripps Research
# All rights reserved.

"""
   A crude command line utility which reads the coordinates of 3 points,
   finds the circle which passes through them, and returns the center
   and the radius of that circle.

   Usage:

     curvature3pts.py < coordinates.txt

   Details:

     This program reads a text file from the standard input in multi-column
   format, with N numbers on each line delimited by spaces. (Usually N=2 or 3.)
   This file should contain exactly 3 lines (corresponding to the 3 points
   on the circle).

   This program works in an arbitrary number of dimensions.
   (The number of dimensions is specified by the number of columns in the file.)

"""

from math import *
import sys
import numpy as np

def CircleFrom3Points2D(r1, r2, r3):
    """
    3 points pass through a circle.  Find the center of that circle
    and its radius.  3 eqns (below) with 3 unknowns (x0, y0, r)
       (x1 - x0)^2 + (y1 - y0)^2  =  r^2
       (x2 - x0)^2 + (y2 - y0)^2  =  r^2
       (x3 - x0)^2 + (y3 - y0)^2  =  r^2
    Solve for (x0, y0) using A * (x0, y0) = B where:
    """
    B = np.array([r2[0]**2 - r1[0]**2 +
                  r2[1]**2 - r1[1]**2,
                  r3[0]**2 - r2[0]**2 +
                  r3[1]**2 - r2[1]**2])
    A = np.array([[2.0 * (r2[0] - r1[0]),
                   2.0 * (r2[1] - r1[1])],
                  [2.0 * (r3[0] - r2[0]),
                   2.0 * (r3[1] - r2[1])]])
    x0, y0 = np.linalg.solve(A,B)
    r = sqrt((r1[0] - x0)**2 + (r1[1] - y0)**2)
    return r, x0, y0

def CircleFrom3Points(r1, r2, r3):
    """
    This is the N-dimensional generalization of CircleFrom3Points2D().
    (It works in 3D and also higher dimensions.
     This function is not necessary for "sabl.py" which is a 2D program.
     Consequently, I never got around to testing it carefully.
     Hopefully this function works, but test it first.  -A 2020-6-10)
    """

    # Decompose this into a 2D problem using Graham-Schmidt decomposition
    # of the original vectors into the basis defined by va=r1-r2 and vb=r3-r2.
    # Then apply CircleFrom3Points2D() to find the radius of curvature
    # and the central point.

    va = r1-r2
    vb = r3-r2

    ea = va / np.linalg.norm(va)
    eb = vb - np.inner(vb,ea)*ea
    eb /= np.linalg.norm(eb)

    # Now express the vectors r1-r2, r2-r2, and r3-r2
    # in the basis formed by unit vectors ea and eb.
    # The resutling _r1, _r2, _r3 vectors are 2D vectors.

    _r1 = np.array([np.inner(va, ea), np.inner(va, eb)])
    _r2 = np.array(r2-r2)  # (this should be the zero vector)
    _r3 = np.array([np.inner(vb, ea), np.inner(vb, eb)])

    # Now invoke "CircleFrom3Points2D()" to calculate the radius and center
    # of the circle in this 2D coordinate system
    r, x0, y0 =  CircleFrom3Points2D(_r1, _r2, _r3)

    # Now convert x0, y0 back into the original coordinate system
    r0 = r2 + x0*ea + y0*eb

    # Now return the results to the caller
    return r, r0

def main():
    lines = sys.stdin.readlines()

    # r1 r2 and r3 are arrays containing the xyz coordinates of the 3 points
    r1 = np.array(list(map(float, lines[0].strip().split())))  #x,y,z of point 1
    r2 = np.array(list(map(float, lines[1].strip().split())))  #x,y,z of point 2
    r3 = np.array(list(map(float, lines[2].strip().split())))  #x,y,z of point 3

    radius, center = CircleFrom3Points(r1, r2, r3)

    sys.stdout.write('circle_center =')
    for i in range(0, center.size):
        sys.stdout.write(' '+str(center[i]))
    sys.stdout.write('\n')

    sys.stdout.write('radius = '+str(radius)+'\n')

if __name__ == '__main__':
    main()


# https://github.com/llSourcell/Intro_to_the_Math_of_intelligence
# Create a linear regression model and train using Gradient Descent from scratch.

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

datapath = "data.csv"
alpha = 3e-5         # learning rate
num_iter = 100     # number of iterations for gradient descent

def data_process(path):
    "Import and preprocess data"
    df = pd.read_csv(path)
    df = df.reindex(np.random.permutation(df.index))
    return df
'''
def randomize(df):
    "Randomly shuffle the items of data"
    r_data = df.reindex(np.random.permutation(df.index))
    return r_data
'''
def gradient_descent(x, y, N, m, b, alpha, num_iter):
    "Optimize the model using Gradient Descent algorithm"
    loss_info = []
    inter_loss = 0
    for i in range(num_iter):
        b_loss = 0
        m_loss = 0
        for j in range(N):
            y_pred = m * x[j] + b
            b_loss += (y[j] - y_pred)
            m_loss += (y[j] - y_pred) * x[j]

        m = m - alpha * (-2/float(N) * m_loss)
        b = b - alpha * (-2/float(N) * b_loss)

        for k in range(N):
            y_pred = m * x[k] + b
            inter_loss += (y_pred - y[k])**2
        inter_loss = inter_loss / float(N)
        loss_info.append([i, inter_loss])

    return m, b, loss_info

def linear_regressor(r_data):
    "Create a linear regression model"

    x = r_data["Distance cycled"]
    y = r_data["Calories burnt"]
    N = len(r_data)
    m_init = 0    #1.4
    b_init = 0
    loss = 0

    m, b, loss_info = gradient_descent(x, y, N, m_init, b_init, alpha, num_iter)
    print "m, b, alpha = %0.7s, %0.7s, %s" % (m, b, alpha)

    for i in range(N):
        #y_pred = m_init * x[i] + b_init
        y_pred = m * x[i] + b
        loss += (y_pred - y[i])**2

    final_loss = loss / float(N)
    return final_loss, loss_info, m, b

def plot_loss(loss_info):
    iter_no = []
    loss_value = []
    for item in loss_info:
        iter_no.append(item[0])
        loss_value.append(item[1])

    plt.plot(iter_no, loss_value)
    plt.show()

def main():
    data = data_process(datapath)
    avg_loss, loss_info, slope, intercept = linear_regressor(data)
    #plot_loss(loss_info)

    x = data["Distance cycled"]
    y = data["Calories burnt"]
    y_pred = slope * x + intercept

    x_new = [72, 73.5, 74.9, 76.5, 78.5, 80]
    y_new = [(slope * i + intercept) for i in x_new]

    plt.plot(x, y, 'ro', x, y_pred, x_new, y_new, 'bs')
    plt.show()

    print "Final avg loss = %s" % avg_loss

if __name__ == '__main__':
    main()

'''
Results-
m, b, alpha = 1.478566841371454, 0.047075978779471006, 0.0003
Final loss = 112.639852759
'''


# Copyright 2016 Enthought, Inc. All Rights Reserved
"""
Topics: NumPy array indexing and array math.

Use array slicing and math operations to calculate the
numerical derivative of ``sin`` from 0 to ``2*pi``.  There is no
need to use a for loop for this.

Plot the resulting values and compare to ``cos``.

Bonus
~~~~~

Implement integration of the same function using Riemann sums or the
trapezoidal rule.

"""
from numpy import linspace, pi, sin, cos, cumsum
from matplotlib.pyplot import plot, show, subplot, legend, title

# calculate the sin() function on evenly spaced data.
x = linspace(0,2*pi,101)
y = sin(x)

# calculate the derivative dy/dx numerically.
# First, calculate the distance between adjacent pairs of
# x and y values.
dy = y[1:]-y[:-1]
dx = x[1:]-x[:-1]

# Now divide to get "rise" over "run" for each interval.
dy_dx = dy/dx

# Assuming central differences, these derivative values
# centered in-between our original sample points.
centers_x = (x[1:]+x[:-1])/2.0

# Plot our derivative calculation.  It should match up
# with the cos function since the derivative of sin is
# cos.
subplot(1,2,1)
plot(centers_x, dy_dx,'rx', centers_x, cos(centers_x),'b-')
title(r"$\rm{Derivative\ of}\ sin(x)$")

# Trapezoidal rule integration.
avg_height = (y[1:]+y[:-1])/2.0
int_sin = cumsum(dx * avg_height)

# Plot our integration against -cos(x) - -cos(0)
closed_form = -cos(x)+cos(0)
subplot(1,2,2)
plot(x[1:], int_sin,'rx', x, closed_form,'b-')
legend(('numerical', 'actual'))
title(r"$\int \, \sin(x) \, dx$")
show()


import numpy as np
import matplotlib.pyplot as plt

def estimate_coefficients(x, y):
    # size of the dataset OR number of observations/points
    n = np.size(x)

    # mean of x and y
    # Since we are using numpy just calling mean on numpy is sufficient
    mean_x, mean_y = np.mean(x), np.mean(y)

    # calculating cross-deviation and deviation about x
    SS_xy = np.sum(y*x - n*mean_y*mean_x)
    SS_xx = np.sum(x*x - n*mean_x*mean_x)

    # calculating regression coefficients
    b_1 = SS_xy / SS_xx
    b_0 = mean_y - b_1*mean_x

    return(b_0, b_1)

    # x,y are the location of points on graph
    # color of the points change it to red blue orange play around

def plot_regression_line(x, y, b):
    # plotting the points as per dataset on a graph
    plt.scatter(x, y, color = "m",marker = "o", s = 30)

    # predicted response vector
    y_pred = b[0] + b[1]*x

    # plotting the regression line
    plt.plot(x, y_pred, color = "g")

    # putting labels for x and y axis
    plt.xlabel('Size')
    plt.ylabel('Cost')

    # function to show plotted graph
    plt.show()

def main():
    # Datasets which we create
    x = np.array([ 1,   2,   3,   4,   5,   6,   7,   8,    9,   10])
    y = np.array([300, 350, 500, 700, 800, 850, 900, 900, 1000, 1200])

    # estimating coefficients
    b = estimate_coefficients(x, y)
    print("Estimated coefficients:\nb_0 = {} \nb_1 = {}".format(b[0], b[1]))

    # plotting regression line
    plot_regression_line(x, y, b)

if __name__ == "__main__":
    main()


# The population of salmon next year is given by
# f(S) = Se^r(1 - S / P ),
# where:
# S is this years' salmon population,
# P is the equilibrium population,
# r is a constant that depends upon how fast the population grows.

# Find f'(S0) and solve the equation f'( S0 ) = 1.
# Graph f'(S0) and y = 1.

# Find the population for which the maximum sustainable harvest occurs if r = 0.4 and P = 850

import math
from sympy import solve, lambdify, symbols, diff, pprint, pretty, simplify, exp
import matplotlib.pyplot as plt
import numpy as np

S = symbols( 'S' )
r, P = 0.4, 850

F = S * exp( r *( 1 - S / P ) )

dF = diff( F, S )

# intersection = round( solve( dF - 1, S )[ 0 ], 3 )

# Graph

domain_end = 20

g_xlim = [ 0, 500 ]
g_ylim = [-5, 10 ]

lam_x = lambdify( S, dF, np )

x_vals = np.linspace( g_xlim[0], g_xlim[1], 1000, endpoint=True )
y_vals = lam_x( x_vals )

plt.plot( x_vals, y_vals, label = '' )
plt.hlines( y = 1, xmin = g_xlim[ 0 ], xmax = g_xlim[ 1 ], color = 'Orange', zorder = 1 )
plt.scatter( intersection, 1, color = 'R' )
plt.show()


import numpy as numpy
from  scipy.special import factorial

def fdcoeffV(k,xbar,x):
    """
    fdcoeffV routine modified from Leveque (2007) matlab function

    Params:
    -------

    k: int
        order of derivative
    xbar: float
        point at which derivative is to be evaluated
    x: ndarray
        numpy array of coordinates to use in calculating the weights

    Returns:
    --------
    c: ndarray
        array of floats of coefficients.

    Compute coefficients for finite difference approximation for the
    derivative of order k at xbar based on grid values at points in x.

    WARNING: This approach is numerically unstable for large values of n since
    the Vandermonde matrix is poorly conditioned.  Use fdcoeffF.m instead,
    which is based on Fornberg's method.

     This function returns a row vector c of dimension 1 by n, where n=length(x),
     containing coefficients to approximate u^{(k)}(xbar),
     the k'th derivative of u evaluated at xbar,  based on n values
     of u at x(1), x(2), ... x(n).

     If U is an array containing u(x) at these n points, then
     c.dot(U) will give the approximation to u^{(k)}(xbar).

     Note for k=0 this can be used to evaluate the interpolating polynomial
     itself.

    Requires len(x) > k.
    Usually the elements x(i) are monotonically increasing
    and x(1) <= xbar <= x(n), but neither condition is required.
    The x values need not be equally spaced but must be distinct.

    Modified rom  http://www.amath.washington.edu/~rjl/fdmbook/  (2007)
    """

    n = x.shape[0]
    assert  k < n, " The order of the derivative must be less than the stencil width"

    # Generate the Vandermonde matrix from the Taylor series
    A = numpy.ones((n,n))
    xrow = (x - xbar)  # displacements x-xbar
    for i in range(1,n):
        A[i,:] = (xrow**(i))/factorial(i);

    b = numpy.zeros(n)    # b is right hand side,
    b[k] = 1              # so k'th derivative term remains

    c = numpy.linalg.solve(A,b)          # solve n by n system for coefficients

    return c


import random

import numpy as np

def poisson_disk(r, width, height, k=30):
    """
    Poisson-disc sampling according to https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf and
    similar to the JavaScript implementation from https://www.jasondavies.com/poisson-disc/.

    :param r: points are generated in the radius interval [r, 2r] from their neighbors (see paper)
    :param width: width of the domain (e.g. image)
    :param height: height of the domain (e.g. image)
    :param k: number of attempts to generate valid neighbors for each point (see paper)
    :return: numpy array of Poisson-disc sampled points
    """
    # Step 0: Initialization
    cell_size = r / np.sqrt(2)

    points = []
    grid = np.ones((np.ceil(height / cell_size).astype(np.int), np.ceil(width / cell_size).astype(np.int)), dtype=np.int) * -1
    active = []

    # Normalizing constant for annulus sampling
    r_sqr = r ** 2
    a = 3 * r_sqr

    def emit_sample(point):
        points.append(point)
        index = len(points) - 1
        int_point = (point // cell_size).astype(np.int)
        assert(grid[int_point[0], int_point[1]] == -1)
        grid[int_point[0], int_point[1]] = index
        active.append(index)

    def generate_around(point):
        theta = random.random() * 2 * np.pi
        radius = np.sqrt(random.random() * a + r_sqr)

        return point + np.array([radius * np.sin(theta), radius * np.cos(theta)])

    def check_extents(point):
        return 0 < point[0] < height and 0 < point[1] < width

    def check_neighborhood(point):
        iy, ix = (point // cell_size).astype(np.int)
        y0 = max(0, iy - 1)
        x0 = max(0, ix - 1)
        y1 = min(iy + 2, grid.shape[0])
        x1 = min(ix + 2, grid.shape[1])

        for ny in range(y0, y1):
            for nx in range(x0, x1):
                grid_index = grid[ny, nx]
                if grid_index == -1:
                    continue
                grid_point = points[grid_index]
                if np.square(grid_point - point).sum() < r_sqr:
                    return False

        return True

    def check_valid(point):
        return check_extents(point) and check_neighborhood(point)

    # Step 1: Initial sample
    emit_sample(np.random.random_sample((2,)) * np.array([height, width]))

    # Step 2: Sampling
    while len(active) > 0:
        i = active[random.randint(0, len(active) - 1)]

        candidate_found = False
        for j in range(k):
            new_point = generate_around(points[i])
            if check_valid(new_point):
                candidate_found = True
                emit_sample(new_point)
                break

        if not candidate_found:
            active.remove(i)

    return np.array(points)


#!/usr/bin/env python
# coding: utf-8

# # my lession 1 on Linear Algebra
#
# https://github.com/engineersCode/EngComp4_landlinear

# In[23]:

import numpy
get_ipython().run_line_magic('matplotlib', 'inline')
from matplotlib import pyplot

# In[24]:

import sys
sys.path.append('../scripts/')

# Our helper with the functions
# plot_vector, plot_linear_transformation, plot_linear_transformations
from plot_helper import *

# In[25]:

vectors = [(2,2)]
tails = [(-3, -2), (-3, 1), (0, 0), (1, -3)]

# In[26]:

plot_vector(vectors, tails)

# In[27]:

vectors = [(2, 4)]
tails = [(0,0), (-1, 1)]
plot_vector(vectors, tails)

# Vector addition

# In[34]:

a = numpy.array((-2, 1))
b = numpy.array((1, -3))
origin = numpy.array((0, 0))
vectors = [a, b, a+b]
tails = [origin, a, origin]
plot_vector(vectors, tails)

# Vector scaling

# In[29]:

c = numpy.array((2, 1))
vectors = [c, 2*c]
plot_vector(vectors)

# In[30]:

c = numpy.array((2, 1))
vectors = [c, -2*c]
plot_vector(vectors)

# In[31]:

i = numpy.array((1, 0))
j = numpy.array((0, 1))

vec = 3*1 + 2*j

vectors = [i, j, 3*i, 2*j, vec]
plot_vector(vectors)

# **Linear combinations** -- adding together two vectors that were each scaled
#
# **Span** -- set of all possible linear combinations

# In[32]:

from numpy.random import randint

vectors = []
for _ in range(40):
    m = randint(-10, 10)
    n = randint(-10, 10)
    vectors.append(m*i + n*j)

plot_vector(vectors)

# In[36]:

from numpy.random import randint

vectors = []
for _ in range(40):
    m = randint(-10, 10)
    n = randint(-10, 10)
    vectors.append(m*a + n*b)

plot_vector(vectors)

# In[37]:

d = numpy.array((-1, 0.5))

# In[38]:

vectors = []
for _ in range(40):
    m = randint(-10, 10)
    n = randint(-10, 10)
    vectors.append(m*a + n*d)

plot_vector(vectors)

# In[39]:

a

# In[40]:

d

# a and d share the same span because they describe the same line.  d is a multiple of a. therefore, they are linearly dependent.

# In[43]:

a

# In[41]:

b

# In[42]:

c

# We have vectors $\mathbf{a}$, and $\mathbf{b}$
#
# $\mathbf{c}= 2*\mathbf{i} + 1*\mathbf{j}$
#
# Use the components of $\mathbf{c}$ to make a linear combination of $\mathbf{a}$ and $\mathbf{b}$

# In[47]:

c_prime = 2*a + 1*b
c_prime

# In[48]:

# c_prime has the coordinates (2,1), i.e. c, in the a,b system of coordinates, wherein a,b are the basis vectors
# c_prime has the coordinate (-3,-1), in the i,j system of coordinates

# In[50]:

2*a

# In[51]:

1*b

# In[53]:

A = [[-2,1], [1,-3]]
A = numpy.array(A)
print(A)

# In[54]:

print(c)

# In[55]:

A.dot(c)

# In[56]:

A.dot(i) #=> a

# In[58]:

A.dot(j) #=> b

# In[59]:

plot_linear_transformation(A)

# In[60]:

M = [[1,2], [2,1]]
M = numpy.array(M)
print(M)

# In[61]:

M.dot(i)

# In[62]:

M.dot(j)

# In[63]:

plot_linear_transformation(M)

# In[65]:

x = numpy.array((0.5, 1))
vectors = [x, M.dot(x)]
plot_vector(vectors)

# In[67]:

N = numpy.array([[1,2], [-1,2]])
print(N)

# In[68]:

plot_linear_transformation(N)

# In[69]:

X = numpy.array([[2,3], [1,3]])
print(X)

# In[70]:

plot_linear_transformation(X)

# In[71]:

X = numpy.array([[-1,2], [2,-1]])
print(X)

# In[72]:

plot_linear_transformation(X)

# In[74]:

rotation = numpy.array([[0, -1], [1, 0]])
print(rotation)

# In[75]:

plot_linear_transformation(rotation)

# In[77]:

shear = numpy.array([[1,1], [0, 1]])
print(shear)

# In[78]:

plot_linear_transformation(shear)

# In[79]:

scaling = numpy.array([[2, 0], [0, 0.5]])
print(scaling)

# In[80]:

plot_linear_transformation(scaling)

# Special scaling matric that leaves the unit vectors the same length:
# **identity** matrix

# In[81]:

rotation_90_clockwise = numpy.array([[0, 1], [-1, 0]])
print(rotation_90_clockwise)

# In[82]:

plot_linear_transformation(rotation_90_clockwise)

# In[83]:

print(shear@rotation)

# In[84]:

plot_linear_transformation(shear@rotation)

# In[85]:

plot_linear_transformation(rotation@shear)

# **Inverse of a matrix** undoes the effect of the linear transformation

# In[88]:

from numpy.linalg import inv

# In[89]:

M

# In[93]:

M_inv = inv(M)

# In[91]:

plot_linear_transformations(M, M_inv)

# In[ ]:


  # -*- coding: utf-8 -*-
"""
Created on Sun Aug 25 19:14:27 2019

@author: Browsing
"""
import numpy as np
import matplotlib.pyplot as plt

def f(x, y):
    return (x+20.0*y)*np.sin(x*y)
#    return 3*x

def RK2(startX , startY , endX , h , a2):
    a1 = 1.0- a2
    p1 = 0.5/a2
    q11 = 0.5/a2
    x = list()
    y = list()
    while startX <= endX:
        x.append(startX)
        y.append(startY)
        k1 = f(startX , startY)
        k2 = f(startX +p1 *h , startY + q11 * k1 * h)
        startY = startY + (a1 * k1 + a2 * k2 ) * h
        startX = startX + h
    return x,y

def Euler(startX , startY , endX , h ):
    x = list()
    y = list()
    while startX <= endX:
        x.append(startX)
        y.append(startY)
        startY = startY + f(startX , startY ) * h
        startX = startX + h
    return x,y

def Heun( startX , startY , endX , h ):
    return RK2(startX , startY , endX , h , 0.5 )

def MidPoint( startX , startY , endX , h ):
    return RK2(startX , startY , endX , h , 1.0 )

def Ralston( startX , startY , endX , h ):
    return RK2(startX , startY , endX , h , 2.0/3.0 )

def RK4(startX , startY , endX , h):
    x = list()
    y = list()
    while startX <= endX:
        x.append(startX)
        y.append(startY)
        k1 = f(startX, startY)
        k2 = f( startX + 0.5 * h , startY + 0.5 * k1 * h)
        k3 = f(startX + 0.5 * h , startY +  0.5 * k2 * h)
        k4 = f(startX + h , startY + k3* h )
        startY = startY + (k1 + 2 *k2 + 2* k3 + k4 ) * h /6.0
        startX = startX + h
    return x,y

def SubPlot1(startX , startY , endX , hs , Title , func ):

    plt.title(Title)
    for h in hs:
        x , y = func(startX , startY , endX , h)
#        plt.ylim(-200.0 , 200.0)
        plt.plot(x,y , label = "h = %f"%(h))
    plt.legend()

def SubPlot2(startX , startY , endX , h , Title ):
    plt.title(Title)
    x,y= Euler(startX , startY , endX , h )
    plt.plot( x, y , label = "Euler method")

    x,y= Heun(startX , startY , endX , h )
#    plt.ylim(-200.0 , 200.0)
    plt.plot( x, y , label = "Heun's Method")

    x,y= MidPoint(startX , startY , endX , h )
#    plt.ylim(-200.0 , 200.0)
    plt.plot( x, y , label = "Midpoint Method")

    x,y= Ralston(startX , startY , endX , h )
#    plt.ylim(-200.0 , 200.0)
    plt.plot( x, y , label = "Ralston’s Method")

    x,y= RK4(startX , startY , endX , h )
#    plt.ylim(-200.0 , 200.0)
    plt.plot( x, y , label = "4th order RK Method")

    plt.legend()

def Plot1(startX , startY , endX , hs):
    plt.figure(1,[10,10] )
    SubPlot1(startX,startY , endX , hs , "Euler Method" ,Euler )
    plt.figure(2,[10,10] )
    SubPlot1(startX,startY , endX , hs , "Heun's Method" ,Heun )
    plt.figure(3,[10,10] )
    SubPlot1(startX,startY , endX , hs , "Midpoint Method" ,MidPoint )
    plt.figure(4,[10,10] )
    SubPlot1(startX,startY , endX , hs , "Ralston’s Method" ,Ralston )
    plt.figure(5,[10,10] )
    SubPlot1(startX,startY , endX , hs , "4th order RK Method" ,RK4 )

def Plot2(startX , startY , endX , hs):

    for i in range( len(hs) ):
        plt.figure(i+6,[10, 10])
        h=hs[i]
        SubPlot2(startX , startY , endX , h , "h = %f"%(h)   )

if __name__ == '__main__':
#    print(RK2(0.0 , 1.0 , 10.0 , 0.5 , 0.5))

    Plot1(0.0 , 4.0 , 10.0 ,[0.01, 0.05, 0.1, 0.5 ])
    Plot2(0.0 , 4.0 , 10.0 ,[0.01, 0.05, 0.1, 0.5 ])

Numerical/Offline 5 on ODE/Numerical Offline RK method.py


%matplotlib inline

from matplotlib import pyplot as plt
import numpy as np

# define activation function and its derivative
def sigmoid(x):
    return 1/(1+np.exp(-x))

def sigmoid_p(x):
    return sigmoid(x)*(1-sigmoid(x))

T = np.linspace(-5,5,100)
plt.plot(T,sigmoid(T),c='r')
plt.plot(T,sigmoid_p(T),c='b')

## Assignment a)
data = [0.5, 1.5]
w1 = 1.0
w2 = 1.0
learning_rate = 1#?
b = 2.0
# calculate initial output
y = data[0]*w1 + data[1]*w2 + b
z = sigmoid(y)
z_ = 1.0
#cost function
cost = (z_-z)
# derivatives of the cost function w.r.t. weights
dcost_prediction = -1
dpred_dy = sigmoid_p(y)
dy_dw1 = data[0]
dy_dw2 = data[1]
dy_db = 1

# applying chain rule
dcost_dw1 = dcost_prediction * dpred_dy * dy_dw1
dcost_dw2 = dcost_prediction * dpred_dy * dy_dw2
dcost_db = dcost_prediction * dpred_dy * dy_db

# updating weights
w1 = w1 - learning_rate * dcost_dw1
w2 = w2 - learning_rate * dcost_dw2
b = b - learning_rate * dcost_db

#check output with new weights
y = data[0]*w1 + data[1]*w2 + b
z = sigmoid(y)
print(f"The error of the simple network a) with updated weights is:\n {str(z_-z)}")
print(f"\nThe values of the updated weights are:\n w1={str(w1)} w2={str(w2)} b={str(b)}")

##Assignment b)
data = [0.5, 1.5]
w1 = 1.0
w2 = 1.0
learning_rate = 1#?
b = 2.0
# calculate initial output
y = data[0]*w1 + data[1]*w2 + b
z = sigmoid(y)
z_ = 1.0
#cost function
cost = np.square((z_-z))/2
# derivatives of the cost function w.r.t. weights
dcost_prediction = (z_-z)
dpred_dy = sigmoid_p(y)
dy_dw1 = data[0]
dy_dw2 = data[1]
dy_db = 1

# applying chain rule
dcost_dw1 = dcost_prediction * dpred_dy * dy_dw1
dcost_dw2 = dcost_prediction * dpred_dy * dy_dw2
dcost_db = dcost_prediction * dpred_dy * dy_db

# updating weights
w1 = w1 - learning_rate * dcost_dw1
w2 = w2 - learning_rate * dcost_dw2
b = b - learning_rate * dcost_db

#check output with new weights
y = data[0]*w1 + data[1]*w2 + b
z = sigmoid(y)
print(f"The error of the simple network b) with updated weights is:\n {np.square((z_-z))/2}")
print(f"\nThe values of the updated weights are:\n w1={str(w1)} w2={str(w2)} b={str(b)}")


import numpy as np
import statistics
import matplotlib.pyplot as plt
import seaborn as sns

np_normal_dis = np.random.normal(5, 0.5, 100)
print(np_normal_dis.min())
print(np_normal_dis.max())
print(np_normal_dis.mean())
# print(np_normal_dis.median()) # not working
print(np_normal_dis.std())

two_dimension_array = np.array([(1,2,3), [4,5,6]])
print(two_dimension_array)
print('Max row:', np.amax(two_dimension_array, axis=0))
print('Max column:', np.amax(two_dimension_array, axis=1))
print('Min column:', np.amin(two_dimension_array, axis=0))
print('Min column:', np.amin(two_dimension_array, axis=1))

a = [1, 2, 3]
print('Tile:', np.tile(a, 2)) # just repeat a list
print('Repeat:', np.repeat(a, 2)) # sort repeat

print(np.random.random()) # between 0 - 1
r = np.random.random(size=[2, 3]) # 2 row 3 column
print(r)

print(np.random.choice(['a', 'e', 'i', 'o', 'u'], size=10)) # escolhe 10 vezes algo da lista
print(np.random.choice([1, 2, 3, 4, 5], size=10)) # escolhe 10 vezes algo da lista

print(np.random.rand(2,2))
print(np.random.randn(2,2))
print(np.random.randint(0, 10, size=[5,3]))

from scipy import stats

np_normal_dis = np.random.normal(5, 0.5, 1000)
print(np.max(np_normal_dis))
print(np.min(np_normal_dis))
print(np.mean(np_normal_dis))
print(np.mean(np_normal_dis))
print(np.median(np_normal_dis))
print(stats.mode(np_normal_dis))
print(np.std(np_normal_dis))

plt.hist(np_normal_dis, color='grey', bins=21)
plt.show()

24_Day_Statistics/NumPy Statistical Functions with Example.py


#This function gives the approximate derivative of a user defined function or any function from Numpy at a point x = a
#Call the function as follows: derivative(function, a, Number step size) example:print(derivative(np.sin,np.pi,0.00001))
#Number step size (h) should tend to zero for a better approximate

import numpy as np

def derivative(f,a,h):

	return (f(a + h) - f(a - h))/(2*h)


from sympy.core.numbers import igcd
from primetest import isprime
from factor_ import factorint

def totient_(n):
    """returns the number of integers less than n
    and relatively prime to n"""
    if n < 1:
        raise ValueError("n must be a positive integer")
    tot = 0
    for x in xrange(1, n):
        if igcd(x, n) == 1:
            tot += 1
    return tot

def n_order(a, n):
    """ returns the order of a modulo n
    Order of a modulo n is the smallest integer
    k such that a^k leaves a remainder of 1 with n.
    """
    if igcd(a, n) != 1:
        raise ValueError("The two numbers should be relatively prime")
    group_order = totient_(n)
    factors = factorint(group_order)
    order = 1
    if a > n:
        a = a % n
    for p, e in factors.iteritems():
        exponent = group_order
        for f in xrange(0, e + 1):
            if (a ** (exponent)) % n != 1:
                order *= p ** (e - f + 1)
                break
            exponent = exponent // p
    return order

def is_primitive_root(a, p):
    """
    returns True if a is a primitive root of p
    """
    if igcd(a, p) != 1:
        raise ValueError("The two numbers should be relatively prime")
    if a > p:
        a = a % p
    if n_order(a, p) == totient_(p):
        return True
    else:
        return False

def is_quad_residue(a, p):
    """
    returns True if a is a quadratic residue of p
    p should be a prime and a should be relatively
    prime to p
    """
    if not isprime(p) or p == 2:
        raise ValueError("p should be an odd prime")
    if igcd(a, p) != 1:
        raise ValueError("The two numbers should be relatively prime")
    if a > p:
        a = a % p

    def square_and_multiply(a, n, p):
        if n == 0:
            return 1
        elif n == 1:
            return a
        elif n % 2 == 1:
            return ((square_and_multiply(a, n // 2, p) ** 2) * a) % p
        else:
            return (square_and_multiply(a, n // 2, p) ** 2) % p

    return (square_and_multiply(a, (p - 1) // 2, p) % p) == 1

def legendre_symbol(a, p):
    """
    return 1 if a is a quadratic residue of p
    else return -1
    p should be an odd prime by definition
    """
    if not isprime(p) or p == 2:
        raise ValueError("p should be an odd prime")
    if igcd(a, p) != 1:
        raise ValueError("The two numbers should be relatively prime")
    if a > p:
        a = a % p
    if is_quad_residue(a, p):
        return 1
    else:
        return -1


import numpy as np
import sympy as sp
from sympy.core import S, Dummy
from sympy.polys.orthopolys import (legendre_poly, laguerre_poly,
                                    hermite_poly, jacobi_poly)
from sympy.polys.rootoftools import RootOf

def symbolic_gauss_legendre(n):
    """
    Computes the symbolic Gauss-Legendre quadrature points and weights.

    Parameters
    ----------
    n : Integer
        Number of integration points.
    x : Symbol
        Symbolic sysmbol.

    Returns
    -------
    xi : Abscisca
        Symbolic location of roots.
    wi : Weights
        Symbolic weights of roots.

    """
    x = Dummy("x")
    Pnx = sp.legendre(n,x)
    Pp = sp.diff(Pnx,x)
    xi = sp.solve( Pnx, x )
    wi = [ sp.simplify(2/(1 - xj**2)/(Pp.subs(x,xj))**2) for xj in xi ]
    return xi, wi

def gauss_legendre(n, n_digits):
    r"""
    Computes the Gauss-Legendre quadrature [1]_ points and weights.

    The Gauss-Legendre quadrature approximates the integral:

    .. math::
        \int_{-1}^1 f(x)\,dx \approx \sum_{i=1}^n w_i f(x_i)

    The nodes `x_i` of an order `n` quadrature rule are the roots of `P_n`
    and the weights `w_i` are given by:

    .. math::
        w_i = \frac{2}{\left(1-x_i^2\right) \left(P'_n(x_i)\right)^2}

    Parameters
    ==========

    n : the order of quadrature

    n_digits : number of significant digits of the points and weights to return

    Returns
    =======

    (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats.
             The points `x_i` and weights `w_i` are returned as ``(x, w)``
             tuple of lists.

    Examples
    ========

    >>> from sympy.integrals.quadrature import gauss_legendre
    >>> x, w = gauss_legendre(3, 5)
    >>> x
    [-0.7746, 0, 0.7746]
    >>> w
    [0.55556, 0.88889, 0.55556]
    >>> x, w = gauss_legendre(4, 5)
    >>> x
    [-0.86114, -0.33998, 0.33998, 0.86114]
    >>> w
    [0.34785, 0.65215, 0.65215, 0.34785]

    See Also
    ========

    gauss_laguerre, gauss_gen_laguerre, gauss_hermite, gauss_chebyshev_t, gauss_chebyshev_u, gauss_jacobi, gauss_lobatto

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Gaussian_quadrature
    .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/legendre_rule/legendre_rule.html
    """
    x = Dummy("x")
    p = legendre_poly(n, x, polys=True)
    pd = p.diff(x)
    xi = []
    wi = []
    for r in p.real_roots():
        if isinstance(r, RootOf):
            r = r.eval_rational(S(1)/10**(n_digits+2))
        xi.append(r.n(n_digits))
        wi.append((2/((1-r**2) * pd.subs(x, r)**2)).n(n_digits))
    return xi, wi

digits = 36
npoints = list(range(1,17))

for n in npoints:
    xi, wi = gauss_legendre(n, digits)

    print('\nN = %3d'%(n))
    for i in range(0,n):
        print('%3d %40.36f %40.36f'%(i+1,xi[i],wi[i]))


#!/usr/bin/env python3
#
# Script to test LUFactors and LUPFactors on a variety of linear systems.
#
# Daniel R. Reynolds
# SMU Mathematics
# Math 4315

# imports
import numpy
from LUFactors  import LUFactors
from LUPFactors import LUPFactors

# set matrix sizes for tests
nvals = [50, 100, 200, 400]

# set tolerance for tests
tol = numpy.sqrt(numpy.finfo(float).eps)

# tests for diagonally-dominant matrices
for n in nvals:

  print("Testing LUFactors with diagonally-dominant matrix of dimension ", n)

  # create the matrix
  A = numpy.random.rand(n,n) + n*numpy.eye(n)

  # construct and test LU factorization
  L, U = LUFactors(A)
  checks_out = True
  if ( (numpy.linalg.norm(L - numpy.tril(L))>tol) or
       (numpy.linalg.norm(numpy.diag(L)-numpy.ones([n,1])) > tol) ):
    checks_out = False
    print("  LUFactors failure: L is not unit lower-triangular")
  if (numpy.linalg.norm(U - numpy.triu(U))>tol):
    checks_out = False
    print("  LUFactors failure: U is not upper-triangular")
  if (numpy.linalg.norm(A-L@U)>tol):
    checks_out = False
    print("  LUFactors failure: A != LU")
  if (checks_out):
    print("  LUFactors passes all tests")

# tests for nonsingular (but non-diagonally-dominant) matrices
for n in nvals:

  print("Testing LUPFactors with non-diagonally-dominant matrix of dimension ", n)

  # create the matrix
  A = numpy.random.rand(n,n)
  for i in range(n):
    A[i,n-1-i] += n

  # construct and test LUP factorization
  L, U, P = LUPFactors(A)
  checks_out = True
  if ( (numpy.linalg.norm(L - numpy.tril(L))>tol) or
       (numpy.linalg.norm(numpy.diag(L)-numpy.ones([n,1])) > tol) ):
    checks_out = False
    print("  LUPFactors failure: L is not unit lower-triangular")
  if (numpy.linalg.norm(U - numpy.triu(U))>tol):
    checks_out = False
    print("  LUPFactors failure: U is not upper-triangular")
  if (numpy.linalg.norm(P.T@P - numpy.eye(n))>tol):
    checks_out = False
    print("  LUPFactors failure: P is not a permutation matrix")
  if (numpy.linalg.norm(A-P.T@L@U)>tol):
    checks_out = False
    print("  LUPFactors failure: A ~= P^T L U")
  if (checks_out):
    print("  LUPFactors passes all tests")

# ensure that singular case fails
n = 100
print("Testing with singular matrices (should fail)")
A = numpy.random.rand(n,n)
A[:,n-21] = A[:,0] - 4*A[:,9]
try:
  L, U = LUFactors(A)
except:
  print("   singularity correctly caught by LUFactors")
try:
  L, U, P = LUPFactors(A)
except:
  print("   singularity correctly caught by LUPFactors")

# end of script


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

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

'''
Interpolation is the process of
finding a value between two points on a line
or a curve.
'''

kinds = ('nearest',
         'zero',
         'linear',
         'slinear',
         'quadratic',
         'cubic')

''' x- axis values '''
x = np.array([0.0,1.0,2.0,3.0,
              4.0,5.0,6.0,7.0,
              8.0,9.0])

''' y- axis values '''
y = np.array([10,5,39,3,6,9,41,73,57,88])

''' plot original values '''
plt.plot(x, y, 'o')

''' new x-axis values to be interpolated '''
new_x = np.linspace(0.0, 9.0, 25)

''' interpolate for kind = 'nearest' '''
new_y = interpolate.interp1d(x,y,kind='nearest')(new_x)
plt.plot(new_x, new_y, color='C1', linewidth=1)

''' interpolate for kind = 'zero' '''
new_y = interpolate.interp1d(x,y,kind='zero')(new_x)
plt.plot(new_x, new_y, color='C2', linewidth=1)

''' interpolate for kind = 'linear' '''
new_y = interpolate.interp1d(x,y,kind='linear')(new_x)
plt.plot(new_x, new_y, color='C3', linewidth=1)

''' interpolate for kind = 'slinear' '''
new_y = interpolate.interp1d(x,y,kind='slinear')(new_x)
plt.plot(new_x, new_y, color='C4', linewidth=1)

''' interpolate for kind = 'cubic' '''
new_y = interpolate.interp1d(x,y,kind='cubic')(new_x)
plt.plot(new_x, new_y, color='C5', linewidth=1)

plt.legend(['original','nearest','zero',
            'linear','slinear','cubic'])
plt.show()

''' plotting all types in single graph '''
fig, axs = plt.subplots(
        nrows=len(kinds)+1,
        sharex=True)
axs[0].plot(x, y, 'bo-')
axs[0].set_title('raw')
for ax, kind in zip(axs[1:], kinds):
    new_y = interpolate.interp1d(
            x,
            y,
            kind=kind)(new_x)
    ax.plot(new_x, new_y, 'ro-')
    ax.set_title(kind)
plt.show()

''' spliners '''
''' to draw smooth curves through data points '''
from scipy.interpolate import UnivariateSpline

''' x- axis values '''
x = np.array([0.0,1.0,2.0,3.0,
              4.0,5.0,6.0,7.0,
              8.0,9.0])

''' y- axis values '''
y = np.array([10,25,39,58,16,91,41,73,57,88])
''' plot original values '''
plt.plot(x, y, 'o')

new_x = np.linspace(0.0, 9.0, 25)
new_y = interpolate.interp1d(x,y,kind='nearest')(new_x)
spl = UnivariateSpline(new_x, new_y)
spl.set_smoothing_factor(0.001)
plt.plot(new_x, spl(new_x), color='C6', linewidth=2)


import cupy as cp

class root_finders():

    def __init__(self):
        pass

    def bisection(self, fn,x0,x1,e):
        """
        Bisection Method for finding real root of nonlinear
        equation in python programming language.
        In this python program, x0 and x1 are two initial guesses,
        e is tolerable error and nonlinear function f(x) is defined
        using python function definition.

        Algorithm:

        1. start

        2. Define function f(x)

        3. Choose initial guesses x0 and x1 such that f(x0)f(x1) < 0

        4. Choose pre-specified tolerable error e.

        5. Calculate new approximated root as x2 = (x0 + x1)/2

        6. Calculate f(x0)f(x2)
            a. if f(x0)f(x2) < 0 then x0 = x0 and x1 = x2
            b. if f(x0)f(x2) > 0 then x0 = x2 and x1 = x1
            c. if f(x0)f(x2) = 0 then goto (8)

        7. if |f(x2)| > e then goto (5) otherwise goto (8)

        8. Display x2 as root.

        9. Stop
        """
        step = 1

        condition = True
        while condition:
            x2 = (x0 + x1)/2
            print('Iteration-%d, x2 = %0.6f and f(x2) = %0.6f' % (step, x2, fn(x2)))

            if fn(x0) * fn(x2) < 0:
                x1 = x2
            else:
                x0 = x2

            step = step + 1
            condition = abs(fn(x2)) > e

        print('\nRequired Root is : %0.8f' % x2)

    def newton_raphson(self,fn,d_fn,x0,e,N):
        """
        Newton Raphson method for finding real root of nonlinear function in python
        programming language.
        x0 is initial guess, e
        is tolerable error, fn(x) is non-linear function whose
        root is being obtained using Newton Raphson method.
        1. Start

        2. Define function as fn(x)

        3. Define first derivative of fn(x) as d_fn(x)

        4. Input initial guess (x0), tolerable error (e)
        and maximum iteration (N)

        5. Initialize iteration counter i = 1

        6. If d_fn(x0) = 0 then print "Mathematical Error"
        and goto (12) otherwise goto (7)

        7. Calcualte x1 = x0 - fn(x0) / d_fn(x0)

        8. Increment iteration counter i = i + 1

        9. If i >= N then print "Not Convergent"
        and goto (12) otherwise goto (10)

        10. If |fn(x1)| > e then set x0 = x1
            and goto (6) otherwise goto (11)

        11. Print root as x1

        12. Stop
        """

        step = 1
        flag = 1
        condition = True
        while condition:
            if d_fn(x0) == 0.0:
                print('Divide by zero error!')
                break

            x1 = x0 - fn(x0)/d_fn(x0)
            print('Iteration-%d, x1 = %0.6f and f(x1) = %0.6f' % (step, x1, fn(x1)))
            x0 = x1
            step = step + 1

            if step > N:
                flag = 0
                break

            condition = abs(fn(x1)) > e

        if flag==1:
            print('\nRequired root is: %0.8f' % x1)
        else:
            print('\nNot Convergent.')


# 1. Write a NumPy program to compute the multiplication of two given matrixes.
import numpy as np

p = [[1, 0], [0, 1]]
q = [[1, 2], [3, 4]]
print("original matrix:")
print(p)
print(q)
result1 = np.dot(p, q)
print("Result of the said matrix multiplication:")
print(result1)
# ----------------------------------------------------------------------------------#
# 2. Write a NumPy program to compute the outer product of two given vectors.
import numpy as np

p = [[1, 0], [0, 1]]
q = [[1, 2], [3, 4]]
print("original matrix:")
print(p)
print(q)
result = np.outer(p, q)
print("Outer product of the said two vectors:")
print(result)
# ----------------------------------------------------------------------------------#
# 3. Write a NumPy program to compute the cross product of two given vectors.
import numpy as np

p = [[1, 0], [0, 1]]
q = [[1, 2], [3, 4]]
print("original matrix:")
print(p)
print(q)
result1 = np.cross(p, q)
result2 = np.cross(q, p)
print("cross product of the said two vectors(p, q):")
print(result1)
print("cross product of the said two vectors(q, p):")
print(result2)
# ----------------------------------------------------------------------------------#
# 4. Write a NumPy program to compute the determinant of a given square array.
import numpy as np
from numpy import linalg as LA

a = np.array([[1, 0], [1, 2]])
print("Original 2-d array")
print(a)
print("Determinant of the said 2-D array:")
print(np.linalg.det(a))
# ----------------------------------------------------------------------------------#
# 5. Write a NumPy program to evaluate Einstein's summation convention of two given multidimensional arrays.
import numpy as np

a = np.array([1, 2, 3])
b = np.array([0, 1, 0])
print("Original 1-d arrays:")
print(a)
print(b)
result = np.einsum("n,n", a, b)
print("Einstein’s summation convention of the said arrays:")
print(result)
x = np.arange(9).reshape(3, 3)
y = np.arange(3, 12).reshape(3, 3)
print("Original Higher dimension:")
print(x)
print(y)
result = np.einsum("mk,kn", x, y)
print("Einstein’s summation convention of the said arrays:")
print(result)
# ----------------------------------------------------------------------------------#
# 6. Write a NumPy program to compute the inner product of vectors for 1-D arrays (without complex conjugation) and in higher dimension.
import numpy as np

a = np.array([1, 2, 5])
b = np.array([2, 1, 0])
print("Original 1-d arrays:")
print(a)
print(b)
print
result = np.inner(a, b)
print("Inner product of the said vectors:")
x = np.arange(9).reshape(3, 3)
y = np.arange(3, 12).reshape(3, 3)
print("Higher dimension arrays:")
print(x)
print(y)
result = np.inner(x, y)
print("Inner product of the said vectors:")
print(result)
# ----------------------------------------------------------------------------------#
# 7. Write a NumPy program to compute the eigenvalues and right eigenvectors of a given square array.
import numpy as np

m = np.mat("3 -2;1 0")
print("Original matrix:")
print("a\n", m)
w, v = np.linalg.eig(m)
print("Eigenvalues of the said matrix", w)
print("Eigenvectors of the said matrix", v)
# ----------------------------------------------------------------------------------#
# 8. Write a NumPy program to compute the Kronecker product of two given mulitdimension arrays.
import numpy as np

a = np.array([1, 2, 3])
b = np.array([0, 1, 0])
print("Original 1-d arrays:")
print(a)
print(b)
result = np.kron(a, b)
print("Kronecker product of the said arrays:")
print(result)
x = np.arange(9).reshape(3, 3)
y = np.arange(3, 12).reshape(3, 3)
print("Original Higher dimension:")
print(x)
print(y)
result = np.kron(x, y)
print("Kronecker product  of the said arrays:")
print(result)
# ----------------------------------------------------------------------------------#
# 9. Write a NumPy program to compute the condition number of a given matrix.
import numpy as np
from numpy import linalg as LA

a = np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]])
print("Original matrix:")
print(a)
print("The condition number of the said matrix:")
print(LA.cond(a))
# ----------------------------------------------------------------------------------#
# 10. Write a NumPy program to find a matrix or vector norm.
import numpy as np

v = np.arange(7)
result = np.linalg.norm(v)
print("Vector norm:")
print(result)
m = np.matrix('1, 2; 3, 4')
result1 = np.linalg.norm(m)
print("Matrix norm:")
print(result1)
# ----------------------------------------------------------------------------------#
# 11. Write a NumPy program to compute the determinant of an array.
import numpy as np

a = np.array([[1, 2], [3, 4]])
print("Original array:")
print(a)
result = np.linalg.det(a)
print("Determinant of the said array:")
print(result)
# ----------------------------------------------------------------------------------#
# 12. Write a NumPy program to compute the inverse of a given matrix.
import numpy as np

m = np.array([[1, 2], [3, 4]])
print("Original matrix:")
print(m)
result = np.linalg.inv(m)
print("Inverse of the said matrix:")
print(result)
# ----------------------------------------------------------------------------------#
# 13. Write a NumPy program to calculate the QR decomposition of a given matrix.
import numpy as np

m = np.array([[1, 2], [3, 4]])
print("Original matrix:")
print(m)
result = np.linalg.qr(m)
print("Decomposition of the said matrix:")
print(result)
# ----------------------------------------------------------------------------------#
# 14. Write a NumPy program to compute the condition number of a given matrix.
import numpy as np

m = np.array([[1, 2], [3, 4]])
print("Original matrix:")
print(m)
result = np.linalg.cond(m)
print("Condition number of the said matrix:")
print(result)
# ----------------------------------------------------------------------------------#
# 15. Write a NumPy program to compute the sum of the diagonal element of a given array.
import numpy as np

m = np.arange(6).reshape(2, 3)
print("Original matrix:")
print(m)
result = np.trace(m)
print("Condition number of the said matrix:")
print(result)
# ----------------------------------------------------------------------------------#
# 16. Write a NumPy program to get the lower-triangular L in the Cholesky decomposition of a given array.
import numpy as np

a = np.array([[4, 12, -16], [12, 37, -53], [-16, -53, 98]], dtype=np.int32)
print("Original array:")
print(a)
L = np.linalg.cholesky(a)
print("Lower-trianglular L in the Cholesky decomposition of the said array:")
print(L)
# ----------------------------------------------------------------------------------#
# 17. Write a NumPy program to get the qr factorization of a given array.
import numpy as np

a = np.array([[4, 12, -14], [12, 37, -53], [-14, -53, 98]], dtype=np.int32)
print("Original array:")
print(a)
q, r = np.linalg.qr(a)
print("qr factorization of the said array:")
print("q=\n", q, "\nr=\n", r)
# ----------------------------------------------------------------------------------#
# 18. Write a NumPy program to compute the factor of a given array by Singular Value Decomposition.
import numpy as np

a = np.array([[1, 0, 0, 0, 2], [0, 0, 3, 0, 0], [0, 0, 0, 0, 0], [0, 2, 0, 0, 0]], dtype=np.float32)
print("Original array:")
print(a)
U, s, V = np.linalg.svd(a, full_matrices=False)
q, r = np.linalg.qr(a)
print("Factor of a given array  by Singular Value Decomposition:")
print("U=\n", U, "\ns=\n", s, "\nV=\n", V)
# ----------------------------------------------------------------------------------#
# 19. Write a NumPy program to calculate the Frobenius norm and the condition number of a given array.
import numpy as np

a = np.arange(1, 10).reshape((3, 3))
print("Original array:")
print(a)
print("Frobenius norm and the condition number:")
print(np.linalg.norm(a, 'fro'))
print(np.linalg.cond(a, 'fro'))
# ----------------------------------------------------------------------------------#

NumPy Linear Algebra: Exercises & solutions.py


import numpy as np

# This function returns a value closer to the limit
def x2(x1): return x1 + (x1 * (10**-10))

# This function calculates de variation when we move x closer to the limit
# It is a good approximation for the derivative
def derivative(f, x): return (f(x2(x))-f(x))/(x2(x)-x)

# Sample and test data

x = np.array([1, 2, 3, 4, 5, 6, 7, 9, 10])

# Test the derivative for x**2
print("Testing x**2 ...")
func = derivative(lambda x: x**2, x)
_derivative = np.round(func, 3)
print(_derivative)
error = (np.sum(np.round(func - 2*x, 3)))
print("PASSED:", error == 0.0, "\n")

# Test the derivative for x**3
print("Testing x**3 ...")
func = derivative(lambda x: x**3, x)
_derivative = np.round(func, 3)
print(_derivative)
error = (np.sum(np.round(func - 3*x**2, 3)))
print("PASSED:", error == 0.0, "\n")

# Test the derivative for sin(x)
print("Testing sin(x) ...")
func = derivative(lambda x: np.sin(x), x)
_derivative = np.round(func, 3)
print(_derivative)
error = (np.sum(np.round(derivative(lambda x: np.sin(x), x) - np.cos(x), 3)))
print("PASSED:", error == 0.0, "\n")


# -*- coding: utf-8 -*-
"""
Created on Sun Jan 16 14:53:16 2022

"""
import numpy as np
from MyQR import QR_householder
from Mychol import mychol

def ls_with_qr(C,b):
    A = C.copy()
    m = A.shape[0]
    n = A.shape[1]

    [Q,R,rank] = QR_householder(A)

    b = np.matmul(Q.T,b)
    min_dim = min(m,n)

    if rank < min_dim: # rank A < min_dim; Minimum norm solution is needed
        ReducedR = R[0:rank,:]
        L = mychol(np.matmul(ReducedR,ReducedR.T)) # L is a lower triangular matrix such that LL' = reducedA

        w = forward(L,b[:rank]) # solve L w = b;; note that b is Q'b.

        z = backward(L.T,w)  # solve L'z = w;
        x = np.matmul(ReducedR.T,z)

    else:      # R is nonsingular and square
        x = backward(R,b[:n])
    return(x)

def forward(A,b):
    n = A.shape[1]
    bn = b.shape[0]
    if n != bn:
        print("Number of rows of A  and b must be equal and A must be square");

    x = np.zeros((n,1))

    x[0] = b[0]/A[0,0]

    for i in range(1,n):
        sum = np.matmul(A[i,:i],x[:i])
        x[i] = (b[i] - sum)/A[i,i]

    return x

def backward(A,b):

    n = A.shape[1]
    bn = b.shape[0]
    if n != bn:
        print("Number of rows of A  and b must be equal and A must be square")

    x = np.zeros((n,1))

    x[n-1] = b[n-1]/A[n-1,n-1]

    for i in range(n-2,-1,-1):
        sum = np.matmul(A[i,i+1:n],x[i+1:n])
        x[i] = (b[i] - sum)/A[i,i]
    return x

'''
The main code
'''
def main():
    # A = np.array([[1.,2.,3.],[4.,5.,6.],[7.,8.,9.],[10.,11.,12.]])
    # print(A)
    # b = np.reshape(np.array([-1.,-5.,2.,1.]),(4,1))
    # print(b)
    # x = ls_with_qr(A,b)
    # print(x)
    A = np.array([[1.8162 ,   0.7361 ,  -1.6029],
                 [1.7961,    0.3619 ,  -0.6157],
                 [1.4627 ,  -0.0455  ,  0.3997],
                 [4.8991  ,  1.1483  , -2.1063]])
    print(A)
#    b = np.reshape(np.array([-1.,-5.,2.,1.]),(4,1))
    b = np.reshape(np.array([-0.0301, -0.1649, 0.6277, 1.0933]),(4,1))
    print(b)
    x = ls_with_qr(A,b)
    print(x)
if __name__ == "__main__":  ## This command executes the main function
    main()


import numpy as np

def riemann(func,a,b,n=10):
    """A simple Riemann-sum approximation to the integral of a function

Parameters
----------
func: callable
    Function to integrate, should be a function of one parameter
a: float
    Lower limit of the integration range
b: float
    Upper limit of the integration range
n: int, optional
    Number of intervals to split [a,b] into for the Riemann sum

Returns
-------
float
    Integral of func(x) over [a,b]
"""
    return np.sum(func(np.linspace(a,b,n))*(b-a)/n)

def simps(func,a,b,n=10):
    """Integrate a function using Simpson's rule

Parameters
----------
func: callable
    Function to integrate, should be a function of one parameter
a: float
    Lower limit of the integration range
b: float
    Upper limit of the integration range
n: int, optional
    Number of major intervals to split [a,b] into for the Simpson rule

Returns
-------
float
    Integral of func(x) over [a,b]

Notes
-----
Applies Simpson's rule as

.. math::

    \\int_a^b \\mathrm{d}x f(x) \\approx \\frac{(b-a)}{6n}\\,\\left[f(a)+4f(a+h/2)+2f(a+h)+4f(a+3h/2)+

    \ldots+2f(b-h)+4f(b-h/2)+f(b)\\right]

See Also
--------
exampy.integrate.riemann: Integrate a function with a simple Riemann sum
"""
    try:
        return (2.*np.sum(func(np.linspace(a,b,n+1)))
                -func(a)-func(b) # adjust double-counted first and last
                +4.*np.sum(func(np.linspace(a+(b-a)/n/2,b-(b-a)/n/2,n))))\
                *(b-a)/n/6.
    except TypeError:
        raise TypeError("Provided func needs to be callable on arrays of inputs")


from sklearn import svm
import numpy as np

classification_vector= np.array([-1,
                                 -1,
                                 -1,
                                 -1,
                                 -1,
                                  1,
                                  1,
                                  1,
                                  1,
                                  1])

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

clf= svm.LinearSVC(C=1, loss='hinge', penalty='l2')
clf2= svm.LinearSVC()

clf.fit(data_vector, classification_vector)

clf.coef_
clf.intercept_
clf.get_params

## Using the parameters theta_0 and theta corresponding to the maximun margin separator,
#What is the sum of the hinge loses

def hinge_loss_full(feature_matrix, labels, theta, theta_0):
    total_loss=[]
    for i, x in enumerate(feature_matrix):
        if (labels[i]*(np.dot(theta, feature_matrix[i]) + theta_0)) >= 1:
            loss= 0
        else:
            loss= 1 - (labels[i]*(np.dot(theta, feature_matrix[i])+ theta_0))
        total_loss.append(loss)
    return sum(total_loss)

hinge_loss_full(data_vector, classification_vector, clf.coef_, clf.intercept_ )

##Dividing theta and theta 0 by two
hinge_loss_full(data_vector, classification_vector, (clf.coef_/2), (clf.intercept_/2))


import random
import numpy as np
from statistics import mean

# Using "random" to create a dataset
def create_dataset(hm, variance, step=2, correlation=False):
    val = 1
    sy = []
    for i in range(hm):
        y = val + random.randrange(-variance, variance)
        sy.append(y)
        if correlation and correlation == 'pos':
            val += step
        elif correlation and correlation == 'neg':
            val -= step

    sx = [i for i in range(len(sy))]

    return np.array(sx, dtype=np.float64), np.array(sy, dtype=np.float64)

# finding best fit slope and intercept of dataset
def best_fit_slope_and_intercept(sx, sy):
    m = (((mean(sx) * mean(sy)) - mean(sx * sy)) /
         ((mean(sx) * mean(sx)) - mean(sx * sx)))

    b = mean(sy) - m * mean(sx)

    return m, b

# dataset - coefficient of determination
def coefficient_of_determination(sy_orig, sy_line):
    y_mean_line = [mean(sy_orig) for y in sy_orig]

    squared_error_regr = sum((sy_line - sy_orig) * (sy_line - sy_orig))
    squared_error_y_mean = sum((y_mean_line - sy_orig) * (y_mean_line - sy_orig))

    print(squared_error_regr)
    print(squared_error_y_mean)

    r_squared = 1 - (squared_error_regr / squared_error_y_mean)

    return r_squared

def get_result():
    sx, sy = create_dataset(20, 10, 3)
    m, b = best_fit_slope_and_intercept(sx, sy)
    regression_line = [(m * x) + b for x in sx]
    r_squared = coefficient_of_determination(sy, regression_line)
    print('Rsquared result: ')
    return r_squared


from sympy.abc import x
import sympy as sp
import math

def improved_Newton_iteration(x0, func, tol=1e-9, Max_iter=100):
    """ Solve non-linear equation by improved Newton iteration method.

    Args:
        x0: double, iteration initial value
        func: function object, the function to be solved
        tol: double, iteration accuracy
        Max_iter: int, maximum iteration number

    Returns:
        k: int, iteration number
        z: double, root of the non-linear equation
    """
    # derivative
    y_diff = sp.diff(func(x))

    # first iteration
    k = 1
    u = x0
    v = u - func(u) / y_diff.subs(x, u)
    z = u - 2 * func(u) / (y_diff.subs(x, u) + y_diff.subs(x, v))

    # iteration
    while math.fabs(z - u) >= tol and y_diff.subs(x, z) != 0 and k < Max_iter:
        k += 1
        u = z
        v = u - func(u) / y_diff.subs(x, u)
        z = u - 2 * func(u) / (y_diff.subs(x, u) + y_diff.subs(x, v))

    return k, z

def f(x):
    return sp.Pow(x, 3) + sp.Pow(x, 2) - 3 * x - 3

if __name__ == '__main__':
    n, root = improved_Newton_iteration(2.0, f, 1e-6)
    print(f"The root of the non-linear equation is {root:.7f} by improved Newton iteration method.")
    print(f"Iteration number is {n}")


"""
# !/usr/bin/env python
# -*- coding: utf-8 -*-
@Time        : 2022/3/26 18:13
@File        : softmax.py
"""

# Solution is available in the other "solution.py" tab
import numpy as np

def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    # TODO: Compute and return softmax(x)

    return np.exp(x) / np.sum(np.exp(x), axis=0)
    # x = np.divide(np.exp(x), np.sum(np.exp(x)))
    # return x

logits = [3.0, 1.0, 0.2]
print(softmax(logits))
# [0.8360188  0.11314284 0.05083836]
# [0.8360188  0.11314284 0.05083836]

udacity-program_self_driving_car_engineer_v1.0/part01-computer vision and deep learning/module03-deep learning/lesson03-introduction to tensorflow/exercise17-softmax/softmax.py


import numpy as np
import math
from copy import deepcopy

def ReadMatrixAndVec():
    matrixSize = int(input())

    matrix = [list(map(float, input().split())) for i in range(matrixSize)]
    vec = list(map(float, input().split()))

    return matrix, vec

def swapLines(matrix, idxFrom, idxTo):
    matrix[idxFrom], matrix[idxTo] = matrix[idxTo], matrix[idxFrom]

def pivot(matrix):
    p = []
    mtrx = matrix.copy()
    for j in range(len(mtrx)):
        tmp = [(mtrx[i][j], i) for i in range(j, len(mtrx))]
        idx = max(tmp, key=lambda x: abs(x[0]))[1]
        if idx != j:
            p.append((j, idx))
            swapLines(mtrx, j, idx)
    return p, mtrx

def LUDecomp(matrix):
    shape = len(matrix)
    lower = np.zeros((shape, shape))
    upper = np.zeros((shape, shape))
    pivo, pMtx = pivot(matrix)

    for i in range(shape):
        lower[i][i] = 1.0

        for j in range(i + 1):
            upper[j][i] = pMtx[j][i] - math.fsum([lower[j][k] * upper[k][i] for k in range(j)])
        for j in range(i, shape):
            lower[j][i] = (pMtx[j][i] - math.fsum([lower[j][k] * upper[k][i] for k in range(i)])) / upper[i][i]

    return lower, upper, pivo

def DirectSubstit(lowerMatrix, valVec):
    res = deepcopy(valVec)

    for i in range(1, len(valVec)):
        res[i] = valVec[i] - math.fsum([lowerMatrix[i][j] * res[j] for j in range(i)])

    return res

def InverseSubstit(upperMatrix, valVec):
    res = [0 for i in range(len(valVec))]
    res[-1] = valVec[-1] / upperMatrix[-1][-1]

    for i in reversed(range(len(valVec) - 1)):
        res[i] = (valVec[i] - math.fsum([upperMatrix[i][j] * res[j] for j in range(i + 1, len(valVec))])) / upperMatrix[i][i]

    return res

def pivotVec(vec, pivo):
    res = vec.copy()

    for i in range(len(pivo)):
        fromIdx, toIdx = pivo[i]
        swapLines(res, fromIdx, toIdx)

    return res

def SolveSLAU(matrix, valVec, isSingle):
    lower, upper, pivo = LUDecomp(matrix)

    if isSingle:
        print("Lower:")
        print(lower)
        print("Upper:")
        print(upper)

    pVec = pivotVec(valVec, pivo)

    if isSingle:
        print("Pivoted vector:")
        print(pVec)

    rhsVec = DirectSubstit(lower, pVec)

    if isSingle:
        print("rhsVec:")
        print(rhsVec)

    solVec = InverseSubstit(upper, rhsVec)

    return solVec

def InverseMtx(matrix):
    res = [[] for i in range(len(matrix))]
    eVec = [0.0 for i in range(len(matrix))]

    for i in range(len(matrix)):
        eVec[i] = 1.0
        tmp = SolveSLAU(matrix, eVec, False)
        for j in range(len(tmp)):
            res[j].append(tmp[j])
        eVec[i] = 0

    return res

def submatrix(M, c):
    B = [[1] * len(M) for i in range(len(M))]

    for l in range(len(M)):
        for k in range(len(M)):
            B[l][k] = M[l][k]

    B.pop(0)

    for i in range(len(B)):
        B[i].pop(c)
    return B

def det(mtx):
    res = 0

    if len(mtx) <= 2:
        return mtx[0][0] * mtx[1][1] - mtx[0][1] * mtx[1][0]
    else:
        for i in range(len(mtx)):
            res += ((-1) ** (i)) * mtx[0][i] * det(submatrix(mtx, i))

    return res

if __name__ == "__main__":
    matrix, vec = ReadMatrixAndVec()
    print("Orig Matrix:")
    print(matrix)
    print("valVec:")
    print(vec)

    result = np.matrix(SolveSLAU(matrix, vec, True)).transpose()
    npMtx = np.matrix(matrix)
    npVec = np.matrix(vec).transpose()

    print("Result:")
    print(result)
    print("matrix determinant:")
    print(det(matrix))
    print("matrix inverse:")
    print(np.matrix(InverseMtx(matrix)))


import numpy as np
import numpy.linalg as linalg
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.stats import chi2

x, y = np.random.normal(size=(2, 100))
z = x + y + np.random.normal(size=(1, 100))
points = np.array([x, y, z.squeeze()])

A = np.cov(points)

center = points.mean(axis=1)

# your ellispsoid and center in matrix form
# A = np.array([[10,0,0],[0,2,0],[0,0,2]])

# A = np.array([[ 0.00009, -0.00024, -0.00014],
#        [-0.00024,  0.00095,  0.00068],
#        [-0.00014,  0.00068,  0.00063]])

# center = [0,0,0]

# find the rotation matrix and radii of the axes
U, s, rotation = linalg.svd(A)
radii = np.sqrt(s)

# multiply sizes by desired percentile from chi square dist
chi_dist = chi2(3)
mag = np.sqrt(chi_dist.ppf(.95))
radii = radii * mag

# todo no scaling based on confidence interval yet -- test this but with points and confidence intervals
# see https://www.mathworks.com/matlabcentral/fileexchange/4705-error_ellipse

# now carry on with EOL's answer
u = np.linspace(0.0, 2.0 * np.pi, 100)
v = np.linspace(0.0, np.pi, 100)
x = radii[0] * np.outer(np.cos(u), np.sin(v))
y = radii[1] * np.outer(np.sin(u), np.sin(v))
z = radii[2] * np.outer(np.ones_like(u), np.cos(v))
for i in range(len(x)):
    for j in range(len(x)):
        [x[i,j],y[i,j],z[i,j]] = np.dot([x[i,j],y[i,j],z[i,j]], rotation) + center

# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.scatter(points[0], points[1], points[2])

ax.plot_wireframe(x, y, z,  rstride=1, cstride=1, color='b', alpha=0.2)

# lim = 2
# ax.set_xlim([-lim, lim])
# ax.set_ylim([-lim, lim])
# ax.set_zlim([-lim, lim])

# with points
p = points
ax.set_xlim([p[0].min() - .1, p[0].max() + .1])
ax.set_ylim([p[1].min() - .1, p[1].max() + .1])
ax.set_zlim([p[2].min() - .1, p[2].max() + .1])

plt.show()
plt.close(fig)
del fig


import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

def helper_best_rank_k_approximation(input_matrix, k, print_result=False):
    """
    Helper function for best_rank_k_approximation.
    Performs SVD on a given matrix and creates lower (k) dim matrix.

    Args:
    input_matrix (np.ndarray): a matrix representing one of r/g/b image
    k (int): the desired rank of out_matrix
    print_result (bool): default False

    Returns:
        out_matrix
    """
    matrix = np.array(input_matrix)
    u, singular_values, v = np.linalg.svd(matrix, full_matrices=False)
    rows = np.ma.size(matrix, 0)
    columns = np.ma.size(matrix, 1)
    out_matrix = np.zeros((rows, columns))

    for i in range(k):
        temp = (singular_values[i]*u[:, i:i+1])
        out_matrix += (np.matmul(temp, v[i:i+1, :]))

    singular_values_squared = singular_values*singular_values
    calculated_rate = sum(singular_values_squared[k:]) / sum(singular_values_squared)

    if print_result:
        print("When k is ", k, ", error of red matrix is ", calculated_rate)

    return out_matrix, calculated_rate

def best_rank_k_approximation(image_name, k_list):
    """
    Creates 'compressed' images of lower resolution from a given image.
    Each k value in k_list will create a save a new image with corresponding name.
    Args:
    image_name (str): file location, assumes location same folder.
    k_list (list): the desired k values to be used.
    """
    image = Image.open(image_name)
    b, g, r = image.split()
    rates_b = []
    rates_r = []
    rates_g = []

    for k_value in k_list:
        r_new, rate_r = helper_best_rank_k_approximation(r, k_value, print_result=True)
        g_new, rate_g = helper_best_rank_k_approximation(g, k_value)
        b_new, rate_b = helper_best_rank_k_approximation(b, k_value)
        image_r_new = Image.fromarray(r_new.clip(0, 255).astype('uint8'))
        image_g_new = Image.fromarray(g_new.clip(0, 255).astype('uint8'))
        image_b_new = Image.fromarray(b_new.clip(0, 255).astype('uint8'))
        image_new = Image.merge("RGB", (image_b_new, image_g_new, image_r_new))
        image_new.save(str(k_value)+"_approximation_"+image_name)
        rates_b.append(rate_b*100)
        rates_g.append(rate_g*100)
        rates_r.append(rate_r*100)

    plt.plot(k_list, rates_b, 'b', k_list, rates_r, 'r', k_list, rates_g, 'g')
    plt.xlabel('k (values)')
    plt.ylabel('Error Rate (percentage)')
    plt.title('Best Rank K Approximation')
    plt.show()

def main():
    k = list(range(5, 165, 5))
    best_rank_k_approximation("cute_dog.jpg", k)

main()


# sample 2d objective function
from numpy import arange
from numpy import meshgrid

# objective function
def objective(x, y):
	return x**2.0 + y**2.0

# define range for input
r_min, r_max = -5.0, 5.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# summarize some of the input domain
print(x[:5, :5])
# compute targets
results = objective(x, y)
# summarize some of the results
print(results[:5, :5])
# create a mapping of some inputs to some results
for i in range(5):
	print('f(%.3f, %.3f) = %.3f' % (x[i,0], y[i,0], results[i,0]))


'''quadrature.py
Author: Huaiyu Duan (UNM)
Description: Python module that define the mesh and weights of various quadrature rules.
'''
import numpy as np

RULES = {} # dictionary of the quadrature rules

def mesh(a, b, n, rule='midpoint'):
    '''Compute the abscissas x[n] and weights w[n] using n nodes and the specified rule.
    The sum of f(x[i]) * w[i] gives an approximation of integral of f(x) for x from a to b.
    a : lower limit of the integral
    b : upper limit of the integral
    n : number of nodes
    rule : name of the quadrature rule

    return : x[n], w[n]
    '''
    assert rule in RULES, f"Unknown quadrature rule '{rule}''. The available choices are {list(RULES)}."
    return RULES[rule](a, b, n)

def _midpoint(a, b, n):
    '''Compute the mesh points x[n] and weights w[n] using the (composite) midpoint rule.
    The sum of f(x[i]) * w[i] gives an approximation of integral of f(x) for x from a to b.
    a : lower limit of the integral
    b : upper limit of the integral
    n : number of points

    return : x[n], w[n]
    '''
    n = int(n)
    assert n >= 1, "Number of abscissas should be at least 1 for the midpoint rule."
    dx = (b - a) / n # mesh interval
    x = a + (np.arange(n) + 0.5) * dx
    w = np.ones_like(x) * dx
    return x, w
RULES['midpoint'] = _midpoint

def _trapezoid(a, b, n):
    '''Compute the mesh points x[n] and weights w[n] using the (composite) trapezoid rule.
    The sum of f(x[i]) * w[i] gives an approximation of integral of f(x) for x from a to b.
    a : lower limit of the integral
    b : upper limit of the integral
    n : number of points

    return : x[n], w[n]
    '''
    n = int(n)
    assert n >= 2, "Number of abscissas should be at least 2 for the trapezoid rule."
    x = np.linspace(a, b, n)
    w = np.ones_like(x) * (x[1] - x[0])
    w[0] *= 0.5; w[-1] *= 0.5
    return x, w
RULES['trapezoid'] = _trapezoid

def _simpson(a, b, n):
    '''Compute the mesh points x[n] and weights w[n] using the (composite) Simpson's rule.
    The sum of f(x[i]) * w[i] gives an approximation of integral of f(x) for x from a to b.
    a : lower limit of the integral
    b : upper limit of the integral
    n : number of points

    return : x[n], w[n]
    '''
    n = int(n)
    assert n >= 3 and n%2 ,  "The number of abscissas should be an odd number larger than or equal to 3 for Simpson's rule."
    x = np.linspace(a, b, n)
    w = np.ones_like(x) * (x[1] - x[0])
    w[0] *= 1/3; w[-1] *= 1/3
    w[1:-1:2] *= 4/3; w[2:-2:2] *= 2/3
    return x, w
RULES['simpson'] = _simpson

def _simpson2(a, b, n):
    '''Compute the mesh points x[n] and weights w[n] using an alternative Simpson's rule that works better for functions with narrow peaks.
    The sum of f(x[i]) * w[i] gives an approximation of integral of f(x) for x from a to b.
    a : lower limit of the integral
    b : upper limit of the integral
    n : number of points

    return : x[n], w[n]
    '''
    n = int(n)
    assert n >= 6,  "The number of abscissas should be at least 6 for the alternative Simpson's rule."
    x = np.linspace(a, b, n)
    w = np.ones_like(x) * (x[1] - x[0])
    w[0] *= 9/24; w[-1] *= 9/24
    w[1] *= 28/24; w[-2] *= 28/24
    w[2] *= 23/24; w[-3] *= 23/24
    return x, w
RULES['simpson2'] = _simpson2

def _chebyshev(a, b, n):
    '''Compute the mesh points x[n] and weights w[n] using the Chebyshev-Gaussian quadrature.
    The sum of f(x[i]) * w[i] gives an approximation of integral of f(x) for x from a to b.
    a : lower limit of the integral
    b : upper limit of the integral
    n : number of points

    return : x[n], w[n]
    '''
    from scipy.special import roots_chebyt
    y, w = roots_chebyt(n)
    x = (b-a)*0.5*np.array(y) + (b+a)*0.5 # transform from [-1, 1] to [a, b]
    w *= 0.5 * (b - a) * np.sqrt(1 - y**2)
    return x, w
#     # y = [np.cos(np.pi*(j+0.5)/n) for j in range(n)] # Chebyshev nodes
#     # x = (b-a)*0.5*np.array(y) + (b+a)*0.5 # transform from [-1, 1] to [a, b]
#     # w = [np.sqrt(1-y[j]**2) for j in range(n)]
#     # w = 0.5*(b-a)*(np.pi/n)*np.array(w) # weights
#     # return x, w
RULES['chebyshev'] = _chebyshev

def _legendre(a, b, n):
    '''Compute the mesh points x[n] and weights w[n] using the Legendre-Gaussian quadrature.
    The sum of f(x[i]) * w[i] gives an approximation of integral of f(x) for x from a to b.
    a : lower limit of the integral
    b : upper limit of the integral
    n : number of points

    return : x[n], w[n]
    '''
    from scipy.special import roots_legendre
    y, w = roots_legendre(n)
    x = (b-a)*0.5*np.array(y) + (b+a)*0.5 # transform from [-1, 1] to [a, b]
    w *= 0.5 * (b - a)
    return x, w
RULES['legendre'] = _legendre


"""
This script demonstrates the implementation of the Sigmoid function.

The function takes a vector of K real numbers as input and then 1 / (1 + exp(-x)).
After through Sigmoid, the element of the vector mostly 0 between 1. or 1 between -1.

Script inspired from its corresponding Wikipedia article
https://en.wikipedia.org/wiki/Sigmoid_function
"""

import numpy as np

def sigmoid(vector: np.array) -> np.array:
    """
    Implements the sigmoid function

    Parameters:
        vector (np.array): A  numpy array of shape (1,n)
        consisting of real values

    Returns:
        sigmoid_vec (np.array): The input numpy array, after applying
        sigmoid.

    Examples:
    >>> sigmoid(np.array([-1.0, 1.0, 2.0]))
    array([0.26894142, 0.73105858, 0.88079708])

    >>> sigmoid(np.array([0.0]))
    array([0.5])
    """
    return 1 / (1 + np.exp(-vector))

if __name__ == "__main__":
    import doctest

    doctest.testmod()


import numpy as np

class Regularizations:

    @staticmethod
    def lasso_l1(w,lambda_):
        """Computes lasso regularization

        Args:
            w (np.ndarray): matrix of layer's weights
            lambda_ (float): regularization hyperparameter

        Returns:
            np.float64: value of regularization/penalty term
        """
        return lambda_ * np.linalg.norm(w,1)

    @staticmethod
    def lasso_l1_der(w,lambda_):
        """Computes lasso regularization's derivative

        Args:
            w (np.ndarray): matrix of layer's weights
            lambda_ (float): regularization hyperparameter

        Returns:
            np.ndarray: derivative of lasso regularization
        """
        return lambda_ * np.sign(w)

    @staticmethod
    def ridge_regression_l2(w,lambda_):
        """Computes ridge regression

        Args:
            w (np.ndarray): matrix of layer's weights
            lambda_ (float): regularization hyperparameter

        Returns:
            np.float64: value of regularization/penalty term
        """
        return 1/2 * lambda_ * np.linalg.norm(w,2) * np.linalg.norm(w,2)

    @staticmethod
    def ridge_regression_l2_der(w,lambda_):
        """Computes ridge regression's derivative

        Args:
            w (np.ndarray): matrix of layer's weights
            lambda_ (float): regularization hyperparameter

        Returns:
            np.ndarray: derivative of ridge regression
        """
        return lambda_ * w

    @staticmethod
    def init_regularization(name):
        if name == "lasso":
            return Regularizations.lasso_l1, Regularizations.lasso_l1_der, name
        elif name == "ridge_regression":
            return Regularizations.ridge_regression_l2, Regularizations.ridge_regression_l2_der, name
        else:
            raise NameError(name+ " is not recognized! Check for correct names and possible regularizations in init_regularization!")


# model fitting problem with logistic loss and L1 regularization.
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import cvxpy as cp

# In this example, we use CVXPY to train a logistic regression classifier with ℓ1 regularization. We are given data (xi,yi), i=1,…,m. The xi∈Rn are feature vectors, while the yi∈{0,1} are associated boolean classes.
# Our goal is to construct a linear classifier ŷ =𝟙[βTx>0], which is 1 when βTx is positive and 0 otherwise. We model the posterior probabilities of the classes given the data linearly, with
# logPr(Y=1∣X=x)Pr(Y=0∣X=x)=βTx.
# This implies that
# Pr(Y=1∣X=x)=exp(βTx)1+exp(βTx),Pr(Y=0∣X=x)=11+exp(βTx).
# We fit β by maximizing the log-likelihood of the data, plus a regularization term λ‖β‖1 with λ>0:
# ℓ(β)=∑i=1myiβTxi−log(1+exp(βTxi))−λ‖β‖1.
# Because ℓ is a concave function of β, this is a convex optimization problem.

# Construct Z given X.
def pairs(Z):
    m, n = Z.shape
    k = n * (n + 1) // 2
    X = np.zeros((m, k))
    count = 0
    for i in range(n):
        for j in range(i, n):
            X[:, count] = Z[:, i] * Z[:, j]
            count += 1
    return X

# Generate data for logistic model fitting problem.
np.random.seed(1)
n = 10
k = n * (n + 1) // 2
m = 200
TEST = 100
sigma = 1.9
DENSITY = 1.0
theta_true = np.random.randn(n, 1)

idxs = np.random.choice(range(n), int((1 - DENSITY) * n), replace=False)
for idx in idxs:
    beta_true[idx] = 0

Z = np.random.binomial(1, 0.5, size=(m, n))
Y = np.sign(Z.dot(theta_true) + np.random.normal(0, sigma, size=(m, 1)))
X = pairs(Z)
X = np.hstack([X, np.ones((m, 1))])
Z_test = np.random.binomial(1, 0.5, size=(TEST, n))
Y_test = np.sign(Z_test.dot(theta_true) + np.random.normal(0, sigma, size=(TEST, 1)))
X_test = pairs(Z_test)
X_test = np.hstack([X_test, np.ones((TEST, 1))])

theta = cp.Variable((k + 1, 1))
lambd = cp.Parameter(nonneg=True)
loss = cp.sum(
    cp.log_sum_exp(cp.hstack([np.zeros((m, 1)), -cp.multiply(Y, X @ theta)]), axis=1)
)
reg = cp.norm(theta[:k], 1)
prob = cp.Problem(cp.Minimize(loss / m + lambd * reg))
# Compute a trade-off curve and record train and test error.
TRIALS = 100
train_error = np.zeros(TRIALS)
test_error = np.zeros(TRIALS)
lambda_vals = np.logspace(-4, 0, TRIALS)
for i in range(TRIALS):
    lambd.value = lambda_vals[i]
    prob.solve(solver=cp.SCS)
    train_error[i] = (
        np.sign(Z.dot(theta_true)) != np.sign(X.dot(theta.value))
    ).sum() / m
    test_error[i] = (
        np.sign(Z_test.dot(theta_true)) != np.sign(X_test.dot(theta.value))
    ).sum() / TEST
# Plot the train and test error over the trade-off curve.

plt.plot(lambda_vals, train_error, label="Train error")
plt.plot(lambda_vals, test_error, label="Test error")
plt.xscale("log")
plt.legend(loc="upper left")
plt.xlabel(r"$\lambda$", fontsize=16)
plt.show()

# Below we plot |θk|, k=1,…,55, for the λ that minimized the test error. Each |θk| is placed at position (i,j) where zizj=xk. Notice that many θk are 0, as we would expect with ℓ1 regularization.

# Solve model fitting problem with the lambda that minimizes test error.
idx = np.argmin(test_error)
lambd.value = lambda_vals[idx]
prob.solve(solver=cp.SCS)

# Plot the absolute value of the entries in theta corresponding to each feature.
P = np.zeros((n, n))
count = 0
for i in range(n):
    for j in range(i, n):
        P[i, j] = np.abs(theta.value[count])
        count += 1
row_labels = range(1, n + 1)
column_labels = range(1, n + 1)

fig, ax = plt.subplots()
heatmap = ax.pcolor(P, cmap=plt.cm.Blues)

# put the major ticks at the middle of each cell
ax.set_xticks(np.arange(P.shape[1]) + 0.5, minor=False)
ax.set_yticks(np.arange(P.shape[0]) + 0.5, minor=False)

# want a more natural, table-like display
ax.invert_yaxis()
ax.xaxis.tick_top()

ax.set_xticklabels(column_labels, minor=False)
ax.set_yticklabels(row_labels, minor=False)

plt.xlabel(r"$z_i$", fontsize=16)
ax.xaxis.set_label_position("top")
plt.ylabel(r"$z_j$", fontsize=16)
plt.show()


import numpy as np
import math
import scipy
from sklearn.metrics import mean_squared_log_error

def rrse_(y_true, y_pred):
  return np.sqrt(np.sum((y_true - y_pred) ** 2) / np.sum((y_true - np.mean(y_true)) ** 2))

def CORR(y_true, y_pred):
  N = y_true.shape[0]
  total = 0.0
  for i in range(N):
    if math.isnan(scipy.stats.pearsonr(y_true[i], y_pred[i])[0]):
      N -= 1
    else:
      total += scipy.stats.pearsonr(y_true[i], y_pred[i])[0]
  return total / N

def mean_absolute_percentage_error(y_true, y_pred):
  """
  Use of this metric is not recommended; for illustration only.
  See other regression metrics on sklearn docs:
    http://scikit-learn.org/stable/modules/classes.html#regression-metrics
  Use like any other metric
  >>> y_true = [3, -0.5, 2, 7]; y_pred = [2.5, -0.3, 2, 8]
  >>> mean_absolute_percentage_error(y_true, y_pred)
  Out[]: 24.791666666666668
  """

  # y_true, y_pred = check_arrays(y_true, y_pred)

  ## Note: does not handle mix 1d representation
  # if _is_1d(y_true):
  #    y_true, y_pred = _check_1d_array(y_true, y_pred)

  return np.mean(np.abs((y_true - y_pred) / y_true)) * 100

def root_relative_squared_error(y_true, y_pred):
  mn = np.mean(y_true)
  return np.sqrt(np.average((y_true - y_pred) ** 2)) / np.sqrt(np.average((y_true - mn) ** 2))

# Root Mean Squared Logarithmic Error (RMSLE)
# def rmsle(y, y0):
#   return np.sqrt(np.mean(np.square(np.log1p(y) - np.log1p(y0))))
def RMSLE(y_true, y_pred):
  return np.sqrt(mean_squared_log_error(y_true,y_pred))


# ==============================================================================
# Author: Eddie Guo
# Date: November 6, 2021
#
# Author's notes:
# You may share and modify (and do whatever you want) with this code.
# I hope this helps you with your assignments :)
#
# This script creates a linear regression model and plots the output.
# ==============================================================================

import matplotlib.pyplot as plt  # for plotting
import numpy as np               # for arrays
import pandas as pd              # for data manipulation

# Ordinary Least Squares (OLS): y = A + Bx
def Delta(x):
	# N \sum x^2 - \left( \sum x \right)^2
	return len(x) * np.sum(x**2) - np.sum(x)**2

def A(x, y):
	# A = \frac{(\sum x^2 \sum y - \sum x \sum xy)}{\Delta}
	return (np.sum(x**2)*np.sum(y)-np.sum(x)*np.sum(x*y)) / Delta(x)

def B(x, y):
	# B = \frac{N \sum xy - \sum x \sum y}{\Delta}
	return (len(x)*np.sum(x*y)-np.sum(x)*np.sum(y)) / Delta(x)

# Step 1. Read in the data.
df = pd.read_csv('cheese_and_deaths.csv')
print(df.head())

# Step 2. Visualize the data and verify a linear relationship.
plt.scatter(df['year'], df['deaths'])
plt.show()

# Step 3. Create the OLS model.
intercept = A(df['year'], df['deaths'])
slope = B(df['year'], df['deaths'])
# I love Python f-strings
# https://www.geeksforgeeks.org/formatted-string-literals-f-strings-python/
print(f'The slope is {slope} and the intercept is {intercept}.')

# Step 4. Re-visualize with your model.
x = np.linspace(2000, 2010, 10)  # 10 equally spaced points btw 2000 and 2010
y = slope*x + intercept
plt.scatter(df['year'], df['deaths'], color='black')
plt.plot(x, y, color='black')
plt.xlabel('Year')
plt.ylabel('Bedide tanglies (deaths)')
# Uncomment the line below to save the figure.
# plt.savefig('my_figs_name.png', dpi=250)
plt.show()

# Fun aside ====================================================================
# Question: What is the correlation between per capita cheese consumption and
# number of people who die by becoming tangled in their bedsheets?

# Answer: Let's visualize it!
intercept_cheese = A(df['year'], df['cheese_consumption'])
slope_cheese = B(df['year'], df['cheese_consumption'])
y1 = slope_cheese*x + intercept_cheese

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x, y, color='red')
ax2.plot(x, y1, color='black')

ax1.scatter(df['year'], df['deaths'], color='red')
ax2.scatter(df['year'], df['cheese_consumption'], color='black')

ax1.set_xlabel('Year')
ax1.set_ylabel('Besheet tanglies (deaths)', color='red')
ax2.set_ylabel('Cheese consumed (lbs)')
plt.tight_layout()
plt.show()

# Answer 2: r = 0.9471 (or 94.71%)
# Correlation != causation (or does it... haha)


import numpy as np

def diff_of_means(data_1, data_2):
    """Difference in means of two arrays."""

    # The difference of means of data_1, data_2: diff
    diff = np.mean(data_1) - np.mean(data_2)

    return diff

def _permutation_sample(data):

    permuted_data = np.random.permutation(data)

    return permuted_data

def permutation_over_two_series(data1, data2):
    """Generate a permutation sample from two data sets."""

    # Concatenate the data sets: data
    data = np.concatenate((data1, data2))

    # Permute the concatenated array: permuted_data
    permuted_data = _permutation_sample(data)

    # Split the permuted array into two: perm_sample_1, perm_sample_2
    perm_sample_1 = permuted_data[:len(data1)]
    perm_sample_2 = permuted_data[len(data1):]

    return perm_sample_1, perm_sample_2

def draw_perm_reps(data_1, data_2, func, size=1):
    """Generate multiple permutation replicates."""

    # Initialize array of replicates: perm_replicates
    perm_replicates = np.empty(size)

    for i in range(size):
        # Generate permutation sample
        perm_sample_1, perm_sample_2 = permutation_over_two_series(data_1, data_2)

        # Compute the test statistic
        perm_replicates[i] = func(perm_sample_1, perm_sample_2)

    return perm_replicates

def eval_permutation_p_value(samples_A, samples_B, reducer=diff_of_means, size=10000, condition='gt'):

    # Compute difference of mean impact force from experiment: empirical_diff_means
    empirical_diff_means = reducer(samples_A, samples_B)

    # Draw 10,000 permutation replicates: perm_replicates
    perm_replicates = draw_perm_reps(samples_A, samples_B,
                                     reducer, size=size)

    # Compute p-value:
        # the p_value is the sum of all positive cases divided by the
        #number of samples
        # the positive cases are those whose perm_replicate value is equal
        #or higher (more extreme) than the observed value.

    if condition.lower() == 'gt':

        p_value = np.mean(perm_replicates >= empirical_diff_means)

    elif condition.lower() == 'lt':
        p_value = np.mean(perm_replicates <= empirical_diff_means)

    else:
        mask1 = perm_replicates <= empirical_diff_means

        mask2 = perm_replicates >= empirical_diff_means

        Conditional = perm_replicates[mask1 | mask2]
        p_value = np.sum(Conditional)/len(perm_replicates)

    return {'p_value':p_value,
            'perm_replicates':perm_replicates,
            'empirical_diff_means':empirical_diff_means}

if '__main__' == __name__:
    """
    Description:

        Assuming that we wish to evaluate the probability that two
        populations have same statistics (i.e.: mean).

        A possible solution for this hypothesis test is the t-statistics.

        Another solution is the permutation replicate technique using a
        mean difference as a reducer function.

    Case example:
        samples_A: samples from population A (i.e.: petal length without treatment)
        samples_B: samples from population B (i.e.: petal length with treatment)

        Reducer: diff_of_means. It evaluates the difference between
        Population Means

        H0: Population means are the same
        Ha: Population means are not equal

    Result:
        p_value (reprents the probability of retrieving a value more extreme
                 than the observed, assuming that hypothesis null is True.

        When we assume alpha == 0.05, if p_value < alpha, we reject H0,
        and accept the alternative (Ha).

    """

    print('''Case example:
        samples_A: samples from population A (i.e.: petal length without treatment)
        samples_B: samples from population B (i.e.: petal length with treatment)

        '''
        )

    samples_A = np.random.normal(4, 5, size=500)

    samples_B = np.random.normal(3.7, 7.5, size=500)

    # Compute difference of mean impact force from experiment: empirical_diff_means
    empirical_diff_means = diff_of_means(samples_A, samples_B)

    # Draw 10,000 permutation replicates: perm_replicates
    perm_replicates = draw_perm_reps(samples_A, samples_B,
                                     diff_of_means, size=10000)

    # Compute p-value: p
    p_value = np.mean(perm_replicates >= empirical_diff_means)

    print(perm_replicates)
    # Print the result
    print('p-value =', p_value)

if '__main__' == __name__:
    # Case 2 (Bernoulli situation)

    """
    Description:

        Assume that a given population A (democrats - dems) have a
        certain tendency towards voting in favor (boolean False X True)
        for a given Legislation, and population B (republicans - reps)
        have a second tendency.

        We wish to evaluate the probability that the populations have equal
        tendency in the voting.

    Conditions:

        Since it is a Bernoulli situation, the differences in population
        does no longer apply for this test.

        It is necessary to evaluate the respective frequencies of voting
        per group:

    """
    print('''
          \n\n

          ---------------------------------------------------

          Description:

        Assume that a given population A (democrats - dems) have a
        certain tendency towards voting in favor (boolean False X True)
        for a given Legislation, and population B (republicans - reps)
        have a second tendency.

        We wish to evaluate the probability that the populations
        have equal tendency in the voting.

        \n\n

        ------------------------------------ \n'''
        )

    # Construct arrays of data: dems, reps
    dems = np.array([True] * 153 + [False] * 91)
    reps = np.array([True] * 136 + [False] * 35)

    def frac_yea_dems(group_A, group_B=None):
        """Compute fraction of group_A."""
        frac = group_A[group_A==True].size / (group_A.size)
        return frac

    # Acquire permutation samples: perm_replicates
    perm_replicates = draw_perm_reps(dems, reps, frac_yea_dems, 10000)
    real_fraction = frac_yea_dems(dems)
    # Compute and print p-value: p
    p = np.sum(perm_replicates <= real_fraction) / len(perm_replicates)
    print('p-value =', p)

if '__main__' == __name__:
    """
    Description:

        population A: each sample from population A represents the
        amount of time (in accumulated months) that a given event reoccurs.

        After a given treatment, which could have changed the rates
        in the events of population A, a second sample set (Population B)
        was evaluated.

    Question:
        Is there a significant statistical change prior and after the
        treatment?

        In another words: what is the probability that the treatment
        causes a negative change in the event occurence given that the null hypothesis is true

    Condition:
        since the treatment changed the rates in the events in a negative
        way (i.e., longer average time between event occurrence), we are interested in the "<=" condition for p-value test statistics

    Hypothesis:
        H0: treatment does not change the rates in the events occurence.
        In another words, rates of Population A are equal to Population B:
        Ha: the treatment does change the rates
    """

    print('''
          \n\n

          Description:

        population A: each sample from population A represents the
        amount of time (in accumulated months) that a given event reoccurs.

        After a given treatment, which could have changed the rates in
        the events of population A, a second sample set (Population B)
        was evaluated.

    Question:
        Is there a significant statistical change prior and after the treatment?

        In another words: what is the probability that the treatment
        causes a negative change in the event occurence given that
        the null hypothesis is true

        \n\n

        ------------------------------------ \n'''
        )

    eval_permutation_p_value(samples_A, samples_B, reducer=diff_of_means, size=10000, condition='gt')

if '__main__' == __name__:
    """
    Correlation test statistics

    Description:

        In this example, the statistical confidence (p-value) of the
        correlation coefficient is evaluated.

    The condition evaluated is:
        what is the probability of getting a correlation
        coefficient equal or higher than the observed?

    """

    def pearson_r (x,y):

        return np.corrcoef(x,y)[0][1]

    Result = eval_permutation_p_value(samples_A, samples_B, reducer=pearson_r, size=10000, condition='gt')

    print('p_value =', Result['p_value'])


import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
import math

def predict_using_sklean():
    df = pd.read_csv("exe_gradent_decent.csv")
    r = LinearRegression()
    r.fit(df[['math']],df.cs)
    return r.coef_, r.intercept_

def gradient_descent(x,y):
    m_curr = 0
    b_curr = 0
    iterations = 1000000
    n = len(x)
    learning_rate = 0.0002

    cost_previous = 0

    for i in range(iterations):
        y_predicted = m_curr * x + b_curr
        cost = (1/n)*sum([value**2 for value in (y-y_predicted)])
        md = -(2/n)*sum(x*(y-y_predicted))
        bd = -(2/n)*sum(y-y_predicted)
        m_curr = m_curr - learning_rate * md
        b_curr = b_curr - learning_rate * bd
        if math.isclose(cost, cost_previous, rel_tol=1e-20):
            break
        cost_previous = cost
        print ("m {}, b {}, cost {}, iteration {}".format(m_curr,b_curr,cost, i))

    return m_curr, b_curr

if __name__ == "__main__":
    df = pd.read_csv("exe_gradent_decent.csv")
    x = np.array(df.math)
    y = np.array(df.cs)

    m, b = gradient_descent(x,y)
    print("Using gradient descent function: Coef {} Intercept {}".format(m, b))

    m_sklearn, b_sklearn = predict_using_sklean()
    print("Using sklearn: Coef {} Intercept {}".format(m_sklearn,b_sklearn))


import numpy as np

def rotation_x(phi: float) -> np.ndarray:
    """
    Return the Rotation matrix around the X-axis for phi radians.
    """
    R_rot = np.array([[1, 0, 0],
                      [0, np.cos(phi), -np.sin(phi)],
                      [0, np.sin(phi), np.cos(phi)]])
    return R_rot

def rotation_y(theta: float) -> np.ndarray:
    """
    Return the Rotation matrix around the Y-axis for theta radians.
    """
    R_rot = np.array([[np.cos(theta), 0, -np.sin(theta)],
                      [0, 1, 0],
                      [np.sin(theta), 0, np.cos(theta)]])
    return R_rot

def rotation_z(psi: float) -> np.ndarray:
    """
    Return the Rotation matrix around the Z-axis for psi radians.
    """
    R_rot = np.array([[np.cos(psi), -np.sin(psi), 0],
                      [np.sin(psi), np.cos(psi), 0],
                      [0, 0, 1]])
    return R_rot

def euler_rotation(roll: float, pitch: float, yaw: float, is_radians: bool = False) -> np.ndarray:
    """
    Returns the Euler rotation matrix for 3 rotation angles around X-Y-Z
    """
    # Convert to Radians:
    if not is_radians:
        roll, pitch, yaw = np.deg2rad(roll), np.deg2rad(pitch), np.deg2rad(yaw)

    R = rotation_z(yaw) @ rotation_y(pitch) @ rotation_x(roll)
    return R