#!/usr/bin/env python3
# Marcos del Cueto
import math
import matplotlib.pyplot as plt
import random
import numpy as np
from sklearn import preprocessing
from sklearn.model_selection import LeaveOneOut
from sklearn.metrics import mean_squared_error
from sklearn import neighbors
from matplotlib.ticker import (MultipleLocator)

Ntest = 10 # select here number of test points to print
file_name='Figure_2_3.png'

### 1) Generate data
list_x = []
list_y = []
random.seed(19)
for i in np.arange(5, 7, 0.2):
    x = i
    y = math.exp(x)
    list_x.append(x)
    list_y.append(y)
list_x = np.array(list_x).reshape(-1, 1)
list_y = np.array(list_y)
basic_x = np.arange(4.9, 7.0, 0.01)
basic_y = [math.exp(x) for x in basic_x]
### 2) Leave one out
best_rmse = 0
best_k = 0
possible_k = [2]
for k in possible_k:
    y_pred = []
    x_pred = []
    X = np.array(list_x).reshape(-1, 1)
    validation=LeaveOneOut().split(X)
    for train_index, test_index in validation:
        X_train, X_test = list_x[train_index], list_x[test_index]
        y_train, y_test = list_y[train_index], list_y[test_index]
        # scale data
        scaler = preprocessing.StandardScaler().fit(X_train)
        X_train_scaled = scaler.transform(X_train)
        X_test_scaled = scaler.transform(X_test)
        knn = neighbors.KNeighborsRegressor(n_neighbors=k, weights='uniform')
        pred = knn.fit(X_train_scaled, y_train).predict(X_test_scaled)
        y_pred.append(pred)
        x_pred.append(X_test)
    mse = mean_squared_error(y_pred,list_y)
    rmse = np.sqrt(mse)
    print('k: %i, RMSE: %f' %(k, rmse))
    if rmse < best_rmse or k==possible_k[0]:
        best_rmse = rmse
        best_k = k
print('BEST:')
print("kNN, k=%i, RMSE: %f" %(best_k,best_rmse))
# Plot graph
#Ntest = Ntest+1
plt.plot(basic_x,basic_y,color='C0',linestyle='dashed',linewidth=1)
plt.scatter(list_x, list_y,color='C0')
plt.scatter(x_pred[0:Ntest], y_pred[0:Ntest],color='C1')
plt.xlabel('$x$',fontsize=15)
plt.ylabel('$y$',fontsize=15)
plt.xticks(np.arange(5,7,0.2))
plt.xlim(4.92,6.88)
plt.ylim(100,1000)
axes = plt.gca()
axes.xaxis.set_minor_locator(MultipleLocator(0.05))
# Save plot into png
plt.savefig(file_name,format='png',dpi=600)
plt.close()


import numpy
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
import random

print('----------Question 1--------------------')
meanclassA = numpy.random.randn(3)
print('mean of classA', meanclassA)
meanclassB = numpy.random.randn(3)
print('mean of classB', meanclassB)
cov = [[100, 0, 0], [0, 50, 0], [0, 0, 1]]  # diagonal covariance
print('Covariance matrix', cov)

xclassA, yclassA, zclassA = numpy.random.multivariate_normal(
    meanclassA, cov, 1000).T

xclassB, yclassB, zclassB = numpy.random.multivariate_normal(
    meanclassB, cov, 1000).T

fig = plt.figure()
ax = fig.add_subplot(2, 2, 1, projection='3d')
ax.scatter(xclassA, yclassA, zclassA,label= "classA", color='red', marker='x')
ax.scatter(xclassB, yclassB, zclassB, label= "classB", color='blue', marker='o')
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
plt.legend()

ax = fig.add_subplot(2, 2, 2, projection='3d')
ax.scatter(xclassA, yclassA, zclassA, label= "classA", color='red', marker='x')
ax.plot(xclassA, zclassA, 'y+', label= "xz projection", zdir='y', zs=-50)
ax.plot(yclassA, zclassA, 'g+', label= "yz projection", zdir='x', zs=-50)
ax.plot(xclassA, yclassA, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()

ax = fig.add_subplot(2, 2, 3, projection='3d')
ax.scatter(xclassB, yclassB, zclassB,label= "classB", color='blue', marker='o')
ax.plot(xclassB, zclassB, 'y+',  label= "xz projection", zdir='y', zs=-50)
ax.plot(yclassB, zclassB, 'g+',  label= "yz projection", zdir='x', zs=-50)
ax.plot(xclassB, yclassB, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()
plt.title('Question1, different mean, and diagonal covariance matrix')
plt.show()

print('----------Question 2--------------------')
matrixSize = 3
A = numpy.random.rand(matrixSize,matrixSize)
cov = numpy.dot(A,A.transpose()) #PSD Cov matrix
print('Cov matrix PSD', cov)
meanclassA = numpy.random.randn(3)
print('mean of classA', meanclassA)
meanclassB = numpy.random.randn(3)
print('mean of classB', meanclassB)

xclassA, yclassA, zclassA = numpy.random.multivariate_normal(
    meanclassA, cov, 1000).T

xclassB, yclassB, zclassB = numpy.random.multivariate_normal(
    meanclassB, cov, 1000).T

fig = plt.figure()
ax = fig.add_subplot(2, 2, 1, projection='3d')
ax.scatter(xclassA, yclassA, zclassA,label= "classA", color='red', marker='x')
ax.scatter(xclassB, yclassB, zclassB, label= "classB", color='blue', marker='o')
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
plt.legend()

ax = fig.add_subplot(2, 2, 2, projection='3d')
ax.scatter(xclassA, yclassA, zclassA, label= "classA", color='red', marker='x')
ax.plot(xclassA, zclassA, 'y+', label= "xz projection", zdir='y', zs=-5)
ax.plot(yclassA, zclassA, 'g+', label= "yz projection", zdir='x', zs=-5)
ax.plot(xclassA, yclassA, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()

ax = fig.add_subplot(2, 2, 3, projection='3d')
ax.scatter(xclassB, yclassB, zclassB,label= "classB", color='blue', marker='o')
ax.plot(xclassB, zclassB, 'y+',  label= "xz projection", zdir='y', zs=-5)
ax.plot(yclassB, zclassB, 'g+',  label= "yz projection", zdir='x', zs=-5)
ax.plot(xclassB, yclassB, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()
plt.title('Question2, different mean, and PSD matrix')
plt.show()

print('----------Question 3--------------------')
matrixSize = 3
A = numpy.random.rand(matrixSize,matrixSize)
covClassA = numpy.dot(A,A.transpose()) #PSD Cov matrix
print('Covariance matrix for class A', covClassA)
A = numpy.random.rand(matrixSize,matrixSize)
covClassB = numpy.dot(A,A.transpose()) #PSD Cov matrix
print('Covariance matrix for class B', covClassB)
mean = numpy.random.randn(3)
print('mean', mean)

xclassA, yclassA, zclassA = numpy.random.multivariate_normal(
    mean, covClassA, 1000).T

xclassB, yclassB, zclassB = numpy.random.multivariate_normal(
    mean, covClassB, 1000).T

fig = plt.figure()
ax = fig.add_subplot(2, 2, 1, projection='3d')
ax.scatter(xclassA, yclassA, zclassA,label= "classA", color='red', marker='x')
ax.scatter(xclassB, yclassB, zclassB, label= "classB", color='blue', marker='o')
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
plt.legend()

ax = fig.add_subplot(2, 2, 2, projection='3d')
ax.scatter(xclassA, yclassA, zclassA, label= "classA", color='red', marker='x')
ax.plot(xclassA, zclassA, 'y+', label= "xz projection", zdir='y', zs=-5)
ax.plot(yclassA, zclassA, 'g+', label= "yz projection", zdir='x', zs=-5)
ax.plot(xclassA, yclassA, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()

ax = fig.add_subplot(2, 2, 3, projection='3d')
ax.scatter(xclassB, yclassB, zclassB,label= "classB", color='blue', marker='o')
ax.plot(xclassB, zclassB, 'y+',  label= "xz projection", zdir='y', zs=-5)
ax.plot(yclassB, zclassB, 'g+',  label= "yz projection", zdir='x', zs=-5)
ax.plot(xclassB, yclassB, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()
plt.title('Question3, same mean, and different cov matrix')
plt.show()


# Problem: https://projecteuler.net/problem=164

"""
    Let DP[n,a,b] indicate the number of such a number described in the problem statement
                  that has n-digit and a and b being the last two digits.
    For a fixed b and c, DP[n,b,c] = sum_{a | a + b + c <= 9}(DP[n-1,a,b]).
"""

import numpy as np

N = 20

ans = 0

DP = np.zeros((N+1, 10, 10), dtype = np.int64)

for i in range(1, 10):
    DP[1, 0, i] = 1

for n in range(2, N+1):
    for a in range(10):
        for b in range(10):
            for c in range(9-a-b+1):
                DP[n, b, c] = DP[n, b, c] + DP[n-1, a, b]

for i in range(10):
    for j in range(10):
        ans = ans + DP[N, i, j]

print(ans)


"""

https://apmonitor.com/pdc/index.php/Main/ModelSimulation
https://apmonitor.com/pdc/index.php/Main/SecondOrderSystems

"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from scipy import signal
from sympy.abc import s, t
from sympy.integrals import inverse_laplace_transform
from math import *

# tau * dy2/dt2 + 2*zeta*tau*dy/dt + y = Kp*u
Kp = 2.0    # gain
tau = 1.0   # time constant
zeta = 0.5 # damping factor
theta = 0.0 # no time delay
du = 1.0    # change in u

# (1) Transfer Function
num = [Kp]
den = [tau**2,2*zeta*tau,1]
sys1 = signal.TransferFunction(num,den)
t1,y1 = signal.step(sys1)

# (2) State Space
A = [[0.0,1.0],[-1.0/tau**2,-2.0*zeta/tau]]
B = [[0.0],[Kp/tau**2]]
C = [1.0,0.0]
D = 0.0
sys2 = signal.StateSpace(A,B,C,D)
t2,y2 = signal.step(sys2)

# (3) ODE Integrator
def model3(x,t):
    y = x[0]
    dydt = x[1]
    dy2dt2 = (-2.0*zeta*tau*dydt - y + Kp*du)/tau**2
    return [dydt,dy2dt2]
t3 = np.linspace(0,25,100)
x3 = odeint(model3,[0,0],t3)
y3 = x3[:,0]

# (4) Closed Form
# ay'' + by' + cy = K*u(t-theta)
# u(t) is the unit step function
# a = tau*tau
# b = 2*zeta*tau
# c = 1
# theta = 0
# This differential equation has 3 different solutions
# based on the value of zeta
def closed(t):
    # magnitude of step function
    M = 1
    if zeta < 1: # underdamped
        z = sqrt(1 - zeta*zeta)
        A = cos(t/tau*z)
        B = zeta/z * sin(t/tau*z)
        v = 1 - exp(-zeta*t/tau)*(A + B)
    elif zeta > 1: # overdamped
        z = sqrt(zeta*zeta - 1)
        A = cosh(t/tau*z)
        B = zeta/z * sinh(t/tau*z)
        v = 1 - exp(-zeta*t/tau)*(A + B)
    else: # critically damped
        v = 1 - (1 + t/tau)*exp(-t/tau)
    return Kp*M*v
y4 = np.array([closed(t) for t in t3])

# (5) Symbolic
# Multiply transfer function G(s) with unit step U(s)
# then take the inverse laplace transform for equation
G = Kp/(tau*tau*s*s + 2*zeta*tau*s + 1)
U = 1/s
Y = inverse_laplace_transform(G*U, s, t)
y5 = np.array([Y.subs(t, ts) for ts in t3])

print("Transfer Function: {}".format(G))

plt.figure(1)
plt.plot(t1,y1*du,'b--',linewidth=5,label='Transfer Function')
plt.plot(t2,y2*du,'g:',linewidth=4,label='State Space')
plt.plot(t3,y3,'r-',linewidth=3,label='ODE Integrator')
plt.plot(t3,y4,'p-',linewidth=2,label='Closed Form')
plt.plot(t3,y5,'c-',linewidth=1,label='Symbolic')
y_ss = Kp * du
plt.plot([0,max(t1)],[y_ss,y_ss],'k:')
plt.xlim([0,max(t1)])
plt.xlabel('Time')
plt.ylabel('Response (y)')
plt.legend(loc='best')
plt.savefig('2nd_order.png')
plt.show()


"""
  Name     : c10_44_implied_vol_binary_search.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""

from scipy import log,exp,sqrt,stats
S=42;X=40;T=0.5;r=0.01;c=3.0
def bsCall(S,X,T,r,sigma):
    d1=(log(S/X)+(r+sigma*sigma/2.)*T)/(sigma*sqrt(T))
    d2 = d1-sigma*sqrt(T)
    return S*stats.norm.cdf(d1)-X*exp(-r*T)*stats.norm.cdf(d2)
#
def impliedVolBinary(S,X,T,r,c):
    k=1
    volLow=0.001
    volHigh=1.0
    cLow=bsCall(S,X,T,r,volLow)
    cHigh=bsCall(S,X,T,r,volHigh)
    if cLow>c or cHigh<c:
        raise ValueError
    while k ==1:
        cLow=bsCall(S,X,T,r,volLow)
        cHigh=bsCall(S,X,T,r,volHigh)
        volMid=(volLow+volHigh)/2.0
        cMid=bsCall(S,X,T,r,volMid)
        if abs(cHigh-cLow)<0.01:
            k=2
        elif cMid>c:
            volHigh=volMid
        else:
            volLow=volMid
    return volMid, cLow, cHigh
#
print("Vol,     cLow,      cHigh")
print(impliedVolBinary(S,X,T,r,c))


import math
from copy import deepcopy

import numpy as np

from DecisonTree import Leaf, Question, DecisionNode, class_counts, unique_vals
from utils import *

"""
Make the imports of python packages needed
"""

class ID3:
    def __init__(self, label_names: list, min_for_pruning=0, target_attribute='diagnosis'):
        self.label_names = label_names
        self.target_attribute = target_attribute
        self.tree_root = None
        self.used_features = set()
        self.min_for_pruning = min_for_pruning

    @staticmethod
    def entropy(rows: np.ndarray, labels: np.ndarray):
        """
        Calculate the entropy of a distribution for the classes probability values.
        :param rows: array of samples
        :param labels: rows data labels.
        :return: entropy value.
        """
        # TODO:
        #  Calculate the entropy of the data as shown in the class.
        #  - You can use counts as a helper dictionary of label -> count, or implement something else.

        counts = class_counts(rows, labels)
        impurity = 0.0

        # ====== YOUR CODE: ======
        entropy_val = 0
        num_of_labels = len(labels)
        for label, count in counts.items():
            entropy_val += count / num_of_labels * math.log2(count / num_of_labels)
        impurity = -1 * entropy_val
        # ========================

        return impurity

    def info_gain(self, left, left_labels, right, right_labels, current_uncertainty):
        """
        Calculate the information gain, as the uncertainty of the starting node, minus the weighted impurity of
        two child nodes.
        :param left: the left child rows.
        :param left_labels: the left child labels.
        :param right: the right child rows.
        :param right_labels: the right child labels.
        :param current_uncertainty: the current uncertainty of the current node
        :return: the info gain for splitting the current node into the two children left and right.
        """
        # TODO:
        #  - Calculate the entropy of the data of the left and the right child.
        #  - Calculate the info gain as shown in class.
        assert (len(left) == len(left_labels)) and (len(right) == len(right_labels)), \
            'The split of current node is not right, rows size should be equal to labels size.'

        info_gain_value = 0.0
        # ====== YOUR CODE: ======
        total_samples = len(left) + len(right)
        left_weight = len(left) / total_samples
        right_weight = len(right) / total_samples
        left_entropy = self.entropy(left, left_labels)
        right_entropy = self.entropy(right, right_labels)
        info_gain_value = current_uncertainty - (left_weight * left_entropy + right_weight * right_entropy)
        # ========================

        return info_gain_value

    def partition(self, rows, labels, question: Question, current_uncertainty):
        """
        Partitions the rows by the question.
        :param rows: array of samples
        :param labels: rows data labels.
        :param question: an instance of the Question which we will use to partition the data.
        :param current_uncertainty: the current uncertainty of the current node
        :return: Tuple of (gain, true_rows, true_labels, false_rows, false_labels)
        """
        # TODO:
        #   - For each row in the dataset, check if it matches the question.
        #   - If so, add it to 'true rows', otherwise, add it to 'false rows'.
        #   - Calculate the info gain using the `info_gain` method.

        gain, true_rows, true_labels, false_rows, false_labels = None, None, None, None, None
        assert len(rows) == len(labels), 'Rows size should be equal to labels size.'

        # ====== YOUR CODE: ======
        true_rows = []
        true_labels = []
        false_rows = []
        false_labels = []
        for row, label in zip(rows, labels):
            if question.match(row):
                true_rows.append(row)
                true_labels.append(label)
            else:
                false_rows.append(row)
                false_labels.append(label)
        gain = self.info_gain(false_rows, false_labels, true_rows, true_labels, current_uncertainty)
        # ========================

        return gain, true_rows, true_labels, false_rows, false_labels

    def find_best_split(self, rows, labels):
        """
        Find the best question to ask by iterating over every feature / value and calculating the information gain.
        :param rows: array of samples
        :param labels: rows data labels.
        :return: Tuple of (best_gain, best_question, best_true_rows, best_true_labels, best_false_rows, best_false_labels)
        """
        # TODO:
        #   - For each feature of the dataset, build a proper question to partition the dataset using this feature.
        #   - find the best feature to split the data. (using the `partition` method)
        best_gain = - math.inf  # keep track of the best information gain
        best_question = None  # keep train of the feature / value that produced it
        best_false_rows, best_false_labels = None, None
        best_true_rows, best_true_labels = None, None
        current_uncertainty = self.entropy(rows, labels)

        # ====== YOUR CODE: ======
        attributes_names = deepcopy(self.label_names)
        attributes_names.remove(self.target_attribute)
        for attribute_index, attribute_name in enumerate(attributes_names):
            values_for_column = unique_vals(rows, attribute_index)
            values_for_column = list(values_for_column)
            values_for_column.sort()
            for val1, val2 in zip(values_for_column[:-1], values_for_column[1:]):
                avg_value = (val1 + val2) / 2
                temp_question = Question(attribute_name, attribute_index, avg_value)
                t_gain, t_true_rows, t_true_labels, t_false_rows, t_false_labels = self.partition(rows, labels,
                                                                                temp_question,
                                                                                current_uncertainty)
                if t_gain >= best_gain:
                    best_gain = t_gain
                    best_question = temp_question
                    best_false_rows = t_false_rows
                    best_false_labels = t_false_labels
                    best_true_rows = t_true_rows
                    best_true_labels = t_true_labels
        # ========================

        return best_gain, best_question, best_true_rows, best_true_labels, best_false_rows, best_false_labels

    def build_tree(self, rows, labels):
        """
        Build the decision Tree in recursion.
        :param rows: array of samples
        :param labels: rows data labels.
        :return: a Question node, This records the best feature / value to ask at this point, depending on the answer.
                or leaf if we have to prune this branch (in which cases ?)

        """
        # TODO:
        #   - Try partitioning the dataset using the feature that produces the highest gain.
        #   - Recursively build the true, false branches.
        #   - Build the Question node which contains the best question with true_branch, false_branch as children
        best_question = None
        true_branch, false_branch = None, None

        # ====== YOUR CODE: ======
        best_gain, best_question, best_true_rows, best_true_labels, best_false_rows, best_false_labels = self.find_best_split(
            rows, labels)
        if best_question is None:
            return Leaf(rows, labels)
        if len(best_true_rows) <= self.min_for_pruning or len(class_counts(best_true_rows, best_true_labels)) == 1:
            true_branch = Leaf(best_true_rows, best_true_labels)
        else:
            true_branch = self.build_tree(best_true_rows, best_true_labels)
        if len(best_false_rows) <= self.min_for_pruning or len(class_counts(best_false_rows, best_false_labels)) == 1:
            false_branch = Leaf(best_false_rows, best_false_labels)
        else:
            false_branch = self.build_tree(best_false_rows, best_false_labels)
        self.used_features.add(best_question.column)
        # ========================

        return DecisionNode(best_question, true_branch, false_branch)

    def fit(self, x_train, y_train):
        """
        Trains the ID3 model. By building the tree.
        :param x_train: A labeled training data.
        :param y_train: training data labels.
        """
        # TODO: Build the tree that fits the input data and save the root to self.tree_root

        # ====== YOUR CODE: ======
        self.tree_root = self.build_tree(x_train, y_train)
        # ========================

    def predict_sample(self, row, node: DecisionNode or Leaf = None):
        """
        Predict the most likely class for single sample in subtree of the given node.
        :param row: vector of shape (1,D).
        :return: The row prediction.
        """
        # TODO: Implement ID3 class prediction for set of data.
        #   - Decide whether to follow the true-branch or the false-branch.
        #   - Compare the feature / value stored in the node, to the example we're considering.

        if node is None:
            node = self.tree_root
        prediction = None

        # ====== YOUR CODE: ======
        # Traverse tree nodes as long as we didn't arrive at a leaf
        while isinstance(node, DecisionNode):
            # This is a tree node, so we need to go down the tree based on the row value
            # print(Question)
            # print(row)
            if node.question.match(row):
                # Going to true branch
                node = node.true_branch
            else:
                # Going to false branch
                node = node.false_branch
        if isinstance(node, Leaf):
            # Reached a leaf we can classify by
            max_label_count = -np.inf
            label_count_dict = node.predictions
            for label, label_count in label_count_dict.items():
                if label_count >= max_label_count:
                    max_label_count = label_count
                    prediction = label
        # ========================

        return prediction

    def predict(self, rows):
        """
        Predict the most likely class for each sample in a given vector.
        :param rows: vector of shape (N,D) where N is the number of samples.
        :return: A vector of shape (N,) containing the predicted classes.
        """
        # TODO:
        #  Implement ID3 class prediction for set of data.

        y_pred = None

        # ====== YOUR CODE: ======
        y_pred = np.array([])
        for row in rows:
            y_pred = np.append(y_pred, self.predict_sample(row, None))
        # ========================

        return y_pred


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

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1Pymr8RvZdZkyufT_rl1nNGpH-1CkjNup

Same as linear Regression except , we pass the output through sigmoid activation function for classification
"""

import numpy as np

class LogisticRegression:

    def __init__(self , learning_rate = 0.001 , n_iters = 1000):
        self.learning_rate = learning_rate
        self.n_iters = n_iters
        self.weights = None
        self.bias = None

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

    def fit(self , X , y):
        n_samples , n_features = X.shape

        #initializing weights and bias
        self.weights = np.zeros(n_features)
        self.bias = 0

        for i in range(n_samples):
            #applying approximation function for prediction
            linear_prediction = np.dot(X , self.weights)+self.bias

            #passing the output of logistic  regression model through a sigmoid function
            logistic_prediction = self.sigmoid_activation(linear_prediction)

            #calculating wwights and bias gradients (this tells the direction in which algo should proceed to reach the global minima)
            dw = (1/n_samples)*np.dot(2*X.T, (logistic_prediction - y))
            db = (1/n_samples) * 2*np.sum(logistic_prediction-y)

            #adjusting weights and bias
            self.weights -= self.learning_rate*dw
            self.bias -= self.learning_rate*db

    def predict(self , X):
        #applying approximation function for prediction
        linear_prediction = np.dot(X , self.weights)+self.bias

        #passing the output of logistic  regression model through a sigmoid function
        y_pred = self.sigmoid_activation(linear_prediction)

        classes = [1 if i > 0.5 else 0 for i in y_pred]

        return classes

from sklearn.model_selection import train_test_split
from sklearn import datasets

def accuracy(y_true, y_pred):
    accuracy = np.sum(y_true == y_pred) / len(y_true)
    return accuracy

bc = datasets.load_breast_cancer()
X, y = bc.data, bc.target

X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=1234
    )

regressor = LogisticRegression(learning_rate=0.0001, n_iters=1000)
regressor.fit(X_train, y_train)
predictions = regressor.predict(X_test)

print("LR classification accuracy:", accuracy(y_test, predictions))

from sklearn.metrics import confusion_matrix
y_true = y
y_pred = regressor.predict(X)
confusion_matrix(y_true, y_pred)


import scipy as sp
import numpy as np
from scipy.fftpack import fft, ifft
from scipy.io import wavfile
from matplotlib import pyplot as plt
from pyfftw.interfaces import scipy_fftpack as fftw

#==============================================================================
# PROBLEM 1
#==============================================================================
def prob1():
    samplerate=44100 # 44100 samples per second
    freq=60   #60Hz sine wave
    length=1 # ... which will last for 1 seconds.
    stepsize=freq*2*sp.pi/samplerate
    sig=sp.sin(sp.arange(0,stepsize*length*samplerate,stepsize))
    scaled = sp.int16(sig/sp.absolute(sig).max() * 32767)
    plt.plot(scaled)
    plt.show()
    plt.close()
    wavfile.write('sine.wav',samplerate,scaled)
#==============================================================================
# PROBLEM 2 - Naive FT
#==============================================================================
def myft(f):
    N = f.size
    c = sp.zeros((N,), dtype=sp.complex128)
    for k in xrange(0,N):
        c[k] = (1/float(N))*(sp.exp(-2*sp.pi*1j*k*sp.arange(N)/float(N))*f).sum()
#        c[k] = (f * sp.exp(-2*sp.pi*1j*k*sp.arange(N)/N)).sum()
    return c
#==============================================================================
# PROBLEM 3
#==============================================================================
def prob3():
    rate, sig = wavfile.read('pianoclip.wav')
    sig = sp.float32(sig)
    fsig = fftw.fft(sig.T).T
    f = sp.absolute(fsig)
    plt.plot(f[0:f.shape[0]/2])
    plt.show() #spike at about 3088
    freq = rate/float(sig.shape[0]) * 3088
#note is approximately an f#5
#==============================================================================
# Problem 4
#==============================================================================
def prob4():
    old_rate, in_sig = wavfile.read('saw.wav')
    new_rate = 11025
    fin = fftw.fft(sp.float32(in_sig))
    nsiz = sp.floor(in_sig.size*new_rate/old_rate)
    nsizh = sp.floor(nsiz/2)
    fout = sp.zeros(nsiz) + 0j
    fout[0:nsizh] = fin[0:nsizh]
    fout[nsiz-nsizh+1:] = sp.conj(sp.flipud(fout[1:nsizh]))
    out = sp.ifft(fout)
    out = sp.real(out)
    out = sp.int16(out/sp.absolute(out).max() * 32767)
    wavfile.write('prob4_saw.wav',new_rate,out)
#===============================================================================
# Problem 5
#==============================================================================
def prob5():
    old_rate, in_sig = wavfile.read('saw.wav')
    new_rate = 36000
    fin = fftw.fft(sp.float32(in_sig))
    nsiz = sp.floor(in_sig.size*new_rate/old_rate)
    nsizh = sp.floor(nsiz/2)
    fout = sp.zeros(nsiz) + 0j
    fout[0:nsizh] = fin[0:nsizh]
    fout[nsiz-nsizh+1:] = sp.conj(sp.flipud(fout[1:nsizh]))
    out = fftw.ifft(fout)
    out = sp.real(out)
    out = sp.int16(out/sp.absolute(out).max() * 32767)
    wavfile.write('prob5_saw.wav',new_rate,out)
#===============================================================================


# import lib
from numpy import arange
from pandas import read_csv
from scipy.optimize import curve_fit
from matplotlib import pyplot

# define the true objective function
def objective(x, a, b):
	return a * x + b

# set data
data = [
        [10, 15.4],
	[19, 19.4],
	[28, 10.8],
	[37, 30.2],
	[46, 11.7],
	[55, 49.3],
	[64, 8.86],
	[73, 61.1],
	[82, 69.3],
	[91, 75]
]

x = []
y = []

for i in range(0, len(data)):
    value = data[i]
    x.append(value[0])
    y.append(value[1])

print(x)
print(y)

# # load the dataset from online
# url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/longley.csv'
# dataframe = read_csv(url, header=None)
# data = dataframe.values
# # choose the input and output variables
# x, y = data[:, 4], data[:, -1]
# print(x)
# print(y)
# curve fit
popt, _ = curve_fit(objective, x, y)
# summarize the parameter values
a, b = popt
print('y = %.5f * x + %.5f' % (a, b))
# plot input vs output
pyplot.scatter(x, y)
# define a sequence of inputs between the smallest and largest known inputs
x_line = arange(min(x), max(x), 1)
# calculate the output for the range
y_line = objective(x_line, a, b)
# create a line plot for the mapping function
title = 'Trend Line Equation: y = %.5f * x + %.5f' % (a, b)
pyplot.plot(x_line, y_line, '--', color='red')
pyplot.title(title)
pyplot.show()


# backwards compatibility imports that allow using both python 2 and 3
from __future__ import print_function
from __future__ import division

import numpy as np

def get_plane(coords):
    """
    three reference points points in form

    np.array(
    [
      [x_1, y_1, z_1],
      [x_2, y_2, z_2],
      [x_3, y_3, z_3]
    ]
    )

    make sure that three points are not in one line
      as this "breaks" math used here.
    """

    # variables expanded
    x_1, y_1, z_1 = coords[0]
    x_2, y_2, z_2 = coords[1]
    x_3, y_3, z_3 = coords[2]

    # first we get vectors between points:
    # p_1 to p_2
    r_12 = x_2-x_1, y_2-y_1, z_2-z_1
    # p_1 to p_3
    r_13 = x_3-x_1, y_3-y_1, z_3-z_1

    # Now we compute the normal vector of the plane
    n = (
        r_12[1]*r_13[2] - r_12[2]*r_13[1],
        r_12[2]*r_13[0] - r_12[0]*r_13[2],
        r_12[0]*r_13[1] - r_12[1]*r_13[0],
        )

    # We add values to get plane equation
    plane = (
        n[0],
        n[1],
        n[2],
        n[0]*x_1+n[1]*y_1+n[2]*z_1
        )
    return plane

def get_point_projection(plane, p):
    """
    plane - defined by plane equation parameters
    p - point np.array([x, y, z])
    """
    # plane equation parameters expanded
    a, b, c, d = plane

    # original point coordinates expanded as (x0, y0, z0)
    x0, y0, z0 = p

    # now we need any point on a plane
    # let's denote it (px, py, pz)
    # try/except clauses that take care of potential errors with zero division
    try:
        px, py, pz = 0, 0, d/c
    except ZeroDivisionError:
        try:
            px, py, pz = 0, d/b, 0
        except ZeroDivisionError:
            px, py, pz = d/a, 0, 0

    # parameter t that is intermediate step in calculation of projection
    t = (a*px-a*x0 + b*py-b*y0 + c*pz-c*z0)/(a**2+b**2+c**2)

    # point projected on the plane has coordinates (x, y, z)
    x = x0+t*a
    y = y0+t*b
    z = z0+t*c

    return np.array((x, y, z), dtype=float)

def get_projections(plane, coords):
    """
    plane - defined by plane equation parameters

    coords - array of point coordinates shaped as follows:
    np.array(
        [
            [x_1, y_1, z_1],
            [x_2, y_2, z_2],
            [x_3, y_3, z_3],
            ...
        ]
    )
    """
    projections = np.array(
        list(
            map(
                lambda p: get_point_projection(plane, p),
                coords
                )
            ),
        dtype=float
    )
    return projections

def get_length(coords):
    """
    calculates length of a line connecting all points in:
    coords - points shaped as follows
    np.array(
        [
            [x_1, y_1, z_1],
            [x_2, y_2, z_2],
            [x_3, y_3, z_3]
            ...
        ]
    )
    """
    length = 0
    for i in range(1, coords.shape[0]):
        # coords[i] point i
        # coords[i-1] point i-1

        length += np.linalg.norm(coords[i]-coords[i-1])
    return length


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

# code starts here
df = pd.read_csv(path)
total = len(df)
p_a = len(df[df["fico"]>700])/total
p_b = len(df[df["purpose"]=="debt_consolidation"])/total
df1 = df[df["purpose"]=="debt_consolidation"]
p_a_and_b = len(df[(df["fico"]>700) & (df["purpose"]=="debt_consolidation")])/total
p_a_b = p_a_and_b/p_a
p_b_a = p_a_and_b/p_b
result = p_b_a == p_a
print(result)
# code ends here

# --------------
# code starts here
total = len(df)
new_df = df[df["paid.back.loan"]=="Yes"]
prob_lp = len(new_df)/total
df2 = df[df["credit.policy"]=="Yes"]
prob_cs = len(df2)/total
df_lp_cs = df[(df["paid.back.loan"]=="Yes") & (df["credit.policy"]=="Yes")]
prob_lp_a_cs = len(df_lp_cs)/total
# prob_pd_cs = prob_lp_a_cs/prob_cs
prob_pd_cs = new_df[new_df['credit.policy'] == 'Yes'].shape[0] / new_df.shape[0]
bayes = (prob_pd_cs * prob_lp) / prob_cs
print(bayes)
# code ends here

# --------------
# code starts here
df_purpose = df["purpose"].value_counts()
df_purpose.plot(kind="bar")
df1 = df[df["paid.back.loan"]=="No"]
df1["purpose"].value_counts().plot(kind="bar")
# code ends here

# --------------
# code starts here
inst_median = df["installment"].median()
inst_mean = df.installment.mean()
df.installment.hist()
df["log.annual.inc"].hist()
# code ends here


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

# Folsom annual inflow data
# summary statistics, histogram, QQ plot
annQ = np.loadtxt('data/folsom-annual-flow.csv', delimiter=',', skiprows=1, usecols=[1])
N = len(annQ)

m = np.mean(annQ)
s = np.std(annQ)
g = stats.skew(annQ)

# print('Mean = %f' % m)
# print('Std. Dev. = %f' % s)
# print('Skew Coef. = %f' % g) # no skew function in numpy

# other keyword arguments: bins, normed (for PDF instead of count)
# plt.hist(annQ, density=True, color='gray', edgecolor='none')

# plot the fitted pdf on top of the histogram
# x = np.arange(min(annQ),max(annQ), 10) # points to plot at
# pdf = stats.norm.pdf(x, loc=m, scale=s)
# plt.plot(x, pdf, color='k', linewidth=2)
# plt.xlabel('inflow (taf/yr)')
# plt.ylabel('pdf')
# plt.show()

# next figure - qq plot and find ppcc
# plt.figure()
# quantiles = np.arange(1,N+1)/float(N+1)
# print(quantiles)
# Zp = stats.norm.ppf(quantiles)

# Qpred = m + s*Zp
# plt.scatter(Qpred, np.sort(annQ), color='red')
# plt.plot([-1000,7000],[-1000,7000], color='k', linewidth=2)
# plt.xlabel('Theoretical Quantiles (TAF/yr)')
# plt.ylabel('Observed Quantiles (TAF/yr)')
# plt.show()

# r,p = stats.pearsonr(Qpred, np.sort(annQ))
# print('PPCC = %f' % r)


from numba import jit
import numpy as np

@jit(nopython=True)
def RK4(odefun,ics,h,span,degree):

    N= int( (span[1]-span[0])/h )

    tY = np.zeros((N+1,degree+1))
    tY[0,1:] = ics

    for  i in range(N):
        tY[i+1,0] = tY[i,0] + h

        k1= odefun(tY[i,0]       , tY[i,1:])
        k2= odefun(tY[i,0] +(h/2), tY[i,1:] +(h*k1)/2 )
        k3= odefun(tY[i,0] +(h/2), tY[i,1:] +(h*k2)/2)
        k4= odefun(tY[i,0] +(h)  , tY[i,1:] +(h*k3))

        tY[i+1,1:] = tY[i,1:] + h*(1/6) * (k1+2*k2+2*k3+k4)

    return tY[:,0],tY[:,1:]


from scipy.stats import mode
from pickle import load, dump
from numpy import empty, mean, linalg, diag, einsum, argsort, genfromtxt, dtype

load_or_pickle_data = 'load'   # 'load' / 'pickle'
k_PCA = 40
k_NN = 6

# ---------------------------------------------------------------------------------------------------------------------
# Reducing the dimensionality of the MNIST data with PCA (via SVD) before running KNN can save both time and accuracy
# (lower dimensions means less calculations and potentially less overfitting):
def svd_pca(data, k):
    """Reduce the data using its K principal components."""

    data = data.astype("float64")
    data -= mean(data, axis=0)
    U, S, V = linalg.svd(data, full_matrices=False)
    return U[:,:k].dot(diag(S)[:k,:k])

# ---------------------------------------------------------------------------------------------------------------------
# Load or Pickle the data:
def load_data(load_or_pickle_data_, file_):

    X = empty([1,1])

    if load_or_pickle_data_ == 'load':

        # Numpy's genfromtxt function is an easy way to get the .csv data into a matrix:
        X = genfromtxt(file_, delimiter=',', skip_header=1).astype(dtype('uint8'))

        # Serialize the numpy matrix with the pickle module after the first load:
        with open(file_ + '.p', 'wb') as f:
            dump(X, f)

    elif load_or_pickle_data_ == 'pickle':

        # Load the saved pickle object on all subsequent runs (quicker):
        with open(file_ + '.p', 'rb') as f:
            X = load(f)

    return X

# ---------------------------------------------------------------------------------------------------------------------
# KNN class, based on euclidean distance (einsum):
class KNN:
    def __init__(self, data_, labels_, k_):
        self.data = data_
        self.labels = labels_
        self.k = k_

    def predict(self, sample):
        differences = (self.data - sample)
        distances = einsum('ij, ij->i', differences, differences)  # distance[x]=sum((differences[x]).^2)
        nearest = self.labels[argsort(distances)[:self.k]]
        return mode(nearest)[0][0]

# ---------------------------------------------------------------------------------------------------------------------
# Main course:
print "1. Loading Train and Test data..."
train_data_ = load_data(load_or_pickle_data, 'data/train.csv')
test_data_  = load_data(load_or_pickle_data, 'data/test.csv')

print "2. Building and Training a KNN classifier object..."
train_data = svd_pca(train_data_[:,1:], k_PCA)
labels = train_data_[:,0]
KNN_obj = KNN(train_data, labels, k_NN)

print "3. Testing the KNN classifier over the train data..."
for i in range(10):
    pred = KNN_obj.predict(train_data[i])
    res = 'FAIL'
    if pred == labels[i]:
        res = 'PASS'
    print("   - prediction = %d --> %s" % (pred, res))

print "4. Testing the KNN classifier over the test data..."
test_data = svd_pca(test_data_[:,1:], k_PCA)
for i in range(10):
    pred = KNN_obj.predict(test_data[i])
    print("   - prediction = %d" % pred)

print "Done!"


import numpy as np
import matplotlib.pyplot as plt
from sklearn.cross_validation import KFold
from scipy.optimize import fsolve

data = np.genfromtxt('web_traffic.tsv', delimiter='\t')
print data.shape
print data[:10]

x = data[:, 0]
y = data[:, 1]
print np.sum(np.isnan(y))

x = x[~np.isnan(y)]
y = y[~np.isnan(y)]

plt.scatter(x, y, s=10)
plt.title('Page views over last month')
plt.xlabel('Time')
plt.ylabel('PV/hour')
# set x ticks by week count
plt.xticks([w*7*24 for w in range(10)], ['week %d'%w for w in range(10)])
plt.autoscale(tight=True)
plt.grid(True, linestyle='-', color='0.75')
# plt.show()

def error(f, x, y):
    return np.sum(np.power(f(x) - y, 2))

fp1 = np.polyfit(x, y, 1)
print fp1  # [   2.59619213  989.02487106]
f1 = np.poly1d(fp1)
print type(f1)  # <class 'numpy.lib.polynomial.poly1d'>
print f1  # 2.596 x + 989
print error(f1, x, y)  # (317389767.34+0j)

# fx = np.linspace(0, x[-1], 1000)
fx = np.linspace(0, 6*7*24, 1000)
plt.ylim(0, 10000)
plt.plot(fx, f1(fx), linewidth=4)
# plt.legend(['d = %d' % f1.order], loc='upper left')
# plt.show()

fp2 = np.polyfit(x, y, 2)
print fp2  # [  1.05322215e-02  -5.26545650e+00   1.97476082e+03]
f2 = np.poly1d(fp2)
print type(f2)  # <class 'numpy.lib.polynomial.poly1d'>
print f2  # 0.01053 x^2 - 5.265 x + 1975
print error(f2, x, y)  # (179983507.878+0j)

plt.plot(fx, f2(fx), linewidth=4)
# plt.legend(['d = %d' % f.order for f in [f1, f2]], loc='upper left')
# plt.show()

fp3 = np.polyfit(x, y, 3)
f3 = np.poly1d(fp3)
print error(f3, x, y)
plt.plot(fx, f3(fx), linewidth=4)

fp10 = np.polyfit(x, y, 10)
f10 = np.poly1d(fp10)
print error(f10, x, y)
plt.plot(fx, f10(fx), linewidth=4)

fp50 = np.polyfit(x, y, 50)
f50 = np.poly1d(fp50)
print error(f50, x, y)
plt.plot(fx, f50(fx), linewidth=4)

fs = [f1, f2, f3, f10, f50]
plt.legend(['d = %d' % f.order for f in fs], loc='upper left', prop={'size': 10})

table = plt.table(cellText=[['%.2e' % error(f, x, y) for f in fs]],
    colWidths=[0.13] * len(fs),
    rowLabels=['error'],
    colLabels=['order %d' % f.order for f in fs],
    loc='upper right', zorder=100)
table.scale(1, 1.5)
# plt.show()

def gen_model(x, y, degree):
    fp = np.polyfit(x, y, degree)
    return np.poly1d(fp)

plt.figure()
plt.scatter(x, y, s=10)
plt.title('Page views over last month')
plt.xlabel('Time')
plt.ylabel('PV/hour')
# set x ticks by week count
plt.xticks([w*7*24 for w in range(10)], ['week %d'%w for w in range(10)])
plt.autoscale(tight=True)
plt.grid(True, linestyle='-', color='0.75')
plt.ylim(0, 10000)
y_lastweek = y[x > 3.5*7*24]
x_lastweek = x[x > 3.5*7*24]
degrees = [1, 2, 3, 10, 50]
fs = [gen_model(x_lastweek, y_lastweek, i) for i in degrees]
for f in fs:
    plt.plot(fx, f(fx), linewidth=4)
plt.legend(['d = %d' % f.order for f in fs], loc='upper left', prop={'size': 10})
table = plt.table(cellText=[['%.2e' % error(f, x, y) for f in fs]],
    colWidths=[0.13] * len(fs),
    rowLabels=['error'],
    colLabels=['order %d' % f.order for f in fs],
    loc='upper right', zorder=100)
table.scale(1, 1.5)
# plt.show()

result = {}
for train, test in KFold(y_lastweek.shape[0], 5):
    # print train.shape, test.shape
    x_train, x_test = x_lastweek[train], x_lastweek[test]
    y_train, y_test = y_lastweek[train], y_lastweek[test]
    for d in [1, 2, 3, 10, 50]:
        if d not in result:
            result[d] = []
        f = gen_model(x_train, y_train, d)
        result[d].append(error(f, x_test, y_test))

for k, v in result.iteritems():
    print k, '\t'.join('%.2e' % x for x in v), '\t%.2e' % np.mean(v)

bestF = gen_model(x_lastweek, y_lastweek, 2) - 10000
roots = fsolve(bestF, x0=800) / (7.0 * 24)
print roots  # [ 5.0628393]

plt.figure()
plt.xticks([w*7*24 for w in range(10)], ['week %d'%w for w in range(10)])
plt.autoscale(tight=True)
plt.grid(True, linestyle='-', color='0.75')
plt.ylim(0, 10000)
plt.plot(fx, bestF(fx), linewidth=4)
plt.show()


import numpy as np

# Function to compute sigmoid function
def sigmoid(x):
    return 1/(1+np.exp(-x))


import numpy as np

def reconstruct_dp(knapsacks, capacity1, capacity2):
    n_knapsacks = len(knapsacks)
    sub_sols = np.zeros((n_knapsacks + 1, capacity1 + 1, capacity2 + 1))

    for i in range(1, n_knapsacks + 1):
        w = knapsacks[i - 1][1]
        v = knapsacks[i - 1][0]
        for c1 in range(0, capacity1 + 1):
            for c2 in range(0, capacity2 + 1):
                val1 = sub_sols[i - 1][c1][c2]
                if c1 >= w:
                    val1 = sub_sols[i - 1][c1 - w][c2] + v

                val2 = sub_sols[i - 1][c1][c2]
                if c2 >= w:
                    val2 = sub_sols[i - 1][c1][c2 - w] + v
                sub_sols[i][c1][c2] = max(val1, val2, sub_sols[i-1][c1][c2])
    return sub_sols[n_knapsacks][capacity1][capacity2]

if __name__ == "__main__":
    knapsacks = [(7, 1), (1, 3), (4, 4), (10, 8), (9, 15)]
    assert reconstruct_dp(knapsacks, 2, 29) == 30

    knapsacks = [(3, 3), (2, 2), (8, 8)]
    assert reconstruct_dp(knapsacks, 10, 3) == 13

    knapsacks = [(3, 3), (5, 5), (8, 8)]
    assert reconstruct_dp(knapsacks, 10, 3) == 11


import numpy as np

# import torch
from PIL import Image
import matplotlib.pyplot as plt

from functools import reduce

A = np.identity(4)
A

P = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])
P
Q = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
Q

B = np.arange(16).reshape((4, 4))
B

B
np.dot(B, P)
np.dot(P, np.dot(B, P))

def blockMatrix(blocks):
    """creates a bloack 0-1 matrix.
    param blocks: list of non-negative integers which is the size of the blocks.
    a 0 block size corresponds to a 0 on the main diagonal.
    """
    blocks = np.array(blocks).astype("int64")
    f = lambda x: 1 if x == 0 else x
    n = np.sum([f(x) for x in blocks])
    n = int(n)
    A = np.zeros((n, n))
    pos = 0
    for i in range(len(blocks)):
        b = blocks[i]
        if b > 0:
            A[pos : pos + b, pos : pos + b] = np.ones((b, b))
        pos += f(b)
    return A

def permutationMatrix(ls):
    """returns a permutation matrix of size len(ls)^2.
    param ls: should be a reordering of range(len(ls)), which defines the
    permutation on the ROWS.
    returns a permutation matrix P.
    np.dot(P,A) should be rearrangement of the rows of A according to P.
    To permute the columns of a matrix A use:
    Q = np.transpose(P), then: np.dot(A,Q).
    """
    n = len(ls)
    P = np.zeros((n, n))
    for i in range(n):
        P[i, ls[i]] = 1
    return P

def shuffleCopyMatrix(lins, louts, msize):
    """Returns a matrix P that represents switch and copy operations
    on the rows of a matrix.
    param msize: the size (of the square matrix).
    param lins: row indices to be replaced.
    param louts: row that replace the ones listed in lins.
    lins and louts must be of the same length and contain indiced within
    range(msize).
    These operations are performed on the identity matrix, and the result
    is the return value P.
    """
    # P = np.zeros((msize,msize))
    P = np.identity(msize)
    I = np.identity(msize)
    if not len(lins) == len(louts):
        return P
    for i in range(len(lins)):
        P[lins[i]] = I[louts[i]]
    return P

def scoreMatrix(n):
    """The score function of the matrix. The assumption is that the true
    arrangement maximized the interaction close to the main diagonal.
    The total sum of the interaction is an invariant, preserved by permuations.
    param n: size of ca 2-d n over n array.
    returns the score matrix, which is used to calculate the score of any given
    n^2 matrix.
    """
    s = np.arange(n)
    s = np.exp(-s)
    S = np.zeros((n, n))
    for i in range(n):
        S[i][i:] = s[: n - i]
    return S

def score(A, S):
    return np.sum(A * S)

def reindexMatrix(iss, jss, A):
    """iss and jss are lists of indices of equal size, representing
    a permuation: iss[i] is replaced with jss[i]. all other indices which are
    not in the lists left unchanged.
    """
    n = len(A)
    B = np.zeros_like(A)
    tss = [i for i in range(n)]
    for i in range(len(iss)):
        tss[iss[i]] = jss[i]
    print(tss)
    for i in range(n):
        for j in range(n):
            B[i, j] = A[tss[i], tss[j]]
    return B

reindexMatrix([1, 5], [5, 1], np.arange(36).reshape((6, 6)))

X = permutationMatrix([0, 4, 3, 2, 5, 1])
Y = np.transpose(X)

S = shuffleCopyMatrix([1, 3], [0, 2], 4)
S

T = np.transpose(S)
T

R = shuffleCopyMatrix([0, 1, 2, 3], [2, 0, 3, 1], 4)
R

blockMatrix([2, 3])

blockMatrix([2, 0, 0, 3])

blocks = [1, 3, 0, 3]

np.random.shuffle(B)

Z = blockMatrix([10, 20, 0, 0, 10, 20, 30]).astype("int64")
Z
ZZ = 255.0 * Z

im = Image.fromarray(ZZ)

im.show()

plt.ion()
# plt.ioff()

plt.imshow(ZZ)

plt.imshow(im)

plt.matshow(ZZ)

plt.close()

# ls = [10,20,0,0,10,20,30]
l1 = [i for i in range(25)]
l2 = [i + 25 for i in range(27)]
l3 = [i + 25 + 27 for i in range(20)]
l4 = [i + 25 + 27 + 20 for i in range(20)]

l3b = l3.copy()
l3b.reverse()
l3b

ZZ.shape

# rows
PP1 = permutationMatrix(l3 + l1 + l2 + l4)
PP2 = permutationMatrix(l1 + l2 + l3b + l4)
PP3 = permutationMatrix(l1 + l3b + l2 + l4)
# columns
QQ1 = np.transpose(PP1)  # then: np.dot(A,QQ).
QQ2 = np.transpose(PP2)  # then: np.dot(A,QQ).
QQ3 = np.transpose(PP3)  # then: np.dot(A,QQ).
ZZZ1 = np.dot(np.dot(PP1, ZZ), QQ1)
ZZZ2 = np.dot(np.dot(PP2, ZZ), QQ2)
ZZZ3 = np.dot(np.dot(PP3, ZZ), QQ3)

# plt.imshow(ZZZ)
# plt.imshow(ZZ)

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 4), sharex=True, sharey=True)

ax1.imshow(ZZ)
ax2.imshow(ZZZ)

fig, axs = plt.subplots(nrows=2, ncols=2)

fig.suptitle("original pattern and permutations")

axs[0, 0].imshow(ZZ)
axs[0, 0].set_title("original")

axs[0, 1].imshow(ZZZ1)
axs[0, 1].set_title("[52:73] moved to the start")

axs[1, 0].imshow(ZZZ2)
axs[1, 0].set_title("[52:73] reversed")

axs[1, 1].imshow(ZZZ3)
axs[1, 1].set_title("[52:73] moved to [25:52] and reversed")

plt.close()

sm = scoreMatrix(len(ZZ))
sm

score(ZZ, sm)
score(ZZZ1, sm)
score(ZZZ2, sm)
score(ZZZ3, sm)

def scorePair(iss, jss, refmat, scoremat):
    A = np.zeros_like(refmat)
    l = iss + jss
    n = len(l)
    for i in range(n):
        for j in range(n):
            A[i, j] = refmat[l[i], l[j]]
    return score(A, scoremat)

[scorePair(l2, l4, ZZ, sm)]

np.argmax([1, 9, 0, 3])

# reassembl
cs = [l2, l4, l1, l3]
cs

while len(cs) > 1:
    xs = cs.pop()
    l = np.argmax([scorePair(xs, y, ZZ, sm) for y in cs])
    sl = scorePair(xs, cs[l], ZZ, sm)
    r = np.argmax([scorePair(y, xs, ZZ, sm) for y in cs])
    sr = scorePair(cs[r], xs, ZZ, sm)
    if sl > sr:
        cs[l] = xs + cs[l]
    else:
        cs[r] = cs[r] + xs
    print(l, sl, r, sr)

test = cs[0]
test == l1 + l2 + l3 + l4

def scorePair2(iss, jss, refmat):
    s = 0
    temp = 0
    for i in range(len(iss)):
        for j in range(len(jss)):
            temp = np.exp(-np.abs(i - j))
            # we only care about interaction between the 2 segments and not
            # inside each one of them which wouldn't be affected by
            # rearrangement.
            s += refmat[iss[i], jss[j]] * temp
    return s

# reassembly 2
cs = [l2, l4, l1, l3]
cs

while len(cs) > 1:
    xs = cs.pop()
    l = np.argmax([scorePair2(xs, y, ZZ) for y in cs])
    sl = scorePair2(xs, cs[l], ZZ)
    r = np.argmax([scorePair2(y, xs, ZZ) for y in cs])
    sr = scorePair2(cs[r], xs, ZZ)
    if sl > sr:
        cs[l] = xs + cs[l]
    else:
        cs[r] = cs[r] + xs
    print(l, sl, r, sr)

test == l1 + l2 + l3 + l4

myblocks = [10, 15, 17, 19, 17, 15, 10]
mymatrix = blockMatrix(myblocks)

dmatrix = scoreMatrix(len(mymatrix))

dmatrix += np.transpose(dmatrix)

dmatrix -= np.identity(len(dmatrix))

plt.matshow(mymatrix)

plt.matshow(np.log10(dmatrix))

fig, axs = plt.subplots(nrows=1, ncols=2)
fig.suptitle("ideal distribution of 1s and 0s")
axs[0].imshow(dmatrix)
axs[0].set_title("original")
axs[1].imshow(np.log(dmatrix))
axs[1].set_title("log scale")

myblocks

# mysegments = [8, 19, 20, 21, 22, 13]
mysegments = [15, 29, 20, 21, 18]
np.cumsum(myblocks)
np.cumsum(mysegments)

# and the corrsponding indices are:
def articulate(l):
    """l is a list of positive integers.
    returns the implied articulation, meaning a list of lists (or 1d arrays)
    ls, such that ls[0] it the numbers 0 to l[0]-1, ls[1] is a list of the
    numbers ls[1] to ls[2]-1 etc.
    """
    # ls = [np.arange(l[0]).astype('uint64')]
    ls = []
    offsets = np.cumsum([0] + l)
    for i in range(0, len(l)):
        xs = np.arange(l[i]).astype("uint64") + offsets[i]
        ls.append(xs)
    return ls

# the blocks, explicitly indexed
articulate(myblocks)

temp = articulate(myblocks)

reduce(lambda x, y: x + list(y), temp, [])

# the original segments
mysegments
articulate(mysegments)

np.nancumsum(myblocks)
np.cumsum(mysegments)

# shuffle the order of the segments:
newOrder = np.random.permutation(len(mysegments))
newOrder

temp = articulate(mysegments)
temp

reindexlist = [temp[newOrder[i]] for i in newOrder]

reindexlist

reindexlist
# we shuffled the order, now lets reverse a few of the segments:
for i in [1, 4]:
    reindexlist[i] = np.flip(reindexlist[i])
reindexlist

reindexing = reduce(lambda x, y: x + list(y), reindexlist, [])
reindexing

# now lets see the original matrix and the matrix after the transformation:

newmatrix = np.zeros_like(mymatrix)
for i in range(len(mymatrix)):
    for j in range(len(mymatrix)):
        newmatrix[i, j] = mymatrix[reindexing[i], reindexing[j]]

fig, axs = plt.subplots(nrows=1, ncols=2)
fig.suptitle("original block matrix anb its transformation")
axs[0].imshow(mymatrix)
# axs[0].set_title('')
axs[1].imshow(newmatrix)
# axs[1].set_title('')

# what we have to work with is newmatrix, as well as a list of the segments
# in their shuffled order, not the orignal of course.
newsegments = [mysegments[newOrder[i]] for i in range(len(newOrder))]
newsegments

# so we need to reshuffle the segments
newsegments
# so that eventually they will be order like that (after re-indexing)
mysegments
# and some of the new segments we'll have to reverse as well
# can we do that?

def scorePair3(iss, jss, refmat, lreverse=False, rreverse=False):
    """iss, jss must be lists of segments of the index range of refmat,
    our reference matrix.
    reurns the interaction score of iss and jss as if reindexed the matrix so
    that they will be adjuscent to each other.
    """
    s = 0
    temp = 0
    for i in range(len(iss)):
        for j in range(len(jss)):
            x = i
            y = j
            if lreverse:
                x = iss[-1] - i
            if rreverse:
                y = jss[-1] - j
            # temp = np.exp(-np.abs(i-j))
            temp = np.exp(-np.abs(x - y))
            # we only care about interaction between the 2 segments and not
            # inside each one of them which wouldn't be affected by
            # rearrangement.
            s += refmat[iss[i], jss[j]] * temp
    return s

cs = articulate(newsegments)
cs = [list(x) for x in cs]
cs

xyz = np.zeros_like(newmatrix)
l = cs[0] + cs[1]
for i in l:
    for j in l:
        xyz[i, j] = newmatrix[i, j]
plt.imshow(xyz)

xyz = np.zeros_like(newmatrix)
l = cs[5]
for i in l:
    for j in l:
        xyz[i - l[0], j - l[0]] = newmatrix[i, j]
plt.imshow(xyz)

xyz = np.zeros_like(newmatrix)
l = cs[0] + cs[3]
# l = cs[0] + np.flip(cs[3])
for i in range(len(l)):
    for j in range(len(l)):
        xyz[i, j] = newmatrix[l[i], l[j]]
print(scorePair2(cs[0], cs[3], mymatrix))
print(scorePair2(cs[0], cs[3], newmatrix))
print(scorePair2(np.flip(cs[0]), cs[3], newmatrix))  # this is the problem?
plt.imshow(xyz)

plt.imshow(mymatrix)

for i in cs:
    for j in cs:
        # print(scorePair2(i,j, newmatrix))
        print(scorePair2(i, j, mymatrix))

reconstructionMatrix = np.zeros_like(newmatrix)

while len(cs) > 1:
    xs = cs.pop()
    print(xs)
    xsrev = xs.copy()
    xsrev.reverse()
    newmatrixrev = reindexMatrix(xs, xsrev, newmatrix)
    l = np.argmax([scorePair2(xs, y, newmatrix) for y in cs])
    sl = scorePair2(xs, cs[l], newmatrix)
    lrev = np.argmax(
        # [scorePair2(xsrev, y, newmatrix) for y in cs]
        [scorePair2(xs, y, newmatrixrev) for y in cs]
    )
    # slrev = scorePair2(xsrev, cs[l], newmatrix)
    slrev = scorePair2(xs, cs[lrev], newmatrixrev)
    r = np.argmax([scorePair2(y, xs, newmatrix) for y in cs])
    sr = scorePair2(cs[r], xs, newmatrix)
    rrev = np.argmax(
        # [scorePair2(y, xsrev, newmatrix) for y in cs]
        [scorePair2(y, xs, newmatrixrev) for y in cs]
    )
    # srrev = scorePair2(cs[r], xsrev,  newmatrix)
    srrev = scorePair2(cs[rrev], xs, newmatrixrev)
    iascores = [sl, slrev, sr, srrev]
    candidates = [cs[l], cs[lrev], cs[r], cs[rrev]]
    maxscore = np.max(iascores)
    if maxscore == sl:
        cs[l] = xs + cs[l]
    elif maxscore == sr:
        cs[r] = cs[r] + xs
    elif maxscore == lrev:
        cs[lrev] = xsrev + cs[lrev]
    else:
        cs[rrev] = cs[rrev] + xsrev

# reconstruction of the matrix
reconstructionMatrix = np.zeros_like(newmatrix)
myindices = cs[0]
myindices

n = len(newmatrix)
for i in range(n):
    for j in range(n):
        reconstructionMatrix[i, j] = newmatrix[myindices[i], myindices[j]]
        # reconstructionMatrix[myindices[i],myindices[j]] = newmatrix[i, j]

plt.imshow(newmatrix)

plt.imshow(reconstructionMatrix)

#### new try

reconstructionMatrix = np.zeros_like(newmatrix)
reconstructionMatrix = newmatrix.copy()

while len(cs) > 1:
    xs = cs.pop()
    print(xs)
    xsrev = xs.copy()
    xsrev.reverse()
    reconstructionMatrixrev = reindexMatrix(xs, xsrev, reconstructionMatrix)
    l = np.argmax([scorePair3(xs, y, reconstructionMatrix) for y in cs])
    sl = scorePair3(xs, cs[l], reconstructionMatrix)
    lrev = np.argmax(
        # [scorePair2(xsrev, y, reconstructionMatrix) for y in cs]
        # [scorePair2(xs, y, reconstructionMatrixrev) for y in cs]
        [scorePair3(xs, y, reconstructionMatrix, lreverse=True) for y in cs]
    )
    # slrev = scorePair2(xsrev, cs[l], reconstructionMatrix)
    slrev = scorePair3(xs, cs[lrev], reconstructionMatrix, lreverse=True)
    r = np.argmax([scorePair3(y, xs, reconstructionMatrix) for y in cs])
    sr = scorePair3(cs[r], xs, reconstructionMatrix)
    rrev = np.argmax(
        # [scorePair2(y, xsrev, reconstructionMatrix) for y in cs]
        [scorePair3(y, xs, reconstructionMatrix, rreverse=True) for y in cs]
    )
    # srrev = scorePair2(cs[r], xsrev,  reconstructionMatrix)
    srrev = scorePair3(cs[rrev], xs, reconstructionMatrix, rreverse=True)
    iascores = [sl, slrev, sr, srrev]
    candidates = [cs[l], cs[lrev], cs[r], cs[rrev]]
    maxscore = np.max(iascores)
    if maxscore == sl:
        cs[l] = xs + cs[l]
    elif maxscore == sr:
        cs[r] = cs[r] + xs
    elif maxscore == lrev:
        reconstructionMatrix = reindexMatrix(xs, xsrev, reconstructionMatrix)
        # cs[lrev] = xsrev + cs[lrev]
        cs[lrev] = xs + cs[lrev]
    else:
        reconstructionMatrix = reindexMatrix(xs, xsrev, reconstructionMatrix)
        # cs[rrev] = cs[rrev] + xsrev
        cs[rrev] = cs[rrev] + xs

n = len(newmatrix)
temp = np.zeros_like(newmatrix)
for i in range(n):
    for j in range(n):
        temp[i, j] = reconstructionMatrix[myindices[i], myindices[j]]
        # reconstructionMatrix[myindices[i],myindices[j]] = newmatrix[i, j]

######
plt.imshow(newmatrix)

cs = articulate(newsegments)
cs = [list(x) for x in cs]
cs
mysegments
newsegments

def improve(xss, yss, A):
    pass


# Authors: Federico Vaggi
# License: MIT
# Source: https://bitbucket.org/FedericoV/numpy-tip-complex-modeling/

import numpy as np
from math import *

def arc_distance_python_nested_for_loops(a, b):
    """
    Calculates the pairwise arc distance between all points in vector a and b.
    """
    a_nrows = a.shape[0]
    b_nrows = b.shape[0]

    distance_matrix = np.zeros([a_nrows, b_nrows])

    for i in range(a_nrows):
        theta_1 = a[i, 0]
        phi_1 = a[i, 1]
        for j in range(b_nrows):
            theta_2 = b[j, 0]
            phi_2 = b[j, 1]
            temp = (pow(sin((theta_2 - theta_1) / 2), 2)
                    +
                    cos(theta_1) * cos(theta_2)
                    * pow(sin((phi_2 - phi_1) / 2), 2))
            distance_matrix[i, j] = 2 * (atan2(sqrt(temp), sqrt(1 - temp)))
    return distance_matrix

def arc_distance_numpy_tile(a, b):
    """
    Calculates the pairwise arc distance between all points in vector a and b.
    """
    theta_1 = np.tile(a[:, 0], (b.shape[0], 1)).T
    phi_1 = np.tile(a[:, 1], (b.shape[0], 1)).T

    theta_2 = np.tile(b[:, 0], (a.shape[0], 1))
    phi_2 = np.tile(b[:, 1], (a.shape[0], 1))

    temp = (np.sin((theta_2 - theta_1) / 2)**2
            +
            np.cos(theta_1) * np.cos(theta_2)
            * np.sin((phi_2 - phi_1) / 2)**2)
    distance_matrix = 2 * (np.arctan2(np.sqrt(temp), np.sqrt(1 - temp)))

    return distance_matrix

def arc_distance_numpy_broadcast(a, b):
    """
    Calculates the pairwise arc distance between all points in vector a and b.
    """
    theta_1 = a[:, 0][:, None]
    theta_2 = b[:, 0][None, :]
    phi_1 = a[:, 1][:, None]
    phi_2 = b[:, 1][None, :]

    temp = (np.sin((theta_2 - theta_1) / 2)**2
            +
            np.cos(theta_1) * np.cos(theta_2)
            * np.sin((phi_2 - phi_1) / 2)**2)
    distance_matrix = 2 * (np.arctan2(np.sqrt(temp), np.sqrt(1 - temp)))
    return distance_matrix

benchmarks = (
    arc_distance_python_nested_for_loops,
    arc_distance_numpy_tile,
    arc_distance_numpy_broadcast,
)


import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

def plot_poly(x,y,degree, *args):
    """ Plot the data with given degree of polynomial.

    Example:
    plot_poly(x,y,3)
    plot_poly(x,y,3,'x','y','title')

    Returns: p

    Usage:
    x_value = 100
    p(x_value) gives the polynomial fit of x_value

    """
    plt.figure(figsize=(12,8))
    plt.title('')
    plt.plot(x, y,'b.')

    # polynomial fit
    z = np.polyfit(x ,y,degree)
    p = np.poly1d(z)

    # polynomial plot
    xp = np.linspace(x.min(), x.max(), 1000)
    signs = ['+' if i>0 else '-' for i in p.coeffs][::-1]
    label = '  '.join(['%s %.2g x**%i'%(signs[i], abs(p.coeffs[~i]),i) for i in reversed(range(degree+1))])
    label = label.replace('x**0','')
    label = label.replace('x**1','x')
    label = label.lstrip('+')
    plt.plot(xp, p(xp), '-',label=label)
    if len(args) > 0: plt.xlabel(args[0])
    if len(args) > 1: plt.ylabel(args[1])
    if len(args) > 2: plt.title(args[2])
    plt.legend()

    return p

x = np.arange(10)
y = x**2 + 2*x - 3

degree = 2
xlabel = 'x'
ylabel = 'y'
title = 't'
plot_polynomial(x,y,degree,xlabel,ylabel,title)


import scipy.stats
import numpy as np

def hypergeometric(binary_classification,feature_matrix):

    X = feature_matrix
    y = binary_classification
    nF = feature_matrix.shape[1]

    pvals = np.ones(nF) #p-values

    M = feature_matrix.shape[0] #total number of objects
    n = sum(y) #number in class
    for feature_idx in range(nF):
        N = sum(X[:,feature_idx]) #number positive for this feature
        k = sum(np.multiply(X[:,feature_idx],y)) #number of positives within this set
        pvals[feature_idx] = scipy.stats.hypergeom.sf(k,M,n,N)

    return pvals

def FE(class_vector,feature_matrix):
    '''Calculate fold enrichment of class members in feature categories

    Args:
        class_vector: 1-by-nG array
        feature_matrix: nG-by-nF array
    Returns:
        1-by-nF array

    Example:
    >>> class_vector = np.array([0,1,1,0,0,0,0])
    >>> feature_matrix = np.array([[0,0],[1,0],[1,1],[1,0],[1,0],[0,1],[1,1]])
    >>> FE(class_vector,feature_matrix)
    array([ 1.4       ,  1.16666667])'''

    assert(all([x in [0,1] for x in class_vector])) #classification vector is binary

    nF = feature_matrix.shape[1]

    fold_enrich = np.ones(nF) #fold enrichment

    M = feature_matrix.shape[0] #total number of objects
    n = sum(class_vector) #number in class
    for feature_idx in range(nF):
        feature_vector = feature_matrix[:,feature_idx]
        N = sum(feature_vector) #number positive for this feature
        k = sum(np.multiply(feature_vector,class_vector)) #number of positives within this set
        try:
            fold_enrich[feature_idx] = (float(k)/n)/(float(N)/M)
        except ZeroDivisionError:
            fold_enrich[feature_idx] = 1.0

    return fold_enrich


import numpy
from LU_Decomposition import backward_substitution, forward_substitution

t = linspace(0,1,50)
n = 12
m = 50
A = []
for i in range(m):
    z = []
    for j in range(n):
        z.append(t[i]**j)
    A.append(z)
A = array(A)
b = array([cos(4*t)]).T

# LU decomposition
print("Chlolesky")
M = A.T@A
L = cholesky(M) ## This returns a lower triangular matrix such that M = L@L.T
y = forward_substitution(L, A.T@b)
x = backward_substitution(L.T, y)
print('residual', norm(A@x - b))
print(x)


import numpy as np

def compute_mse(img1, img2, **kwargs):
    """
    returns Mean Squared Error between two images
    :param img1:
    :param img2:
    :return:
    """
    return np.mean((img1 - img2) * (img1 - img2))

def compute_psnr(img1, img2, **kwargs):
    """
    Computes Peak Signal-to-Noise Ratio between two images.
    :param img1: image 1
    :param img2: image 2
    :param data_range: the maximum possible pixel value of the images.
                        For 8-bit images, range is 255.0; 1.0 for float images
    :return: psnr value
    """

    data_range = kwargs.pop("data_range", 1.0)

    mse = compute_mse(img1, img2)
    return 10.0 * np.log10(data_range * data_range / mse)


import math
import numpy as np
from enum import IntEnum

class Mode(IntEnum):
    CUSTOM          = 0
    EQUAL           = 1
    GAUSS           = 2
    GAUSS_SYM       = 3
    PYRAMID         = 4
    PYRAMID_SYM     = 5
    SIVEROO_1       = 6
    SIVEROO_2       = 7

#This function will return an list of value, like below:
# [0,1,2,3,...,n] -> [a,...,b]
def scaleRange(n: int, a: int, b: int):
    return [(x*(b-a)/(n-1))+a for x in range(0,n)]

def equal(n: int):
    return [1/n]*n

def gauss(n: int):
    r = range(n,0,-1)
    val = [math.exp(-(2.0*x/n)**2) for x in r]
    val = val/np.sum(val)
    return val

def gauss_sym(n: int):
    n = n/2
    r = range(int(n),-math.ceil(n),-1)
    val = ([math.exp(-(2.0*x/(n))**2) for x in r])
    val = val/np.sum(val)
    return val

def pyramid(n: int):
    r = range(1,n+1)
    val = [x/n for x in r]
    val = val/np.sum(val)
    return val

def pyramid_sym(n: int):
    r = range(0,n)
    val = [(n/2)-abs(x-(n-1)/2) for x in r]
    val = val/np.sum(val)
    return val

def siveroo1(n: int):
    r = scaleRange(n,-3,0.1)
    val = [math.floor(3*math.exp(-(x/1.9)**2))/3+0.1 for x in r]
    val = val/np.sum(val)
    return val

# this function will stretch the given array (w) to a specific length (n)
# example : n = 10, w = [1,2]
# result : val = [1,1,1,1,1,2,2,2,2,2] , flip it, and then normalize it so its sum is equal to 1
def stretch(n: int, w: int):
    r = scaleRange(n,0,len(w)-0.1)

    val = []
    idx = [math.floor(x) for x in r]
    for x in range(0,n):
        index = int(idx[x])
        val.append(w[index])
    val = val/np.sum(val)
    return val

def null(n: int):
    return [0]*n

def get_weight(mode: Mode, count: int):
    if count == 1:
        return [1.0]
    else:
        return {
            Mode.EQUAL          : equal(count),
            Mode.GAUSS          : gauss(count),
            Mode.GAUSS_SYM      : gauss_sym(count),
            Mode.PYRAMID        : pyramid(count),
            Mode.PYRAMID_SYM    : pyramid_sym(count),
            Mode.SIVEROO_1      : siveroo1(count),
            Mode.SIVEROO_2      : stretch(count,[1,3,3,2,2])
        }.get(mode, [1, 0]) # fallback to [1,0] if fucked up

def modeName(mode: Mode):
    return {
            Mode.EQUAL          : "[1] Equal",
            Mode.GAUSS          : "[2] Gaussian Asymmetric",
            Mode.GAUSS_SYM      : "[3] Gaussian Symmetric",
            Mode.PYRAMID        : "[4] Pyramid Asymmetric",
            Mode.PYRAMID_SYM    : "[5] Pyramid Symmetric",
            Mode.SIVEROO_1      : "[6] Siveroo's Preset I",
            Mode.SIVEROO_2      : "[7] Siveroo's Preset II"
        }[mode]


from numpy import loadtxt, zeros, ones, mean, std

def feature_normalize(input):
    mu = input.mean(axis=0)
    sd = input.std(axis=0)
    input_norm = (input - mu) / sd
    return input_norm

def cost_function(x, y, theta):
    m = x.shape[0]
    predictions = x.dot(theta)
    sqErrors = (predictions - y)
    J = (1.0 / (2 * m)) * sqErrors.T.dot(sqErrors)

    return J

def gradient_descent(x, y, theta):
    num_iters = 100
    alpha = 0.01
    theta_size = theta.size
    m = x.shape[0]

    J_history = zeros(shape=(num_iters, 1))

    for i in range(num_iters):
        predictions = x.dot(theta)

        for j in range(theta_size):
            temp = x[:, j]
            temp.shape = (m, 1)

            errors_x1 = (predictions - y) * temp

            theta[j][0] = theta[j][0] - alpha * (1.0 / m) * errors_x1.sum()

        J_history[i, 0] = cost_function(x, y, theta)

    return theta, J_history

data = loadtxt('ex1data2.txt', delimiter=',')
x = data[:, :-1]
y = data[:, -1]

m, n = x.shape
y.shape = (m, 1)

x_norm = feature_normalize(x)
#theta0, theta1, thetaM
x = ones(shape=(m, 3))
x[:, 1:3] = x_norm

y = feature_normalize(y)

theta = zeros(shape=(n+1, 1))

theta, j_history = gradient_descent(x, y, theta)
print theta, j_history


import numpy as np

def branin_hoo_factory(a, b, c, r, s, t):
    def branin_hoo(x):
        # f(x) = a(y - b*x^2 + c*x - r)^2 + s (1 - t) cos(x) + s
        return (
            a * (x[1] - b * x[0] ** 2 + c * x[0] - r) ** 2
            + s * (1 - t) * np.cos(x[0])
            + s
        )

    return branin_hoo

branin_hoo_params = dict(
    a=1, b=5.1 / (4 * np.pi ** 2), c=5 / np.pi, r=6, s=10, t=1 / (8 * np.pi)
)

def branin_hoo_fn(x):
    """The Branin-Hoo function is a popular benchmark for Bayesian optimization.
    """
    z = branin_hoo_factory(**branin_hoo_params)(x)
    return z

def sample_branin_hoo(n_samples, domain=[[-5, 15], [0, 10]]):
    """Take samples from the Branin-Hoo function.

    Args:
        n_samples (int): number of samples to draw

    Returns:
        np.array: 2d array of x, y z points
        np.array: 1d array of z points
    """
    [x_min, x_max], [y_min, y_max] = domain

    xy = np.random.uniform(
        low=[x_min, y_min], high=[x_max, y_max], size=(n_samples, 2)
    ).T
    z = branin_hoo_fn(xy)

    return xy, z


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

def compare_poission_exp():
    """
    This post explained the relation between these two distribution
      - https://stats.stackexchange.com/a/2094/134555
      - P(Xt <= x) = 1 - e^(-lambda * x)
    Now, I suppose lambda=1, just like this example(from wiki, Poisson_distribution):
      - On a particular river, overflow floods occur once every 100 years on average.
    :return:
    """
    x = np.arange(20)
    y1 = 1 - np.power(np.e, -x)  # lambda = 1
    y2 = 1 - np.power(np.e, -0.2*x)  # lambda = 0.2
    y3 = 1 - np.power(np.e, -5*x)  # lambda = 1.5
    print(y1)
    print(y2)
    print(y3)
    fig, ax = plt.subplots(1, 1)
    ax.plot(x, y1, 'r-', label='lambda=1')
    ax.plot(x, y2, 'g-', label='lambda=0.2')
    ax.plot(x, y3, 'b-', label='lambda=5')
    ax.legend(loc='best', frameon=False)
    plt.ylabel('Probability')
    plt.title('CDF of exponential distribution')
    plt.show()

compare_poission_exp()


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

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

class gradient_descent_algorithm:
    def __init__(self,x,y,alpha,total_iteration):
        self.x = x    # x represents input features of each training examples. x is a nxm matrix.
        self.y = y    # y represents outputs of each training examples. y is a real number.
        self.alpha = alpha
        self.total_iteration = total_iteration

    def get_cost_function(self,theta):
        m = self.x.shape[1]    # m represents number of training examples. Amount of columns in matrix x is passed to m.
        squared_error = 0
        squared_error_sum = 0
        for mm in range(0,m):
            hypothesis = theta.transpose() * self.x[:,mm]
            squared_error = (hypothesis[0,0] - self.y[0,mm]) ** 2
            squared_error_sum += squared_error
        cost_function = squared_error_sum / (2 * m)
        return cost_function

    def gradient_descent(self):
        n = self.x.shape[0]    # n represents number of features. Amount of rows in matrix x is passed to n.
        m = self.x.shape[1]    # m represents number of training examples. Amount of columns in matrix x is passed to m.
        iteration_count = 0
        cost_function   = 0
        theta = np.matrix([[None]]*n)    # theta represents parameters of x of each training examples. theta is a nx1 matrix.
        for nn in range(0,n):
            theta[nn,0] = 0
        cost_function_vs_iteration_count_plot = plt.subplot(212)
        cost_function_vs_iteration_count_plot.set_xlabel('Iterations of gradient descent')
        cost_function_vs_iteration_count_plot.set_ylabel(r'Cost function J($\Theta$)')
        for iteration in range(0,self.total_iteration):
            cost_function = self.get_cost_function(theta)
            partial_derivative_of_squared_error_sum = np.matrix([[None]]*n)
            for nn in range(0,n):
                partial_derivative_of_squared_error_sum[nn,0] = 0
            for mm in range(0,m):
                hypothesis = theta.transpose() * self.x[:,mm]
                cost = (hypothesis - self.y[0,mm])[0,0]
                partial_derivative_of_squared_error = self.x[:,mm] * cost * 2
                partial_derivative_of_squared_error_sum += partial_derivative_of_squared_error
            partial_derivative_of_cost_function = partial_derivative_of_squared_error_sum / (2 * m)
            theta = theta - partial_derivative_of_cost_function * self.alpha
            iteration_count += 1
            print '========================================'
            print 'iteration_count = ' , iteration_count
            print 'cost_function = '   , cost_function
            print 'theta = '           , theta
            cost_function_vs_iteration_count_plot.plot(iteration_count, cost_function, color='black', marker='o', markersize=2)
        print '========================================'
        print 'Learning rate = ', self.alpha
        print 'Final theta = ', theta
        print '========================================'
        return theta

class linear_random_number_generator:
    def __init__(self,number_of_training_examples,number_of_features):
        self.number_of_training_examples = number_of_training_examples
        self.number_of_features = number_of_features

    def linear_random_number_generator_method(self):
        x = np.matrix([[None]*self.number_of_training_examples]*self.number_of_features)
        y = np.matrix([[None]*self.number_of_training_examples])
        for training_example in range(0,self.number_of_training_examples):
            x[0,training_example] = 1
            x[1,training_example] = training_example
            y[0,training_example] = x[1,training_example] + random.uniform(0,1) * 10
        return x, y

class plot_hypothesis_and_linear_random_number:
    def __init__(self,x,y,theta):
        self.x = x
        self.y = y
        self.theta = theta

    def plot_hypothesis_and_linear_random_number_method(self):
        number_of_training_examples = self.x.shape[1]
        plot_generator = plt.subplot(211)
        plot_generator.set_xlabel('Input x of training examples')
        plot_generator.set_ylabel('Output y of training examples')
        for training_example in range(0,number_of_training_examples):
            plot_generator.plot(self.x[1,training_example], self.y[0,training_example], color='red', marker='o', markersize=6)
        theta_1 = self.theta[1,0]
        theta_0 = self.theta[0,0]
        x_axis = np.arange(0.,100.,0.2)
        hypothesis = theta_1 * x_axis + theta_0
        y_axis = hypothesis
        plot_generator.plot(x_axis, y_axis, color='black', linestyle='solid', linewidth=2)

# Main function
plt.figure()
L = linear_random_number_generator(number_of_training_examples=100, number_of_features=2)
input_x, output_y = L.linear_random_number_generator_method()
G = gradient_descent_algorithm(x=input_x, y=output_y, alpha=0.000001, total_iteration=4000)
final_theta = G.gradient_descent()
P = plot_hypothesis_and_linear_random_number(x=input_x, y=output_y, theta=final_theta)
P.plot_hypothesis_and_linear_random_number_method()
plt.show()


import itertools
import math

import numpy as np

def remove_outliers_by_percentiles(arr, lower=2.5, upper=97.5):
    """
    A more straight-forward way of removing outliers using percentile, only the one in between lower and
    upper bounds are retained, which in the default case is 95%.
   Args:
       arr (np.ndarray): the target array of floats
       lower (float): the lower limit that data under that will be removed
       upper (float): the upper limit that data over that will be removed
   Returns:
       np.ndarray: the filtered array

   """
    percentiles = np.percentile(arr, [lower, upper])
    return arr[(percentiles[0] < arr) & (percentiles[1] > arr)]

def remove_outliers(arr, rate=1.5, poisson_distribution=True):
    """
     Use interquartile range for filtering outliers in data. Outliers here are defined as observations that fall below
     Q1 − rate * IQR or above Q3 + rate * IQR. The default rate is set to 1.5.
     For possion distribution, we follow the modification of IQR in https://wis.kuleuven.be/stat/robust/papers/2008/outlierdetectionskeweddata-revision.pdf
    Args:
        arr (np.ndarray): 1D array containing data with `float` type.
        rate (float): the rate decides the gaps for upper-bound and lower-bound
        poisson_distribution (bool): whether we assume the data distribution is Possion

    Returns:
        np.ndarray: the filtered array

    """
    upper_quartile = np.percentile(arr, 75)
    lower_quartile = np.percentile(arr, 25)
    IQR = upper_quartile - lower_quartile
    lower_offset, upper_offset = _cal_offset(arr, IQR, rate) if poisson_distribution else (IQR * rate, IQR * rate)
    quartile_set = (lower_quartile - lower_offset, upper_quartile + upper_offset)
    result = arr[np.where((arr >= quartile_set[0]) & (arr <= quartile_set[1]))]

    return np.array(result)

def _cal_offset(arr, IQR, rate):
    """
    calculate the offset for cutting-off in Poission distribution
    Args:
        arr (np.ndarray): 1D array containing data with `float` type.
        IQR (float):
        rate (float:

    Returns:
        tuple(float, float): the lower and upper offsets

    """
    left_median = []
    right_median = []
    med = np.median(arr)
    for x in arr:
        if x < med:
            left_median.append(x)
        else:
            right_median.append(x)
    h = []
    for x, y in list(itertools.product(left_median, right_median)):
        h.append(float((y - med) - (med - x)) / (y - x))
    mc = np.median(h)
    compensation_pos, compensation_neg = math.exp(-4 * mc) if mc > 0 else math.exp(-3 * mc), math.exp(3 * mc) if mc > 0 else math.exp(3 * mc)
    return IQR * rate * compensation_pos, IQR * rate * compensation_neg


# -*- coding: utf-8 -*-
"""
es7
"""

import numpy as np
import interpolL as fz
import matplotlib.pyplot as plt
import math
from funzioni_Interpolazione_Polinomiale import InterpL

def zeri_Cheb(a,b,n):
    t1=(a+b)/2
    t2=(b-a)/2
    x=np.zeros((n+1,))
    for k in range(n+1):
        x[k]=t1+t2*np.cos(((2*k+1)/(2*(n+1))*math.pi))

    return x

sceltaf=input("Scegli funzione ")

scelta_funzione = {
        '1': [lambda x: np.sin(x)-2*np.sin(2*x),-math.pi, math.pi],
        '2': [lambda x: np.sinh(x),-2,2],
        '3': [lambda x: np.abs(x), -1,1],
        '4': [lambda x: 1/(1+x**2), -5,5]
}
f,a,b=scelta_funzione.get(sceltaf)

sceltap= input("Scegli tipo punti : 1(equidistanti) 2(Chebishev)\n ")

n=int(input('Grado del polinomio '))
scelta_punti = {
        '1': np.linspace(a,b,n+1),
        '2': zeri_Cheb(a,b,n)
        }
x=scelta_punti.get(sceltap)
xx = np.linspace(a,b,200)
y = f(x)
yy = f(xx)
p = fz.interpolL(x, y, xx)

err = (np.abs(f(xx) - p) )
plt.plot(x, y, "ro", xx, p, "g", xx, f(xx), "b")
plt.legend(["Pti interpolazione", "Polinomio", "Funzione"])
plt.show()
plt.plot(xx, err)
plt.title("ErroreRel")
plt.show()


"""
This module implements a generic Runge-Kutta solver.
"""
import numpy as np

def runge_kutta(f, x0, t_start, t_end, delta_t):
    """
    The Runge-Kutta method for `n` dimensional (non-linear) systems.

    Parameters
    ----------
    f: function
        A list containing the entries of the vector-valued function `f`.
    x0: list
        Contains the initial values of the system.
    t_start: float
        The starting time.
    t_end: float
        The ending time.
    delta_t: float
        The time step used when solving the system.

    Return
    ------
    tuple
        The first entry is a list containing all the employed time steps.
        The second entry is a list, containing the corresponding at each time step.
    """
    t = []
    x = []
    t_last = t_start
    x_last = x0
    while t_last < t_end:

        k1 = f(x_last) * delta_t
        k2 = f(x_last + k1 * 0.5) * delta_t
        k3 = f(x_last + k2 * 0.5) * delta_t
        k4 = f(x_last + k3) * delta_t

        x_last = x_last + 1. / 6. * (k1 + k2 * 2. + k3 * 2 + k4)
        x.append(x_last)
        t_last = t_last + delta_t
        t.append(delta_t)

    return np.array(t), np.array(x)

if __name__ == "__main__":
    import doctest
    doctest.testmod()


# AUTORES:
# Ana Beatriz Popazoglo 9806892
# Lucas Viana Vilela 10748409
# Murilo Ramos Botto  9288561

from sympy import * # Biblioteca para trabalhar com variáveis simbólicas
from sympy.abc import _clash1 # Reconhecimento de 'x' como uma variável na função sympify()
from statistics import mean # Função que calcula a média entre uma lista de valores

expr_f = sympify("21*x**4 - 11*x**3 + 19*x**2 - 11*x - 2") # Expressão de f(x)
expr_df = diff(expr_f,'x') # Expressão de f'(x)
expr_psi = simplify(sympify('x') - (expr_f/expr_df)) # Expresão de psi(x) = x - f(x)/f'(x)

# Intervalos: [a[0], b[0]] = [-1, 0] e [a[1], b[1]] = [0, 1]
a = [-1, 0]
b = [0, 1]

sol = [-1/7, 2/3] # Raízes exatas nos respectivos intervalos

epsilon = 10**-6 # Precisão
MAXITER = 10**3 # Máximo de iterações

def f(value: float): # f(x) = expr_f - Função que vai avaliar f(x) em um determinado ponto
    variable = sympify('x')
    return (expr_f.subs(variable,value)).evalf()

def df(value: float): # f(x) = expr_f - Função que vai avaliar f(x) em um determinado ponto
    variable = sympify('x')
    return (expr_df.subs(variable,value)).evalf()

def psi(value: float): # f(x) = expr_f - Função que vai avaliar f(x) em um determinado ponto
    variable = sympify('x')
    return (expr_psi.subs(variable,value)).evalf()

for i in range(2): # Loop que vai fazer os cálculos para ambos os intervalos
    output = open("./newton_saída<%d>.txt" % i,"w") # Abre o arquivo de saída

    # Este e todos os outros output.write() vão escrever dados no arquivo de saída
    output.write("\n {:^15} {:^30} {:^30} {:^30} {:^30} \n".format("k", "x_k", "f(x_k)", "f'(x_k)", "e_k"))

    # Apenas dois valores x_k serão armazenados por vez, o atual e o anterior
    x = [b[i], 0]

    # Inicializa em 0 o índice auxiliar que será utilizado para acessar o valor atual de x
    j = 0

    output.write(" {:^15} {: ^ 30.8f} {: ^ 30.8e} {: ^ 30.8e} {: ^ 30.8e} \n".format("0", x[j], float(f(x[j])), float(df(x[j])), float(abs(x[j] - sol[i]))))

    # Loop pelo processo de cálculo até que o número máximo de iterações sera atingido
    for k in range(1,MAXITER+1):
        j += 1 if j < 1 else -j # j = 0 --> j = 1 --> j = 0 --> j = 1 ...

        x[j] = psi(x[j-1]) # Valor do x atual

        e = abs(x[j] - sol[i]) # Erro em relação à raiz exata

        output.write(" {:^15} {: ^ 30.8f} {: ^ 30.8e} {: ^ 30.8e} {: ^ 30.8e} \n".format(k, x[j], float(f(x[j])), float(df(x[j])), float(abs(x[j] - sol[i]))))

        # Checa se uma aproximação satisfatória foi encontrada
        isGoodApprox = ( f(x[j]) == 0 ) or ( abs(x[j] - x[j-1]) < epsilon * max(1,abs(x[j])) )
        if isGoodApprox: break # Se sim, encerra o cálculo neste intervalo

    output.write("\n")
    output.close() # Fecha o arquivo de saída

    # Se sair do 'for', significa que chegou-se a uma aproximação satisfatória ou que o número máximo de iterações foi atingido
    if isGoodApprox: continue
    else: raise Exception("The maximum number of iterations was met and no root was found.")


import numpy as np

def min_max_norm(y):
    """calculates the min max norm of an array

    Args:
        y(array-like): an array of int or floats

    Returns:
        out(array-like): a normalized array

    Raises:
        ValueError: if y is not array-like
    """
    try:
        _y = np.asarray(y)
    except BaseException as be:
        raise ValueError(
            "y must be scalar or array like :: {}".format(
                str(be)))
    return (_y - _y.min()) / (_y.max() - _y.min())

NORM_MAP = {
    "min_max": min_max_norm
}

def get_normalizer(normalizer: str):
    """returns a normalizer function using a string

    Args:
        normalizer(str): normalizer identifier key

    Returns:
        normalizer(Callable): a normalizing method

    Raises:
        KeyError: if normalizer is not available or key not found
    """
    if normalizer in NORM_MAP:
        return NORM_MAP[normalizer]
    else:
        raise KeyError("normalizer is not in available normalizers: [{}]".format(
            NORM_MAP.keys()
        ))