import numpy as np
import scipy

from ... import operators
#------------------------------------------
def ar_levenson_durbin(x, order,mode='same',unbias = False):
    '''
    The autoregressive model approximation,
        based on the Levenson-Dubrin itterative method
        for solution toeplitz matrix equations.

    Parameters
    -------------------
    * x: is 1-d input ndarray.
    * order: int,
        is the order of the desired model.
    * mode: string,
        mode of correlation function,
        mode = {full, same, straight}.
    * unbias: bool,
        if True, the unbiased autocorrleation
         function will be taken.

    Returns
    ---------------------
    * a: 1d ndarray,
        autoregression coefficients,
    * noise_variace: float or complex,
        variance of model residulas.

    See also
    ------------
    yule_walker,
    lsar,
    covar,
    burg.

    Examples
    -----------

    References
    --------------------
    [1a] Stoica, Petre, and Randolph L. Moses.
            "Spectral analysis of signals." (2005).
    [1b] http://www2.ece.ohio-state.edu/~randy/SAtext/
    - Dr.Moses Spectral Analysis of Signals: Resource Page
    [2a] M.H. Hayes. Statistical Digital Signal Processing
                        and Modeling, John Wiley & Sons, 1996.
    [2b] https://www.mathworks.com/matlabcentral/fileexchange/
        2183-statistical-digital-signal-processing-and-modeling
    [3]  S.L. Marple, Digital spectral analysis with applications.
                                    – New-York: Present-Hall, 1986.

    '''
    x = np.asarray(x)
    N = x.shape[0]

    r  = operators.correlation(x,y=None,mode=mode,
                        take_mean=False,unbias=unbias)

    a = np.zeros((order,), x.dtype)
    var  = r[0] - (r[1] *np.conj(r[1]))/r[0]
    a[0] = -r[1] / r[0]

    for i in np.arange(1,order):
        k   = -(r[i+1] + np.sum(a[:i]*r[i:0:-1]))/var #r[i:0:-1]=np.flipud(r[1:i+1])
        var = var*(1-(k *np.conj(k)))

        # same as a[:i+1] = [a,0] + k[a~,1] in Stoic
        a[:i] = a[:i] + k*np.conj(a[i-1::-1])
        a[i]  = k

    # here is sign "-" is already taken
    a = np.append(1,a)

    return a, var


"""
Newton (Polynomial Interpolation)
"""

import numpy as np

def newton(X, Y):
    """
    Polynomial Interpolation using Newton's method

    Parameters
    ------------
        X : list
            list of x values
        Y : list
            list of y values
    Returns
    -------
        function
            function of polynomial interpolation (using  newton's method)
    """
    n = len(X)
    K = np.zeros((n, n))
    K[:, 0] = Y

    for k in range(1, n):
        K[k:, k] = [(K[i, k - 1] - K[i - 1, k - 1]) / (X[i] - X[i - k]) for i in range(k, K.shape[0])]
    D = np.ndarray.tolist(np.diag(K))

    def f(x):
        A = np.ones((n))
        A[1: n] = [(x - X[k]) for k in range(n - 1)]
        return sum(D[i] * np.prod(A[: i + 1]) for i in range(n))

    return f


"""
This example produces a sequence of samples for the uniform distribution
U([0,1]) using a linear congruential generator (LCG). A description of
the method can be found at
https://en.wikipedia.org/wiki/Linear_congruential_generator.
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
from matplotlib.ticker import MaxNLocator

rc('font', **{'size':12, 'family':'serif', 'serif':['Computer Modern Roman']})
rc('text', usetex=True)

# Parameters for LCG https://en.wikipedia.org/wiki/Linear_congruential_generator
X_0 = 0
M = np.power(2, 32)
A = 1664525
C = 1013904223

NUM_DRAWS = 1000000
NUM_BINS = 100

PATH = 'uniform_samples.pdf'

def compute_uniform_samples():
	"""
	Random numbers are computed according to the formula X_{n+1} = (A*X_{N} + C) mod M.
	Uniform Samples U_{i} are then computed as U_{i} = X_{i} / M for i = [1, NUM_DRAWS]
	"""
	X_values = np.zeros(NUM_DRAWS)
	X_values[0] = X_0

	for i in range(0, (NUM_DRAWS-1)):
		X_values[i+1] = (A*X_values[i] + C) % M

	return (X_values/M)

def visualization(samples):
	"""
	Plotting the draws in form of a histogram clusterd in NUM_BINS bins and saving figure.
	"""
	n, bins, patches = plt.hist(samples, NUM_BINS, color='white', histtype='bar', ec='black')

	ax = plt.gca()
	ax.set_xlim([0,1])

	ax.yaxis.set_major_locator(MaxNLocator(integer=True, prune='lower'))

	plt.xlabel(r'Drawn values')
	plt.ylabel(r'Frequency')

	plt.savefig(PATH, bbox_inches='tight')
	plt.show()

def main():
	samples = compute_uniform_samples()
	visualization(samples)

if __name__ == "__main__":
	main()


"""
 __  __             _          ____           _
|  \/  | ___  _ __ | |_ ___   / ___|__ _ _ __| | ___
| |\/| |/ _ \| '_ \| __/ _ \ | |   / _` | '__| |/ _ \
| |  | | (_) | | | | ||  __/ | |__| (_| | |  | | (_) |
|_|  |_|\___/|_| |_|\__\___|  \____\__,_|_|  |_|\___/

@author: Jonathan Wang
@coding: utf-8
@environment: Manjaro 18.1.5 Juhraya
@date: 15th Jan., 2020

"""

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Circle

n = 100000
r = 1.0
a, b = 0.0, 0.0

y_max, y_min, x_max, x_min = b + r, b - r, a + r, a - r

x = np.random.uniform(x_min, x_max, n)
y = np.random.uniform(y_min, y_max, n)

distance = np.sqrt((x - a)**2 + (y - b)**2)
cnt = np.sum(np.where(distance <= r, 1, 0))

pi = 4 * cnt / n
print("Pi = ", pi)

fig = plt.figure(figsize=(12, 12))
ax = fig.add_subplot(1, 1, 1)
plt.scatter(x, y, marker='.', s=10, alpha=0.5)
circle = Circle(xy=(a, b), radius=r, alpha=0.5, color='green')
ax.add_patch(circle)
ax.grid(color='k', linestyle='--', alpha=0.2, linewidth=2)

plt.show()


# Marsaglia and Bray method
import matplotlib.pyplot as plt #pip install matplotlib
import numpy as np #pip install numpy
import random
import math
import statistics
import time

#each of this corresponds to a list where nxy_z is z values of N(x,y) distribution
n01_100=[]
n01_10000=[]
n05_100=[]
n05_10000=[]
n55_100=[]
n55_10000=[]
def find_values_N01(): #this function uses the Marsaglia and Bray algorithm and generates 100 and 10000 values of N(0,1)
    count_acc=0
    count_rej=0
    while(1):
        u1=random.uniform(0,1)
        u2=random.uniform(0,1)
        u1=2*u1-1
        u2=2*u2-1
        x=u1**2+u2**2
        if(x>1):
            count_rej=count_rej+1
            continue
        else:
            count_acc=count_acc+1
            y=math.sqrt(-2*math.log(x)/x)
            z1=u1*y
            z2=u2*y
            if(count_acc%2==0):
                n01_100.append(z1)
            else:
                n01_100.append(z2)
            if(count_acc==100):
                break
    print('For 100 values the rejected proportion is ',count_rej/(count_rej+count_acc))
    count_acc=0
    count_rej=0
    while(1):
        u1=random.uniform(0,1)
        u2=random.uniform(0,1)
        u1=2*u1-1
        u2=2*u2-1
        x=u1**2+u2**2
        if(x>1):
            count_rej=count_rej+1
            continue
        else:
            count_acc=count_acc+1
            y=math.sqrt(-2*math.log(x)/x)
            z1=u1*y
            z2=u2*y
            if(count_acc%2==0):
                n01_10000.append(z1)
            else:
                n01_10000.append(z2)
            if(count_acc==10000):
                break
    print('For 10000 values the rejected proportion is ',count_rej/(count_rej+count_acc))
    print('The value of 1-pi/4 is ',1-(math.pi/4))
    print('\n')

def q1a_sample_meanvar(): #this function calculates and prints the sample mean and variance
    print('For 100 values')
    print('Sample mean=',statistics.mean(n01_100))
    print('Sample variance= {}\n'.format(statistics.variance(n01_100)))
    print('For 10000 values')
    print('Sample mean=',statistics.mean(n01_10000))
    print('Sample variance=',statistics.variance(n01_10000))

def q1b_plot_freqvsval(l): #this function prints the freq vs x histograms for N(0,1)
    l.sort()
    plt.title('Frequency vs sample values for N(0,1) for {} values'.format(len(l)))
    plt.xlabel('Sample values')
    plt.ylabel('Frequency')
    plt.hist(l,bins=50,rwidth=0.8)
    plt.show()

def q1c_find_n05_and_n55(): #this function finds the values of N(0,5) and N(5,5)
    for i in range(0,100):
        n05_100.append(n01_100[i]*math.sqrt(5))
        n55_100.append(n01_100[i]*math.sqrt(5)+5)
    for i in range(0,10000):
        n05_10000.append(n01_10000[i]*math.sqrt(5))
        n55_10000.append(n01_10000[i]*math.sqrt(5)+5)

def return_pdf_normal(x,neu,sigmasq,bw,lenl): #f(x)=(1/(sigma*sqrt(2*pi))*e^(-1/2*((x-neu)/sigma)^2))
    fx=[]
    for i in range(0,len(x)):
        power_term=-(((x[i]-neu)**2)/sigmasq)/2
        num=math.exp(power_term)
        den=math.sqrt(2*math.pi*sigmasq)
        fx.append(num*bw*lenl/den)
    return fx

def plotandcompare(l,typ): #this function plots the N(0,5) and N(5,5) and compares with the drawn histograms
    l.sort()
    distri=''
    _,bins,_=plt.hist(l,bins=50,rwidth=0.8,label='Observed')
    bw=bins[1]-bins[0]
    x=np.arange(bins[0]-0.5,bins[-1]+0.5,0.1)
    if(typ==0):
        distri='N(0,5)'
        y=return_pdf_normal(x,0,5,len(l),bw)
    else:
        distri='N(5,5)'
        y=return_pdf_normal(x,5,5,len(l),bw)
    plt.plot(x,y,label='Expected-'+distri)
    plt.title('Observed and Expected values for {} vs Sample values'.format(distri))
    plt.xlabel('Sample values')
    plt.ylabel('Frequency')
    plt.legend()
    plt.show()

start=time.time() #start keeping a track of time

print('Marsaglia and Bray method')
#call all functions
find_values_N01()
q1a_sample_meanvar()
q1b_plot_freqvsval(n01_100)
q1b_plot_freqvsval(n01_10000)
q1c_find_n05_and_n55()
plotandcompare(n05_100,0)
plotandcompare(n05_10000,0)
plotandcompare(n55_100,5)
plotandcompare(n55_10000,5)

end=time.time() #end keeping track of time
print(f"Runtime of the program is {end - start}") #print the overall runtime

Semester 5/MA 323 (Monte Carlo)/Lab Submissions/Lab 5/Marsaglia_and_Bray_ABSatyaprakash_180123062.py


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

N = 1000

def get_sample(N):
    x = np.random.beta(0.1,0.1,N)
    return x

s = np.array(sorted(get_sample(N)))
sns.distplot(s, hist=True, kde=False, bins=50)
plt.show()

#%% Seaborn KDE:
sns.kdeplot(s, bw=0.005, clip=[0,1], shade=True)
plt.show()

#%% Manually implement KDE
def gaussian(x, mu, sigma):
    return 1/np.sqrt(2 * np.pi * sigma**2) * np.exp(-1/(2 * sigma**2) * (x - mu)**2)

def areaplot(x, y, ax=None, label=''):
    if ax is None:
        fig, ax = plt.subplots()
    sns.lineplot(x, y, ax=ax, label=label)
    ax.fill_between(x, 0, y, alpha=0.25)

sigma = 0.002
ds = np.concatenate((np.array((0,)), np.diff(s)))

x = np.linspace(0,1,N)
dx = 1/(N-1)

g = np.array(list(map(lambda mu: gaussian(x, mu, sigma), s)))
p = np.sum(g, axis=0)
p /= np.sum(p * dx)
areaplot(x, p, label='uniform spacing')

g_samp = np.array(list(map(lambda mu: gaussian(s, mu, sigma), s)))
p_samp = np.sum(g_samp, axis=0)
p_samp /= np.sum(p_samp*ds)
areaplot(s, p_samp, ax=plt.gca(), label='sampled spacing')
plt.show()

#%% Estimating entropy manually
h_s = -np.sum(p_samp*np.log(p_samp)*ds)

h_x = -np.sum(p*np.log(p)*dx)

K = lambda x: np.exp(-x**2 / (2*sigma**2)) / (sigma * np.sqrt(2*np.pi))

# two sample approach
t = get_sample(1000)
st = (s[np.newaxis] - t[:, np.newaxis])
h_st = np.log(len(t)) - 1/len(s) * np.sum(np.log(np.sum(K(st), axis=1)), axis=0)

# one-sample approach
ss = (s[np.newaxis] - s[:, np.newaxis])
h_ss = np.log(len(s)-1) - 1/len(s) * np.sum(np.log(np.sum(K(ss), axis=1)-K(0)), axis=0)

print('h_s =', h_s)
print('h_x =', h_x)
print('h_st =', h_st)
print('h_ss =', h_ss)

#%% Tuning sigma manually
N = 1000
eps = 1e-6
ss = (s[np.newaxis] - s[:, np.newaxis])
sigmas = np.logspace(-4,0,50)
h_ss = np.zeros_like(sigmas)
for i, sigma in enumerate(sigmas):
    K = lambda x: np.exp(-x**2 / (2*sigma**2)) / (sigma * np.sqrt(2*np.pi))
    h_ss[i] = np.log(len(s)-1) - 1/len(s) * np.sum(np.log(eps + np.sum(K(ss), axis=1)-K(0)), axis=0)
fig, ax = plt.subplots()
ax.semilogx(sigmas, h_ss)

best_idx = np.argmin(h_ss)
print('h_hat =', h_ss[best_idx])
print('sigma =', sigmas[best_idx])

#%% Tuning sigma manually
N = 1000
eps = 1e-6
s = np.random.uniform(0,1,N)
ss = (s[np.newaxis] - s[:, np.newaxis])
sigmas = np.logspace(-4,0,50)
h_ss = np.zeros_like(sigmas)
for i, sigma in enumerate(sigmas):
    K = lambda x: np.exp(-x**2 / (2*sigma**2)) / (sigma * np.sqrt(2*np.pi))
    h_ss[i] = np.log(len(s)-1) - 1/len(s) * np.sum(np.log(eps + np.sum(K(ss), axis=1)-K(0)), axis=0)
fig, ax = plt.subplots()
ax.semilogx(sigmas, h_ss)

best_idx = np.argmin(h_ss)
print('h_true =', np.log(1-0))
print('h_hat =', h_ss[best_idx])
print('sigma =', sigmas[best_idx])

#%% Tuning sigma manually
s = np.random.normal(0,1,N)
eps = 1e-6
ss = (s[np.newaxis] - s[:, np.newaxis])
sigmas = np.logspace(-4,1,50)
h_ss = np.zeros_like(sigmas)
for i, sigma in enumerate(sigmas):
    K = lambda x: np.exp(-x**2 / (2*sigma**2)) / (sigma * np.sqrt(2*np.pi))
    h_ss[i] = np.log(len(s)-1) - 1/len(s) * np.sum(np.log(eps + np.sum(K(ss), axis=1)-K(0)), axis=0)
fig, ax = plt.subplots()
ax.semilogx(sigmas, h_ss)

best_idx = np.argmin(h_ss)
print('h_true =', np.log(np.sqrt(2*np.pi*np.e)))
print('h_hat =', h_ss[best_idx])
print('sigma =', sigmas[best_idx])


import numpy as np
import math

#### Miscellaneous functions and Classes
def f_sigmoid(x):
    """The sigmoid function."""
    return 1.0/(1.0+math.exp(-x))

def df_sigmoid(x):
    """Derivative of the sigmoid function."""
    return f_sigmoid(x)*(1-f_sigmoid(x))

class SigmoidActivation(object):
   '''
      Sigmoid Activation Function. This's the default activation function.
   '''
   @staticmethod
   def f(x):
      return f_sigmoid(x)

   @staticmethod
   def df(x):
      return df_sigmoid(x)

class QuadraticCost(object):
   @staticmethod
   def fn(a, y):
      error = (a-y)
      return 0.5*np.sum(error**2)

   @staticmethod
   def gradient(a, y):
      return (a-y)

class CrossEntropyCost(object):
   #  Implement cross-entropy cost function: (Sum for each output neuron)
   #  C = -sum([yln(a) + (1-y)ln(1-a)])
   @staticmethod
   def fn(a,y):
      return -1*np.sum(y*np.log(a) +
            ((np.ones(y.shape)-y)*np.log(np.ones(a.shape)-a)))

   #gradient(C) = (a-y)/(a(1-a))
   @staticmethod
   def gradient(a,y):
      return (a-y)/(a*(np.ones(a.shape)-a))

'''
On Regularization methods:
   @w: List of NumpyArrays representing the multilayer network
   @n: Number of weights in the Network
'''
class RegularizationNone(object):
   @staticmethod
   def fn(lmbda, n, w):
      return 0

   @staticmethod
   def df(lmbda, n, w):
      return [ np.zeros(wi.shape) for wi in w]

class RegularizationL1(object):
   @staticmethod
   def fn(lmbda, n, w):
      w_sum = 0
      for wi in w:
         w_sum += np.sum(np.absolute(wi))

      return (lmbda*w_sum)/n

   @staticmethod
   def df(lmbda, n, w):
      return [ (lmbda*np.sign(wi))/n for wi in w]

class RegularizationL2(object):
   @staticmethod
   def fn(lmbda, n, w):
      w_sum = 0
      for wi in w:
         w_sum += np.sum(wi**2)

      return (0.5*lmbda*w_sum)/n

   @staticmethod
   def df(lmbda, n, w):
      return [ (lmbda*wi)/n for wi in w]

regularization_available = {
   RegularizationNone.__name__: RegularizationNone,
   RegularizationL1.__name__: RegularizationL1,
   RegularizationL2.__name__: RegularizationL2,
}

activation_function_available = {
  SigmoidActivation.__name__: SigmoidActivation,
}

cost_function_available = {
   QuadraticCost.__name__: QuadraticCost,
   CrossEntropyCost.__name__: CrossEntropyCost,
}

# class_number (0,output_size-1)
def get_one_zero_nn_output(output_size, class_number):
   out = np.zeros(output_size)
   if class_number >= output_size:
      return None

   out[class_number] = 1
   return out


import numpy as np
import numpy.linalg as la
from io import StringIO

def print_mat(mat):

    stream = StringIO()
    np.savetxt(stream, mat, fmt="%.3f")
    print( stream.getvalue() )

# -----------------------------------

def get_convergent_vector(L, r_0, threshold=0.01):

    '''
    :param L: transition matrix
    :param r_0: initial vector
    :param threshold: parameter for convergence condition
    :return: convergent vector
    '''

    r_cur = r_0

    while True:

        r_next = np.matmul(L, r_cur)

        if la.norm(r_next - r_cur) < threshold:
            # check convergence condition is met or not
            break

        r_cur = r_next

    return r_cur

# -----------------------------------
def get_matrix_with_damping(matrix, damping=0.5):

    # get the size of matrix
    n, _ = matrix.shape

    return matrix * damping + ( 1 - damping ) * np.ones((n, n)) / n

# -----------------------------------

# get the size
n = int( input() )

# get the name of website
websites = input().split()

# get transition matrix
matrix = [ [ 0.0 for x in range(n)] for y in range(n) ]

for y in range(n):
    matrix[y] = [ *map( float, input().split() ) ]

# get the name of target website
target = input()

# convert to numpy array
matrix = np.array(matrix)

matrix_with_damping = get_matrix_with_damping(matrix, damping=0.5)

r_0 = ( np.ones(n) / n) * 100

# compute pagerank
r_pagerank = get_convergent_vector(L=matrix_with_damping, r_0=r_0, threshold=0.01)

# output result

web_pagerank_dict = {}
for idx in range(n):
    web_pagerank_dict[ websites[idx] ] = r_pagerank[idx]

# target is always on the top
result = [ target ]

# remove target from dictionary
del web_pagerank_dict[target]

# sorted by pagerank and name of website in ascending order
for website in sorted(web_pagerank_dict, key=lambda w: (web_pagerank_dict[w], w), reverse=True):
    result.append( website )

# output top 5 results
for idx, website in enumerate(result):

    if idx < 5:
        print(website)
    else:
        break


import numpy as np
import math

def float_to_bits(num, exp_b, sig_b):
    sign = ('1' if num<0 else '0')
    num = abs(num)
    bias = (2**(exp_b-1)-1)
    e = (0 if num==0 else math.floor(math.log(num,2)+bias))
    if(e>(2**(exp_b)-2)):#overflow
        exponent = '1'*exp_b
        mantissa = '0'* sig_b
    else:
        if(e>0):#normal
            s = num/2**(e-bias)-1
            exponent = bin(e)[2:].zfill(exp_b)
        else:   #submoral
            s = num/2**(-bias+1)
            exponent = '0'*exp_b
        mantissa = bin(int(s*(2**sig_b)+0.5))[2:].zfill(sig_b)[:sig_b]
    return sign+exponent+mantissa


# Least square method of multivariate linear regression.
# Solution/weight coeffecients = inv(X`X)X`y
# X -> 1 x1 x2 x3 ...
# https://web.stanford.edu/~mrosenfe/soc_meth_proj3/matrix_OLS_NYU_notes.pdf

import numpy as np
import matplotlib.pyplot as plt
from sklearn import preprocessing

class LinearRegression:
    def __init__(self):
        self._X = None
        self._y = None
        self._correlation = []
        self._coeffecients = None

    def _mean(self, arr):
        return np.mean(arr)

    def _diff(self, arr, num):
        return np.subtract(arr, num)

    def fit(self, X, y):
        self._X = np.insert(X, 0, [1], axis=1)
        self._y = y
        self._compute_correlation()
        self._coeffecients = np.dot(np.dot(np.linalg.inv(np.dot(np.transpose(self._X), self._X)), np.transpose(self._X)), self._y)  # inv(X'X)X'y
        train_pred = self.predict(X)
        return self._rmse_error(y, train_pred)

    def _rmse_error(self, y, y_pred):
        return np.sqrt(np.mean(np.subtract(y, y_pred) ** 2))

    def predict(self, input):
        input_feature = np.insert(input, 0, [1], axis=1)
        return np.dot(input_feature, self._coeffecients)

    def weight_coefficients(self, decimal=4):
        return self._coeffecients

    def _compute_correlation(self):
        sum_y = np.sum(self._y)
        n = len(self._y)
        for i in range(1, len(self._X[0]), 1):
            curr_feature = self._X[:, i]
            sum_x = np.sum(curr_feature)
            sum_xy = np.sum(np.multiply(curr_feature, self._y))
            sum_xsquare = np.sum(np.multiply(curr_feature, curr_feature))
            sum_ysquare = np.sum(np.multiply(self._y, self._y))

            self._correlation.append((n * sum_xy - sum_x * sum_y) / np.sqrt(((n * sum_xsquare - sum_x ** 2) * (n * sum_ysquare - sum_y ** 2))))

    def correlation(self, decimal=4):
        return self._correlation

def main():
    linear_reg = LinearRegression()

    X = np.genfromtxt('../resources/boston_housing/train_wo_col_labels.csv', dtype=np.float32, delimiter=',')
    X_train = X[:, 1:-1]
    y_train = X[:, -1]
    # X = [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]
    # y = [6, 8, 9, 11, 13, 16, 17, 19, 20, 24]

    scaler = preprocessing.MinMaxScaler(feature_range=(-1, 1)).fit(X_train)
    X_train = scaler.transform(X_train)

    rmse_error = linear_reg.fit(X_train, y_train)
    weights = linear_reg.weight_coefficients()
    print 'Weight coefficients for y = b0 + b1x1 + b2x2 + ... {} \n'.format(weights)
    print 'Correlation between X and y: {}\n'.format(linear_reg.correlation())
    print 'Root mean-squared error for training: {}\n'.format(rmse_error)

    # X_test = [[3], [5], [10]]
    X_test = np.genfromtxt('../resources/boston_housing/test_wo_col_labels.csv', dtype=np.float32, delimiter=',')
    X_test = X_test[:, 1:]
    X_test = scaler.transform(X_test)
    prediction = linear_reg.predict(X_test)
    # print 'Prediction of {}: {}'.format(X_test, (linear_reg.predict(X_test)))
    print 'Prediction on test instances: \n'
    for each_pred in prediction:
        print round(each_pred, 1)

if __name__ == '__main__':
    main()


import numpy as np
from scipy.stats import norm
N = norm.cdf

def bsm(S:float, K:float, T:float, r:float, sigma:float,call_value="both"):

    """
    bsm(): Implementation of Black-Scholes formula for non-dividend paying options

    :param S: Current asset price
    :type S: float
    :param K: Strike price of the option
    :type K: float
    :param T: Time until option expiration
    :type T: float
    :param r: Risk-free interest rate.
    :type r: float
    :param sigma: Annualized volatility of the asset's returns
    :type sigma: float
    :param call_value: Takes input as "call", "put", "both"
    :type call_value: string
    :return: Price of the options
    :rtype: float. Returns a dictionary when call_value is set to "both"
    """
    d1 = (np.log(S/K) + (r + sigma**2/2)*T) / (sigma*np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)
    Value = 0

    if(call_value=="both"):
        Value_call =  S * N(d1) - K * np.exp(-r*T)* N(d2)
        Value_put = K*np.exp(-r*T)*N(-d2) - S*N(-d1)
        return {"put":Value_put, "call":Value_call}

    if(call_value=="call"):
        Value = S * N(d1) - K * np.exp(-r*T)* N(d2)

    elif(call_value=="put"):
        Value = K*np.exp(-r*T)*N(-d2) - S*N(-d1)
    return Value

def bsm_div(S:float, K:float, T:float, r:float, q:float, sigma:float,call_value="both"):

    """
    bsm_div(): Implementation of Black-Scholes formula for Dividend paying options

    :param S: Current asset price
    :type S: float
    :param K: Strike price of the option
    :type K: float
    :param T: Time until option expiration
    :type T: float
    :param r: Risk-free interest rate.
    :type r: float
    :param q: The dividend rate of the asset.
    :type q: float
    :param sigma: Annualized volatility of the asset's returns.
    :type sigma: float
    :param call_value: Takes input as "call", "put", "both"
    :type call_value: string
    :return: Price of the options
    :rtype: float. Returns a dictionary when call_value is set to "both"
    """
    d1 = (np.log(S/K) + (r - q + sigma**2/2)*T) / (sigma*np.sqrt(T))
    d2 = d1 - sigma* np.sqrt(T)
    Value = 0

    if(call_value=="both"):
        Value_put = K*np.exp(-r*T)*N(-d2) - S*np.exp(-q*T)*N(-d1)
        Value_call = S*np.exp(-q*T) * N(d1) - K * np.exp(-r*T)* N(d2)
        return {"put":Value_put, "call":Value_call}

    if (call_value=="call"):
        Value = S*np.exp(-q*T) * N(d1) - K * np.exp(-r*T)* N(d2)

    elif(call_value=="put"):
        Value = K*np.exp(-r*T)*N(-d2) - S*np.exp(-q*T)*N(-d1)

    return Value


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Feb 28 10:53:23 2019

@author: alankar
"""

import numpy as np
import matplotlib.pyplot as plt

def DFT_slow(x):
    """Compute the discrete Fourier Transform of the 1D array x"""
    x = np.asarray(x, dtype=float)
    N = len(x)
    n = np.arange(N)
    k = n.reshape((N, 1))
    M = np.exp(-2j * np.pi * k * n / N)
    return np.dot(M, x)

def FFT(x):
    #A recursive implementation of the 1D Cooley-Tukey FFT
    x = np.asarray(x, dtype=float)
    N = x.shape[0]

    if N % 2 > 0:
        raise ValueError("size of x must be a power of 2")
    elif N <= 32:  # this cutoff should be optimized
        return DFT_slow(x)
    else:
        X_even = FFT(x[::2])
        X_odd = FFT(x[1::2])
        factor = np.exp(-2j * np.pi * np.arange(N//2) / N)
        return np.concatenate([X_even + factor * X_odd,
                               X_even - factor * X_odd])

pitch = np.loadtxt('pitch.txt')
pitch_fft_custom = np.abs(FFT(pitch))**2
#pitch_fft_np = np.fft.fft(pitch)**2

plt.figure(figsize=(13,10))
plt.plot(np.arange(0,len(pitch)//2,1),pitch_fft_custom[:len(pitch)//2])
plt.grid()
plt.xlabel(r'$f (Hz)$',size=18)
plt.ylabel(r'$A(f)$',size=20)
plt.title(r'Piano Music', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.ylim(0,10000)
plt.savefig('6.png')
plt.show()

"""
In FFT implementation, at the lowest recursion level we perform N/N_min identical matrix-vector products
"""

"""
def FFT(x):
    #A vectorized, non-recursive version of the Cooley-Tukey FFT
    x = np.asarray(x, dtype=float)
    N = len(x)
    N_min = 32

    if np.log2(N) % 1 > 0:
        raise ValueError("size of x must be a power of 2")

    # N_min here is equivalent to the stopping condition above,
    # and should be a power of 2 and needs to be optimized
    N_min = min(N, N_min)

    # Perform an O[N^2] DFT on all length-N_min sub-problems at once (each of size N/N_min)
    n = np.arange(0,N_min,1)
    k = n[:, None]
    M = np.exp(-2j * np.pi * n * k / N_min)
    X = np.dot(M, x.reshape((N_min, -1)))

    # build-up each level of the recursive calculation all at once
    while X.shape[0] < N:
        X_even = X[:, :X.shape[1] / 2]
        X_odd = X[:, X.shape[1] / 2:]
        factor = np.exp(-1j * np.pi * np.arange(X.shape[0])
                        / X.shape[0])[:, None]
        X = np.vstack([X_even + factor * X_odd,
                       X_even - factor * X_odd])

    return np.reshape(X,-1)
"""


from math import exp

import numpy as np

# A small constant to avoid division by 0 in categorical cross entropy calculations
EPSILON = 1e-10

class LogisticRegressor:
    """
    A logistic regressor optimized through gradient descent
    """

    def __init__(self, dat, theta=None):
        # number of samples and features in the dataset
        self.num_samples = dat.shape[0]
        self.num_features = dat.shape[1]

        # For feature normalization
        self.mu = None
        self.sigma = None

        # row for the bias terms
        biases = np.ones(self.num_samples)
        normalized_features = self.feature_normalize(dat[:, :-1])
        self.x = np.column_stack((biases, normalized_features))
        self.y = np.reshape(dat[:, -1], (self.num_samples, 1))

        self.theta = theta if theta else np.zeros((self.num_features, 1))

    def feature_normalize(self, x):
        """
        Normalize the features in x
        After normalization the mean value of each feature is 0, and the standard deviation is 1

        Arguments:
            x {np.ndarray} -- An N x K dimensional numpy array of data samples
        Returns:
            {np.ndarray} -- An N x K dimensional numpy array of normalized data samples
        """

        # Initialize normalization params on training data set
        if self.mu is None or self.sigma is None:
            self.mu = np.mean(x, axis=0)
            self.sigma = np.std(x, axis=0)
            zero_mask = self.sigma == 0
            self.sigma += zero_mask  # ensure no division by zero (if == 0, set = 1)

        return (x - self.mu) / self.sigma

    def gradient_descent(self, alpha, num_iters, l1, l2):
        """ Perform gradient descent to fit theta to the data x, given y

        Arguments:
            alpha {float} -- the learning rate
            num_iters {int} -- the number of iterations to perform
            l1 {float} -- the l1 regularisation constant for gradient calculations
            l2 {float} -- the l2 regularisation constant for gradient calculations
        """

        scalar = alpha / len(self.y)
        for i in range(num_iters):
            gradient = self.compute_gradient(l1, l2)
            self.theta -= scalar * gradient

    def compute_gradient(self, l1, l2):
        """ Compute the elementwise regularised gradient of theta for logistic regression

        Arguments:
            l1 {float} -- the l1 regularisation constant for gradient calculations
            l2 {float} -- the l2 regularisation constant for gradient calculations
        Returns:
            {float} -- the elementwise gradient of theta for this logistic model
        """

        pred = self.x.dot(self.theta)
        sigmoid_pred = self.sigmoid(pred)
        err = sigmoid_pred - self.y

        gradients = self.x.transpose().dot(err)
        gradients[1:, :] += l1 * np.sign(self.theta[1:, :])  # l1 regularisation
        gradients[1:, :] += 2 * l2 * self.theta[1:, :]  # l2 regularisation

        return gradients

    def compute_cost(self, l1, l2):
        """ Compute the cost for logistic regression

        Returns:
            {float} -- the cross entropy cost of this logistic model
            l1 {float} -- the l1 regularisation constant for cost calculations
            l2 {float} -- the l2 regularisation constant for cost calculations
        """

        scalar = -1 / len(self.y)
        pred = self.x.dot(self.theta)
        sigmoid_pred = self.sigmoid(pred)

        # Add small EPSILON to each term to avoid division by zero
        first_term = self.y.transpose().dot(np.log(sigmoid_pred + EPSILON))
        second_term = (1 - self.y).transpose().dot(np.log(1 - sigmoid_pred + EPSILON))

        loss = scalar * (first_term + second_term).sum()
        loss += l1 * np.sign(self.theta).sum()  # l1 regularisation
        loss += l2 * np.square(self.theta).sum()  # l2 regularisation

        return loss

    def predict(self, x):
        """ Predict the output of the logistic model against sample data using learned parameters

        Arguments:
            x {np.ndarray} -- An N x K dimensional numpy array of data samples to predict
        Returns:
            {[float]} -- a list of the predicted outputs of the model, clips to 0 or 1
        """

        pred = x.dot(self.theta)
        return self.sigmoid(pred) >= 0.5

    @staticmethod
    def sigmoid(x):
        # Vectorized method is faster than broadcasted exponential
        vectorized_sigmoid = np.vectorize(LogisticRegressor.single_sigmoid)
        return vectorized_sigmoid(x)

    @staticmethod
    def single_sigmoid(x):
        return 1 / (1 + exp(-x))

def example_main(dat):
    # Hyper-parameters
    alpha = 0.1
    num_iters = 1500
    l1 = 0.1
    l2 = 0.01

    regressor = LogisticRegressor(dat)
    print("Starting Cost:", regressor.compute_cost(l1, l2))
    print("Training...")
    regressor.gradient_descent(alpha=alpha, num_iters=num_iters, l1=l1, l2=l2)
    print("Final Cost:", regressor.compute_cost(l1, l2))
    print("Final Theta:\n", regressor.theta)

if __name__ == "__main__":
    print("Beginning Multivariate Logistic Regression 1:\n")
    multivariate_dat1 = np.loadtxt("../../datasets/regression/logistic_multivariate1.txt", delimiter=",")
    example_main(multivariate_dat1)

    print("\nBeginning Multivariate Logistic Regression 2:\n")
    multivariate_dat2 = np.loadtxt("../../datasets/regression/logistic_multivariate2.txt", delimiter=",")
    example_main(multivariate_dat2)


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

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1Buozu5j5KbsyQDOsoX6164cqit953oQF
"""
import numpy as np
import math
import time

"""## MULTIPERCEPTRON AND SIGMOID FUNCTION RANDOM"""

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

def derived_sigmoid(sig):
    return sig * (1 - sig)

inputs = np.array([[0, 0],
                   [0, 1],
                   [1, 0],
                   [1, 1]])

output_ = np.array([[0],
                    [1],
                    [1],
                    [0]])

weight1 = 2*np.random.random((2,3)) - 1
weight2 = 2*np.random.random((3,1)) - 1

epoch = 10
learning_rate = 0.7
moment = 1.005

for j in range(epoch):
    input_layer = inputs
    sum_synapse1 = np.dot(input_layer, weight1)
    hidden_layer = sigmoid(sum_synapse1)

    sum_synapse2 = np.dot(hidden_layer, weight2)
    output_layer = sigmoid(sum_synapse2)

    output_layer_error = output_ - output_layer
    absolute_average = np.mean(np.abs(output_layer_error))
    print("Error: " + str(absolute_average))

    derivative_output = derived_sigmoid(output_layer)
    delta_output = output_layer_error * derivative_output

    weight2_transposed = weight2.T
    delta_outputx_weight = delta_output.dot(weight2_transposed)
    delta_hidden_layer = delta_outputx_weight * derived_sigmoid(hidden_layer)

    hidden_layer_transposed = hidden_layer.T
    new_weights2 = hidden_layer_transposed.dot(delta_output)
    weight2 = (weight2 * moment) + (new_weights2 * learning_rate)

    transposed_entry_layer = input_layer.T
    new_weights1 = transposed_entry_layer.dot(delta_hidden_layer)
    weight1 = (weight1 * moment) + (new_weights1 * learning_rate)

# OUTPUT
# Error: 0.5001204158164341
# Error: 0.5001405195380415
# Error: 0.5001506241088196
# Error: 0.5001544495503312
# Error: 0.5001546931967644
# Error: 0.5001531551819314
# Error: 0.5001509494614835
# Error: 0.5001487129123772
# Error: 0.5001467743541796
# Error: 0.5001452761393818


import math
import sympy as sp
import numpy as np
import ipywidgets as widgets
import plotly.graph_objects as go

def point2(x=0, y=0):
    """Return a 2D point in Cartesian coordinates."""
    return sp.Matrix([x, y])

def hpoint2(x=0, y=0, w=1):
    """Return a 2D point in homogeneous coordinates."""
    return sp.Matrix([x, y, w])

def point3(x=0, y=0, z=0):
    """Return a 3D point in Cartesian coordinates."""
    return sp.Matrix([x, y, z])

def hpoint3(x=0, y=0, z=0, w=1):
    """Return a 3D point in homogeneous coordinates."""
    return sp.Matrix([x, y, z, w])

def unit_vector(vector, coordinates='Cartesian'):
    """
    Normalize the given vector.

    :param vector: is vector to be normalized.
    :type vector: Matrix
    ...
    :param coordinates: are coordinates in which the vector is described, 'Cartesian' or 'homogeneous'.
    :type coordinates: string
    ...
    :raise ValueError: if the value of coordinates is invalid.
    ...
    :return: the normalized vector.
    """
    if coordinates == 'Cartesian':
        return vector / math.sqrt(sum(vector.multiply_elementwise(vector)))
    elif coordinates == 'homogeneous':
        vector = vector[:-1, :]
        vector = vector / math.sqrt(sum(vector.multiply_elementwise(vector)))
        return sp.Matrix.vstack(vector, sp.Matrix([0]))
    raise ValueError(f'Invalid coordinates value. Expected "Cartesian" or "homogeneous" but "{coordinates}:was given.')

def rotation3(axis, angle):
    """
    Return a rotation matrix in 3D Cartesian coordinates that represents the rotation by `angle` around `axis`.

    :param axis: is the axis of direction. It should be one of ['x', 'y', 'z', 'n', 'o', 'a'].
    :type axis: string
    ...
    :param angle: is the angle of rotation.
    :type angle: sympy expression, sympy symbol or a number
    ...
    raise ValueError: if an invalid axis value is received.
    ...
    :return: the rotation matrix
    """
    if axis in ['x', 'n']:
        return sp.Matrix([
            [1, 0, 0],
            [0, sp.cos(angle), -sp.sin(angle)],
            [0, sp.sin(angle), sp.cos(angle)]
        ])
    elif axis in ['y', 'o']:
        return sp.Matrix([
            [sp.cos(angle), 0, sp.sin(angle)],
            [0, 1, 0],
            [-sp.sin(angle), 0, sp.cos(angle)]
        ])
    elif axis in ['z', 'a']:
        return sp.Matrix([
            [sp.cos(angle), -sp.sin(angle), 0],
            [sp.sin(angle), sp.cos(angle), 0],
            [0, 0, 1]
        ])
    else:
        raise ValueError(f'Expected one of [x, y, z, n, o, a] but received {axis}.')

def hrotation3(axis, angle):
    """
    Return a transformation matrix in 3D homogeneous coordinates that represents the rotation by `angle` around `axis`.

    :param axis: is the axis of rotation. It should be one of ['x', 'y', 'z', 'n', 'o', 'a'].
    :type axis: string
    ...
    :param angle: is the angle of rotation.
    :type angle: sympy expression, sympy symbol or a number
    ...
    raise ValueError: if an invalid axis value is received.
    ...
    :return: the rotation matrix
    """
    if axis in ['x', 'n']:
        return sp.Matrix([
            [1, 0, 0, 0],
            [0, sp.cos(angle), -sp.sin(angle), 0],
            [0, sp.sin(angle), sp.cos(angle), 0],
            [0, 0, 0, 1]
        ])
    elif axis in ['y', 'o']:
        return sp.Matrix([
            [sp.cos(angle), 0, sp.sin(angle), 0],
            [0, 1, 0, 0],
            [-sp.sin(angle), 0, sp.cos(angle), 0],
            [0, 0, 0, 1]
        ])
    elif axis in ['z', 'a']:
        return sp.Matrix([
            [sp.cos(angle), -sp.sin(angle), 0, 0],
            [sp.sin(angle), sp.cos(angle), 0, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ])
    else:
        raise ValueError(f'Expected one of [x, y, z, n, o, a] but received {axis}.')

def rotation2(angle):
    """Return a rotation matrix in 2D Cartesian coordinates that represents the rotation by the given angle."""
    return sp.Matrix([
        [sp.cos(angle), -sp.sin(angle)],
        [sp.sin(angle), sp.cos(angle)]
    ])

def hrotation2(angle):
    """Return a rotation matrix in 2D homogeneous coordinates that represents the rotation by the given angle."""
    return sp.Matrix([
        [sp.cos(angle), -sp.sin(angle), 0],
        [sp.sin(angle), sp.cos(angle), 0],
        [0, 0, 1]
    ])

def htranslation3(x=0, y=0, z=0):
    """Return transformation matrix in 3D homogeneous coordinates with embedded translation."""
    return sp.Matrix([[1, 0, 0, x],
                      [0, 1, 0, y],
                      [0, 0, 1, z],
                      [0, 0, 0, 1]])

def htranslation2(x=0, y=0):
    """Return transformation matrix in 2D homogeneous coordinates with embedded translation."""
    return sp.Matrix([[1, 0, x],
                      [0, 1, y],
                      [0, 0, 1]])

def is_right_hand_coordinate_system3(pose):
    """Checks whether the given pose follows the right-hand rule."""
    n, o, a = pose[:3, 0], pose[:3, 1], pose[:3, 2]
    return n.dot(n).simplify() == 1 and o.dot(o).simplify() == 1 and a.dot(a).simplify() == 1 and sp.simplify(n.cross(o)) == a

def hpose3(x=(1, 0, 0), y=(0, 1, 0), z=(0, 0, 1), t=(0, 0, 0)):
    """Return a pose in 3D homogeneous coordinates where the arguments are the new frame's axes and translation vector."""
    return sp.Matrix.vstack(sp.Matrix([tuple(x), tuple(y), tuple(z), tuple(t)]).T, sp.Matrix([0, 0, 0, 1]).T)

def euler_angles(pose, sequence):
    """
    Return the euler angles for the given rotation.

    :param pose: is a rotation or transformation matrix.
    :type pose: Matrix
    ...
    :param sequence: is the euler angles configuration.
    :type sequence: string
    ...
    raise ValueError: if an invalid or unsupported euler angle sequence is received.
    ...
    :return: the euler angles.
    """
#     Ова се дел од Ојлеровите агли.
#     За останатите оди на https://www.geometrictools.com/Documentation/EulerAngles.pdf
    sequence = sequence.lower()
    if sequence == 'zyz':
        if pose[2, 2] == 1:
            theta_1, theta_2, theta_3 = sp.atan2(pose[1, 0], pose[1, 1]), 0, 0
        elif pose[2, 2] == -1:
            theta_1, theta_2, theta_3 = -sp.atan2(pose[1, 0], pose[1, 1]), sp.pi, 0
        else:
            theta_1 = sp.atan2(pose[1, 2], pose[0, 2])
            theta_2 = sp.acos(pose[2, 2])
            theta_3 = sp.atan2(pose[2, 1], -pose[2, 0])
        return sp.Matrix([theta_1, theta_2, theta_3])
    elif sequence == 'zyx':
        if pose[2, 0] == 1:
            theta_1, theta_2, theta_3 = sp.atan2(-pose[1, 2], pose[1, 1]), -sp.pi/2, 0
        elif pose[2, 0] == -1:
            theta_1, theta_2, theta_3 = -sp.atan2(-pose[1, 2], pose[1, 1]), sp.pi/2, 0
        else:
            theta_1 = sp.atan2(pose[1, 0], pose[0, 0])
            theta_2 = sp.asin(-pose[2, 0])
            theta_3 = sp.atan2(pose[2, 1], pose[2, 2])
        return sp.Matrix([theta_1, theta_2, theta_3])
    elif sequence == 'xyz':
        if pose[0, 2] == 1:
            theta_1, theta_2, theta_3 = sp.atan2(pose[1, 0], pose[1, 1]), sp.pi/2, 0
        elif pose[0, 2] == -1:
            theta_1, theta_2, theta_3 = -sp.atan2(pose[1, 0], pose[1, 1]), -sp.pi/2, 0
        else:
            theta_1 = sp.atan2(-pose[1, 2], pose[2, 2])
            theta_2 = sp.asin(pose[0, 2])
            theta_3 = sp.atan2(-pose[0, 1], pose[0, 0])
        return sp.Matrix([theta_1, theta_2, theta_3])
    elif sequence == 'xzy':
        if pose[0, 1] == 1:
            theta_1, theta_2, theta_3 = sp.atan2(-pose[2, 0], pose[2, 2]), -sp.pi/2, 0
        elif pose[0, 1] == -1:
            theta_1, theta_2, theta_3 = -sp.atan2(-pose[2, 0], pose[2, 2]), sp.pi/2, 0
        else:
            theta_1 = sp.atan2(pose[2, 1], pose[1, 1])
            theta_2 = sp.asin(-pose[0, 1])
            theta_3 = sp.atan2(pose[0, 2], pose[0, 0])
        return sp.Matrix([theta_1, theta_2, theta_3])
    raise ValueError(f'Invalid or unsupported euler angle sequence {sequence}.')

def rotation_matrix_from_euler_angles(euler_angles, sequence):
    """
    Return a transformation matrix for the given euler angles.

    :param euler_angles: are the euler angles.
    :type euler_angles: Matrix
    ...
    :param sequence: is the euler angles configuration.
    :type sequence: string
    ...
    raise ValueError: if an invalid or unsupported euler angle sequence is received.
    ...
    :return: a transformation matrix for the given euler angles.
    """
    sequence = sequence.lower()
    if not all([char in 'xyz' for char in sequence]) or len(sequence) != 3:
        raise ValueError(f'Invalid or unsupported euler angle sequence {sequence}.')
    T1 = hrotation3(sequence[0], euler_angles[0])
    T2 = hrotation3(sequence[1], euler_angles[1])
    T3 = hrotation3(sequence[2], euler_angles[2])
    return T1 * T2 * T3

def lagrangian(L, jvars):
    """
    Implementation of Lagrange's equations.

    :param L: is the Lagrangian.
    :type L: Matrix
    ...
    :param jvars: is a list of all variables used in the Lagrangian.
    :type jvars: list or Matrix
    ...
    :return: the generalized forces equations.
    """
    jvars = sp.Matrix(jvars)
    return sp.simplify(L.diff(jvars.diff('t')).diff('t') - L.diff(jvars))

def dynamic_model_with_4_matrices(L, jvars):
    """
    Create the dynamic model through implementation of Lagrange's equations as by the book.

    :param L: is the Lagrangian.
    :type L: Matrix
    ...
    :param jvars: is a list of all variables used in the Lagrangian.
    :type jvars: list or Matrix
    ...
    :return: the 4 matrices that create the dynamic model.
    """
    import itertools
    from sympy.core.add import Add

    n = len(jvars)
    M, Cf, Ck, G = sp.zeros(n), sp.zeros(n), sp.zeros(n, n*(n-1)//2), sp.zeros(n, 1)
    jvars = sp.Matrix(jvars)
    v_jvars, a_jvars = jvars.diff('t'), jvars.diff('t', 2)
    v_jvars_squared = sp.matrix_multiply_elementwise(v_jvars, v_jvars)
    Ck_vars = [var[0] * var[1] for var in itertools.combinations(v_jvars, 2)]
    for row, eq in enumerate(lagrangian(L, jvars).expand()):
        args = eq.args if eq.func is Add else [eq]
        M_args = [arg for arg in args if arg.has(*a_jvars)]
        M[row, :] = [[sum([arg for arg in args if arg.has(var)]) for var in a_jvars]]
        Cf[row, :] = [[sum([arg for arg in args if arg.has(var)]) for var in v_jvars_squared]]
        if n > 1:
            Ck[row, :] = [[sum([arg for arg in args if arg.has(var)]) for var in Ck_vars]]
        G[row, :] = [sum([arg for arg in args if not arg.has(*a_jvars) and not arg.has(*v_jvars)])]
    return sp.simplify(M), sp.simplify(Cf), sp.simplify(Ck), sp.simplify(G)

def dynamic_model_with_3_matrices(L, jvars):
    """
    Create the dynamic model through implementation of Lagrange's equations as by the book.

    :param L: is the Lagrangian.
    :type L: Matrix
    ...
    :param jvars: is a list of all variables used in the Lagrangian.
    :type jvars: list or Matrix
    ...
    :return: the 3 matrices that create the dynamic model.
    """
    import itertools
    from sympy.core.add import Add

    n = len(jvars)
    M, C, G = sp.zeros(n), sp.zeros(n, n*(n+1)//2), sp.zeros(n, 1)
    jvars = sp.Matrix(jvars)
    v_jvars, a_jvars = jvars.diff('t'), jvars.diff('t', 2)
    v_jvars_squared = sp.matrix_multiply_elementwise(v_jvars, v_jvars)
    C_vars = [var[0] * var[1] for var in itertools.combinations_with_replacement(v_jvars, 2)]
    for row, eq in enumerate(lagrangian(L, jvars).expand()):
        args = eq.args if eq.func is Add else [eq]
        M_args = [arg for arg in args if arg.has(*a_jvars)]
        M[row, :] = [[sum([arg for arg in args if arg.has(var)]) for var in a_jvars]]
        C[row, :] = [[sum([arg for arg in args if arg.has(var)]) for var in C_vars]]
        G[row, :] = [sum([arg for arg in args if not arg.has(*a_jvars) and not arg.has(*v_jvars)])]
    return sp.simplify(M), sp.simplify(C), sp.simplify(G)

def force_transformation_matrix(pose):
    """ Return the force transformation matrix for the given pose."""
    R, P = pose[:3, :3], pose[:3, 3]
    return sp.Matrix.vstack(
        sp.Matrix.hstack(R, sp.zeros(3)),
        sp.Matrix.hstack(delta3(*P)*R, R))

def dh_joint_to_joint(theta, d, a, alpha):
    """ Get the DH model elementary matrix consisted of theta, d, a, apha parameters. """
    return hrotation3('z', theta) * htranslation3(x=a, z=d) * hrotation3('x', alpha)

def frame_lines(pose, line_length=1):
    pose = pose.evalf()
    t = pose[:3, -1]
    line_n = sp.Matrix.hstack(t, t + line_length * pose[:3, 0])
    line_o = sp.Matrix.hstack(t, t + line_length * pose[:3, 1])
    line_a = sp.Matrix.hstack(t, t + line_length * pose[:3, 2])
    return line_n, line_o, line_a

def draw_frame(pose, labels='x-y-z', colors=('red', 'green', 'blue'), line_width=3, line_length=1):
    def go_scatter(x, y, z, name, color, width):
        return go.Scatter3d(
            x=x, y=y, z=z,
            marker=dict(size=2),
            line=dict(color=color, width=width),
            name=name)

    labels = labels.split('-')
    line_n, line_o, line_a = frame_lines(pose, line_length)
    scatter_n = go_scatter(*line_n.tolist(), labels[0], colors[0], line_width)
    scatter_o = go_scatter(*line_o.tolist(), labels[1], colors[1], line_width)
    scatter_a = go_scatter(*line_a.tolist(), labels[2], colors[2], line_width)
    return scatter_n, scatter_o, scatter_a

def plot(pose=hpose3(), point=hpoint3()):
    scatter_xyz = draw_frame(hpose3(), labels='x-y-z', colors=('red', 'green', 'blue'), line_width=3, line_length=1.5)
    scatter_noa = draw_frame(pose, labels='n-o-a', colors=('cyan', 'magenta', 'yellow'), line_width=6)
    point = point.tolist()
    scatter_point = go.Scatter3d(x=point[0], y=point[1], z=point[2], marker=dict(size=2), line=dict(color='black'), name='p')
    plot_data = scatter_xyz + scatter_noa + (scatter_point,)
    fig = go.FigureWidget(data=plot_data)
    fig.update_layout(width=800, height=600, autosize=False, scene=dict(aspectmode='data'))
    return fig

def delta3(dRx=0, dRy=0, dRz=0):
    """
    Return the differential operator for rotation matrix in 3D.

    :params dRx, dRy, dRz: are the differential rotations.
    :type dRx, dRy, dRz: Symbol or number
    ...
    :return: the differential operator for rotation matrix in 3D.
    """
    return sp.Matrix([
        [0, -dRz, dRy],
        [dRz, 0, -dRx],
        [-dRy, dRx, 0]
    ])

def hdelta3(dRx=0, dRy=0, dRz=0, dx=0, dy=0, dz=0):
    """
    Return the differential operator for homogeneous matrix in 3D.

    :params dRx, dRy, dRz: are the differential rotations.
    :type dRx, dRy, dRz: Symbol or number
    ...
    :params dx, dy, dz: are the differential translations.
    :type dx, dy, dz: Symbol or number
    ...
    :return: the differential operator for homogeneous matrix in 3D.
    """
    return sp.Matrix([
        [0, -dRz, dRy, dx],
        [dRz, 0, -dRx, dy],
        [-dRy, dRx, 0, dz],
        [0, 0, 0, 0],
    ])

def extract_hdelta3(delta):
    """
    Return a vector consisted of differential rotations and translations.
    The vector elements are [δx, δy, δz, dx, dy, dz].

    :param delta: is the differential operator.
    :type delta: Matrix
    ...
    raise ValueError: if an invalid delta operator is received.
    ...
    :return: the vector of differential rotations and translations.
    """
    if delta[2, 1] != -delta[1, 2] or delta[2, 1] != -delta[1, 2] or delta[2, 1] != -delta[1, 2]:
        raise ValueError('delta is not a valid differential operator.')
    dRx, dRy, dRz = delta[2, 1], delta[0, 2], delta[1, 0]
    dx, dy, dz = delta[0, 3], delta[1, 3], delta[2, 3]
    return sp.Matrix([dRx, dRy, dRz, dx, dy, dz])

def polynomial(n, char='c'):
    """ Create an n-degree polynomial with custom symbols / coefficients. """
    symbols = [sp.symbols(f'{char}{i}') for i in range(n+1)]
    p = sum([sp.symbols('t')**i * symbols[i] for i in range(n+1)])
    return p, symbols

def trajectory_polynomial_3(ti, tf, xi, xf, vi, vf):
    """
    Create a trajectory with 3th degree polynomial.

    :param ti: is the initial time.
    :type ti: number
    ...
    :param tf: is the final time.
    :type tf: number
    ...
    :param xi: is the initial position.
    :type xi: number
    ...
    :param xf: is the final position.
    :type xf: number
    ...
    :param vi: is the initial velocity.
    :type vi: number
    ...
    :param vf: is the final velocity.
    :type vf: number
    ...
    :return: the coefficients and position equation.
    """
    theta, symbols = polynomial(3)
    v_theta = theta.diff('t')
    eq_xi = theta.subs('t', ti) - xi
    eq_xf = theta.subs('t', tf) - xf
    eq_vi = v_theta.subs('t', ti) - vi
    eq_vf = v_theta.subs('t', tf) - vf
    solution = sp.linsolve([eq_xi, eq_xf, eq_vi, eq_vf], symbols)
    for sub in zip(symbols, solution.args[0]):
        theta =theta.subs(*sub)
    return theta, solution

def trajectory_polynomial_5(ti, tf, xi, xf, vi, vf, ai, af):
    """
    Create a trajectory with 5th degree polynomial.

    :param ti: is the initial time.
    :type ti: number
    ...
    :param tf: is the final time.
    :type tf: number
    ...
    :param xi: is the initial position.
    :type xi: number
    ...
    :param xf: is the final position.
    :type xf: number
    ...
    :param vi: is the initial velocity.
    :type vi: number
    ...
    :param vf: is the final velocity.
    :type vf: number
    ...
    :param ai: is the initial acceleration.
    :type ai: number
    ...
    :param af: is the final acceleration.
    :type af: number
    ...
    :return: the coefficients and position equation.
    """
    theta, symbols = polynomial(5)
    v_theta = theta.diff('t')
    a_theta = theta.diff('t', 2)
    eq_xi = theta.subs('t', ti) - xi
    eq_xf = theta.subs('t', tf) - xf
    eq_vi = v_theta.subs('t', ti) - vi
    eq_vf = v_theta.subs('t', tf) - vf
    eq_ai = a_theta.subs('t', ti) - ai
    eq_af = a_theta.subs('t', tf) - af
    solution = sp.linsolve([eq_xi, eq_xf, eq_vi, eq_vf, eq_ai, eq_af], symbols)
    for sub in zip(symbols, solution.args[0]):
        theta =theta.subs(*sub)
    return theta, solution

def trajectory_linear_parabolic_segments(ti, tf, xi, xf, w=None, tb=None):
    """
    Create a linear trajectory with parabolic end-segments.

    Note: Only one argument of w and tb is enough to get numerical solution.

    :param ti: is the initial time.
    :type ti: number
    ...
    :param tf: is the final time.
    :type tf: number
    ...
    :param xi: is the initial position.
    :type xi: number
    ...
    :param xf: is the final position.
    :type xf: number
    ...
    :param w: is the constant velocity in the linear segment.
    :type w: number
    ...
    :param tb: is the duration of each parabolic segment.
    :type tb: number
    ...
    :return: the coefficients w and tb and position equation of each segment.
    """
    if w and tb is None:
        tb = (xi - xf + w*tf) / w
    elif w is None and tb:
        w = (xi - xf) / (tb - tf)
    elif w is None and tb is None:
        w, tb = sp.symbols('t_b, w')
    t = sp.symbols('t')
    theta_s1 = xi + w / (2*tb) * t**2
    theta_s2 = theta_s1.subs(t, tb) - w*tb + w*t
    theta_s3 = xf - w / (2*tb) * (tf-t)**2
    return theta_s1, theta_s2, theta_s3, w, tb

def plot_trajectories(segments):
    """
    Plot the trajectories of the given list of segments.
    A segment is defined as a tuple (equation, ti, tf).

    :param segments: is a list of segments.
    :type segments: list
    ...
    :return: the figure to be displayed.
    """
    import numpy as np
    import plotly.graph_objects as go

    x_data, v_data, a_data, time_data = np.array([]), np.array([]), np.array([]), np.array([])
    for theta, ti, tf in segments:
        time = np.linspace(ti, tf, int(100*(tf-ti)))
        x = sp.lambdify('t', theta)(time)
        v = sp.lambdify('t', theta.diff('t'))(time)
        a = sp.lambdify('t', theta.diff('t', 2))(time)
        time_data = np.hstack((time_data, time))
        x_data = np.hstack((x_data, x*np.ones_like(time) if isinstance(x, int) or isinstance(x, float) else x))
        v_data = np.hstack((v_data, v*np.ones_like(time) if isinstance(v, int) or isinstance(v, float) else v))
        a_data = np.hstack((a_data, a*np.ones_like(time) if isinstance(a, int) or isinstance(a, float) else a))

    line_position = go.Scatter(x=time_data, y=x_data, name='Позиција')
    line_velocity = go.Scatter(x=time_data, y=v_data, name='Брзина')
    line_acceleration = go.Scatter(x=time_data, y=a_data, name='Забрзување')
    return go.Figure([line_position, line_velocity, line_acceleration])

class AnimationPlayground:
    def play(self, checkpoint_pose=hpose3(), point=hpoint3()):
        self.checkpoint_pose = checkpoint_pose
        self.point = point

        self.toggle_action = widgets.ToggleButtons(options=['Ротирај', 'Придвижи се'])
        self.floattext = widgets.BoundedFloatText(
            value=0, min=-180, max=180, step=1, layout={'width': 'min-content'})
        self.dropdown_axis = widgets.Dropdown(
            options=['x', 'y', 'z', 'n', 'o', 'a'], description='по оската', layout={'width': 'min-content'})
        self.progress = widgets.FloatProgress(value=0, min=0, max=1, step=1)
        self.play = widgets.Play(value=0, min=0, max=1, step=1, show_repeat=False, interval=1)
        self.button_next = widgets.Button(description='Следно', disabled=True)

        self.floattext.observe(self.update_play_max, 'value')
        self.play.observe(self.update_fig, 'value')
        self.button_next.on_click(self.on_button_next_clicked)

        widgets.jslink((self.play, 'value'), (self.progress, 'value'))
        widgets.jslink((self.play, 'max'), (self.progress, 'max'))

        widget_box1 = widgets.HBox([self.toggle_action, self.floattext, self.dropdown_axis])
        widget_box2 = widgets.HBox([self.progress, self.play, self.button_next])

        self.fig = plot(self.checkpoint_pose, point)
        self.animation_box = widgets.VBox([widget_box1, widget_box2, self.fig])
        return self.animation_box

    def set_noa(self, pose):
        line_n, line_o, line_a = frame_lines(pose)
        with self.fig.batch_update():
            self.fig.data[3].x, self.fig.data[3].y, self.fig.data[3].z = line_n.tolist()
            self.fig.data[4].x, self.fig.data[4].y, self.fig.data[4].z = line_o.tolist()
            self.fig.data[5].x, self.fig.data[5].y, self.fig.data[5].z = line_a.tolist()

    def set_point_noa(self, point):
        point = self.current_pose * point
        point = point.evalf().tolist()
        with self.fig.batch_update():
            self.fig.data[6].x, self.fig.data[6].y, self.fig.data[6].z = point[0]*2, point[1]*2, point[2]*2

    def on_button_next_clicked(self, button):
        if self.play.value != self.play.max:
            return
        self.checkpoint_pose = self.current_pose
        self.play.value = 0
        self.floattext.value = 0

    def update_fig(self, change):
        if self.toggle_action.value == 'Ротирај':
            angle = sp.sign(self.floattext.value) * change.new / 180 * sp.pi
            if self.dropdown_axis.value in ['x', 'y', 'z']:
                self.current_pose = hrotation3(self.dropdown_axis.value, angle) * self.checkpoint_pose
            else:
                self.current_pose = self.checkpoint_pose * hrotation3(self.dropdown_axis.value, angle)
        elif self.toggle_action.value == 'Придвижи се':
            length = sp.sign(self.floattext.value) * change.new
            axis = self.dropdown_axis.value.replace('n', 'x').replace('o', 'y').replace('a', 'z')
            t = htranslation3(**{axis: length})
            if self.dropdown_axis.value in ['x', 'y', 'z']:
                self.current_pose = t * self.checkpoint_pose
            else:
                self.current_pose = self.checkpoint_pose * t
        line_n, line_o, line_a = frame_lines(self.current_pose)
        current_point = self.current_pose * self.point
        current_point = current_point.evalf().tolist()
        with self.fig.batch_update():
            self.fig.data[3].x, self.fig.data[3].y, self.fig.data[3].z = line_n.tolist()
            self.fig.data[4].x, self.fig.data[4].y, self.fig.data[4].z = line_o.tolist()
            self.fig.data[5].x, self.fig.data[5].y, self.fig.data[5].z = line_a.tolist()
            self.fig.data[6].x, self.fig.data[6].y, self.fig.data[6].z = current_point[0]*2, current_point[1]*2, current_point[2]*2

        self.toggle_action.disabled = True if self.play.value != 0 else False
        self.floattext.disabled = True if self.play.value != 0 else False
        self.dropdown_axis.disabled = True if self.play.value != 0 else False
        self.button_next.disabled = True if self.play.value != self.play.max else False

    def update_play_max(self, change):
        self.play.max = abs(self.floattext.value)

class SerialLinkRobot:
    """
    A class to easily create and interact with robotic arm.
    """

    def __init__(self):
        self.reset()

    def reset(self):
        """ Reset the robotic arm data. """
        self.links = []
        self.joint_variables = []
        self.subs_joints = []
        self.subs_additional = []
        self.stop_update_for_slider_joint = False
        self.trail_color_index = 1
        self.trail_color_list = []

    def add_revolute_joint(self, theta, d, a, alpha):
        """
        Add a revolute joint to the robotic arm according to the DH convention.

        :param theta: is the angle of rotation around z-axis.
        :type theta: Symbol
        ...
        :param d: is the displacement along z-axis.
        :type d: number or Symbol
        ...
        :param a: the displacement along x-axis.
        :type a: number or Symbol
        ...
        :param alpha: the angle of rotation around x-axis.
        :type alpha: number or Symbol
        """
        self.links.append(('revolute', theta, d, a, alpha))
        self.joint_variables.append(theta)
        self.subs_joints.append((theta, 0))

    def add_prismatic_joint(self, theta, d, a, alpha):
        """
        Add a prismatic joint to the robotic arm according to the DH convention.

        :param theta: is the angle of rotation around z-axis.
        :type theta: number or Symbol
        ...
        :param d: is the displacement along z-axis.
        :type d: Symbol
        ...
        :param a: the displacement along x-axis.
        :type a: number or Symbol
        ...
        :param alpha: the angle of rotation around x-axis.
        :type alpha: number or Symbol
        """
        self.links.append(('prismatic', theta, d, a, alpha))
        self.joint_variables.append(d)
        self.subs_joints.append((d, 1))

    def add_subs(self, subs):
        """
        Add the symbol values for plotting purposes.

        :param subs: is a list of tuples, each consisted of a symbol and its value.
        :type subs: [(symbol1, value1), (symbol2, value2), ... (symbol3, value3)]
        """
        self.subs_additional = subs

    def get_dh_joint_to_joint(self, start_joint, end_joint):
        """
        Get the DH model subsection transformation matrix for the joint id range(start_joint, end_joint).

        :param start_joint: is the starting joint id of the desired dh model susbsection.
        :type start_joint: integer
        ...
        :param end_joint: is the final joint id of the desired dh model susbsection.
        :type end_joint: integer
        ...
        :return: DH model subsection transformation matrix for joint id range(start_joint, end_joint).
        """
        pose = hpose3()
        for link in self.links[start_joint:end_joint]:
            joint_type, theta, d, a, alpha = link
            pose = pose * dh_joint_to_joint(theta, d, a, alpha)
        pose.simplify()
        return pose

    def get_dh_matrix(self):
        """ Get the DH model transformation matrix for the whole robotic arm. """
        return self.get_dh_joint_to_joint(start_joint=0, end_joint=len(self.links))

    def get_dh_table(self):
        """ Return the DH table intended for visual purposes only. """
        return sp.Matrix(self.links)[:, 1:]

    def linear_jacobian(self):
        """ Return the linear jacobian for this robotic arm. """
        linear_jacobian = self.get_dh_matrix()[:3, 3].jacobian(self.joint_variables)
        linear_jacobian.simplify()
        return linear_jacobian

    def angular_jacobian(self):
        """ Return the angular jacobian for this robotic arm. """
        pose = hpose3()
        angular_jacobian = sp.Matrix([[], [], []])
        for link in self.links:
            joint_type, theta, d, a, alpha = link
            z_i_m1 = sp.Matrix([0, 0, 0]) if joint_type == 'prismatic' else pose[:3, 2]
            angular_jacobian = sp.Matrix.hstack(angular_jacobian, z_i_m1)
            pose = pose * dh_joint_to_joint(theta, d, a, alpha)

        angular_jacobian.simplify()
        return angular_jacobian

    def jacobian(self):
        """ Return the jacobian for this robotic arm. """
        return sp.Matrix.vstack(self.linear_jacobian(), self.angular_jacobian())

    def update_toggle_joint(self, change):
        self.stop_update_for_slider_joint = True
        joint_id = int(change.new.split()[-1]) - 1
        joint_type = self.links[joint_id][0]
        minn, maxx, step = (0, 5, 0.1) if joint_type == 'prismatic' else (-180, 180, 1)
        self.slider_joint.min, self.slider_joint.max, self.slider_joint.step = minn, maxx, step
        joint_value = self.subs_joints[joint_id][1]
        self.slider_joint.value = joint_value if joint_type == 'prismatic' else int(180 * joint_value / sp.pi.evalf())
        self.slider_joint.description = 'Призматичен' if joint_type == 'prismatic' else 'Ротационен'
        self.stop_update_for_slider_joint = False

    def update_slider_joint(self, change):
        if self.stop_update_for_slider_joint:
            return
        joint_id = int(self.toggle_joint.value.split()[-1]) - 1
        joint_type = self.links[joint_id][0]
        joint_value = change.new if joint_type == 'prismatic' else change.new * sp.pi / 180
        self.subs_joints[joint_id] = self.subs_joints[joint_id][0], joint_value
        pose = hpose3()
        subs = self.subs_joints + self.subs_additional
        with self.fig.batch_update():
            for index, link in enumerate(self.links):
                index *= 5
                joint_type, theta, d, a, alpha = link
                next_pose = pose * dh_joint_to_joint(theta, d, a, alpha)
                pose_for_rectangluar_robot_shape = pose * htranslation3(z=d)
                pose_numeric = pose.subs(subs).evalf()
                next_pose_numeric = next_pose.subs(subs).evalf()
                pose_for_rectangluar_robot_shape_numeric = pose_for_rectangluar_robot_shape.subs(subs).evalf()
                pose_second_cone_numeric = pose_for_rectangluar_robot_shape_numeric if joint_type =='prismatic' else pose_numeric

                line_n, line_o, line_a = frame_lines(pose_numeric, line_length=0.5)
                self.fig.data[index+0].x, self.fig.data[index+0].y, self.fig.data[index+0].z = line_n.tolist()
                self.fig.data[index+1].x, self.fig.data[index+1].y, self.fig.data[index+1].z = line_a.tolist()

                cone_xyz = [pose_numeric[0, 3]], [pose_numeric[1, 3]], [pose_numeric[2, 3]]
                self.fig.data[index+2].x, self.fig.data[index+2].y, self.fig.data[index+2].z = cone_xyz
                cone_uvw = [pose_numeric[0, 2]], [pose_numeric[1, 2]], [pose_numeric[2, 2]]
                self.fig.data[index+2].u, self.fig.data[index+2].v, self.fig.data[index+2].w = cone_uvw

                cone_xyz = [pose_second_cone_numeric[0, 3]], [pose_second_cone_numeric[1, 3]], [pose_second_cone_numeric[2, 3]]
                self.fig.data[index+3].x, self.fig.data[index+3].y, self.fig.data[index+3].z = cone_xyz
                cone_uvw = [-pose_numeric[0, 2]], [-pose_numeric[1, 2]], [-pose_numeric[2, 2]]
                self.fig.data[index+3].u, self.fig.data[index+3].v, self.fig.data[index+3].w = cone_uvw

                self.fig.data[index+4].x = [pose_numeric[0, 3], pose_for_rectangluar_robot_shape_numeric[0, 3], next_pose_numeric[0, 3]]
                self.fig.data[index+4].y = [pose_numeric[1, 3], pose_for_rectangluar_robot_shape_numeric[1, 3], next_pose_numeric[1, 3]]
                self.fig.data[index+4].z = [pose_numeric[2, 3], pose_for_rectangluar_robot_shape_numeric[2, 3], next_pose_numeric[2, 3]]
                pose = next_pose

            line_n, line_o, line_a = frame_lines(pose.subs(subs).evalf(), line_length=0.5)
            self.fig.data[-3].x, self.fig.data[-3].y, self.fig.data[-3].z = line_n.tolist()
            self.fig.data[-2].x, self.fig.data[-2].y, self.fig.data[-2].z = line_a.tolist()

            if self.toggle_trail.value:
                if self.subs_joints[2][1] == sp.pi*0.5:
                    self.fig.data[-1].x = self.fig.data[-1].x + (line_n.tolist()[0][0],)
                    self.fig.data[-1].y = self.fig.data[-1].y + (line_n.tolist()[1][0],)
                    self.fig.data[-1].z = self.fig.data[-1].z + (line_n.tolist()[2][0],)
                    self.trail_color_list += [self.trail_color_index]
                    self.fig.data[-1].marker['color'] = self.trail_color_list
                elif self.subs_joints[2][1] == -sp.pi*0.5:
                    x,y,z=list(self.fig.data[-1].x),list(self.fig.data[-1].y),list(self.fig.data[-1].z)
                    ind_x = [i for i,val in enumerate(x) if val > line_n.tolist()[0][0]-0.2 and val < line_n.tolist()[0][0]+0.2]
                    ind_y = [i for i,val in enumerate(y) if val > line_n.tolist()[1][0]-0.2 and val < line_n.tolist()[1][0]+0.2]
                    ind_remove = [i for i in ind_x if i in ind_y]
                    for ind in ind_remove:
                        x.pop(ind_remove[0])
                        y.pop(ind_remove[0])
                        z.pop(ind_remove[0])
                        self.fig.data[-1].x , self.fig.data[-1].y, self.fig.data[-1].z = tuple(x),tuple(y),tuple(z)

    def update_button_trail_color(self, button):
        self.trail_color_index += 1

    def update_toggle_trail(self, change):
        change.owner.description = 'Вклучи цртање' if change.new == False else 'Исклучи цртање'

    def update_button_remove_trail(self, button):
        self.trail_color_index = 1
        self.trail_color_list = []
        with self.fig.batch_update():
            self.fig.data[-1].x,  self.fig.data[-1].y,  self.fig.data[-1].z = [], [], []

    def write_text(self,text,ofset):
        self.update_button_remove_trail(None)
        for letter in text:
            self.write_letter(letter,ofset)
            ofset[0]+=1.5

    def write_letter(self,letter,ofset):
        # frame of the letter i 1x1
        if letter == 'T':
            #(pocx, pocy,po so da odi i koja nasoka, kolku da otide)
            comands = [(0,1,'1 0',np.arange(0.0,1.0,0.05)),(0.5,0,'0 1',np.arange(0.0,1.0,0.05))]
        elif letter == 'E':
            comands = [(0,1,'1 0',np.arange(0.0,1.0,0.05)),(0,0.5,'1 0',np.arange(0.0,1.0,0.05)),
            (0,0,'1 0',np.arange(0.0,1.0,0.05)),(0,0,'0 1',np.arange(0.0,1.0,0.05))]
        elif letter == 'I':
            comands = [(0.5,0,'0 1',np.arange(0.0,1.0,0.05)),(0.2,0,'1 0',np.arange(0.0,0.6,0.05)),(0.2,1,'1 0',np.arange(0.0,0.6,0.05))]
        elif letter == 'L':
            comands = [(0,0,'0 1',np.arange(0.0,1.0,0.05)),(0,0,'1 0',np.arange(0.0,1.0,0.05))]
        elif letter == 'H':
            comands = [(0,0,'0 1',np.arange(0.0,1.0,0.05)),(1,0,'0 1',np.arange(0.0,1.0,0.05)),(0,0.5,'1 0',np.arange(0.0,1.0,0.05))]
        elif letter == 'F':
            comands = [(0,0,'0 1',np.arange(0.0,1.0,0.05)),(0,1,'1 0',np.arange(0.0,1.0,0.05)),(0,0.5,'1 0',np.arange(0.0,1.0,0.05))]
        elif letter == 'N':
            comands = [(0,0,'0 1',np.arange(0.0,1.0,0.05)),(1,0,'0 1',np.arange(0.0,1.0,0.05)),(0,1,'1 -1',np.arange(0.0,1.0,0.05))]
        elif letter == 'M':
            comands = [(0,0,'0 1',np.arange(0.0,1.0,0.05)),(1,0,'0 1',np.arange(0.0,1.0,0.05)),
            (0,1,'1 -1',np.arange(0.0,0.5,0.05)),(0.5,0.5,'1 1',np.arange(0.0,0.5,0.05))]
        elif letter == 'X':
            comands = [(0,1,'1 -1',np.arange(0.0,1.0,0.05)),(0,0,'1 1',np.arange(0.0,1.0,0.05))]
        elif letter == 'V':
            comands = [(0,1,'0.5 -1',np.arange(0.0,1,0.05)),(0.5,0,'0.5 1',np.arange(0.0,1,0.05))]
        elif letter == 'A':
            comands = [(0,0,'0.5 1',np.arange(0.0,1,0.05)),(0.5,1,'0.5 -1',np.arange(0.0,1,0.05)),(0.3,0.5,'1 0',np.arange(0.0,0.5,0.05))]
        for comand in comands:
            for i in comand[3]:
                scale = comand[2].split()
                x, y, d1, d2 = (comand[0]+ofset[0])+i*float(scale[0]),(comand[1]+ofset[1])+i*float(scale[1]), self.links[0][3], self.links[1][3]
                theta2 = sp.acos((x**2 + y**2 - d1**2 - d2**2)/(2*d1*d2))
                theta1 = sp.atan2(y,x) - sp.atan2((d2*sp.sin(theta2)),(d1+d2*sp.cos(theta2)))
                self.subs_joints[0] = self.subs_joints[0][0], theta1.evalf()
                self.subs_joints[1] = self.subs_joints[1][0], theta2.evalf()
                pose = hpose3()
                subs = self.subs_joints + self.subs_additional
                with self.fig.batch_update():
                    for index, link in enumerate(self.links):
                        index *= 5
                        joint_type, theta, d, a, alpha = link
                        next_pose = pose * dh_joint_to_joint(theta, d, a, alpha)
                        pose_for_rectangluar_robot_shape = pose * htranslation3(z=d)
                        pose_numeric = pose.subs(subs).evalf()
                        next_pose_numeric = next_pose.subs(subs).evalf()
                        pose_for_rectangluar_robot_shape_numeric = pose_for_rectangluar_robot_shape.subs(subs).evalf()
                        pose_second_cone_numeric = pose_for_rectangluar_robot_shape_numeric if joint_type =='prismatic' else pose_numeric

                        line_n, line_o, line_a = frame_lines(pose_numeric, line_length=0.5)
                        self.fig.data[index+0].x, self.fig.data[index+0].y, self.fig.data[index+0].z = line_n.tolist()
                        self.fig.data[index+1].x, self.fig.data[index+1].y, self.fig.data[index+1].z = line_a.tolist()

                        cone_xyz = [pose_numeric[0, 3]], [pose_numeric[1, 3]], [pose_numeric[2, 3]]
                        self.fig.data[index+2].x, self.fig.data[index+2].y, self.fig.data[index+2].z = cone_xyz
                        cone_uvw = [pose_numeric[0, 2]], [pose_numeric[1, 2]], [pose_numeric[2, 2]]
                        self.fig.data[index+2].u, self.fig.data[index+2].v, self.fig.data[index+2].w = cone_uvw

                        cone_xyz = [pose_second_cone_numeric[0, 3]], [pose_second_cone_numeric[1, 3]], [pose_second_cone_numeric[2, 3]]
                        self.fig.data[index+3].x, self.fig.data[index+3].y, self.fig.data[index+3].z = cone_xyz
                        cone_uvw = [-pose_numeric[0, 2]], [-pose_numeric[1, 2]], [-pose_numeric[2, 2]]
                        self.fig.data[index+3].u, self.fig.data[index+3].v, self.fig.data[index+3].w = cone_uvw

                        self.fig.data[index+4].x = [pose_numeric[0, 3], pose_for_rectangluar_robot_shape_numeric[0, 3], next_pose_numeric[0, 3]]
                        self.fig.data[index+4].y = [pose_numeric[1, 3], pose_for_rectangluar_robot_shape_numeric[1, 3], next_pose_numeric[1, 3]]
                        self.fig.data[index+4].z = [pose_numeric[2, 3], pose_for_rectangluar_robot_shape_numeric[2, 3], next_pose_numeric[2, 3]]
                        pose = next_pose

                    line_n, line_o, line_a = frame_lines(pose.subs(subs).evalf(), line_length=0.5)
                    self.fig.data[-3].x, self.fig.data[-3].y, self.fig.data[-3].z = line_n.tolist()
                    self.fig.data[-2].x, self.fig.data[-2].y, self.fig.data[-2].z = line_a.tolist()

                    if self.toggle_trail.value:
                        if self.subs_joints[2][1] == sp.pi*0.5:
                            self.fig.data[-1].x = self.fig.data[-1].x + (line_n.tolist()[0][0],)
                            self.fig.data[-1].y = self.fig.data[-1].y + (line_n.tolist()[1][0],)
                            self.fig.data[-1].z = self.fig.data[-1].z + (line_n.tolist()[2][0],)
                            self.trail_color_list += [self.trail_color_index]
                            self.fig.data[-1].marker['color'] = self.trail_color_list
                        elif self.subs_joints[2][1] == -sp.pi*0.5:
                            x,y,z=list(self.fig.data[-1].x),list(self.fig.data[-1].y),list(self.fig.data[-1].z)
                            ind_x = [i for i,val in enumerate(x) if val > line_n.tolist()[0][0]-0.2 and val < line_n.tolist()[0][0]+0.2]
                            ind_y = [i for i,val in enumerate(y) if val > line_n.tolist()[1][0]-0.2 and val < line_n.tolist()[1][0]+0.2]
                            ind_remove = [i for i in ind_x if i in ind_y]
                            for ind in ind_remove:
                                x.pop(ind_remove[0])
                                y.pop(ind_remove[0])
                                z.pop(ind_remove[0])
                                self.fig.data[-1].x , self.fig.data[-1].y, self.fig.data[-1].z = tuple(x),tuple(y),tuple(z)

    def interact(self, figure_width=800, figure_height=600):
        """ Interact with the constructed robot arm. """
        if not self.links:
            return 'Роботската рака нема зглобови, па нема што да се црта.'
        self.toggle_trail = widgets.ToggleButton(value=True, description='Исклучи цртање')
        self.toggle_trail.observe(self.update_toggle_trail, 'value')
        self.button_trail_color = widgets.Button(description='Промени боја')
        self.button_trail_color.on_click(self.update_button_trail_color)
        self.button_remove_trail = widgets.Button(description='Избриши патека')
        self.button_remove_trail.on_click(self.update_button_remove_trail)
        self.toggle_joint = widgets.ToggleButtons(options=[f'Зглоб {x+1}' for x in range(len(self.links))])
        self.toggle_joint.observe(self.update_toggle_joint, 'value')
        joint_value = self.subs_joints[0][1]
        value, minn, maxx, step = (joint_value, 0, 5, 0.1) if self.links[0][0] == 'prismatic' else (joint_value, -180, 180, 1)
        decription = 'Призматичен' if self.links[0][0] == 'prismatic' else 'Ротационен'
        self.slider_joint = widgets.FloatSlider(
            value=value, min=minn, max=maxx, step=step, continuous_update=True, description=decription)
        self.slider_joint.observe(self.update_slider_joint, 'value')

        fig = self.plot(figure_width, figure_height)
        trail_buttons = widgets.HBox([self.toggle_trail, self.button_trail_color, self.button_remove_trail])
        widget_box = widgets.VBox([trail_buttons, self.toggle_joint, self.slider_joint, fig])
        return widget_box

    def plot(self, figure_width=800, figure_height=600):
        scatter_data = self.get_plot_data()
        self.fig = go.FigureWidget(data=scatter_data)
        self.fig.update_layout(width=figure_width, height=figure_height, showlegend=False, scene=dict(aspectmode='data'))
        return self.fig

    def get_plot_data(self):
        pose = hpose3()
        scatter_data = []
        self.joints_values = []
        subs = self.subs_joints + self.subs_additional
        for index, link in enumerate(self.links):
            joint_type, theta, d, a, alpha = link
            next_pose = pose * dh_joint_to_joint(theta, d, a, alpha)
            pose_for_rectangluar_robot_shape = pose * htranslation3(z=d)
            pose_numeric = pose.subs(subs).evalf()
            next_pose_numeric = next_pose.subs(subs).evalf()
            pose_for_rectangluar_robot_shape_numeric = pose_for_rectangluar_robot_shape.subs(subs).evalf()
            pose_second_cone_numeric = pose_for_rectangluar_robot_shape_numeric if joint_type =='prismatic' else pose_numeric
            colorscales = ['YlGnBu', 'agsunset', 'blues', 'bluered', 'amp']
            colorscale = colorscales[index % len(colorscales)]

            scatter_pose = draw_frame(pose_numeric, labels=f'x{index}-y{index}-z{index}', line_width=5, line_length=0.5)
            joint_1 = go.Cone(
                x=[pose_numeric[0, 3]], y=[pose_numeric[1, 3]], z=[pose_numeric[2, 3]],
                u=[pose_numeric[0, 2]], v=[pose_numeric[1, 2]], w=[pose_numeric[2, 2]],
                anchor='center', name=f'J{index}', showscale=False, colorscale=colorscale, sizemode='absolute', sizeref=0.5)
            joint_2 = go.Cone(
                x=[pose_second_cone_numeric[0, 3]], y=[pose_second_cone_numeric[1, 3]], z=[pose_second_cone_numeric[2, 3]],
                u=[-pose_numeric[0, 2]], v=[-pose_numeric[1, 2]], w=[-pose_numeric[2, 2]],
                anchor='center', name='', showscale=False, colorscale=colorscale, sizemode='absolute', sizeref=0.5)
            line_link = go.Scatter3d(
                x=[pose_numeric[0, 3], pose_for_rectangluar_robot_shape_numeric[0, 3], next_pose_numeric[0, 3]],
                y=[pose_numeric[1, 3], pose_for_rectangluar_robot_shape_numeric[1, 3], next_pose_numeric[1, 3]],
                z=[pose_numeric[2, 3], pose_for_rectangluar_robot_shape_numeric[2, 3], next_pose_numeric[2, 3]],
                mode='lines', name='', line=dict(color='black', width=1))
            scatter_data += scatter_pose[:1] + scatter_pose[2:] + (joint_1, joint_2, line_link)
            pose = next_pose

        scatter_pose = draw_frame(
            pose.subs(subs).evalf(), labels=f'x{index+1}-y{index+1}-z{index+1}', colors=('cyan', 'magenta', 'yellow'),
            line_width=5, line_length=0.5)
        scatter_data += scatter_pose[:1] + scatter_pose[2:]
        x, y, z = scatter_pose[0].x[0], scatter_pose[0].y[0], scatter_pose[0].z[0]
        scatter_data += (go.Scatter3d(x=[x], y=[y], z=[z], mode='markers', name='', marker_size=5, marker_cmin=0),)
        return scatter_data


import numpy as np # import numpy

# relevant constants
GREEK_P = np.pi
EULER_NUMBER = np.e

# print greek_p and euler_number on the screen
print("Archimedes' constant is " + str(GREEK_P))
print("Euler's number is " + str(EULER_NUMBER))

# trigonometric functions
x = np.sin(GREEK_P / 2) # x = 1 expected

# print the result on the screen
print("The sine of a quarter of radiant is " + str(x))

# defining a 1D array in numpy
my_array = np.array([4, 8, 27])
# print myArray on the screen
print("myArray is equal to " + str(my_array))

log10_my_array = np.log10(my_array)

# print the result on the screen
print("The base-10 logarithms of the elements in myArray are")
print(log10_my_array)

'''
Output:
Archimedes' constant is 3.141592653589793
Euler's number is 2.718281828459045
The sine of a quarter of radiant is 1.0
myArray is equal to [ 4  8 27]
The base-10 logarithms of the elements in myArray are
[0.60205999 0.90308999 1.43136376]
'''


# -*- coding: utf-8 -*-mode
import numpy as np
import sys
from scipy.optimize import fmin_bfgs

class logisticRegression:

	def __init__(self):
		# do nothing particularly
		pass

	def fit(self,data,label):
		# data is to be given in a two dimensional numpy array (nData,nVariables)
		# label is to be given in an one dimensional numpy array (nData,) which contains value of either 0 or 1
		self.data = np.hstack((np.ones((data.shape[0],1)),data))
		self.label = label
		self.nData = data.shape[0]
		self.nEta = data.shape[1]+1
		self.eta = fmin_bfgs(self.costFunction,np.zeros(self.nEta),fprime=self.gradient)

	def predict(self,data):
		# data is to be given in a two dimensional numpy array (nData,nVariables)
		data = np.hstack((np.ones((data.shape[0],1)),data))
		return self.sigmoid(np.dot(data,self.eta))

	def sigmoid(self,z):
		# define sigmoid function
		return 1.0 / ( 1.0+np.exp(-z) )

	def costFunction(self,eta):
		# return cost to minimize
		prob = self.sigmoid(np.dot(self.data,eta))
		prob_ = 1.0-prob
		np.place(prob,prob==0.0,sys.float_info.min)
		np.place(prob_,prob_==0.0,sys.float_info.min)
		self.cost = np.dot(self.label,np.log(prob))
		self.cost += np.dot((1.0-self.label),np.log(prob_))
		return -self.cost

	def gradient(self,eta):
		# return gradient of cost function
		grad = np.zeros(self.nEta)
		for i in range(grad.shape[0]):
			grad[i] = ( (self.sigmoid(np.dot(self.data,eta))-self.label)*self.data[:,i] ).sum()
		return grad

	def summary(self):
		# calculate deviance, residual deviance, and Akaike Information Criterion (AIC)
		deviance = -2*self.cost
		fullmodel = -np.log(1.0)*self.nData
		rDeviance = deviance-fullmodel
		aic = deviance+2*self.nEta
		# display information
		print "Estimated Regression Coefficient Values:"
		print self.eta
		print "Log Likelihood:\t\t %f (df=%d)"%(self.cost,self.nEta)
		print "Residual Deviance:\t %f"%(rDeviance)
		print "AIC:\t\t\t %f"%(aic)

	def save(self,name):
		# save object as a file
		with open(name,"wb") as output:
			cPickle.dump(self.__dict__,output,protocol=cPickle.HIGHEST_PROTOCOL)

	def load(self,name):
		# load object from a file
		with open(name,"rb") as input:
			self.__dict__.update(cPickle.load(input))


# -*- coding: utf-8 -*-
"""
Created on Sat Jan 20 07:22:01 2018

@author: Batman
"""
import numpy as np

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

def sigmoidGradient(z):
    g = 1 / (1 + np.exp(-z))
    return g * (1 - g)

def costFunction(theta, X, y):
    m = y.size
    if m == 0:
        return np.inf

    h = sigmoid(X.dot(theta))
    hMinusOne = h-1
    hMinusOne[hMinusOne < 1e-315] = 1e-315
    J = (1/m)*(np.log(h).dot(-y)-np.log(hMinusOne).dot(1-y))

    return float(J)

def costFunctionReg(theta, X, y, learningRate):
    m = y.size
    if m == 0:
        return np.inf

    h = sigmoid(X.dot(theta))
    hMinusOne = h-1
    hMinusOne[hMinusOne < 1e-315] = 1e-315
    J = (1/m)*(np.log(h).dot(-y)-np.log(hMinusOne).dot(1-y)) + (learningRate / (2 * m)) * np.sum(theta**2)

    return float(J), h

def gradient(theta, X, y):
    m = y.size
    if m == 0:
        return np.inf

    error = sigmoid(X.dot(theta)) - y

    return X.T.dot(error) / m

def gradientReg(theta, X, y, learningRate):
    m = y.size
    if m == 0:
        return np.inf

    error = sigmoid(X.dot(theta)) - y

    return (X.T.dot(error) + learningRate*np.r_[np.array([0]), theta[1:]]) / m

def predict(theta, X, threshold=0.5):
    p = sigmoid(X.dot(theta.T)) >= threshold
    return(p.astype('int'))


#Program: Chi-Square fitting to a straight line
#NOTICE: fit y=a+bx.
#Give out a and b with error bars, no output goodness-of-fit.
#REFERENCE http://mathworld.wolfram.com/LeastSquaresFitting.html

import numpy as np
import pylab

def fit(x, y):
    data_len = np.float64( len(x) )

    xmean = x.mean()
    ymean = y.mean()
    Sxx   = (x*x).sum()-xmean*xmean*data_len
    Syy   = (y*y).sum()-ymean*ymean*data_len
    Sxy   = (x*y).sum()-xmean*ymean*data_len
    S     = np.sqrt( (Syy-Sxy*Sxy/Sxx)/(data_len-2.0) )

    b=Sxy/Sxx
    a=ymean-b*xmean
    da=S*np.sqrt( 1.0/data_len + xmean*xmean/Sxx )
    db=S/np.sqrt( Sxx )

    print "parameters:", a   , b
    print "param err :", da  , db
    print "relative  :", da/a, db/b

    return np.array([a,b]),np.array([da,db])

def fit_plot(x, y, popt, xmin="none", xmax="none", save=0):
    pylab.figure()
    pylab.xlabel('$x$')
    pylab.ylabel('$y$')
    pylab.plot(x, y,'bo',label='data')

    if xmin=="none":
       xmin=np.amin(x)
    if xmax=="none":
       xmax=np.amax(x)
    fitx =np.linspace(xmin,xmax,512,endpoint=True)
    fity =popt[0]+fitx*popt[1]
    pylab.plot(fitx, fity,'r-',label='fit curve')

    pylab.legend(loc='upper right',frameon=False)

    if save==0:
       pylab.show()
    else:
       pylab.savefig('fit.pdf',bbox_inches='tight')


import pandas as pd
import pymc3 as pm
import numpy as np
import arviz as az
from arviz_json import get_dag, arviz_to_json

## Discrete Variables Model
## Reference: https://docs.pymc.io/notebooks/getting_started.html#Case-study-2:-Coal-mining-disasters
#data
disaster_data = pd.Series([4, 5, 4, 0, 1, 4, 3, 4, 0, 6, 3, 3, 4, 0, 2, 6,
                           3, 3, 5, 4, 5, 3, 1, 4, 4, 1, 5, 5, 3, 4, 2, 5,
                           2, 2, 3, 4, 2, 1, 3, np.nan, 2, 1, 1, 1, 1, 3, 0, 0,
                           1, 0, 1, 1, 0, 0, 3, 1, 0, 3, 2, 2, 0, 1, 1, 1,
                           0, 1, 0, 1, 0, 0, 0, 2, 1, 0, 0, 0, 1, 1, 0, 2,
                           3, 3, 1, np.nan, 2, 1, 1, 1, 1, 2, 4, 2, 0, 0, 1, 4,
                           0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1])
years = np.arange(1851, 1962)
years_missing=[1890,1935]

#model-inference
fileName='coal_mining_disasters_PyMC3'
samples=10000
tune=10000
chains=2
coords = {"year": years}
with pm.Model(coords=coords) as disaster_model:
    switchpoint = pm.DiscreteUniform('switchpoint', lower=years.min(), upper=years.max(), testval=1900)
    early_rate = pm.Exponential('early_rate', 1)
    late_rate = pm.Exponential('late_rate', 1)
    rate = pm.math.switch(switchpoint >= years, early_rate, late_rate)
    disasters = pm.Poisson('disasters', rate, observed=disaster_data, dims='year')
	#inference
	trace = pm.sample(samples, chains=chains, tune=tune)
    prior = pm.sample_prior_predictive(samples=samples)
    posterior_predictive = pm.sample_posterior_predictive(trace,samples=samples)

## STEP 1
# will also capture all the sampler statistics
data = az.from_pymc3(trace=trace, prior=prior, posterior_predictive=posterior_predictive)

## STEP 2
# extract dag
dag = get_dag(disaster_model)
# insert dag into sampler stat attributes
data.sample_stats.attrs["graph"] = str(dag)

## STEP 3
# save data
arviz_to_json(data, fileName+'.npz')


""" Python code for a Fractal Fern the IFS defined in Table 3.8.3 of
Michael Barnsley's "Fractals Everywhere" just type "Fern" in the Matlab
command window to run it"""

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

## clear all
iterations = 10000 ##number of iterations
## recall, the transformations are of the form A*[x,y]+t
## and, there are four such transformations ...
A1 = np.array([0, 0, 0, 0.16])
A2 = np.array([0.85, 0.04, -0.04, 0.85]),
A3 = np.array([0.2, -0.26, 0.23, 0.22])
A4 = np.array([-0.15, 0.28, 0.26, 0.24])
t1 = np.array([0, 0])
t2 = np.array([0, 1.6])
t3 = np.array([0, 1.6])
t4 = np.array([0, 0.44])
## here are the probabilities with which each transformation is applied
## notice that p1+p2+p3+p4 = 1
p1 = 0.01
p2 = 0.85
p3 = 0.07
p4 = 0.07
## the initial point
x = np.empty([0, 0])
y = np.empty([0, 0])

x[1] = 0
y[1] = 0

## but, let's write the (x,y) points as a vector, v
v = np.array([0, 0]) ## here is the intial point
import ipdb; ipdp.set_trace()

# for n = 2:iterations
 # ## choose a random number, k, between 0 and 1
 # k = rand
 # ## depending on your random number ...
 # ## do one of the four transformations to get a new point
 # if k<p1
   # v = A1*v+t1
 # elseif k<p1+p2
   # v = A2*v+t2
 # elseif k<p1+p2+p3
   # v = A3*v+t3
 # else
   # v = A4*v+t4
 # end
 # ## now, go back and define your (x,y) point as elements of the vector v
 # x(n) = v(1)
 # y(n) = v(2)
# end
# ##now, let's plot all those (x,y) points that we just computed!
# ## opengl software ##this is a fix to ensure that Matlab won't crash :)
# hold off
# plot(x,y,'.','Color',[0.5 0.5 0],'Markersize',1)
# ## uncomment the next two lines to see the initial point in red
# ## hold on
# ## plot(x(1),y(1),'r*','Markersize',4)
# axis('equal','off')
# set(gcf, 'color','white')


import laff
import numpy as np
from math import sqrt

def norm2( x ):

	"""
    Compute the 2-norm of a vector, returning alpha

    x can be a row or column vector.
    """
	assert type(x) is np.matrix and len(x.shape) is 2, \
		"laff.norm2: vector x must be a 2D numpy.matrix"

	m, n = np.shape(x)
	assert m is 1 or n is 1, \
		"laff.norm2: x is not a vector"

	#Ensure that we don't modify x in
	#any way by copying it to a new vector, y
	y = np.matrix( np.zeros( (m,n) ) )
	laff.copy( x, y )

	#Initialize variables that we will use to appropriate values
	alpha = 0
	maxval = y[ 0, 0 ]

	if m is 1: #y is a row
		#Find a value to scale by to avoid under/overflow
		for i in range(n):
			if abs(y[ 0, i ]) > maxval:
				maxval = abs(y[ 0, i ])

	elif n is 1: #y is a column
		#Find a value to scale by to avoid under/overflow
		for i in range(m):
			if abs(y[ i, 0 ]) > maxval:
				maxval = abs(y[ i, 0 ])

	#If y is the zero vector, return 0
	if abs(maxval) < 1e-7:
		return 0

	#Scale all of the values by 1/maxval to prevent under/overflow
	laff.scal( 1.0/maxval, y )

	alpha = maxval * sqrt( laff.dot( y, y ) )

	return alpha


# based on http://cs231n.github.io/python-numpy-tutorial/

# If you want to use this material in your own trainning please let me know.

#%%
import numpy as np

# documentation available at
#http://docs.scipy.org/doc/numpy-1.10.1/reference/routines.indexing.html

# Create the following rank 2 array with shape (3, 4)
# [[ 1  2  3  4]
#  [ 5  6  7  8]
#  [ 9 10 11 12]]
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
print(a)
print(a[0, 1])   # Prints "2"

#%%

# Use slicing to pull out the subarray consisting of the first 2 rows
# and columns 1 and 2; b is the following array of shape (2, 2):
# [[2 3]
#  [6 7]]
b = a[:2, 1:3]
print(b)

#%% A slice of an array is a view into the same data, so modifying it
# will modify the original array.
print(a[0, 1])   # Prints "2"
b[0, 0] = 77    # b[0, 0] is the same piece of data as a[0, 1]
print( a[0, 1])   # Prints "77"

#%% Create the following rank 2 array with shape (3, 4)
# [[ 1  2  3  4]
#  [ 5  6  7  8]
#  [ 9 10 11 12]]
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])

# Two ways of accessing the data in the middle row of the array.
# Mixing integer indexing with slices yields an array of lower rank,
# while using only slices yields an array of the same rank as the
# original array:
row_r1 = a[1, :]    # Rank 1 view of the second row of a
row_r2 = a[1:2, :]  # Rank 2 view of the second row of a
print(row_r1, row_r1.shape)  # Prints "[5 6 7 8] (4,)"
print(row_r2, row_r2.shape)  # Prints "[[5 6 7 8]] (1, 4)"

#%% We can make the same distinction when accessing columns of an array:
col_r1 = a[:, 1]
col_r2 = a[:, 1:2]
print(col_r1, col_r1.shape)  # Prints "[ 2  6 10] (3,)"
print(col_r2, col_r2.shape)  # Prints "[[ 2]
                            #          [ 6]
                            #          [10]] (3, 1)"

#%% Integer array indexing:

a = np.array([[1,2], [3, 4], [5, 6]])

# An example of integer array indexing.
# The returned array will have shape (3,) and
print(a[[0, 1, 2], [0, 1, 0]])  # Prints "[1 4 5]"

# The above example of integer array indexing is equivalent to this:
print( np.array([a[0, 0], a[1, 1], a[2, 0]]))  # Prints "[1 4 5]"

# When using integer array indexing, you can reuse the same
# element from the source array:
print( a[[0, 0], [1, 1]])  # Prints "[2 2]"

# Equivalent to the previous integer array indexing example
print(np.array([a[0, 1], a[0, 1]]))  # Prints "[2 2]"

#%%  Create a new array from which we will select elements
a = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])

print(a)  # prints "array([[ 1,  2,  3],
         #                [ 4,  5,  6],
         #                [ 7,  8,  9],
         #                [10, 11, 12]])"

# Create an array of indices
b = np.array([0, 2, 0, 1])

# Select one element from each row of a using the indices in b
print( a[np.arange(4), b])  # Prints "[ 1  6  7 11]"

#%% Mutate one element from each row of a using the indices in b
a[np.arange(4), b] += 10

print( a)  # prints "array([[11,  2,  3],
         #                [ 4,  5, 16],
         #                [17,  8,  9],
         #                [10, 21, 12]])

#Boolean array indexing

a = np.array([[1,2], [3, 4], [5, 6]])

bool_idx = (a > 2)  # Find the elements of a that are bigger than 2;
                    # this returns a numpy array of Booleans of the same
                    # shape as a, where each slot of bool_idx tells
                    # whether that element of a is > 2.

print( bool_idx)      # Prints "[[False False]
                    #          [ True  True]
                    #          [ True  True]]"

# We use boolean array indexing to construct a rank 1 array
# consisting of the elements of a corresponding to the True values
# of bool_idx
print(a[bool_idx])  # Prints "[3 4 5 6]"

# We can do all of the above in a single concise statement:
print(a[a > 2])     # Prints "[3 4 5 6]"


from typing import List, Tuple
import numpy as np
import pandas as pd
from scipy import stats
import random

def seed_everything(seed):
    random.seed(seed)
    np.random.seed(seed)

def gini_index(targets) -> float:
    """
    Computes the gini index for a given target labels.
    Arguments:
        targets: A numpy array of shape (n_samples, )
    Returns:
        The gini index for the given target labels.
    """
    label_count = np.bincount(targets)
    return 1 - np.sum((label_count / len(targets)) ** 2)

def entropy(targets) -> float:
    """
    Computes the entropy for a given target labels.
    Arguments:
        targets: A numpy array of shape (n_samples, )
    Returns:
        The entropy for the given target labels.
    """
    label_count = np.bincount(targets).astype(float)
    label_count += 1e-7
    return -np.sum((label_count / len(targets)) * np.log2(label_count / len(targets)))

def gini_gain(data: pd.DataFrame, attribute: str, targets: pd.Series) -> float:
    """
    Computes the gini gain for a given attribute and target labels.
    Arguments:
        data: A numpy array of shape (n_samples, n_features)
        attribute: Attribute name to compute the gini gain for
        targets: A numpy array of shape (n_samples, )
    Returns:
        The gini gain for the given attribute and target labels.
    """
    init_gini = gini_index(targets)  # Initial gini index
    # All possible values of the attribute
    attribute_values = np.unique(data.iloc[:][attribute])
    new_gini = 0  # Initialize the new gini index

    for value in attribute_values:  # For each value of the attribute
        # Get the data for the current value
        sub_data = data.iloc[:][data.iloc[:][attribute] == value]
        # Get the targets for the current value
        sub_targets = targets.iloc[:][data.iloc[:][attribute] == value]
        sub_gini = gini_index(sub_targets)
        new_gini += len(sub_data) / len(data) * sub_gini
    return init_gini - new_gini  # Return the gini gain

def information_gain(data: pd.DataFrame, attribute: str, targets: pd.Series) -> float:
    """
    Computes the information gain for a given attribute and target labels.
    Arguments:
        data: A numpy array of shape (n_samples, n_features)
        attribute: Attribute name to compute the information gain for
        targets: A numpy array of shape (n_samples, )
    Returns:
        The information gain for the given attribute and target labels.
    """
    init_entropy = entropy(targets)
    attribute_values = np.unique(data.iloc[:][attribute])
    new_entropy = 0
    for value in attribute_values:
        # Get the data for the current value
        sub_data = data.iloc[:][data.iloc[:][attribute] == value]
        # Get the targets for the current value
        sub_targets = targets.iloc[:][data.iloc[:][attribute] == value]
        sub_entropy = entropy(sub_targets)
        new_entropy += len(sub_data) / len(data) * sub_entropy
    return init_entropy - new_entropy

def shuffle_dataset(dataset):
    """
    Shuffles the dataset and the target labels.
    Arguments:
        data: A numpy array of shape (n_samples, n_features)
        targets: A numpy array of shape (n_samples, )
    Returns:
        A shuffled dataset and target labels.
    """
    dataset.df = dataset.df.sample(frac=1, replace=False)

def split_dataset(
    data: pd.DataFrame,
    targets: pd.Series,
    split_size: float = 0.2
) -> Tuple[Tuple[pd.DataFrame, pd.Series], Tuple[pd.DataFrame, pd.Series]]:
    num_samples = data.shape[0]
    num_train = int(num_samples*split_size)

    train_data = data.iloc[:num_train]
    train_targets = targets.iloc[:num_train]

    valid_data = data.iloc[num_train:]
    valid_targets = targets.iloc[num_train:]

    return (train_data, train_targets), (valid_data, valid_targets)

def chi2_contingency(matrx):
    return stats.chi2_contingency(matrx)