# -*- coding: utf-8 -*-
"""Computing Running Statistics from STDIN.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/18XZ5vEJ_la98Wz5pkQWXrlLUScJ0RGFu

Hello,

Thank you for completing the assessments.

The next step is to complete the coding challenge for Robert, our lead Instructor, to review.

Coding Challenge

Write a program that reads a list of numbers and for each number outputs an estimate of the running mean, running standard deviation, and running median.  The input should be read from standard in, with one number per line.  For each line of input, the program should print to standard out the estimated running mean, running standard deviation, and running median.  That is, given the input

1

2

3

137.036

the program should output values close to

1,0,1

1.5,0.5,1.5

2,0.816,2

35.759,58.477,2.5

Some notes:

Using either the biased or unbiased estimate of the standard deviation is fine, but you should report a number even for the first step.
There are several ways to define the median for an even number of samples; you may use any of these definitions.
Output numbers at a reasonable precision.

You will probably have to strike a balance between the accuracy of the results and  resources your program requires.  Choose a sensible tradeoff.  Better yet, allow this to be configurable.

Ideally, this program should be able to handle arbitrarily long lists of inputs.  It would be nice for it to output results as soon as possible, instead of needing to wait for all of standard in to be read.  You do not have to worry about malicious input (there won’t be 10 GB on a single line), but gracefully handling malformed input lines is a plus.

Upload your solution to the public DVCS host of your choice, and send us a link to the repository.  You may use any programming language or packages you like, but if you use anything outside of Python 3, Pandas, or Numpy, please include instructions to help us run your code.

Thank you,
"""

from collections import deque
import math
import numpy as np

class RunningStatistics:

    def __init__(self, max_med_samples=10000, sd_type='unbiased'):
        self.n = 0
        self.old_mean = 0
        self.new_mean = 0
        self.old_std = 0
        self.new_std = 0
        self.med_q = deque(maxlen=max_med_samples)
        self.sd_type = sd_type

    def clear(self):
        self.n = 0
        self.med_q.clear()

    def push(self, x):
        self.n += 1

        # For mean and standard deviation
        if self.n == 1:
            self.old_mean = self.new_mean = x
            self.old_std = 0
        else:
            self.new_mean = self.old_mean + (x - self.old_mean) / self.n
            self.new_std = self.old_std + (x - self.old_mean) * (x - self.new_mean)

            self.old_mean = self.new_mean
            self.old_std = self.new_std

        # For median
        self.med_q.append(x)

    def mean(self):
        return self.new_mean if self.n else 0.0

    def standard_deviation(self):
        if self.sd_type=='unbiased':
          return math.sqrt(self.new_std / (self.n - 1)) if self.n > 1 else 0.0
        else:
          return math.sqrt(self.new_std / (self.n)) if self.n > 1 else 0.0

    def median(self):
        return np.median(self.med_q)

rs = RunningStatistics(1000,'biased')
display_dec_places = 4

while True:

  # Prompt and get user input (one value at a time)
  input_val = input("Enter a numeric value (or type q to exit): ")

  # Break on specified input value
  if input_val=='q':
    break

  # Check input value for proper type, ignore if not
  try:
    input_num = float(input_val)
  except:
    continue

  # Check input value for NaN or +/-Inf, ignore if so
  if math.isnan(input_num) or math.isinf(input_num):
    continue

  # Do the computations
  rs.push(input_num)

  # Return the result
  print(np.round(rs.mean(), display_dec_places),
         np.round(rs.standard_deviation(), display_dec_places),
         np.round(rs.median(), display_dec_places))


"""
==========================
Discrete Fourier Transform
==========================

This example shows how to approximate a time series using only
some of its Fourier coefficients using
:class:`pyts.approximation.DiscreteFourierTransform`.
"""

import numpy as np
import matplotlib.pyplot as plt
from pyts.approximation import DiscreteFourierTransform

# Parameters
n_samples, n_timestamps = 100, 48

# Toy dataset
rng = np.random.RandomState(41)
X = rng.randn(n_samples, n_timestamps)

# DFT transformation
n_coefs = 30
dft = DiscreteFourierTransform(n_coefs=n_coefs, norm_mean=False,
                               norm_std=False)
X_dft = dft.fit_transform(X)

# Compute the inverse transformation
if n_coefs % 2 == 0:
    real_idx = np.arange(1, n_coefs, 2)
    imag_idx = np.arange(2, n_coefs, 2)
    X_dft_new = np.c_[
        X_dft[:, :1],
        X_dft[:, real_idx] + 1j * np.c_[X_dft[:, imag_idx],
                                        np.zeros((n_samples, ))]
    ]
else:
    real_idx = np.arange(1, n_coefs, 2)
    imag_idx = np.arange(2, n_coefs + 1, 2)
    X_dft_new = np.c_[
        X_dft[:, :1],
        X_dft[:, real_idx] + 1j * X_dft[:, imag_idx]
    ]
X_irfft = np.fft.irfft(X_dft_new, n_timestamps)

# Show the results for the first time series
plt.figure(figsize=(12, 8))
plt.plot(X[0], 'o--', label='Original')
plt.plot(X_irfft[0], 'o--', label='DFT - {0} coefs'.format(n_coefs))
plt.legend(loc='best', fontsize=14)
plt.show()


# This little code fragment implements a fourth order compact finite difference scheme with edge formulae
# RMC May 2019 U. Alcala, in Alcala de Henares

from numpy import linalg as LA
import numpy as np # Already done earlier in the notebook
from scipy import sparse
import scipy as sp # Already done earlier in the notebook

n = 673
if n<= 3:
    raise Exception('Sorry, friend, but this code needs 4 or more pieces of data')

x = np.array([np.cos(np.pi*(n-1-i)/(n-1)) for i in range(n)])
h = np.diff(x)

y = np.zeros(x.shape)

# Build tridiagonal matrix (floating point type, by contagion)
# subs == vector of subdiagonal entries
# diags == vector on diagonal entries
# supers == vector of superdiagonal entries
# Matrix as in the paper Compact Finite Differences and Cubic Splines
diags = np.array([4.0 for i in range(n)])
diags[0] = h[1]*(h[1]+h[2])/(h[0]+h[1])/(h[0]+h[1]+h[2])
diags[-1] = h[-2]*(h[-3]+h[-2])/(h[-2]+h[-1])/(h[-3]+h[-2]+h[-1])

subs = np.array([4.0*h[i+1]**2/(h[i]+h[i+1])**2 for i in range(n-2)]+[1])

supers = np.array([1]+[4.0*h[i]**2/(h[i]+h[i+1])**2 for i in range(n-2)])

# This factors the tridiagonal matrix.  We need only adjust the
# lower vector and the diagonal vector (the matrix is positive definite)
ell = np.zeros(subs.shape)
de  = np.zeros(diags.shape)
de[0] = diags[0]
for i in range(1,n):
    ell[i-1] = subs[i-1]/de[i-1]
    de[i]    = diags[i] - ell[i-1]*supers[i-1]

# Some made-up function to differentiate (to test)
fndata = np.zeros(diags.shape)
def fn(x):
    return 1.0/(1+x*x) # np.cos(x)
def dfn(x):
    return -2*x/(1+x*x)**2 #-1*np.sin(x)

for i in range(n):
    fndata[i] = fn(x[i])

# We will carry out the B.b operation manually; this will save storing the vectors
b = np.zeros(fndata.shape)

# Hand-translated from the Matlab (quite surprising that it worked, really)
a0 = (4*h[0]**2+6*h[0]*h[1]+3*h[0]*h[2]+2*h[1]**2+2*h[1]*h[2])*h[1]*(h[2]+h[1])/(h[0]+h[1])**2/(h[0]+h[1]+h[2])**2/h[0]
a1 = 1/h[0]*((-2*h[1]+h[0])*h[2]+2*h[1]*(-h[1]+h[0]))/h[1]/(h[1]+h[2])
a2 = -h[0]**2*(h[2]+h[1])/(h[1]+h[0])**2/(h[1])/h[2]
a3 = h[0]**2*h[1]/(h[2]+h[1]+h[0])**2/(h[2]+h[1])/h[2]
b[0] = -(a0*fndata[0] + a1*fndata[1] + a2*fndata[2] + a3*fndata[3])
for i in range(1,n-1):
    a0 = 4*h[i]**2*2*(2*h[i-1]+h[i])/h[i-1]/(h[i-1]+h[i])**3
    a1 = -8*(-h[i-1]+h[i])/h[i-1]/h[i]
    a2 = -8*h[i-1]**2*(h[i-1]+2*h[i])/(h[i-1]+h[i])**3/h[i]
    b[i] = -(a0*fndata[i-1] + a1*fndata[i] + a2*fndata[i+1])

a0 = -h[-2]*h[-1]**2/(h[-3]+h[-2]+h[-1])**2/(h[-3]+h[-2])/h[-3]
a1 = h[-1]**2/(h[-2]+h[-1])**2/h[-2]/h[-3]*(h[-2]+h[-3])
a2 = 1/h[-1]*((2*h[-2]-h[-1])*h[-3]+2*h[-2]*(h[-2]-h[-1]))/h[-2]/(h[-3]+h[-2])
a3 = -(4*h[-1]**2+(6*h[-2]+3*h[-3])*h[-1]+2*h[-2]*(h[-3]+h[-2]))/h[-1]/(h[-2]+h[-1])**2/(h[-3]+h[-2]+h[-1])**2*(h[-2])*(h[-2]+h[-3])

b[-1] = -(a0*fndata[-4] + a1*fndata[-3] + a2*fndata[-2] + a3*fndata[-1])

# This solves the tridiagonal system, given the factoring above.
# I should really peel those out into TRIDECOMP and TRISOLVE,
# implementing the so-called Thomas algorithm in a reasonably
# maintainable and re-usable way. Well, this is at this point
# just an exercise, and who will ever see this code except me?
#
# Oh, wait.  That never happens.  This code is going to escape into the wild,
# isn't it.
#
# Oops. nvm
#
y = np.zeros(b.shape)
y[0] = b[0]
for i in range(1,n):
    y[i] = b[i] - ell[i-1]*y[i-1]

sol = np.zeros(x.shape)
sol[-1] = y[-1]/de[-1]

for i in range(n-2,-1,-1): # start, stop, step (goofy language)
    sol[i] = (y[i]-supers[i]*sol[i+1])/de[i]

dy = np.zeros(x.shape)
for i in range(n):
    dy[i] = dfn(x[i])

print(LA.norm(sol-dy,2)*(n/2)**4) # This is the scaled error of derivatives at all the nodes

import numpy,from numpy,import scipy,from scipy


import sympy as sp
from final_exam.numerical_diff import *

x = sp.Symbol('x')
f = sp.E**(-x)
x0 = 1

print(cd(f, x, x0))
print(bd(f, x, x0))
print(fd(f, x, x0))
print()
print(cd2(f, x, x0))
print(bd2(f, x, x0))
print(fd2(f, x, x0))


import numpy as np

def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    # TODO: Compute and return softmax(x)
    result = np.exp(x)/np.sum(np.exp(x), axis=0)
    return result

logits = [3.0, 1.0, 0.2]
print(softmax(logits))


#!/usr/bin/env python -W ignore
import numpy as np

# матриця Леслі
L = np.array([
	[  0,   6, 15],
	[0.5,   0,  0],
	[  0, 0.5,  0]
])

# початковий розподіл популяції
x0 = np.array([1, 1, 1])

# розподіл через t = 5 кроків обчислюємо як L^t * x0
t = 5
xt = np.dot(np.linalg.matrix_power(L, t), x0)
print(f'x({t}) = {xt}')  # x(5) = [239.625   55.125   16.6875]

# знаходимо власні значення і власні вектори
_lambda, x = np.linalg.eig(L)
# знаходимо найбільше додатне власне значення
lambda_L = np.max(_lambda)

print(f'lambda_L = {np.float_(lambda_L)}')  # lambda_L = 2.173738392044369

# знаходимо власний вектор що йому відповідає
x_L = x[:, _lambda == lambda_L].T[0]
# нормуємо цей вектор в || ||_1 нормі
x_L /= np.linalg.norm(x_L, 1)

print(f'x_L = {np.abs(np.float_(x_L))}')  # x_L = [0.77946759 0.17929195 0.04124046]

# знаходимо min t: ||x(t)||_1 > 75
t = 0
while np.sum(x0) <= 75:
	x0 = np.dot(L, x0)
	t += 1

print(f't = {t}, X({t}) = {np.sum(x0)}')  # t = 3, X(3) = 77.25


from __future__ import division             # forces floating point division
import numpy as np                          # Numerical Python
import matplotlib.pyplot as plt             # Python plotting
from PIL import Image                       # Python Imaging Library
from numpy.fft import fft2, fftshift, ifft2 # Python DFT
import math

img = Image.open("Waldo_temp.jpg")
data = img.getdata()

r,g,b = img.split()
print(r, g, b)

r_np = np.array(r)
g_np = np.array(g)
b_np = np.array(b)

g_b_surpessed = r_np-(0.5*g_np+b_np)

img_one = Image.fromarray(g_b_surpessed )
plt.imshow(img_one)
plt.show()

I = img_one.convert('L')                     # 'L' for gray scale mode
A3 = np.asarray(I, dtype = np.float64)  # Image class instance, I1, to float32 Numpy array, a

H,W = np.shape(A3)
hW = int(np.fix(0.5*W))
hH = int(np.fix(0.5*H))

F3 = fft2(A3)/(W*H)
F3 = fftshift(F3)

xx,yy = np.meshgrid(np.linspace(-W/2,W/2,W), np.linspace(-H/2,H/2,H))
sf=0.2031
sigma = 1/sf;  #width of Gaussian (1/e half-width)
Gaussian = np.exp(-(xx**2+yy**2)/sigma**2);

gratingSin = np.sin(2*np.pi*sf*yy)
GaborSin = gratingSin*Gaussian;
F3_sin = fft2(GaborSin)/(W*H)
F3_sin = fftshift(F3_sin)
Filter_sin = F3*F3_sin
filtImgSin = ifft2(Filter_sin)*W*H
filtImgSin = fftshift(filtImgSin)

gratingCos = np.cos(2*np.pi*sf*yy)
GaborCos = gratingCos*Gaussian;
F3_cos = fft2(GaborCos)/(W*H)
F3_cos = fftshift(F3_cos)
Filter_cos = F3*F3_cos
filtImgCos = ifft2(Filter_cos)*W*H
filtImgCos = fftshift(filtImgCos)

filtImg = np.sqrt(filtImgSin**2+filtImgCos**2)
# filtImg = filtImgSin

attenuateImg = filtImg/np.max(filtImg)
attenuateImg = np.abs((attenuateImg+.25)/1.2)

center_coord = np.where(attenuateImg == np.amax(attenuateImg))
box_radius = 25
for x_coord in range(center_coord[0][0]-box_radius, center_coord[0][0]+box_radius):
	for y_coord in range(center_coord[0][0]-box_radius, center_coord[1][0]+box_radius):
		attenuateImg[x_coord, y_coord] = 0.9

attenuateImg_transposed = np.asarray([attenuateImg]*3).transpose(1, 2, 0)
# attenuateImg = np.abs(filtImg)
new_img = np.asarray(np.asarray(img, dtype = np.float64)*attenuateImg_transposed, dtype="uint8");

plt.imshow(np.abs(new_img))
plt.show()


import numpy as np
from math import log
#calculate the shanno entropy
def calc_shanno_ent(data_set):
    num_entries = len(data_set)
    label_count ={}
    for feature_vec in data_set:
        current_label = data_set[-1]
        if current_label not in label_count.keys():
            label_count[current_label] =0
        label_count[current_label] = label_count[current_label]+1
    shanno_ent = 0
    for key in label_count.keys():
        prob = float(label_count[key] / num_entries)
        shanno_ent -=prob* log(prob,2)
    return shanno_ent
# split the dataset
def split_dataset(dataset, axis, value):
    ret_dataset = []
    for feature in dataset:
        if feature[axis] == value:
            reduced_dataset = feature[:axis]
            reduced_dataset.extend(feature[axis+1:])
            ret_dataset.append(reduced_dataset)
    return ret_dataset
def choose_feature_to_split(dataset):
    num_features = len(dataset)-1
    base_ent = calc_shanno_ent(dataset)
    info_gain = 0
    best_info_gain = 0.0
    best_feature = -1
    for i in range(num_features):
        feature_list = [example[i] for example in dataset]
        #very good job
        unique_value = set(feature_list)

        new_ent = 0.0
        for value in unique_value:
            split_data = split_dataset(dataset,i,value)
            prob = len(split_data)/len(dataset)
            new_ent += prob*calc_shanno_ent(split_data)
        info_gain = base_ent - new_ent
        if info_gain > best_info_gain:
            best_info_gain = info_gain
            best_feature = i
    return best_feature
def majorityCnt(class_list):
    class_count = {}
    for vote in class_list:
        if vote not in class_count.keys():
            class_count[vote] = 0
        class_count[vote] +=1
    sorted_class_count = sorted(class_count.items(), key= lambda x: x[1], reverse=True)
    # Debug and see why the index is [0][0]
    return sorted_class_count[0][0]

def createTree(dataset, labels):
    class_list = [example[-1] for example in dataset]
    if class_list.count(class_list[0]) == len(class_list):
        return class_list[0]
    # debug
    if len(dataset[0] == 1):
        majorityCnt(class_list)
    best_feature = choose_feature_to_split(dataset)
    best_feature_label = labels[best_feature]
    tree = {best_feature_label:{}}
    del(labels[best_feature])
    feature_values = [example[best_feature] for example in dataset]
    unique_feature = set(feature_values)
    for value in unique_feature:
        # prevent changing the original list by reference
        sub_labels = labels[:]
        tree[best_feature_label][value] = createTree(split_dataset(dataset, best_feature,value), sub_labels)

    return tree

def classify(input_tree, feature_labels, test_vec):
    first_str = input_tree.keys()[0]
    second_dict = input_tree[first_str]
    feature_index = feature_labels.index(first_str)
    for key in second_dict.keys():
        if test_vec[feature_index] == key:
            if type(second_dict[key]) == 'dict':
                class_label = classify(second_dict[key], feature_labels, test_vec)
            else:
                class_label =  second_dict[key]
    return class_label


import numpy as np
from collections import defaultdict
import space
import scipy.linalg
import scipy.sparse
import scipy.sparse.linalg
from bc import Boundary

def ddx(f, dx):
    return (f[1:-1,2:] - f[1:-1,:-2])/2.0/dx

def ddy(f, dy):
    return (f[2:,1:-1] - f[:-2,1:-1])/2.0/dy

def laplacian(f, dx, dy):
    return (f[1:-1,2:] - 2.0*f[1:-1,1:-1] + f[1:-1,:-2])/dx/dx + \
           (f[2:,1:-1] -2.0*f[1:-1,1:-1] + f[:-2,1:-1])/dy/dy

def div(u,v,dx,dy):
    return ddx(u,dx) + ddy(v,dy)

def momentum(u, v, dx, dy, nu):
    # u rhs: - d(uu)/dx - d(vu)/dy + ν d2(u)
    # v rhs: - d(uv)/dx - d(vv)/dy + ν d2(v)

    return (
        - ddx(u*u,dx) - ddy(v*u,dy) + nu*laplacian(u,dx,dy),
        - ddx(u*v,dx) - ddy(v*v,dy) + nu*laplacian(v,dx,dy)
    )

def momentum_staggered(u, v, dx, dy, nu):
    # do x-momentum - u is of size (nx + 2) x (ny + 2) - only need to do the interior points
    # u is horizontonal component of velocity, dimension 1
    # LL = u[1,2] , UR = u[n,n]

    ue = 0.5*(u[1:-1, 2:-1] + u[1:-1, 3:  ])
    uw = 0.5*(u[1:-1, 1:-2] + u[1:-1, 2:-1])
    un = 0.5*(u[1:-1, 2:-1] + u[2:,   2:-1])
    us = 0.5*(u[:-2,  2:-1] + u[1:-1, 2:-1])
    vn = 0.5*(v[2:,   1:-2] + v[2:,   2:-1])
    vs = 0.5*(v[1:-1, 1:-2] + v[1:-1, 2:-1])

    convection = - (ue**2 - uw**2)/dx - (un*vn - us*vs)/dy
    diffusion = nu * laplacian(u,dx,dy)[:,1:]#[1:-1,2:-1]

    mx = convection + diffusion

    # do y-momentum - only need to do interior points
    # v is vertical component of velocity, staggered negative on dimension 0
    # v LL = v[2,1], UR = v[n,n]

    ve = 0.5*(v[2:-1, 1:-1] + v[2:-1, 2:  ])
    vw = 0.5*(v[2:-1, :-2 ] + v[2:-1, 1:-1])
    vn = 0.5*(v[2:-1, 1:-1] + v[3:,   1:-1])
    vs = 0.5*(v[1:-2, 1:-1] + v[2:-1, 1:-1])
    ue = 0.5*(u[1:-2, 2:  ] + u[2:-1, 2:  ])
    uw = 0.5*(u[1:-2, 1:-1] + u[2:-1, 1:-1])

    convection = - (ue*ve - uw*vw)/dx - (vn**2 - vs**2)/dy
    diffusion = nu * laplacian(v,dx,dy)[1:,:]#[2:-1,1:-1]

    my = convection + diffusion

    return mx, my

def pressure_poisson(u, v, dx, dy, dt, tol, max_its, b=None, p=None, bcs=None):
    bcs = bcs if bcs else []

    if b is None:
        b = np.zeros_like(u)

    if p is None:
        p = np.zeros_like(u)

    b[1:-1,1:-1] = div(u,v,dx,dy)/dt

    pn = p.copy()
    it = 0
    err = float("inf")

    while it < max_its and err > tol:
        for bc in bcs:
            bc.apply(p)

        np.copyto(pn, p)

        p[1:-1, 1:-1] = (((pn[1:-1, 2:] + pn[1:-1, 0:-2]) * dy**2 +
                          (pn[2:, 1:-1] + pn[0:-2, 1:-1]) * dx**2) /
                          (2 * (dx**2 + dy**2)) -
                          dx**2 * dy**2 / (2 * (dx**2 + dy**2)) *
                          b[1:-1,1:-1])

        err = np.linalg.norm(p - pn, 2)
        it += 1

    return p, err

def sparse_pressure_matrix(nx, ny, dx, dy, bc_left, bc_right, bc_bottom, bc_top):

    Ap = np.zeros([ny,nx])
    Ae = 1.0/dx/dx*np.ones([ny,nx])
    As = 1.0/dy/dy*np.ones([ny,nx])
    An = 1.0/dy/dy*np.ones([ny,nx])
    Aw = 1.0/dx/dx*np.ones([ny,nx])

    # a little optimistic that this generalizes to non-dirichlet bcs
    bc_left.apply(Aw)
    bc_right.apply(Ae)
    bc_bottom.apply(As)
    bc_top.apply(An)

    Ap = -(Aw + Ae + An + As)

    n = nx*ny
    d0 = Ap.reshape(n)
    de = Ae.reshape(n)[:-1]
    dw = Aw.reshape(n)[1:]
    ds = As.reshape(n)[nx:]
    dn = An.reshape(n)[:-nx]
    A1 = scipy.sparse.diags([d0, de, dw, dn, ds], [0, 1, -1, nx, -nx], format='csr')

    return A1

def pressure_poisson_sparse(A1, u, v, dx, dy, dt, nx, ny):
    # do pressure - prhs = 1/dt * div(uhat)
    # we will only need to fill the interior points. This size is for convenient indexing
    divut = np.zeros([ny+2,nx+2])
    #divut[1:-1,1:-1] = ddx(ut,dx) + ddy(vt,dy)#div(ut,vt,dx,dy)
    divut[1:-1,1:-1] = (u[1:-1,2:] - u[1:-1,1:-1])/dx + (v[2:,1:-1] - v[1:-1,1:-1])/dy

    prhs = 1.0/dt * divut

    ###### Use the sparse linear solver
    #     pt = scipy.sparse.linalg.spsolve(A1,prhs[1:-1,1:-1].ravel()) #theta=sc.linalg.solve_triangular(A,d)
    pt,info = scipy.sparse.linalg.bicg(A1,prhs[1:-1,1:-1].ravel(),tol=1e-10) #theta=sc.linalg.solve_triangular(A,d)
    return pt.reshape([ny,nx])

class Fluid:
    def __init__(self, space):
        self.space = space
        self.bcs = defaultdict(list)

    def add_boundary_condition(self, name, bc, **kwargs):
        bctype = bc.pop('type')
        self.bcs[name].append(bctype(space=self.space,**bc,**kwargs))

    def add_boundary_conditions(self, name, bcs, **kwargs):
        for bc in bcs:
            self.add_boundary_condition(name,bc,**kwargs)

    def get_boundary_conditions(self, name):
        return self.bcs[name]

    def get_boundary_condition(self, name, dim, b):
        for bc in self.bcs[name]:
            if bc.dim == dim and bc.b == b:
                return bc
        return None

    def solve(self, dt, cb=None, **kwargs):
        raise NotImplementedError

class NavierStokesProjectionMethod(Fluid):
    def __init__(self, N, extent, rho, nu, f=None):
        super().__init__(space=space.RegularGrid(N, extent))

        self.rho = rho # density
        self.nu = nu # viscosity
        self.f = np.zeros(2) if f is None else f

        self.u = np.zeros(self.space.N)
        self.v = np.zeros(self.space.N)
        self.p = np.zeros(self.space.N)

        self._x = [ np.zeros_like(self.p) for _ in range(3) ]

    def solve(self, dt, cb=None, its=100, p_tol=1e-3, p_max_its=50):
        cb = cb if cb is not None else lambda a,b,c,d: None

        u,v,p,uh,vh,b = self.u, self.v, self.p, self._x[0], self._x[1], self._x[2]

        dx,dy = self.space.delta

        p_bcs = self.get_boundary_conditions('p')
        u_bcs = self.get_boundary_conditions('u')
        v_bcs = self.get_boundary_conditions('v')

        for i in range(its):

            for bc in u_bcs:
                bc.apply(u)

            for bc in v_bcs:
                bc.apply(v)

            # do the x-momentum RHS
            uRHS, vRHS = momentum(u,v,dx,dy,self.nu)

            uh[1:-1,1:-1] = u[1:-1,1:-1] + dt*uRHS
            vh[1:-1,1:-1] = v[1:-1,1:-1] + dt*vRHS

            p,err = pressure_poisson(uh, vh, dx, dy, dt, b=b, p=p,
                                     tol=p_tol, max_its=p_max_its,
                                     bcs=p_bcs)

            # finally compute the true velocities
            # u_{n+1} = uh - dt*dpdx
            u[1:-1,1:-1] = uh[1:-1,1:-1] - dt*ddx(p,dx)
            v[1:-1,1:-1] = vh[1:-1,1:-1] - dt*ddy(p,dy)

            cb(i, u, v, p)

        np.copyto(self.u, u)
        np.copyto(self.v, v)

class NavierStokesFVM(Fluid):
    def __init__(self, N, extent, nu, beta):
        super().__init__(space=space.StaggeredGrid(N, extent))

        self.nu = nu
        self.beta = beta

        # initialize velocities - we stagger everything in the negative direction. A scalar cell owns its minus face, only.
        # Then, for example, the u velocity field has a ghost cell at x0 - dx and the plus ghost cell at lx
        self.u = np.zeros(self.space.N+2) # include ghost cells

        # # same thing for the y-velocity component
        self.v = np.zeros(self.space.N+2) # include ghost cells

        self.ut = np.zeros_like(self.u)
        self.vt = np.zeros_like(self.v)

        # initialize the pressure
        self.p = np.zeros(self.space.N+2) # include ghost cells

    def solve(self, dt, cb=None, its=100):
        ny,nx = self.space.N
        dy,dx = self.space.delta

        u,v,ut,vt,p = self.u, self.v, self.ut, self.vt, self.p

        u_bcs = self.get_boundary_conditions('u')
        v_bcs = self.get_boundary_conditions('v')
        p_bcs = [
            self.get_boundary_condition('p',dim=1,b=Boundary.MIN), # left
            self.get_boundary_condition('p',dim=1,b=Boundary.MAX), # right
            self.get_boundary_condition('p',dim=0,b=Boundary.MIN), # bottom
            self.get_boundary_condition('p',dim=0,b=Boundary.MAX), # top
        ]
        A1 = sparse_pressure_matrix(nx, ny, dx, dy, *p_bcs)

        nsteps = 1000
        for n in range(0,nsteps):
            for bc in u_bcs:
                bc.apply(u)

            for bc in v_bcs:
                bc.apply(v)

            mx, my = momentum_staggered(u, v, dx, dy, self.nu)

            ut[1:-1,2:-1] = u[1:-1,2:-1] + dt * mx
            vt[2:-1,1:-1] = v[2:-1,1:-1] + dt * my

            p[:,:] = 0
            p[1:-1,1:-1] = pressure_poisson_sparse(A1, ut, vt, dx, dy, dt, nx, ny)

            # time advance
            u[1:-1,2:-1] = ut[1:-1,2:-1] - dt * (p[1:-1,2:-1] - p[1:-1,1:-2])/dx
            v[2:-1,1:-1] = vt[2:-1,1:-1] - dt * (p[2:-1,1:-1] - p[1:-2,1:-1])/dy

class LatticeBoltzmann(Fluid):
    def __init__(self, N, extent, rho0, tau):
        super().__init__(space=space.LatticeGrid(N, extent))

        self.rho0 = 100    # average density
        self.tau = 0.6    # collision timescale

        self.F = np.zeros(np.append(self.space.N, [self.space.NL]))
        self.rho = np.zeros(self.space.N)
        self.ux = np.zeros(self.space.N)
        self.uy = np.zeros(self.space.N)

        self.object_mask = self.space.grid_coords[0] < 0

    @property
    def vorticity(self):
        v = ( (np.roll(self.ux, -1, axis=0) - np.roll(self.ux, 1, axis=0)) -
              (np.roll(self.uy, -1, axis=1) - np.roll(self.uy, 1, axis=1)) )
        v[self.object_mask] = np.nan
        return v

    def solve(self, dt, its, cb=None):
        Ny, Nx = self.space.N
        idxs, cxs, cys, weights = self.space.idxs, self.space.cxs, self.space.cys, self.space.weights

        self.rho = np.sum(self.F,2)
        for i in idxs:
            self.F[:,:,i] *= self.rho0 / self.rho

        # Simulation Main Loop
        for it in range(its):
            # Drift
            for i, cx, cy in zip(idxs, cxs, cys):
                self.F[:,:,i] = np.roll(self.F[:,:,i], cx, axis=1)
                self.F[:,:,i] = np.roll(self.F[:,:,i], cy, axis=0)

            # Set reflective boundaries
            bndryF = self.F[self.object_mask,:]
            bndryF = bndryF[:,[0,5,6,7,8,1,2,3,4]]

            # Calculate fluid variables
            self.rho = np.sum(self.F,2)
            self.ux  = np.sum(self.F*cxs,2) / self.rho
            self.uy  = np.sum(self.F*cys,2) / self.rho

            # Apply Collision
            Feq = np.zeros(self.F.shape)
            for i, cx, cy, w in zip(idxs, cxs, cys, weights):
                Feq[:,:,i] = self.rho * w * ( 1 + 3*(cx*self.ux+cy*self.uy) +
                                              9*(cx*self.ux+cy*self.uy)**2/2 -
                                              3*(self.ux**2+self.uy**2)/2 )

            self.F += -(1.0/self.tau) * (self.F - Feq)

            # Apply boundary
            self.F[self.object_mask,:] = bndryF
            self.ux[self.object_mask] = 0
            self.uy[self.object_mask] = 0

            if it % 10 == 0:
                if cb:
                    cb(it, self)


import numpy as np
import scipy.linalg

def matrix_transform():
    """
        目的是通过矩阵乘法实现变换 ------ 旋转，翻转
        **直接**进行切片操作的此处就不介绍了
    """
    # A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    A = np.arange(1, 10).reshape(3, 3)
    print('----- 原始矩阵 -----\n', A)

    B = np.eye(3)[[2, 1, 0]]
    # C = np.transpose(A)
    C = A.T

    print('----- 上下翻转 ----')
    A1 = B.dot(A)       # 左乘
    print(A1)
    print('----- 左右翻转 ----')
    A2 = A.dot(B)       # 右乘
    print(A2)

    print('----- 旋转180° ----')
    print(np.dot(A1, B))    # rotate180 = filpud + filplr
    print(np.dot(B, A2))    # rotate180 = filplr + filpud

    print('----- 旋转90°(以顺时针为正) ----')
    print(np.dot(C, B))     # rotate90 = transpose + filpud
    print('----- 旋转-90°(以顺时针为正) ----')
    print(np.dot(B, C))     # rotate-90 = transpose + filplr

def matrix_decomposition_lu():
    """
        https://baike.baidu.com/item/lu%E5%88%86%E8%A7%A3/764245?fr=aladdin
        scipy采用'列主元三角分解': A=PLU
        手动计算(高斯消元法): A=LU
    """
    A = np.matrix('2,3; 5,4')
    b = np.matrix('4,3').T

    print('----- LU分解 (返PLU参数)-----')
    p, l, u = scipy.linalg.lu(A)
    print(p)
    print(l)
    print(u)

    print('----- Ax=b (A=PLU) 求解x -----')
    y = np.linalg.solve(np.dot(p, l), b)
    x = np.linalg.solve(u, y)
    print(x)

    print('----- 直接解方程 -----')
    x = np.linalg.solve(A, b)
    print(x)

    """
    # 这个L相当于是上述的PL，形式很怪，此处不采用
    print('----- LU分解 (返LU参数)-----')
    l, u = scipy.linalg.lu(A, permute_l=True)
    print(l)
    print(u)
    """

if __name__ == "__main__":
    # matrix_transform()
    matrix_decomposition_lu()


from scipy import array, ndarray
from scipy.interpolate import CubicSpline
from sympy import sympify, SympifyError
from gaussian_quadrature import gaussian_quadrature
from utils import print_error

def get_limits():
    try:
        a, b = map(float, input('Insert the limits with the format "a, b": ').split(','))
    except ValueError:
        print_error('Error: Incorrect format, the limits should be a numbers and the separator should be a comma ",".')
    return a, b

def get_tabular_form_input():
    try:
        n = int(input('Insert the number of points of the function to integrate: '))
    except ValueError:
        print_error('Error: The number of points should be a integer.')
    print('Insert the points with the format "x, y":')
    x_points, y_points = [], []
    for _ in range(n):
        try:
            x, y = map(float, input().split(','))
        except ValueError:
            print_error('Error: Incorrect format, the points should be a numbers and the separator should be a comma ",".')
        x_points.append(x)
        y_points.append(y)
    return x_points, y_points

def get_tabular_form():
    x_points, y_points = get_tabular_form_input()
    function = CubicSpline(x_points, y_points)
    return function

def get_analytical_form():
    try:
        function = sympify(input('Insert the analytical form of the function: '))
    except SympifyError:
        print_error('Error: It is not a recognizable function')
    return lambda arg: array([function.evalf(subs={'x': i}) for i in arg]) if isinstance(arg, (tuple, list, ndarray)) else function.evalf(subs={'x': arg})

if __name__ == "__main__":
    try:
        option = int(input('Insert 1 if the function to be integrated is in the form of a table or 2 if it is in analytical form: '))
        if option != 1 and option != 2:
            print_error('Error: The number of option should be 1 or 2.')
    except ValueError:
        print_error('Error: The number of option should be a integer.')
    function = get_tabular_form() if option == 1 else get_analytical_form()
    a, b = get_limits()
    print(gaussian_quadrature(function, a, b))


import numpy as np
import math

def findZeros(values, axis=None, start=0, end=-1):
  """Find zeros in a one dimensional numpy array

  The algorithm checks for zero crossings and then performs a linear
  interpolation to determine an approximate value for the zero crossing.

  Parameters
  ----------
  values: numpy array
    The array containeng the function values
  axis: numpy array, optional
    An array containing the x-axis values. These will be used to determine
    the zero crossings. Requires axis.size = values.size.
    If not supplied, the indices of the values array will be used to calculate
    the zero crossings
  start: int, optional
    A starting index to look for zero crossings. Negative values indicate offsets
    from the end of the array. (default = 0)
  end: int, optional
    An end index to look for zero crossings. Negative values indicate offsets
    from the end of the array. (default = -1)
  """
  def getX(index):
    if (axis is None):
      return index
    lo = math.floor(index)
    alpha = index - lo
    if (alpha == 0.0):
      return axis[lo]
    else:
      return (1-alpha)*axis[lo] + alpha*axis[lo+1]

  n = values.size
  if (end<0):
    end = n + end
  if (start<0):
    start = n + start

  lastval = values[start]
  result = []

  for i in range(start+1, end+1):
    nextval = values[i]
    if (nextval==0.0):
      result.append(getX(i))
    elif (lastval*nextval < 0.0):
      result.append(getX( i - nextval/(nextval-lastval) ))
    lastval = nextval

  return np.array(result)


"""
Transformation of features in df, including,
1. log transformation for positive skewed data
1. sign-log transformation for real value skewed data

References:
1. "Practical Data Science with R" by Nina Zumel and John Mount, Chapter 3, 4
"""

import numpy as np
import pandas as pd

##################### variable transformation #######################

def log_transform(df, feat_names, copy = True, prefix = "log_"):
	"""
	Transform data using log, specially for data generated by mulplicative process.
	The data to be transformed must be POSITIVE, otherwise remove the bad data first
	or use signed_log_transform instead.
	df: DataFrame
	feat_names: names of features to be transformed.
	"""
	for f in feat_names:
		if np.any(df[f] <= 0):
			raise ValueError(f+":log_transform is for positive values, remove bad data or use signed_log_transform")
	result = df.copy() if copy else df
	#result.loc[:, feat_names] = np.log(result.loc[:, feat_names])
	for f in feat_names:
		result[prefix+f] = np.log(result[f])
	return result

def arcsinh_transform(df, feat_names, copy = True, prefix = "arcsinh_"):
	"""
	One of the universal methods other than signed logarithms to transform real values,
	- the arcsinh function (see http://mng.bz/ZWQa)
	but they also distort data near zero and make almost any data appear to be bimodal.
	"""
	result = df.copy() if copy else df
	#result.loc[:, feat_names] = np.arcsinh(result.loc[:, feat_names])
	for f in feat_names:
		result[prefix+f] = np.arcsinh(result[f])
	return result

def signed_log_transform(df, feat_names, copy = True, prefix = "slog_"):
	"""
	One of the universal methods other than signed logarithms to transform real values,
	- the signed log function.
	"""
	result = df.copy() if copy else df
	#d = result.loc[:, feat_names]
	#result.loc[:, feat_names] = np.where(np.abs(d) <= 1, 0, np.sign(d) * np.log(np.abs(d)))
	for f in feat_names:
		d = df[f]
		result[prefix+f] = np.where(np.abs(d) <= 1, 0, np.sign(d) * np.log(np.abs(d)))
	return result

#################### discretization of continouse numerical variable #####################

def discretize_numerical(df, feat_names, copy = True, max_qcut = 10, feat_bins = None, prefix = "discrete_"):
	"""
	Discretize numerical variables by cut them into bins (equal size / equal percentile).
	For a specific feature, it can be discretiezed in 3 different ways:
	(1) customized bins to cut - specify in feat_bins that {fname: bins_to_cut}
	(2) customized number of enqual bins to cut - specify in feat_bins that {fname: ncut}
	(3) quantile cut - specify max_qcut and leave in feat_bins {fname: None }
	df: DataFrame
	feat_names: names of features to discretize
	copy: whether to make a new copy or use df
	feat_bins : either None (default quantile-cut of 10 peices) or dictionary {fname: bins_to_cut}, e.g.
	{"x1": 10, "x2": [1, 2, 3], "x3": None}
	max_qcut: number of quantile cuts if feat_bins is not specified for that feature
	"""
	result = df.copy() if copy else df
	feat_bins = {} if feat_bins is None else feat_bins
	for f in feat_names:
		bins = feat_bins.get(f, None)
		if bins is None:
			## there is a bug in current numpy percentile - use pandas quantile instead
			bins = np.unique(df[f].quantile(np.linspace(0., 1., max_qcut)))
		result[prefix+f] = np.asarray(pd.cut(result[f], bins, include_lowest=True))
	return result


# -*- coding: utf-8 -*-
"""
Created on Tue Jul 27 11:36:37 2021

@author: uqcleon4
"""

import numpy as np
import matplotlib.pyplot as plt

def f1(x):
    return x*x*x

def f1Prime(x):
    return 3*x*x

# Create arrays for x and f(x)
h = 0.5
x = np.arange(-5,6,h,dtype=float)
f = f1(x)

# Calculate the forward, backward, and central difference
forwardD = np.zeros_like(f)
forwardD[:-1] = (f[1:] - f[:-1])/h

backwardD = np.zeros_like(f)
backwardD[1:] = (f[1:]-f[:-1])/h

centralD = np.zeros_like(f)
centralD[1:-1] = (f[2:]-f[:-2])/(2*h)

# Calculate the error associated with each difference approach
fd = f1Prime(x)
errors = np.zeros((3,x.size))
errors[0,:-1] = fd[:-1] - forwardD[:-1]
errors[1,1:] = fd[1:] - backwardD[1:]
errors[2,1:-1] = fd[1:-1] - centralD[1:-1]

fig,ax =plt.subplots()
ax.plot(x, f,
        x[:-1], forwardD[:-1], '-.',
        x[1:], backwardD[1:], ':',
        x[1:-1], centralD[1:-1], '--')
ax.grid()
ax.legend(["f", "forwardD", "backwardD", "centralD"])

fig2,ax2 = plt.subplots()
ax2.plot(x[:-1], errors[0,:-1], '-.',
        x[1:], errors[1,1:], ':',
        x[1:-1], errors[2,1:-1], '--')
ax2.grid()
ax2.legend(["forwardD", "backwardD", "centralD"])

plt.show()


import matplotlib.pyplot as plt
import numpy as np

def g(x):
    return x**4 - 4*(x**2) + 5

def dg(x):
    return 4*(x**3) - 8*x

def gradient_descent(derivative_func, initial_guess, taxa_de_aprendizado = 0.01, precisao = 0.000001):

    x_novo = initial_guess

    lista_x = []
    lista_inc = []

    for i in range(500):
        x_anterior = x_novo
        gradiente = derivative_func(x_anterior)
        x_novo = x_anterior - gradiente * taxa_de_aprendizado
        lista_x.append(x_novo)
        lista_inc.append(derivative_func(x_novo))

        if(abs(x_anterior - x_novo) < precisao):

            break

    return x_novo, lista_x, lista_inc

x = np.linspace(start = -2, stop = 2, num = 1000)

minimo, lista_x, lista_deriv = gradient_descent(derivative_func = dg,initial_guess=3)
print("Valor minimo local:", minimo)
print("Passos dados:", len(lista_x))

plt.subplot(1, 2, 1)
plt.title("Função de custo")
plt.xlim(-2, 2)
plt.ylim(0.5, 5.5)

plt.xlabel("x")
plt.ylabel("g(x)")

plt.plot(x, g(x), color = "blue", linewidth = 5, alpha = 0.8)
plt.scatter(lista_x, g(np.array(lista_x)), color = "red", s = 100, alpha = 0.6)

plt.subplot(1, 2, 2)
plt.title("Derivada da funçao de custo")
plt.xlim(-2, 2)
plt.ylim(-6, 8)
plt.grid("True")

plt.xlabel("x")
plt.ylabel("dg(x)")

plt.plot(x, dg(x), color = "skyblue", linewidth = 5, alpha = 0.8)
plt.scatter(lista_x, lista_deriv, color = "red", s = 100, alpha = 0.6)

plt.savefig("Aula 06.jpg")


import numpy as np

import h5py
import time
import copy
from random import randint

# Function that calculates the softmax
def softmax(input_vector):
    exp_vec = np.exp(input_vector)
    return np.divide(exp_vec, np.sum(exp_vec))

# Function that calculates loss
def loss(theta, x, y):
    loss = 0
    for i in range(len(x)):
        soft = forward(theta, x[i])[0]
        loss -= np.log(soft[y[i]])
    return loss

# Function that applies reLU to a vector
def reLU(vector):
    return vector.clip(min=0)

# Function that calculates the derivative of the reLU function
def reLU_prime(vector):
    vector[vector > 0] = 1
    return vector

# Function that calcuate the gradient for all parameters
def gradient(x, y, theta):
    elem = np.random.randint(0, len(x))
    x_cur = x[elem]
    y_cur = y[elem]

    output = forward(theta, x_cur)

    dp_dU = np.empty(10)
    for z in range(len(dp_dU)):
            softZ = output[0][z]
            if z == y_cur:
                dp_dU[z] = -1*(1 - softZ)
            else:
                dp_dU[z] = -1*(-softZ)
    dp_db2 = dp_dU
    dp_dC = np.matmul(dp_dU[:,None], output[2][:,None].T)
    sigma = np.matmul(theta[0][1].T, dp_dU)
    dp_db1 = np.multiply(sigma, reLU_prime(output[2]))
    dp_dW = np.matmul(dp_db1[:,None], x_cur[:,None].T)
    return [dp_dW, dp_dC, dp_db1, dp_db2]

# Function that calculates forward pass of neural network
def forward(weight, img):
    Z = np.add(np.matmul(weight[0][0], img), weight[1][0])
    H = reLU(Z)
    U = np.add(np.matmul(weight[0][1], H), weight[1][1])
    soft_res = softmax(U)
    return_object = [soft_res, Z, H, U]
    return return_object

# Function that calculates accuracy of prediction
def test(x_given, y_given):
    total_correct = 0
    for n in range(len(x_given)):
        y = y_given[n]
        x = x_given[n][:]
        p = np.argmax(forward(theta,x)[0])
        if p == y:
            total_correct += 1
    print(total_correct/np.float(len(x_test)))

# Main
if __name__ == "__main__":
    # Load MNIST data
    MNIST = h5py.File('MNISTdata.hdf5', 'r')
    x_train = np.float32(MNIST['x_train'][:])
    y_train = np.int32(np.array(MNIST['y_train'][:,0]))
    x_test = np.float32(MNIST['x_test'][:])
    y_test = np.int32(np.array(MNIST['y_test'][:,0]))

    MNIST.close()

    # Initialize variables
    weight = [np.random.randn(64,784)/np.sqrt(784), np.random.randn(10,64)/np.sqrt(784)]
    bias = [np.random.randn(64)/np.sqrt(784), np.random.randn(10)/np.sqrt(784)]
    theta = [weight, bias]

    alpha = 0.01
    iteration = 0
    epoch = 0

    # Training
    while True:
        # Calculate gradient for random element
        grad = gradient(x_train, y_train, theta)

        # Take step for all parameters based on alpha and gradient
        for i in range(4):
            theta[int(i/2)][i%2] = theta[int(i/2)][i%2] - alpha * grad[i]

        # Increment iteration
        iteration += 1

        # Increment epoch every 20000 iterations
        if iteration % 20000 == 0:
            epoch += 1
            print(epoch)

        # After 60000 iterations, divide alpha by 10
        if iteration == 60000:
            alpha = alpha/10

        # After 120000 iterations, end training
        if iteration == 120000:
            break

    # Testing
    test(x_test, y_test)


from numpy import array, arange, cos, pi
from typing import List

def normalise_spacing(spacing: array):
    smin = spacing.min()
    smax = spacing.max()
    return (spacing-smin)/(smax-smin)

def semi_cosine_spacing(num: int):
    th = arange(num, -1, -1, dtype=float)*pi/2/num
    spc = cos(th)
    spc[0] = 0.0
    return spc.tolist()

def full_cosine_spacing(num: int) -> list:
    th = arange(num, -1, -1, dtype=float)*pi/num
    spc = (cos(th)+1.0)/2
    return spc.tolist()

def equal_spacing(num: int) -> list:
    spc = arange(0, num+1, 1, dtype=float)/num
    return spc.tolist()

def linear_bias_left(spc: List[float], ratio: float):
    ratio = abs(ratio)
    if ratio > 1.0:
        ratio = 1.0/ratio
    m = 1.0 - ratio
    return [s*(ratio + m*s) for s in spc]

def linear_bias_right(spc: List[float], ratio: float):
    ratio = abs(ratio)
    if ratio > 1.0:
        ratio = 1.0/ratio
    m = 1.0 - ratio
    return [1.0 - (1.0 - s)*(ratio + m*(1.0 - s)) for s in spc]


import numpy as np

def qammod(b, mod):
    """
    This function takes specific set of bits and maps them into a desired QAM
    modulation.
    Real part : +-1 +-3 +-5 .......
    Imag part : +-1 +-3 +-5 .......
    """
    if b.size != mod:
        print('number of bits do not match the modulation scheme')
        return -1
    elif mod not in [2, 4, 6, 8, 10]:
        print('Currently supporting only QPSK, 16QAM, 64QAM, 256QAM, 1024QAM')
        return -1
    else:
        dims = np.power(2,mod//2) # one side of the square
        #coord = qamcoord[0:dims]
        xdim = 0
        ydim = 0
        for i in range(0, mod//2):
            xdim = xdim+b[i]*np.power(2,i)
            ydim = ydim+b[i+mod//2]*np.power(2,i)
        return np.complex(dims-(2*xdim+1), dims-(2*ydim+1))

def normFactor(mod):
    if mod not in [2, 4, 6, 8, 10]:
        print('Currently supporting only QPSK, 16QAM, 64QAM, 256QAM, 1024QAM')
        return -1
    dims = np.power(2,mod//2)
    ene_sum = 0
    for i in range(0,dims//2):
        for j in range(0, dims//2):
            ene_sum = ene_sum+np.power(2*i+1,2)+np.power(2*j+1,2)
    ene_sum = ene_sum/(np.power(dims,2)//4)
    return ene_sum


"""
Contains functions which implement a variety of Fractals

Written by: Benjamin Smith
     email: ben297@gmail.com
    github: https://github.com/obbiwan
"""
import math
import random

import numba
import numpy as np

from utils import PlotData

def barnsley(n_points: int = 10**6) -> PlotData:
    """
    Calculates a list of (x, y) points according to Barnsley's Fern
    https://en.wikipedia.org/wiki/Barnsley_fern
    :param n_points: The number of (x, y) plot points to calculate
    :return: a PlotData dictionary, containing the data necessary to plot the Duffing Map
    """

    @numba.jit
    def rule(p):
        """Applies the Barnsley's Fern transformation to an (x, y) point to get the next (x, y) point"""
        r = random.random()
        if r < 0.01:
            return +0.00*p[0] + 0.00*p[1], +0.00*p[0] + 0.16*p[1] + 0.00
        elif r < 0.86:
            return +0.85*p[0] + 0.04*p[1], -0.04*p[0] + 0.85*p[1] + 1.60
        elif r < 0.93:
            return +0.20*p[0] - 0.26*p[1], +0.23*p[0] + 0.22*p[1] + 1.60
        else:
            return -0.15*p[0] + 0.28*p[1], +0.26*p[0] + 0.24*p[1] + 0.44

    # numpy arrays to store plot points
    x = np.zeros(n_points)
    y = np.zeros(n_points)

    # Set the starting point
    x[0], y[0] = 0.0, 0.0

    # Calculate the points
    for i in range(n_points-1):
        x[i+1], y[i+1] = rule((x[i], y[i]))

    # Calculate the plot area
    x_min, x_max = min(x), max(x)
    y_min, y_max = min(y), max(y)

    return PlotData(x=x, y=y, x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max, plot_title="Barnsley's Fern",
                    filename='barnsley')

def mandelbrot(x_min: float = -1.5, x_max: float = 0.5, y_min: float = -1.0, y_max: float = 1.0,
               samples: int = 10**6, max_iterations: float = 100) -> PlotData:
    """
    Calculates a list of (x, y) points according to the Mandelbrot Set
    https://en.wikipedia.org/wiki/Mandelbrot_set
    :param x_min: The lower bound of the horizontal axis
    :param x_max: The upper bound of the horizontal axis
    :param y_min: The lower bound of the vertical axis
    :param y_max: The upper bound of the vertical axis
    :param samples: The number of samples points to test from the plot area
    :param max_iterations: The maximum number of iterations to apply to each point before deciding it does not diverge
    :return: a PlotData dictionary, containing the data necessary to plot the Logistic Map
    """

    n_x = math.floor(math.sqrt(samples))
    n_y = math.floor(math.sqrt(samples))
    p_x, p_y = [], []

    @numba.jit
    def diverges(a: float, b: float):
        """Repeatedly applies the Mandelbrot transformation to a point and returns whether the point diverged"""
        z = 0.0
        i = 0
        while i < max_iterations:
            z = z ** 2 + (a + b * 1j)
            i += 1
            if abs(z) >= 2.0:
                return True
        return False

    for x in np.linspace(x_min, x_max, n_x):
        for y in np.linspace(y_min, y_max, n_y):

            if not diverges(x, y):
                p_x.append(x)
                p_y.append(y)

    return PlotData(x=p_x, y=p_y, x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max, plot_title='Mandelbrot Set',
                    filename='mandelbrot')


from random import random
import numpy as np

def neurona(w, p, b):
    a = np.dot(w, p[:, q]) + b
    if(a >= 0):
        return 1
    else:
        return 0

p = [
    # a  b  c  d  e  f  g
    [1, 1, 1, 1, 1, 1, 0],  # %0
    [0, 1, 1, 0, 0, 0, 0],  # %1
    [1, 1, 0, 1, 1, 0, 1],  # %2
    [1, 1, 1, 1, 0, 0, 1],  # %3
    [0, 1, 1, 0, 0, 1, 1],  # %4
    [1, 0, 1, 1, 0, 1, 1],  # %5
    [1, 0, 1, 1, 1, 1, 1],  # %6
    [1, 1, 1, 0, 0, 0, 0],  # %7
    [1, 1, 1, 1, 1, 1, 1],  # %8
    [1, 1, 1, 1, 0, 1, 1]  # %9
]

p = np.array(p)
p = np.transpose(p)

tpar = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  # Numeros pares
tmay5 = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1]  # Mayores que 5
tprim = [0, 0, 1, 1, 0, 1, 0, 1, 0, 0]  # Numeros Primos

def menu():
    print('Seleccione una opcion: \n',
    '1 Numeros pares\n',
    '2 Numeros mayores que 5\n',
    '3 Numeros primos')

if __name__ == '__main__':
    menu()
    x = input('Seleccione una opcion:')
    t = tpar
    while(True):
        if(x == '2'):
            t = tmay5
            break
        elif(x == '3'):
            t = tprim
            break
        elif(x == '1'):
            break
        else:
            print('opcion no valida')
            menu()
            x = input('Seleccione una opcion:')

    w = 2*np.random.rand(1, 7)-1
    b = 2*random()-1
    e = np.array([None]*10)

    for epocas in range(0, 500):
        for q in range(0, 10):
            e[q] = t[q] - neurona(w, p, b)
            w += e[q] * np.transpose(p[:, q])
            b += e[q]

    print('Error: \n', e)
    print('\nPesos sinapticos: \n', w)
    print('\nPolarizacion: \n', b)
    input('\nPresione enter para salir')


import numpy as np
import matplotlib.pyplot as plt
from tdma import tdma

# Determine error in FDM and FVM approaches for COUETTE FLOW (Laplace equation)

# Create a tridiagonal matrix
def tridiag(a, b, c, k1, k2, k3): # ki is the ith diagonal relative to principal diagonal
    return np.diag(a, k1) + np.diag(b, k2) + np.diag(c, k3)
L = 1 # length of the domain
Tl = -0.5; # Left Dir BC
Tr = +0.5; # Right Dir BC
error_fdm = np.zeros(256-4);
error_fvm = np.zeros(256-4);

for N in range(4,256):
# FINITE DIFFERENCE FORMULATION
    h = L/(N-1);   # spacing
    x = np.linspace(0,L,N) # grid

    # Coefficent matrix
    a = np.ones(N-3);    # upper and lower diagonal
    b = -2*np.ones(N-2); # principal diagonal
    A = tridiag(a, b, a, -1, 0, 1)

    # Constant matrix
    X = np.zeros((N-2,1));X[0] = -Tl; X[-1] = -Tr;

    # Solution
    T_actual = x-0.5;
    T_num = tdma(A,X); # TDMA
    T_fdm = np.append(np.append(Tl,T_num),Tr) # Append BCs

    # Error
    err_fdm = np.sqrt(np.sum(np.power(T_fdm-T_actual,2)))/N;
    error_fdm[N-4] = err_fdm;

    # FINITE VOLUME FORMULATION
    dy = L/(N-1);   # spacing (dyn = dys = dy)
    dys = dy;
    dyn = dy;

    # Coefficent matrix
    a = np.ones(N-3)/dyn;    # upper diagonal
    b = -np.ones(N-2)*(1/dys+1/dyn); # principal diagonal
    a = np.ones(N-3)/dys;    # lower diagonal
    A = tridiag(a, b, a, -1, 0, 1)

    # Constant matrix
    X = np.zeros((N-2,1));X[0] = -Tl; X[-1] = -Tr; X = X/dy;

    # Solution
    T_actual = x-0.5;
    T_num = tdma(A,X); # TDMA
    T_fvm = np.append(np.append(Tl,T_num),Tr) # Append BCs

    # Error
    err_fvm = np.sqrt(np.sum(np.power(T_fdm-T_actual,2)))/N;
    error_fvm[N-4] = err_fvm;

# Plot error
plt.plot((error_fdm),'r*')
plt.plot((error_fvm),'g.',markersize = 3)
plt.legend(('finite difference','finite volume'));
plt.xlabel('number of nodes');plt.ylabel('$L_2$  error');
plt.show()


import matplotlib.pyplot as plt
import numpy as np
from scipy.fftpack import fft

M = 64
N = 1024
hN = N//2
hM = M//2
fftbuffer = np.zeros(N)
mX1 = np.zeros(N)

plt.figure(1, figsize=(9.5, 6))
fftbuffer[hN-hM:hN+hM]=np.ones(M)
plt.subplot(2,1,1)
plt.plot(np.arange(-hN, hN), fftbuffer, 'b', lw=1.5)
plt.axis([-hN, hN, 0, 1.1])
plt.title('w (rectangular window), M = 64')

X = fft(fftbuffer)
mX = 20*np.log10(abs(X))
mX1[:hN] = mX[hN:]
mX1[N-hN:] = mX[:hN]

plt.subplot(2,1,2)
plt.plot(np.arange(-hN, hN), mX1-max(mX), 'r', lw=1.5)
plt.axis([-hN,hN,-40,0])
plt.title('mW, N = 1024')
plt.annotate('main-lobe', xy=(0,-10), xytext=(-200, -5), fontsize=16, arrowprops=(dict(facecolor='black', width=2, headwidth=6, shrink=0.01)))
plt.annotate('highest side-lobe', xy=(32,-13), xytext=(100, -10), fontsize=16, arrowprops=(dict(facecolor='black', width=2, headwidth=6, shrink=0.01)))

plt.tight_layout()
plt.savefig('rectangular-1.png')
plt.show()


import numpy as np

N = 100
D = 2

X = np.random.randn(N, D)
# center first 50 points at (-2, -2)
X[:50, :] = X[:50, :] - 2 * np.ones((50, D))
# center last 50 points at (2, 2)
X[50:, :] = X[50:, :] + 2 * np.ones((50, D))
# labels: first 50 = 0, last 50 = 1
T = np.array([0] * 50 + [1] * 50)

ones = np.array([[1] * N]).T
Xb = np.concatenate((ones, X), axis=1)

W = np.random.random(D + 1)
z = Xb.dot(W)

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

Y = sigmoid(z)

def cross_entropy(T, Y):
    E = 0
    for i in range(N):
        if T[i] == 1:
            E -= np.log(Y[i])
        else:
            E -= np.log(1 - Y[i])
    return E

learning_rate = 0.1
l2 = 0.1
for i in range(100):
    if i % 10 == 0:
        print(cross_entropy(T, Y))

    # L2 penalty for weight
    W += learning_rate * (Xb.T.dot(T - Y) - l2*W)
    Y = sigmoid(Xb.dot(W))

print("Final w:", W)


import numpy as np

# Create a function that will produce arrays of (x,y) positions for an arc
# segment, from position ri to rf, divided into N equally spaced intervals
def arcSegment(Ti, Tf, N):
    r = 5
    theta = np.linspace(Ti, Tf, N)
    x = r*np.cos(theta)
    y = r*np.sin(theta)
    dS = (r*(theta[1]-theta[0])) #ds = r*dtheta
    return x, y, dS

# Return the electric field, E, due to a charge "q" located at position (xq,yq).
# Evaluate V at the given position (x,y).
def eField(q, xq, yq, x, y):
    k = 9.0e9
    denom = ( (x - xq)**2 + (y - yq)**2 )**1.5
    dEx = k * q * (x - xq) / denom
    dEy = k * q * (y - yq) / denom
    return dEx, dEy

# Create a function that will integrate, using Simpson's Rule
# Inputs should be A function f, and a segment length dS (associated with dQ)
def simps (f, dS):
    simp = 0
    for i in range (1, Nsegments-1):
        if (i%2 == 0):
            simp += 2*f[i]
        else:
            simp += 4*f[i]

    simp += f[0]
    simp += f[-1]
    simp = simp*(dS/3)

    return simp

# Create a function that will integrate, using Simpson's Rule
# Inputs should be A function f, and a segment length dS (associated with dQ)
def trapz (f, dL):
    return dL/2*np.sum(f[1:] + f[:-1])

# Define characteristics of the uniformly charged arc segment
Ti = 0
Tf = np.pi

Nsegments = 101
Qtot = 3e-6
S = 5*np.pi #length of arc s=r*theta

# linear charge density & charge per segment dS
lamb = Qtot/S

# position at which electric field is calculated
Ro = np.array([0,0])
Ro2 = np.array([10,0])

X, Y, dS = arcSegment(Ti, Tf, Nsegments)

# compute dEx, dEy at position Ro due to each dQ contribution along the
# charged line segment.
# NOTE:  the *Ro - assigns Ro values to appropriate variables, in order of how
#        they are defined in function
dEx, dEy = eField(lamb,X,Y,*Ro)
dEx2, dEy2 = eField(lamb,X,Y,*Ro2)

# Apply simps function to integrate all contributions dEx, dEy to compute the
# total electric field at position Ro
print("UNIFORMLY CHARGED ARC SEGMENT:\n")

Ex = trapz(dEx, dS)
Ey = trapz(dEy, dS)

print("Using the Trapezoid Rule at the point (0, 0): \nEx = %.6E \nEy = %.6f \n" % (Ex, Ey))

Ex2 = trapz(dEx2, dS)
Ey2 = trapz(dEy2, dS)

print("Using the Trapezoid Rule at the point (10, 0): \nEx = %.6f \nEy = %.6f \n" % (Ex2, Ey2))

Ex = simps(dEx, dS)
Ey = simps(dEy, dS)

print("Using Simpson's Rule at the point (0, 0): \nEx = %.6E \nEy = %.6f \n" % (Ex, Ey))

Ex2 = simps(dEx2, dS)
Ey2 = simps(dEy2, dS)

print("Using Simpson's Rule at the point (10, 0): \nEx = %.6f \nEy = %.6f \n" % (Ex2, Ey2))


import jax.numpy as jnp
from jax import grad, jit, vmap
from jax import random

key = random.PRNGKey(0)

grad_tanh = grad(jnp.tanh)
print(grad_tanh(2.0))

print(grad(grad(jnp.tanh))(2.0))
print(grad(grad(grad(jnp.tanh)))(2.0))

def sigmoid(x):
    return 0.5 * (jnp.tanh(x / 2) + 1)

# Outputs probability of a label being true.
def predict(W, b, inputs):
    return sigmoid(jnp.dot(inputs, W) + b)

# Build a toy dataset.
inputs = jnp.array([[0.52, 1.12,  0.77],
                   [0.88, -1.08, 0.15],
                   [0.52, 0.06, -1.30],
                   [0.74, -2.49, 1.39]])
targets = jnp.array([True, True, False, True])

# Training loss is the negative log-likelihood of the training examples.
def loss(W, b):
    preds = predict(W, b, inputs)
    label_probs = preds * targets + (1 - preds) * (1 - targets)
    return -jnp.sum(jnp.log(label_probs))

# Initialize random model coefficients
key, W_key, b_key = random.split(key, 3)
W = random.normal(W_key, (3,))
b = random.normal(b_key, ())

# Differentiate `loss` with respect to the first positional argument:
W_grad = grad(loss, argnums=0)(W, b)
print('W_grad', W_grad)

# Since argnums=0 is the default, this does the same thing:
W_grad = grad(loss)(W, b)
print('W_grad', W_grad)

# But we can choose different values too, and drop the keyword:
b_grad = grad(loss, 1)(W, b)
print('b_grad', b_grad)

# Including tuple values
W_grad, b_grad = grad(loss, (0, 1))(W, b)
print('W_grad', W_grad)
print('b_grad', b_grad)


#obtained from http://stackoverflow.com/questions/13903922/multinomial-pmf-in-python-scipy-numpy

import math
import numpy as np #usage of np.log instead - makes the function vectorizable

class Multinomial(object):
  def __init__(self, params):
    self._params = params

  def pmf(self, counts):
    if not(len(counts)==len(self._params)):
      raise ValueError("Dimensionality of count vector is incorrect")

    prob = 1.
    for i,c in enumerate(counts):
      prob *= self._params[i]**counts[i]

    return prob * np.exp(self._log_multinomial_coeff(counts))

  def log_pmf(self,counts):
    if not(len(counts)==len(self._params)):
      raise ValueError("Dimensionality of count vector is incorrect")

    prob = 0.
    for i,c in enumerate(counts):
      prob += counts[i]*np.log(self._params[i])

    return prob + self._log_multinomial_coeff(counts)

  def _log_multinomial_coeff(self, counts):
    return self._log_factorial(sum(counts)) - sum(self._log_factorial(c)
                                                    for c in counts)

  def _log_factorial(self, num):
    if not round(num)==num and num > 0:
      raise ValueError("Can only compute the factorial of positive ints")
    return np.sum(np.log(n) for n in range(1,num+1))


"""
Description:
    This is a simple script to demonstrate Scipy's curve_fit module using a few common mathematical functions.
============================================================================================
License_info:
    LCD-Composer is subject to the terms of the MIT license.
"""

__author__ = 'Sean M Cascarina'
__copyright__ = 'Copyright 2021'
__credits__ = ['Sean M Cascarina']
__license__ = 'MIT'
__version__ = '1.0'
__maintainer__ = 'Sean M Cascarina'
__email__ = 'Sean.Cascarina@colostate.edu'

# IMPORTS
from scipy.optimize import curve_fit
import random
import matplotlib.pyplot as plt
import math
import numpy as np

def main():

    # COMMENT OR UNCOMMENT THESE LINES TO RUN DIFFERENT SIMULATIONS
    print('\n')
    run_linear_fit()
    run_gaussian_fit()
    run_quadratic_fit()
    run_cubic_fit()
    run_sine_fit()
    print('\n')

def lin_objective(x, m, b):
    """Linear objective function"""
    return m*x + b

def run_linear_fit(slope=2, xnoise=0.2, ynoise=0.4):
    """Run linear fit simulation.
        By default, the slope in the simulated data is == 2."""

    # SIMPLE LINEAR EXAMPLE=============================================================
    xdata = [x+random.uniform(-xnoise, xnoise) for x in range(1, 21)]
    ydata = [lin_objective(x, slope, random.uniform(-ynoise, ynoise)) for x in range(1, 21)]

    popt, pcov = curve_fit(lin_objective, xdata, ydata)
    m, b = popt

    print('Linear estimated parameters:', 'Slope(m)='+str(m), 'Intercept(b)='+str(b))

    plt.scatter(xdata, ydata)
    plt.plot([x for x in range(1, 21)], [lin_objective(x, m, b) for x in range(1, 21)], color='#d62728')
    plt.show()
    #===================================================================================

def gauss_objective(x, amplitude, sigma, mu):
    """Gaussian distribution objective function.
        This differs from many normal distribution equations online but the amplitude is required to
        stretch the Gaussian upward to match your specific data size.

        This equation was adapted from: https://stackoverflow.com/questions/19206332/gaussian-fit-for-python,
        but also matches the first equation on the Gaussian function Wikipedia page: https://en.wikipedia.org/wiki/Gaussian_function,
        and can be found elsewhere in this form as well."""

    # return amplitude * (1 / (math.sqrt(2*math.pi*sigma**2))) * math.e**(-1 * (x-mu)**2 / 2*sigma**2)
    # return amplitude * np.exp(-(x - mu)**2 / (2 * sigma**2))
    return amplitude * math.e**(-(x - mu)**2 / (2 * sigma**2))

def run_gaussian_fit(sigma=1, mu=0, noise=0.1):
    """Run Gaussian fit simulation"""

    # GENERATE A SAMPLE OF NORMALLY DISTRIBUTED DATA
    data = np.random.normal(mu, sigma, 1000)
    data = [x+random.uniform(-noise, noise) for x in data]  # ADD NOISE TO THE DATA

    # PLOT HISTOGRAM AND STORE BIN LOCATIONS AND CORRESPONDING FREQUENCIES IN xvals AND yvals, RESPECTIVELY
    yvals, xvals, patches = plt.hist(data, bins=30)

    # SHIFT xvals BY HALF OF THE bin_width TO ACCOUNT FOR THE FACT THAT plt.hist() RETURNS AN ARRAY WITH THE LEFT BIN EDGES AND THE FINAL ELEMENT IN THE LIST BEING THE FINAL RIGHT BIN EDGE
    # THIS ADJUSTMENT MAKES THE CURVE FITTING PARAMETERS MORE ACCURATE/CENTERED, BUT ALSO SEEMS TO CONSISTENTLY RESULT IN A NEGATIVE STANDARD DEVIATION ESTIMATE AND ALL VALUES END UP BEING IN SCIENTIFIC NOTATION AFTER THIS ADJUSTMENT
    bin_width = xvals[-1] - xvals[-2]
    # print(bin_width)
    xvals = [x+bin_width/2 for x in xvals[:-1]]

    # CALCULATE amplitude AS MAXIMUM OF yvals...THIS IS USED TO STRETCH THE GAUSSIAN CURVE TO ACTUALLY FIT OUR DATA SIZE
    amplitude = max(yvals)

    # RUN CURVE FITTING WITH THE xvals AND yvals DETERMINED FROM THE HISTOGRAM
    popt, pcov = curve_fit(gauss_objective, xvals, yvals)
    a, s, m = popt

    print('Gaussian estimated parameters:', 'Amplitude='+str(a), 'Sigma='+str(s), 'Mu='+str(m))

    # CALCULATE VALUES TO PLOT THE GAUSSIAN FIT USING THE PARAMETERS DETERMINED FROM curve_fit()
    fit_data = [gauss_objective(x, max(yvals), s, m) for x in xvals]
    plt.plot(xvals, fit_data, color='#d62728')
    plt.show()

def quadratic_objective(x, a, b, c):
    """Quadratic objective function."""
    return a*x**2 + b*x + c

def run_quadratic_fit(a=1, b=1, c=1, noise=5, data_range=100):
    """Run quadratic fit simulation."""

    # SAMPLE DATA USING OBJECTIVE FUNCTION
    xvals = np.linspace(-data_range, data_range, num=100)
    yvals = [quadratic_objective(x, a, b, c) for x in xvals]

    # ADD NOISE TO DATA
    xdata = [x+random.uniform(-noise, noise) for x in xvals]
    # ydata = [y+random.uniform(-noise, noise) for y in yvals]
    ydata = [y+quadratic_objective(random.uniform(-noise, noise), a, b, c)-c for y in yvals]  # USES THE QUADRATIC OBJECTIVE FUNCTION TO MAKE THE NOISE PROPORTIONAL IN MAGNITUDE ON THE Y-AXIS

    # RUN CURVE FITTING USING SIMULATED DATA
    popt, pcov = curve_fit(quadratic_objective, xdata, ydata)

    # UNPACK PARAMETER ESTIMATES
    est_a, est_b, est_c = popt
    print('Quadratic estimated parameters:', 'a='+str(est_a), 'b='+str(est_b), 'c='+str(est_c))

    # PLOT SIMULATED DATA IN SCATTER PLOT (BLUE), AND CURVE FIT FUNCTION AS A LINE (RED)
    plt.scatter(xdata, ydata)
    plt.plot([x for x in range(-data_range, data_range)], [quadratic_objective(x, est_a, est_b, est_c) for x in range(-data_range, data_range)], color='#d62728')
    plt.show()

def cubic_objective(x, a, b, c, d):
    """Cubic objective function."""
    return a*x**3 + b*x**2 + c*x + d

def run_cubic_fit(a=1, b=1, c=1, d=1, noise=5, data_range=100):
    """Run cubic fit simulation."""

    # SAMPLE DATA USING OBJECTIVE FUNCTION
    xvals = np.linspace(-data_range, data_range, num=100)
    yvals = [cubic_objective(x, a, b, c, d) for x in xvals]

    # ADD NOISE TO DATA
    xdata = [x+random.uniform(-noise, noise) for x in xvals]
    # ydata = [y+random.uniform(-noise, noise) for y in yvals]
    ydata = [y+cubic_objective(random.uniform(-noise, noise), a, b, c, d)-d for y in yvals]  # USES THE CUBIC OBJECTIVE FUNCTION TO MAKE THE NOISE PROPORTIONAL IN MAGNITUDE ON THE Y-AXIS

    # RUN CURVE FITTING USING SIMULATED DATA
    popt, pcov = curve_fit(cubic_objective, xdata, ydata)

    # UNPACK PARAMETER ESTIMATES
    est_a, est_b, est_c, est_d = popt
    print('Cubic estimated parameters:', 'a='+str(est_a), 'b='+str(est_b), 'c='+str(est_c), 'd='+str(est_d))

    # PLOT SIMULATED DATA IN SCATTER PLOT (BLUE), AND CURVE FIT FUNCTION AS A LINE (RED)
    plt.scatter(xdata, ydata)
    plt.plot([x for x in range(-data_range, data_range)], [cubic_objective(x, est_a, est_b, est_c, est_d) for x in range(-data_range, data_range)], color='#d62728')
    plt.show()

def sine_objective(x, a, b):
    """Sine objective function."""
    return a*np.sin(x) + b

def run_sine_fit(a=2, b=5, noise=0.1, data_range=10):
    """Run sine fit simulation."""

    # SAMPLE DATA USING OBJECTIVE FUNCTION
    xvals = np.linspace(-data_range, data_range, num=100)
    yvals = [sine_objective(x, a, b) for x in xvals]

    # ADD NOISE TO DATA
    xdata = [x+random.uniform(-noise, noise) for x in xvals]
    # ydata = [y+random.uniform(-noise, noise) for y in yvals]
    ydata = [y+sine_objective(random.uniform(-noise, noise), a, b)-b for y in yvals]  # USES THE SINE OBJECTIVE FUNCTION TO MAKE THE NOISE PROPORTIONAL IN MAGNITUDE ON THE Y-AXIS. SUBTRACTING THE INTERCEPT (b) IS NECESSARY SO THAT THE INTERCEPT ISN'T ADDED TO EACH Y VALUE TWICE.

    # RUN CURVE FITTING USING SIMULATED DATA
    popt, pcov = curve_fit(sine_objective, xdata, ydata)

    # UNPACK PARAMETER ESTIMATES
    est_a, est_b = popt
    print('Sine function estimated parameters', 'a='+str(est_a), 'b='+str(est_b))

    # PLOT SIMULATED DATA IN SCATTER PLOT (BLUE), AND CURVE FIT FUNCTION AS A LINE (RED)
    plt.scatter(xdata, ydata)
    plt.plot(xvals, [sine_objective(x, est_a, est_b) for x in xvals], color='#d62728')
    plt.show()

if __name__ == '__main__':
    main()


# Linear Regression Gradient Descent with Multiple Features

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

house_sales = pd.read_csv('house_sales.csv')

features = house_sales.iloc[:, 3:9].values

data_m = features
stdm = np.std(data_m, axis=0)
mum = np.mean(data_m, axis=0)
data_m = (data_m - mum) / stdm
ones = np.ones((len(data_m), 1))
data_m = np.hstack((ones, data_m))

print(np.shape(data_m))

# setup targets
data_y = house_sales[['price']].values.flatten()[:, np.newaxis]

# separate into training and testing datasets
order = np.random.permutation(len(data_m))
portion = 10000
test_mx = data_m[order[:portion]]
test_my = data_y[order[:portion]]
train_mx = data_m[order[portion:]]
train_my = data_y[order[portion:]]

def get_gradient_multi(w, x, y):
    y_estimate = x.dot(w).flatten()[:, np.newaxis]
    error = (y - y_estimate)

    mse = (1 / len(x)) * np.sum(np.power(error, 2))

    gradient = -(1 / len(x)) * np.transpose(x).dot(error)

    return gradient, mse

# variables for use in gradient descent algorithm
w = np.zeros((np.size(data_m, axis=1), 1))
alpha = 0.05
tolerance = 1e-5

# Perform Gradient Descent
iterations = 1
while True:
    gradient, error = get_gradient_multi(w, train_mx, train_my)
    new_w = w - alpha * gradient

    # Stopping Condition
    if np.sum(abs(new_w - w)) < tolerance:
        print('converged')
        break

    # Print error every 10 iterations
    if iterations % 10 == 0:
        print("Iteration:", iterations)
        print("Error:", np.sum(error))

    iterations += 1
    w = new_w

print('w =', w)
print('Test Cost = ', get_gradient_multi(w, test_mx, test_my)[1])

# sort model output for plot
datadotw = data_m.dot(w).flatten()
data = data_m[:, 2]
sorted_index = np.argsort(data)
sorted_x = data[sorted_index]
sorted_y = datadotw[sorted_index]

print('features', test_mx[1, :])
print('actual price', test_my[1, :])
print('predicted price', test_mx[1, :].dot(w))

# plot
plt.plot(sorted_x, sorted_y, c='g', label='Model')
plt.scatter(train_mx[:,1], train_my, c='b', label='Train Set')
plt.scatter(test_mx[:,1], test_my, c='r', label='Test Set')
plt.grid()
plt.legend(loc='best')
plt.xlabel('X')
plt.ylabel('Y')
# plt.show()


#!/usr/bin/env python3

import numpy as np
from numpy.core.fromnumeric import size
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.datasets import load_boston
from warnings import filterwarnings
filterwarnings('ignore')

class LinearRegression (object) :
    def fit(self, X, y, intercept = False) :
        if intercept == False :
            ones = np.ones(len(X)).reshape(len(X), 1)
            X = np.concatenate((ones, X), axis = 1)
        self.X = np.array(X)
        self.y = np.array(y)
        self.N, self.D = self.X.shape

        # ------------- estimate parameters -----------------
        XtX = np.dot(self.X.T, self.X)
        XtX_inverse = np.linalg.inv(XtX)
        Xty = np.dot(self.X.T, self.y)
        self.beta_hats = np.dot(XtX_inverse, Xty)

        # ------------ in-sample prediction -----------------
        self.y_hat = np.dot(self.X, self.beta_hats)

        # -------------- Calculate Loss ---------------------
        self.L = .5 * np.sum((self.y - self.y_hat)**2)

    def predict(self, X_test, intercept = True) :
        self.y_test_hat = np.dot(X_test, self.beta_hats)

# ----------------------- Load data ----------------------------

data = load_boston()
X = data['data']
y = data['target']
model = LinearRegression()
model.fit(X, y, intercept = False)

plt.style.use('seaborn-whitegrid')
plt.figure(figsize = (12, 6), dpi = 80)
sns.scatterplot(model.y, model.y_hat, color = 'green' )
plt.xlabel(r'$y$', size = 16)
plt.ylabel(r'$\hat{y}$', rotation = 0, size = 16, labelpad = 15)
plt.title(r'$y$ vs. $\hat{y}$', size = 20, pad = 10)
plt.show()


import numpy as np
from quadratic_programming import qp
class NonNegativeLinearRegressor(object):
    """ Class for computing nonnegative least squares solution if lambda_l2 is zero,
        otherwise nonnegative ridge regression."""

    def __init__(self, lambda_l2=0., intercept=True, **kwargs):
        self.intercept = intercept
        self.lambda_l2 = lambda_l2
        self.qp_kwargs = kwargs

    def fit(self, X, Y, **kwargs):
        if self.intercept:
            X = np.insert(X, -1, 1., axis=1)

        Q = X.T @ X
        p = - X.T @ Y

        if self.lambda_l2 > 0:
            Q += self.lambda_l2 * np.eye(X.shape[1])

        # Apart from the constraint coefs => 0, which is already implemented as
        # default in the qp function, no further constraint is needed
        G = np.empty((0, X.shape[1]))
        h = np.empty((0, ))
        A = np.empty((0, X.shape[1]))
        b = np.empty((0, ))

        self.coefs = qp(Q, p, G, h, A, b, **self.qp_kwargs)

    def predict(self, X):
        if self.intercept:
            X = np.insert(X, -1, 1., axis=1)
        return np.dot(X, self.coefs)

if __name__ == '__main__':
    from sklearn import datasets
    from plot import scatter

    # Load the diabetes dataset
    X, Y = datasets.load_diabetes(return_X_y=True)

    # Split the data into training/testing sets
    X_train, X_test = X[:-20], X[-20:]
    Y_train, Y_test = Y[:-20], Y[-20:]

    regr = NonNegativeLinearRegressor(lambda_l2=0.001,
                           max_steps_qp=5000, alpha=0.001, tol_eigen_value=1e-1, mu=0.000001 # qp parameters
                           )
    regr.fit(X_train, Y_train)
    Y_pred = regr.predict(X_test)
    print("coefficients:")
    print(regr.coefs)
    scatter(Y_test, Y_pred)


import math
import numpy as np

def norm(v):
    return np.linalg.norm(v)

def unit_vector(v):
    return v / norm(v)

def dot(v1, v2):
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)
    return np.clip(np.dot(v1_u, v2_u), -1.0, 1.0)

def angle_between_coords(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
    """calculate radian between 2 (lat,lon) coords in radian

    Extended description of function.

    Args:
        lat1: latitude of first coordination
        lon1: longtitude of first coordination
        lat2: latitude of first coordination
        lon2: longtitude of first coordination

    Returns:
        radian between two coordinations
    """
    lat1, lon1, lat2, lon2 = np.deg2rad((lat1, lon1, lat2, lon2))
    return np.arccos(np.sin(lat1)*np.sin(lat2) + np.cos(lat1)*np.cos(lat2) * np.cos(abs(lon1 - lon2)))

def distance_between_coords(lat1: float, lon1: float, lat2: float, lon2: float, body_radius: float) -> float:
    """calculate distance between 2 (lat,lon) coords

    Extended description of function.

    Args:
        lat1: latitude of first coordination
        lon1: longtitude of first coordination
        lat2: latitude of second coordination
        lon2: longtitude of second coordination
        body_radius: radius of body

    Returns:
        distance between two coordinations
    """
    lat1, lon1, lat2, lon2 = np.deg2rad((lat1, lon1, lat2, lon2))
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = np.sin(0.5*dlat)**2.0 + np.cos(lat1)*np.cos(lat2)*(np.sin(0.5*dlon)**2.0)
    c = 2.0*np.arctan2(np.sqrt(a), np.sqrt(1-a))
    distance = body_radius * c
    return distance

def bearing_between_coords(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
    """calculate initial bearing between 2 (lat,lon) coords

    Extended description of function.

    Args:
        lat1: latitude of first coordination
        lon1: longtitude of first coordination
        lat2: latitude of second coordination
        lon2: longtitude of second coordination
        body_radius: radius of body

    Returns:
        initial bearing from first to second (degree)
    """
    lat1, lon1, lat2, lon2 = np.deg2rad((lat1, lon1, lat2, lon2))
    dlon = lon2 - lon1

    y = np.sin(dlon) * np.cos(lat2)
    x = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(dlon)
    bearing = np.rad2deg(np.arctan2(y, x)) % 360
    return bearing

def bearing_and_distance_between_coords(lat1: float, lon1: float, lat2: float, lon2: float, body_radius: float) -> ():
    """calculate distance and initial bearing between 2 (lat,lon) coords

    Extended description of function.

    Args:
        lat1: latitude of first coordination
        lon1: longtitude of first coordination
        lat2: latitude of second coordination
        lon2: longtitude of second coordination
        body_radius: radius of body

    Returns:
        distance between two coordinations
        initial bearing from first to second (degree)
    """
    return distance_between_coords(lat1, lon1, lat2, lon2, body_radius), bearing_between_coords(lat1, lon1, lat2, lon2)

def latlon(vector):
    """function to calculate (lat, lon) from state vectors (x,y,z)

    Extended description of function.

    Args:
        vector: (x,y,z) state vector

    Returns:
        lattitude in degree
        longtitude in degree
    """
    lon = np.arctan2(vector[2], vector[0])
    p = (vector[0]**2.0 + vector[2]** 2.0)**0.5
    lat = np.arctan2(vector[1], p)
    lat, lon = np.rad2deg((lat, lon))
    return lat, lon

def angle_between(v1, v2):
    """Calculate angle between two vector.

    Extended description of function.

    Args:
        v1: vector
        v2: vector

    Returns:
        radian between two vector
    """
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)
    return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))

def clamp_2pi(x):
    """
    clamp radians to a single revolution
    """
    while x > (2 * math.pi):
        x -= (2 * math.pi)

    return x

class PIDController(object):
    """ Robust, single parameter, proportional-integral-derivative controller
        http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/ """

    def __init__(self, ut:float = 0, Kp:float = 1, Ki:float = 0, Kd:float = 0):
        self.set_params(Kp = Kp, Ki = Ki, Kd = Kd)
        self.Ti = 0
        self.last_ut = ut
        self.last_error = 0

    def set_params(self, Kp:float = None, Ki:float = None, Kd:float = None):
        if Kp != None:
            self.Kp = Kp
        if Ki != None:
            self.Ki = Ki
        if Kd != None:
            self.Kd = Kd

    def update(self, input, set_point, ut, min_output, max_output):
        d_ut = ut - self.last_ut
        error = set_point - input
        d_error = error - self.last_error
        self.Ti += self.Ki * error * d_ut
        self.Ti = np.maximum(min_output, np.minimum(max_output, self.Ti))
        if d_ut != 0:
            output = self.Kp * error + self.Ti - self.Kd * d_error / d_ut
        else:
            output = self.Kp * error + self.Ti
        output = np.maximum(min_output, np.minimum(max_output, output))
        self.last_error = error
        self.latst_ut = ut
        return output


import numpy as np
def elim_gauss(M, N):
    n = M.shape[0]
    x = np.zeros(n)
    for i in range(n-1):
        for j in range(i+1, n):
            m = M[j][i]/M[i][i]
            for k in range(i, n):
                M[j][k] = M[j][k] - m*M[i][k]
            N[j] = N[j] - m*N[i]
    for j in range(n-1,-1,-1):
        x[j] = N[j]/M[j][j]
        for i in range(j-1,-1,-1):
            N[i] = N[i] - M[i][j]*x[j]
    return x
def impr(M, N):
    for i in range(M.shape[0]):
        linha = []
        for j in range(M.shape[1]):
            linha.append('{0}*x{1}'.format(M[i, j], j + 1))
        print(" + ".join(linha), "=", N[i])
    print()
MAXITER = 100
M = np.array([[4., -7., 9., 1.],
              [-16., 0., -2., 10.],
              [6., -2., 7., -12.],
              [0., 5., -2., 7.]])
N = np.array([-9., 14., 6., 26.])
impr(M, N)
x = elim_gauss(M, N)
print(x)
input()


import numpy as np

def omega(power,N):
    return np.exp(2.0*np.pi*power*1j/N)

def ope1(a,b):
    return a+b

def ope2(a,b,power,N):
    return (a-b)*omega(power,N)

def FFT_2(a,b):
    return [ope1(a,b),ope2(a,b,0,2)]

def FFT(xs,N):
    if N==2:
        return FFT_2(xs[0],xs[1])
    else:
        xs_former=[]
        half_len=len(xs)//2
        for i in range(half_len):
            a,b=xs[i],xs[i+half_len]
            xs_former.append(ope1(a,b))
        ys_former=FFT(xs_former,N//2)

        xs_latter=[]
        for i in range(half_len):
            a,b=xs[i],xs[i+half_len]
            xs_latter.append(ope2(a,b,i,N))
        ys_latter=FFT(xs_latter,N//2)

        return ys_former+ys_latter

def calc_FFT(xs):
    N=len(xs)
    if not N & (N-1) == 0:
        raise ValueError("you can not use iff len(xs)==2**k for some k")
    outs=FFT(xs,N)
    #resort output
    ys=[None for _ in range(len(outs))]
    for i in range(len(outs)):
        bit=format(i,'0{}b'.format(int(np.log2(N))))
        ys[int(bit[::-1],2)]=outs[i]
    return ys

def main():
    N=4
    xs=[1,1,0,0]
    print(calc_FFT(xs))
    xs=[0,np.pi/4,2*np.pi/4,3*np.pi/4,np.pi,3*np.pi/4,2*np.pi/4,np.pi/4]
    print(calc_FFT(xs))

if __name__ == '__main__':
    main()


import numpy as np

def cov(x, y, xm, ym, n):
    p = 0.0
    for i in range(len(x)):
        p += (x[i]-xm)*(y[i]-ym)
    return p/(n-1)

X = np.array([4, 4.2, 3.9, 4.3, 4.1])
Y = np.array([2, 2.1, 2.0, 2.1, 2.2])
Z = np.array([0.6, 0.59, 0.58, 0.62, 0.63])

xm = np.average(X)
ym = np.average(Y)
zm = np.average(Z)
n = 5

xx = cov(X, X, xm, ym, n)
xy = cov(X, Y, xm, ym, n)
xz = cov(X, Z, xm, zm, n)
yx = cov(Y, X, ym, xm, n)
yy = cov(Y, Y, ym, ym, n)
yz = cov(Y, Z, ym, zm, n)
zx = cov(Z, X, zm, xm, n)
zy = cov(Z, Y, zm, ym, n)
zz = cov(Z, Z, zm, zm, n)

print("Mean {:.5f} {:.5f} {:.5f}".format(xm, ym, zm))
print("{:.5f} {:.5f} {:.5f}".format(xx, xy, xz))
print("{:.5f} {:.5f} {:.5f}".format(yx, yy, yz))
print("{:.5f} {:.5f} {:.5f}".format(zx, zy, zz))


import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
from pathlib import Path

def julia_set(c: complex, iterations: int, x_limit: float, y_limit: float, ppl: int = 1000, x_origin: float = 0., y_origin: float = 0.) -> np.ndarray:
    threshold = (1 + np.sqrt(1 + 4 * np.abs(c))) / 2
    n_x = int(ppl * x_limit)
    n_y = int(ppl * y_limit)
    y, x = np.ogrid[y_limit:-y_limit:n_y * 1j, -x_limit:x_limit:n_x * 1j]
    points = (x + x_origin) + 1j * (y + y_origin)
    iterations_to_diverge = np.ones(points.shape) * iterations
    is_diverged = np.zeros(points.shape, dtype=bool)

    for iteration in range(iterations):
        points = np.square(points) + c
        points_distance = np.abs(points)
        new_diverged = (points_distance > threshold)
        is_diverged[new_diverged] = True
        iterations_to_diverge[new_diverged] = iteration
        points[is_diverged] = 0

    return iterations_to_diverge

def show_julia_set(iterations_to_diverge: np.ndarray, file_name: str = None) -> None:
    fig = plt.figure(figsize=np.array(iterations_to_diverge.shape)[::-1] / 100)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(iterations_to_diverge, cmap='twilight_shifted')
    Path("images").mkdir(parents=True, exist_ok=True)
    if file_name:
        plt.savefig(file_name)
    # plt.show()
    plt.close()

def make_zoom_video():
    c = -0.7 + .3j
    # x_origin, y_origin = 2.087805e-9, (0.2499 + 3.53e-10)
    x_origin, y_origin = 2.0877695e-9, (0.2499 + 3.53e-10)
    length_limit = 1
    zoom_factor = 1.1
    ppl = 1000
    iterations = 200
    frames = 320

    dir_name = f'videos/julia_c={c}_itr={iterations}_l={length_limit}_zoom={zoom_factor}_temp_temp'
    Path(dir_name).mkdir(parents=True, exist_ok=True)
    for frame in tqdm(range(frames)):
        iterations_to_diverge = julia_set(
            c=c,
            iterations=int(iterations),
            x_limit=length_limit,
            y_limit=length_limit,
            ppl=ppl,
            x_origin=x_origin,
            y_origin=y_origin
        )
        show_julia_set(iterations_to_diverge, f"{dir_name}/{frame}.jpg")
        length_limit /= zoom_factor
        ppl *= zoom_factor
        iterations += 0.5

if __name__ == '__main__':
    make_zoom_video()
    # c = -0.7 + .3j
    # iterations = 1000
    # iterations_to_diverge = julia_set(
    #     c=c,
    #     iterations=iterations,
    #     x_limit=1.55,
    #     y_limit=0.95,
    #     ppl=1000,
    # )
    # file_name = f'images/julia_c={c}_itr={iterations}_w={iterations_to_diverge.shape[1]}_h={iterations_to_diverge.shape[0]}.jpg'
    # show_julia_set(iterations_to_diverge, file_name)


import matplotlib.pyplot as plt
from numpy import linspace

from probability.distributions import Beta
from mpl_format.axes.axis_utils import new_axes

# https://en.wikipedia.org/wiki/Beta_distribution

x = linspace(0, 1, 101)

def plot_wikipedia_pdfs():
    """
    https://en.wikipedia.org/wiki/Beta_distribution#/media/
    File:Beta_distribution_pdf.svg
    """
    ax = new_axes(width=10, height=10)
    Beta(0.5, 0.5).pdf().plot(x=x, color='red', mean=True, std=True, ax=ax)
    Beta(5, 1).pdf().plot(x=x, color='blue', mean=True, ax=ax)
    Beta(1, 3).pdf().plot(x=x, color='green', mean=True, ax=ax)
    Beta(2, 2).pdf().plot(x=x, color='purple', mean=True, ax=ax)
    Beta(2, 5).pdf().plot(x=x, color='orange', mean=True, ax=ax)
    ax.set_ylim(0, 2.5)
    ax.set_title('Probability density function')
    ax.legend(loc='upper center')
    plt.show()

def plot_wikipedia_cdfs():
    """
    https://en.wikipedia.org/wiki/Beta_distribution#/media/
    File:Beta_distribution_cdf.svg
    """
    ax = new_axes(width=10, height=10)
    Beta(0.5, 0.5).cdf().plot(x=x, color='red', ax=ax)
    Beta(5, 1).cdf().plot(x=x, color='blue', ax=ax)
    Beta(1, 3).cdf().plot(x=x, color='green', ax=ax)
    Beta(2, 2).cdf().plot(x=x, color='purple', ax=ax)
    Beta(2, 5).cdf().plot(x=x, color='orange', ax=ax)
    ax.set_title('Cumulative distribution function')
    ax.legend(loc='upper left')
    plt.show()

if __name__ == '__main__':

    plot_wikipedia_pdfs()
    plot_wikipedia_cdfs()


# coding: utf-8

# In[2]:

'''
Statistical Computing for Scientists and Engineers
Homework 4 Problem 3 b1
Fall 2018
University of Notre Dame
'''
import numpy as np
import matplotlib.pyplot as plt
import math
import scipy.stats

# the true distribution
def f(v):
    return scipy.stats.gamma.pdf(v,a=4.3,scale=1/6.2)
# the proposal distribution
def q(v):
    return scipy.stats.gamma.pdf(v,a=5,scale=1/6)

#Initialization
numSamples = 50000

samp= np.zeros(numSamples)
samp[0]=5
#Accept - Reject algorithm, Sample from laplacian
#def accept_reject():
#    samples = np.random.gamma(4,1/7,numSamples)
#    acceptanceProb = f(samples)/(M*q(samples))
#    unif_samp = np.random.rand(1,numSamples)
#    accepted = unif_samp < acceptanceProb
#    return samples, accepted, unif_samp

# all the samps, accepted, unif_samps
#samps,accepteds,unif_samps = accept_reject()

for i in range(1, numSamples):
    y = scipy.stats.gamma.rvs(5,0,scale=1/6);
    prob = min(1, q(samp[i-1])/q(y)*(f(y)/f(samp[i-1])));
    u = np.random.uniform()
    if ( u <= prob):
        samp[i] = y;
    else:
        samp[i] = samp[i-1];

#calculate the expectation
E = np.array([0.0]*numSamples)
Sum = 0
List = np.array([0]*numSamples)
for i in range(0,numSamples):
        Sum= Sum+samp[i];
        E[i]=Sum/(i+1);
        List[i]=i+1

#plot the expectation
plt.figure(figsize=(8,8))
plt.plot(List,E)
plt.ylabel("<E>")
plt.xlabel("Iteration")
plt.savefig('h4p3b21.png')
plt.show()

#calculate the convergence
cov = np.array([0.0]*numSamples)
for i in range(0,numSamples):
    cov[i]=np.mean(pow(E[0:i]-samp[0:i],2))

#plot the covergence
plt.figure(figsize=(8,8))
plt.plot(List,cov)
plt.ylabel("COV")
plt.xlabel("Iteration")
plt.savefig('h4p3b22.png')
plt.show()

x = np.linspace(0,10,100000)
# plot histogram & true distribution
plt.figure(figsize=(8,8))
plt.hist(samp,bins=100, alpha=0.4, label=u'sampled histogram', normed=True)
plt.plot(x, f(x), 'r', label=u'True distribution') # f(x) is the True distribution
plt.legend()
plt.xlim([0,8])
plt.savefig('h4p3b23.png')
plt.show()

# In[4]:

f=open("/Users/shijiale1995/ecovb2.txt","a+")
for i in range(0,numSamples):
    f.write(str(E[i]))
    f.write(" ")
    f.write(str(cov[i]))
    f.write("\n")
f.close()


import numpy as np

def sample_brownian_bridge(t0, x0, t1, x1, sigma, N):
    """Sample Brownian bridge between `(t0, x0)` and `(t1, x1)` with diffusivity :math:`\\sigma^2`.
        Uses a recursive scheme, partitioning the interval `(t0, t1)` into :math:`2^N` steps.
    """
    def samp(t0, x0, t1, x1, sigma, t):
        return np.random.normal(x0 + (t - t0)/(t1 - t0)*(x1 - x0), (t1 - t)*(t - t0)/(t1 - t0)*sigma**2 * np.eye(x0.shape[0]))
    if N == 0:
        return np.array([]), np.array([])
    x_mid = np.array(samp(t0, x0, t1, x1, sigma, (t0 + t1)/2))
    t_mid = np.array([0.5*(t0 + t1), ])
    if N == 1:
        return x_mid, t_mid
    x_left, t_left = sample_brownian_bridge(t0, x0, 0.5*(t0 + t1), x_mid, sigma, N//2)
    x_right, t_right = sample_brownian_bridge(0.5*(t0 + t1), x_mid, t1, x1, sigma, N//2)
    return np.concatenate([x_left, x_mid, x_right]), np.concatenate([t_left, t_mid, t_right])

def sample_schrodinger_bridge(t0, t1, gamma, mu_spt, nu_spt, sigma, N, M):
    """Sample Schrodinger bridge from `t0` to `t1`.

    :param t0: initial time
    :param t1: final time
    :param gamma: coupling to sample from
    :param mu_spt: support of initial marginal
    :param nu_spt: support of final marginal
    :param sigma: square root of diffusivity (diffusivity is :math:`\\sigma^2`)
    :param N: passed to `sample_brownian_bridge`
    :param M: number of paths to sample
    """
    gamma_norm = (gamma/gamma.sum()).flatten()
    samp = np.random.choice(gamma_norm.shape[0], size = M, p = gamma_norm)
    path_x_all = []
    path_t = None
    for k in range(len(samp)):
        idx_i = samp[k] // gamma.shape[1]
        idx_j = samp[k] % gamma.shape[1]
        path_x, path_t = sample_brownian_bridge(t0, mu_spt[idx_i, :].reshape(1, -1), t1, nu_spt[idx_j, :].reshape(1, -1), sigma = sigma, N = N)
        path_x_all += [path_x, ]
    return path_x_all, path_t, samp // gamma.shape[1], samp % gamma.shape[1]

def sample_coupling(gamma, N = 1, norm = True):
    """Sample from coupling

    :param gamma: coupling to sample from
    :param N: number of pairs to sample
    :param norm: if `True`, re-normalise `gamma`.
    """
    gamma_norm = gamma/gamma.sum() if norm else gamma
    gamma_norm = gamma_norm.flatten()
    samp = np.random.choice(gamma_norm.shape[0], size = N, p = gamma_norm)
    return samp // gamma.shape[1], samp % gamma.shape[1]

def sample_paths(gamma_all, N = 1, coord = False, x_all = None, get_gamma_fn = None, num_couplings = None):
    """Sample paths from sequence of couplings

    :param gamma_all: sequence of `T-1` couplings to sample from. If `None`, then `get_gamma_fn` is used.
    :param N: number of paths to sample
    :param coord: if `True`, return samples in coordinates (rather than indices)
    :param x_all: supports of `T` marginals corresponding to the `T-1` provided couplings
    :param get_gamma_fn: `get_gamma_fn(i)` should return the `i`th coupling (of `T-1` couplings)
    :param num_couplings: if `gamma_all == None`, need to specify number of total couplings (`T-1`)
    """
    def path_idx_to_coord(paths, x_all):
        return np.array([[x_all[j][paths[i, j]] for j in range(paths.shape[1])] for i in range(paths.shape[0])])
    def sample_conditional(idx, gamma):
        return np.array([np.random.choice(gamma.shape[1], size = 1, p = (gamma[i, :]/gamma[i, :].sum()).flatten()) for i in idx]).flatten()
    idx_all = []
    if gamma_all is not None:
        num_couplings = len(gamma_all)
    gamma = gamma_all[0] if gamma_all is not None else get_gamma_fn(0)
    idx0, idx1 = sample_coupling(gamma, N = N)
    idx_all += [idx0, idx1]
    for i in range(1, num_couplings):
        gamma = gamma_all[i] if gamma_all is not None else get_gamma_fn(i)
        idx_all += [sample_conditional(idx_all[-1], gamma), ]
    paths = np.array(idx_all).T
    if coord:
        return path_idx_to_coord(paths, x_all)
    else:
        return paths


# Code for solving the 1+1 dimensional diffusion equation
# du/dt = ddu/ddx on a rectangular grid of size L x (T*dt),
# with with L = 1, u(x,0) = g(x), u(0,t) = u(L,t) = 0

import numpy, sys, math
import matplotlib.animation as animation
import time, glob, os
from  matplotlib import pyplot as plt
import numpy as np

def forward_step(alpha,u,uPrev,N):
    """
    Steps forward-euler algo one step ahead.
    Implemented in a separate function for code-reuse from crank_nicolson()
    """

    for x in xrange(1,N+1): #loop from i=1 to i=N
        u[x] = alpha*uPrev[x-1] + (1.0-2*alpha)*uPrev[x] + alpha*uPrev[x+1]

def forward_euler(alpha,u,N,T):
    """
    Implements the forward Euler sheme, results saved to
    array u
    """

    #Skip boundary elements
    for t in xrange(1,T):
        forward_step(alpha,u[t],u[t-1],N)

def tridiag(alpha,u,N):
    """
    Tridiagonal gaus-eliminator, specialized to diagonal = 1+2*alpha,
    super- and sub- diagonal = - alpha
    """
    d = numpy.zeros(N) + (1+2*alpha)
    b = numpy.zeros(N-1) - alpha

    #Forward eliminate
    for i in xrange(1,N):
        #Normalize row i (i in u convention):
        b[i-1] /= d[i-1];
        u[i] /= d[i-1] #Note: row i in u = row i-1 in the matrix
        d[i-1] = 1.0
        #Eliminate
        u[i+1] += u[i]*alpha
        d[i] += b[i-1]*alpha
    #Normalize bottom row
    u[N] /= d[N-1]
    d[N-1] = 1.0

    #Backward substitute
    for i in xrange(N,1,-1): #loop from i=N to i=2
        u[i-1] -= u[i]*b[i-2]
        #b[i-2] = 0.0 #This is never read, why bother...

def backward_euler(alpha,u,N,T):
    """
    Implements backward euler scheme by gaus-elimination of tridiagonal matrix.
    Results are saved to u.
    """
    for t in xrange(1,T):
        u[t] = u[t-1].copy()
        tridiag(alpha,u[t],N) #Note: Passing a pointer to row t, which is modified in-place

def crank_nicolson(alpha,u,N,T):
    """
    Implents crank-nicolson scheme, reusing code from forward- and backward euler
    """
    for t in xrange(1,T):
        forward_step(alpha/2,u[t],u[t-1],N)
        tridiag(alpha/2,u[t],N)

def g(x):
    """Initial condition u(x,0) = g(x), x \in [0,1]"""
    return numpy.sin(math.pi*x)

# Number of integration points along x-axis
N       =   100
# Step length in time
dt      =   0.01
# Number of time steps till final time
T       =   100
# Define method to use 1 = explicit scheme, 2= implicit scheme, 3 = Crank-Nicolson
method  =   2

dx = 1/float(N+1)
alpha = dt/(dx**2)
x = np.linspace(0, 1, N+2)
t  = np.linspace(0, 1, T)
u = numpy.zeros((T,N+2),numpy.double)
#Initial codition
u[0,:] = g(x)
u[0,0] = u[0,N+1] = 0.0 #Implement boundaries rigidly
if   method == 1:
     forward_euler(alpha,u,N,T)
elif method == 2:
    backward_euler(alpha,u,N,T)
elif method == 3:
    crank_nicolson(alpha,u,N,T)
else:
    print "Please select method 1,2, or 3!"
    import sys
    sys.exit(0)

# Make a movie of the results
# Make a first plot (save the lines objects returned from plt.plot)
fig = plt.figure()
plt.axis([0, 1, 0,  10])
lines = plt.plot([], [])
plt.xlabel('x')
plt.ylabel('u')

# Function to return the background plot in the animation
def init():
    lines[0].set_data([], [])  # empty plot
    return lines

# Function to return a frame in the movie
def frame(args):
    frame_no, s, x, lines = args

    lines[0].set_data(x, u)
    # Does not work: lines[0].set_label('s=%4.2f' % s)
    # Does not work: plt.legend(['s=%4.2f' % s])
    # Does not work: plt.savefig('tmp_%04d.png' % frame_no)
    return lines

# Construct list of all arguments to frame function
# (each call sends frame number, s value, x array, and lines list)
all_args = [(frame_no, s, x, lines)
            for frame_no, s in enumerate(t)]

# Run the animation
anim = animation.FuncAnimation(
    fig, frame, all_args, interval=150, init_func=init, blit=True)

# Make movie file in MP4 format
anim.save('movie1.mp4', fps=5)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Tue Apr  7 00:36:01 2020

@author: Mrityunjay1.Pandey
"""

class complex_numbers:
    def __init__(self, real, imag):
        self.real = real
        self.imag = imag

    def __repr__(self):
        if self.real == 0.0 and self.imag == 0.0:
            return "0.00"
        if self.real == 0:
            return "%.2fi" % self.imag
        if self.imag == 0:
            return "%.2f" % self.real
        return "%.2f %s %.2fi" % (self.real, "+" if self.imag >= 0 else "-", abs(self.imag))

    def __add__(self, other):
         r=self.real + other.real
         i=self.imag + other.imag
         return complex_numbers(r,i)

    def __sub__(self, other):
         r=self.real - other.real
         i=self.imag - other.imag
         return complex_numbers(r,i)

    def __mul__(self,other):
        if self.imag!=0:
            r=(self.real*other.real)-(self.imag*other.imag)
            i=(self.imag*other.real)+(self.real*other.imag)
        else:
            r=(self.real*other.real)
            i=(self.imag*other.real)
        return complex_numbers(r,i)

    def __truediv__(self,other):
        r=((self.real*other.real)+(self.imag*other.imag))/((other.real**2)+(other.imag**2))
        i=((self.imag*other.real)-(self.real*other.imag))/((other.real**2)+(other.imag**2))
        return complex_numbers(r,i)

    def absolute(self):
        absolute=(self.real**2+self.imag**2)**(1/2)
        return absolute

    def argument(self):
        import numpy as np
        import math
        arg=math.degrees(np.arctan2(self.imag,self.real))
        return arg

    def conjugate(self):
        conj_r=self.real
        conj_i=self.imag*-1
        return complex_numbers(conj_r,conj_i)

comp_1=complex_numbers(3,5)
comp_2=complex_numbers(4,4)

comp_sum=complex_numbers.__add__(comp_1,comp_2)
comp_diff=complex_numbers.__sub__(comp_1,comp_2)
comp_prod=complex_numbers.__mul__(comp_1,comp_2)
comp_quot=complex_numbers.__truediv__(comp_1,comp_2)
comp_abs=complex_numbers.absolute(comp_1)
comp_conj=complex_numbers.conjugate(comp_1)
comp_arg=complex_numbers.argument(comp_1)
print("Complex numbers provided were {} & {}.\n \n==Below are the results of operation==\nAdditon is::{}\nDifference is::{}\nMultiplication is::{}\nDivison is::{}\nAbsolute Value is::{}\nConjugate Value is::{}\nArgument Value is::{}".format(comp_1,comp_2,comp_sum,comp_diff,comp_prod,comp_quot,comp_abs,comp_conj,comp_arg))


# 선형 회귀
## k-최근접 이웃의 한계
import numpy as np

perch_length = np.array(
    [8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0,
     21.0, 21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5,
     22.5, 22.7, 23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5,
     27.3, 27.5, 27.5, 27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0,
     36.5, 36.0, 37.0, 37.0, 39.0, 39.0, 39.0, 40.0, 40.0, 40.0,
     40.0, 42.0, 43.0, 43.0, 43.5, 44.0]
     )
perch_weight = np.array(
    [5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0,
     110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0,
     130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0,
     197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0,
     514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0,
     820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0,
     1000.0, 1000.0]
     )

from sklearn.model_selection import train_test_split

# 훈련 세트와 테스트 세트로 나눕니다
train_input, test_input, train_target, test_target = train_test_split(
    perch_length, perch_weight, random_state=42)
# 훈련 세트와 테스트 세트를 2차원 배열로 바꿉니다
train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)

from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor(n_neighbors=3)
# k-최근접 이웃 회귀 모델을 훈련합니다
knr.fit(train_input, train_target)

print(knr.predict([[50]]))
# [1033.33333333]

import matplotlib.pyplot as plt
# 50cm 농어의 이웃을 구합니다
distances, indexes = knr.kneighbors([[50]])

# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 훈련 세트 중에서 이웃 샘플만 다시 그립니다
plt.scatter(train_input[indexes], train_target[indexes], marker='D')
# 50cm 농어 데이터
plt.scatter(50, 1033, marker='^')
plt.show()

print(np.mean(train_target[indexes]))
# 1033.3333333333333

print(knr.predict([[100]]))
# [1033.33333333]

# 100cm 농어의 이웃을 구합니다
distances, indexes = knr.kneighbors([[100]])

# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 훈련 세트 중에서 이웃 샘플만 다시 그립니다
plt.scatter(train_input[indexes], train_target[indexes], marker='D')
# 100cm 농어 데이터
plt.scatter(100, 1033, marker='^')
plt.show()

## 선형 회귀
from sklearn.linear_model import LinearRegression

lr = LinearRegression()
# 선형 회귀 모델 훈련
lr.fit(train_input, train_target)

# 50cm 농어에 대한 예측
print(lr.predict([[50]]))
# [1241.83860323]

print(lr.coef_, lr.intercept_)
# [39.01714496] -709.0186449535477

# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 15에서 50까지 1차 방정식 그래프를 그립니다
plt.plot([15, 50], [15*lr.coef_+lr.intercept_, 50*lr.coef_+lr.intercept_])
# 50cm 농어 데이터
plt.scatter(50, 1241.8, marker='^')
plt.show()

print(lr.score(train_input, train_target))
print(lr.score(test_input, test_target))
# 0.9398463339976041
# 0.8247503123313559

## 다항 회귀
train_poly = np.column_stack((train_input ** 2, train_input))
test_poly = np.column_stack((test_input ** 2, test_input))

print(train_poly.shape, test_poly.shape)
# (42, 2) (14, 2)

lr = LinearRegression()
lr.fit(train_poly, train_target)

print(lr.predict([[50**2, 50]]))
# [1573.98423528]

print(lr.coef_, lr.intercept_)
# [  1.01433211 -21.55792498] 116.05021078278259

# 구간별 직선을 그리기 위해 15에서 49까지 정수 배열을 만듭니다
point = np.arange(15, 50)
# 훈련 세트의 산점도를 그립니다
plt.scatter(train_input, train_target)
# 15에서 49까지 2차 방정식 그래프를 그립니다
plt.plot(point, 1.01*point**2 - 21.6*point + 116.05)
# 50cm 농어 데이터
plt.scatter([50], [1574], marker='^')
plt.show()

print(lr.score(train_poly, train_target))
print(lr.score(test_poly, test_target))
# 0.9706807451768623
# 0.9775935108325121