"""
Example:
Let's concider the problem
15x1 + 8x2 + 80x3 -> min        (1)
subjected to
x1 + 2x2 + 3x3 <= 15              (2)
8x1 +  15x2 +  80x3 <= 80      (3)
8x1  + 80x2 + 15x3 <=150      (4)
100x1 +  10x2 + x3 >= 800     (5)
80x1 + 8x2 + 15x3 = 750         (6)
x1 + 10x2 + 100x3 = 80           (7)
x1 >= 4                                     (8)
-8 >= x2 >= -80                        (9)
"""

from numpy import *
from openopt import LP
f = array([15,8,80])
A = mat('1 2 3; 8 15 80; 8 80 15; -100 -10 -1') # numpy.ndarray is also allowed
b = [15, 80, 150, -800] # numpy.ndarray, matrix etc are also allowed
Aeq = mat('80 8 15; 1 10 100') # numpy.ndarray is also allowed
beq = (750, 80)

lb = [4, -80, -inf]
ub = [inf, -8, inf]
p = LP(f, A=A, Aeq=Aeq, b=b, beq=beq, lb=lb, ub=ub)
#or p = LP(f=f, A=A, Aeq=Aeq, b=b, beq=beq, lb=lb, ub=ub)

#r = p.minimize('glpk') # CVXOPT must be installed
#r = p.minimize('lpSolve') # lpsolve must be installed
r = p.minimize('pclp')
#search for max: r = p.maximize('glpk') # CVXOPT & glpk must be installed
#r = p.minimize('nlp:ralg', ftol=1e-7, xtol=1e-7, goal='min', plot=1)

print('objFunValue: %f' % r.ff) # should print 204.48841578
print('x_opt: %s' % r.xf) # should print [ 9.89355041 -8.          1.5010645 ]


from __future__ import division;
import numpy as np;
from matplotlib import pyplot as plt;
from matplotlib import colors
import matplotlib as mpl;
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import mlab;
from matplotlib import gridspec;
import pandas as pd
from IPython.display import display
import sklearn;
from sklearn.kernel_ridge import KernelRidge;

from notebook.services.config import ConfigManager
cm = ConfigManager()
cm.update('livereveal', {
              'theme': 'simple',
              'transition': 'none',
              'start_slideshow_at': 'selected',
});

def regression_example_draw(degree1, degree2, degree3, ifprint):
    x = np.linspace(0, 2*np.pi, 13);
    # np.random.randn generates gaussian samples
    y = np.sin(x) + np.random.randn(x.shape[0]) * 0.2;
    xx = np.linspace(0, 2*np.pi, 100);
    plt.figure(figsize=(12,7.5))
    plt.subplot(221)
    plt.plot(xx, np.sin(xx), "g", linestyle='--'); plt.hold(True)
    plt.plot(x, y, "or"); plt.hold(False)
    plt.legend(['True Curve','Data']); plt.title('Data and True Curve');

    # Here were are going to take advantage of numpy's 'polyfit' function
    # This implements a "polynomial fitting" algorithm
    # coeffs are the optimal coefficients of the polynomial
    coeffs = np.polyfit(x, y, degree1); # 0 is the degree of the poly
    # We construct poly(), the polynomial with "learned" coefficients
    poly = np.poly1d(coeffs);
    plt.subplot(222)
    plt.plot(xx, np.sin(xx), "g", linestyle='--'); plt.hold(True)
    plt.plot(x, y, "or");
    plt.plot(xx, poly(xx), color='b', linestyle='-'); plt.hold(False)
    plt.legend(['True Curve','Data','Learned Curve']); plt.title(str(degree1)+'th Order Polynomial')
    exprsn1=''
    for i in range(degree1+1):
        if i>0 and coeffs[i]>0:
            exprsn1 += '+%.3fx^%d' %(coeffs[i], i)
        elif i==0:
            exprsn1 += '%.3f' %(coeffs[i])
        else:
            exprsn1 += '%.3fx^%d' %(coeffs[i], i)

    coeffs = np.polyfit(x, y, degree2); # Now let's try degree = 1
    poly = np.poly1d(coeffs);
    plt.subplot(223)
    plt.plot(xx, np.sin(xx), "g", linestyle='--'); plt.hold(True)
    plt.plot(x, y, "or");
    plt.plot(xx, poly(xx), color='b', linestyle='-'); plt.hold(False)
    plt.legend(['True Curve','Data','Learned Curve']); plt.title(str(degree2)+'th Order Polynomial')
    exprsn2 = ''
    for i in range(degree2+1):
        if i>0 and coeffs[i]>0:
            exprsn2 += '+%.3fx^%d' %(coeffs[i], i)
        elif i==0:
            exprsn2 += '%.3f' %(coeffs[i])
        else:
            exprsn2 += '%.3fx^%d' %(coeffs[i], i)

    coeffs = np.polyfit(x, y, degree3); # Now degree = 3
    poly = np.poly1d(coeffs);
    plt.subplot(224)
    plt.plot(xx, np.sin(xx), "g", linestyle='--'); plt.hold(True)
    plt.plot(x, y, "or");
    plt.plot(xx, poly(xx), color='b', linestyle='-'); plt.hold(False)
    plt.legend(['True Curve','Data','Learned Curve']); plt.title(str(degree3)+'th Order Polynomial')
    plt.show()
    exprsn3 = ''
    for i in range(degree3+1):
        if i>0 and coeffs[i]>0:
            exprsn3 += '+%.3fx^%d' %(coeffs[i], i)
        elif i==0:
            exprsn3 += '%.3f' %(coeffs[i])
        else:
            exprsn3 += '%.3fx^%d' %(coeffs[i], i)

    if ifprint:
        print('The expression for the first polynomial is y=' + exprsn1)
        print('The expression for the second polynomial is y=' + exprsn2)
        print('The expression for the third polynomial is y=' + exprsn3)

def set_nice_plot_labels(axs):
    axs[0].set_title(r"$ \phi_j(x) = x^j$", fontsize=18, y=1.08);
    axs[0].set_xlabel("Polynomial", fontsize=18);
    axs[1].set_title(r"$ \phi_j(x) = \exp\left( - \frac{(x-\mu_j)^2}{2s^2} \right)$", fontsize=18, y=1.08);
    axs[1].set_xlabel("Gaussian", fontsize=18);
    axs[2].set_title(r"$ \phi_j(x) = (1  + \exp\left(\frac{\mu_j-x}{s}\right))^{-1}$", fontsize=18, y=1.08);
    axs[2].set_xlabel("Sigmoid", fontsize=18);

def basis_function_plot():
    x = np.linspace(-1,1,100);
    f, axs = plt.subplots(1, 3, sharex=True, figsize=(12,4));
    for j in range(8):
        axs[0].plot(x, np.power(x,j));
        axs[0].hold(True)
        axs[1].plot(x, np.exp( - (x - j/7 + 0.5)**2 / 2*5**2 ));
        axs[1].hold(True)
        axs[2].plot(x, 1 / (1 + np.exp( - (x - j/5 + 0.5) * 5)) );
        axs[2].hold(True)
    axs[0].hold(False)
    axs[1].hold(False)
    axs[2].hold(False)

    set_nice_plot_labels(axs) # I'm hiding some helper code that adds labels


import numpy as np

class DataCreator(object):
    '''
    Creates random datapoints (x,y) of the underlying function
        y = f + N(0,noise^2)
    with
        f = sin(5/2*x)*son(3/2*x)
    and
        noise = 0.15 + 0.25*(1-sin(5/2*x))^2

    '''
    def __init__(self,num_samples, x_min=0, x_max=2*np.pi,random_seed=42):
        np.random.seed(random_seed)
        self.num_samples = num_samples
        self.x_min = x_min
        self.x_max = x_max

    '''
    call to sample new data points
    '''
    def create_datapoints(self):
        self.x = np.linspace(self.x_min,self.x_max,num=self.num_samples)
        self.f = np.sin(5*self.x/2)*np.sin(3*self.x/2)
        self.noise = 0.15 + 0.25*(1-np.sin(5*self.x/2))**2
        self.y = self.f+np.random.randn(self.num_samples)*self.noise

        self.f = self.f.reshape(self.num_samples,1)
        self.noise = self.noise.reshape(self.num_samples,1)
        self.x = self.x.reshape(self.num_samples,1)
        self.y = self.y.reshape(self.num_samples,1)

    def get_data(self):
        return self.x, self.y, self.f, self.noise

    '''
    get normalized samples
    '''
    def get_normalized_data(self):
        mux = np.mean(self.x)
        sigx = np.std(self.x)
        muy = np.mean(self.y)
        sigy = np.std(self.y)

        return (self.x-mux)/sigx, (self.y-muy)/sigy, mux, sigx, muy, sigy


from math import pi, sqrt
import math
import numpy as np
import pandas as pd
np.set_printoptions(suppress=True)

def EMalgorithm(x, K, conv):
    # Function takes in the dataset, number of clusters and thee precision you want to converge with 10^-conv
    D = len(x[0]) # the Dimensions/# of features
    N = len(x)  # number of observations
    # Initializing Parameters
    x_col = np.matrix(np.mean(x, axis=0)).T # mean of column/feature
    sd_col = np.matrix(np.std(x, axis=0, ddof=1)).T # std dev of column/feature
    randn = np.random.randn(1,K) # 1xK random array
    # compute Initial Values
    mu_k = np.array(x_col*np.ones(K) + np.matmul(sd_col,randn))
    sd_k = np.mean(sd_col)*np.ones(K)
    p_k = [1/K]*K

    p_kn_1 = 0 #previous place holder for convergence
    condition = True
    i = 0
    while condition:
        print()
        print("iteration: ", i+1)
        p_kn = E_Step(x,K, D, mu_k,sd_k, p_k) # run the E_step Function
        print("p_kn: ",p_kn)
        condition = np.any(np.abs((p_kn-p_kn_1))>(10**-conv)) #update the loop condition
        p_kn_1 = p_kn # update the prev matrix
        mu_k, sd_k, p_k = M_Step(x,p_kn, K,D,N) # run the M_step Function
        print("mu_k: ",mu_k)
        print("sd_k: ",sd_k)
        print("p_k: ", p_k)
        i+=1

    return

def E_Step(x, K, D, mu_k, sd_k, p_k):
    # Function takes dataset, # of clusters, Dimensions, mean of cluster, std dev of cluster, p_k

    g = [None]*K
    k = 0 # cluster counter
    while k < K:
        g[k] = (x-mu_k[:,k])
        g[k] = np.linalg.norm(g[k],axis=1)**2
        g[k] = -1/2* (g[k]/(sd_k[k]**2))
        g[k] = np.exp(g[k])
        g[k] = 1/(((np.sqrt(2*math.pi))*sd_k[k]))**D * g[k]
        k+=1
    g = np.asarray(g) #format to array

    k=0
    p_kn =[None]*K
    while k<K:
        p_kn[k] = p_k[k]*g[k]
        k+=1
    p_kn = np.asarray(p_kn)

    p_kn = p_kn / np.sum(p_kn, axis=0)

    return p_kn

def M_Step(x,p_kn, K, D, N):
    # Function takes dataset, probability of observation being in a cluster matrix, # clusters, Dimensions, # of observations

    k = 0
    mu_i = [None]*K
    while k<K:
        mu_i[k] = np.matmul(p_kn[k],x)/np.sum(p_kn[k])
        k+=1
    mu_i= np.asarray(mu_i).T

    k = 0
    sd_i = [None]*K
    while k<K:
        norm = (x-mu_i[:,k])
        norm = np.linalg.norm(norm,axis=1)**2
        norm = np.sum(norm*p_kn[k])
        sd_i[k] = np.sqrt(1/D * norm/sum(p_kn[k]))
        k+=1

    p_i = 1/N * np.sum(p_kn, axis=1)

    return mu_i, sd_i, p_i

if __name__ == "__main__":
    Iris = pd.read_csv("iris.csv",header=0)
    IrisMatrix = Iris.to_numpy()
    IrisMatrix = IrisMatrix[:,0:4]
    IrisMatrix = IrisMatrix.astype(float)
    # print(IrisMatrix)

    x = np.array([[1,2],[4,2],[1,3],[4,3]])
    # run the Expectation maximization algorithm EMalgorithm(Data, # of clusters, 10^-precision value)
    # EMalgorithm(x,2,6)
    EMalgorithm(IrisMatrix,3,6)

Algorithms for Data Science/Homework 3/Homework3_Q1_Q2.py


import numpy as np
from matplotlib import pyplot as plt

# return the matrix A with the correct coefficients
def generate_coef(n):
	mat = np.zeros((n ** 2, n ** 2));
	for i in range(n):
		for j in range(n):
			ni = i * n + j
			# dx
			if i < n - 1:
				mat.itemset(ni, (i + 1) * n + j, 1)
			if i > 0:
				mat.itemset(ni, (i - 1) * n + j, 1)

			# dy
			if j < n - 1:
				mat.itemset(ni, i * n + j + 1, 1)
			if j > 0:
				mat.itemset(ni, i * n + j - 1, 1)

			mat.itemset(ni, ni, -4);
	return -((n + 1) ** 2) * mat

# create a n * n matrix from a value function
def func_to_matrix(f, n):
	return np.fromfunction(lambda i, j: f(i/(n - 1), j/(n - 1)), (n, n))

# create a vector of size n² from a matri of size n * n
def matrix_to_vector(m):
	return m.flatten().T

# create a matrix of size n * n from a vector of size n²
def vector_to_matrix(v):
	n = int(np.sqrt(v.size))
	return v.reshape((n, n))

# show the imge corresponding to the given function
def show_image(f, solver, N, title):
	A = generate_coef(N)

	b = matrix_to_vector(func_to_matrix(f, N))
	print("b = ")
	print(b)

	x = vector_to_matrix(solver(A, b))
	print("x = ")
	print(x)

	plt.imshow(x, cmap='hot', extent=(0, 1, 0, 1), interpolation='bilinear')
	plt.title(title)
	plt.show()

# the function f defining the initial heat of a point radiator
def point_radiator(i, j):
	return np.where((i - 0.5) ** 2 + (j - 0.5) ** 2 < 0.01, 1, 0)

# the function f defining the initial heat of a point radiator
def wall_radiator(i, j):
	return np.where(i < 0.0001, 1, 0)

def test_equation():
        N = 32
        show_image(point_radiator, np.linalg.solve, N, "Radiateur Point")
        show_image(wall_radiator, np.linalg.solve, N, "Radiateur Mur")

if __name__ == '__main__':
        test_equation()


import numpy as np
import matplotlib.pyplot as plt

def SD(A,b,x0):
    """
    Parameters:
    A - Input Matrix
    b - vector
    x0 - Initial solution
    """
    x = x0
    count = 0
    r = np.ones_like(b)
    while(np.linalg.norm(r)>1e-6):
        r = b - A*x
        alpha = np.transpose(r)*r/(np.transpose(r)*A*r)
        print(alpha)
        x_next = x + alpha*r
        x = x_next
        count +=1
    return x,count

def CG(A,b,x0):
    """
    Parameters:
    A - Input matrix
    b - Vector
    x0 - Initial Solution
    """
    assert np.all(A == np.transpose(A)) ,print('error conjugate gradient method cannot be used since Matrix is not Positive Semi Definite')
    x = x0
    count = 0
    r = np.ones_like(b)
    d = np.ones_like(b)
    while(np.linalg.norm(r)>1e-6):
        r = b - A*x
        alpha = np.transpose(r)*r/(np.transpose(d)*A*d)
        x = x + alpha*d
        r_next = r - alpha*A*d
        beta = np.transpose(r_next)*r_next/(np.transpose(r)*r)
        d = r_next + beta*d
        r = r_next
        count+=1
    return x,count

A = np.array([[3,2],[2,6]])
b = np.array([2,-8])
x0 = np.array([-3,-3])

value = SD(A,b,x0)
cg = CG(A,b,x0)
print(value)
print(cg)


import matplotlib.pyplot as plt
import numpy as np

def power_negative_3_solution(t: float, x0: float):
    """
    This function implements the solution to the differential equation

    x' = -x^3

    with initial condition x(t=0) = x0.
    """
    return np.sqrt(1.0 / (2.0 * t + 1.0 / x0**2))

def power_negative_1_solution(t: float, x0: float):
    """
    This function implements the solution to the differential equation

    x' = -x

    with initial condition x(t=0) = x0.
    """
    return x0 * np.exp(-t)

if __name__ == "__main__":
    # Get the time points for the x-axis
    ts = np.linspace(0.0, 10.0, 1000)

    # Get the solution of the differential equation x'=-x^3 evaluated at these points
    x_pm3 = power_negative_3_solution(ts, x0=10.)

    # Get the solution to the differential equation x'=-x evaluated at these points
    x_pm1 = power_negative_1_solution(ts, x0=10.)

    # Plot the solutions for comparison
    plt.plot(ts, x_pm3, label=r'$\dot{x}=-x^3$')
    plt.plot(ts, x_pm1, label=r'$\dot{x}=-x$')

    # Cosmetics
    plt.title(r"Comparisons of the solutions to the DE's $\dot{x}=-x^3$ and $\dot{x}=-x$")
    plt.xlabel("t (a.u.)")
    plt.ylabel("x (a.u.)")
    plt.legend()
    plt.show()


# -*- coding: utf-8 -*-

from __future__ import absolute_import

import numpy as np

__all__ = [
    'log_likelihood',
    'logistic_sigmoid'
]

def log_likelihood(X, y, w):
    """Compute the log-likelihood function.

    Computes the log-likelihood function over the training data.
    The key to the log-likelihood is that the log of the product of
    likelihoods becomes the sum of logs. That is (in pseudo-code),

        np.log(np.product([f(i) for i in range(N)]))

    is equivalent to:

        np.sum([np.log(f(i)) for i in range(N)])

    The log-likelihood function is used in computing the gradient for
    our loss function since the derivative of the sum (of logs) is equivalent
    to the sum of derivatives, which simplifies all of our math.

    Parameters
    ----------
    X : np.ndarray, shape=(n_samples, n_features)
        The training data.

    y : np.ndarray, shape=(n_samples,)
        The target vector of 1s or 0s.

    w : np.ndarray, shape=(n_features,)
        The vector of feature weights (coefficients)

    References
    ----------
    .. [1] For a very thorough explanation of the log-likelihood function, see
           https://www.coursera.org/learn/ml-classification/lecture/1ZeTC/very-optional-expressing-the-log-likelihood
    """
    weighted = X.dot(w)
    return (y * weighted - np.log(1. + np.exp(weighted))).sum()

def logistic_sigmoid(x):
    """The logistic function.

    Compute the logistic (sigmoid) function over a vector, ``x``.

    Parameters
    ----------
    x : np.ndarray, shape=(n_samples,)
        A vector to transform.
    """
    return 1. / (1. + np.exp(-x))


from statistics import mean
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

xs = [1,2,3,4,5,6]
ys = [5,4,6,5,6,7]

# plt.plot(xs,ys)
# plt.show()

# Since above are not np array they are just a python array, to change them which will give us
# more powerful way to iterate over then values in the array,

xs = np.array(xs, dtype = np.float64)  # you can also specify the data-type into your array
ys = np.array(ys, dtype = np.float64)

# first we need a function to calculate the slop as

def best_fit_slop(xs,ys):

    m = ( ((mean(xs)*mean(ys)) - mean(xs*ys)) /
          (mean(xs)**2-mean(xs**2)))

# remember the PEMDAS the order of the operations in computing
    return m

m = best_fit_slop(xs,ys)
print(m)

Machine_Learning_Old_Files/[8] linear regression P.1.py


"""
The Monty Hall Problem [http://en.wikipedia.org/wiki/Monty_Hall_problem] is a probability brain teaser that has a
rather unintuitive solution.

The gist of it, taken from Wikipedia:
Suppose you're on a game show, and you're given the choice of three doors: Behind one door is a car; behind the others,
goats. You pick a door, say No. 1 [but the door is not opened], and the host, who knows what's behind the doors, opens
another door, say No. 3, which has a goat. He then says to you, "Do you want to pick door No. 2?" Is it to your
advantage to switch your choice? (clarification: the host will always reveal a goat)

Your task is to write a function that will compare the strategies of switching and not switching over many random
position iterations. Your program should output the proportion of successful choices by each strategy. Assume that if
both unpicked doors contain goats the host will open one of those doors at random with equal probability.

If you want to, you can for simplicity's sake assume that the player picks the first door every time. The only aspect
of this scenario that needs to vary is what is behind each door.

Thanks to SleepyTurtle for posting this idea at /r/dailyprogrammer_ideas! Do you have a problem you think would be good
for us! Head on over there and post it!
"""

import numpy as np
import numpy.random as nprnd
import time

TRIALS = 1000000
doors = [1, 0, 0]

start_time = time.time()
keep = 0
switch = 0
for _ in range(TRIALS):
    """ permutes doors to create random combination of doors and then runs the keep and switch scenarios simultaneously.
    in the case of initially selecting the correct door, the choice of removing one of the other doors does not matter,
    so it just chooses the first one.
    """
    perm = nprnd.permutation(doors)
    keep += perm[0]

    zero = np.where(perm[1:] == 0)[0]
    perm = np.delete(perm, zero[0]+1)
    switch += perm[1]

print('Kept:         {}%'.format(100*keep/TRIALS))
print('Switched:     {}%'.format(100*switch/TRIALS))
print('Elapsed time: {}'.format(time.time()-start_time))

start_time = time.time()
keep = 0
switch = 0
for _ in range(TRIALS):
    """ optimization so that it doesn't actually simulate the monty hall problem. instead it is based on the realization
    that if the permutation's first door is correct, the keep scenario always wins and if the first is incorrect the
    switch scenario always wins. Time reduced to less than half of the above method.
    """
    perm = nprnd.permutation(doors)
    if perm[0]:
        keep += 1
    else:
        switch += 1

print('Kept:     {}%'.format(100*keep/TRIALS))
print('Switched: {}%'.format(100*switch/TRIALS))
print('Elapsed time: {}'.format(time.time()-start_time))


# -*- coding: utf-8 -*-
"""
Make a simple 2D Gaussian profile.  One example is in the representation of an
ideal laser beam.

"""

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import gaussian1D_profile as gp

def gaussian_2D_profile(x_min, x_max, x_step, y_min, y_max, y_step,
                        xc, yc, sigma_x, sigma_y, amplitude):
    """Function to produce a 2D Gaussian profile.

    Parameters
    ----------

    x_min, x_max, x_step: float, float, float
        Creates a sequence (1D ndarray) of x points over which to compute the Gaussian
    y_min, y_max, y_step: float, float, float
        Creates a sequence (1D ndarray) of y points over which to compute the Gaussian
    xc,yc: float, float
        The center points in x and y of the gaussian profile
    sigma_x, sigma_y: float, float
        1/e-squared width of beam in the x and y axis respectively
    amplitude: float
        Amplitude at peak value

    Returns
    -------

    Z: ndarray
        The 2D gaussian profile amplitude values

    """

    idx = np.arange(x_min, x_max, x_step)
    idy = np.arange(y_min, y_max, y_step)

    d0x = 2*sigma_x  #beam diameter (1/e-squared) is 2 * standard deviation
    d0y = 2*sigma_y

    x = amplitude * np.e**(-2*np.power((idx-xc)/d0x,2))
    y = amplitude * np.e**(-2*np.power((idy-yc)/d0y,2))

    plt.plot(idx, x, label='x')
    plt.plot(idy, y, label='y')
    plt.title('Gaussian 1D Profiles in x and y')
    plt.legend()
    plt.show()

    #now 2d...can just multiply the two 1D curves together
    X,Y = np.meshgrid(idx,idy)

    #Z = np.e**(-2*(((X-xc)/d0x)**2 * ((Y-yc)/d0y)**2))  #interesting star type pattern

    # The following methods are equivalent e^x * e^y = e^(x+y)
    #Z = np.e**(-2*np.power((X-xc)/d0x,2))*np.e**(-2*np.power((Y-yc)/d0y,2)) #1.92ms per loop
    #Z = np.e**(-2*((X-xc)/d0x)**2) * np.e**(-2*((Y-yc)/d0y)**2) # 1.03ms per loop
    Z = np.e**(-2*(((X-xc)/d0x)**2 + ((Y-yc)/d0y)**2))          # 586us per loop (4x faster)

    return Z

def plot_2d_gaussian(Z):
    """Plot the gaussian profile as a 2D image and contour lines.

    Parameters
    ----------

    Z: ndarray
        2D Gaussian values

    """

    plt.figure()
    #plt.axis('off')
    plt.imshow(Z,extent=(-50,50,-50,50))
    plt.contour(Z)
    plt.title('2D Gaussian Profile')
    plt.show()

if __name__ == '__main__':
    Z = gaussian_2D_profile(-50,50,1,-50,50,1, 0, 0, 10, 5, 1)
    plot_2d_gaussian(Z)


""" This module implements the Fourier transforms on linear grids.

The following code approximates the continuous Fourier transform (FT) on
equidistantly spaced grids. While this is usually associated with
'just doing a fast Fourier transform (FFT)', surprisingly, much can be done
wrong.

The reason is that the correct expressions depend on the grid location. In
fact, the FT can be calculated with one DFT but in general it requires a prior
and posterior multiplication with phase factors.

The FT convention we are going to use here is the following::

    Ẽ(w) = 1/2pi ∫ E(t) exp(+i w t) dt
    E(t) =       ∫ Ẽ(w) exp(-i t w) dw

where w is the angular frequency. We can approximate these integrals by their
Riemann sums on the following equidistantly spaced grids::

    t_k = t_0 + k Δt, k=0, ..., N-1
    w_n = w_0 + n Δw, n=0, ..., N-1

and define E_k = E(t_k) and Ẽ_n = Ẽ(w_n) to obtain::

    Ẽ_n = Δt/2pi ∑_k E_k exp(+i w_n t_k)
    E_k = Δw     ∑_n Ẽ_n exp(-i t_k w_n).

To evaluate the sum using the FFT we can expand the exponential to obtain::

    Ẽ_n = Δt/2pi exp(+i n t_0 Δw) ∑_k [E_k exp(+i t_k w_0) ] exp(+i n k Δt Δw)
    E_k = Δw     exp(-i t_k w_0)  ∑_n [Ẽ_n exp(-i n t_0 Δw)] exp(-i k n Δt Δw)

Additionally, we have to require the so-called reciprocity relation for
the grid spacings::

          !
    Δt Δw = 2pi / N = ζ     (reciprocity relation)

This is what enables us to use the DFT/FFT! Now we look at the definition of
the FFT in NumPy::

     fft[x_m] -> X_k =     ∑_m exp(-2pi i m k / N)
    ifft[X_k] -> x_m = 1/N ∑_k exp(+2pi i k m / N)

which gives the final expressions::

    Ẽ_n = Δt N/2pi r_n   ifft[E_k s_k  ]
    E_k = Δw       s_k^*  fft[Ẽ_n r_n^*]

    with r_n = exp(+i n t_0 Δw)
         s_k = exp(+i t_k w_0)

where ^* means complex conjugation. We see that the array to be transformed
has to be multiplied with an appropriate phase factor before and after
performing the DFT. And those phase factors mainly depend on the starting
points of the grids: w_0 and t_0. Note also that due to our sign convention
for the FT we have to use ifft for the forward transform and vice versa.

Trivially, we can see that for ``w_0 = t_0 = 0`` the phase factors vanish and
the FT is approximated well by just the DFT. However, in optics these
grids are unusual.
For ``w_0 = l Δw`` and ``t_0 = m Δt``, where l, m are integers (i.e., w_0 and
t_0 are multiples of the grid spacing), the phase factors can be
incorperated into the DFT. Then the phase factors can be replaced by circular
shifts of the input and output arrays.

This is exactly what the functions (i)fftshift are doing for one specific
choice of l and m, namely for::

    t_0 = -floor(N/2) Δt
    w_0 = -floor(N/2) Δw.

In this specific case only we can approximate the FT by::

    Ẽ_n = Δt N/2pi fftshift(ifft(ifftshift(E_k)))
    E_k = Δw       fftshift( fft(ifftshift(Ẽ_n))) (no mistake!)

We see that the ifftshift _always_ has to appear on the inside. Failure to do
so will still be correct for even N (here fftshift is the same as ifftshift)
but will produce wrong results for odd N.

Additionally you have to watch out not to violate the assumptions for the
grid positions. Using a symmetrical grid, e.g.,::

    x = linspace(-1, 1, 128)

will also produce wrong results, as the elements of x are not multiples of the
grid spacing (but shifted by half a grid point).

The main drawback of this approach is that circular shifts are usually far more
time- and memory-consuming than an elementwise multiplication, especially for
higher dimensions. In fact I see no advantage in using the shift approach at
all. But for some reason it got stuck in the minds of people and you find the
notion of having to re-order the output of the DFT everywhere.

Long story short: here we are going to stick with multiplying the correct
phase factors. The code tries to follow the notation used above.

Good, more comprehensive expositions of the issues above can be found in
[Briggs1995]_ and [Hansen2014]_. For the reason why the first-order
approximation to the Riemann integral suffices, see [Trefethen2014]_.
"""
import numpy as np
# scipy.fftpack is still faster than numpy.fft (should change in numpy 1.17)
import scipy.fftpack as fft
from . import io
from .lib import twopi, sqrt2pi
_fft_backend = 'scipy'
try:
    import pyfftw
    _fft_backend = 'pyfftw'
except ImportError:
    pass

class FourierTransformBase(io.IO):
    """ This class implements the Fourier transform on linear grids.

    This simple implementation is mainly for educational use.

    Attributes
    ----------
    N : int
        Size of the grid
    dt : float
        Temporal spacing
    dw : float
        Frequency spacing (angular frequency)
    t0 : float
        The first element of the temporal grid
    w0 : float
        The first element of the frequency grid
    t : 1d-array
        The temporal grid
    w : 1d-array
        The frequency grid (angular frequency)
    """
    _io_store = ['N', 'dt', 'dw', 't0', 'w0']

    def __init__(self, N, dt=None, dw=None, t0=None, w0=None):
        """ Creates conjugate grids and calculates the Fourier transform.

        Parameters
        ----------
        N : int
            Array size
        dt : float, optional
            The temporal grid spacing. If ``None`` will be calculated by the
            reciprocity relation ``dt = 2 * pi / (N * dw)``. Exactly one of
            ``dt`` or ``dw`` has be provided.
        dw : float, optional
            The spectral grid spacing. If ``None`` will be calculated by the
            reciprocity relation ``dw = 2 * pi / (N * dt)``. Exactly one of
            ``dt`` or ``dw`` has be provided.
        t0 : float, optional
            The first element of the temporal grid. If ``None`` will be
            ``t0 = -floor(N/2) * dt``.
        w0 : float, optional
            The first element of the spectral grid. If ``None`` will be
            ``w0 = -floor(N/2) * dw``.
        """
        if dw is None and dt is not None:
            dw = np.pi / (0.5 * N * dt)
        elif dt is None and dw is not None:
            dt = np.pi / (0.5 * N * dw)
        else:
            raise ValueError("Exactly one of the grid spacings has to be "
                             "provided!")

        if t0 is None:
            t0 = -np.floor(0.5 * N) * dt
        if w0 is None:
            w0 = -np.floor(0.5 * N) * dw
        self.N = N
        self.dt = dt
        self.dw = dw
        self.t0 = t0
        self.w0 = w0
        self._post_init()

    def _post_init(self):
        """ Hook to initialize an object from storage.
        """
        # calculate the grids
        n = k = np.arange(self.N)
        self.t = self.t0 + k * self.dt
        self.w = self.w0 + n * self.dw
        # pre-calculate the phase factors
        # TODO: possibly inaccurate for large t0, w0
        self._fr = self.dt * self.N / twopi * np.exp(1.0j * n * self.t0 *
                                                     self.dw)
        self._fs = np.exp(1.0j * self.t * self.w0)
        # complex conjugate of the above
        self._br = np.exp(-1.0j * n * self.t0 * self.dw)
        self._bs = self.dw * np.exp(-1.0j * self.t * self.w0)

    def forward_at(self, x, w):
        """ Calculates the forward Fourier transform of `x` at the
        frequencies `w`.

        This function calculates the Riemann sum directly and has quadratic
        runtime. However, it can evaluate the integral at arbitrary
        frequencies, even if they are non-equidistantly spaced. Effectively,
        it performs a trigonometric interpolation.
        """
        Dnk = self.dt / twopi * np.exp(1.0j * w[:, None] * self.t[None, :])
        return Dnk @ x

    def backward_at(self, x, t):
        """ Calculates the backward Fourier transform of `x` at the
        times `t`.

        This function calculates the Riemann sum directly and has quadratic
        runtime. However, it can evaluate the integral at arbitrary
        times, even if they are non-equidistantly spaced. Effectively,
        it performs a trigonometric interpolation.
        """
        Dkn = self.dw * np.exp(-1.0j * t[:, None] * self.w[None, :])
        return Dkn @ x

# =============================================================================
# Fourier backend selection
# =============================================================================
if _fft_backend == "scipy":
    class FourierTransform(FourierTransformBase):

        def forward(self, x, out=None):
            """ Calculates the (forward) Fourier transform of ``x``.

            For n-dimensional arrays it operates on the last axis, which has
            to match the size of `x`.

            Parameters
            ----------
            x : ndarray
                The array of which the Fourier transform will be calculated.
            out : ndarray or None, optional
                A location into which the result is stored. If not provided or
                None, a freshly-allocated array is returned.
            """
            if out is None:
                out = np.empty(x.shape, dtype=np.complex128)
            out[:] = self._fr * fft.ifft(self._fs * x)
            return out

        def backward(self, x, out=None):
            """ Calculates the backward (inverse) Fourier transform of ``x``.

            For n-dimensional arrays it operates on the last axis, which has
            to match the size of `x`.

            Parameters
            ----------
            x : ndarray
                The array of which the Fourier transform will be calculated.
            out : ndarray or None, optional
                A location into which the result is stored. If not provided or
                None, a freshly-allocated array is returned.
            """
            if out is None:
                out = np.empty(x.shape, dtype=np.complex128)
            out[:] = self._bs * fft.fft(self._br * x)
            return out

elif _fft_backend == "pyfftw":
    class FourierTransform(FourierTransformBase):

        def _post_init(self):
            super()._post_init()
            # do not need the additional N factor
            n = np.arange(self.N)
            self._fr = self.dt / twopi * np.exp(1.0j * n * self.t0 * self.dw)
            # create the aligned arrays
            a = self._field = pyfftw.empty_aligned(self.N, dtype="complex128")
            b = self._spectrum = pyfftw.empty_aligned(self.N,
                                                      dtype="complex128")
            # instantiate the FFTW objects
            self._fft = pyfftw.FFTW(b, a, direction="FFTW_FORWARD")
            self._ifft = pyfftw.FFTW(a, b, direction="FFTW_BACKWARD")

        def forward(self, x, out=None):
            """ Calculates the (forward) Fourier transform of ``x``.

            For n-dimensional arrays it operates on the last axis, which has
            to match the size of `x`.

            Parameters
            ----------
            x : ndarray
                The array of which the Fourier transform will be calculated.
            out : ndarray or None, optional
                A location into which the result is stored. If not provided or
                None, a freshly-allocated array is returned.
            """
            if out is None:
                out = np.empty(x.shape, dtype=np.complex128)
            f, s = self._field, self._spectrum
            if x.ndim == 1:
                # fast code path for single dimension
                f[:] = x
                f *= self._fs
                self._ifft.execute()
                s *= self._fr
                out[:] = s
            else:
                # implicitly work along last axis and return copy
                for idx in np.ndindex(x.shape[:-1]):
                    f[:] = x[idx]
                    f *= self._fs
                    self._ifft.execute()
                    s *= self._fr
                    out[idx] = s
            return out

        def backward(self, x, out=None):
            """ Calculates the backward (inverse) Fourier transform of ``x``.

            For n-dimensional arrays it operates on the last axis, which has
            to match the size of `x`.

            Parameters
            ----------
            x : ndarray
                The array of which the Fourier transform will be calculated.
            out : ndarray or None, optional
                A location into which the result is stored. If not provided or
                None, a freshly-allocated array is returned.
            """
            if out is None:
                out = np.empty(x.shape, dtype=np.complex128)
            f, s = self._field, self._spectrum
            if x.ndim == 1:
                # fast code path for single dimension
                s[:] = x
                s *= self._br
                self._fft.execute()
                f *= self._bs
                out[:] = f
            else:
                # implicitly work along last axis and return copy
                for idx in np.ndindex(x.shape[:-1]):
                    s[:] = x[idx]
                    s *= self._br
                    self._fft.execute()
                    f *= self._bs
                    out[idx] = f
            return out

class Gaussian:
    """ This class can be used for testing the Fourier transform.
    """

    def __init__(self, dt, t0=0.0, phase=0.0):
        """ Instantiates a shifted Gaussian function.

        The Gaussian is calculated by::

            f(t) = exp(-0.5 (t - t0)^2 / dt^2) * exp(1.0j * phase)

        Its Fourier transform is::

            F(w) = dt/sqrt(2pi) exp(-0.5 * (w + phase)^2 * dt^2 +
                                     1j * t0 * w)

        Parameters
        ----------
        dt : float
            The standard deviation of the temporal amplitude distribution.
        t0 : float
            The center of the temporal amplitude distribution.
        phase : float
            The linear phase coefficient of the temporal distribution.
        """
        self.dt = dt
        self.t0 = t0
        self.phase = phase

    def temporal(self, t):
        """ Returns the temporal distribution.
        """
        arg = (t - self.t0) / self.dt
        return np.exp(-0.5 * arg**2) * np.exp(1.0j * self.phase * t)

    def spectral(self, w):
        """ Returns the spectral distribution.
        """
        w = w + self.phase
        arg = w * self.dt
        return (self.dt * np.exp(-0.5 * arg**2) * np.exp(1.0j * self.t0 * w) /
                sqrt2pi)


# -*- coding:utf-8 -*-

"""Strassen算法求矩阵乘法
"""

import numpy as np

def strassen(A, B):
    """Strassen算法求矩阵乘法

    Args:
        A(np.array): 矩阵1
        B(np.array): 矩阵2

    Return:
        C(np.array): 矩阵乘法结果
    """
    n, _ = A.shape

    if n == 1:
        C = np.array(A[0, 0] * B[0, 0])
        return C
    else:
        A11 = A[0:n//2, 0:n//2]
        A12 = A[0:n//2, n//2:]
        A21 = A[n//2:, 0:n//2]
        A22 = A[n//2:, n//2:]
        B11 = B[0:n//2, 0:n//2]
        B12 = B[0:n//2, n//2:]
        B21 = B[n//2:, 0:n//2]
        B22 = B[n//2:, n//2:]

        S1 = B12 - B22
        S2 = A11 + A12
        S3 = A21 + A22
        S4 = B21 - B11
        S5 = A11 + A22
        S6 = B11 + B22
        S7 = A12 - A22
        S8 = B21 + B22
        S9 = A11 - A21
        S10 = B11 + B12

        P1 = strassen(A11, S1)
        P2 = strassen(S2, B22)
        P3 = strassen(S3, B11)
        P4 = strassen(A22, S4)
        P5 = strassen(S5, S6)
        P6 = strassen(S7, S8)
        P7 = strassen(S9, S10)

        C11 = P5 + P4 - P2 + P6
        C12 = P1 + P2
        C21 = P3 + P4
        C22 = P5 + P1 - P3 - P7

        C = np.vstack((np.hstack((C11, C12)), np.hstack((C21, C22))))
        return C

if __name__ == '__main__':
    A = np.array([
        [1, 2, 3, 4],
        [4, 3, 2, 1],
        [1, 3, 5, 7],
        [7, 5, 3, 1],
    ])
    B = np.array([
        [2, 4, 6, 8],
        [8, 6, 4, 2],
        [1, 2, 3, 4],
        [4, 3, 2, 1],
    ])

    print(strassen(A, B))


#!/usr/bin/python
#   X = FEJER(N) returns N Chebyshev points X in (-1,1).
#
#   X, W = FEJER(N) returns also a vector W of weights for
#   Fejer quadrature.
#
#   X, W = FEJER(N, [A, B]) returns the nodes and weights
#   for the interval [A, B].

import numpy as np

def fejer(N, dom=[-1,1]):
    t = np.zeros(shape=(N,1))
    x = np.zeros(shape=(N,1))
    for k in range(N):
        t[k] = (0.5+k)*np.pi/N;
        x[k] = -np.cos(t[k])

    # Weights
    V_trans = np.zeros(shape=(N,N))
    for j in range(N):
        for k in range(N):
            V_trans[j][k] = np.cos(j*t[k]);
    rhs = np.zeros(shape=(N,1))
    for j in range(0, N, 2):
        rhs[j] = 2./(1.-j*j);
    c = np.linalg.solve(V_trans, rhs);

    # Scale
    a = dom[0];
    b = dom[1];
    x = .5*(b-a)*x + .5*(b+a);
    c = .5*(b-a)*c;

    return (x, c)
if __name__ == "__main__":
    x, c = fejer(5, [-1, 1])
    print 'x = ', x
    print 'c = ', c


""""
    This program calculates the inverse of a matrix by many ways.
    Marina von Steinkirch, spring/2013 (based on Mike Zingale's codes)

"""

import numpy as npy
from scipy import linalg
from inverseGauss import inverseGauss
from inverseNewton import inverseNewton

def main():
    A = npy.array([ [4, 3, 4, 10], [2, -7, 3, 0], [-2, 11, 1, 3], [3, -4, 0, 2] ], dtype=npy.float64)

    """
    print "\nInverse matrix calculated by the Numpy API:"
    AinvNpy = linalg.inv(A)
    print "NumPy: A . Ainv = \n", npy.dot(A, AinvNpy)
    print "NumPy: Ainv = \n", AinvNpy

    print "\nInverse matrix calculated by the Gauss method:"
    AinvGauss = inverseGauss(A)
    print "Gauss: A . Ainv = \n", npy.dot(A, AinvGauss)
    print "Gauss: Ainv = \n", AinvGauss

    """

    print "\nInverse matrix calculated by the Newton method:"
    AinvNewton, error, nIter = inverseNewton(A)
    print "Newtow: A . Ainv = \n", npy.dot(A, AinvNewton)
    print "Newtow: Ainv = \n", AinvNewton
    print "Number of iterations: ", nIter
    print "Error from Numpy API results: ", error

    print "\nDone!"

if __name__ == "__main__":
    main()


"""
Author : Achintya Gupta
Date Created : 22-09-2020
"""

"""
Problem Statement
------------------------------------------------
Q)  A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
    Find the largest palindrome made from the product of two 3-digit numbers.
"""
from utils import timing_decorator, check_palindrome
import numpy as np

@timing_decorator
def find_Lpal(N=3):
    maxN = 10**N-1
    minN = 10**(N-1)
    max_product = 0
    palin_list = []

    for i in reversed(range(minN,maxN+1)):
        for j in reversed(range(minN,maxN+1)):
            prod = i*j
            if prod<max_product:
                break
            if check_palindrome(prod) and prod > max_product:
                palin_list.append(prod)
                max_product = prod

    print(f'Largest palindrome : {max(palin_list)}')

find_Lpal(3)


import numpy as np
import operator as op
from functools import reduce

def ncr(n, r):
    """n choose r"""
    r = min(r, n - r)  # This works since it's symmetric

    numer = reduce(op.mul, range(n, n - r, -1), 1)
    denom = reduce(op.mul, range(1, r + 1), 1)
    return numer / denom

def bernstein_poly(i, n, t):
    """
     The Bernstein polynomial of n, i as a function of t
    """
    return ncr(n, i) * (t ** (n - i)) * (1 - t)**i

def compute_bezier_points(points, n_times=25):
    """
        Returns a list of points that can be used to construct a bezier curve.
    """
    n_points = len(points)
    x_points, y_points = np.array([p[0] for p in points]), np.array([p[1] for p in points])

    t = np.linspace(0.0, 1.0, n_times)
    polynomial_array = np.array([bernstein_poly(i, n_points-1, t) for i in range(0, n_points)])

    x_vals, y_vals = np.dot(x_points, polynomial_array), np.dot(y_points, polynomial_array)

    return [(x, y) for x, y in zip(x_vals, y_vals)]

def colour_linear_interpolation(col_a, col_b, t):
    """
        Linearly interpolates between two colours.
    """
    col = tuple([a + (b - a) * t for a, b in zip(col_a, col_b)])
    return col

def map_from_to(x, a, b, c, d):
    """
        Maps a value x from a-b to c-d.
    """
    return (x - a) / (b - a) * (d - c) + c


import numpy as np
import math

def factorial(n):
	fact = 1
	for i in range(1,n+1):
		fact *= i
	return fact

summation = 1
for i in range(1,7):
	val = (-1)**i * (math.pi/3)**(2*i) / factorial(2*i)
	summation += val
	print "val: ", val
	print "summation: ", summation

print "Final summation: ", summation


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd

def entangled_two_circles(number_of_points_per_class, plot = True):
    '''
    Creates the dataset of two circles in R^3, a blue and a red one, each inside of the other.
    Inputs: number of points in each class of the dataset, if plot is true, shows the dataset in R^3
    Outputs: (dataset in 3 coordinates, labels of the dataset)
    '''
    theta = np.linspace(0,2*np.pi,number_of_points_per_class)
    x_1 = np.cos(theta)
    y_1 = np.sin(theta)
    z_1 = np.zeros(len(theta))

    x_2 = np.zeros(len(theta))
    y_2 = np.cos(theta)+np.ones(len(theta))
    z_2 = np.sin(theta)

    x = np.concatenate((x_1, x_2))
    y = np.concatenate((y_1, y_2))
    z = np.concatenate((z_1, z_2))

    target = np.concatenate((np.zeros(len(x_1)), np.ones(len(x_2))))

    x = np.reshape(x,(-1,1))
    y = np.reshape(y,(-1,1))
    z = np.reshape(z,(-1,1))
    data = np.concatenate((x,y,z), axis = 1)

    if plot == True:
        fig = plt.figure()
        ax = Axes3D(fig)

        scatter = ax.scatter(x_1,y_1,z_1, c= 'r')
        scatter = ax.scatter(x_2,y_2,z_2, c= 'b')
        plt.show()

    return (data, target)

def entangled_four_circles(number_of_points_per_class, plot = True):
    '''
    Creates the dataset of four circles in R^3, two blue and two larger red one, each entangled inside the other.
    Inputs: 4*(number of points) in each class of the dataset, if plot is true, shows the dataset in R^3
    Outputs: (dataset in 3 coordinates, labels of the dataset)
    '''
    theta = np.linspace(0,2*np.pi,number_of_points_per_class)

    x_r1 = 1/2*np.cos(theta)
    y_r1 = 1/2*np.sin(theta)+np.ones(len(theta))
    z_r1 = np.zeros(len(theta))

    x_r2 = 1/2*np.cos(theta)
    y_r2 = 1/2*np.sin(theta)-np.ones(len(theta))
    z_r2 = np.zeros(len(theta))

    x_r = np.concatenate((x_r1, x_r2))
    y_r = np.concatenate((y_r1, y_r2))
    z_r = np.concatenate((z_r1, z_r2))

    x_b1 = 1/3*np.ones(len(theta))
    y_b1 = np.cos(theta)
    z_b1 = np.sin(theta)

    x_b2 = -1/3*np.ones(len(theta))
    y_b2 = np.cos(theta)
    z_b2 = np.sin(theta)

    x_b = np.concatenate((x_b1, x_b2))
    y_b = np.concatenate((y_b1, y_b2))
    z_b = np.concatenate((z_b1, z_b2))

    x = np.concatenate((x_r, x_b))
    y = np.concatenate((y_r, y_b))
    z = np.concatenate((z_r, z_b))

    target = np.concatenate((np.zeros(len(x_r)), np.ones(len(x_b))))

    x = np.reshape(x,(-1,1))
    y = np.reshape(y,(-1,1))
    z = np.reshape(z,(-1,1))
    data = np.concatenate((x,y,z), axis = 1)

    if plot == True:
        fig = plt.figure()
        ax = Axes3D(fig)

        scatter = ax.scatter(x_r,y_r,z_r, c= 'r',  alpha = 0.5)
        scatter = ax.scatter(x_b,y_b,z_b, c= 'b')

    return (data, target)

def circle_inside_torus(number_of_points_per_class, plot = True):
    '''
    Creates a dataset of a circle of radius 2 inside a torus of inner radius 1 and outter radius 3 of number_of_points_per_class
    in each of these manifolds. If plot = True, it plots both the surface and the scatter plot for the torus sampled data
    Inputs: number_of_points_per_class; plot = True
    '''
    theta_circle = np.linspace(0,2*np.pi,number_of_points_per_class)
    x_circle = 2*np.cos(theta_circle)
    y_circle = 2*np.sin(theta_circle)
    z_circle = np.zeros(len(theta_circle))
    x_circle = np.reshape(x_circle,(-1,1))
    y_circle = np.reshape(y_circle,(-1,1))
    z_circle = np.reshape(z_circle,(-1,1))
    data_circle = np.concatenate((x_circle,y_circle,z_circle), axis = 1)

    theta_torus = 2*np.pi*np.random.rand(round(number_of_points_per_class**1/2)) #takes the square root so that each class has the same
    #number of instances
    phi_torus = 2*np.pi*np.random.rand(round(number_of_points_per_class**1/2))
    c, a = 2, 1
    x_torus = (c + a*np.cos(theta_torus)) * np.cos(phi_torus)
    y_torus = (c + a*np.cos(theta_torus)) * np.sin(phi_torus)
    z_torus = a * np.sin(theta_torus)
    x_torus = np.reshape(x_torus,(-1,1))
    y_torus = np.reshape(y_torus,(-1,1))
    z_torus = np.reshape(z_torus,(-1,1))
    data_torus = np.concatenate((x_torus,y_torus,z_torus), axis = 1)

    data =  np.concatenate((data_circle, data_torus), axis = 0)
    target = np.concatenate((np.zeros(number_of_points_per_class),np.ones(len(data_torus))), axis = 0)

    if plot == True:
        n = 100 #we will make 100 points so to make the surface plot visible
        theta_plot = np.linspace(0,2*np.pi,n)
        phi_plot = np.linspace(0, 2*np.pi, n)
        theta_plot, phi_plot = np.meshgrid(theta_plot, phi_plot)
        c, a = 2, 1
        x_torus_plot = (c + a*np.cos(theta_plot)) * np.cos(phi_plot)
        y_torus_plot = (c + a*np.cos(theta_plot)) * np.sin(phi_plot)
        z_torus_plot = a * np.sin(theta_plot)

        fig = plt.figure(figsize = [10,10])
        ax = fig.add_subplot(1, 2, 1, projection='3d')
        ax.scatter(x_circle,y_circle,z_circle, c= 'r',alpha=1)
        ax.plot_surface(x_torus_plot, y_torus_plot, z_torus_plot,
                                  color='b', alpha =0.4, rstride=5, cstride=5, edgecolors='w')
        ax.set_zlim(-3,3)

        ax = fig.add_subplot(1, 2, 2, projection='3d')
        ax.scatter(x_circle,y_circle,z_circle, c= 'r',alpha=1)
        ax.scatter(x_torus, y_torus, z_torus,
                                  color='b', alpha =0.4)
        ax.set_zlim(-3,3)

    return (data, target)

def swiss_roll(number_of_points_per_class, plot = True):
    '''
    Creates a dataset of a swiss roll of number_of_class points of half of its is in class red and half in class blue.
    If plot = True, it plots both the scatter plot for the sampled data
    Inputs: number_of_points_per_class; plot = True
    '''
    #makes the cross section of the swiss roll (ie. spiral)
    theta_blue = np.linspace(0, 5*np.pi, round(number_of_points_per_class/10))
    theta_red = np.linspace(5*np.pi, 10*np.pi, round(number_of_points_per_class/10))

    x_blue =  theta_blue*np.cos(theta_blue)
    y_blue =  theta_blue*np.sin(theta_blue)
    z_blue = np.zeros(len(x_blue))

    x_red =  theta_red*np.cos(theta_red)
    y_red =  theta_red*np.sin(theta_red)
    z_red = np.zeros(len(x_red))

    #reshape arrays
    x_blue = np.reshape(x_blue,(-1,1))
    y_blue = np.reshape(y_blue,(-1,1))
    z_blue = np.reshape(z_blue,(-1,1))

    x_red = np.reshape(x_red,(-1,1))
    y_red = np.reshape(y_red,(-1,1))
    z_red = np.reshape(z_red,(-1,1))

    blue_coord = np.concatenate((x_blue, y_blue, z_blue), axis = 1)
    red_coord = np.concatenate((x_red, y_red, z_red), axis = 1)

    #lifts the spiral to the swiss roll by making 9 extra layers of points
    for i in range(1, 10):
        z_new = i*np.ones(len(x_blue))
        z_new = np.reshape(z_new, (-1,1))

        new_blue_cord = np.concatenate((x_blue, y_blue, z_new), axis = 1)
        new_red_cord = np.concatenate((x_red, y_red, z_new), axis = 1)

        blue_coord = np.concatenate((blue_coord, new_blue_cord), axis = 0)
        red_coord = np.concatenate((red_coord, new_red_cord), axis = 0)

    #makes dataset and target
    data = np.concatenate((red_coord, blue_coord), axis = 0)

    target_red = np.zeros(len(red_coord))
    target_blue = np.ones(len(blue_coord))
    target = np.concatenate((target_red, target_blue), axis = 0)
    if plot == True:
        fig = plt.figure()
        ax = Axes3D(fig)

        scatter = ax.scatter(blue_coord[:,0],blue_coord[:,1],blue_coord[:,2], c = 'b')
        scatter = ax.scatter(red_coord[:,0],red_coord[:,1],red_coord[:,2], c = 'r')
        plt.show()

    return (data, target)


import numpy as np

class LogisticRegression:
    def __init__(self, alpha=0.01, iteration=1500, intercept=True):
        self.alpha=alpha
        self.iteration=iteration
        self.intercept=intercept

    def sigmoid(self,x):
        return 1 / (1+np.exp(-x))

    def fit(self, x, y):
        self.m = len(x)
        self.n = np.size(x,1)
        if self.intercept :
            x = np.hstack((np.ones((len(x),1)), x))

        self.theta=np.zeros(x.shape[1])

        for _ in range(self.iteration):
            self.z=np.dot(x, self.theta)
            self.h=self.sigmoid(self.z)
            grad=np.dot(x.T, (self.h-y)) / y.size
            self.theta = self.alpha * grad

    def loss(self, y):
        return ((-y*np.log(self.h))- ((1-y)*np.log(1-self.h))/self.m)

    def predict_prob(self, x):
        if self.intercept :
            x = np.hstack((np.ones((len(x),1)), x))
        return self.sigmoid(np.dot(x, self.theta))

    def predict(self, x, threshold=0.5):
        return self.predict_prob(x)>= threshold


# coding=utf-8
"""Hermite and Bezier curves using python, numpy and matplotlib"""

import numpy as np
#import matplotlib.pyplot as plt
#from mpl_toolkits.mplot3d import Axes3D

__author__ = "Daniel Calderon"
__license__ = "MIT"

def generateT(t):
    return np.array([[1, t, t**2, t**3]]).T

def hermiteMatrix(P1, P2, T1, T2):

    # Generate a matrix concatenating the columns
    G = np.concatenate((P1, P2, T1, T2), axis=1)

    # Hermite base matrix is a constant
    Mh = np.array([[1, 0, -3, 2], [0, 0, 3, -2], [0, 1, -2, 1], [0, 0, -1, 1]])

    return np.matmul(G, Mh)

def bezierMatrix(P0, P1, P2, P3):

    # Generate a matrix concatenating the columns
    G = np.concatenate((P0, P1, P2, P3), axis=1)

    # Bezier base matrix is a constant
    Mb = np.array([[1, -3, 3, -1], [0, 3, -6, 3], [0, 0, 3, -3], [0, 0, 0, 1]])

    return np.matmul(G, Mb)

def catmullRomMatrix(P0, P1, P2, P3):
    # Generate a matrix concatenating the columns
    G = np.concatenate((P0, P1, P2, P3), axis=1)

    # Camull-Rom base matrix is a constant
    Mcr = 1/2*np.array([[0, -1, 2, -1], [2, 0, -5, 3], [0, 1, 4, -3], [0, 0, -1, 1]])

    return np.matmul(G, Mcr)

def plotCurve(ax, curve, label, color=(0,0,1)):

    xs = curve[:, 0]
    ys = curve[:, 1]
    zs = curve[:, 2]

    ax.plot(xs, ys, zs, label=label, color=color)

# M is the cubic curve matrix, N is the number of samples between 0 and 1
def evalCurve(M, N):
    # The parameter t should move between 0 and 1
    ts = np.linspace(0.0, 1.0, N)

    # The computed value in R3 for each sample will be stored here
    curve = np.ndarray(shape=(N, 3), dtype=float)

    for i in range(len(ts)):
        T = generateT(ts[i])
        curve[i, 0:3] = np.matmul(M, T).T

    return curve

def evalCurveTime(M, t):
    T = generateT(t)
    curve_point = np.matmul(M, T)
    return curve_point

def matricesCRCurve(points):
    control_points = [points[0], points[len(points)-1]]
    matrices = []

    for i in range(len(points)-3):
        Mcr = catmullRomMatrix(points[i], points[i+1], points[i+2], points[i+3])
        matrices += [Mcr]
    return matrices

def evalCRCurveTime(t, matrices, times):
    N_curves = len(matrices)
    curve_point = evalCurveTime(matrices[0], t)

    for i in range(N_curves):
        if times[i] <= t <= times[i+1]:
            normalized_t = (t-times[i]) / (times[i+1]-times[i])
            curve_point = evalCurveTime(matrices[i], normalized_t)

    return curve_point

if __name__ == "__main__":

    # Number of samples to plot
    N = 500

    # Setting up the matplotlib display for 3D
    fig = plt.figure()
    ax = fig.gca(projection='3d')

    """
    Example for Catmull-Rom curve
    """

    P0 = np.array([[-0.3, -0.25, 1]]).T*25
    P1 = np.array([[-0.5, 0.25, 1.15]]).T*25
    P2 = np.array([[-0.15, 0.15, 1]]).T*25
    P3 = np.array([[0, 0.5, 0.75]]).T*25
    P4 = np.array([[0.15, 0.15, 1]]).T*25
    P5 = np.array([[0.4, 0.25, 1.15]]).T*25
    P6 = np.array([[0.15, -0.15, 1]]).T*25
    P7 = np.array([[0.4, -0.5, 0.75]]).T*25
    P8 = np.array([[0, -0.25, 1]]).T*25
    P9 = np.array([[-0.4, -0.5, 1.15]]).T*25
    P10 = np.array([[-0.25, -0.15, 1]]).T*25
    P11 = np.array([[-0.5, 0.25, 1.15]]).T*25
    P12 = np.array([[-0.15, 0.25, 1]]).T*25

    points = [P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12]
    times = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    matrices = matricesCRCurve(points)

    catmullRomCurve = np.ndarray(shape=(N, 3), dtype=float)
    ts = np.linspace(0.0, len(times)-1, N)

    for i in range(N):
        catmullRomCurve[i, 0:3] = evalCRCurveTime(ts[i], matrices, times).T
        #if 0<= ts[i] <= 1:
        #    T = generateT(ts[i])
        #    catmullRomCurve[i, 0:3] = np.matmul(matrices[0], T).T

        #elif 1 <= ts[i] <= 2:
        #    T = generateT(ts[i]-1)
        #    catmullRomCurve[i, 0:3] = np.matmul(matrices[1], T).T

        #elif 2 <= ts[i] <= 3:
        #    T = generateT(ts[i]-2)
        #    catmullRomCurve[i, 0:3] = np.matmul(matrices[2], T).T

        #elif 3 <= ts[i] <= 4:
        #    T = generateT(ts[i]-3)
        #    catmullRomCurve[i, 0:3] = np.matmul(matrices[3], T).T

    plotCurve(ax, catmullRomCurve, "Catmull-Rom curve")

    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    ax.legend()

    plt.show()


import numpy as np

def gaussian2d(shape=(3,3),sigma=0.5):
    """
    2D gaussian mask - should give the same result as MATLAB's
    fspecial('gaussian',[shape],[sigma])
    """
    m,n = [(ss-1.)/2. for ss in shape]
    y,x = np.ogrid[-m:m+1,-n:n+1]
    h = np.exp( -(x*x + y*y) / (2.*sigma*sigma) )
    h[ h < np.finfo(h.dtype).eps*h.max() ] = 0
    sumh = h.sum()
    if sumh != 0:
        h /= sumh
    return h


import numpy as np
import matplotlib.pyplot as plt

# Discrete Fourier Transform (1D)
def DFT1D(f):
  n = f.shape[0]
  F = np.zeros(f.shape, dtype=np.complex64)
  # For each frequency u=0..n-1
  for u in np.arange(n):
    # For each element of `f` x=0..n-1
    for x in np.arange(n):
      F[u] += f[x] * np.exp(-1j * (2 * np.pi) * (u * x) / n)
  # Normalize the fourer
  return F / np.sqrt(n)

# Faster Discrete Fourier Transform (1D)
def fasterDFT1D(f):
  n = f.shape[0]
  F = np.zeros(f.shape, dtype=np.complex64)
  x = np.arange(n)
  # For each frequency u=0..n-1
  for u in np.arange(n):
    F[u] += f[x] * np.exp(-1j * (2 * np.pi) * (u * x) / n)
  # Normalize the fourer
  return F / np.sqrt(n)

# Define a signal
t = np.arange(0, 1, 0.005)
f = 1 * np.sin(t * (2 * np.pi) * 2) + 0.6 * np.cos(t * (2 * np.pi) * 8) + \
    0.4 * np.cos(t * (2 * np.pi) * 16)

# Computing DFT1D of `f`
F = DFT1D(f)

fq = np.arange(200)
plt.figure(figsize=(10, 4))

# Plot the magnitude of the DFT
plt.plot(fq, np.abs(F), 'r')
plt.savefig('dft_magnitude.png')

# Due to the symmetric property of the sine and cosine functions (which are
# similar in shape, but shifted), the Fourier Transform is also symmetric with
# respect to its central coefficient. One interpretation is that both the
# positive and negative frequency sinusoids are 90 degrees out of phase, but
# the magnitude of their response will be the same. In other words, they both
# respond to real signals in the same way.

# Plot only part of the frequencies
limit = 32

fq = np.arange(limit)
plt.figure(figsize=(10, 4))

# Plot the magnitude of the DFT
plt.plot(fq, np.abs(F[:limit]), 'r')
plt.savefig('dft_magnitude_limit.png')


#Import the Libraries
import numpy as np
import matplotlib.pyplot as plt
#%matplotlib inline

#Function to genertae Loss Function (y)
def gen_y(a):
    u=np.sin(a/10)*(5*(np.cos(a+10))-np.cos(a/2000))
    return u+20*np.sin(a)

#The derivative function
def der(a):
    u=(np.sin(a/10)*((np.sin(a/2000)/2000)-5*np.sin(a/10)))
    v=(np.cos(a/10)*(5*np.cos(a+10)- np.cos(a/200)))/10
    return u + v + (20*np.cos(a))

def Gradient_Descent(W,W_prev=0,eta=0.004,tol=0.004,epochs=1):
    #Base Condition, from Equation 2
    if(W-W_prev<tol):
        print(f'Returning after {epochs} number of epochs')
        return W
    # We calculate the gradient value at W
    g=der(W)
    # We memorize the the W
    W_prev=W
    # We update the weight with the help of the previous one
    # eta is the learning rate and tol is the tolerance
    W=W_prev-eta*g
    #Itertaive Process and we also count the number of epochs
    return Gradient_Descent(W,W_prev,epochs=epochs+1)

##################################  MAIN #######################################################

#Generate loss Function
x=np.linspace(15,25,500)
y=gen_y(x)

#Visulaize the plot
fig,ax=plt.subplots(nrows=1,ncols=2,figsize=(14,8))
ax[0].plot(x,y,color='r',linewidth=3)
ax[0].set_xlabel('Weight',color='g',fontsize=20)
ax[0].set_ylabel('Loss',color='r',fontsize=20)
ax[0].set_title('Loss Function',fontsize=25,color='b')

#Choose a random starting point and use the gradient descent function to reach the minima
start=15.25
best_weight=np.round(Gradient_Descent(start),2)
print(f'The optimal weight is {best_weight}')
#To plot iterations
x_plot=np.linspace(start+0.1,best_weight-0.3,152)

##A Visual Plot of our excercise
ax[1].plot(x,y,color='r',linewidth=3)
ax[1].scatter(best_weight,gen_y(best_weight),linewidth=16,label='Optimal Wight',color='blue')
ax[1].scatter(start,gen_y(start),linewidth=16,label='Start',color='orange')
ax[1].scatter(x_plot,gen_y(x_plot),linewidth=16,label='Iteration',color='#FDDF00',alpha=0.3)
ax[1].set_xlabel('Weight',color='g',fontsize=20)
ax[1].set_ylabel('Loss',color='r',fontsize=20)
ax[1].set_title('Finding Minima with Gradient Descent',fontsize=25,color='b')
ax[1].legend(markerscale=0.2)
plt.tight_layout()


import math

import numpy as np
import utils

def execute(data, max_iterations, clusters, epsilon):
    # Number of features
    features = len(data[0])
    # Number of rows
    rows = len(data)
    # create the first membership matrix
    u_matrix = []
    # fuzzy constant
    fc = 2.00

    # create the membershipo metric using random numbers
    for i in range(rows):
        vals = np.random.rand(clusters)
        s = sum(vals)
        memb = [v / s for v in vals]
        u_matrix.append(memb)

    # iteration counter
    iteration = 0
    cntrs = []
    old_cntrs = []

    # Check if the number of iterations has reached the maximum or if the iterations have converged
    while (iteration < max_iterations) and (check_iteration(old_cntrs, cntrs, epsilon)):
        old_cntrs = cntrs
        # calcualte the Centroids
        cntrs = get_centroids(data, u_matrix, clusters, fc)
        # calculate the membership matrix
        u_matrix = calculate_membership_matrix(data, cntrs, u_matrix, fc)
        iteration += 1

    return u_matrix, cntrs

# Check if the iterations have converged
def check_iteration(c1, c2, epsilon):
    if not c1 or not c2:
        return True
    distances = [utils.distance(c1[i], c2[i]) for i in range(len(c1))]
    return not (np.amax(distances) < epsilon)

def get_centroids(data, membership, clusters, fuzzy_constant):
    centroids = []
    # Separate the memberships by cluster number
    mem = zip(*membership)

    for i in range(clusters):
        #  The cluster i memberships for every row
        mm = mem[i]
        # elevate every membership by the fuzzy constant
        mm_prod = [u ** fuzzy_constant for u in mm]
        denominator = sum(mm_prod)

        numerator = []
        # In every column calculate the ith centroid
        for j in range(len(data[0])):
            temp = []
            for k in range(len(data)):
                # uij**m * xi
                temp.append(mm_prod[k] * data[k][j])
            numerator.append(temp)

        numerator = [sum(x) for x in numerator]
        centroid = []
        # Calculate the centroid
        for n in numerator:
            centroid.append(n / denominator)
        centroids.append(centroid)

    return centroids

# Calculate a new membership function using the centroids
def calculate_membership_matrix(data, centroids, membership, fuzzy_constant):
    u_matrix = membership
    fc = 2 / (fuzzy_constant - 1)
    for i in range(len(data)):
        # Calculate the distance of the row with the centroids
        distances = [utils.distance(data[i], centroids[k]) for k in range(len(centroids))]
        for k in range(len(centroids)):
            den = sum([math.pow(float(distances[k] / distances[c]), fc) for c in range(len(centroids))])
            u_matrix[i][k] = float(1 / den)
    return u_matrix

# Calculate which cluster is the one with the highest membership for every row
def get_labels(rows, membership):
    labels = []
    for i in range(rows):
        max_membership = -1
        max_mem_index = -1
        for j in range(len(membership[i])):
            if membership[i][j] > max_membership:
                max_membership = membership[i][j]
                max_mem_index = j
        labels.append(max_mem_index)
    return labels


# SETUP begin...

# install necessary libraries first

# if requirements not already satisfied

    # pip install pyprimes

    # pip install pycryptodome

# import necessary modules

import random

import pyprimes

import warnings

from Crypto.Hash import SHA3_256 # for hash

import os.path # for GenerateorRead

import string # for generating random string in fixed range

import sympy # check if the number is prime

import sys # for path

# will be utilized in the implementation, taken from homeworks provided before

def egcd(a, b):

    x,y, u,v = 0,1, 1,0

    while a != 0:

        q, r = b//a, b%a

        m, n = x-u*q, y-v*q

        b,a, x,y, u,v = a,r, u,v, m,n

    gcd = b

    return gcd, x, y

def modinv(a, m):

    if a < 0:

        a = a+m

    gcd, x, y = egcd(a, m)

    if gcd != 1:

        return None  # modular inverse does not exist

    else:

      return x % m

def random_prime(bitsize):

    warnings.simplefilter('ignore')

    chck = False

    while chck == False:

        p = random.randrange(2**(bitsize-1), 2**bitsize-1)

        chck = pyprimes.isprime(p)

    warnings.simplefilter('default')

    return p

def large_DL_Prime(q, bitsize):

    warnings.simplefilter('ignore')

    chck = False

    while chck == False:

        k = random.randrange(2**(bitsize-1), 2**bitsize-1)

        p = k*q+1

        chck = pyprimes.isprime(p) and (p.bit_length() == 2048)

    warnings.simplefilter('default')

    return p

# will be utilized in the implementation, taken from homeworks provided before

# generates public parameters q,p,g

# generate random primes such that q|p-1

# algorithm for choosing the generator:

  # choose a random alpha

  # try a random alpha raised to the power ((p-1)) % q mod(p)

  # if anything other than 1, it's the generator of Z*p with q elements

def Param_Generator(sizeq,sizep): # qsize = 224-bit, psize = 2048-bit

  q = random_prime(sizeq)

  diff = sizep - sizeq # determine the range for generation of p

  p = large_DL_Prime(q, diff) # discrete log prime

  hold = (p)//q

  g = 1

  while g == 1:

      alpha1 = random.randrange(1, p)

      g = pow(alpha1, hold, p)

  return q, p, g

# generate public and private keys for the users

# generates private key -> secret k integer such that 0 < k < q-1

# computes public key by taking modulo p of g^k which is beta

def KeyGen(q,p,g):

  alpha = random.randint(1,q-2) # private

  beta = pow(g,alpha,p) # public

  return alpha, beta

# signature generation

# signature = (s,r)

  # hash function is 256-bit SHA

    # take modulo q after hashing -> get h value

  # select j (random integer) such that range = [1,q-2]

  # compute r such that (g^j(mod(p))) mod(q) is r  -> signature part1

  # compute s such that k*r - j*h (mod(q)) is s -> signature part2 (remember that k is the secret key)

  # plaintext = m

def SignGen(m,q,p,g,alpha):

  hashval = SHA3_256.new() # create new SHA object here

  hashval.update(m) # hash here

  hold = hashval.digest()

  h = int.from_bytes(hold, byteorder='big') # conversion from bytes to integer

  k = random.randint(1,q-2)

  r = pow(g,k,p) % q

  s = ((alpha*r)-(k*h)) % q

  return s,r # returning as a tuple or by comma is essentially the same thing post-function call

# signature verification

  # now (s,r) is the signature for m

  # carry out the same procedure as above with SHA encryption

  # take modulo inverse of h in modulo q and equate this to v

  # compute z1 such that s*v mod(q) is z1

  # compute z2 such that r*v mod(q) is z2

  # finally, compute (((g^-z1)*(beta^z2)) mod(p)) mod(q) and equate this to u

  # only verify the signature if u = r

# Signature verification

def SignVer(m,s,r,q,p,g,beta):

  hashval = SHA3_256.new() # create new SHA object here

  hashval.update(m) # hash here

  hold = hashval.digest()

  h = int.from_bytes(hold, byteorder='big') % q # conversion from bytes to integer

  v = modinv(h,q)

  z1 = (s*v) % q

  z2 = (r*v) % q

  u = ((modinv(pow(g,z1,p),p)*pow(beta,z2,p)) % p) % q

  if(u == r):

    return 0

  else:

    return random.randint(1,42) # return an arbitrary value for failure

# read the file

# if exists, read public parameters from the file

# else, create public parameters and write them to pubparams.txt

def GenerateOrRead(filename):

    if os.path.isfile(filename) == True:   #if we can reach the file open it

            file = open(filename, 'r+')

            if os.stat(filename).st_size == 0:      #if file is empty generate new parameters

                q, p, g = Param_Generator(224, 2048)

                file.write(str(q) + '\n' + str(p) + '\n' + str(g))  #write the new  parameters to file

                file.close()

                return int(q), int(p), int(g)

            else:     #if file is not empty

                file = open(filename, 'r+')   #open the file

                txt_info = file.readlines()  #read each line

                j = 0

                while(j != len(txt_info)):    #set each line to the parameters with respect to their order

                    q = int(txt_info[j])

                    p = int(txt_info[j+1])

                    g = int(txt_info[j+2])

                    return q, p, g

    else:       #if we can't reach the file open new files for reading and writing

        file = open(filename,'w+')

        q, p, g = Param_Generator(224, 2048)

        file.write(str(q) + '\n' + str(p) + '\n' + str(g))  #write the new  parameters to file

        file.close()

        return int(q), int(p), int(g)

# return a random combination of lowercase letters, uppercase letters and digits

def random_string(str_size):

    randomized = "".join(random.choices(string.ascii_lowercase + string.digits + string.ascii_uppercase, k = str_size))

    return randomized

# SETUP end...


import numpy as np
import load_data as ld
from evaluators import *

class LogisticRegression():
    """
    Class for performing logistic regression.
    """
    def __init__(self, learning_rate = 0.7, max_iter = 1000):
        self.learning_rate = learning_rate
        self.max_iter = max_iter
        self.theta = []
        self.no_examples = 0
        self.no_features = 0
        self.X = None
        self.Y = None

    def add_bias_col(self, X):
        bias_col = np.ones((X.shape[0], 1))
        return np.concatenate([bias_col, X], axis=1)

    def hypothesis(self, X):
        return 1 / (1 + np.exp(-1.0 * np.dot(X, self.theta)))

    def cost_function(self):
        """
        We will use the binary cross entropy as the cost function. https://en.wikipedia.org/wiki/Cross_entropy
        """
        predicted_Y_values = self.hypothesis(self.X)
        cost = (-1.0/self.no_examples) * np.sum(self.Y * np.log(predicted_Y_values) + (1 - self.Y) * (np.log(1-predicted_Y_values)))
        return cost

    def gradient(self):
        predicted_Y_values = self.hypothesis(self.X)
        grad = (-1.0/self.no_examples) * np.dot((self.Y-predicted_Y_values), self.X)
        return grad

    def gradient_descent(self):
        for iter in range(1,self.max_iter):
            cost = self.cost_function()
            delta = self.gradient()
            self.theta = self.theta - self.learning_rate * delta
            print("iteration %s : cost %s " % (iter, cost))

    def train(self, X, Y):
        self.X = self.add_bias_col(X)
        self.Y = Y
        self.no_examples, self.no_features = np.shape(X)
        self.theta = np.ones(self.no_features + 1)
        self.gradient_descent()

    def classify(self, X):
        X = self.add_bias_col(X)
        predicted_Y = self.hypothesis(X)
        predicted_Y_binary = np.round(predicted_Y)
        return predicted_Y_binary

to_bin_y = { 1: { 'Iris-setosa': 1, 'Iris-versicolor': 0, 'Iris-virginica': 0 },
             2: { 'Iris-setosa': 0, 'Iris-versicolor': 1, 'Iris-virginica': 0 },
             3: { 'Iris-setosa': 0, 'Iris-versicolor': 0, 'Iris-virginica': 1 }
             }

X_train, y_train, X_test, y_test = ld.iris()

Y_train = np.array([to_bin_y[3][x] for x in y_train])
Y_test = np.array([to_bin_y[3][x] for x in y_test])

print("training Logistic Regression Classifier")
lr = LogisticRegression()
lr.train(X_train, Y_train)
print("trained")
predicted_Y_test = lr.classify(X_test)
f1 = f1_score(predicted_Y_test, Y_test, 1)
print("F1-score on the test-set for class %s is: %s" % (1, f1))

# from sklearn.linear_model import LogisticRegression
# logistic = LogisticRegression()
# logistic.fit(X_train,Y_train)
# predicted_Y_test = logistic.predict(X_test)
# f1 = f1_score(predicted_Y_test, Y_test, 1)
# print("F1-score on the test-set for class %s is: %s" % (1, f1))


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

import numpy as np

class Losses:
    def __init__(self):
        self.target = None
        self.achieved = None

    def forward(self, target, achieved):
        pass

    def backward(self):
        pass

class SSELoss(Losses):
    def forward(self, target, achieved):
        assert np.shape(target) == np.shape(achieved), 'Shape of target and achieved is not same'

        self.target = target
        self.achieved = achieved

        sse_loss = np.multiply(np.power(np.subtract(self.target, self.achieved), 2), 0.5)
        return sse_loss

    def backward(self):
        assert np.shape(self.target) == np.shape(self.achieved), "Shape of target and achieved is not same"
        assert self.achieved is not None, "Need to compute loss before computing gradient"
        assert self.target is not None, "Need to compute loss before computing gradient"

        sse_grad = np.subtract(self.achieved, self.target)

        return sse_grad


import numpy as np
from scipy.stats import norm
import pandas as pd
import matplotlib.pyplot as plt

# Function to estimate point of intersection of normal curves of two classes, at the point of intersection diff
# is zero or a point where the difference changes it sign

def intersection(f, g, x):
    d = f - g
    for i in range(len(d) - 1):
        if d[i] == 0. or d[i] * d[i + 1] < 0.:
            x_ = x[i]
            return x_

def FisherLDA(ds, s):
    row, col = ds.shape
    ds_0 = ds[ds[col-1] == 0].iloc[:, 0:-1].values
    ds_1 = ds[ds[col-1] == 1].iloc[:, 0:-1].values
    m0 = np.mean(ds_0, axis=0)
    m1 = np.mean(ds_1, axis=0)
    SW_0 = np.cov(np.transpose(ds_0))
    SW_1 = np.cov(np.transpose(ds_1))
    SW = SW_0 + SW_1
    SW_inv = np.linalg.inv(SW)
    w = np.dot(SW_inv, (m1-m0))
    wT = np.transpose(w)
    y_0 = []
    y_1 = []
    for i in range(len(ds_0)):
        y_0.append(np.dot(wT, ds_0[i, :]))
    for i in range(len(ds_1)):
        y_1.append(np.dot(wT, ds_1[i, :]))

    fig1, ax1 = plt.subplots()
    ax1.scatter(y_0, np.zeros(np.shape(y_0)), s=1,
                color='r', label="Points in Class 0")
    ax1.scatter(y_1, np.zeros(np.shape(y_1)), s=1,
                color='g', label="Points in Class 1")
    ax1.legend(loc='upper right')
    plt.title("One Dimension transformation for dataset "+str(s))

    mu_0 = np.mean(y_0)
    std_0 = np.std(y_0)
    xmin, xmax = plt.xlim()
    x = np.linspace(xmin, xmax, 10000)
    p_0 = norm.pdf(x, mu_0, std_0)
    fig2, ax2 = plt.subplots()
    ax2.set_xlim([-15, 20])
    ax2.set_ylim([0, 0.7])
    ax2.plot(x, p_0, 'k', linewidth=2, color='red', label="Class 0")
    mu_1 = np.mean(y_1)
    std_1 = np.std(y_1)
    x = np.linspace(xmin, xmax, 10000)
    p_1 = norm.pdf(x, mu_1, std_1)
    ax2.plot(x, p_1, 'k', linewidth=2, color='green', label="Class 1")
    ax2.legend(loc='upper right')
    intr = intersection(p_0, p_1, x)
    misclass = 0
    if mu_0 < intr:
        for y in y_0:
            if y > intr:
                misclass = misclass+1
        for y in y_1:
            if y < intr:
                misclass = misclass+1
    else:
        for y in y_0:
            if y < intr:
                misclass = misclass+1
        for y in y_1:
            if y > intr:
                misclass = misclass+1
    plt.title("Normal curve for dataset "+str(s)+"\nThreshold is "+str(intr))
    print("Threshold values is", intr)
    print("Accuracy :", 100-(misclass*100/row))
    plt.show()

def main():
    ds1 = pd.read_csv("./../../datasets/a1_d1.csv", header=None)
    ds2 = pd.read_csv("./../../datasets/a1_d2.csv", header=None)
    FisherLDA(ds1, 1)
    FisherLDA(ds2, 2)

main()


import sys
if 'autograd.numpy' not in sys.modules:
    import numpy as np
from qubiter.BitVector import *

class HadamardTransform:
    """
    This class contains only static methods and no constructor. Its
    functions are related to the Hadamard Transform.

    """

    @staticmethod
    def ht(num_qbits, in_arr):
        """
        This function calculates the Hadamard transform of in_arr. Let H be
        the 2 dimensional Hadamard matrix and let ht be the num_bit-fold
        tensor product of H. Then this function returns the matrix product
        ht*in_arr = out_arr. in_arr and out_arr both have the same shape (
        2^num_qbits,).

        Parameters
        ----------
        num_qbits : int
            The number of bits. The dimension of the Hadamard tranform is
            2^num_qbits

        in_arr : np.ndarray
            Input array.

        Returns
        -------
        np.ndarray

        """
        length = (1 << num_qbits)
        half_len = (length >> 1)
        assert len(in_arr) == length, \
            "in_arr for Hadamard Transform has wrong length"
        prev_arr = np.zeros(length, dtype=float)
        root2 = np.sqrt(2)
        out_arr = np.copy(in_arr)
        for beta in range(num_qbits):
            prev_arr[:] = out_arr[:]
            for k in range(half_len):
                x = prev_arr[2*k]
                y = prev_arr[2*k+1]
                out_arr[k] = (x + y)/root2
                out_arr[half_len+k] = (x - y)/root2
        return out_arr

    @staticmethod
    def hadamard_mat(num_qbits, is_quantum=True):
        """
        This function return a numpy array with the num_qbits-fold tensor
        product of the 2 dim Hadamard matrix H. If is_quantum=True (False,
        resp.), it returns a complex (real, resp.) array.

        Parameters
        ----------
        num_qbits : int
        is_quantum : bool

        Returns
        -------

        """
        num_rows = (1 << num_qbits)
        norma = np.sqrt(num_rows)
        if is_quantum:
            ty = complex
        else:
            ty = float
        mat = np.full((num_rows, num_rows),
                      fill_value=1/norma, dtype=ty)
        bvec = BitVector(num_qbits, 0)
        for j in range(num_rows):
            for k in range(num_rows):
                bvec.dec_rep = j & k
                if bvec.get_num_T_bits() % 2 == 1:
                    mat[j, k] = - mat[j, k]
        return mat

if __name__ == "__main__":
    def main():
        num_qbits = 3
        length = 1 << num_qbits
        in_arr = np.random.rand(length) - 0.5
        out_arr = HadamardTransform.ht(num_qbits, in_arr)
        in_arr1 = HadamardTransform.ht(num_qbits, out_arr)
        # print("in_arr=", in_arr)
        # print("out_arr=", out_arr)
        err = np.linalg.norm(in_arr-in_arr1)
        print("error=", err)

        print(HadamardTransform.hadamard_mat(2))
    main()


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

alpha, beta = 0.4, 0.5
def f(x1, x2):
    return alpha * np.log(x1) + beta * np.log(x2)

p1, p2, m = 1.0, 1.2, 4
def budget_line(x1):
    return (m - p1 * x1) / p2

x1star = (alpha / (alpha + beta)) * (m / p1)
x2star = budget_line(x1star)
maxval = f(x1star, x2star)

xgrid = np.linspace(1e-2, 4, 50)
ygrid = xgrid

# === plot value function === #
fig, ax = plt.subplots(figsize=(8,6))
x, y = np.meshgrid(xgrid, ygrid)

if 1:
    ax.plot(xgrid, budget_line(xgrid), 'k-', lw=2, alpha=0.8)
    #ax.fill_between(xgrid, xgrid * 0.0, budget_line(xgrid), facecolor='blue', alpha=0.3)

if 0:  # Annotate with text
    ax.text(1, 1, r'$p_1 x_1 + p_2 x_2 < m$', fontsize=16)

    ax.annotate(r'$p_1 x_1 + p_2 x_2 = m$',
             xy=(2, budget_line(2)),
             xycoords='data',
             xytext=(40, 40),
             textcoords='offset points',
             fontsize=16,
             arrowprops=dict(arrowstyle="->"))

if 1:  # Add maximizer
    ax.annotate(r'$(x_1^*, x_2^*)$',
             xy=(x1star, x2star),
             xycoords='data',
             xytext=(30, 30),
             textcoords='offset points',
             fontsize=16,
             arrowprops=dict(arrowstyle="->"))
    ax.plot([x1star], [x2star],  'ro', alpha=0.6)

if 1:  # Plot with contours
    #points = [-10, -2, -1, 0, 0.4, 0.6, 0.8, 1.0, 1.2, 4]
    points = [-10, -2, -1, 0, 0.6, 1.0, 1.2, 4]
    ax.contourf(x, y, f(x, y), points, cmap=cm.jet, alpha=0.5)
    cs = ax.contour(x, y, f(x, y), points, colors='k', linewidth=2, alpha=0.7,
            antialias=True)
    plt.clabel(cs, inline=1, fontsize=12)

ax.set_xlim(0, 4)
ax.set_ylim(0, 4)
ax.set_xticks((0.0, 1.0, 2.0, 3.0))
ax.set_yticks((1.0, 2.0, 3.0))
ax.set_xlabel(r'$x_1$', fontsize=16)
ax.set_ylabel(r'$x_2$', fontsize=16)
plt.show()


# -*- coding: utf-8 -*-
"""
Making array of zeros without numpy: https://stackoverflow.com/questions/4056768/how-to-declare-array-of-zeros-in-python-or-an-array-of-a-certain-size
"""
from pylab import *
from numpy import *
import scipy as sp
import time

C = array([[4,12,-16],[12,37,-43],[-16,-43,98]]) # Test matrix from wikipedia page
random.seed(20)

n = 300
E = random.rand(n,n)*random.random()
#D = 0.5*(D+transpose(D))
E = matmul(E,transpose(E)) # Generating random positive definite matrix

print('Randomly generated matrix E is',E)

test = linalg.cholesky(E)

def choleskymanual(A):
#    L=[[0]]*len(A) # Numpy independent method
#    for i in range(len(A)):
#        L[i]=L[i]*len(A)
    L = zeros((len(A),len(A)))
    for i in range(len(A)):
        for j in range(i+1):
            lsum=0
            for k in range(j):
                lsum+=sum(L[i,k]*conj(L[j,k]))
            if i>j:
                L[i,j]=(1/L[j,j])*(A[i,j]-lsum)
            else:
                L[j,j]=sqrt(A[j,j]-lsum)
    return L

def cmLDL(A):
    L = zeros((len(A),len(A)))
    D = zeros((len(A),len(A)))
    for i in range(len(A)):
        for j in range(i+1):
            lsum=0
            lsum2=0 # Require seperate sums as the calculations for elements of D and L use different elements from matrix L
            for k in range(j):
                lsum+=L[j,k]*conj(L[j,k])*D[k,k]
                lsum2+=L[i,k]*conj(L[j,k])*D[k,k]
            D[j,j]=A[j,j]-lsum
#            if i>j:
            L[i,j]=(1/D[j,j])*(A[i,j]-lsum2)
    return L,D

L=choleskymanual(E)
LD,D=cmLDL(E)
#print(L)
Ls=conj(transpose(L))
LDs=conj(transpose(LD))
Anew=matmul(L,Ls)
ADnew=matmul(LD,matmul(D,LDs))

print('L matrix from LL decomposition:',L)
print('L matrix from LDL decomposition:',LD)
print('Diagonal elements from D matrix from LDL decomposition:',diag(D))

#%%
"""
1st Application: Matrix Inversion
"""
start = time.time()
# How to get inverse: [A]][Ainv]=[LL*][Ainv]=I
# Print statements for troubleshooting
def cholinverse(A):
    L=choleskymanual(A)
    Ls=conj(transpose(L))
    I = identity((len(L)))
    U = zeros([len(A),len(A)])
    Ainv = zeros([len(A),len(A)])
    for i in range(len(U)):
        for j in range(i+1):
            if i==j:
                U[i,i]=(I[i,i]/L[i,i])
                Ainv[i,i]=(U[i,i]/Ls[i,i])
#    print(U)
#    print(Ainv)
    for i in range(len(U)):
#        print('i=',i)
#        lsum=0
        for j in range(i+1):
#            print('j=',j)
            lsum=0
            for k in range(i):
                lsum+=L[i,k]*U[k,j]
#                print('k=',k)
#                print(lsum)
#            print(lsum)
#            print(U)
            if i>j:
                U[i,j]=(-lsum)/L[i,i]
    for i in range(len(U)-1,-1,-1):
#        print('i=',i)
#        lsum=0
        for j in range(i,-len(U),-1):
#            print('j=',j)
            lsum=0
            for k in range(i-1,-len(U),-1):
                lsum+=Ls[i,k]*Ainv[k,j]
#                print('k=',k)
#                print(lsum)
#            print(lsum)
#            print(Ainv)
#            if i>j:
            Ainv[i,j]=(U[i,j]-lsum)/Ls[i,i]
            Ainv[j,i]=(U[i,j]-lsum)/Ls[i,i]
    return U,Ainv

AI = linalg.inv(E)
SA  = cholinverse(E)
print('The matrix inversion from numpy.linalg is')
print(AI)
print('The matrix inversion from manual method is')
print(SA[1])

ratioinv=AI/SA[1]
ratioinvf=array([])
for i in range(len(ratioinv)):
    o=mean(ratioinv[i])
    ratioinvf=append(ratioinvf,o)

ratioinvf=mean(ratioinvf)

print('The average ratio between the numpy and manual method at n =',n,' for matrix inversion is',ratioinvf)

#%%
"""
2nd Application: Monte-Carlo Simulation - Corellating unrelated random variables
"""

ranvar = random.normal(0,1,(n,100000)) # Generating n amount of variables for 2 variables from a gaussian distribution
#ranvar[1]=ranvar[1]*10000
#random.seed(40)
#ranvar1 = random.normal(0,1,100000)
#random.seed(60)
#ranvar2 = random.normal(0,1,100000)*10000
#ranvart=zeros(2,dtype=object)
#ranvart[0],ranvart[1]=muarray,ranvar2
#Imat = [[0.6,1],[1,0.4]]

#covmat = matmul(transpose(C),C)
covmat = matmul(transpose(E),E)

L = choleskymanual(covmat)

xcorr = dot(L,ranvar)
#xstd = sqrt(diag(covmat))

plot(abs(xcorr[0]),abs(xcorr[n-2]),'.')
grid()
xlabel('Correlated data set for variable x0')
ylabel('Correlated data set for variable x%s'%(n-2))
title('Correlation between correlated variables x0 and x%s'%(n-2))
figure()
plot(ranvar[0],ranvar[n-1],'.')
grid()
xlabel('Uncorrelated data set for variable x0')
ylabel('Uncorrelated data set for variable x%s'%(n-2))
title('Correlation between uncorrelated variables x0 and x%s'%(n-2))
#%%
"""
3rd Application: Least-Squares

Because I've already computed the inverse of the matrix via Cholesky, I can simply take Ax=b -> x=Ainvb to get the system of unknown variables x
"""
#B=[0,1,2] # Use for C matrix or other random 3x3 matrix
B = random.rand(n,1)

X = matmul(cholinverse(E)[1],B)
print('The linear equation for a solution')
print(B)
print('via matrix inversion from cholesky gives vector x =')
print(X)

"""
Or I  can use linalg.solve
"""
X = linalg.solve(E,B)
print('The linear equation for a solution')
print(B)
print('via linalg.solve from cholesky gives vector x =')
print(X)

"""
Or I can calculate it more explicitly.

This is a modification of the cholinverse function since they both use back/forward substitution
Ax=b -> A*Ax=A*b -> (LLs)x=A*b -> (L)u=A*b
"""

def cholLS(A,b):
    posdef = matmul(transpose(A),A)
    newb=matmul(transpose(A),b)
    L=choleskymanual(posdef)
    Ls=conj(transpose(L))
    U = zeros([len(b)])
    X = zeros([len(b)])
    for i in range(len(A)):
        U[i]=(newb[i]-dot((L[i]),U))/L[i,i]
    for i in range(len(A)-1,-1,-1):
        X[i]=(U[i]-dot(Ls[i],X))/Ls[i,i]
    return X

print('The linear equation for a solution')
print(B)

Y = cholLS(E,B)

print('via least squares linear regression from cholesky gives vector x =')
print(Y)

ratiols=transpose(X)/Y
ratiolsf=array([])
for i in range(len(ratiols)):
    o=mean(ratiols[i])
    ratiolsf=append(ratiolsf,o)

ratiolsf=mean(ratiolsf)

print('The average ratio between the numpy and manual method at n =',n,' for matrix inversion is',ratioinvf)
print('The average ratio between the numpy and manual method at n =',n,' for least squares is',ratiolsf)

end = time.time()
print('Total time taken to run code = ',end - start)


import numpy as np

if __name__ == "__main__":
    # initializing matrices
    x = np.array([[1, 2], [4, 5]])
    y = np.array([[7, 8], [9, 10]])

    # element wise addition
    print("The element wise addition of matrix is : ")
    print(np.add(x, y))

    # element wise subtraction
    print("The element wise subtraction of matrix is : ")
    print(np.subtract(x, y))

    # element wise division
    print("The element wise division of matrix is : ")
    print(np.divide(x, y))

    # element wise multiplication
    print("The element wise multiplication of matrix is : ")
    print(np.multiply(x, y))

    # matrix multiplication
    print("Matrix multiplication is : ")
    print(np.dot(x, y))

    # matrix transpose
    print("Matrix transpose is : ")
    print(np.transpose(x))

    # matrix inverse
    print("Matrix inverse is : ")
    print(np.linalg.inv(x))


import numpy as np
import matplotlib.pyplot as plt

x_data = [ 338., 333., 328., 207., 226., 25., 179., 60., 208.,  606. ]
y_data = [ 640., 633., 619., 393., 428., 27., 193., 66., 226., 1591. ]
# y_data = w * x_data + bias

x = np.arange(-200, -100, 1) # bias
y = np.arange(-5, 5, 0.1) # weight
Z = np.zeros((len(x), len(y)))
X, Y = np.meshgrid(x, y)

for i in range(len(x)):
    for j in range((len(y))):
        b = x[i]
        w = y[j]
        Z[j][i] = 0
        for n in range(len(x_data)):
           Z[j][i] = Z[j][i] +(y_data[n] - b - w*x_data[n])**2
        Z[j][i] = Z[j][i]/len(x_data)

b = -129 # intialize b
w = -4 # intialize w
lr = 0.0000001 # learning rate
iteration = 100000

# Store intial values for plotting
b_history = [b]
w_history = [w]

# Iteration
for i in range(iteration):
    b_grad = 0.0
    w_grad = 0.0
    for n in range(len(x_data)):
        b_grad = b_grad - 2.0*(y_data[n] - b - w*x_data[n])*1.0
        w_grad = w_grad - 2.0*(y_data[n] - b - w*x_data[n])*x_data[n]

    # Update parameters
    b = b - lr * b_grad
    w = w - lr * w_grad

    # Store the parameters for plotting
    b_history.append(b)
    w_history.append(w)

# plot the figure
plt.contour(x, y, Z, 50, alpha=0.5, cmap=plt.get_cmap('jet'))
plt.plot([-188.4], [2.67], 'x', ms=12, markeredgewidth=3, color='orange')
plt.plot(b_history, w_history, 'o-', ms=3, lw=1.5, color='black')
plt.xlim(-200, -100)
plt.ylim(-5,5)
plt.xlabel(r'$b$', fontsize=16)
plt.ylabel(r'$w$', fontsize=16)
plt.show()

## Change Learning_Rate to Learning_Rate*10
b = -129 # intialize b
w = -4 # intialize w
lr = 0.000001 # learning rate
iteration = 100000

# Store intial values for plotting
b_history = [b]
w_history = [w]

# Iteration
for i in range(iteration):
    b_grad = 0.0
    w_grad = 0.0
    for n in range(len(x_data)):
        b_grad = b_grad - 2.0*(y_data[n] - b - w*x_data[n])*1.0
        w_grad = w_grad - 2.0*(y_data[n] - b - w*x_data[n])*x_data[n]

    # Update parameters
    b = b - lr * b_grad
    w = w - lr * w_grad

    # Store the parameters for plotting
    b_history.append(b)
    w_history.append(w)

# plot the figure
plt.contour(x, y, Z, 50, alpha=0.5, cmap=plt.get_cmap('jet'))
plt.plot([-188.4], [2.67], 'x', ms=12, markeredgewidth=3, color='orange')
plt.plot(b_history, w_history, 'o-', ms=3, lw=1.5, color='black')
plt.xlim(-200, -100)
plt.ylim(-5,5)
plt.xlabel(r'$b$', fontsize=16)
plt.ylabel(r'$w$', fontsize=16)
plt.show()

## Change Learning_Rate to Learning_Rate*10
b = -129 # intialize b
w = -4 # intialize w
lr = 0.00001 # learning rate
iteration = 100000

# Store intial values for plotting
b_history = [b]
w_history = [w]

# Iteration
for i in range(iteration):
    b_grad = 0.0
    w_grad = 0.0
    for n in range(len(x_data)):
        b_grad = b_grad - 2.0*(y_data[n] - b - w*x_data[n])*1.0
        w_grad = w_grad - 2.0*(y_data[n] - b - w*x_data[n])*x_data[n]

    # Update parameters
    b = b - lr * b_grad
    w = w - lr * w_grad

    # Store the parameters for plotting
    b_history.append(b)
    w_history.append(w)

# plot the figure
plt.contour(x, y, Z, 50, alpha=0.5, cmap=plt.get_cmap('jet'))
plt.plot([-188.4], [2.67], 'x', ms=12, markeredgewidth=3, color='orange')
plt.plot(b_history, w_history, 'o-', ms=3, lw=1.5, color='black')
plt.xlim(-200, -100)
plt.ylim(-5,5)
plt.xlabel(r'$b$', fontsize=16)
plt.ylabel(r'$w$', fontsize=16)
plt.show()

    ## Using adagrad to solve the problem
'''
If increasing the iteration to speed up the rate of convergence, that would spped down actually.
Cause it have to calculate gradient at each iteration, but that would cost lots of time.
Therefore, we modify the learning rate, and change improve rate of convergence at meantime.
'''
b = -129
w = -4
lr = 1
iteration = 100000

b_lr = 0.0
w_lr = 0.0

# Store initial values for plotting
w_history = [w]
b_history = [b]

# Iteration
for i in range(iteration):
    b_grad = 0.0
    w_grad = 0.0
    for n in range(len(x_data)):
        b_grad = b_grad - 2.0*(y_data[n] - b - w*x_data[n])*1.0
        w_grad = w_grad - 2.0*(y_data[n] - b - w*x_data[n])*x_data[n]

    # Add
    b_lr = b_lr + b_grad ** 2
    w_lr = w_lr + w_grad ** 2

    # Update parameters
    b = b - lr /np.sqrt(b_lr) * b_grad
    w = w - lr /np.sqrt(w_lr) * w_grad

    # Store the parameters for plotting
    b_history.append(b)
    w_history.append(w)

# plot the figure
plt.contour(x, y, Z, 50, alpha=0.5, cmap=plt.get_cmap('jet'))
plt.plot([-188.4], [2.67], 'x', ms=12, markeredgewidth=3, color='orange')
plt.plot(b_history, w_history, 'o-', ms=3, lw=1.5, color='black')
plt.xlim(-200, -100)
plt.ylim(-5,5)
plt.xlabel(r'$b$', fontsize=16)
plt.ylabel(r'$w$', fontsize=16)
plt.show()


import numpy as np
import scipy as sp
from matplotlib import pyplot as plt

import shapes

#Prob 1

def polarZ(z):
	if(z == 0):
		return (0,0)
	else :
		a = z.real
		b = z.imag
		return( sp.hypot(a,b), sp.arctan(b/a))

#Prob 2

def fFunc(z):
	return (3*z - 4)/(z-2)

def gFunc(z):
	return z**2 - 2
def confMap(shape,mapfunc):
	shapemapped = [None]*len(shape)
	for i in range(0,len(shape)):
		shapemapped[i] = mapfunc(shape[i])

	plt.scatter(sp.real(shape),sp.imag(shape),color='r')
	plt.scatter(sp.real(shapemapped),sp.imag(shapemapped),color='b')
	plt . show ()

'''
Examples:

triangle = shapes.genRTriangle(1.,1.,0.,0.,100)
confMap(triangle,fFunc)

box = shapes.genBox(1.,1.,0.,0.,400)
confMap(box,gFunc)

circle = shapes.genCircle(1.,1.,0.,0.,400)
confMap(circle,gFunc)
'''

#Prob 3

def testHolo(func,a,b,r,tol):
	circle = shapes.genCircle(r,a,b,400)
	vals = func(circle)
	diff = max(vals) - min(vals)
	if(diff < tol ):
		return "differentiable"
	else:
		return "not differentiable {0}".format(diff)
#my solution apparently didn't work, it says that all the functions are not differentiable

print testHolo(lambda z: sp.real(z),1,1,0.01,1)
print testHolo(lambda z: sp.absolute(z),1,1,0.01,1)
print testHolo(lambda z: sp.conj(z),1,0,0.01,1)
print testHolo(lambda z: z*sp.real(z),0,0,0.01,1)
print testHolo(lambda z: z*sp.real(z),1,1,0.01,1)
print testHolo(lambda z: sp.exp(z),0,np.pi,0.01,1)

print testHolo(lambda z: sp.sin(sp.real(z))*sp.cosh(sp.imag(z))+
	sp.cos(sp.real(z))*sp.sinh(sp.imag(z))*1j ,0,np.pi,0.01,1)
print testHolo(lambda z: sp.sin(sp.real(z))*sp.cosh(sp.imag(z))
	-sp.cos(sp.real(z))*sp.sinh(sp.imag(z))*1j,0,np.pi,0.01,1)


# -*- coding: utf-8 -*-
"""
Interpolaciones de diferente orden para la funcion

  2*exp(x) + sin(3*x)

"""
from __future__ import division, print_function
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import lagrange
import sympy as sym

plt.rcParams["axes.spines.right"] = False
plt.rcParams["axes.spines.top"] = False
plt.rcParams["mathtext.fontset"] = "cm"

def base_lagrange(x_datos, var, cont):
    """Crea un polinomio base de Lagrange para los datos x"""
    prod = sym.prod((var - x_datos[i])/(x_datos[cont] - x_datos[i])
                    for i in range(len(x_datos)) if i != cont)

    return sym.lambdify(var, sym.simplify(prod), "numpy")

def deriv_base_lagrange(x_datos, var, cont):
    """Crea un polinomio base de Lagrange para los datos x"""
    prod = sym.prod((var - x_datos[i])/(x_datos[cont] - x_datos[i])
                    for i in range(len(x_datos)) if i != cont)
    return sym.lambdify(var, sym.simplify(prod.diff(var)), "numpy")

fun = lambda x: 2*np.exp(x) + np.sin(3*x)
grad = lambda x: 2*np.exp(x) + 3*np.cos(3*x)
x = np.linspace(-1, 1, 101)
y = fun(x)
dy = grad(x)

plt.close("all")
plt.figure(figsize=(5, 6))

npts = [2, 3, 5]
for cont in range(3):
    ax = plt.subplot(3, 2, 2*cont + 1)
    if cont==0:
        plt.title("Interpolación")
    x_inter = np.linspace(-1, 1, npts[cont])
    y_inter = fun(x_inter)
    f_inter = lagrange(x_inter, y_inter)
    plt.plot(x, y)
    plt.plot(x, f_inter(x), linestyle="dashed",
             label="Orden {}".format(npts[cont] - 1))
    plt.plot(x_inter, y_inter, "ok")
    plt.ylabel("$y$", fontsize=14)
    plt.legend(frameon=False)
    if cont != 2:
        ax.xaxis.set_ticks([])
        ax.spines["bottom"].set_color("none")
    if cont == 2:
        plt.xlabel("$x$", fontsize=14)

for cont in range(3):
    ax = plt.subplot(3, 2, 2*cont + 2)
    if cont==0:
        plt.title("Funciones base")
    for cont_base in range(npts[cont]):
        x_inter = np.linspace(-1, 1, npts[cont])
        base = base_lagrange(x_inter, sym.symbols("x"), cont_base)
        y_base = base(x)
        plt.plot(x, y_base)
        plt.ylim(-0.6, 1.2)
        plt.yticks(np.linspace(-0.5, 1, 4))
        plt.ylabel("$y$", fontsize=14)
    if cont != 2:
        ax.xaxis.set_ticks([])
        ax.spines["bottom"].set_color("none")
    if cont == 2:
        plt.xlabel("$x$", fontsize=14)

plt.tight_layout()
plt.savefig("interp_multiple.pdf", bbox_inches="tight", transparent=True)

#%% Derivadas
plt.figure(figsize=(5, 2.5))
plt.subplot(122)
plt.title("Funciones base")
x_inter = np.linspace(-1, 1, 5)
y_inter = fun(x_inter)
dy_inter = np.zeros_like(x)
for cont_base in range(5):
    deriv_base = deriv_base_lagrange(x_inter, sym.symbols("x"), cont_base)
    y_base = deriv_base(x)
    dy_inter += y_base * y_inter[cont_base]
    plt.plot(x, y_base)
    plt.xlabel("$x$", fontsize=14)
    plt.ylabel("$y$", fontsize=14)

plt.subplot(121)
plt.title("Derivadas")
plt.plot(x, dy)
plt.plot(x, dy_inter, linestyle="dashed")
plt.plot(x_inter, grad(x_inter), "ok")
plt.xlabel("$x$", fontsize=14)
plt.ylabel("$y$", fontsize=14)
plt.tight_layout()
plt.savefig("interp_multiple_deriv.pdf", bbox_inches="tight", transparent=True)
#plt.show()


"""
**shape**

The shape tool gives a tuple of array dimensions and can be used to change the dimensions of an array.
(a). Using shape to get array dimensions

import numpy

my__1D_aaray = numpy.array([1, 2, 3, 4, 5])
print my_1D_array.shape #(5,) -> 5 rows and 0 columns

my__2D_array = numpy.array([[1,2],[3,4], [6,5]])
print my_2D_aaray.shape    #(3,2) -> 3 rows and 2 columns

(b). Using shape to change array dimensions

import numpy

change_array = numpy.array([1,2,3,4,5,6])
change_array.shape = (3,2)
print(change_array)

# Output
[[1 2]
[3 4]
[5 6]]

reshape

The reshape tool gives a new shape to an array without
changing its data. It creates a new array and does not modify
the origin array itself.

import numpy

my_array = numpy.array([1,2,3,4,5,6])
print numpy.reshape(my_array, (3,2))

# Output
[[1 2]
[3 4]
[5 6]]

Task

"""

import numpy as np

# my solution
my_array = np.array([1,2,3,4,5,6,7,8,9])
my_array.shape = (3,3)
print(my_array)

# solution 2
print(np.array(input().split(), int).reshape(3,3))

# solution 3

import numpy as np
a=np.array(list(map(int,input().split())))
a.shape=(3,3)
print(a)

# solution 4

import numpy

print (numpy.fromstring(input(), dtype=int, sep=" ").reshape(3,3))