import numpy as np
import pandas as pd
import scipy.stats as stats

# The data set contains users split into a control and experimental group.
# Each user visits a website and maybe clicks on the download button (1)
# or not (0)

data = pd.read_csv('statistical_significance_data.csv')
print(data.head())

# number of visitors
n_visits = data.shape[0]

# number of visitors in control group
n_control = data.groupby('condition').size()[0]

### check the invariant metric first, i.e. that the control group size is compatible wit the null hypothesis
print('Check the invariant metric')

# null hypothesis
p_null = 0.5

# 1. Solution via Simulation
n_trials = 200000
samples = np.random.binomial(n_visits, p_null, n_trials)
p_value = np.logical_or(samples <= n_control, samples >= (n_visits - n_control)).mean()
print('p-value {:.3f}'.format(p_value))

# 2. Analytic solution via Standardisation
exp = p_null * n_visits
sd = np.sqrt(p_null * (1-p_null) * n_visits)
z = (n_control - exp) / sd
p_value = 2 * stats.norm.cdf(z)
print('p-value {:.3f}'.format(p_value))

### Check the evaluation metric, i.e. that the click rate of the experimental group is significantly higher than
# the click rate of the control group
print('Check the evaluation metric')

# null hypothesis
p_null = data['click'].mean()

# size of control and experimental group
n_control = data.groupby('condition').size()[0]
n_exp = data.groupby('condition').size()[1]

# click probabilit of control and exp groups
p_click = data.groupby('condition').mean()['click']
p_control_click = p_click[0]
p_exp_click = p_click[1]

# ratio R as random variable
R = p_exp_click - p_control_click

# 1. Simulation Solution
n_trials = 20000
control_clicks = np.random.binomial(n_control, p_null, n_trials)
exp_clicks = np.random.binomial(n_exp, p_null, n_trials)
samples = exp_clicks/n_exp - control_clicks/n_control
p_value = (samples >= R).mean()
print('p-value {:.3f}'.format(p_value))

# 2. Analytic Solution
sd = np.sqrt(p_null * (1-p_null) * (1/n_control + 1/n_exp))
z = R / sd
p_value = 1 - stats.norm.cdf(z)
print('p-value {:.3f}'.format(p_value))


import sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from scipy.spatial import distance
from sklearn import datasets

#Funcion logistica
def logit(t):
  return 1.0 / (1 + np.exp(-t))

def batchGradientDescent(x,y,alpha,threshold):
  #Añadiendo una columna con 1's a x
  m1,n1 = x.shape
  aux = np.ones(m1)
  aux = aux.reshape(m1,1)
  x = np.append(aux,x,axis=1)
  #Creando beta
  m2,n2 = x.shape
  aux = np.ones(n2)
  beta = np.zeros(n2)
  beta = beta.reshape(n2,1)         #beta = np.ones((np.shape(x)[1],1))
  #beta[0] = 0.5
  #Cambiando forma de y sin perder sus datos
  y = y.reshape(-1,1)
  x_transpose = x.transpose()
  j = 0
  betasResta = 1
  #Algoritmo
  while betasResta > threshold:
    B0 = beta
    t = np.dot(x,beta)
    functLogistica = logit(t) - y
    beta = beta - (alpha/m2)*((np.dot(x_transpose,functLogistica))/m2)
    betasResta = np.linalg.norm(B0-beta)
    j = j + 1
  return beta,j

def tasaDeError(x,y,beta):
  mse = 0
  j = 0
  b = beta.transpose()
  probTotal = []
  vectorProb = []
  #Se obtienen todas las predicciones, se guardan y despues de clasifican dependiendo su valor
  for i in x:
    prob = logit(np.dot(b,i))
    vectorProb.append(prob)
    if prob >= 0.5:
      probTotal.append(1.0)
    else:
      probTotal.append(0)
    #Se obtiene taza de error Sum(I(Y^,y))
  for i in y:
    if probTotal[j] != i :
      mse = mse + 1
    j = j+1
  mse = (1/len(x))* mse
  #MSE = Taza de error , Probtotal = Y^ , VectorProb = p^
  return mse,probTotal,vectorProb

def sciKitLearnMethod(x,y,xTest,yTest,alpha,threshold):
  model = LogisticRegression()
  #Entrenando modelo
  model.fit(x,y)
  y_pred = model.predict(x)
  #Encontrando valores de theta
  beta1 = model.coef_
  beta0 = model.intercept_
  print("Vector beta usando Scikit-Learn: Coeficiente =",beta1,"Intercepcion =",beta0)
  #Encontrando precison del modelo
  accuracy = accuracy_score(y,y_pred)
  print("Taza de error usando Scikit-Learn:",accuracy)

def main():
  # genero.txt  Default.txt
  dataSet =  sys.argv[1] # param1: nombre del archivo del dataset.
  trainSetProc = sys.argv[2]#  param2: porcentaje de elementos del training set (ocupar valores enteros).
  testSetProc =  sys.argv[3]#  param3: porcentaje de elementos del test set (ocupar valores enteros).
  alpha = sys.argv[4] # param4: valor de α.  0.5 o 0.0005
  threshold =  sys.argv[5] # param5: valor del threshold (1x10^-4).0.0001
  semillaGem = sys.argv[6] # param6: semilla del generador de números pseudoaleatorios. 37

  #Links a los archivos que se encuentran en Github y se guarda la información en una matriz
  if dataSet == "Default.txt":
    url = "https://raw.githubusercontent.com/DiegoMontano2705/MachineLearning/main/Parcial1/Default.txt"
    data = pd.read_csv(url, sep='\t')
    default = {'Yes': 0, 'No': 1}
    student = {'Yes': 0, 'No': 1}
    data.default = [default[item] for item in data.default]
    data.student = [student[item] for item in data.student]
    x = data[['student','balance','income']] #features del vector de entrada
    y = data['default'] #Categoria a ser predecida
  elif dataSet == "genero.txt":
    url = "https://raw.githubusercontent.com/DiegoMontano2705/MachineLearning/main/Parcial1/genero.txt"
    data = pd.read_csv(url)
    gender = {'Male': 0, 'Female': 1}
    data.Gender = [gender[item] for item in data.Gender]
    x = data[['Height','Weight']] #features del vector de entrada
    y = data['Gender'] #Categoria a ser predecida
  else:t
    print("Cant process file")

  #Creando vector de entrenamiento
  x_train,x_test,y_train,y_test = train_test_split(x, y, test_size=testSetProc, random_state=semillaGem)
  x_train = x_train.values  #dataset datos aletorios
  y_train = y_train.values  #dataset datos aletorios
  y_test = y_test.values
  betaFinal,n = batchGradientDescent(x_train,y_train,alpha,threshold)
  #Error cuadratico medio
  m1,n1 = x_test.shape
  aux = np.ones(m1)
  aux = aux.reshape(m1,1)
  x_test = np.append(aux,x_test,axis=1)
  y_test = y_test.reshape(-1,1)

  pred,y_gorrito,vectorProb = tasaDeError(x_test,y_test,betaFinal)

  #Valores finales
  print("Vector beta =",betaFinal)
  print("Interacciones necesarias para entrenar modelo =",n)
  print("Valor de alpha empleado =",alpha)
  print("Valor de threshold empleado =",threshold)
  print("Taza de error que tiene el modelo =",pred)

  #Vectores finales guardados en archivo
  file = open('vectores.txt', 'w')
  file.write("Vector X\n")
  np.savetxt(file,x_test,fmt='%1.3f')
  file.write("Vector Y_^\n")
  file.write(str(y_gorrito))
  file.write('\n')
  file.write("Vector Y\n")
  np.savetxt(file,y_test, fmt='%1.3f')
  file.write("Vector Probabilidades(P^) Y\n")
  file.write(str(vectorProb))
  file.close()

  #Grafica de la clasificacion para archivo Default.txt
  #if dataSet == "Default.txt":
    #Y^ vs X

  print("\nRegresion Logistica con Scikit-Learn:")
  sciKitLearnMethod(x_train,y_train,x_test,y_test,alpha,threshold)

if __name__ == '__main__':
  main()


import numpy as np

def linear_regression(x, y):
    """
    Function which evaluate 'slope' and 'intercept' coefficients for linear regression:
        y_hat = intercept + slope * x;
    where:
        x       input value
        y_hat   predicted value

    :param x: input data
    :param y: output data
    :return: tuple of coefficients (slope, intercept)
    """
    assert (len(x) == len(y))

    x_mean = np.mean(x)
    y_mean = np.mean(y)

    numerator = sum([xi * yi for xi, yi in zip(x, y)])
    denominator = sum([xi ** 2 for xi in x])

    slope = numerator / denominator
    intercept = y_mean - slope * x_mean

    return slope, intercept

def rmse(x, y, y_hat):
    """
    Function which evaluate RMSE regarding to this formula:
        @link http://www.saedsayad.com/images/RMSE.png

    :param x: input values
    :param y: real output values
    :param y_hat: function we use for predicate output
    :return: evaluated Root Mean Square Error
    """

    assert (len(x) == len(y))
    n = len(x)

    return np.sqrt(sum(map(lambda (xi, yi): (yi - y_hat(xi))**2, zip(x, y)))/n)


import numpy as np

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

def sigmoid_der(x):
    return  x * (1 - x)

def tanh(x):
    return (np.exp(x) - np.exp(-x))/(np.exp(x) + np.exp(-x))

def tanh_der(x):
    return (1 - (x**2))


#The first two consecutive numbers to have two distinct prime factors are:

#14 = 2 × 7
#15 = 3 × 5

#The first three consecutive numbers to have three distinct prime factors are:

#644 = 2² × 7 × 23
#645 = 3 × 5 × 43
#646 = 2 × 17 × 19.

#Find the first four consecutive integers to have four distinct prime factors each. What is the first of these numbers?

import math
import timeit
import numpy as np

primes = []
def Esieve(n):
    # Create a boolean array "prime[0..n]" and initialize
    #  all entries it as true. A value in prime[i] will
    # finally be false if i is Not a prime, else true.
    prime = [True for i in range(n+1)]
    p = 2
    while (p * p <= n):

        # If prime[p] is not changed, then it is a prime
        if (prime[p] == True):

            # Update all multiples of p
            for i in range(p * p, n+1, p):
                prime[i] = False
        p += 1

    # Print all prime numbers
    for p in range(2, n):
        if prime[p]:
            primes.append(p)

def factor(num):
    fact = []
    for p in primes:
        while num % p == 0:
            fact.append(p)
            num = num/p
        if num == 1:
            if len(np.unique(fact)) == 4:
                return True
            else:
                return False

Esieve(40000)

solution = False
num_count = 0
i = 209
while solution == False:
    i += 1
    num = i
    fact = []
    if factor(num) == True:
        num_count += 1
        if num_count == 4:
            print(i-3)
            break
        else:
            continue
    else:
        num_count = 0


import numpy as np
import math

def logistic_fit(X, y, w=None, batch_size=None, learning_rate=1e-2,
                 num_iterations=1000, return_history=False):
    '''Implements the logistic regression algorithm to find the weights array'''
    N, d = X.shape
    Xe = np.hstack((np.ones((N, 1)), X))

    if w is None:
        w = np.random.rand(d + 1)
    w_list = [w]

    if batch_size is None or batch_size > N:
        batch_size = N
    batch_num = math.ceil(N/batch_size)

    for _ in range(num_iterations):
        batch_ini = 0
        for __ in range(batch_num):
            batch_end = batch_ini + batch_size

            gradient = (-1/batch_size) * sum(
                y[i]*Xe[i] / (1 + np.exp((y[i]*w.T).dot(Xe[i])))
                for i in range(batch_ini, batch_end)
            )

            w = w - learning_rate * gradient
            w_list += [w]

            batch_ini = batch_end

    def in_sample_error(w):
        return 1/N * sum(np.log1p(np.exp(-yn*w.T.dot(xn))) for xn, yn in zip(Xe, y))

    if return_history:
        return w, list(map(in_sample_error, w_list))
    return w

def logistic_predict(X, w):
    '''Returns the 1D array of predictions'''
    N, d = X.shape
    Xe = np.hstack((np.ones((N, 1)), X))

    def h(s): return np.exp(s) / (1 + np.exp(s))

    return [h(w.T.dot(x)) for x in Xe]


# coding:utf8

"""
Description:不均衡的数据归一化处理方法
Author：伏草惟存
Prompt: code in Python3 env
"""

from numpy import *
from lossval import replaceNanWithMean

'''数值归一化：特征值转化为0-1之间：newValue = (oldValue-min)/(max-min)'''
def norm_dataset(dataset):
    minVals = dataset.min(0)  # 参数0是取得列表中的最小值，而不是行中最小值
    maxVals = dataset.max(0)
    ranges = maxVals - minVals

    normdataset = zeros(shape(dataset)) # 生成原矩阵一样大小的0矩阵
    m = dataset.shape[0]

    # tile:复制同样大小的矩阵
    molecular = dataset - tile(minVals,(m,1))  # 分子： (oldValue-min)
    Denominator = tile(ranges,(m,1))           # 分母：(max-min)
    normdataset = molecular / Denominator     # 归一化结果。

    print('归一化的数据结果：\n'+str(normdataset))
    return normdataset,ranges,minVals

if __name__=='__main__':
    dataset = replaceNanWithMean()
    # print(dataset)
    normdataset,ranges,minVals = norm_dataset(dataset[:,:-1])
    # print(normdataset[:100])


import numpy as np
import random
from utils import show

def fit_lsq(x, y):
    """
    return linear model: a*x + b
    """

    x = np.matrix(np.vstack((x, np.ones(len(x))))).T
    y = np.matrix(y).T

    model_vec = np.linalg.inv(x.T * x) * x.T * y

    a = model_vec[0, 0]
    b = model_vec[1, 0]

    return model_vec, lambda p: a * p + b

def dist(model, x, y):
    return np.abs(y - model(x))

def ransac(data, sample_size=10, num_iter=100, inlier_thr=10, target_inliers=None):
    """ """

    x, y = np.where(data == 1)
    z = list(zip(x, y))

    best_inlier_count = 0
    best_model = None
    best_inliers = None

    for it in range(num_iter):
        sample = random.sample(z, sample_size)

        sx = np.array([e[0] for e in sample])
        sy = np.array([e[1] for e in sample])

        model_vec, model = fit_lsq(sx, sy)
        # print(f"[{it}] -> {model_vec.T}")

        inliers = [
            (px, py) for (px, py) in zip(x, y) if dist(model, px, py) <= inlier_thr
        ]

        if len(inliers) > best_inlier_count:
            best_inlier_count = len(inliers)
            # best_model = (model_vec, model)
            best_inliers = inliers[:]

            best_model = fit_lsq(
                x=np.array([e[0] for e in inliers]), y=np.array([e[1] for e in inliers])
            )

            if target_inliers is not None and len(inliers) > target_inliers:
                break

        if it % (num_iter // 10) == 0:
            print(f"[+] iter {it}")

    print(f"[+] Took {it} iterations. Best inlier count = {best_inlier_count}.")

    return best_model, best_inliers

def strip_inliers(data, inliers):
    print(f"[+] Removing {len(inliers)} inliers")
    for px, py in inliers:
        data[px, py] = 0
    return data

def find_sequential(data, config):
    cdata = data.copy()

    models = []
    model_vecs = []

    for line in range(config["num_lines"]):
        (model_vec, model), inliers = ransac(
            cdata, config["sample_size"], config["num_iter"], config["inlier_thr"]
        )

        cdata = strip_inliers(cdata, inliers)
        # show(cdata, cmap='gray')

        models.append(model)
        model_vecs.append(model_vec)

        print(f"[+] [Fitted line idx {line}] Best model: {model_vec.T}")

    return model_vecs, models


import matplotlib.pyplot as plt
import numpy as np
import xlsxwriter as xl
import time

# Função utilizada: f(x) = 3 cos(x) - e^x/3 // Intervalo: [-5; 2]// h: 0.75 // Precisão: 10^-5

tempo_inicial = time.time()

"""# Plotando o gráfico, e isolando as raizes:
x = np.arange(-5, 2, 0.75)
print(x)
plt.plot(x, 3*np.cos(x) - np.exp(x) / 3)
plt.show()"""

# Utilizando o método da bisseccao para o intervalo [1; 1.75]:

def test(x):
    valor_aplicado = (3*np.cos(x) - np.exp(x) / 3)
    return valor_aplicado

def bisseccao():
    c = 0
    valores_a = [1]  # Valores positivos
    valores_b = [1.75]  # Valores negativos
    x_barra_aplicados = []
    x_barra = []
    while True:
        x_barra_atualizado = (valores_a[-1] + valores_b[-1]) / 2
        x_barra.append(x_barra_atualizado)
        validacao = test(x_barra[-1])
        if validacao < 0:
            modulo_validacao = validacao * (-1)
            valores_b.append(x_barra_atualizado)
            valores_a.append(valores_a[-1])
        else:
            modulo_validacao = validacao
            valores_a.append(x_barra_atualizado)
            valores_b.append(valores_b[-1])
        if modulo_validacao < 10 ** -5:
            x_barra_aplicados.append(validacao)
            break
        else:
            x_barra_aplicados.append(validacao)
            c += 1
    raiz_aproximada = f'{x_barra[-1]:.5f}'
    return raiz_aproximada, c, x_barra, x_barra_aplicados, valores_a, valores_b

raiz_aproximada_bissecao = bisseccao()[0]
c_total = bisseccao()[1]
print('Questao 02, intervalo [1; 1.75]:')
print(f'A raiz aproximada é: {raiz_aproximada_bissecao}')
print(f'Para encontrar essa raiz, foram necessárias {c_total} iterações.')

"""# Criando a tabela com os valores obtidos
# Criando o arquivo
outWorkbook = xl.Workbook("Questao_03_3o_intervalo_bisseccao.xlsx")
outSheet = outWorkbook.add_worksheet()

# Declarando os dados:
valores_a = bisseccao()[4]
valores_b = bisseccao()[5]
x_barra_list = bisseccao()[2]
x_barra_aplicado = bisseccao()[3]

# Declarando os títulos

# Escrevendo os dados no arquivo
# .write(location_y, location_x, thing to be written)
outSheet.write("A1", "a")
outSheet.write("B1", "b")
outSheet.write("C1", "x_barra")
outSheet.write("D1", "f(x_barra)")

for number, item in enumerate(valores_a):
    outSheet.write(number + 1, 0, item)
for number, item in enumerate(valores_b):
    outSheet.write(number + 1, 1, item)
for number, item in enumerate(x_barra_list):
    outSheet.write(number + 1, 2, item)
for number, item in enumerate(x_barra_aplicado):
    outSheet.write(number + 1, 3, item)
outWorkbook.close()"""

tempo_final = time.time()
tempo_gasto = tempo_final - tempo_inicial
print(f'Foram gastos {tempo_gasto}s.')


#!/usr/bin/env python3

import numpy as np
import matplotlib.pyplot as plt

def cosine_lookup_table(length):
    table = np.cos(np.linspace(0, np.pi/2, length))
    return table

pi = np.pi
two_pi = 2*pi
half_pi = pi/2

#  def cos_1q(x):
#      cosine_lookup = cosine_lookup_table(N)
#      index_float = x / half_pi * (N-2)
#      value = cosine_lookup[int(index_float)]
#      return value

def cos_1q(x, cosine_lookup):
    N = len(cosine_lookup)
    index_float = x / half_pi * (N-2)
    index_low = int(index_float)
    index_high = int(index_float+1)
    cos_low = cosine_lookup[index_low]
    cos_high = cosine_lookup[index_high]
    # a = dy/dx,   1 cause it is by index
    a = (cos_high - cos_low) / 1
    value = cos_low + a * (index_float - index_low)
    return value

#  # or from taylor series
#  def cos_1q(x, _):
#      import math
#      fac = math.factorial
#      return 1 - x**2/fac(2) + x**4/fac(4)   - x**6 / fac(6)   #+ x**8 / fac(8)

def cos(x, cosine_lookup):
    # modulo to range [0, 2pi]
    x = x % two_pi
    # cos(-x) = cos(x)
    if x < 0:
        x = -x
    # check in which quadrant we are
    quadrant = int(x / half_pi)
    # use trigonometric identites to convert x to 1 quadrant
    if quadrant == 0:
        return cos_1q(x, cosine_lookup)
    elif quadrant == 1:
        return -cos_1q(pi - x, cosine_lookup)
    elif quadrant == 2:
        return -cos_1q(x - pi, cosine_lookup)
    elif quadrant == 3:
        return cos_1q(two_pi - x, cosine_lookup)
    assert 0, "Something is not yes"

def sin(x, cosine_lookup):
    return cos(x - half_pi, cosine_lookup)

def test_error(x, cosine_lookup):
    cos_ref = np.cos(x)
    cos_approx = np.array([cos(xi, cosine_lookup) for xi in x])
    error = cos_ref - cos_approx
    mse = np.sum(error**2) / len(error)
    return np.max(np.abs(error)), mse

def plot_test_error(N, cosine_lookup):
    print('dx = %f' % (x[1] - x[0]))
    error_only = True

    if 1:
        plt.figure()
        sin_ref = np.sin(x)
        sin_approx = np.array([sin(xi, cosine_lookup) for xi in x])
        error = sin_ref - sin_approx
        mse = np.sum(error**2) / len(error)
        print('Mean square error = %e' % mse)
        if error_only:
            plt.plot(x, error / 1 * 100, label='error [%]')
        else:
            plt.plot(x, sin_ref, label='sin_ref(x)')
            plt.plot(x, sin_approx, label='sin_approx(x)')
            plt.plot(x, error, label='sin_ref(x) - sin_approx(x)')
        plt.xlabel('x')
        plt.ylabel('f(x)')
        plt.grid(True)
        plt.legend()

    if 1:
        plt.figure()
        cos_ref = np.cos(x)
        cos_approx = np.array([cos(xi, cosine_lookup) for xi in x])
        error = cos_ref - cos_approx
        mse = np.sum(error**2) / len(error)
        print('Mean square error = %e' % mse)
        if error_only:
            plt.plot(x, error / 1 * 100, label='error [%]')
        else:
            plt.plot(x, cos_ref, label='cos_ref(x)')
            plt.plot(x, cos_approx, label='cos_approx(x)')
            plt.plot(x, error, label='cos_ref(x) - cos_approx(x)')
        plt.xlabel('x')
        plt.ylabel('f(x)')
        plt.grid(True)
        plt.legend()

    plt.show()

if __name__ == '__main__':
    x = np.linspace(0, 2*np.pi, 100000)

    if 0:
        cosine_lookup = cosine_lookup_table(1000)
        plot_test_error(x, cosine_lookup)
    else:
        max_errors = []
        mse_values = []
        #  n_range = np.arange(100, 500, 50)
        n_range = np.logspace(2, 3, 20)
        for n in n_range:
            print('n = %d' % n)
            cosine_lookup = cosine_lookup_table(n)
            max_err, mse = test_error(x, cosine_lookup)
            max_errors.append(max_err)
            mse_values.append(mse)
        fig, ax = plt.subplots(2)
        ax[0].plot(n_range, max_errors, '.-', label='Max error(N)')
        ax[0].grid(True)
        ax[0].legend()
        ax[1].plot(n_range, mse_values, '.-', label='Mean square error(N)')
        ax[1].grid(True)
        ax[1].legend()
        plt.show()


# Each octopus slowly gains energy over time and flashes brightly for a moment when its energy is full.
# step:
# First, the energy level of each octopus increases by 1.
# Then, any octopus with an energy level greater than 9 flashes. This increases the energy level of all adjacent octopuses by 1, including octopuses that are diagonally adjacent. If this causes an octopus to have an energy level greater than 9, it also flashes. This process continues as long as new octopuses keep having their energy level increased beyond 9. (An octopus can only flash at most once per step.)
# Finally, any octopus that flashed during this step has its energy level set to 0, as it used all of its energy to flash.

import numpy as np

with open('input.txt', 'r') as f:
    lines = f.readlines()

lines = [line[:-1] for line in lines]
lines = [list(line) for line in lines]
grid = np.array([list(int(item) for item in line) for line in lines])

totalFlashes = 0
print(grid)
for step in range(1, 101):
    # 1. increase energy level by 1
    grid += 1

    # 2./3. flashing
    while np.count_nonzero(grid > 9) > 0:
        # loops aslong as there are values greater then 9 in the grid

        for (x, y), val in np.ndenumerate(grid):
            if val > 9:
                grid[x][y] = 0  # octopus flashes
                totalFlashes += 1

                # increases the energy level of all adjacent octopuses by 1
                for dx in range(-1, 2):
                    for dy in range(-1, 2):
                        # checks which didnt flash this step (padding for the borders)
                        if np.pad(grid, 1, mode="constant", constant_values=0)[1 + x + dx][1 + y + dy] != 0:
                            grid[x + dx][y + dy] += 1

print("How many total flashes are there after 100 steps?", totalFlashes)

# part 2
grid = np.array([list(int(item) for item in line) for line in lines])

numSteps = 0
for step in range(1, 100000):
    # 1. increase energy level by 1
    grid += 1

    # 2./3. flashing
    while np.count_nonzero(grid > 9) > 0:
        # loops aslong as there are values greater then 9 in the grid

        for (x, y), val in np.ndenumerate(grid):
            if val > 9:
                grid[x][y] = 0  # octopus flashes
                totalFlashes += 1

                # increases the energy level of all adjacent octopuses by 1
                for dx in range(-1, 2):
                    for dy in range(-1, 2):
                        # checks which didnt flash this step (padding for the borders)
                        if np.pad(grid, 1, mode="constant", constant_values=0)[1 + x + dx][1 + y + dy] != 0:
                            grid[x + dx][y + dy] += 1

    if not np.any(grid):
        numSteps = step
        break

print("What is the first step during which all octopuses flash?", numSteps)


# 使用mini batch处理单个或多个数据，并进行交叉熵误差
import numpy as np
# 如何从数据集中随机选取十个data？
'''
这里的x_trian， t_data是在第三个例子中的MNIST的train data
train_size = x_trian.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
y_batch = t_train[batch_mask]
'''

# use one-hot way:
def one_hot_cross_entropy_error(y , t):
    if y.ndim == 1:  # 处理单个数据需要重塑矩阵形状
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    delta = 1e-7
    batch_size = y.shape[0] # 获得batch的size
    return -np.sum(t * np.log(y + delta)) / batch_size

# don't use one-hot way:
'''
当label不再是0， 1表示的时候，而是7，2这样表示的时候
np.arange(batch_size):表示的是从0-batch_size - 1的数组
假设t = [2, 7, 0, 9, 4]
那么y[np.arange(batch_size), t] = [y[0, 2], y[1, 7], y[2, 0], y[3, 9], y[4, 4]]
'''
def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    delta = 1e-7
    batch_size = y.shape[0]


# Copyright 2017 Battelle Energy Alliance, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#***************************************
#* Simple analytic test ExternalModule *
#***************************************
#
# From Ishigami and Homma, 1990
#     Analytic UQ problem with mean, variance, sensitivities
#
import numpy as np

def run(self,Input):
  self.ans  = np.sin(self.x1) + 7.*np.sin(self.x2)**2 + 0.1*self.x3**4*np.sin(self.x1)

# included in Sudret, Global Sensitivity Analysis (2008) and many other places
# x1, x2, x3 are all distributed uniformly on [-pi, pi], a=7, b=0.1
#
# The variance is given by
#   a^2/8 + b*pi^4/5 + b^2*pi^8/18 + 1/2 = 13.8446
# Partial variance are given as
# D_1 = b*pi^4/5 + b^2*pi^8/50 + 1/2     = 4.34589
# D_2 = a^2/8 = 49/8                     = 6.125
# D_3 = 0                                = 0
# D_12 = D_23 = 0                        = 0
# D_13 = 8*b^2*pi^8/225                  = 3.3737
# D_123 = 0                              = 0
#
# Sobol sensitivities are the above divided by the total, which is
# S_1   = 0.3138
# S_2   = 0.4424
# S_3   = 0
# S_12  = 0
# S_13  = 0.2436
# S_23  = 0
# S_123 = 0
#
#  This model has analytic mean and variance documented in raven/docs/tests
#


import numpy as np

print(np.__version__)
# 1.19.0

a_1d = np.array([1, 2, 3, 4, 5, 6])
print(a_1d)
# [1 2 3 4 5 6]

print(np.cumsum(a_1d))
# [ 1  3  6 10 15 21]

print(np.cumsum(a_1d, dtype=float))
# [ 1.  3.  6. 10. 15. 21.]

print(a_1d.cumsum())
# [ 1  3  6 10 15 21]

print(a_1d.cumsum(dtype=float))
# [ 1.  3.  6. 10. 15. 21.]

l = [1, 2, 3, 4, 5, 6]

print(np.cumsum(l))
# [ 1  3  6 10 15 21]

print(type(np.cumsum(l)))
# <class 'numpy.ndarray'>

a_2d = a_1d.reshape(2, 3)
print(a_2d)
# [[1 2 3]
#  [4 5 6]]

print(np.cumsum(a_2d))
# [ 1  3  6 10 15 21]

print(np.cumsum(a_2d, axis=0))
# [[1 2 3]
#  [5 7 9]]

print(np.cumsum(a_2d, axis=1))
# [[ 1  3  6]
#  [ 4  9 15]]

print(a_2d.cumsum())
# [ 1  3  6 10 15 21]

print(a_2d.cumsum(axis=0))
# [[1 2 3]
#  [5 7 9]]

print(a_2d.cumsum(axis=1))
# [[ 1  3  6]
#  [ 4  9 15]]

l_2d = [[1, 2, 3], [4, 5, 6]]

print(np.cumsum(l_2d))
# [ 1  3  6 10 15 21]

print(np.cumsum(l_2d, axis=0))
# [[1 2 3]
#  [5 7 9]]

print(np.cumsum(l_2d, axis=1))
# [[ 1  3  6]
#  [ 4  9 15]]

l_2d_error = [[1, 2, 3], [4, 5]]

print(np.cumsum(l_2d_error))
# [list([1, 2, 3]) list([1, 2, 3, 4, 5])]
#
# /usr/local/lib/python3.8/site-packages/numpy/core/_asarray.py:83: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
#   return array(a, dtype, copy=False, order=order)

print(np.cumprod(a_1d))
# [  1   2   6  24 120 720]

print(np.cumprod(a_1d, dtype=float))
# [  1.   2.   6.  24. 120. 720.]

print(a_1d.cumprod())
# [  1   2   6  24 120 720]

print(a_1d.cumprod(dtype=float))
# [  1.   2.   6.  24. 120. 720.]

print(np.cumprod(a_2d))
# [  1   2   6  24 120 720]

print(np.cumprod(a_2d, axis=0))
# [[ 1  2  3]
#  [ 4 10 18]]

print(np.cumprod(a_2d, axis=1))
# [[  1   2   6]
#  [  4  20 120]]

print(a_2d.cumprod())
# [  1   2   6  24 120 720]

print(a_2d.cumprod(axis=0))
# [[ 1  2  3]
#  [ 4 10 18]]

print(a_2d.cumprod(axis=1))
# [[  1   2   6]
#  [  4  20 120]]

print(np.cumprod(l))
# [  1   2   6  24 120 720]

print(np.cumprod(l_2d))
# [  1   2   6  24 120 720]

print(np.cumprod(l_2d, axis=0))
# [[ 1  2  3]
#  [ 4 10 18]]

print(np.cumprod(l_2d, axis=1))
# [[  1   2   6]
#  [  4  20 120]]


# -*- coding: utf-8 -*-
"""
Created on Fri Mar 19 11:12:06 2021

@author: gualandi
"""

import pyomo

from pyomo.environ import ConcreteModel, Var, Objective, Constraint, SolverFactory
from pyomo.environ import maximize, Binary, RangeSet, PositiveIntegers, NonNegativeReals

# Problem Description
"""
Variant with fixed cost for block
"""

# Data of the problem (in theory, read data from .csv or excel file)

# Blocks you can byu
Blocks = ['Block1','Block2','Block3','Block4','Block5','Block6']

Weights = [30, 90, 50, 70, 60, 50]  # In quintal
Costs = [50, 100, 80, 85, 92, 115]  # Thousand of euros

# Componets of metal in each block (given in percetange)
Cs = [[93, 76, 74, 65, 72, 68],  # Ferro
      [5, 13, 11, 16, 6, 23],    # Cromo
      [0, 11, 12, 14, 20, 8],    # Nichel
      [2, 0, 3, 5, 2, 1]]        # Impurità

# Create concrete model
m = ConcreteModel()

# Set of indices
m.I = RangeSet(0, len(Blocks)-1)

# Variables
def fb(m, i):
    return 0, Weights[i]
m.x = Var(m.I, domain=NonNegativeReals, bounds = fb)

m.y = Var(m.I, domain=Binary)

# Objective Function
m.obj = Objective(expr = sum(Costs[i]*Weights[i]*m.y[i] for i in m.I))

# Production Constraints
m.c1 = Constraint(expr = sum(Cs[0][i]/100*m.x[i] for i in m.I) >= 65)

m.c2 = Constraint(expr = sum(Cs[1][i]/100*m.x[i] for i in m.I) == 18)

m.c3 = Constraint(expr = sum(Cs[2][i]/100*m.x[i] for i in m.I) == 10)

m.c4 = Constraint(expr = sum(Cs[3][i]/100*m.x[i] for i in m.I) <= 1)

# Overall production
m.c5 = Constraint(expr = sum(m.x[i] for i in m.I) == 100)

# Bound implication
def ImplyBound(m, i):
    return m.x[i] <= Weights[i]*m.y[i]
m.imply = Constraint(m.I, rule = ImplyBound)

# Write the LP model in standard format
m.write("misc.lp")

# Solve the model
sol = SolverFactory('glpk').solve(m, tee=True)

# CHECK SOLUTION STATUS

# Get a JSON representation of the solution
sol_json = sol.json_repn()

if sol_json['Solver'][0]['Status'] == 'ok':
    print("Optimal solution value:", round(m.obj(), 1))

    print("\tValues of the decision variables:")
    for i,b in enumerate(Blocks):
        print(b, m.x[i]())
else:
    print('Error in solving the model')


"""
Exercise: Logistic Regression

@auth: Yu-Hsiang Fu
@date: 2018/09/20
"""
# --------------------------------------------------------------------------------
# 1.Import packages
# --------------------------------------------------------------------------------
import copy
import matplotlib.pyplot as plt
import numpy as np
import pickle
import sys

# --------------------------------------------------------------------------------
# 2.Const variables
# --------------------------------------------------------------------------------
# program variable
# ROUND_DIGITS = 8

# plot variable
PLOT_X_SIZE = 5
PLOT_Y_SIZE = 5
PLOT_DPI = 300
PLOT_FORMAT = "png"

# --------------------------------------------------------------------------------
# 3.Define function
# --------------------------------------------------------------------------------
def p_deepcopy(data, dumps=pickle.dumps, loads=pickle.loads):
    return loads(dumps(data, -1))

def normalize_data(d):
    for i in range(d.shape[1]):
        mean = np.mean(d[:, i])
        sigma = np.std(d[:, i])
        d[:, i] = (d[:, i] - mean) / sigma

    return  d

def matricization(x):
    x0 = np.ones([x.shape[0], 1])

    return np.hstack([x0, x])

def f_w(x, w):
    return 1 / (1 + np.exp(-np.dot(x, w)))

def classify(x, w):
    return (f_w(x, w) >= 0.5).astype(np.int)

def logistic_regression(x, y, w, rate_learning):
    max_epoch = 1000

    # DO UPDATE-WEIGHT
    for i in range(max_epoch):
        w = w - rate_learning * np.dot((f_w(x, w) - y), x)

        print(i + 1, w)

    return w

def draw_plot(x, y, w):
    # scatter-plot
    fig, ax = plt.subplots(figsize=(PLOT_X_SIZE, PLOT_Y_SIZE), facecolor='w')

    # draw plot
    x1_p = np.linspace(-2, 2)
    x2_p = -((w[0] + w[1] * x1_p) / w[2])
    plt.plot(x[y == 1, 0], x[y == 1, 1], "o", color="b", ms=7, mew=1)
    plt.plot(x[y == 0, 0], x[y == 0, 1], "x", color="r", ms=7, mew=2)
    plt.plot(x1_p, x2_p, color="r", ls="--")

    # plot setting
    ax.grid(color="k", linestyle="dotted", linewidth=0.8, alpha=0.8)
    ax.set_xlabel(r"$x_1$", fontdict={"fontsize": 12})
    ax.set_ylabel(r"$x_2$", fontdict={"fontsize": 12})
    # ax.set_xlim(0, 525)
    # ax.set_ylim(0, 525)
    ax.set_title("Logistic Regression", fontdict={"fontsize": 12})
    ax.tick_params(axis="both", direction="in", which="major", labelsize=8)

    # legend-text
    legend_text = ["class1", "class2", "logistic"]
    ax.legend(legend_text, loc=4, fontsize="small", prop={"size": 8}, ncol=1, framealpha=1)

    # save image
    plt.tight_layout()
    plt.savefig("logistic-regression.png", dpi=PLOT_DPI, format=PLOT_FORMAT)
    plt.close()

# --------------------------------------------------------------------------------
# 4.Main function
# --------------------------------------------------------------------------------
def main_function():
    # input, output
    d = np.loadtxt("data3.csv", delimiter=",")
    x = d[:, 0:2]
    y = d[:, 2]
    x = normalize_data(x)
    X = matricization(x)

    # --------------------------------------------------
    # logistic regression
    rate_learning = 0.01
    w = np.random.rand(3)
    w = logistic_regression(X, y, w, rate_learning)

    # --------------------------------------------------
    # draw scatter-line plot and contour-plot
    draw_plot(x, y, w)

    # --------------------------------------------------
    # # test-data
    # xx = np.matrix([[200, 100], [100, 200]])
    # xx = matricization(normalize_data(xx))
    # print(classify(xx, w))

if __name__ == '__main__':
    main_function()


import numpy as np
import argparse
from mpi4py import MPI

comm = MPI.COMM_WORLD
rank = comm.Get_rank()

parser = argparse.ArgumentParser()
parser.add_argument("--points", "-pts", type=int, help="The number of points for a process to simulate in a parallelized Monte Carlo estimation of pi.")
args = parser.parse_args()

num_points = 1000

if args.points:
    #print("printing...",args.points)
    num_points = args.points

def generate_points(num_pairs):
    print('generating points...')
    return np.random.uniform(high=1, size=(num_pairs,2))

points = generate_points(num_points)

def calc_lengths(pairs):
    return np.array([np.linalg.norm(i) for i in pairs])

lengths = calc_lengths(points)

def ratio(lengths):
    count = len(np.where(lengths < 1)[0])
    #print(count)
    return count / len(lengths)

def comm_length_data(lengths):
    new_lengths = comm.gather(lengths, root=0)
    if rank == 0:
        print("root rank", rank, "gathering lengths from other ranks")
        lengths = np.concatenate(new_lengths,axis=0)
    else:
        print("non-root rank", rank, "leaving lengths alone")
        lengths = lengths
        #print("rank 0 new_lengths size", new_lengths.size)
        #print("rank 0 new_lengths", new_lengths)
    return lengths

gathered_lengths = comm_length_data(lengths)

lengths = gathered_lengths

inv = ratio(lengths)

pi_estimate = inv*4

how_close = np.abs(np.pi - pi_estimate)

print("Rank: ", comm.Get_rank(), "pi estimate:", pi_estimate, "with closeness:", how_close, "on a ratio calculated by the number of lengths", len(lengths))


#
# Local mass and stiffness matrices
# =================================
#
# by Alberto Costa Nogueira Jr. (Matlab and Python versions)
#    Renato Cantao (Python version)
#
import numpy

def local_mass(quad_data, basis):
    """Constructs the elemental mass matrix

    Arguments:
        quad_data - Quadrature points and weights
        basis - Basis and respective derivatives

    Returns:
        Mass matrix M, where m_ij = \int_k psi_i psi_j
    """
    return numpy.dot(quad_data.w*basis.psi.T, basis.psi)

def local_mass_diagonal(quad_data, basis):
    """Constructs the elemental mass matrix, diagonal version

    Arguments:
        quad_data - Quadrature points and weights
        basis - Basis and respective derivatives

    Returns:
        Mass matrix M, where m_ii = \int_k psi_i psi_i
    """
    return numpy.sum(quad_data.w*basis.psi.T**2, axis=1)

def local_stiffness(quad_data, basis):
    """Constructs the elemental stiffness matrix

    Arguments:
        quad_data - Quadrature points and weights
        basis - Basis and respective derivatives

    Returns:
        Stiffness matrix S, where s_ij = \int_k psi_i Dpsi_j/dx
    """
    return numpy.dot(quad_data.w*basis.psi.T, basis.dpsi)

def stress_stiffness(quad_data, basis, mat_prop, n):
    """Constructs the elemental stress stiffness matrix

    Arguments:
        quad_data - Quadrature points and weights
        basis - Basis and respective derivatives
        n - the element number
    """
    return (numpy.dot(quad_data.w*mat_prop.mu[:, n]*basis.psi.T, basis.dpsi).T, \
            numpy.dot(quad_data.w*mat_prop.dmudx[:, n]*basis.psi.T, basis.psi).T)

#-- local_matrices.py ----------------------------------------------------------


import numpy as np

def rot1(theta):
    """
    Args:
        theta (float): Angle in radians
    Return:
        numpy.ndarray : 3x3 rotation matrix of angle theta around the X-axis
    """
    return np.array(
        [
            [1, 0, 0],
            [0, np.cos(theta), np.sin(theta)],
            [0, -np.sin(theta), np.cos(theta)],
        ]
    )

def rot2(theta):
    """
    Args:
        theta (float): Angle in radians
    Return:
        numpy.ndarray : 3x3 rotation matrix of angle theta around the Y-axis
    """
    return np.array(
        [
            [np.cos(theta), 0, -np.sin(theta)],
            [0, 1, 0],
            [np.sin(theta), 0, np.cos(theta)],
        ]
    )

def rot3(theta):
    """
    Args:
        theta (float): Angle in radians
    Return:
        numpy.ndarray : 3x3 rotation matrix of angle theta around the Z-axis
    """
    return np.array(
        [
            [np.cos(theta), np.sin(theta), 0],
            [-np.sin(theta), np.cos(theta), 0],
            [0, 0, 1],
        ]
    )


#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt

y0 = np.arange(0, 11) ** 3

mean = [69, 0]
cov = [[15, 8], [8, 15]]
np.random.seed(5)
x1, y1 = np.random.multivariate_normal(mean, cov, 2000).T
y1 += 180

x2 = np.arange(0, 28651, 5730)
r2 = np.log(0.5)
t2 = 5730
y2 = np.exp((r2 / t2) * x2)

x3 = np.arange(0, 21000, 1000)
r3 = np.log(0.5)
t31 = 5730
t32 = 1600
y31 = np.exp((r3 / t31) * x3)
y32 = np.exp((r3 / t32) * x3)

np.random.seed(5)
student_grades = np.random.normal(68, 15, 50)

# your code here
figure, axis = plt.subplots(3,2,figsize=(20, 10))
figure.tight_layout(pad=4)
x= np.arange(0, 11)

axis[0, 0].plot(x, y0, color='red')

# For scatter men Height vs Weight
axis[0, 1].scatter(x1, y1)
axis[0, 1].set_title("Men\'s Height vs Weight")
axis[0, 1].set_xlabel("Height (in)")
axis[0, 1].set_ylabel("Weight (lbs)")

# Line - logarithmically scaled
axis[1, 0].plot(x2, y2)
axis[1, 0].set_title("Exponential Decay of C-14")
axis[1, 0].set_xlabel("Time (years)")
axis[1, 0].set_ylabel("Fraction Remaining")

# For Exponential Decay of Radioactive Elements
axis[1, 1].plot(x3, y31, color="red", linestyle="--", label="C-14")
axis[1, 1].plot(x3, y32, color="green", linestyle="-", label="Ra-226")
axis[1, 1].set_title("Exponential Decay of Radioactive Elements")
axis[1, 1].set_xlabel("Time (years)")
axis[1, 1].set_ylabel("Fraction Remaining")
axis[1, 1].axis(xmin=0,xmax=20000)
axis[1, 1].axis(ymin=0,ymax=1)
axis[1, 1].legend(loc="best", fontsize="small")

axis[2, 0].hist(student_grades, bins=10, edgecolor='black', align='mid')
axis[2, 0].set_title("Project A")
axis[2, 0].set_xlabel("Grades")
axis[2, 0].set_ylabel("Number of Students")

axis[2, 1].set_visible(False)

plt.show()


import itertools
import pandas as pd
import numpy as np

data = None # READ EXPERT RESPONSES HERE AS PANDAS DATAFRAME
expImp = None # READ EXPERT IMPORTANCE COEFFICIENTS

def mapToFuzzyNumbers(option):
    '''
    Maps selected option (Strongly agree, Neutral etc.) to fuzzy number via Likert scale.

    Arguments:
    `option` - string, should be one of the follows: 'Strongly agree', 'Agree', 'Neutral', 'Disagree' or 'Strongly disagree', otherwise default value of (0.0, 0.05, 0.1) will be applied.

    Returns: converted fuzzy number, tuple of three floats.
    '''
    if option == 'Strongly agree':
        return (0.6, 0.8, 1.0)
    if option == 'Agree':
        return (0.4, 0.6, 0.8)
    if option == 'Neutral':
        return (0.2, 0.4, 0.6)
    if option == 'Disagree':
        return (0.0, 0.2, 0.4)
    if option == 'Strongly disagree':
        return (0.0, 0.0, 0.2)
    return (0.0, 0.05, 0.1)

def interval(l, n):
    '''
    Split interval l into n equal sub-intervals.

    Arguments:
    `l` - tuple that contains two numerical values - start and end of the interval;
    `n` - number of intervals.

    Returns:
    list of points that correspond to splitted sub-intervals.
    '''
    w = (l[1] - l[0]) / n
    return [round((l[0] + i * w + l[0] + (i + 1) * w),2) / 2 for i in range(n)]

N = 5
xRange = interval([0,1], N)

def memb(x, R):
    '''
    Triangular membership function.

    Arguments:
    `x` - point at which membership function is to be evaluated;
    `R` - fuzzy number, tuple of three floats.

    Return:
    float number that coresponds to membership function value of fuzzy number `R` at point `x`.
    '''
    a, b, c = R
    if x < a or x > c:
        return 0
    if x < b and x >= a:
        return (x - a) / (b - a)
    if x <= c and x >= b:
        return (b + c - b - x) / (c - b)

def similarity(R1, R2):
    '''
    Similarity between two fuzzy numbers.

    Arguments:
    `R1`, `R2` - fuzzy numbers for which similarity will be measured.

    Returns:
    float number that represents similarity.
    '''
    mR1 = [memb(x, R1) for x in xRange]
    mR2 = [memb(x, R2) for x in xRange]
    return np.sum(np.minimum(mR1, mR2) ** 2) / np.sum(np.maximum(mR1, mR2) ** 2)

def hamDist(R1, R2):
    '''
    Hammington distance between `R1` and `R2` fuzzy numbers.

    Arguments:
    `R1`, `R2` - fuzzy numbers, distance between which will be measured.

    Returns:
    float, distance.
    '''
    mR1 = np.array([memb(x, R1) for x in xRange])
    mR2 = np.array([memb(x, R2) for x in xRange])
    return np.abs(mR1 - mR2) @ xRange

def infDist(R1, R2):
    '''
    Infinum distance between `R1` and `R2` fuzzy numbers.

    Arguments:
    `R1`, `R2` - fuzzy numbers, distance between which will be measured.

    Returns:
    float, distance.
    '''

    return np.min(np.abs(np.array(list(itertools.product(R1, R2)))[:,0]
            - np.array(list(itertools.product(R1, R2)))[:,1]))

def dist(R1, R2):
    '''
    Combined distance (average of Hammington and infinum distances) between `R1` and `R2` fuzzy numbers.

    Arguments:
    `R1`, `R2` - fuzzy numbers, distance between which will be measured.

    Returns:
    float, distance.
    '''

    return (hamDist(R1, R2) + infDist(R1, R2)) / 2

def normDist(R1, R2, R):
    '''
    Normalized distance between `R1` and `R2` fuzzy numbers.

    Arguments:
    `R1`, `R2` - fuzzy numbers, distance between which will be measured.

    Returns:
    float, distance.
    '''

    return dist(R1, R2) / max([dist(Ri, Rj) for Ri, Rj in list(itertools.product(R, R))])

def consMes(R1, R2, R, beta=0.5):
    '''
    Consensus measure between `R1` and `R2` fuzzy numbers in `R` space of all fuzzy numbers that coresponds to selected options in questionary.

    Arguments:
    `R1`, `R2` - fuzzy numbers, consensus between which will be measured;
    `R` - list of fuzzy numbers (tuples or lists of three float numbers) that coresponds to selected options in questionary;
    `beta` - relative importance degree between the similarity and the distance to the decision-maker. Default: 0.5.

    Returns:
    float, distance.
    '''

    return beta * similarity(R1, R2) + (1 - beta) * (1 - normDist(R1, R2, R))

def weightedConsDegree(i, expImp, R=None, consMeasures=None):
    '''
    Weighted consistency degree for expert.

    Arguments:
    `i` - index of expert for which weighted consistency degree will be calculated.
    `expImp` - list of degree of importance for each expert.

    `R` - list of fuzzy numbers (tuples or lists of three float numbers) that coresponds to selected options in questionary. Default: None.
    `consMeasures` - Cached list of consistency measures for each fuzzy number. Included for speeding the algorithm. Default: None.
    *Either `R` or `consMeasures` should be passed.*

    Returns:
    Weigthed consistency degree, float.
    '''

    assert R is not None or consMeasures is not None

    if consMeasures is None:
        consMeasure = np.array([consMes(R[i], Rj, R) for Rj in R])
    else:
        consMeasure = consMeasures[i]
    return consMeasure @ expImp

def aggWeight(i, expImp, R=None, weightedConsDegrees=None):
    '''
    Aggregation weight for expert.

    Arguments:
    `i` - index of expert for which aggregation weight will be calculated.
    `expImp` - list of degree of importance for each expert.
    `R` - list of fuzzy numbers (tuples or lists of three float numbers) that coresponds to selected options in questionary. Default: None.
    `weightedConsDegrees` - Cached list of weighted consistency measures for each fuzzy number. Included for speeding the algorithm. Default: None.
    *Either `R` or `weightedConsDegrees` should be passed.*

    Returns:
    Aggregation weight, float.
    '''

    assert R is not None or consMeasures is not None

    if weightedConsDegrees is None:
        weightedConsDegrees = [weightedConsDegree(j, expImp, R=R) for j in range(len(R))]
    return weightedConsDegrees[i] / sum(weightedConsDegrees)

def groupOpinion(R, expImp, weightedConsDegrees):
    '''
    Calculates group opinion.

    Arguments:
    `R` - list of fuzzy numbers (tuples or lists of three float numbers) that coresponds to selected options in questionary.
    `expImp` - list of degree of importance for each expert.
    `weightedConsDegrees` - List of weighted consistency measures for each fuzzy number.

    Returns:
    Group opinion, tuple of three elements (fuzzy number).
    '''

    weights = [aggWeight(i, R, expImp, weightedConsDegrees) for i in range(len(R))]
    return weights @ np.array(R)

def defuziffication(opinion):
    '''
    Defuzzification.

    Arguments:
    `opinion` - group opinion that should be defuzzified.

    Returns:
    Defuzzified opinion, float.
    '''

    return np.mean(opinion)

def delphi(question, data, expImp, xRange=xRange, S=0.75):
    '''
    Implements consistency aggregation method.

    Arguments:
    `question` - label of column in `data` dataframe that contains desired question for evaluation;
    `data` - pandas dataframe with obtained responses;
    `expImp` - list of degree of importance for each expert;
    `xRange` - list of points to use in membership function evaluation;
    `S` - retainment threshold for rank of each item.

    Return:
    Returns tuple of three items, where:
    1. first item is obtained defuzzified rank of question;
    2. second item is obtained conensus rate;
    3. third item is verdict ("Retained" / "Discarder") based on `S` threshold value.
    '''

    subData = data[question]
    R = list(subData.apply(mapToFuzzyNumbers))
    consMeasures = np.array([[consMes(Ri, Rj, R) for Rj in R] for Ri in R])
    weightedConsDegrees = [weightedConsDegree(j, expImp, R, consMeasures) for j in range(len(R))]
    cons = sum(weightedConsDegrees) / len(subData)
    return defuziffication(groupOpinion(R, expImp, weightedConsDegrees)), cons, 'Retained' if cons >= S else 'Discarded'

ranking = pd.DataFrame(columns=['Name', 'Rank', 'Consensus', 'Verdict'])
for col in data.columns:
    res = delphi(col, data, expImp, S=0.53)
    ranking.loc[len(ranking)] = [col, res[0], res[1], res[2]]

ranking = ranking.sort_values(by=['Rank'], ascending=False)
ranking = ranking.set_index('Name')

ranking.to_csv('results.csv')


import numpy as np

#######################################################
# put `sigmoid_forward` and `sigmoid_grad_input` here #
#######################################################
def sigmoid_forward(x_input):
    """sigmoid nonlinearity
    # Arguments
        x_input: np.array of size `(n_objects, n_in)`
    # Output
        the output of relu layer
        np.array of size `(n_objects, n_in)`
    """
    #################
    ### YOUR CODE ###
    #################

    output = []
    for x in x_input:
        output.append(1/(1+np.exp(-x)))
    output = np.asarray(output)
    return output

def sigmoid_grad_input(x_input, grad_output):
    """sigmoid nonlinearity gradient.
        Calculate the partial derivative of the loss
        with respect to the input of the layer
    # Arguments
        x_input: np.array of size `(n_objects, n_in)`
        grad_output: np.array of size `(n_objects, n_in)`
            dL / df
    # Output
        the partial derivative of the loss
        with respect to the input of the function
        np.array of size `(n_objects, n_in)`
        dL / dh
    """
    #################
    ### YOUR CODE ###
    #################
    output = []
    for x in x_input:
        one = (1/(1+np.exp(-x)))
        two = (np.exp(-x)/(1+np.exp(-x)))
        output.append(one*two)
    output = np.asarray(output*grad_output)

    return output
#######################################################
#      put `nll_forward` and `nll_grad_input` here    #
#######################################################
def nll_forward(target_pred, target_true):
    """Compute the value of NLL
        for a given prediction and the ground truth
    # Arguments
        target_pred: predictions - np.array of size `(n_objects, 1)`
        target_true: ground truth - np.array of size `(n_objects, 1)`
    # Output
        the value of NLL for a given prediction and the ground truth
        scalar
    """
    #################
    ### YOUR CODE ###
    #################
    n = len(target_pred)
    output = -(1/n) * np.sum((target_true*np.log(target_pred))+((1-target_true)*np.log(1-target_pred)))
    return output

def nll_grad_input(target_pred, target_true):
    """Compute the partial derivative of NLL
        with respect to its input
    # Arguments
        target_pred: predictions - np.array of size `(n_objects, 1)`
        target_true: ground truth - np.array of size `(n_objects, 1)`
    # Output
        the partial derivative
        of NLL with respect to its input
        np.array of size `(n_objects, 1)`
    """
    #################
    ### YOUR CODE ###
    #################

    grad_input = (target_pred - target_true) / (target_pred*(1-target_pred)) * 1/len(target_pred)

    return grad_input


import numpy as np

# Setting the random seed, feel free to change it and see different solutions.
np.random.seed(42)

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

def sigmoid_prime(x):
    return sigmoid(x) * (1 - sigmoid(x))

def prediction(X, W, b):
    return sigmoid(np.matmul(X, W) + b)

def error_vector(y, y_hat):
    return [-y[i] * np.log(y_hat[i]) - (1 - y[i]) * np.log(1 - y_hat[i]) for i in range(len(y))]

def error(y, y_hat):
    ev = error_vector(y, y_hat)
    return sum(ev) / len(ev)

# EXERCISE: Fill in the code below to calculate the gradient of the error function.
# The result should be a list of three lists:
# The first list should contain the gradient (partial derivatives) with respect to w1
# The second list should contain the gradient (partial derivatives) with respect to w2
# The third list should contain the gradient (partial derivatives) with respect to b
def dErrors(X, y, y_hat):
    y_hat = np.reshape(y_hat, -1)
    DErrorsDx1 = (y-y_hat) * X[:, 0]
    DErrorsDx2 = (y-y_hat) * X[:, 1]
    DErrorsDb = y-y_hat
    return DErrorsDx1, DErrorsDx2, DErrorsDb

# EXERCISE: Fill in the code below to implement the gradient descent step.
# The function should receive as inputs the data X, the labels y,
# the weights W (as an array), and the bias b.
# It should calculate the prediction, the gradients, and use them to
# update the weights and bias W, b. Then return W and b.
# The error e will be calculated and returned for you, for plotting purposes.
def gradientDescentStep(X, y, W, b, learn_rate=0.01):
    # EXERCISE: Calculate the prediction
    y_hat = prediction(X, W, b)
    # EXERCISE: Calculate the gradient
    gradient = dErrors(X, y, y_hat)
    # EXERCISE: Update the weights  W -= learn_rate *
    W[0] += learn_rate * np.sum(gradient[0])
    W[1] += learn_rate * np.sum(gradient[1])
    b += learn_rate * np.sum(gradient[2])
    # This calculates the error
    e = error(y, y_hat)
    return W, b, e

# This function runs the perceptron algorithm repeatedly on the dataset,
# and returns a few of the boundary lines obtained in the iterations,
# for plotting purposes.
# Feel free to play with the learning rate and the num_epochs,
# and see your results plotted below.
def trainLR(X, y, learn_rate=0.01, num_epochs=100):
    x_min, x_max = min(X.T[0]), max(X.T[0])
    y_min, y_max = min(X.T[1]), max(X.T[1])
    # Initialize the weights randomly
    W = np.array(np.random.rand(2, 1)) * 2 - 1
    b = np.random.rand(1)[0] * 2 - 1
    # These are the solution lines that get plotted below.
    boundary_lines = []
    errors = []
    for i in range(num_epochs):
        # In each epoch, we apply the gradient descent step.
        W, b, error = gradientDescentStep(X, y, W, b, learn_rate)
        boundary_lines.append((-W[0] / W[1], -b / W[1]))
        errors.append(error)
    return boundary_lines, errors

data = np.genfromtxt('B_data.csv', delimiter=',')
trainLR((data[:, 0:2]), data[:, 2])


from sympy import *

x = symbols('x')
init_printing(use_unicode=True)

#f=Lambda(x, x**2-3)
#f=Lambda(x, 3*x-2-x**2+math.e**x) #f funcion que a x le asigna 3*x....
#f=Lambda(x, x**2+10*cos(x)+x)
#f=Lambda(x, 3*x**2+exp(x)-2)
#f=Lambda(x, x**3-3*x**2*2**(-x)+3*x*4**(-x)-8**(-x))
f=Lambda(x, log(x**2+1)-exp(x/2)*cos(3*x))
f1=Lambda(x, diff(f(x),x))

def newton_raphson(f, x0, n, tol):
    for i in range(n):
        x1 = x0-N(f(x0),32)/N(f1(x0),32)
        print("Iteracion "+str(i+1)+": Valor de X"+str(i+1)+"="+str(x1))
        if abs(x1-x0)<tol:
        #if abs(N(f(x1),32))<tol:    # Residuo
            return N(x1, 18) # N(x,n): evaluacion numerica con n digitos
        else:
            x0=x1
    return N(x1, 18)

y=newton_raphson(f, 0.5, 10, 0)
print("Raíz en x="+str(y))
print("f(x)="+str(f(y)))

# Estudio orden convergencia
"""
n=36
x0=1
for i in range(n-1):
    x1 = x0-N(f(x0),32)/N(f1(x0),32)
    print(abs(x1-y)/abs(x0-y)**2)
    x0=x1

f2=Lambda(x, diff(f1(x),x))
print("f''(x)="+str(N(f2(y))))
"""


import numpy as np

def relu(x):
    return np.maximum(0,x) # ReLU 함수

def sigmoid(x):
    return 1/ (1+ np.exp(-x)) # sigmoid 함수
# -----------------------------------------------------------------------------------------------------------------------------
x = np.array([1.0, 0.5])
w1 = np.array([[0.1, 0.3, 0.5],[0.2, 0.4, 0.6]])
b1 = np.array([0.1, 0.2, 0.3])

print(x.shape)
print(w1.shape)
print(b1.shape)

a1 = np.dot(x, w1) + b1
w2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
b2 = np.array([0.1, 0.2])

z1 = a1

a2 = np.dot(z1, w2) + b2
z2 = sigmoid(a2)

def idenity_function(x): # 항등함수
    return x

w3 = np.array([[0.1, 0.3], [0.2, 0.4]])
b3 = np.array([0.1, 0.2])

a3 = np.dot(w3, z2) + b3
print(a3)

# ------------------------------------------------------------------------------------------------------------------------------
def init_network():
    network = {}
    network['W1'] = np.array([[0.1, 0.3, 0.5],[0.2, 0.4, 0.6]]) # 2*3행렬
    network['b1'] = np.array([0.1, 0.2, 0.3]) #bias 3개

    network['W2'] = np.array([[0.1, 0.4],[0.2, 0.5], [0.3, 0.6]]) # 3*2행렬
    network['b2'] = np.array([0.1, 0.2]) #bias 2개

    network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]]) # 2*2행렬
    network['b3'] = np.array([0.1, 0.2])

    return network

def forward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = idenity_function(a3)

    return y

network = init_network()
x = np.array([1.0, 0.5])  #초기 입력값
y = forward(network, x)
print(y)


import numpy as np

from indi.solvers.regularizationtype import RegularizationType
from indi.exceptions.modelbuilding import HyperParameterException

def sgd(cost_func, X, y,
        learning_rate=0.01,
        max_iter=100,
        regularization=1e-2,
        regularization_type=None,
        tolerance=1e-4,
        verbose=False):
    weights = np.zeros(X.shape[1])
    iteration = 1
    while True:
        if regularization_type is None:
            cost, grad_cost = cost_func(weights, X, y)
        elif regularization_type == RegularizationType.L2:
            cost, grad_cost = cost_func(weights, X, y, regularization)
        else:
            raise HyperParameterException('regularization_type: {} '
                                          'is not applicable for Linear Regression'.format
                                          (regularization_type))
        weights -= (learning_rate * grad_cost)
        if verbose:
            if (iteration <= 10) or \
                    (iteration <= 100 and iteration % 10 == 0) or \
                    (iteration <= 1000 and iteration % 100 == 0) or \
                    (iteration <= 10000 and iteration % 1000 == 0) or \
                    (iteration <= 100000 and iteration % 10000 == 0) or \
                    (iteration > 100000 and iteration % 50000 == 0):
                print('iteration: {:10d}\t\tcost: {:1.4e}'.format(iteration, cost))

        if iteration >= max_iter:
            break
        if np.linalg.norm(grad_cost) < tolerance:
            break
        iteration += 1

    if verbose:
        print('\nOptimization completed: number of iterations: {}, Norm of gradient of the cost: {}\n'.
              format(iteration, np.linalg.norm(grad_cost)))
    return weights, cost

def lasso_coordinate_descent(X, y, regularization=1e3, tolerance=1e-3):
    weights = np.zeros(X.shape[1])
    previous_weights = np.copy(weights)
    while True:
        for i in range(weights.shape[0]):
            weight_i = _lasso_coordinate_descent_step(i, X, y, weights, regularization)
            weights[i] = weight_i
        delta = np.sqrt((weights - previous_weights) ** 2)
        previous_weights = np.copy(weights)
        if (delta < tolerance).all():
            break
    return weights

def _lasso_coordinate_descent_step(i, feature_matrix, output, weights, l1_penalty):
    weight_without_i = weights[np.arange(weights.shape[0]) != i]
    feature_matrix_without_i = feature_matrix[:, np.arange(feature_matrix.shape[1]) != i]
    prediction = _predict_output(feature_matrix_without_i, weight_without_i)
    ro_i = np.dot(feature_matrix[:, i], (output - prediction))

    if ro_i < -l1_penalty / 2.0:
        new_weight_i = ro_i + l1_penalty / 2.0
    elif ro_i > l1_penalty / 2.0:
        new_weight_i = ro_i - l1_penalty / 2.0
    else:
        new_weight_i = 0
    return new_weight_i

def _predict_output(feature_matrix, weights):
    predictions = np.dot(feature_matrix, weights)
    return predictions


import pandas as pd
import numpy as np

# Simple CAGR calculation, designed to be used with df.apply()
def CAGR(row, last_col, first_col, num_periods):
    '''Calculate compound growth rate on a row x of a pandas df'''
    val_T = float(row[last_col])
    val_t = float(row[first_col])
    return (val_T/val_t)**(1./num_periods)-1

# Rolling CAGR calculation template:
#
# df_unpivoted is an unpivoted df with at least Id, Year and Value columns. Manually, the rolling CAGR steps are:
#
#   df_unpivoted['Year+5'] = df_unpivoted.loc[:,'Year'] + 5
#   df_rolling_cagr = pd.merge(df_unpivoted, df_unpivoted, how='inner', left_on=['Id', 'Year'], right_on=['Id', 'Year+5'], suffixes=['_start', '_end'])
#   df_rolling_cagr = df_rolling_cagr[['Id', 'Year_start', 'Value_start', 'Year_end', 'Value_end']]
#   df_rolling_cagr['CAGR (5Y)'] = ((df_rolling_cagr.loc[:,'Value_end'] / df_rolling_cagr.loc[:,'Value_start'])**(0.2)) - 1
#   df_unpivoted = pd.merge(df_unpivoted, df_rolling_cagr, how='left', left_on=['Id', 'Year'], right_on=['Id', 'Year_start'])
#   df_unpivoted['CAGR (5Y)'].replace([np.inf, -np.inf, np.nan], 0.0, inplace=True)
#   df_unpivoted = df_unpivoted[['Id', 'Year', 'Value', 'CAGR (5Y)']]
#
def CAGR_Rolling(df_unpivoted, num_periods=5, cagr_col='CAGR (5Y)', id_vars=['Id'], value_var_col='Year', value_col='Value', sort=True):
    value_var_num_periods = value_var_col + '+' + str(num_periods)
    df_unpivoted.loc[:,value_var_num_periods] = df_unpivoted.loc[:,value_var_col] + num_periods
    df_cagr_inner = pd.merge(df_unpivoted, df_unpivoted, how='inner', left_on=id_vars+[value_var_col], right_on=id_vars+[value_var_num_periods], suffixes=['_start', '_end'])
    df_cagr_inner = df_cagr_inner[id_vars+[value_var_col+'_start', value_col+'_start', value_var_col+'_end', value_col+'_end']]
    df_cagr_inner.loc[:,cagr_col] = ((df_cagr_inner.loc[:,value_col+'_end'] / df_cagr_inner.loc[:,value_col+'_start'])**(1./num_periods)) - 1
    df_cagr_left_outer = pd.merge(df_unpivoted, df_cagr_inner, how='left', left_on=id_vars+[value_var_col], right_on=id_vars+[value_var_col+'_start'])
    df_cagr_left_outer[cagr_col].replace([np.inf, -np.inf, np.nan], 0.0, inplace=True)
    if (sort):
        df_cagr = df_cagr_left_outer[id_vars+[value_var_col, value_col, cagr_col]].sort_values(id_vars+[value_var_col])
        return df_cagr
    else:
        return df_cagr_left_outer[id_vars+[value_var_col, value_col, cagr_col]]


import sympy as sp
from sympy.matrices.expressions.matadd import MatAdd

'''
实现Laplace展开的模块
有矩阵的表达式，请用MatAdd,MatMul实现加和乘
'''

def lap_expand(matrix, n: int = 0, coe = 1, axis: int = 0):
    #axis   0表示按行展开，否则按列展开
    expr = _lap_expr_0(matrix, n, coe) if axis == 0 else _lap_expr_1(matrix, n, coe)
    return expr

def _lap_expr_0(matrix, n: int = 0, coe = 1):
    #按行展开函数的实现
    expr, startInd = _lap_init_expr_0(matrix, n, coe)
    for colInd in range(startInd+1 ,matrix.shape[1]):
        if matrix[n, colInd] == 0:
            t = sp.MatMul(coe*matrix[n, colInd]*(-1)**(n+colInd), matrix.minor_submatrix(n, colInd), evaluate = True)
        else:
            t = sp.MatMul(coe*matrix[n, colInd]*(-1)**(n+colInd), matrix.minor_submatrix(n, colInd), evaluate = False)
        if not isinstance(t, sp.matrices.expressions.special.ZeroMatrix):
            expr = sp.MatAdd(expr, t, evaluate = False)
    return expr

def _lap_init_expr_0(matrix, n: int = 0, coe = 1):
    #用于初始化按行展开的表达式
    expr = 0
    startInd = 0
    for colInd in range(matrix.shape[1]):
        if matrix[n, colInd] == 0: #去掉一些不必要的系数如0，使得输出格式化，-1是必要的因为元素是行列式不是乘法
            expr = sp.MatMul(coe*matrix[n, colInd]*(-1)**(n+colInd), matrix.minor_submatrix(n, colInd), evaluate = True)
        else:
            expr = sp.MatMul(coe*matrix[n, colInd]*(-1)**(n+colInd), matrix.minor_submatrix(n, colInd), evaluate = False)
        if not isinstance(expr, sp.matrices.expressions.special.ZeroMatrix):    #hint 0矩阵不等于0，不能用==判别
            startInd = colInd
            break
    return (expr, startInd)

def _lap_expr_1(matrix, n: int = 0, coe = 1):
    expr, startInd = _lap_init_expr_1(matrix, n)
    for rowInd in range(startInd+1 ,matrix.shape[0]):
        if matrix[rowInd, n] == 0:
            t = sp.MatMul(coe*matrix[rowInd, n]*(-1)**(n+rowInd), matrix.minor_submatrix(rowInd, n), evaluate = True)
        else:
            t = sp.MatMul(coe*matrix[rowInd, n]*(-1)**(n+rowInd), matrix.minor_submatrix(rowInd, n), evaluate = False)
        if not isinstance(t, sp.matrices.expressions.special.ZeroMatrix):
            expr = sp.MatAdd(expr, t, evaluate = False)
    return expr

def _lap_init_expr_1(matrix, n: int = 0, coe = 1):
    expr = 0
    startInd = 0
    for rowInd in range(matrix.shape[1]):
        if matrix[rowInd, n] == 0:
            expr = sp.MatMul(coe*matrix[rowInd, n]*(-1)**(n+rowInd), matrix.minor_submatrix(rowInd, n), evaluate = True)
        else:
            expr = sp.MatMul(coe*matrix[rowInd, n]*(-1)**(n+rowInd), matrix.minor_submatrix(rowInd, n), evaluate = False)
        if not isinstance(expr, sp.matrices.expressions.special.ZeroMatrix):    #hint 0矩阵不等于0，不能用==判别
            startInd = rowInd
            break
    return (expr, startInd)

def lap_exp_mono(expr, n: int = 0, axis: int = 0):
    '''
    矩阵单项式的Laplace展开
    expr    是系数乘矩阵的一个单项式
    '''
    if isinstance(expr, sp.MatrixBase):
        # print(type(expr))
        return lap_expand(expr, n = n, axis = axis)
    coe = 1
    for x in expr.args:
        if isinstance(x, sp.MatrixBase):
            expr = x
        else:
            coe = x
    return lap_expand(expr, coe = coe)

def lapexp_expr(expr):
    '''
    输入为表达式的Laplace展开函数，它兼容矩阵形式的参数输入
    expr: MatrixBaser/Expr
    '''
    if isinstance(expr, sp.MatrixBase):
        return lap_expand(expr)
    args = []
    read_mat_mono(args, expr)
    expr_list = [0 for i in range(len(args))]
    for i in range(len(args)):
        expr_list[i] = lap_exp_mono(args[i])
    expr = sp.MatAdd(*expr_list)
    return expr

def read_mat_mono(args: list, poly: sp.Expr) -> None:
    '''
    读取矩阵多项式的单项式
    Parameter:
        args 用于存储读取的单项式，作为隐式的返回
        poly: 被读取的多项式，兼容单项式
    '''
    if not isinstance(poly, MatAdd):
        args.append(poly)
        return

    for x in poly.args:
        if isinstance(x, MatAdd):
            read_mat_mono(args, x)
        else:
            args.append(x)
    return


import numpy as np

# Enter an expression to compute and set th to the optimal theta
# th = np.linalg.inv((X@np.transpose(X)))@X@np.transpose(Y)

def d_lin_reg_th(x, th, th0):
    """
    Parameters:
        x is d by n : input data
        th is d by 1 : weights
        th0 is 1 by 1 or scalar
    Returns:
        d by n array : gradient of lin_reg(x, th, th0) with respect to th
    """
    return x

def d_square_loss_th(x, y, th, th0):
    """
    Parameters:
        x is d by n : input data
        y is 1 by n : output regression values
        th is d by 1 : weights
        th0 is 1 by 1 or scalar
    Returns:
        d by n array : gradient of square_loss(x, y, th, th0) with respect to th.

    This function should be a one-line expression that uses lin_reg and
    d_lin_reg_th.
    """
    return 2*(d_lin_reg_th(x, th, th0)-y)*(d_lin_reg_th(x, th, th0))

def d_mean_square_loss_th(x, y, th, th0):
    """
    Parameters:
        Same as above
    Returns:
        d by 1 array : gradient of mean_square_loss(x, y, th, th0) with respect to th.

    This function should be a one-line expression that uses d_square_loss_th.
    """
    return np.mean(d_square_loss_th(x, y, th, th0), axis = 1).reshape(-1,1)

def d_lin_reg_th0(x, th, th0):
    """
    Parameters:
        x is d by n : input data
        th is d by 1 : weights
        th0 is 1 by 1 or scalar
    Returns:
        1 by n array : gradient of lin_reg(x, th, th0) with respect to th0
    """
    return np.ones(x.shape[1]).reshape(1,x.shape[1])

def d_square_loss_th0(x, y, th, th0):
    """
    Parameters:
        x is d by n : input data
        y is 1 by n : output regression values
        th is d by 1 : weights
        th0 is 1 by 1 or scalar
    Returns:
        1 by n array : gradient of square_loss(x, y, th, th0) with respect to th0.

    This function should be a one-line expression that uses lin_reg and
    d_lin_reg_th0.
    """
    return -2*(y-d_lin_reg_th0(x, th, th0))*d_lin_reg_th0(x, th, th0)

def d_mean_square_loss_th0(x, y, th, th0):
    """
    Parameters:
        Same as above
    Returns:
        1 by 1 array : gradient of mean_square_loss(x, y, th, th0) with respect to th0.

    This function should be a one-line expression that uses d_square_loss_th0.
    """
    return np.mean(d_square_loss_th0(x, y, th, th0), axis = 1, keepdims = True)

def d_ridge_obj_th(x, y, th, th0, lam):
    return np.mean(d_square_loss_th(x, y, th, th0), axis = 1).reshape(-1,1) + 2*lam*th

def d_ridge_obj_th0(x, y, th, th0, lam):
    return np.mean(d_square_loss_th0(x, y, th, th0), axis = 1, keepdims = True)

# for sgd:
# X: a standard data array (d by n)
# y: a standard labels row vector (1 by n)
# J: a cost function whose input is a data point (a column vector), a label (1 by 1) and a weight vector w (a column vector) (in that order), and which returns a scalar.
# dJ: a cost function gradient (corresponding to J) whose input is a data point (a column vector), a label (1 by 1) and a weight vector w (a column vector) (also in that order), and which returns a column vector.
# w0: an initial value of weight vector ww, which is a column vector.
# step_size_fn: a function that is given the (zero-indexed) iteration index (an integer) and returns a step size.
# max_iter: the number of iterations to perform

# w: the value of the weight vector at the final step
# fs: the list of values of JJ found during all the iterations
# ws: the list of values of intermediate ww found during all the iterations

def sgd(X, y, J, dJ, w0, step_size_fn, max_iter):
    fs=[]
    ws=[]
    w=w0
    for i in range(max_iter):
        ind=np.random.randint(X.shape[1])
        fs.append(J(X[:,ind].reshape(-1,1),y[0][ind],w))
        ws.append(w)
        w=w-step_size_fn(i)*dJ(X[:,ind].reshape(-1,1),y[0][ind],w)
    return (w,fs,ws)


import numpy as np

# adam optimizer adapted from CS231n assi
def adam(x, dx, config=None):
  """
  Uses the Adam update rule, which incorporates moving averages of both the
  gradient and its square and a bias correction term.
  config format:
  - learning_rate: Scalar learning rate.
  - beta1: Decay rate for moving average of first moment of gradient.
  - beta2: Decay rate for moving average of second moment of gradient.
  - epsilon: Small scalar used for smoothing to avoid dividing by zero.
  - m: Moving average of gradient.
  - v: Moving average of squared gradient.
  - t: Iteration number.
  """
  if config is None: config = {}
  config.setdefault('learning_rate', 1e-4)
  config.setdefault('beta1', 0.9)
  config.setdefault('beta2', 0.999)
  config.setdefault('epsilon', 1e-8)
  config.setdefault('m', np.zeros_like(x))
  config.setdefault('v', np.zeros_like(x))
  config.setdefault('t', 0)

  next_x = None
  #############################################################################
  # TODO: Implement the Adam update formula, storing the next value of x in   #
  # the next_x variable. Don't forget to update the m, v, and t variables     #
  # stored in config.                                                         #
  #############################################################################
  lr = config['learning_rate']
  beta1 = config['beta1']
  beta2 = config['beta2']
  eps = config['epsilon']
  m = config['m']
  v = config['v']
  t = config['t']

  t += 1

  m = beta1*m + (1-beta1)*dx
  v = beta2*v + (1-beta2)*(dx**2)
  mb = m/(1-beta1**t)
  vb = v/(1-beta2**t)

  next_x = x - lr * mb / (np.sqrt(vb) + eps)

  config['m'] = m
  config['v'] = v
  config['t'] = t
  #############################################################################
  #                             END OF YOUR CODE                              #
  #############################################################################

  return next_x, config


import numpy as np
from math import sqrt, floor
from scipy.special import eval_hermite

def index_to_hermite(i):
	'''Converts a one-dimensional mode index to the two-dimensional mode index.

	The one-dimensional mode index is converted to a two-dimensional index.
	The two-dimensional index determines the order in the x and y directions.

	Parameters
	----------
	i : int
		The one-dimensional index.

	Returns
	-------
	n : int
		The x order.
	m : int
		The y order.
	'''

	# Get the max order
	o = int((np.sqrt(8 * i + 1) - 1) / 2)
	# Get the y order
	m = int(i - o * (o + 1) / 2)
	# Get the x order
	n = o - m
	return n, m

def gaussian_hermite(n, m, mode_field_diameter=1, grid=None):
	r'''Creates a Gaussian-Hermite mode.

	This function evaluates a (n, m) order Gaussian-Hermite mode on a grid.
	The definition of the modes are the following,

	.. math::
		\exp{\left(-\frac{r^2}{w_0^2}\right)} H_n\left(\sqrt{2}\frac{x}{w_0}\right) H_m\left(\sqrt{2}\frac{y}{w_0}\right).

	Here :math:`w_0` is the mode_field_radius, which is :math:`\mathrm{MFD}/2`. This defintion follows
	the Physicists definition of the Hermite polynomials. The modes are numerical normalized to have a
	total power of 1.

	More details on the Hermite Polynomials can be found on: http://mathworld.wolfram.com/HermitePolynomial.html

	Parameters
	----------
	n : int
		The x order.
	m : int
		The y order.
	mode_field_diameter : scalar
		The mode field diameter of the Gaussian-Laguerre mode.
	grid : Grid
		The grid on which to evaluate the mode.

	Returns
	-------
	Field
		The evaluated mode.
	'''
	from ..field import Field

	if grid.is_separated and grid.is_('cartesian'):
		x = grid.x
		y = grid.y
		r2 = (x**2 + y**2) / (mode_field_diameter / 2)**2
	else:
		x, y = grid.as_('cartesian').coords
		r2 = (2 * grid.r / mode_field_diameter)**2

	# Calculate the mode.
	hg = np.exp(-r2) * eval_hermite(n, 2*sqrt(2) * x / mode_field_diameter) * eval_hermite(m, 2*sqrt(2) * y / mode_field_diameter)

	# Numerically normalize the mode
	hg /= np.sum(np.abs(hg)**2 * grid.weights)

	return Field(hg, grid)

def gaussian_hermite_index(i, mode_field_diameter=1, grid=None):
	'''Creates a Gaussian-Hermite mode.

	This function evaluates the i-th order Gaussian-Hermite mode on a grid.

	Parameters
	----------
	i : int
		The one-dimensional mode index.
	mode_field_diameter : scalar
		The mode field diameter of the Gaussian-Hermite mode.
	grid : Grid
		The grid on which to evaluate the Gaussian-Hermite mode.

	Returns
	-------
	Field
		The evaluated Gaussian-Hermite mode.
	'''

	n, m = index_to_hermite(i)
	return gaussian_hermite(n, m, mode_field_diameter, grid)

def make_gaussian_hermite_basis(grid, num_modes, mode_field_diameter, starting_mode=0):
	'''Creates a Gaussian-Hermite mode basis.

	This function evaluates the starting_mode to num_modes + starting_modes Gaussian-Hermite modes.
	And returns a ModeBasis made out of these Gaussian-Hermite modes.

	Parameters
	----------
	grid : Grid
		The grid on which to evaluate the Gaussian-Hermite mode.
	num_modes : int
		The number of modes to create.
	mode_field_diameter : scalar
		The mode field diameter of the Gaussian-Hermite mode.
	starting_mode : int
		The starting point of the mode indices.

	Returns
	-------
	ModeBasis
		The evaluated Gaussian-Hermite modes.
	'''
	from .mode_basis import ModeBasis

	modes = [gaussian_hermite_index(i, mode_field_diameter, grid) for i in range(starting_mode, starting_mode+num_modes)]
	return ModeBasis(modes)


import matplotlib.pyplot as plt
import numpy as np

def f(x):
    return 2*x**2

x = np.arange(0, 5, 0.001)
y = f(x)

plt.plot(x, y)

# The point and the "close enough" point
p2_delta = 0.0001
x1 = 2
x2 = x1+p2_delta

y1 = f(x1)
y2 = f(x2)

print((x1, y1), (x2, y2))

# Derivative approximation and y-intercept for the tangent line
approximate_derivative = (y2-y1)/(x2-x1)
b = y2 - approximate_derivative*x2

def tangent_line(x):  # Calculating and returning tangent function
    return approximate_derivative*x+b

toPlot = [x1-0.9, x1, x1+0.9]
plt.plot(toPlot, [tangent_line(i) for i in toPlot])

print('Approximate derivative fot f(x)',
      f'where x = {x1} is {approximate_derivative}')

plt.show()

Chapter 7 - Derivatives/approx_derivative.py


import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import sys

training_data = [] # Training Data Matrix
labels = []

def readData(filenameX,filenameY): # Read data from filenames
	global training_data
	global labels,mean,stddev
	training_data = pd.read_csv(filenameX,header=None)
	training_data[1] = training_data[0]
	training_data[0] = 1
	labels = pd.read_csv(filenameY,header=None)

readData(sys.argv[1],sys.argv[2])

examples_count = training_data.shape[0] # Number of examples
dimensions = training_data.shape[1] #Dimensions
training_data = np.array(training_data.values,dtype='f')
labels = np.array(labels.values,dtype='f')
theta = np.zeros([dimensions,1]) # Parameters

theta = np.matmul(np.linalg.inv(np.matmul(np.transpose(training_data),training_data)),np.matmul(np.transpose(training_data),labels))
## Using the normal linear regression

realx_values = []
for e in training_data:
	realx_values.append(e[1])

plt.scatter(realx_values,labels,c='b',label='Data') # Plot data
line_x = np.linspace(min(realx_values),max(realx_values),50)
predicted = theta[0]+theta[1]*line_x
plt.plot(line_x,predicted,c='r',label='Linear Fit') # Plot linear fit
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Linear Regression')
plt.legend(loc='upper left')
plt.show()

bandwidth = float(sys.argv[3]) # Read bandwidth

y_values = []
for e in line_x: # Loop for computing the theta for each x and calculating the values
	weighted_matrix = np.zeros([examples_count,examples_count])
	for i in range(examples_count):
		weighted_matrix[i][i] = np.exp(-1*np.power(e-training_data[i][1],2)/(2*bandwidth*bandwidth)) # Weights
	theta = np.matmul(np.linalg.inv(np.matmul(np.transpose(training_data),np.matmul(weighted_matrix,training_data))),np.matmul(np.transpose(training_data),np.matmul(weighted_matrix,labels)))
	y_values.append(theta[0]+theta[1]*e)

y_values = np.array(y_values)

plt.scatter(realx_values,labels,c='r',label='Data')
line_x = np.linspace(min(realx_values),max(realx_values),50)
plt.plot(line_x,predicted,c='k',label='Linear Fit') # Plot linear fit
plt.plot(line_x,y_values,c='b',label='Weighted Fit') # Plot weighted fit
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Weighted Fit With Bandwidth '+str(bandwidth))
plt.legend(loc='upper left')
plt.show()


'''Geometry Utilities'''

import numpy as np

def pol2cart(vector) -> np.ndarray:
    '''Convert 3D polar coordinates to Cartesian coordinates
    :param rho: is the radius
    :param theta: is the rotation angle in the x-y plane
    :param phi: is the rotation angle to z
    :return: vector with cartesian coordinates
    '''
    rho, theta, phi = vector
    x = rho * np.cos(theta) * np.sin(phi)
    y = rho * np.sin(theta) * np.sin(phi)
    z = rho * np.cos(phi)
    return np.array([x, y, z])

def cart2pol(vector) -> np.ndarray:
    '''Convert Cartesian coordinates to 3D polar coordinates
    :param x: is the first Cartesian coordinate
    :param y: is the second Cartesian coordinate
    :param z: is the third Cartesian coordinate
    :return: vector with polar coordinates
    '''
    x, y, z = vector
    rho = np.sqrt(x * x + y * y + z * z)
    theta = np.arctan2(y, x)
    phi = np.arccos(z / rho)
    return np.array([rho, theta, phi])

def calculate_rotation_matrix(up=[0., 0., 1.]) -> np.ndarray:
    '''Calculate rotation matrix that transforms points into a coordinate system
    whose z-axis points to `up` direction
    :param up: up direction (z-axis) of the target co-sys (3x1 vector)
    :return: rotation matrix (3x3)
    '''
    def normalize(x): return x / np.linalg.norm(x)

    z = normalize(np.asarray(up))
    x = [1., 0., 0.] if not np.isclose(
        np.abs(np.dot([1., 0., 0.], z)), 1.) else [0., 1., 0.]
    y = normalize(np.cross(z, x))
    x = np.cross(y, z)
    R = np.column_stack((x, y, z))

    return R

def generate_spherical_sector(center=[0., 0., 0.], radius=1., up=[0., 1., 0.], sector=[-1., 1.], bounce_amplitude=0.) -> np.ndarray:
    '''Generate a uniformly random sample on a spherical sector
    :param center: sphere center (3x1 vector)
    :param radius: sphere radius
    :param up: sphere up direction (3x1 vector)
    :param sector: sector margins along the up axis between [-1., 1.] (default: [-1., 1.] which is a full sphere)
    :param bounce_amplitude: variance in the radius
    :return: uniformly random position on the spherical sector
    '''
    low, high = sector
    R = calculate_rotation_matrix(up)

    while True:
        rho = radius + np.random.uniform(-bounce_amplitude, bounce_amplitude)
        theta = np.random.uniform(0, np.pi * 2.)
        phi = np.arccos(np.random.uniform(low, high))
        dir = pol2cart((rho, theta, phi))
        sample = center + np.dot(R, dir)

        yield sample

def generate_spherical_spiral(center=[0., 0., 0.], radius=1., up=[0., 1., 0.], steps=200, begin=0, turns=1, sector=[-0.85, 0.85], bounce_amplitude=0., bounce_frequency=0) -> np.ndarray:
    '''Generate consecutive samples on a spherical spiral
    :param center: sphere center (3x1 vector)
    :param radius: sphere radius
    :param up: sphere up direction (3x1 vector)
    :param steps: number of steps to take
    :param begin: index of the step to begin with (defult is zero)
    :param turns: number of full turns around the up axis
    :param sector: sector margins along the up axis between [-1., 1.]
    :return: uniformly random position on the spherical sector
    '''
    low, high = sector
    R = calculate_rotation_matrix(up)

    dtheta = np.pi * 2 * turns / steps
    dphi = (np.arccos(low) - np.arccos(high)) / steps

    rho = radius
    theta = dtheta * begin
    phi = np.arccos(high) + dphi * begin

    for i in range(steps):
        dir = pol2cart([rho, theta, phi])
        sample = center + np.dot(R, dir)

        yield sample

        rho = radius + bounce_amplitude * \
            np.sin(np.pi * 2 * bounce_frequency * turns * i / steps)
        theta += dtheta
        phi += dphi


import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt

# Let's create a function to model and create data
def func(x, a, b):
    return a * x + b

# Generating clean data
x = np.linspace(0, 10, 100)
y = func(x, 1, 2)

# Adding noise to the data
yn = y + 0.9 * np.random.normal(size=len(x))

# Executing curve_fit on noisy data
popt, pcov = curve_fit(func, x, yn)

#popt returns the best fit values for parameters of the given model (func)
print popt

# Plot out the current state of the data and model
ym = func(x, popt[0], popt[1])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, c='k', label='Function')
ax.scatter(x, yn)
ax.plot(x, ym, c='r', label='Best fit')
ax.legend(loc='upper left')
fig.savefig('scipy_311_ex1.pdf', bbox_inches='tight')


# -*- coding: utf-8 -*-
"""
Created on Tue Jul 27 19:30:55 2021

@author: Mahmut Osmanovic
"""

# import warnings filter
from warnings import simplefilter
# ignore all future warnings
simplefilter(action='ignore', category=FutureWarning)

# https://work.caltech.edu/homework/hw8.pdf

# K(x_n, x_m) = exp(-norm(2, x_n-x_m)^2) = RBF
# Consider RBF above in soft-margin SVM approach.
# Focus on 1 vs. 5 classifier.

# Q1:
    # Which of the following values of C results
    # in the lowest E_in?
        # a) C = 0.01
        # b) C = 1
        # c) C = 100
        # d) C = 10**(4)
        # e) C = 10**(6)

# Q2:
    # Which of the following values of C results
    # in the lowest E_out?
        # a) C = 0.01
        # b) C = 1
        # c) C = 100
        # d) C = 10**(4)
        # e) C = 10**(6)

# import libraries
from sklearn import svm
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

import matplotlib.style as style
style.use('bmh')

df_train = pd.read_csv('D:\\AI\\lfd\\lfd_hw8\\features_train.txt', names =
           ['digit', 'intensity', 'symmetry'], sep='\s+', dtype=np.float64)
df_test = pd.read_csv('D:\\AI\\lfd\\lfd_hw8\\features_test.txt', names =
           ['digit', 'intensity', 'symmetry'], sep='\s+', dtype=np.float64)

ones = df_train[df_train['digit'] == 1].assign(y = np.ones(df_train[df_train['digit'] == 1].shape[0]))
fives = df_train[df_train['digit'] == 5].assign(y = -np.ones(df_train[df_train['digit'] == 5].shape[0]))
ones_test = df_test[df_test['digit'] == 1].assign(y = np.ones(df_test[df_test['digit'] == 1].shape[0]))
fives_test = df_test[df_test['digit'] == 5].assign(y = -np.ones(df_test[df_test['digit'] == 5].shape[0]))

df_1_vs_5 = ones.append(fives, ignore_index=True)
df_test_1_vs_5 = ones_test.append(fives_test, ignore_index=True)

X_train_1_vs_5 = np.c_[df_1_vs_5['intensity'], df_1_vs_5['symmetry']]
y_train_1_vs_5 = np.array(df_1_vs_5['y'])
X_test_1_vs_5 = np.c_[df_test_1_vs_5['intensity'], df_test_1_vs_5['symmetry']]
y_test_1_vs_5 = np.array(df_test_1_vs_5['y'])

fig11 = plt.figure(11, dpi = 80)
C_values = [10**k for k in [-2, 0, 2, 4, 6]]
N_train = X_train_1_vs_5.shape[0]
E_in_values = []

print("RBF classifier, 1 vs 5:")
for C in C_values:
    clf_1_vs_5_rbf = svm.SVC(C, kernel = 'rbf', gamma = 1)
    clf_1_vs_5_rbf.fit(X_train_1_vs_5,  y_train_1_vs_5)

    y_predict_train_1_vs_5_rbf = clf_1_vs_5_rbf.predict(X_train_1_vs_5)
    E_in = sum(y_train_1_vs_5 != y_predict_train_1_vs_5_rbf) / N_train

    print("C = {} => \t\tE_in = {}".format(C, E_in))
    E_in_values.append(E_in)
print()

#plt.plot(C_values, E_in_values, 'bo-', label='RBF Kernel')
plt.semilogx(C_values, E_in_values, 'bo-', label='RBF Kernel')

plt.xlabel('C')
plt.ylabel('E_in')
plt.title('Problem 9 - RBF Kernel')
plt.legend()
plt.show()

"""
Result for Problem 9
The lowest $E_{in}$ is achieved for the highest value of $C = 10^6$ which
is to be expected if you recall that $C$ is the penalty factor. For
$C = \infty$ we had the hard margin case with zero in-sample error.
The correct answer is therefore 9[e].
"""

fig12 = plt.figure(12, dpi = 80)
C_values = [10**k for k in [-2, 0, 2, 4, 6]]
N_test = X_test_1_vs_5.shape[0]
E_out_values = []

print("RBF classifier, 1 vs 5:")
for C in C_values:
    clf_1_vs_5_rbf = svm.SVC(C, kernel = 'rbf', gamma = 1)
    clf_1_vs_5_rbf.fit(X_train_1_vs_5,  y_train_1_vs_5)

    y_predict_test_1_vs_5_rbf = clf_1_vs_5_rbf.predict(X_test_1_vs_5)
    E_out = sum(y_test_1_vs_5 != y_predict_test_1_vs_5_rbf) / N_test

    print("C = {} => \t\tE_out = {}".format(C, E_out))
    E_out_values.append(E_out)
print()

plt.semilogx(C_values, E_out_values, 'bo-', label='RBF Kernel')

plt.xlabel('C')
plt.ylabel('E_out')
plt.title('Problem 10 - RBF Kernel')
plt.legend()
plt.show()

"""
Result for Problem 10
The lowest E_{out} is achieved for C = 100, thus answer 10[c] is correct.
"""


from typing import Callable

import numpy as np

def rectangle(
    func: Callable[..., float], a: float, b: float, eps: float = 0.0001, *args, **kwargs
) -> float:

    """
    Metode segi empat adalah metode integrasi paling sederhana.
    Metode ini membagi domain integrasi sebanyak n buah
    dan menjumlahkan seluruh luas segi empat dengan dimensi
    (a + b)/n * f(x), dimana x adalah sudut kiri segiempat

    Parameter:
    func = fungsi input
    a    = batas bawah integrasi
    b    = batas atas integrasi
    eps  = error relatif maksimal

    >>> rectangle(lambda x: x**2, 0, 2)
    2.6992751228325296
    >>> rectangle(lambda x: x**1, 0, 2)
    2.0198019801980194
    """
    try:
        n = 100
        x = np.linspace(a, b, n)
        dx = x[1] - x[0]
        L0 = sum(dx * func(i, *args, **kwargs) for i in x)
        err = 1
        while err > eps:
            n += 1
            x = np.linspace(a, b, n)
            dx = x[1] - x[0]
            L1 = sum(dx * func(i, *args, **kwargs) for i in x)
            err = np.abs(L1 - L0) / np.abs(L1)
            L0 = L1
    except Exception:
        raise RuntimeError("Integrasi gagal, pastikan fungsi anda benar!")
    return L1

if __name__ == "__main__":

    def f(x: float) -> float:
        """
        Test Function
        """
        return x**2

    print(rectangle(f, 0, 2))

    import doctest

    doctest.testmod()


import numpy as np

def runge_kutta4(t0, dt, tf, x0, f):
    """
    Implements the Runge Kutta 4 algorithm for numerically integrating the solution of a system of first order
    differential equations.
    :param t0: Initial time
    :param dt: Fixed time step
    :param tf: Final time
    :param x0: Initial state; NOTE: must be numpy array for non-scalar case
    :param f: System dynamics as f(t,x)
    :return: t, x; the time and state histories
    """
    # Initialize time, state history variables
    ts = [t0]
    xs = [x0]
    dxs = []
    # Determine number of time steps
    n_steps = round((tf - t0) / dt)
    # Initialize time, state
    t = t0
    x = x0

    for i_step in range(0, n_steps):
        # Perform Integration
        k1 = dt*f(t, x)
        k2 = dt*f(t + 0.5*dt, x + 0.5*k1)
        k3 = dt*f(t + 0.5*dt, x + 0.5*k2)
        k4 = dt*f(t + dt, x + k3)
        # Update time, state
        t += dt
        dx = (1.0/6.0)*(k1 + 2*k2 + 2*k3 + k4)
        x = x + dx
        # Record Time, State
        ts.append(t)
        xs.append(x)
        dxs.append(dx/dt)
    return ts, xs, dxs

def sample_time_update_flag(t, dt, t_sample, tol=1e-5):
    x = np.mod(t, t_sample)
    hit = x < tol*dt
    diff = t_sample - x - dt*0.9
    near_miss = diff < 0
    return hit or near_miss  # Second term should handle misses due to numeric precision.


import numpy as np
import scipy.stats as si

    #S: spot price
    #K: strike price
    #T: time to maturity % year; choose 252 trading days or 365 calendar days i.e 30/252 or 30/365, it should be how much time left not total option duration
    #r: interest rate
    #sigma: volatility of underlying asset
    #q: continuous dividend rate

p = print
    # annual dividend yield; use this only if you are unsure
def div_yield(freq, S, div):
    div_yield = np.log((div * freq)/S)
    return div_yield

def err_msg(option_type):
    p('{0} is not valid option type\nchoose c for call and p for put').format(option_type)

def checks(sigma, S, K, r, T, q):
    arr = [sigma, S, K, r, T, q]
    for i in arr:
        if np.isnan(i):
            p('input not number')
        if not i:
            p('input missing\n ')

def d1(sigma, S, K, r, T, q):
    #checks(sigma, S, K, r, T, q)
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    return d1

def d2(sigma, S, K, r, T, q):
    #checks(sigma, S, K, r, T, q)
    d2 = d1(sigma, S, K, r, T, q) - sigma * np.sqrt(T)
    return d2

    #div paying asset using BSM
def bsm_price(option_type, sigma, S, K, r, T, q):

    option_type = option_type.lower()

    #bsm formula for pricing
    _d1 = d1(sigma, S, K, r, T, q)
    _d2 = d2(sigma, S, K, r, T, q)

    if option_type == 'c':
        price = S * si.norm.cdf(_d1, 0.0, 1.0) - K * np.exp(-r * T) * si.norm.cdf(_d2, 0.0, 1.0)
        return price
    elif option_type == 'p':
        price = K * np.exp(-r * T) * si.norm.cdf(-_d2, 0.0, 1.0) - S * np.exp((r - q) * T) * si.norm.cdf(-_d1, 0.0, 1.0)
        return price
    else:
        err_msg(option_type)

    #bsm formula for delta
def delta(option_type, sigma, S, K, r, T, q):
    option_type = option_type.lower()

    _d1 = d1(sigma, S, K, r, T, q)

    if option_type == 'c':
        delta = np.exp(-q*T) * si.norm.cdf(_d1)
        return delta
    elif option_type == 'p':
        delta = np.exp(-q*T) * (si.norm.cdf(_d1) - 1)
        return delta
    else:
        err_msg(option_type)

    #bsm formula for gamma
def gamma(sigma, S, K, r, T, q):

    _d1 = d1(sigma, S, K, r, T, q)

    gamma = np.exp(-q * T - 0.5 * _d1 ** 2)/ (S * sigma * np.sqrt(2 * np.pi * T))
    return gamma

    #bsm formula for vega
def vega(sigma, S, K, r, T, q):

    _d1 = d1(sigma, S, K, r, T, q)

    vega = 0.01 * S * np.exp(-q * T) * np.sqrt(T) * np.exp(- 0.5 * _d1 ** 2)/ np.sqrt(2 * np.pi)
    return vega

# Enhanced IV based on research BS estimator; P and C should have the same unless arbitrage
def IV(option_type, sigma, S, K, r, T, q):
    option_type = option_type.lower()
    option = bsm_price(option_type, sigma, S, K, r, T, q)
    cm1 = np.sqrt( 2 * np.pi )/ ( 2 * ( S + K ))
    br1 = ( 2 * option ) + K - S
    br2 = ( 1.85 * (S + K) * (K - S) ** 2) / ( np.pi * np.sqrt( S * K ))
    IV = cm1 * ( br1 + np.sqrt( br1 ** 2 - br2 ))
    return IV


#@ Model: Transportation Assignment
#@ Description: Model to find minimum cost assignment between warehouses and retailers, given supply and demand constraints $$\min \sum_{i=1}^{3}\sum_{j=1}^{4}c_{ij}x_{ij}$$$$\sum_{j=1}^4 x_{ij}=s_i,\quad\forall i=1,2,3$$$$\sum_{i=1}^3 x_{ij}=d_j,\quad\forall j=1,2,3,4$$$$x_{ij}\geq 0 \quad\forall i,j$$

import cvxpy as cp
import json

#@ Input Object: s
#@ Description: Supply from each warehouse
s = [500,700,800]

#@ Input Object: d
#@ Description: Demand from each retailer
d = [400,900,200,500]

#@ Input Object: c
#@ Description: Matrix of costs
c = [[12,13,4,6],[6,4,10,11],[10,9,12,14]]

#@ Variable: x
#@ Description: Assignment from warehouse i to retailer j (non-negative)
#@ Labels: x_labels
x = cp.Variable((3,4), nonneg=True)

warehouse = ['A', 'B', 'C']
retailer = ['W', 'X', 'Y', 'Z']
x_labels = {}
for i in range(3):
    for j in range(4):
        x_labels[(i,j)] = '{} to {}'.format(warehouse[i], retailer[j])

#@ Function: objectivefn
#@ Description: Costs (minimize) $$\sum_{i=1}^3\sum_{j=1}^4 c_{ij}x_{ij}$$
objectivefn = cp.sum(cp.multiply(c,x.T))

#@ Constraint: supply
#@ Description: $$\sum_{j=1}^3 x_{ij} = s_i,\quad\forall i=1,2,3$$
#@ Labels: supply_labels
supply = cp.sum(x, axis=1) == s

supply_labels = {}
for i in range(3):
    supply_labels[i] = 'supply warehouse {}'.format(warehouse[i])

#@ Constraint: demand
#@ Description: $$\sum_{i=1}^4 x_{ij} = d_j,\quad\forall j=1,2,3,4$$
#@ Labels: demand_labels
demand = cp.sum(x, axis=0) == d

demand_labels = {}
for j in range(4):
    demand_labels[j] = 'demand retailer {}'.format(retailer[j])

#@ Problem: assignment
constraints = []
constraints.append(supply)
constraints.append(demand)

assignment = cp.Problem(cp.Minimize(objectivefn), constraints)

#@ Solver: solver
solver = "ECOS"

assignment.solve(solver=solver, verbose=True)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jan  6 10:12:44 2021

@author: vganapati
"""

import numpy as np
import matplotlib.pyplot as plt

def F(mat2D): # Fourier transform assuming center pixel as center
    return np.fft.fftshift(np.fft.fft2(np.fft.fftshift(mat2D)))

def Ft(mat2D): # Inverse Fourier transform assuming center pixel as center
    return np.fft.ifftshift(np.fft.ifft2(np.fft.ifftshift(mat2D)))

def make_meshgrid(dx = 0.01,
                  dy = 0.01,
                  Lx_max = 1,
                  Ly_max = 1):

    Lx_min = -Lx_max
    Ly_min = -Ly_max
    x = np.arange(Lx_min,Lx_max,dx)
    y = np.arange(Ly_min,Ly_max,dy)
    xm, ym = np.meshgrid(x,y, indexing='ij')
    extent = [Lx_min, Lx_max-dx, Ly_min, Ly_max-dy]

    # Fourier transform dimensions
    F_extent = [len(x)/(4*Lx_min), len(x)/(4*Lx_max) - 1/(Lx_max*2),
                len(y)/(4*Ly_min), len(y)/(4*Ly_max) - 1/(Ly_max*2)]

    return(x,y,xm,ym,extent,F_extent)

def make_circle(dx = 0.01,
                dy = 0.01,
                Lx_max = 1,
                Ly_max = 1,
                a = 1, # stretch in x
                b=1, # stretch in y
                ):

    x,y,xm,ym,extent,F_extent = make_meshgrid(dx,
                                              dy,
                                              Lx_max,
                                              Ly_max)

    circle = np.zeros_like(xm)
    circle[((a*xm)**2+(b*ym)**2) < 1] = 1
    circle[((a*xm)**2+(b*ym)**2) == 1] = 0.5

    F_circle = F(circle)

    return(circle, extent, F_circle, F_extent)

def plot(img, extent, log=False, figsize_x = 10, figsize_y = 10):

    if log:
        img_abs = np.log(np.abs(img))
    else:
        img_abs = np.abs(img)

    plt.figure(figsize = [figsize_x,figsize_y])
    plt.title('Magnitude')
    plt.imshow(img_abs, interpolation='none', extent = extent, cmap='gray')
    plt.colorbar()

    plt.figure(figsize = [figsize_x,figsize_y])
    plt.title('Phase')
    plt.imshow(np.angle(img), interpolation='none', extent = extent, cmap='hsv',
               vmin = -np.pi, vmax = np.pi)
    plt.colorbar()

def rect(mesh):
    output = np.zeros_like(mesh)
    output[np.abs(mesh)<0.5] = 1
    output[np.abs(mesh)==0.5] = 0.5
    return(output)

def triangle(mesh):
    output = np.zeros_like(mesh)
    output[np.abs(mesh)<=1] = 1 - np.abs(mesh[np.abs(mesh)<=1])
    return(output)

def sgn(mesh):
    output = np.zeros_like(mesh)
    output[mesh>0] = 1
    output[mesh<0] = -1
    return(output)

def comb(mesh):
    output = np.zeros_like(mesh)
    output[np.abs(mesh % 1) <= 1e-10] = 1
    return(output)

exp_func_0 = lambda mesh: np.exp(-np.pi*mesh**2)
exp_func_1 = lambda mesh: np.exp(1j*np.pi*mesh)
exp_func_2 = lambda mesh: np.exp(1j*np.pi*mesh**2)
exp_func_3 = lambda mesh: np.exp(-(np.abs(mesh)))

def make_2D(dx = 0.01,
            dy = 0.01,
            Lx_max = 1,
            Ly_max = 1,
            a = 1,
            b=1,
            func=rect # options are rect, triangle, sgn, comb, or exp_func_n
            ):

    x,y,xm,ym,extent,F_extent = make_meshgrid(dx,
                                              dy,
                                              Lx_max,
                                              Ly_max)

    mat2D = func(a*xm)*func(b*ym)

    return(mat2D, extent, F(mat2D), F_extent)

def make_2D_delta(dx = 0.01,
                  dy = 0.01,
                  Lx_max = 1,
                  Ly_max = 1,
                  a = 1,
                  b=1,
                  ):

    x,y,xm,ym,extent,F_extent = make_meshgrid(dx,
                                              dy,
                                              Lx_max,
                                              Ly_max)
    delta_x = np.zeros_like(xm)
    delta_y = np.zeros_like(xm)
    delta_x[np.abs(xm)<1e-10]=1/np.abs(a)
    delta_y[np.abs(ym)<1e-10]=1/np.abs(b)

    mat2D = delta_x*delta_y

    return(mat2D, extent, F(mat2D), F_extent)

if __name__ == "__main__":
    func = rect
    mat2D, extent, F_mat2D, F_extent = make_2D(func=func)
    plot(mat2D, extent)
    plot(F_mat2D, F_extent)

    mat2D, extent, F_mat2D, F_extent = make_2D_delta()
    plot(mat2D, extent)