import json, sys, time
from math import sqrt
import numpy as np
import weave

class Body(object):
    def __init__(self, name, mass, position, velocity):
        self.name = name
        self.mass = mass
        self.x, self.y, self.z = position
        self.vx, self.vy, self.vz = velocity

def read_bodies(filename):
    bodies = []
    with open(filename, 'r') as handle:
        data = json.load(handle)
        if 'unitmass' in data:
            unitmass = data['unitmass']
        else:
            unitmass = 1.0

        for key, value in data.items():
            if key != 'unitmass':
                body = Body(key, unitmass * value['mass'],
                            value['position'], value['velocity'])
                bodies.append(body)
    return bodies

def kinetic_energy(body):
    return 0.5 * body.mass * (body.vx**2 + body.vy**2 + body.vz**2)

def potential_energy(a, b):
    dx = a.x - b.x
    dy = a.y - b.y
    dz = a.z - b.z
    return -a.mass * b.mass / sqrt(dx**2 + dy**2 + dz**2)

def energy(bodies):
    E = 0.0
    for i in range(len(bodies)):
        E += kinetic_energy(bodies[i])
        for j in range(i+1, len(bodies)):
            E += potential_energy(bodies[i], bodies[j])
    return E

def advance(bodies, dt):
    for i in range(len(bodies)):
        a = bodies[i]
        for j in range(i+1, len(bodies)):
            b = bodies[j]

            dx = a.x - b.x
            dy = a.y - b.y
            dz = a.z - b.z
            d2 = dx**2 + dy**2 + dz**2
            ds = sqrt(d2)
            magnitude = dt / (d2 * d2)
            magnitude *= ds

            a.vx -= b.mass * magnitude * dx
            a.vy -= b.mass * magnitude * dy
            a.vz -= b.mass * magnitude * dz

            b.vx += a.mass * magnitude * dx
            b.vy += a.mass * magnitude * dy
            b.vz += a.mass * magnitude * dz

    for b in bodies:
        b.x += dt * b.vx
        b.y += dt * b.vy
        b.z += dt * b.vz

def numpy_positions_get(bodies):
    x,y,z = [],[],[]
    for b in bodies:
        x.append(b.x)
        y.append(b.y)
        z.append(b.z)
    return np.array([x,y,z])

def numpy_velocities_get(bodies):
    vx,vy,vz = [],[],[]
    for b in bodies:
        vx.append(b.vx)
        vy.append(b.vy)
        vz.append(b.vz)
    return np.array([vx,vy,vz])

def numpy_masses_get(bodies):
    m = []
    for b in bodies:
        m.append(b.mass)
    return np.array([m,m,m])

def numpy_positions_set(positions,bodies):
    x,y,z = positions
    for i in range(len(bodies)):
        b = bodies[i]
        b.x = x[i]
        b.y = y[i]
        b.z = z[i]

def numpy_velocities_set(velocities,bodies):
    vx,vy,vz = velocities
    for i in range(len(bodies)):
        b = bodies[i]
        b.vx = vx[i]
        b.vy = vy[i]
        b.vz = vz[i]

def advance_numpy(bodies, dt, n):

    positions = numpy_positions_get(bodies)
    velocities = numpy_velocities_get(bodies)
    masses = numpy_masses_get(bodies)

    # *_repeat has shape (len(bodies),len(bodies),3)
    positions_repeat = np.tile(positions.transpose(),[len(bodies),1,1])
    velocities_repeat = np.tile(velocities.transpose(),[len(bodies),1,1])
    masses_repeat = np.tile(masses.transpose(),[len(bodies),1,1])

    for i in range(n):
        delta_positions = positions_repeat - positions_repeat.transpose((1,0,2))
        d2 = np.sum( delta_positions*delta_positions, axis=-1 )
        ds = np.sqrt(d2)
        magnitude = ds * np.nan_to_num( dt / (d2 * d2) )
        magnitude_repeat = np.tile(magnitude,[3,1,1]).transpose((1,2,0))
        velocities -= np.sum(masses_repeat.transpose((1,0,2)) * magnitude_repeat * delta_positions,axis=0).transpose()
        positions += dt * velocities
        positions_repeat = np.tile(positions.transpose(),[len(bodies),1,1])

    #return positions,velocities

    numpy_positions_set(positions,bodies)
    numpy_velocities_set(velocities,bodies)

def advance_weave(bodies, dt, n):

    positions = numpy_positions_get(bodies)
    velocities = numpy_velocities_get(bodies)
    masses = numpy_masses_get(bodies)[0]

    nb = len(bodies)

    code = """
    float dx,dy,dz,d2,ds,magnitude;

    for (int i=0; i<n; i++){

        for (int bodyi=0; bodyi<nb; bodyi++){
            for (int bodyj=bodyi+1; bodyj<nb; bodyj++){

                dx = positions(0,bodyi) - positions(0,bodyj);
                dy = positions(1,bodyi) - positions(1,bodyj);
                dz = positions(2,bodyi) - positions(2,bodyj);
                d2 = dx*dx + dy*dy + dz*dz;
                ds = sqrt(d2);
                magnitude = dt / (d2 * d2);
                magnitude *= ds;

                velocities(0,bodyi) -= masses(bodyj) * magnitude * dx;
                velocities(1,bodyi) -= masses(bodyj) * magnitude * dy;
                velocities(2,bodyi) -= masses(bodyj) * magnitude * dz;

                velocities(0,bodyj) += masses(bodyi) * magnitude * dx;
                velocities(1,bodyj) += masses(bodyi) * magnitude * dy;
                velocities(2,bodyj) += masses(bodyi) * magnitude * dz;
            }
        }

        for (int bodyk=0; bodyk<nb; bodyk++){
            for (int dimi=0; dimi<3; dimi++){
                positions(dimi,bodyk) += dt * velocities(dimi,bodyk);
            }
        }

    }
    """

    err = weave.inline(code,
        ['n','nb','positions','velocities','masses','dt'],
        type_converters = weave.converters.blitz)

    numpy_positions_set(positions,bodies)
    numpy_velocities_set(velocities,bodies)

def main(filename, timesteps):
    bodies = read_bodies(filename)

    for body in bodies:
        print(body)

    print('Initial Energy: {:.9f}'.format(energy(bodies)))
    start = time.time()
    if False:
        for i in range(timesteps):
            advance(bodies, 0.01)
    elif False:
        advance_numpy(bodies, 0.01, timesteps)
    else:
        advance_weave(bodies, 0.01, timesteps)
    stop = time.time()
    print('Final Energy:   {:.9f}'.format(energy(bodies)))
    print('Elapsed: {}s\n'.format(stop-start))

if __name__ == '__main__':
    if len(sys.argv) < 3:
        raise RuntimeError('usage: python nbody.py <json-file> <time-steps>')

    filename = sys.argv[1]
    timesteps = int(sys.argv[2])

    main(filename, timesteps)


"""
Description: Numpy implementation of activation functions and
             and their derivatives
author: syedmohsinbukhari@googlemail.com

Reference
http://neuralnetworksanddeeplearning.com/chap2.html
"""

from numpy import exp

class ActivationFunction:

    def __init__(self):
        pass

    @staticmethod
    def forward(x):
        pass

    @staticmethod
    def derivative(x):
        pass

class Sigmoid(ActivationFunction):

    @staticmethod
    def forward(x):
        return 1.0 / (1.0 + exp(-1.0*x))

    @staticmethod
    def derivative(x):
        return Sigmoid.forward(x) * (1.0 - Sigmoid.forward(x))

class Relu(ActivationFunction):

    @staticmethod
    def forward(x):
        if x > 0.0:
            return x
        else:
            return 0.0

    @staticmethod
    def derivative(x):
        if x > 0.0:
            return 1.0
        else:
            return 0.0


import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import math
import sys
import os

data_dir = sys.argv[1]
out_dir = sys.argv[2]
test_set = os.path.join(sys.argv[1], 'q2test.csv')

# 2. Sampling and Stochastic Gradient

# (a) Sampling

w = np.array([[3],[1],[2]])
X1 = np.random.normal(3, 2, (int)(1e6))
X2 = np.random.normal(-1, 2, (int)(1e6))
noise = np.random.normal(0, np.sqrt(2), int(1e6)).reshape(-1,1)

X = np.column_stack((X1, X2))
X = np.column_stack((np.ones(X.shape[0]), X))
Y = np.dot(X, w) + noise
m = len(Y)

# (b) Stochastic Gradient

def hw(theta, i, r):
	x = X[i*r:(i+1)*r,]
	return np.dot(x, theta)

def Jw(h, i, r):
	y = Y[i*r:(i+1)*r,]
	return (0.5/r)*np.sum((y - h)**2)

def dJw(h, i, r):
	x = X[i*r:(i+1)*r,]
	y = Y[i*r:(i+1)*r,]
	return (1.0/r)*np.dot(x.T, (y - h))

def stochasticGradientDescent(x, y, r):
	b = (int)(m/r)
	eta = 0.001
	theta = np.zeros((x.shape[1], 1))
	thetaL = [theta]
	prevCost = 0
	converged = False
	itr = 0
	while not converged:
		for i in range(b):
			h = hw(theta, i, r)
			cost = Jw(h, i, r)
			theta = theta + eta*dJw(h, i, r)
			thetaL.append(theta)
		itr += 1
		error = abs(cost - prevCost)
		prevCost = cost
		if error < 1e-7 or itr > 25000:
			converged = True

	return thetaL, itr

thetaL2, maxit2 = stochasticGradientDescent(X, Y, 100)
thetaL3, maxit3 = stochasticGradientDescent(X, Y, 10000)

# (d) 3D Plot of Parameters

def plotTheta(thetaL, r):
	thetaL = np.array(thetaL)
	thetaL = thetaL.reshape(-1, 3)
	ax = plt.axes(projection='3d')
	x = []
	y = []
	z = []
	for i in range(len(thetaL)):
		x.append(thetaL[i][0])
		y.append(thetaL[i][1])
		z.append(thetaL[i][2])

	ax.scatter(x, y, z, color='red')
	ax.set_xlabel(r'$\theta_0$')
	ax.set_ylabel(r'$\theta_1$')
	ax.set_zlabel(r'$\theta_2$')
	ax.set_title("Parameter Plot - " + r'$r = {0}$'.format(r))
	f = "Q2d3DPlot" + str(r) + ".png"
	parameter_plot3d = os.path.join(sys.argv[2], f)
	plt.savefig(parameter_plot3d)
	plt.close()

plotTheta(thetaL2, 100)
plotTheta(thetaL3, 10000)


# coding: utf-8

__author__ = 'Mário Antunes'
__version__ = '0.1'
__email__ = 'mariolpantunes@gmail.com'
__status__ = 'Development'

import logging
import numpy as np
import uts.gradient as grad
import knee.multi_knee as mk

logger = logging.getLogger(__name__)

def knee(points: np.ndarray) -> int:
    """
    Returns the index of the knee point based on the curvature equations:
    $$
    k = \\frac{|f''(x)|}{(1+[f'(2)]^2)^{\\frac{3}{2}}}
    $$

    Args:
        points (np.ndarray): numpy array with the points (x, y)

    Returns:
        int: the index of the knee point
    """

    x = points[:, 0]
    y = points[:, 1]

    gradient1 = grad.cfd(x, y)
    gradient2 = grad.csd(x, y)

    curvature = gradient2 / ((1.0 + gradient1**2.0)**(1.5))
    return np.argmax(curvature[0:-1])

def multi_knee(points: np.ndarray, t1: float = 0.99, t2: int = 3) -> np.ndarray:
    """
    Recursive knee point detection based on the curvature equations.

    It returns the knee points on the curve.

    Args:
        points (np.ndarray): numpy array with the points (x, y)
        t1 (float): coefficient of determination threshold (default 0.99)
        t2 (int): number of points threshold (default 3)

    Returns:
        np.ndarray: knee points on the curve
    """
    return mk.multi_knee(knee, points, t1, t2)


import cv2
import math
import numpy as np
from random import randint
import matplotlib.pyplot as plt

lena_image = cv2.imread("./Images/lena512.bmp", cv2.IMREAD_GRAYSCALE)

# sobel_filter_x = np.array([[-1, 0, 1],
#                            [-2, 0, 2],
#                            [-1, 0, 1]])
# sobel_filter_y = np.array([[-1, -2, -1],
#                            [0, 0, 0],
#                            [1, 2, 1]])

# def fft2convolve(image, kernel):
#     image_width, image_height = image.shape
#     kernel_height, kernel_width = kernel.shape
#     padding_kernel = np.zeros((image_height, image_width))

#     padding_kernel[round(image_height / 2 - (kernel_height - 1) / 2):round(image_height / 2 + (kernel_height - 1) / 2) + 1,
#                    round(image_width / 2 - (kernel_width - 1) / 2):round(image_width / 2 + (kernel_width - 1) / 2) + 1] \
#         = kernel[:, :]

#     image_freq = np.fft.fft2(image)
#     padding_kernel_freq = np.fft.fft2(padding_kernel)

#     result = np.fft.ifft2(image_freq * padding_kernel_freq)

#     return np.fft.fftshift(result).real

# frequency domain
# lena_sobel_x_image = fft2convolve(lena_image, sobel_filter_x)
# lena_sobel_y_image = fft2convolve(lena_image, sobel_filter_y)
# lena_sobel_y_x_image = fft2convolve(lena_sobel_y_image, sobel_filter_x)

# fig, subplot = plt.subplots(1, 4, figsize=(5, 5))
# plt.tight_layout()

# subplot[0].axis('off')
# subplot[0].set_title("Lena")
# subplot[0].imshow(lena_image, cmap='gray')
# subplot[1].axis('off')
# subplot[1].set_title("Sobel X")
# subplot[1].imshow(lena_sobel_x_image, cmap='gray')
# subplot[2].axis('off')
# subplot[2].set_title("Sobel Y")
# subplot[2].imshow(lena_sobel_y_image, cmap='gray')
# subplot[3].axis('off')
# subplot[3].set_title("Sobel X & Y")
# subplot[3].imshow(lena_sobel_y_x_image, cmap='gray')

# plt.show()

# spatial domain
# lena_sobel_x_image = cv2.filter2D(lena_image, cv2.CV_32F, sobel_filter_x)
# lena_sobel_y_image = cv2.filter2D(lena_image, cv2.CV_32F, sobel_filter_y)
# lena_sobel_y_x_image = cv2.filter2D(lena_sobel_y_image, cv2.CV_32F,
#                                     sobel_filter_x)

# fig, subplot = plt.subplots(1, 4, figsize=(5, 5))
# plt.tight_layout()

# subplot[0].axis('off')
# subplot[0].set_title("Lena")
# subplot[0].imshow(lena_image, cmap='gray')
# subplot[1].axis('off')
# subplot[1].set_title("Sobel X")
# subplot[1].imshow(lena_sobel_x_image, cmap='gray')
# subplot[2].axis('off')
# subplot[2].set_title("Sobel Y")
# subplot[2].imshow(lena_sobel_y_image, cmap='gray')
# subplot[3].axis('off')
# subplot[3].set_title("Sobel X & Y")
# subplot[3].imshow(lena_sobel_y_x_image, cmap='gray')

# plt.show()

# image_width, image_height = lena_image.shape
# kernel_height, kernel_width = sobel_filter_x.shape

# padding_sobel_filter_x = np.zeros((image_height, image_width))
# padding_sobel_filter_x[round(image_height / 2 - (kernel_height - 1) / 2):round(image_height / 2 + (kernel_height - 1) / 2) + 1,
#                        round(image_width / 2 - (kernel_width - 1) / 2):round(image_width / 2 + (kernel_width - 1) / 2) + 1] \
#     = sobel_filter_x[:, :]

# padding_sobel_filter_y = np.zeros((image_height, image_width))
# padding_sobel_filter_y[round(image_height / 2 - (kernel_height - 1) / 2):round(image_height / 2 + (kernel_height - 1) / 2) + 1,
#                        round(image_width / 2 - (kernel_width - 1) / 2):round(image_width / 2 + (kernel_width - 1) / 2) + 1] \
#     = sobel_filter_y[:, :]

# padding_sobel_filter_x_freq = np.fft.fft2(padding_sobel_filter_x)
# padding_sobel_filter_y_freq = np.fft.fft2(padding_sobel_filter_y)

# fig, subplot = plt.subplots(2, 4, figsize=(5, 5))
# plt.tight_layout()

# subplot[0][0].axis('off')
# subplot[0][0].set_title("Sobel X Real")
# subplot[0][0].imshow(padding_sobel_filter_x_freq.real, cmap="gray")
# subplot[0][1].axis('off')
# subplot[0][1].set_title("Sobel X Imaginary")
# subplot[0][1].imshow(padding_sobel_filter_x_freq.imag, cmap="gray")
# subplot[0][2].axis('off')
# subplot[0][2].set_title("Sobel X Absolute")
# subplot[0][2].imshow(np.absolute(padding_sobel_filter_x_freq), cmap="gray")
# subplot[0][3].axis('off')
# subplot[0][3].set_title("Sobel X Angle")
# subplot[0][3].imshow(np.angle(padding_sobel_filter_x_freq), cmap="gray")

# subplot[1][0].axis('off')
# subplot[1][0].set_title("Sobel Y Real")
# subplot[1][0].imshow(padding_sobel_filter_y_freq.real, cmap="gray")
# subplot[1][1].axis('off')
# subplot[1][1].set_title("Sobel Y Imaginary")
# subplot[1][1].imshow(padding_sobel_filter_y_freq.imag, cmap="gray")
# subplot[1][2].axis('off')
# subplot[1][2].set_title("Sobel Y Absolute")
# subplot[1][2].imshow(np.absolute(padding_sobel_filter_y_freq), cmap="gray")
# subplot[1][3].axis('off')
# subplot[1][3].set_title("Sobel Y Angle")
# subplot[1][3].imshow(np.angle(padding_sobel_filter_y_freq), cmap="gray")

# plt.show()

# def createCircleMask(shape, distance):
#     mask = np.zeros(shape)
#     rows, columns = shape
#     for x in range(columns):
#         for y in range(rows):
#             if np.sqrt((x - columns/2) ** 2 + (y - rows/2) ** 2) < distance:
#                 mask[y, x] = 1
#     return mask

# def createGaussianMask(shape, distance):
#     mask = np.zeros(shape)
#     rows, columns = shape
#     for x in range(columns):
#         for y in range(rows):
#             mask[y, x] = np.exp(
#                 ((-1 * (np.sqrt((x - columns/2) ** 2 + (y - rows/2) ** 2)))/(2*(distance**2))))
#     return mask

# def mse(image_1, image_2):
#     return ((image_1 - image_2) ** 2).sum() / \
#         (image_1.shape[0] * image_1.shape[1])

# def psnr(image_1, image_2):
#     mse_value = mse(image_1, image_2)

#     if mse_value == 0:
#         return math.inf

#     return 10 * np.log((255 ** 2) / mse_value)

# fig, subplot = plt.subplots(2, 5, figsize=(30, 10))
# plt.tight_layout()

# lena_image_freq = np.fft.fft2(lena_image)
# image_width, image_height = lena_image.shape

# for i in range(5):
#     circle_mask = createCircleMask((image_width, image_height),
#                                    image_width/(2*(5-i)))

#     lena_image_freq_shift = np.fft.fftshift(lena_image_freq)

#     lena_image_freq_mask = lena_image_freq_shift * circle_mask

#     lena_image_freq_mask = np.fft.fftshift(lena_image_freq_mask)

#     result = np.fft.ifft2(lena_image_freq_mask)

#     subplot[0][i].axis(False)
#     subplot[0][i].imshow(circle_mask, cmap="gray")
#     subplot[0][i].set_title("Mask")

#     subplot[1][i].axis(False)
#     subplot[1][i].imshow(result.real, cmap="gray")
#     subplot[1][i].set_title(f"PSNR={psnr(lena_image, result.real)}")

# plt.show()

# fig, subplot = plt.subplots(2, 5, figsize=(30, 10))
# plt.tight_layout()

# lena_image_freq = np.fft.fft2(lena_image)
# image_width, image_height = lena_image.shape

# for i in range(5):
#     x = cv2.getGaussianKernel(image_width, image_width/(2*(5-i)))
#     gaussian_mask = x * x.T

#     lena_image_freq_shift = np.fft.fftshift(lena_image_freq)

#     lena_image_freq_mask = lena_image_freq_shift * gaussian_mask

#     lena_image_freq_mask = np.fft.fftshift(lena_image_freq_mask)

#     result = np.fft.ifft2(lena_image_freq_mask)

#     subplot[0][i].axis(False)
#     subplot[0][i].imshow(gaussian_mask, cmap="gray")
#     subplot[0][i].set_title("Mask")

#     subplot[1][i].axis(False)
#     subplot[1][i].imshow(result.real, cmap="gray")
#     subplot[1][i].set_title(f"PSNR={psnr(lena_image, result.real)}")

# plt.show()

# fig, subplot = plt.subplots(2, 5, figsize=(30, 10))

# squre = np.zeros((10, 10))

# for i in range(5):
#     mask = np.zeros((100, 100))

#     x = randint(0, mask.shape[0] - squre.shape[0])
#     y = randint(0, mask.shape[1] - squre.shape[1])

#     mask[x + squre.shape[0]:x + squre.shape[0] + squre.shape[0],
#          y + squre.shape[1]:y + squre.shape[1] + squre.shape[1]] = squre

#     subplot[0][i].axis(False)
#     subplot[0][i].imshow(mask, cmap="gray")
#     subplot[0][i].set_title("Mask")

#     mask_freq = np.fft.fft2(mask)

#     subplot[1][i].axis(False)
#     subplot[1][i].imshow(mask_freq.real, cmap="gray")
#     subplot[1][i].set_title("Real")

#     subplot[2][i].axis(False)
#     subplot[2][i].imshow(mask_freq.imag, cmap="gray")
#     subplot[2][i].set_title("Imaginary")

#     subplot[3][i].axis(False)
#     subplot[3][i].imshow(np.absolute(mask_freq), cmap="gray")
#     subplot[3][i].set_title("Absolute")

#     subplot[4][i].axis(False)
#     subplot[4][i].imshow(np.angle(mask_freq), cmap="gray")
#     subplot[4][i].set_title("Angle")

# plt.show()

fig, subplot = plt.subplots(5, 4, figsize=(15, 10))

lena_image_freq = np.fft.fft2(lena_image)
image_width, image_height = lena_image.shape

for i in range(20, 100, 20):
    mask = np.zeros((100, 100))
    squre = np.ones((i, i))

    x = randint(0, mask.shape[0] - squre.shape[0])
    y = randint(0, mask.shape[1] - squre.shape[1])

    mask[round(mask.shape[0] / 2 - squre.shape[0] / 2):round(mask.shape[0] / 2 - squre.shape[0] / 2 + squre.shape[0]),
         round(mask.shape[1] / 2 - squre.shape[0] / 2):round(mask.shape[1] / 2 - squre.shape[0] / 2 + squre.shape[1])] = squre

    i = round(i / 20 - 1)
    subplot[0][i].axis(False)
    subplot[0][i].imshow(mask, cmap="gray")
    subplot[0][i].set_title("Mask")

    mask_freq = np.fft.fft2(mask)

    subplot[1][i].axis(False)
    subplot[1][i].imshow(mask_freq.real, cmap="gray")
    subplot[1][i].set_title("Real")

    subplot[2][i].axis(False)
    subplot[2][i].imshow(mask_freq.imag, cmap="gray")
    subplot[2][i].set_title("Imaginary")

    subplot[3][i].axis(False)
    subplot[3][i].imshow(np.absolute(mask_freq), cmap="gray")
    subplot[3][i].set_title("Absolute")

    subplot[4][i].axis(False)
    subplot[4][i].imshow(np.angle(mask_freq), cmap="gray")
    subplot[4][i].set_title("Angle")

plt.show()


#! Python 3
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt
import time

ti = time.time()

def NP_method(expression, derivative, guess, eps=0.001, iterations=30, graph=True):
    f = lambda x: eval(expression)

    diff = lambda x: eval(derivative)

    view = (0, 1)
    domain = np.linspace(view[0], view[1], num=1000)
    range = list()
    for i in domain:
        range.append(f(i))
    diff_range = list()
    for i in domain:
        diff_range.append(diff(i))

    if graph:
        plt.plot(domain, range)
        plt.plot(domain, diff_range)
        plt.grid(True, which='both')
        plt.axhline(y=0, color='k')
        plt.axvline(x=0, color='k')
        plt.show()

    old_guess = guess - 10
    control = 0

    print('Calculando...')
    while eps < abs((guess - old_guess) / 2):
        if control < iterations:
            old_guess = guess
            guess -= f(guess) / diff(guess)
            control += 1
            err = abs((guess - old_guess) / guess)
            print('Valor actual: {cr_guess}. \n Loop Nº{loop}.\n Error: {err}'.format(
                cr_guess=sym.N(guess), loop=control, err=err))
            if err < eps:
                print('Valor final: {guess}'.format(guess=sym.N(guess)))
                break

        else:
            print('End of loop.')
            print('Valor final: {guess}'.format(guess=sym.N(guess)))
            break

def puntofijo(expression, derivative, guess, iteration=30, eps=0.01, err_graph=False):
    control = 0

    f = lambda x: eval(expression)
    diff = lambda x: eval(derivative)
    if not abs(diff(guess)) < 1:
        print('Ésta función no converge.')
        return None

    print('Ésta función converge. \n Calculando...')

    old_guess = guess
    guess = f(guess)
    err_steps = list()

    while eps < abs((guess - old_guess) / guess):
        if control < iteration:
            old_guess = guess
            guess = f(guess)
            control += 1
            err = abs((guess - old_guess) / guess)
            err_steps.append(err)
            print('Valor actual: {cr_guess}. \n Loop Nº{loop}.\n Error: {err}'.format(
                cr_guess=guess, loop=control, err=err))
            if err < eps:
                print('Valor final: {guess}'.format(guess=guess))
                break

        else:
            print('End of loop.')
            break

    print('Valor final: {guess}'.format(guess=guess))
    if err_graph:
        domain_err = np.arange(len(err_steps))
        plt.plot(domain_err, err_steps)
        plt.grid(True, which='both')
        plt.axhline(y=0, color='k')
        plt.axvline(x=0, color='k')
        plt.xlabel('Iteraciones')
        plt.ylabel('Error relativo')
        plt.xscale('log')
        plt.show()
        plt.close()

ecuacion = '-0.13952954729309466/np.sqrt(x**5) - 0.86*np.log(0.07027027027027026/x + (9.168716265367385e-6*x)/np.sqrt(x**5))'
derivada = '(0.34882386823273664 * x ** 4) / (x ** 5) ** 1.5 - (0.86 * (-0.07027027027027026 / x ** 2 - 0.00001375307439805108 / np.sqrt(x ** 5))) /   (0.07027027027027026 / x + (9.168716265367385e-6 * x) / np.sqrt(x ** 5))'

NP_method(ecuacion, derivada, 0.3)


#!/usr/bin/python3

import numpy as np
import matplotlib.pylab as plt

# シグモイド関数
# 自然対数を利用する
# 非線形の曲線のグラフになる
def sigmoid_function(x):
    return 1 / (1 + np.exp(-x))

# シグモイド関数にながす
sa = np.array([-1.0, 1.0, 2.0])
print(sigmoid_function(sa))

# グラフを設定する
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid_function(x)

plt.plot(x, y)
# y軸の範囲を設定する
plt.ylim(-0.1, 1.1)

plt.show()


"""
We use the Full Whiskas Model Python Formulation for the PuLP Modeller
We make the cost of different ingredients vary and observe the corresponding optimal quantity for ingredients
Authors: Antony Phillips, Dr Stuart Mitchell  2007

Modifications: Bertrand Travacca 2017
"""

# Import PuLP modeler functions
from pulp import *
import random as rd
import numpy as np

# Creates a list of the Ingredients
Ingredients = ['CHICKEN', 'BEEF', 'MUTTON', 'RICE', 'WHEAT', 'GEL']

# A dictionary of the costs of each of the Ingredients is created
reference_costs = {'CHICKEN': 0.013,
         'BEEF': 0.008,
         'MUTTON': 0.010,
         'RICE': 0.002,
         'WHEAT': 0.005,
         'GEL': 0.001}

# A dictionary of the protein percent in each of the Ingredients is created
proteinPercent = {'CHICKEN': 0.100,
                  'BEEF': 0.200,
                  'MUTTON': 0.150,
                  'RICE': 0.000,
                  'WHEAT': 0.040,
                  'GEL': 0.000}

# A dictionary of the fat percent in each of the Ingredients is created
fatPercent = {'CHICKEN': 0.080,
              'BEEF': 0.100,
              'MUTTON': 0.110,
              'RICE': 0.010,
              'WHEAT': 0.010,
              'GEL': 0.000}

# A dictionary of the fibre percent in each of the Ingredients is created
fibrePercent = {'CHICKEN': 0.001,
                'BEEF': 0.005,
                'MUTTON': 0.003,
                'RICE': 0.100,
                'WHEAT': 0.150,
                'GEL': 0.000}

# A dictionary of the salt percent in each of the Ingredients is created
saltPercent = {'CHICKEN': 0.002,
               'BEEF': 0.005,
               'MUTTON': 0.007,
               'RICE': 0.002,
               'WHEAT': 0.008,
               'GEL': 0.000}
N_data=1000
optimum_sol=np.zeros((6, N_data))
costs_values=np.zeros((6, N_data))

for k in range(N_data):
    # A dictionary of the costs of each of the Ingredients is created, it takes the reference cost and draws u.a.r
    # independently for each ingredient a cost within 50% of the reference

    costs_values[0,k]=0.013*(0.5+rd.random())
    costs_values[1,k]=0.008*(0.5+rd.random())
    costs_values[2,k]=0.010*(0.5+rd.random())
    costs_values[3,k]=0.002*(0.5+rd.random())
    costs_values[4,k]=0.005*(0.5+rd.random())
    costs_values[5,k]=0.001*(0.5+rd.random())

    costs = {'CHICKEN': costs_values[0,k],
             'BEEF':costs_values[1,k] ,
             'MUTTON': costs_values[2,k],
             'RICE':costs_values[3,k] ,
             'WHEAT': costs_values[4,k],
             'GEL': costs_values[5,k]}

    # Create the 'prob' variable to contain the problem data
    prob = LpProblem("The Whiskas Problem", LpMinimize)

    # A dictionary called 'ingredient_vars' is created to contain the referenced Variables
    ingredient_vars = LpVariable.dicts("Ingr",Ingredients,0)

    # The objective function is added to 'prob' first
    prob += lpSum([costs[i]*ingredient_vars[i] for i in Ingredients]), "Total Cost of Ingredients per can"

    # The five constraints are added to 'prob'
    prob += lpSum([ingredient_vars[i] for i in Ingredients]) == 100, "PercentagesSum"
    prob += lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 8.0, "ProteinRequirement"
    prob += lpSum([fatPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 6.0, "FatRequirement"
    prob += lpSum([fibrePercent[i] * ingredient_vars[i] for i in Ingredients]) <= 2.0, "FibreRequirement"
    prob += lpSum([saltPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 0.4, "SaltRequirement"

    # The problem data is written to an .lp file
    prob.writeLP("WhiskasModel2.lp")

    # The problem is solved using PuLP's choice of Solver
    prob.solve()

    # The status of the solution is printed to the screen
    #print("Status:", LpStatus[prob.status])

    # Each of the variables is printed with it's resolved optimum value
    l=0
    for v in prob.variables():
        optimum_sol[l,k]=v.varValue
        l=l+1


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

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
pp = PdfPages('custom.pdf')
print("Setup Complete")

# Create a figure of size 8x6 inches, 80 dots per inch
plt.figure(figsize=(8, 6), dpi=80)
# Create a new subplot from a grid of 1x1
plt.subplot(1, 1, 1)
X = np.linspace(-np.pi, np.pi, 256)
C, S = np.cos(X), np.sin(X)
plt.plot(X, C, color="blue", linewidth=2.5, linestyle="-",label="sine")
plt.plot(X, S, color="green", linewidth=2.5, linestyle="-",label="cosine")
plt.legend(loc='upper left')
# Set x limits
plt.xlim(X.min() * 1.1, X.max() * 1.1)
# Set x ticks
plt.xticks([-np.pi, -np.pi/2, np.pi/2, np.pi],
[r'$-\pi$', r'$-\pi/2$',  r'$+\pi/2$', r'$+\pi$'])
# Set y limits
plt.ylim(C.min() * 1.1, C.max() * 1.1)
# Set y ticks
plt.yticks([-1, +1],
[r'$-1$', r'$+1$'])
# move spine
# gca stands for 'get current axis'
ax = plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))

#Annotate
t = 2 * np.pi / 3
plt.plot([t, t], [0, np.cos(t)], color='blue', linewidth=2.5, linestyle="--")
plt.scatter([t, ], [np.cos(t), ], 50, color='blue')
plt.annotate(r'$cos(\frac{2\pi}{3} )=-\frac{1} {2} $',
xy=(t, np.cos(t)), xycoords='data',
xytext=(-90, -50), textcoords='offset points', fontsize=16,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))
plt.plot([t, t],[0, np.sin(t)], color='red', linewidth=2.5, linestyle="--")
plt.scatter([t, ],[np.sin(t), ], 50, color='red')
plt.annotate(r'$sin(\frac{2\pi}{3} )=\frac{\sqrt{3} }{2} $',
xy=(t, np.sin(t)), xycoords='data',
xytext=(+10, +30), textcoords='offset points', fontsize=16,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))

#resize ticks
for label in ax.get_xticklabels() + ax.get_yticklabels():
    label.set_fontsize(16)
    label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.65))

# Save figure using 72 dots per inch
plt.savefig("custom.png", dpi=72)
# Show result on screen
plt.show()
pp.savefig()
pp.close()


################################################################################
# Peach - Computational Intelligence for Python
# Jose Alexandre Nalon
#
# This file: tutorial/fuzzy-c-means.py
# Basic example of Fuzzy C-Means
################################################################################

# We import numpy for arrays and peach for the library. Actually, peach also
# imports the numpy module, but we want numpy in a separate namespace:
from numpy import *
import peach as p

# We create the example list (the training set) and the corresponding membership
# values for each example. There are 15 two-dimensional examples, and 15 pairs
# of membership values. This means that each example will be classified in two
# classes, with the corresponding membership values. This means that the vector
# [ 0, 0 ] will be classified with membership values 0.7 in the first class and
# membership values 0.3 in the second class and so on. Notice that, with this
# values, we expect the two centers, after the clustering, to be located at
# [ 1., 1. ] and [ 6., 6. ].

x = array( [
    [ 0., 0. ], [ 0., 1. ], [ 0., 2. ], [ 1., 0. ], [ 1., 1. ], [ 1., 2. ],
    [ 2., 0. ], [ 2., 1. ], [ 2., 2. ], [ 5., 5. ], [ 5., 6. ], [ 5., 7. ],
    [ 6., 5. ], [ 6., 6. ], [ 6., 7. ], [ 7., 5. ], [ 7., 6. ], [ 7., 7. ] ] )

mu = array( [
    [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ],
    [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.7, 0.3 ], [ 0.3, 0.7 ],
    [ 0.3, 0.7 ], [ 0.3, 0.7 ], [ 0.3, 0.7 ], [ 0.3, 0.7 ], [ 0.3, 0.7 ],
    [ 0.3, 0.7 ], [ 0.3, 0.7 ], [ 0.3, 0.7 ] ] )

# Notice that the starting values for the memberships could be randomly choosen,
# at least for simple cases like this. You could try the lines below to
# initialize the membership array:
#
# from numpy.random import random
# mu = random((18, 1))
# mu = hstack((mu, 1.-mu))

# This parameter measures the smoothness of convergence
m = 2.0

# We create the algorithm. We must pass, in this order, the example set, the
# corresponding membership values, and the parameter `m`. This parameter is
# optional, though, and if not given, will default to 2.
fcm = p.FuzzyCMeans(x, mu, m)

# The __call__ interface runs the algorithm till completion. It returns the
# center of the classification. If we want to check the membership values for
# the vectors, the .mu instance variable can be checked. Notice that we pass the
# parameter emax = 0 to the algorithm. This is the maximum error accepted. In
# general, fuzzy c-means will converge very fastly and with little error. A
# imax parameter -- the maximum number of iterations, can also be given. If it
# isn't given, 20 iterations will be assumed.
print "After 20 iterations, the algorithm converged to the centers:"
print fcm(emax=0)
print
print "The membership values for the examples are given below:"
print fcm.mu
print


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

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

from __future__ import print_function, division

import numpy as np

import nsfg
import first

import thinkstats2
import thinkplot

def PercentileRank(scores, your_score):
    """Computes the percentile rank relative to a sample of scores."""
    count = 0
    for score in scores:
        if score <= your_score:
            count += 1

    percentile_rank = 100.0 * count / len(scores)
    return percentile_rank

scores = [55, 66, 77, 88, 99]
your_score = 88

print('score, percentile rank')
for score in scores:
    print(score, PercentileRank(scores, score))
print()

def Percentile(scores, percentile_rank):
    """Computes the value that corresponds to a given percentile rank. """
    scores.sort()
    for score in scores:
        if PercentileRank(scores, score) >= percentile_rank:
            return score

def Percentile2(scores, percentile_rank):
    """Computes the value that corresponds to a given percentile rank.

    Slightly more efficient.
    """
    scores.sort()
    index = percentile_rank * (len(scores)-1) // 100
    return scores[index]

print('prank, score, score')
for percentile_rank in [0, 20, 25, 40, 50, 60, 75, 80, 100]:
    print(percentile_rank,
          Percentile(scores, percentile_rank),
          Percentile2(scores, percentile_rank))

def EvalCdf(sample, x):
    """Computes CDF(x) in a sample.

    sample: sequence
    x: value

    returns: cumulative probability
    """
    count = 0.0
    for value in sample:
        if value <= x:
            count += 1.0

    prob = count / len(sample)
    return prob

sample = [1, 2, 2, 3, 5]

print('x', 'CDF(x)')
for x in range(0, 7):
    print(x, EvalCdf(sample, x))

def PositionToPercentile(position, field_size):
    """Converts from position in the field to percentile.

    position: int
    field_size: int
    """
    beat = field_size - position + 1
    percentile = 100.0 * beat / field_size
    return percentile

def PercentileToPosition(percentile, field_size):
    """Converts from percentile to hypothetical position in the field.

    percentile: 0-100
    field_size: int
    """
    beat = percentile * field_size / 100.0
    position = field_size - beat + 1
    return position

# my time 42:44
print('Percentile rank in field', PositionToPercentile(97, 1633))
print('Percentile rank in age group', PositionToPercentile(26, 256))

percentile = PositionToPercentile(26, 256)
print('Equivalent position in M50-59', PercentileToPosition(percentile, 171))
# 17th place = 46:05
print('Equivalent position in F20-29', PercentileToPosition(percentile, 448))
# 48:28

def MakeExample():
    """Makes a simple example CDF."""
    t = [2, 1, 3, 2, 5]
    cdf = thinkstats2.Cdf(t)
    thinkplot.Clf()
    thinkplot.Cdf(cdf)
    thinkplot.Save(root='cumulative_example_cdf',
                   xlabel='x',
                   ylabel='CDF',
                   axis=[0, 6, 0, 1],
                   legend=False)

def MakeFigures(live, firsts, others):
    """Creates several figures for the book.

    live: DataFrame
    firsts: DataFrame
    others: DataFrame
    """

    first_wgt = firsts.totalwgt_lb
    first_wgt_dropna = first_wgt.dropna()
    print('Firsts', len(first_wgt), len(first_wgt_dropna))
    #assert len(first_wgt_dropna) == 4381

    other_wgt = others.totalwgt_lb
    other_wgt_dropna = other_wgt.dropna()
    print('Others', len(other_wgt), len(other_wgt_dropna))
    #assert len(other_wgt_dropna) == 4706

    first_pmf = thinkstats2.Pmf(first_wgt_dropna, label='first')
    other_pmf = thinkstats2.Pmf(other_wgt_dropna, label='other')

    width = 0.4 / 16

    # plot PMFs of birth weights for first babies and others
    thinkplot.PrePlot(2)
    thinkplot.Hist(first_pmf, align='right', width=width)
    thinkplot.Hist(other_pmf, align='left', width=width)
    thinkplot.Save(root='cumulative_birthwgt_pmf',
                   title='Birth weight',
                   xlabel='weight (pounds)',
                   ylabel='PMF')

    # plot CDFs of birth weights for first babies and others
    first_cdf = thinkstats2.Cdf(firsts.totalwgt_lb, label='first')
    other_cdf = thinkstats2.Cdf(others.totalwgt_lb, label='other')

    thinkplot.PrePlot(2)
    thinkplot.Cdfs([first_cdf, other_cdf])
    thinkplot.Save(root='cumulative_birthwgt_cdf',
                   title='Birth weight',
                   xlabel='weight (pounds)',
                   ylabel='CDF',
                   axis=[0, 12.5, 0, 1]
                   )

def MakeCdf(live):
    """Plot the CDF of pregnancy lengths for live births.

    live: DataFrame for live births
    """
    cdf = thinkstats2.Cdf(live.prglngth, label='prglngth')
    thinkplot.Cdf(cdf)
    thinkplot.Save('cumulative_prglngth_cdf',
                   title='Pregnancy length',
                   xlabel='weeks',
                   ylabel='CDF')

def RandomFigure(live):
    weights = live.totalwgt_lb
    cdf = thinkstats2.Cdf(weights, label='totalwgt_lb')

    sample = np.random.choice(weights, 100, replace=True)
    ranks = [cdf.PercentileRank(x) for x in sample]

    rank_cdf = thinkstats2.Cdf(ranks, label='percentile ranks')
    thinkplot.Cdf(rank_cdf)
    thinkplot.Save(root='cumulative_random',
                   xlabel='percentile rank',
                   ylabel='CDF')

def TestSample(live):
    """Plots the distribution of weights against a random sample.

    live: DataFrame for live births
    """
    weights = live.totalwgt_lb
    cdf = thinkstats2.Cdf(weights, label='totalwgt_lb')

    sample = cdf.Sample(1000)
    sample_cdf = thinkstats2.Cdf(sample, label='sample')

    thinkplot.PrePlot(2)
    thinkplot.Cdfs([cdf, sample_cdf])
    thinkplot.Save(root='cumulative_sample',
                   xlabel='weight (pounds)',
                   ylabel='CDF')

def main(name, data_dir=''):
    thinkstats2.RandomSeed(17)

    MakeExample()
    live, firsts, others = first.MakeFrames()
    RandomFigure(live)
    TestSample(live)
    MakeCdf(live)
    MakeFigures(live, firsts, others)

if __name__ == '__main__':
    import sys
    main(*sys.argv)

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


import numpy as np

def median(input_list: list) -> float:
    """
    中位数
    href: https://baike.baidu.com/item/%E4%B8%AD%E4%BD%8D%E5%80%BC/9501969?fr=aladdin
    input_list: list[int] 数值型数据列表
    returns: float: 中位值
    """
    if not input_list:
        raise ValueError("你输入的是个空列表")

    input_list.sort()
    num = len(input_list)
    if num % 2 == 1:
        return input_list[num // 2]
    else:
        return sum(input_list[num // 2 - 1: num // 2 + 1]) / 2

def median2(input_list: list) -> float:
    return np.median(input_list)

if __name__ == "__main__":
    # print(mean([]))
    print(median([60]))
    print(median([60, 10, 20, 30, 40, 50, 70, 80, 90]))
    print(median2([60, 10, 20, 30, 40, 50, 70, 80, 90]))


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

# In[ ]:

###########################
### The puZZle in a Mug ###
###########################

# importing the libraries
import scipy.stats as stats

# Adding the data
y = [1.90642, 2.22488, 2.10288, 1.69742, 1.52229, 3.15435, 2.61826, 1.98492, 1.42738, 1.99568]

# Calculating the Shapiro-Wilk test
shapiro_stat, shapiro_p_valor = stats.shapiro(y)

language = "portugues"
language = "english"

# Conclusion
if language == "english":
    print("The calculated value for the Shapiro-Wilk test is = " + str(shapiro_stat))
    print("The calculated p-value of the Shapiro-Wilk test is = " + str(shapiro_p_valor))
    # Conclusion
    if shapiro_p_valor >= 0.05:
        print("At 95% confidence level, we have NO evidence to reject the hypothesis of data normality, according to the Shapiro-Wilk test")
    else:
        print("At 95% confidence level, we have evidence to reject the hypothesis of data normality, according to the Shapiro-Wilk test")

elif language == "portugues":
    print("O valor calculado do teste de Shapiro-Wilk eh de = " + str(shapiro_stat))
    print("O p-valor calculado para o teste de Shapiro-Wilk eh de = " + str(shapiro_p_valor))
    # Conclusion
    if shapiro_p_valor >= 0.05:
        print("Com 95% de confianca, não temos evidências para rejeitar a hipótese de normalidade dos dados, segundo o teste de Shapiro-Wilk")
    else:
        print("Com 95% de confianca, temos evidências para rejeitar a hipótese de normalidade dos dados, segundo o teste de Shapiro-Wilk")
else:
    print("Unsupported language")


# Lab 08, Question 03
# AB Satyaprakash, 180123062

# imports
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from math import pow, pi, exp

# functions
# define these as per requirement!

def P(x):
    return -1

def Q(x):
    return 0

def R(x):
    return 1

def A(i):
    return 1-h*P(x[i])/2

def B(i):
    return -2+(pow(h, 2)*Q(x[i]))

def C(i):
    return 1+h*P(x[i])/2

def y(x):
    return 2*exp(x) - x-1

def L(n):
    LArray = np.zeros((n-1, n-1))
    for i in range(n-1):
        if i == 0:
            LArray[0][0], LArray[0][1] = B(1), C(1)
        elif i == n-2:
            LArray[n-2][n-3], LArray[n-2][n-2] = A(n-1), B(n-1)
        else:
            LArray[i][i-1], LArray[i][i], LArray[i][i +
                                                    1] = A(i+1), B(i+1), C(i+1)
    return LArray

def F(n):
    FArray = []
    for i in range(n-1):
        if i == 0:
            FArray.append((pow(h, 2)*R(x[1]))-(alpha*(1-h*P(x[1])/2)))
        elif i == n-2:
            FArray.append((pow(h, 2)*R(x[n-1]))-(beta*(1+h*P(x[n-1])/2)))
        else:
            FArray.append(pow(h, 2)*R(x[i+1]))
    return FArray

# program body
# BVP : y" = y+1. Thus P = -1, Q = 0, R = 1

a, b = 0, 1
alpha, beta = 1, 2*(exp(1)-1)
sol, err = [], []
x = []
h = 1/3
x = np.arange(a, b+h/2, h)
n = len(x)-1
LArray, FArray = L(n), F(n)
UArray = np.linalg.inv(LArray).dot(FArray)
Y = [alpha]
for u in UArray:
    Y.append(u)
Y.append(beta)
Act = []
for xi in x:
    Act.append(y(xi))

df = pd.DataFrame()
df['Nodal points'] = pd.Series(['0', '1/3', '2/3', '1'])
df['Actual'] = pd.Series(Act)
df['Approx'] = pd.Series(Y)
df['Error'] = pd.Series(np.array(Act)-np.array(Y))
print()
print(df)
print()

Semester 6/MA 322 (Scientific Computing Theory)/Labs/Lab 8/Code/q3.py


# note: modules are imported in each function to reduce overhead when using these utilities. This way, only the modules
# that are needed for the functions you use will be imported, rather than the modules needed for all utilities.
import numpy as np
import os

def reduced_chi_sq(model, data, errors, num_parameters):
    """ Does a reduced chi squared calculation

    .. math::
        \\chi^2 = \\sum_{k=1}^{n} \\left( \\frac{\\text{model}_k - \\text{data}_k}{\\text{error}_k} \\right) ^2

        \\chi^2_{\\text{red}} = \\frac{\\chi^2}{n - n_param - 1}

    where :math:`n` is the number of data points.

    :param model: list of values that describe a possible fit to the data
    :param data: list of values that are the data do be fitted
    :param errors: list of errors on the data
    :param num_parameters: Number of tunable pararamters in the model. This is
                           required for the reduced chi-squared calculation.
    :return: value for the reduced chi squared value of the fit of the model to the data
    """
    if not len(model) == len(data) == len(errors):
        raise ValueError("The length of the model, data, and errors need to be the same.")

    # convert to numpy arrays to vectorize computation
    model = np.array(model)
    data = np.array(data)
    errors = np.array(errors)

    chi_squared_values = ((model - data)/errors) ** 2
    chi_squared = sum(chi_squared_values)

    return chi_squared/(len(data) - num_parameters - 1)

def mag_to_flux(mag, zeropoint):
    """Convert a magnitude into a flux.

    We get the conversion by starting with the definition of the magnitude scale.

    .. math::
        m = -2.5 \\log_{10}(F) + C

        2.5 \\log_{10}(F) = C - m

        F = 10^{\\frac{C-m}{2.5}}

    :param mag: magnitdue to be converted into a flux.
    :param zeropoint: zeropoint (in mags) of the magnitude system being used
    :return: flux that corresponds to the given magnitude
    """

    return 10**((zeropoint - mag)/2.5)

def flux_to_mag(flux, zeropoint, bad_data=-99):
    """Convert flux to magnitude with the given zeropoint.

    .. math::
        m = -2.5 \\log_{10} (F) + C

    :param flux: flux in whatever units. Choose your zeropoint correctly to make this work with the units flux is in.
    :param zeropoint: zeropoint of the system (in mags)
    :param bad_data: Value to give data with negative flux. Defaults to -99.
    :return: magnitude that corresponds to the given flux
    """

    if flux > 0:
        return -2.5 * np.log10(flux) + zeropoint  # This is just the definition of magnitude
    else:
        return bad_data

def mag_errors_to_percent_flux_errors(mag_error):
    """Converts a magnitude error into a percent flux error.

    .. math::
        m = -2.5 \\log_{10} (F) + C

        dm = \\frac{-2.5}{\ln(10)} \\frac{dF}{F}

        \\frac{dF}{F} = \\frac{\\ln(10)}{2.5} dm

    The minus sign just tells us that increasing flux gives decreasing magnitudes, so we can safely ignore it.

    :param mag_error: magnitude error
    :return: percentage flux error corresponding to this magnitude error.
    """

    return mag_error * (np.log(10) / 2.5)  # math.log takes natural log unless specified.

def percent_flux_errors_to_mag_errors(percent_flux_error):
    """Converts a percentage flux error into a magnitude error.

    .. math::
        m = -2.5 \\log_{10} (F) + C

        dm = \\frac{-2.5}{\ln(10)} \\frac{dF}{F}

    :param percent_flux_error: percentage flux error
    :return: magnitude error corresponding to the percentage flux error.
    """

    return (2.5 / np.log(10)) * percent_flux_error

def empty_data(datatype):
    """
    Makes an empty data of a given datatype.

    This is useful for filling tables that have missing values.

    Here is what the various datatypes return:

    Float: np.nan

    Integer: -999999999999

    String: Empty string.

    :param datatype: data type, obtained by using `.dtype` on some numpy object.
    """

    if "f" == datatype.kind:
        return np.nan
    elif "i" == datatype.kind:
        return -999999999999
    elif "S" == datatype.kind:
        return ""

def check_if_file(possible_location):
    """
    Check if a file already exists at a given location.

    :param possible_location: File to check. Can be a path to a file, too.
    :type possible_location: str
    :return: bool representing whether or not a file already exists there.
    """

    # have to do separate cases for files in current directory and those
    # elsewhere. Those with paths has os.sep in their location.
    if os.sep in possible_location:
        # get all but the last part, which is the directory
        directory = os.sep.join(possible_location.split(os.sep)[:-1])
        # then the filename is what's left
        filename = possible_location.split(os.sep)[-1]
    else:  # it's in the current directory
        directory = "."
        filename = possible_location

    # Then check if the given file exists
    if filename in os.listdir(directory):
        return True
    else:
        return False

def gaussian(x, mean, sigma, amplitude):
    """
    The Gaussian density at the given value.

    The Gaussian density is defined as

    .. math::
        f(x) = A e ^ {- \\frac{(x - \\mu)^2}{2 \\sigma^2}}

    Note that if you want a normalized Gaussian (so that the total area
    under the curve is 1), then use the `normed_gaussian()` function.

    :param x: location to get the Gaussian density at.
    :type x: float
    :param mean: Mean of the Gaussian.
    :type mean: float
    :param sigma: Standard deviation of the Gaussian. Should be positive
    :type sigma: float
    :param amplitude: Height of the highest point of the Gaussian.
    :return: Gaussain density of the given gaussian at the given x value.

    """

    return amplitude * np.e ** (-1 * ((x - mean) / (np.sqrt(2) * sigma))**2)

def normed_gaussian(x, mean, sigma):
    """
    The Gaussian density at the given value.

    The Gaussian density is defined as

    .. math::
        f(x) = \\frac{1}{\\sigma \\sqrt{2 \\pi}} e ^ {- \\frac{(x - \\mu)^2}{2 \\sigma^2}}

    Noe that if you want a Gaussian that is not normalized (ie where you
    can set the amplitude), then use the `gaussian()` function.

    :param x: location to get the Gaussian density at.
    :type x: float
    :param mean: Mean of the Gaussian.
    :type mean: float
    :param sigma: Standard deviation of the Gaussian. Should be positive
    :type sigma: float
    :return: Gaussain density of the given gaussian at the given x value.

    """

    # first see if amplitude is defined, and if not, normalize it.
    amplitude = 1.0 / (sigma * np.sqrt(2 * np.pi))
    return amplitude * np.e ** (-1 * ((x - mean) / (np.sqrt(2) * sigma))**2)

def flux_conv(flux_zeropont, counts_zeropoint):
    """
    Calculate the conversion factor from counts to flux.

    This is done by using the zeropoints of the magnitudes in flux and counts.
    The magnitude should be the same no matter how we calculate it, which is
    how we can derive this:

    .. math::
        \\text{mag}_\\text{flux} = \\text{mag}_\\text{counts}

        -2.5 \\log_{10}(F) + Z_F = -2.5 \\log_{10}(C) + Z_C

        \\text{where $F = $ Flux, $C = $ counts, and $Z_F$ and $Z_C$ represent the respective zeropoints when using flux or counts}

        -2.5(\\log_{10}(F) - \\log_{10}(C)) = Z_C - Z_F

        \\log_{10} \\left( \\frac{F}{C} \\right) = \\frac{Z_F - Z_C}{2.5}

        \\frac{F}{C} = 10^{\\frac{Z_F - Z_C}{2.5}}

        F = C * 10^{\\frac{Z_F - Z_C}{2.5}}

        F = C * \\text{Flux Conversion}

        \\text{where Flux Conversion $= 10^{\\frac{Z_F - Z_C}{2.5}}$}

    This flux conversion is what is calculated here.

    NOTE: Make sure the magnitudes really are the same (ie AB/Vega).

    :param flux_zeropoint: zero point for calculating magnitudes when using
                           flux, such that mag = -2.5 log(flux) +
                           `flux_zeropoint`. The actual value will depend
                           on what flux units you want. If you want
                           microJanskys, use 23.9, for example.
    :type flux_zeropoint: float
    :param counts_zeropoint: zero point for calculating magnitues when using
                             counts, such that mag = -2.5 log(counts) +
                             `counts_zeropoint`. This is often found in the
                             header of the image in question.
    :type counts_zeropoing: float
    :returns: float containing the conversion from counts to flux, such that
              flux = counts * flux_conv. See above for derivation.

    """

    return 10 ** ((flux_zeropont - counts_zeropoint) / 2.5)

# Store the vega to AB conversions. They must all be of the form
# m_AB = m_Vega + conversion
vega_to_ab_conversions = {"w1": 2.683,
                          "w2": 3.319,
                          "w3": 5.242,
                          "w4": 6.604,
                          "ch1": 2.787,
                          "ch2": 3.260,
                          "sloan_u": 0.904,
                          "sloan_g": -0.098,
                          "sloan_r": 0.146,
                          "sloan_i": 0.357,
                          "sloan_z": 0.521}

def vega_to_ab(vega_mag, band):
    """
    Converts a Vega mag to AB.

    This basically contains a bunch of AB-Vega conversions, so I don't have
    to look them up every time I want to do anything.

    :param vega_mag: magnitude in the Vega system to be converted to AB.
    :type vega_mag: float
    :param band: Band the magnitude is in. Can be one of the following: "w1",
                 "w2", "w3", "w4".
    :type band: str
    :returns: float containing the ab magnitude
    """
    # use the dictionary I defined above.
    try:
        return vega_mag + vega_to_ab_conversions[band]
    except KeyError:
        print("{} has not yet been implemented. Find the conversion "
              "and add it, please. ".format(band))

def ab_to_vega(ab_mag, band):
    """
    Converts a AB mag to Vega.

    This basically contains a bunch of AB-Vega conversions, so I don't have
    to look them up every time I want to do anything.

    :param ab_mag: magnitude in the AB system to be converted to Vega.
    :type ab_mag: float
    :param band: Band the magnitude is in. Can be one of the following: "w1",
                 "w2", "w3", "w4".
    :type band: str
    :returns: float containing the Vega magnitude
    """

    # use the dictionary I defined above.
    try:
        return ab_mag - vega_to_ab_conversions[band]
    except KeyError:
        print("{} has not yet been implemented. Find the conversion "
              "and add it, please. ".format(band))

# Note: these all need to be tested.


# Comparisons, Masks, and Boolean Logic:

# Masking is useful when you want to extract, modify, count, or just
# manipulate values in an array based on some arbitrary criteria.

import numpy as np
import pandas as pd

# %matplotlib inline
import matplotlib.pyplot as plt
import seaborn
seaborn.set()

# Count rainy days for Seattle in 2014.
rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values
inches = rainfall / 254.0   # 1/10mm to inches conversion
inches.shape

plt.hist(inches, 40)
# The histogram plot shows the general shape of data, masking reveals more.

# Masking with Comparison Operator uFuncs:
# <, >, <=, >=, !=, ==

# The returned result is always an array with boolean data type, where
# True == 1, False == 0.

x = np.array([1, 2, 3, 4, 5])
x < 3       # equivalent to np.less(x, 3)
x > 3       # np.greater(x, 3)
x <= 3      # np.less_equal(x, 3)
x >= 3      # np.greater_equal(x, 3)
x != 3      # np.notequal(x, 3)
x == 3      # np.equal(x, 3)

# As before, these work with multi-dimensional arrays.
rng = np.random.RandomState(0)
x = rng.randint(10, size=(3, 4))
x < 6

# Both of these calls count total True entries on x < 6.
np.count_nonzero(x < 6)
np.sum(x < 6)

# using sum, we can break up along axes as well
np.sum(x < 6, axis=1)

np.any(x > 8)   # any vals > 8, returns bool (True)
np.all(x < 0)   # any vals < 0, returns bool (False)
np.all(x < 10)
np.all(x == 6)
# can also break these up along axes:
np.all(x < 8, axis=1)

# Masking with Bitwise Operator uFuncs:
# &, |, ^, ~

# &     np.bitwise_and
# |     np.bitwise_or
# ^     np.bitwise_xor
# ~     np.bitwise_not

# Recall bitwise operator precedence - parens required.
np.sum((inches > .5) & (inches < 1))
np.sum(inches == 0)
np.sum(inches != 0)
np.sum(inches > 0.5)
np.sum((inches > 0) & (inches < .2))

# Boolean Arrays as Masks:

# In prior section, used aggregates computed directly on Boolean arrays.
# Can also use Boolean arrays as masks in order to select subsets of data

# "Get x such that x < 5."
x[x < 5]

# Make rainy the mask of all rainy days.
# Then make summer the mask of all summer days (June 21st is 172nd day).
rainy = (inches > 0)
days = np.arange(365)
summer = (days > 172) & (days < 262)

np.median(inches[rainy])
np.median(inches[summer])
np.max(inches[summer])
np.median(inches[rainy & ~summer])

# Aside: using keywords "and/or" vs Operators "&/|"

# The keywords 'and/or' operate on the entire object
# The symbols  ' & / |' operate on the bits of the inner int/char/etc.

# using "and" or "or" asks Python to treat the object act a single Bool entity

# NOTE: when dealing specifically with numpy arrays,
# ALWAYS use bitwise operators.


from abc import ABC, abstractmethod
import numpy as np

class Activation(ABC):
    'This is an abstract class for activation functions'
    def __init__(self, name):
        self.name = name

    @abstractmethod
    def apply(self, Z):
        return

    @abstractmethod
    def derivative(self, Z, **kwargs):
        return

class Identity(Activation):
    'Applies identity'
    def __init__(self, name='Identity'):
        super().__init__(name)

    def apply(self, Z):
        return Z

    def derivative(self, Z, **kwargs):
        return np.full(Z.shape, 1)

class Sigmoid(Activation):
    'Applies sigmoid'
    def __init__(self, name='Identity'):
        super().__init__(name)

    def apply(self, Z):
        return 1 / (1 + np.exp(-Z))

    def derivative(self, Z, A):
        return A * (1 - A)

class Softmax(Activation):
    'Applies softmax'
    def __init__(self, name='Softmax'):
        super().__init__(name)

    def apply(self, Z):
        ''' Normalizes to probability '''
        # e_Z = np.exp(Z - np.max(Z))
        # return e_Z / e_Z.sum(axis=0)
        assert len(Z.shape) == 2
        s = np.max(Z, axis=0) #.reshape(1, -1)
        e_Z = np.exp(Z - s)
        div = np.sum(e_Z, axis=0) #.reshape(1, -1)
        return e_Z / div

    def derivative(self, Z, **kwargs):
        '''WARNING: The behavior of this function is abnormal due to the math of softmax

        Z ---softmax---> A ---CategoricalCrossEntropy---> Loss

        Turns out that dZ = Yhat - Y, therefore (Yhat - Y) is passed in backpropagation

        Input:
            dA = Yhat - Y
        '''

        return 1

class Relu(Activation):
    'Applies Rectified Linear Unit'
    def __init__(self, name='Relu'):
        super().__init__(name)

    def apply(self, Z):
        return Z.clip(min=0)

    def derivative(self, Z, **kwargs):
        return np.greater(Z, 0).astype(int)

class LeakyRelu(Activation):
    'Applies Leaky Relu'
    def __init__(self, name='Leaky Relu', leak=0.01):
        super().__init__(name)
        self.leak = 0.01

    def apply(self, Z):
        return np.where(Z > 0, Z, Z * self.leak)

    def derivative(self, Z, **kwargs):
        return np.where(Z > 0, 1, self.leak)

def test_softmax():
    soft = Softmax()

    x1 = np.array([[1, 2, 3, 6]]).T

    print(x1)

    print(soft.apply(x1))

if __name__ == '__main__':
    # Testing
    test_softmax()


import numpy as np

ary = np.array([1, 2, 3, 4])  # array([1, 2, 3, 4])

ary[0]  # 1
ary[2]  # 3
ary[::2]  # array([1, 3])

np.array([1, 2, 3, 4], dtype=np.float32)
# array([1., 2., 3., 4.], dtype=float32)

arr = np.array([1, 2, 3, 4])
print(arr.dtype) # dtype('int64')
arr.dtype = np.float32
print(arr)
# [1.e-45 0.e+00 3.e-45 0.e+00 4.e-45 0.e+00 6.e-45 0.e+00]

arr = arr.astype(np.float32)
print(arr)
# [1. 2. 3. 4.]


import numpy as np

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

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

    Returns:
      probs, np array of the same shape as predictions -
        probability for every class, 0..1
    '''
    # Implement softmax
    # Your final implementation shouldn't have any loops

    if predictions.ndim == 1:
      predictions -= np.max(predictions)
      predictions_exp = np.exp(predictions)
      probs = predictions_exp/np.sum(predictions_exp)
      return probs

    batch_max = np.max(predictions, axis=1)[:, np.newaxis]
    predictions -= batch_max
    predictions_exp = np.exp(predictions)
    batch_sum = np.sum(predictions_exp, axis=1)
    probs = predictions_exp/batch_sum[:, np.newaxis]

    return probs

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

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

    Returns:
      loss: single value
    '''
    # Implement cross-entropy
    # Your final implementation shouldn't have any loops
    if probs.ndim == 1:
      loss = - np.log(probs[target_index])
      return loss

    batch_index = np.arange(probs.shape[0])
    batch_size = batch_index.shape[0]
    loss = - np.sum(np.log(probs[batch_index, target_index]))/batch_size

    return loss

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

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

    Returns:
      loss, single value - l2 regularization loss
      gradient, np.array same shape as W - gradient of weight by l2 loss
    '''
    # Copy from previous assignment
    grad = reg_strength*2*W
    loss = reg_strength*np.sum(W**2)

    return loss, grad

def softmax_with_cross_entropy(predictions, target_index):
    '''
    Computes softmax and cross-entropy loss for model predictions,
    including the gradient

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

    Returns:
      loss, single value - cross-entropy loss
      dprediction, np array same shape as predictions - gradient of predictions by loss value
    '''
    # Copy from the previous assignment
    probs = softmax(predictions.copy())
    loss = cross_entropy_loss(probs, target_index)
    dprediction = probs.copy()

    if predictions.ndim == 1:
      dprediction[target_index] -= 1

      return loss, dprediction

    batch_size = target_index.shape[0]
    batch_index = np.arange(batch_size)
    dprediction[batch_index, target_index] -= 1
    dprediction /= batch_size

    return loss, dprediction

class Param:
    '''
    Trainable parameter of the model
    Captures both parameter value and the gradient
    '''
    def __init__(self, value):
        self.value = value
        self.grad = np.zeros_like(value)

class ReLULayer:
    def __init__(self):
        pass

    def forward(self, X):
        # Copy from the previous assignment
        result = np.maximum(X, 0)
        self.X = X

        return result

    def backward(self, d_out):
        # Copy from the previous assignment
        d_result = d_out
        d_result[self.X < 0] = 0

        return d_result

    def params(self):
        return {}

class FullyConnectedLayer:
    def __init__(self, n_input, n_output):
        self.W = Param(0.001 * np.random.randn(n_input, n_output))
        self.B = Param(0.001 * np.random.randn(1, n_output))
        self.X = None

    def forward(self, X):
        # Copy from the previous assignment
        self.X = X
        result = X @ self.W.value + self.B.value

        return result

    def backward(self, d_out):
        # Copy from the previous assignment

        self.W.grad += self.X.T @ d_out
        self.B.grad += np.mean(d_out, axis=0) * d_out.shape[0]

        d_input = d_out @ self.W.value.T

        return d_input

    def params(self):
        return { 'W': self.W, 'B': self.B }

class ConvolutionalLayer:
    def __init__(self, in_channels, out_channels,
                 filter_size, padding):
        '''
        Initializes the layer

        Arguments:
        in_channels, int - number of input channels
        out_channels, int - number of output channels
        filter_size, int - size of the conv filter
        padding, int - number of 'pixels' to pad on each side
        '''

        self.filter_size = filter_size
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.W = Param(
            np.random.randn(filter_size, filter_size,
                            in_channels, out_channels)
        )

        self.B = Param(np.zeros(out_channels))

        self.padding = padding
        self.X = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        out_height = height - self.filter_size + 2*self.padding + 1
        out_width = width - self.filter_size + 2*self.padding + 1
        result = np.empty((batch_size, out_height, out_width, self.out_channels))
        self.X = X

        X_padded = np.zeros((batch_size, height+2*self.padding, width+2*self.padding, channels))
        X_padded[:, self.padding:self.padding + height, self.padding:self.padding + width, :] = X
        self.X_padded = X_padded

        # Implement forward pass
        # Hint: setup variables that hold the result
        # and one x/y location at a time in the loop below

        # It's ok to use loops for going over width and height
        # but try to avoid having any other loops
        for y in range(out_height):
            for x in range(out_width):
                # Implement forward pass for specific location
                receptive_field = X_padded[:, y:y+self.filter_size, x:x+self.filter_size]
                receptive_field_col = receptive_field.reshape(batch_size, -1)
                W = self.W.value.reshape(-1, self.out_channels)
                result[:, y, x, :] = receptive_field_col @ W + self.B.value

        return result

    def backward(self, d_out):
        # Hint: Forward pass was reduced to matrix multiply
        # You already know how to backprop through that
        # when you implemented FullyConnectedLayer
        # Just do it the same number of times and accumulate gradients

        batch_size, height, width, channels = self.X.shape
        _, out_height, out_width, out_channels = d_out.shape

        d_input =  np.zeros_like(self.X_padded)

        # Implement backward pass
        # Same as forward, setup variables of the right shape that
        # aggregate input gradient and fill them for every location
        # of the output

        # Try to avoid having any other loops here too
        for y in range(out_height):
            for x in range(out_width):
                # Implement backward pass for specific location
                # Aggregate gradients for both the input and
                # the parameters (W and B)

                X_expanded = self.X_padded[:, y:y+self.filter_size, x:x+self.filter_size, :, np.newaxis]
                d_out_expanded = d_out[:, y, x, np.newaxis, np.newaxis, np.newaxis, :]
                self.W.grad += np.sum(d_out_expanded * X_expanded, axis=0)

                self.B.grad += np.sum(d_out[:, y, x], axis=0).reshape(-1)

                W = self.W.value.T.reshape(out_channels, -1)
                d_field_column = d_out[:, y, x] @ W
                d_field_inverse = d_field_column.reshape(batch_size, channels,
                                                         self.filter_size, self.filter_size)
                d_field = np.moveaxis(d_field_inverse.T, 3, 0)
                d_input[:, y:y+self.filter_size, x:x+self.filter_size] += d_field

        return d_input[:, self.padding:self.padding+height, self.padding:self.padding+width, :]

    def params(self):
        return { 'W': self.W, 'B': self.B }

class MaxPoolingLayer:
    def __init__(self, pool_size, stride):
        '''
        Initializes the max pool

        Arguments:
        pool_size, int - area to pool
        stride, int - step size between pooling windows
        '''
        self.pool_size = pool_size
        self.stride = stride
        self.X = None
        self.M = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        # Implement maxpool forward pass
        # Hint: Similarly to Conv layer, loop on
        # output x/y dimension

        X = np.moveaxis(X, 3, 1)
        self.M = np.zeros_like(X)
        out_height = int((height - self.pool_size)/self.stride + 1)
        out_width = int((width - self.pool_size)/self.stride + 1)
        result = np.empty((batch_size, out_height, out_width, channels))

        for y in range(out_height):
            for x in range(out_width):
                region = X[:,
                           :,
                           y*self.stride:y*self.stride+self.pool_size,
                           x*self.stride:x*self.stride+self.pool_size]
                region_col = region.reshape(batch_size, channels, -1)
                max_items = region_col.max(axis=2)

                result[:, y, x, :] = max_items

                maximum_region = np.zeros_like(region)
                indices = (region_col == max_items[:,:, np.newaxis]).reshape(region.shape)
                maximum_region[indices] = 1.0
                self.M[:, :, y*self.stride:y*self.stride+self.pool_size, x*self.stride:x*self.stride+self.pool_size] = maximum_region

        self.M = np.moveaxis(self.M, 1, 3)

        return result

    def backward(self, d_out):
        # Implement maxpool backward pass
        _, out_height, out_width, out_channels = d_out.shape

        for y in range(out_height):
            for x in range(out_width):
                region = self.M[:, y*self.stride:y*self.stride+self.pool_size,
                                x*self.stride:x * self.stride+self.pool_size, :]
                d_region = d_out[:, np.newaxis, np.newaxis, y, x, :]
                region *= d_region

        return self.M

    def params(self):
        return {}

class Flattener:
    def __init__(self):
        self.X_shape = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        self.X_shape = X.shape

        # Implement forward pass
        # Layer should return array with dimensions
        # [batch_size, hight*width*channels]

        return X.reshape(batch_size, height*width*channels)

    def backward(self, d_out):
        # Implement backward pass

        return d_out.reshape(self.X_shape)

    def params(self):
        # No params!
        return {}


import matplotlib.pyplot as plt
import numpy as np
import scipy.io as scio
import linearRegCostFunction as lrcf
import trainLinearReg as tlr
import learningCurve as lc
import polyFeatures as pf
import featureNormalize as fn
import plotFit as plotft
import validationCurve as vc

plt.ion()
np.set_printoptions(formatter={'float': '{: 0.6f}'.format})

# ===================== Part 1: Loading and Visualizing Data =====================
# We start the exercise by first loading and visualizing the dataset.
# The following code will load the dataset into your environment and pot
# the data.
#

# Load Training data
print('Loading and Visualizing data ...')

# Load from ex5data1:
data = scio.loadmat('ex5data1.mat')
X = data['X']
y = data['y'].flatten()
Xval = data['Xval']
yval = data['yval'].flatten()
Xtest = data['Xtest']
ytest = data['ytest'].flatten()

m = y.size

# Plot training data
plt.figure()
plt.scatter(X, y, c='r', marker="x")
plt.xlabel('Change in water level (x)')
plt.ylabel('Water folowing out of the dam (y)')

input('Program paused. Press ENTER to continue')

# ===================== Part 2: Regularized Linear Regression Cost =====================
# You should now implement the cost function for regularized linear regression
#

theta = np.ones(2)
cost, _ = lrcf.linear_reg_cost_function(theta, np.c_[np.ones(m), X], y, 1)

print('Cost at theta = [1  1]: {:0.6f}\n(this value should be about 303.993192'.format(cost))

input('Program paused. Press ENTER to continue')

# ===================== Part 3: Regularized Linear Regression Gradient =====================
# You should now implement the gradient for regularized linear regression
#

theta = np.ones(2)
cost, grad = lrcf.linear_reg_cost_function(theta, np.c_[np.ones(m), X], y, 1)

print('Gradient at theta = [1  1]: {}\n(this value should be about [-15.303016  598.250744]'.format(grad))

input('Program paused. Press ENTER to continue')

# ===================== Part 4: Train Linear Regression =====================
# Once you have implemented the cost and gradient correctly, the
# train_linear_reg function will use your cost function to train regularzized linear regression.
#
# Write Up Note : The data is non-linear, so this will not give a great fit.
#

# Train linear regression with lambda = 0
lmd = 0

theta = tlr.train_linear_reg(np.c_[np.ones(m), X], y, lmd)

# Plot fit over the data
plt.plot(X, np.dot(np.c_[np.ones(m), X], theta))

input('Program paused. Press ENTER to continue')

# ===================== Part 5: Learning Curve for Linear Regression =====================
# Next, you should implement the learning_curve function.
#
# Write up note : Since the model is underfitting the data, we expect to
#                 see a graph with "high bias" -- Figure 3 in ex5.pdf
#

lmd = 0
error_train, error_val = lc.learning_curve(np.c_[np.ones(m), X], y, np.c_[np.ones(Xval.shape[0]), Xval], yval, lmd)

plt.figure()
plt.plot(np.arange(m), error_train, np.arange(m), error_val)
plt.title('Learning Curve for Linear Regression')
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('Number of Training Examples')
plt.ylabel('Error')
plt.axis([0, 13, 0, 150])

input('Program paused. Press ENTER to continue')

# ===================== Part 6 : Feature Mapping for Polynomial Regression =====================
# One solution to this is to use polynomial regression. You should now
# complete polyFeatures to map each example into its powers
#

p = 5

# Map X onto Polynomial Features and Normalize
X_poly = pf.poly_features(X, p)
X_poly, mu, sigma = fn.feature_normalize(X_poly)
X_poly = np.c_[np.ones(m), X_poly]

# Map X_poly_test and normalize (using mu and sigma)
X_poly_test = pf.poly_features(Xtest, p)
X_poly_test -= mu
X_poly_test /= sigma
X_poly_test = np.c_[np.ones(X_poly_test.shape[0]), X_poly_test]

# Map X_poly_val and normalize (using mu and sigma)
X_poly_val = pf.poly_features(Xval, p)
X_poly_val -= mu
X_poly_val /= sigma
X_poly_val = np.c_[np.ones(X_poly_val.shape[0]), X_poly_val]

print('Normalized Training Example 1 : \n{}'.format(X_poly[0]))

input('Program paused. Press ENTER to continue')

# ===================== Part 7 : Learning Curve for Polynomial Regression =====================
# Now, you will get to experiment with polynomial regression with multiple
# values of lambda. The code below runs polynomial regression with
# lambda = 0. You should try running the code with different values of
# lambda to see how the fit and learning curve change.
#

lmd = 0
theta = tlr.train_linear_reg(X_poly, y, lmd)

# Plot trainint data and fit
plt.figure()
plt.scatter(X, y, c='r', marker="x")
plotft.plot_fit(np.min(X), np.max(X), mu, sigma, theta, p)
plt.xlabel('Change in water level (x)')
plt.ylabel('Water folowing out of the dam (y)')
plt.ylim([0, 60])
plt.title('Polynomial Regression Fit (lambda = {})'.format(lmd))

error_train, error_val = lc.learning_curve(X_poly, y, X_poly_val, yval, lmd)
plt.figure()
plt.plot(np.arange(m), error_train, np.arange(m), error_val)
plt.title('Polynomial Regression Learning Curve (lambda = {})'.format(lmd))
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('Number of Training Examples')
plt.ylabel('Error')
plt.axis([0, 13, 0, 150])

print('Polynomial Regression (lambda = {})'.format(lmd))
print('# Training Examples\tTrain Error\t\tCross Validation Error')
for i in range(m):
    print('  \t{}\t\t{}\t{}'.format(i, error_train[i], error_val[i]))

input('Program paused. Press ENTER to continue')

# ===================== Part 8 : Validation for Selecting Lambda =====================
# You will now implement validationCurve to test various values of
# lambda on a validation set. You will then use this to select the
# 'best' lambda value.

lambda_vec, error_train, error_val = vc.validation_curve(X_poly, y, X_poly_val, yval)

plt.figure()
plt.plot(lambda_vec, error_train, lambda_vec, error_val)
plt.legend(['Train', 'Cross Validation'])
plt.xlabel('lambda')
plt.ylabel('Error')

input('ex5 Finished. Press ENTER to exit')


"""
From Similarity Preserving Graph Convolutional Networks (SimPGCN).
`Node Similarity Preserving Graph Convolutional Networks
<https://arxiv.org/abs/2011.09643>`

Original implementation: <https://github.com/ChandlerBang/SimP-GCN>
and <https://github.com/DSE-MSU/DeepRobust>
"""

import numpy as np
import scipy.sparse as sp
from sklearn.metrics.pairwise import cosine_similarity
from itertools import product

def knn_graph(x, k=20):
    X = np.zeros_like(x)
    X[x != 0] = 1
    sims = cosine_similarity(X)
    sims[np.diag_indices(len(sims))] = 0.

    for i, sim in enumerate(sims):
        indices_argsort = np.argsort(sim)
        sims[i, indices_argsort[: -k]] = 0.

    adj_knn = sp.csr_matrix(sims)
    return adj_knn

def attr_sim(x, k=5):
    X = np.zeros_like(x)
    X[x != 0] = 1.

    sims = cosine_similarity(X)
    indices_sorted = sims.argsort(1)
    selected = np.hstack((indices_sorted[:, :k],
                          indices_sorted[:, - k - 1:]))

    selected_set = set()
    for i in range(len(sims)):
        for pair in product([i], selected[i]):
            if pair[0] > pair[1]:
                pair = (pair[1], pair[0])
            if pair[0] == pair[1]:
                continue
            selected_set.add(pair)

    sampled = np.transpose(list(selected_set))
#     print('number of sampled:', len(sampled[0]))
    node_pairs = (sampled[0], sampled[1])
    return sims[node_pairs], node_pairs


import numpy as np
import math

def quantify_confusion(N, f=lambda x: x * (x - 1) * 0.5, exact=False):
    if len(N)<=0: return 0
    f = np.vectorize(f, otypes=[np.float])
    b2 = np.ones((N.shape[1], 1), dtype='float')
    b1 = np.ones((1, N.shape[0]), dtype='float')
    m1 = b1.dot(N)
    m2 = N.dot(b2)
    m = b1.dot(N).dot(b2)
    s1 = (b1.dot(f(m2)))
    s2 = (f(m1).dot(b2))
    n = f(m)
    I = (b1.dot(f(N)).dot(b2))
    if exact:
        E = (b1.dot(f(m2).dot(f(m1)) / f(m))).dot(b2)
    else:
        E = (b1.dot(f(m2.dot(m1) / m))).dot(b2)
    AG = float((s1 + s2 - 2 * I) / ((s1 + s2) - 2 * E))
    return 1 - AG

def nmi(e): return quantify_confusion(e, f=lambda x: x * math.log(x) if x > 0 else 0)
def kappa(e): return quantify_confusion(e, f=lambda x: x * x )
def ari(e): return quantify_confusion(e, f=lambda x: x * (x - 1), exact=True)
def ari2(e): return quantify_confusion(e, f=lambda x: x * (x - 1))

'''
def test():
    ct = np.array([[3, 1], [0, 3], [3, 0]])
    nct = ct*1.0/np.sum(ct)

    print nct
    print "NMI: " , nmi(ct), " == ", nmi(nct)
    print "kappa: " , kappa(ct), " == ", kappa(nct)

    # more variations, you can play with function f to get new measures
    print "other variations: ", ari(ct), ari2(ct)

if __name__ == '__main__':
    test()
    '''


"""Fundamental Analysis Trendline Module"""
__docformat__ = "numpy"

from pandas.core.frame import DataFrame
import yfinance as yf
from scipy.stats import linregress

def load_ticker(ticker: str, start_date: str) -> DataFrame:
    """
    Loads a ticker data from Yahoo Finance, adds a data index column data_id and Open-Close High/Low columns.

    Parameters
    ----------
    ticker : str
        The stock ticker.
    start_date : str
        Start date to load stock ticker data formatted YYYY-MM-DD.

    Returns
    -------
    DataFrame
        A Panda's data frame with columns Open, High, Low, Close, Adj Close, Volume, date_id, OC-High, OC-Low.
    """
    print(f"Start date: {start_date}")
    df_data = yf.download(ticker, start=start_date, progress=False)

    df_data["date_id"] = (df_data.index.date - df_data.index.date.min()).astype(
        "timedelta64[D]"
    )
    df_data["date_id"] = df_data["date_id"].dt.days + 1

    df_data["OC_High"] = df_data[["Open", "Close"]].max(axis=1)
    df_data["OC_Low"] = df_data[["Open", "Close"]].min(axis=1)

    return df_data

def find_trendline(df_data: DataFrame, y_key: str, high_low: str = "high") -> DataFrame:
    """Attempts to find a trend line based on y_key column from a given stock ticker data frame.

    Parameters
    ----------
    df_data : DataFrame
        The stock ticker data frame with at least date_id, y_key columns.

    y_key : str
        Column name to base the trend line on.

    high_low: str, optional
        Either "high" or "low". High is the default.

    Returns
    -------
    DataFrame
        An updated Panda's data frame with {y_key}_trend column added containing trend data
            - if a trend is successfully found.
        An original Panda's data frame
            - if no trend was found.
    """

    for iteration in [3, 4, 5, 6, 7]:
        df_temp = df_data.copy()
        while len(df_temp) > iteration:
            reg = linregress(
                x=df_temp["date_id"],
                y=df_temp[y_key],
            )

            if high_low == "high":
                df_temp = df_temp.loc[
                    df_temp[y_key] > reg[0] * df_temp["date_id"] + reg[1]
                ]
            else:
                df_temp = df_temp.loc[
                    df_temp[y_key] < reg[0] * df_temp["date_id"] + reg[1]
                ]

        if len(df_temp) > 1:
            break

    if len(df_temp) == 1:
        return df_data

    reg = linregress(
        x=df_temp["date_id"],
        y=df_temp[y_key],
    )

    df_data[f"{y_key}_trend"] = reg[0] * df_data["date_id"] + reg[1]

    return df_data


# -*- coding: utf-8 -*-
"""
Created on Fri Apr 26 12:26:13 2019

@author: Marcezar
"""

import numpy as np
import numpy.linalg as npl

def A_norm(x,A):
    return np.sqrt(np.vdot(x,A@x))

def cg(A,b,x0=None,tol=1e-5,maxiter=None,M1=None,M3=None,x_exact=None):
    n = A.shape[0]
    if x0 is None:
        x = np.zeros(n)
    else:
        x = x0
    if maxiter is None:
        maxiter = 3*n
    r = b-A@x
    res_arr = np.zeros(maxiter)
    res_arr[0] = npl.norm(r)
    if x_exact is not None:
        err_arr = np.zeros(maxiter)
        err_arr[0] = A_norm(x-x_exact,A)
    i = 0

    # unpreconditioned CG-method
    if M1 is None  and M3 is None:
        p = r

        while res_arr[i] > tol*res_arr[0] and i < maxiter-1:
            i = i+1
            w       = A@p
            aph     = np.vdot(r,r)/np.vdot(p,w)
            x       = x + aph*p
            rnew    = r - aph*w
            bta     = np.vdot(rnew,rnew)/np.vdot(r,r)
            p       = rnew + bta*p
            r       = rnew
            res_arr[i] = npl.norm(r)
            if x_exact is not None:
                err_arr[i] = A_norm(x-x_exact,A)

    # CG method for using a preconditioner
    elif M1 is not None and M3 is None:
        y = p = M1@r
        while res_arr[i] > tol*res_arr[0] and i < maxiter-1:
            i = i+1
            w       = A@p
            aph     = np.vdot(y,r)/np.vdot(p,w)
            x       = x + aph*p
            rnew    = r - aph*w
            ynew    = M1@rnew
            bta     = np.vdot(ynew,rnew)/np.vdot(r,y)
            p       = ynew + bta*p
            r       = rnew
            y       = ynew
            res_arr[i] = npl.norm(r)
            if x_exact is not None:
                err_arr[i] = A_norm(x-x_exact,A)

    # CG method for using the P_DEF-Operator
    elif M1 is not None and M3 is not None:
        y = p = M1@r
        while res_arr[i] > tol*res_arr[0] and i < maxiter-1:
            i = i+1
            w       = M3@(A@p)
            aph     = np.vdot(y,r)/np.vdot(p,w)
            x       = x + aph*p
            rnew    = r - aph*w
            ynew    = M1@rnew
            bta     = np.vdot(ynew,rnew)/np.vdot(r,y)
            p       = ynew + bta*p
            r       = rnew
            y       = ynew
            res_arr[i] = npl.norm(r)
            if x_exact is not None:
                err_arr[i] = A_norm(x-x_exact,A)
        Qb = np.conj(M3.U.T)@b
        Qb = np.linalg.solve(M3.E,Qb)
        Qb = M3.U@Qb
        x = Qb + M3.H@x

    else:
        raise NotImplementedError("no 'M1' given")

    res_arr = np.delete(res_arr,range(i+1,maxiter))

    if x_exact is not None:
        err_arr = np.delete(err_arr,range(i+1,maxiter))
        return (x,res_arr,err_arr)
    else:
        return (x,res_arr)


#!python3

"""
A function to pick a random number according to the power-law distribution,
  with PDF: f(z) = minValue/z^2 for z>=zmin.

Note that it is different than numpy.random.power and scipy.random.powerlaw.

@author Erel Segal-Halevi
@since  2017-10
"""

import numpy as np

def random_powerlaw(minValue, power=2, size=1):
	"""
	Draws a random value according to the power-law distribution, with:
	* CDF: F(z) = 1-(minValue/z)^(power-1) for z>=zmin
	Uses inverse random sampling: https://en.wikipedia.org/wiki/Inverse_transform_sampling

	minValue: smallest value from which the PDF is nonzero.
	size:     number of random values to return (in a numpy array)
	power:    the power of the law.
	"""

	u = np.random.uniform(0,1,size)
	# The inverse CDF is:  F^{-1}(u) = minValue/(1-u)^(1/(k-1))
	return minValue/(1-u)**(1/(power-1))

if __name__ == "__main__":
	import matplotlib.pyplot as plt
	minValue = 0.001
	values=random_powerlaw(minValue=minValue,size=10000,power=2.5)
	plt.hist(values) # All except 20 samples are in [0,500]
	maxValue = max(values)
	xValues = np.linspace(minValue,maxValue,10000)
	plt.plot(xValues, minValue/xValues**2)  # Plot visible only by zooming to [0, 0.1]
	plt.show()


import matplotlib.pyplot as plt
import numpy as np
from functools import partial
from scipy import stats

GAMMA_LIST = np.logspace(0, .8, num=4)
BETA_PRODUCT = 1
NUM = 20_000_000
BIN_NUMBER = 200

LOG_FUDGE_FACTOR = .2

def color_num(gamma):
    return ((np.log(gamma) - np.log(min(GAMMA_LIST)))
    / (
        np.log(max(GAMMA_LIST))
      - np.log(min(GAMMA_LIST))
      + LOG_FUDGE_FACTOR)
    )

def beta(gamma):
    return np.sqrt(1 - 1 / gamma)

def boost_angle_com_to_lab(theta, gamma, beta_product):
    return np.arctan(
        np.sin(theta)
        / gamma
        / (
            np.cos(theta)
            + beta(gamma) / beta_product
        )
    ) % (np.pi)

class SineRandomVariable(stats.rv_continuous):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.a = 0.
        self.b = np.pi

    def _pdf(self, theta):
        return (np.sin(theta) / 2)

    def _ppf(self, x):
        return(np.arccos(1 - 2*x))

def angular_distribution_shift():

    angles_com = SineRandomVariable().rvs(size=NUM)

    colormap = plt.get_cmap('plasma')
    def color(gamma):
        return colormap(color_num(gamma))

    fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})

    for gamma in GAMMA_LIST:
        angles_lab = boost_angle_com_to_lab(angles_com, gamma=gamma, beta_product=BETA_PRODUCT)

        vals, bins_with_edges = np.histogram(angles_lab, bins=BIN_NUMBER)
        bins = (
            bins_with_edges[1:]+
            bins_with_edges[:-1]
            ) / 2
        vals = vals / max(vals)

        ax.plot(bins, vals, label=f'$\gamma=$ {gamma:.1f}', color=color(gamma))
        # ax.plot(2 * np.pi - bins, vals, color=color(gamma))

        ax.axvline(1 / gamma, color=color(gamma), ls=':')
        # ax.axvline(-1 / gamma, color=color(gamma), ls=':')

        # fraction_below_gamma_inv = (
        #     sum(1 for ang in angles_lab if ang < 1/ gamma)
        #     / len(angles_lab)
        # )

        # print(f'Fraction sent below $1/\\gamma$: {100*fraction_below_gamma_inv:.1f}%')

    # ax.set_theta_zero_location("N")
    ax.set_thetamin(0)
    ax.set_thetamax(180)
    # ax.set_xlabel('$\\theta$')
    # ax.set_ylabel('$p(\\theta)$')
    ax.legend()
    ax.set_yticklabels([])
    plt.tight_layout()

if __name__ == "__main__":

    from make_all_figures import plot_and_save
    plot_and_save(angular_distribution_shift)


import numpy as np

def integrateedo(x0, N, h, method):

    if method == "runge-kutta-2":
        xk = x0
        tk = f(0, x0)
        c = 0
        while c < N:
            K1 = f(tk, xk)
            K2 = f(tk + h, xk + h*K1)
            xk1 = xk + (h/2.0)*(K1+K2)
            tk = (c+1)*h
            xk = xk1
            c += 1
        return xk
    elif method == "runge-kutta-4":
        xk = x0
        tk = f(0, x0)
        c = 0
        while c < N:
            K1 = f(tk, xk)
            K2 = f(tk + h/2, xk + (h/2)*K1)
            K3 = f(tk + h/2, xk + (h/2)*K2)
            K4 = f(tk + h, xk + h*K3)
            xk1 = xk + (h/6)*(K1+2*K2+2*K3+K4)
            tk = (c+1)*h
            xk = xk1
            c += 1
        return xk
    elif method == "euler":
        xk = x0
        tk = f(0, x0)
        c = 0
        while c < N:
            K1 = f(tk, xk)
            xk1 = xk + K1*h
            tk = (c+1)*h
            xk = xk1
            c += 1
        return xk
    return 0

def f(t, x):
    return t + x

x0 = 0.0
N = 3
method = "euler"
h=0.1

result = integrateedo(x0, N, h, method)
print(result)


import unittest

import numpy
import condor.utils.rotation as rotation

class TestCaseRotation(unittest.TestCase):
    def test_R_x(self):
        # v =  [x, y, z]
        v_a = numpy.array([0, 1, 0])
        a = numpy.pi/2.
        v_b = rotation.R_x(a).dot(v_a)
        # Rotation matrix around x-axis - observing the right hand rule
        v_b_expected = numpy.array([0, 0, 1])
        for v_b_i, v_b_expected_i in zip(v_b, v_b_expected):
            self.assertAlmostEqual(v_b_i, v_b_expected_i)

    def test_R_y(self):
        # v =  [x, y, z]
        v_a = numpy.array([1, 0, 0])
        a = -numpy.pi/2.
        v_b = rotation.R_y(a).dot(v_a)
        # Rotation matrix around y-axis - observing the right hand rule
        v_b_expected = numpy.array([0, 0, 1])
        for v_b_i, v_b_expected_i in zip(v_b, v_b_expected):
            self.assertAlmostEqual(v_b_i, v_b_expected_i)

    def test_R_z(self):
        # v =  [x, y, z]
        v_a = numpy.array([1, 0, 0])
        a = numpy.pi/2.
        v_b = rotation.R_z(a).dot(v_a)
        # Rotation matrix around z-axis - observing the right hand rule
        v_b_expected = numpy.array([0, 1, 0])
        for v_b_i, v_b_expected_i in zip(v_b, v_b_expected):
            self.assertAlmostEqual(v_b_i, v_b_expected_i)

    def test_quat(self):
        angle = numpy.pi/3.
        u = numpy.array([1., 2., 3.])
        u /= numpy.sqrt((u**2).sum())
        q = rotation.quat(angle, u[0], u[1], u[2])
        q_expected = numpy.array([numpy.cos(angle/2.),
                                  numpy.sin(angle/2.)*u[0],
                                  numpy.sin(angle/2.)*u[1],
                                  numpy.sin(angle/2.)*u[2]])
        for q_i, q_expected_i in zip(q, q_expected):
            self.assertAlmostEqual(q_i, q_expected_i)

    def test_rotate_quat(self):
        angle = numpy.pi/4.
        for i_rot in range(3):
            v0 = numpy.array([0.,0.,0.])
            v0[(i_rot+1)%3] = 1.
            rotation_axis = numpy.array([0.,0.,0.])
            rotation_axis[i_rot] = 1.
            # q = [w, x, y, z] = [cos(a/2), ux sin(a/2), uy sin(a/2), uz sin(a/2)]
            q = numpy.array([numpy.cos(angle/2.),
                             numpy.sin(angle/2.)*rotation_axis[0],
                             numpy.sin(angle/2.)*rotation_axis[1],
                             numpy.sin(angle/2.)*rotation_axis[2]])
            v1 = rotation.rotate_quat(v0, q)
            v1_expected = numpy.array([0.,0.,0.])
            v1_expected[(i_rot+1)%3] = numpy.sqrt(2.)/2.
            v1_expected[(i_rot+2)%3] = numpy.sqrt(2.)/2.
            for v1_i, v1_expected_i in zip(v1, v1_expected):
                self.assertAlmostEqual(v1_i, v1_expected_i)

    def test_quat_from_rotmx(self):
        a = 1.2345
        R = rotation.R_x(a)
        q = rotation.quat_from_rotmx(R)
        # q = [w, x, y, z] = [cos(a/2), ux sin(a/2), uy sin(a/2), uz sin(a/2)]
        q_expected = numpy.array([numpy.cos(a/2.), numpy.sin(a/2.), 0., 0.])
        for q_i, q_expected_i in zip(q, q_expected):
            self.assertAlmostEqual(q_i, q_expected_i)

    def test_unique_representation_quat(self):
        a = 1.2345
        # These quaternions represent equivalent roatations
        l = 1/numpy.sqrt(3)
        q_same = [rotation.unique_representation_quat(rotation.quat(a, l, l, l)),
                  rotation.unique_representation_quat(rotation.quat(-a, -l, -l, -l)),
                  rotation.unique_representation_quat(rotation.quat(a + 2*numpy.pi, l, l, l)),
                  rotation.unique_representation_quat(rotation.quat(a - 2*numpy.pi, l, l, l))]
        q_expected = q_same[0]
        for q in q_same[1:]:
            for q_i, q_expected_i in zip(q, q_expected):
                self.assertAlmostEqual(q_i, q_expected_i)

    def test_rotmx_from_quat(self, N=1000):
        err = numpy.ones(N)
        for i in range(N):
            theta = numpy.random.rand()*2*numpy.pi
            for R,quat in [(rotation.R_x, rotation.quat_x), (rotation.R_y, rotation.quat_y), (rotation.R_z, rotation.quat_z)]:
                R_x0 = R(theta)
                R_x1 = rotation.rotmx_from_quat(quat(theta))
                for i in range(3):
                    for j in range(3):
                        self.assertAlmostEqual(R_x0[i][j], R_x1[i][j])

    def test_rotation_conversions_betw_formalisms(self, N=1000):
        """
        Create random euler angles with different axes formalisms and test conversion in a circular fashion.
        Euler angles => rotation matrix => quaternion => unique representation of quaternion (q_a) => Euler angles => rotation materix => quaterinion => unique representation of quaternion (q_b).
        Finally compare the two quaternions q_a and q_b.
        Repeat N times.
        """
        rotation_matrices = {
            'xyz': (rotation.R_x, rotation.R_y, rotation.R_z),
            'xzy': (rotation.R_x, rotation.R_z, rotation.R_y),
            'yxz': (rotation.R_y, rotation.R_x, rotation.R_z),
            'yzx': (rotation.R_y, rotation.R_z, rotation.R_x),
            'zxy': (rotation.R_z, rotation.R_x, rotation.R_y),
            'zyx': (rotation.R_z, rotation.R_y, rotation.R_x),
            'yxy': (rotation.R_y, rotation.R_x, rotation.R_y),
            'zxz': (rotation.R_z, rotation.R_x, rotation.R_z),
            'xyx': (rotation.R_x, rotation.R_y, rotation.R_x),
            'zyz': (rotation.R_z, rotation.R_y, rotation.R_z),
            'xzx': (rotation.R_x, rotation.R_z, rotation.R_x),
            'yzy': (rotation.R_y, rotation.R_z, rotation.R_y),
        }

        succ = 0
        NN = len(rotation_matrices)*N
        err = numpy.ones(NN)
        k = 0
        for ax,(R1,R2,R3) in rotation_matrices.items():
            for i in range(N):
                euler_a = numpy.random.random(3) * numpy.pi * 2
                euler_a[1] /= 2.
                # Euler angles => rotaion matrix
                R_a = R1(euler_a[0]).dot(R2(euler_a[1]).dot(R3(euler_a[2])))
                # Rotation matrix => unique quaternion
                q_a = rotation.quat_from_rotmx(R_a)
                q_a = rotation.unique_representation_quat(q_a)
                # Unique quaternion => Euler angles
                euler_b = rotation.euler_from_quat(q_a, ax)
                # Euler angles => quaternion
                R_b = R1(euler_b[0]).dot(R2(euler_b[1]).dot(R3(euler_b[2])))
                # Rotation matrix => unique quaternion
                q_b = rotation.quat_from_rotmx(R_b)
                q_b = rotation.unique_representation_quat(q_b)
                # Calculate discrepancy
                for q_a_i, q_b_i in zip(q_a, q_b):
                    self.assertAlmostEqual(q_a_i, q_b_i)

    def test_Rotation_compare_rotation_formalisms(self):
        # Test vector (before and after rotation)
        v0          = numpy.array([0., 1., 0.])
        v1_expected = numpy.array([0., 0., 1.])
        # Positive 90 degree rotation around x-axis
        a = numpy.pi/2.

        kwargs_list = [
            {"formalism" : "quaternion",       "values" : rotation.quat(a, 1., 0., 0.)},
            {"formalism" : "rotation_matrix",  "values" : rotation.R_x(a)},
            {"formalism" : "euler_angles_xyz", "values" : numpy.array([a, 0., 0.])},
            {"formalism" : "euler_angles_zxz", "values" : numpy.array([0., a, 0.])},
            {"formalism" : "euler_angles_zyx", "values" : numpy.array([0., 0., a])},
        ]
        for kwargs in kwargs_list:
            R = rotation.Rotation(**kwargs)
            v1 = R.rotate_vector(v0)
            for v1_i, v1_expected_i in zip(v1, v1_expected):
                self.assertAlmostEqual(v1_i, v1_expected_i)

    def test_Rotation_euler_angles_axes(self):
        angle = 0.25*numpy.pi
        for i_rot,rotation_formalism in zip(range(3),["euler_angles_xzx","euler_angles_yxy","euler_angles_zxz"]):
            rotation_values = numpy.array([angle,0.,0.])
            rotation_mode = "extrinsic"
            R = rotation.Rotation(values=rotation_values, formalism=rotation_formalism)
            v0 = numpy.array([0.,0.,0.])
            v0[(i_rot+1)%3] = 1.
            v1 = R.rotate_vector(v0)
            v1_expected = numpy.array([0.,0.,0.])
            v1_expected[(i_rot+1)%3] = numpy.sqrt(2.)/2.
            v1_expected[(i_rot+2)%3] = numpy.sqrt(2.)/2.
            for v1_i, v1_expected_i in zip(v1, v1_expected):
                self.assertAlmostEqual(v1_i, v1_expected_i)


import numpy as np

#Questions on NumPy Linear Algebra
#Find a matrix or vector norm using NumPy
#norm Return Matrix or vector norm.
np.linalg.norm(np.arange(9) - 4)
np.linalg.norm((np.arange(9) - 4).reshape(3,3))

#Calculate the QR decomposition of a given matrix using NumPy
#qr Compute the qr factorization of a matrix.
np.linalg.qr(np.arange(10).reshape(2, 5))

#Compute the condition number of a given matrix using NumPy
#cond Compute the condition number of a matrix.
np.linalg.cond(np.arange(1,10).reshape(3, 3))

#Compute the eigenvalues and right eigenvectors of a given square array using NumPy?
#eig Compute the eigenvalues and right eigenvectors of a square array.
np.linalg.eig(np.arange(1,10).reshape(3, 3))

#Calculate the Euclidean distance using NumPy
np.linalg.norm(np.array([1, 2, 3]) - np.array([3, 2, 1]))

NumpyTutorial/Questions on NumPy Linear Algebra.py


import numpy as np
import gd

X = np.array([[2, 0], [0, 1], [0, 0]])
y = np.array([[3], [2], [2]])

def fp(w):
    return 2 * X.T @ (X @ w - y)

stepsize = 0.1
maxiter = 1000000
w0 = np.array([[0.0], [0.0]])
w_traces = gd.gd_const_ss(fp, w0, stepsize=stepsize, maxiter=maxiter)

print(
    f'stepsize={stepsize}, number of iterations={len(w_traces)-1}')
print(f'optimal solution:\n{w_traces[-1]}')

sol = np.linalg.solve(X.T @ X, X.T @ y)
print(f'solution from np.linalg.solve:\n{sol}')