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

import cv2
from matplotlib import pyplot as plt
import math
import numpy as np

"""
Devuelve el valor de la gaussiana
centrada en el origen en el punto x

x: punto en el que se evalúa la gaussiana
sigma: desviación típica de la gaussiana
"""
def gaussiana(x, sigma):
  return math.exp((-x*x)/(2.0*sigma*sigma))

"""
Devuelve el valor de la primera derivada de la gaussiana
centrada en el origen en el punto x

x: punto en el que se evalúa la gaussiana
sigma: desviación típica de la gaussiana
"""
def gaussiana1Deriv(x, sigma):
  return (-x/(sigma*sigma))*math.exp((-x*x)/(2.0*sigma*sigma))

"""
Devuelve el valor de la segunda derivada de la gaussiana
centrada en el origen en el punto x

x: punto en el que se evalúa la gaussiana
sigma: desviación típica de la gaussiana
"""

def gaussiana2Deriv(x, sigma):
  return (-1.0/(sigma*sigma) + x*x/pow(sigma,4))*math.exp((-x*x)/(2.0*sigma*sigma))

"""
Devuelve la máscara de la gaussiana o de alguna de sus
dos primeras derivadas discretizada

valor: parámetro para calcular la máscara (desv. típica o tamaño de la máscara)
flag_valor: valor = desviación típica si True, valor = tamaño máscara si False
derivada_mascara: derivada de la gaussiana usada para el cálculo de la máscara
  (0 si se usa la propia gaussiana)
"""
def mascaraGaussiana1D(valor, flag_valor, derivada_mascara):
  # Calcular sigma a partir de valor
  sigma = None
  if flag_valor == True:
    sigma = valor
  else:
    if valor%2 == 0:
      print("Error: la máscara no puede tener un tamaño par")
      exit(-1)
    # Máximo valor de sigma tal que, al truncar hacia arriba 3*sigma,
    # obtenemos (tamaño_mascara-1)/2
    sigma = (valor-1.0)/6.0
  # t = (tamaño_mascara-1)/2
  t = int(math.ceil(3*sigma))

  # Inicializar máscara vacía
  mascara = np.zeros(2*t+1)

  # Calcular máscara en función de la derivada especificada
  if derivada_mascara == 0:
    mascara[t] = 1.0
    suma = mascara[t]
    for i in range(1, t+1):
      mascara[t+i] = mascara[t-i] = gaussiana(i, sigma)
      suma += 2*mascara[t+i]
    # En este caso, se divide por la suma para que la suma de los valores sea 1
    mascara /= suma
  elif derivada_mascara == 1:
    mascara[t] = 0.0
    for i in range(1, t+1):
      mascara[t+i] = gaussiana1Deriv(i, sigma)
      mascara[t-i] = -mascara[t+i]
  elif derivada_mascara == 2:
    mascara[t] = -1.0/(sigma*sigma)
    for i in range(1, t+1):
      mascara[t+i] = mascara[t-i] = gaussiana2Deriv(i, sigma)
  else:
    print("Error: el valor derivada_mascara debe ser 0, 1 ó 2")
    exit(-1)

  return mascara

"""
Devuelve la imagen orlada a izquierda y derecha por bordes de ceros

img: imagen a orlar
t: número de columnas que se añaden tanto a izquierda como a derecha
"""
def orlaCeros(img, t):
  orla = np.zeros((img.shape[0],t))
  return np.hstack((orla,img,orla))

"""
Devuelve la imagen orlada a izquierda y derecha por bordes reflejados (101)

img: imagen a orlar
t: número de columnas que se añaden tanto a izquierda como a derecha
"""
def orlaReflejo(img, t):
  reflejo_izq = np.flip(img[:,0:t], axis=1)
  reflejo_der = np.flip(img[:,img.shape[1]-t:img.shape[1]], axis=1)
  return np.hstack((reflejo_izq,img,reflejo_der))

"""
Devuelve la imagen orlada a izquierda y derecha por bordes replicados

img: imagen a orlar
t: número de columnas que se añaden tanto a izquierda como a derecha
"""
def orlaReplicados(img, t):
  reflejo_izq = np.outer(img[:,0], np.ones(t))
  reflejo_der = np.outer(img[:,img.shape[1]-1], np.ones(t))
  return np.hstack((reflejo_izq,img,reflejo_der))

"""
Devuelve una imagen sobre la que se ha aplicado una máscara replicada por filas

img: imagen sobre la que se aplica la máscara replicada
mascara: máscara a aplicar
flag_borde: tipo de borde con el que se orla la imagen
  0 --> borde con ceros
  1 --> borde reflejo 101
  2 --> borde replicados
"""
def aplicarMascaraReplicada(img, mascara, flag_borde):
  # Obtener una matriz con la máscara replicada por filas
  mascara_replicada = np.outer(np.ones(img.shape[0]), mascara)

  # Orlar la imagen según el borde especificado
  t = int((len(mascara)-1)/2)
  img_orlada = None
  if flag_borde == 0:
    img_orlada = orlaCeros(img, t)
  elif flag_borde == 1:
    img_orlada = orlaReflejo(img, t)
  elif flag_borde == 2:
    img_orlada = orlaReplicados(img, t)
  else:
    print('Error: valor no válido para flag_borde')
    exit(-1)

  # Aplicar la máscara replicada a la imagen
  img_final = np.zeros(img.shape, dtype=img.dtype)
  for i in range(t,img.shape[1]+t):
    img_final[:,i-t] = np.sum(np.multiply(img_orlada[:,i-t:i+t+1], mascara_replicada), axis=1)

  return img_final

"""
Devuelve la imagen que resulta de aplicar una correlación a partir de dos
máscaras de dimensión 1 con la misma longitud, una por filas y otra por columnas

img: imagen sobre la que se aplica la máscara replicada
masc_x: máscara a aplicar por filas
masc_y: máscara a aplicar por columnas
flag_borde: tipo de borde con el que se orla la imagen
  0 --> borde con ceros
  1 --> borde reflejo 101
  2 --> borde replicados
"""
def correlacionMascaras1D(img, masc_x, masc_y, flag_borde):
  if len(masc_x) != len(masc_y):
    print("Error: las máscaras no tienen la misma longitud")
    exit(-1)

  if img.shape[0] < len(masc_x) or img.shape[1] < len(masc_x):
    print("Error: las máscaras tienen dimensiones superiores a las de la imagen")
    exit(-1)

  # Aplicar masc_x por filas
  img_corr = aplicarMascaraReplicada(img, masc_x, flag_borde)
  # Trasponer matriz, aplicar masc_y por filas y trasponer de nuevo
  # (Equivalente a aplicar masc_y por columnas)
  img_corr = img_corr.transpose()
  img_corr = aplicarMascaraReplicada(img_corr, masc_y, flag_borde)
  img_corr = img_corr.transpose()

  return img_corr

"""
Devuelve la imagen que resulta de aplicar el operador laplaciano

img: imagen sobre la que se calcula su laplaciana
valor: parámetro para calcular las máscaras (desv. típica o tamaño de la máscara)
flag_valor: valor = desviación típica si True, valor = tamaño máscara si False
flag_borde: tipo de borde con el que se orla la imagen
  0 --> borde con ceros
  1 --> borde reflejo 101
  2 --> borde replicados
"""
def laplaciana(img, valor, flag_valor, flag_borde):
  # Calcular las máscaras de la gaussiana y de su segunda derivada
  masc_gauss = mascaraGaussiana1D(valor, flag_valor, 0)
  masc_gauss_2deriv = mascaraGaussiana1D(valor, flag_valor, 2)

  # Obtener imágenes aplicando las máscaras de las derivadas parciales segundas
  img_deriv_xx = correlacionMascaras1D(img, masc_gauss, masc_gauss_2deriv, flag_borde)
  img_deriv_yy = correlacionMascaras1D(img, masc_gauss_2deriv, masc_gauss, flag_borde)

  # Calcular sigma según valor
  sigma = valor
  if flag_valor == False:
    sigma = (valor-1.0)/6.0

  # Sumar derivadas parciales y normalizar (multiplicar por sigma^2)
  img_laplaciana = (sigma*sigma)*(img_deriv_xx + img_deriv_yy)

  return img_laplaciana

"""
Devuelve una submuestra reducida compuesta por los píxeles que
están en una fila y una columna impares en la imagen original

img: imagen a submuestrear
"""
def reducirSubmuestra(img):
  # Creo una submuestra con la mitad de filas y de columnas
  img_submuestra = np.zeros((int(img.shape[0]/2),int(img.shape[1]/2)))
  # Los píxeles usados para la submuestra son aquellos que en la
  # imagen original se encuentran en una fila y una columna impares
  for i in range(img_submuestra.shape[0]):
    for j in range(img_submuestra.shape[1]):
      img_submuestra[i][j] = img[2*i+1][2*j+1]

  return img_submuestra

"""
Devuelve una submuestra ampliada en la que los píxeles de la imagen original
se encuentran en una fila y una columna impares en la imagen ampliada. El resto
de píxeles se calculan interpolando los píxeles más próximos obtenidos
directamente de la imagen original.

ig: imagen a submuestrear
flag_fila: si True, se añade una fila adicional para que el número de filas sea impar
flag_columna: si True, se añade una columna adicional para que el número de columnas sea impar
"""
def ampliarSubmuestra(img, flag_fila=False, flag_columna=False):
  fila_adicional = columna_adicional = 0
  if flag_fila == True:
    fila_adicional = 1
  if flag_columna == True:
    columna_adicional = 1

  # Crear la submuestra ampliada
  img_submuestra = np.zeros((img.shape[0]*2+fila_adicional,img.shape[1]*2+columna_adicional))

  # Tratamiento especial para las dos primeras filas y columnas
  img_submuestra[0][0] = img_submuestra[0][1] = img_submuestra[1][0] = img_submuestra[1][1] = img[0][0]
  for i in range(1,img.shape[0]):
    img_submuestra[2*i][0] = img_submuestra[2*i+1][0] = img_submuestra[2*i+1][1] = img[i][0]
    img_submuestra[2*i][1] = (img[i][0]+img[i-1][0])/2
  for j in range(1,img.shape[1]):
    img_submuestra[0][2*j] = img_submuestra[0][2*j+1] = img_submuestra[1][2*j+1] = img[0][j]
    img_submuestra[1][2*j] = (img[0][j]+img[0][j-1])/2

  # Asignación de valores al resto de píxeles
  for i in range(1,img.shape[0]):
    for j in range(1,img.shape[1]):
      img_submuestra[2*i][2*j] = (img[i][j]+img[i-1][j]+img[i-1][j]+img[i-1][j-1])/4
      img_submuestra[2*i+1][2*j] = (img[i][j]+img[i][j-1])/2
      img_submuestra[2*i][2*j+1] = (img[i][j]+img[i-1][j])/2
      img_submuestra[2*i+1][2*j+1] = img[i][j]

  #Si es necesario, se añade una fila o una columna adicional según los flags
  if flag_fila == True:
    img_submuestra[-1,:] = img_submuestra[-2,:]
  if flag_columna == True:
    img_submuestra[:,-1] = img_submuestra[:,-2]

  return img_submuestra

"""
Devuelve la pirámide gaussiana de tamaño 4 de una imagen

img: imagen de la que se calcula su pirámide gaussiana
valor: parámetro para calcular la máscara (desv. típica o tamaño de la máscara)
flag_valor: valor = desviación típica si True, valor = tamaño máscara si False
"""
def piramideGaussiana(img, valor, flag_valor):
  # Crear la imagen de la pirámide gaussiana
  piramide = np.zeros((img.shape[0],img.shape[1]+int(img.shape[1]/2)))
  # Copiar la imagen original a la izquierda de la imagen pirámide
  piramide[0:img.shape[0],0:img.shape[1]] = img

  altura = 0
  img_subm = img.copy()
  mascara_gaussiana = mascaraGaussiana1D(valor, flag_valor, 0)
  for i in range(3):
    # Aplicar filtro gaussiano
    img_subm = correlacionMascaras1D(img_subm, mascara_gaussiana, mascara_gaussiana, 2)
    # Obtener submuestra reducida
    img_subm = reducirSubmuestra(img_subm)
    # Guardar submuestra justo abajo de la submuestra anterior
    # y a la izquierda de la original
    piramide[altura:altura+img_subm.shape[0],img.shape[1]:img.shape[1]+img_subm.shape[1]] = img_subm
    altura += img_subm.shape[0]

  return piramide

"""
Devuelve la imagen normalizada en el intervalo [0,1]

img: imagen a normalizar
"""
def normalizarIntervalo01(img):
  # Obtener máximo y mínimo
  minimo, maximo = 0, 1
  for i in range(img.shape[0]):
    for j in range(img.shape[1]):
      if minimo > img[i][j]:
        minimo = img[i][j]
      elif maximo < img[i][j]:
        maximo = img[i][j]

  # Normalizar si es necesario
  img_normalizada = img.copy()
  if minimo < 0 or maximo > 1:
    img_normalizada = (1/(maximo-minimo))*(img-minimo*np.ones(img.shape))

  return img_normalizada

"""
Devuelve la lista de imágenes normalizada en el intervalo [0,1], usando como
máximo y mínimo los valores máximo y mínimo globales de entre todas las
imágenes, respectivamente.

lista_imgs: lista de imágenes a normalizar
"""
def normalizarListaImagenes01(lista_imgs):
  # Obtener máximo y mínimo globales
  minimo, maximo = 0, 1
  for img in lista_imgs:
    for i in range(img.shape[0]):
       for j in range(img.shape[1]):
        if minimo > img[i][j]:
          minimo = img[i][j]
        elif maximo < img[i][j]:
          maximo = img[i][j]

  # Normalizar si es necesario
  lista_norm = []
  if minimo < 0 or maximo > 1:
    for img in lista_imgs:
      lista_norm.append( (1/(maximo-minimo))*(img-minimo*np.ones(img.shape)) )
  else:
    lista_norm = lista_imgs.copy()

  return lista_norm

"""
Devuelve la pirámide laplaciana de tamaño 4 de una imagen

img: imagen de la que se calcula su pirámide laplaciana
valor: parámetro para calcular la máscara (desv. típica o tamaño de la máscara)
flag_valor: valor = desviación típica si True, valor = tamaño máscara si False
"""
def piramideLaplaciana(img, valor, flag_valor):
  # Crear la imagen de la pirámide laplaciana
  piramide = np.zeros((img.shape[0],img.shape[1]+int(img.shape[1]/2)))

  # Crear una lista con todas las submuestras a añadir a la pirámide
  lista_submuestras = []

  # Crear las tres primeras imágenes laplacianas y añadirlas a la lista
  img_subm = img.copy()
  mascara_gaussiana = mascaraGaussiana1D(valor, flag_valor, 0)
  for i in range(3):
    # Crear copia de submuestra reducida anterior con filtro gaussiano
    img_subm_auxiliar = correlacionMascaras1D(img_subm, mascara_gaussiana, mascara_gaussiana, 2)
    # Reducir submuestra anterior con filtro gaussiano
    img_subm = reducirSubmuestra(img_subm_auxiliar)
    # Actualizar flags de filas y columnas impares
    flag_fila = flag_columna = False
    if img_subm_auxiliar.shape[0]%2 == 1:
      flag_fila = True
    if img_subm_auxiliar.shape[1]%2 == 1:
      flag_columna = True
    # Añadir nuevaa laplaciana a la lista:
    # submuestra con filtro gaussiano menos ella misma reducida y luego ampliada
    lista_submuestras.append(
        img_subm_auxiliar - ampliarSubmuestra(img_subm, flag_fila, flag_columna) )

  # Normalizar lista de submuestras laplacianas al intervalo [0,1]
  lista_submuestras = normalizarListaImagenes01(lista_submuestras)

  # Añadir la última imagen obtenida con filtro gaussiano, previamente normalizada
  lista_submuestras.append( normalizarIntervalo01(img_subm) )

  # Guardar la primera submuestra laplaciana a la izquierda de la pirámide
  piramide[0:lista_submuestras[0].shape[0],0:lista_submuestras[0].shape[1]] = (
      lista_submuestras[0] )

  # Guardar el resto de submuestras justo debajo de la submuestra anterior
  # y a la izquierda de la primera
  h = 0
  for i in range(1,4):
    dims = lista_submuestras[i].shape
    piramide[h:h+dims[0],img.shape[1]:img.shape[1]+dims[1]] = (
        lista_submuestras[i] )
    h += lista_submuestras[i].shape[0]

  return piramide

"""
Devuelve la imagen híbrida que resulta de hacer la suma de una imagen con
un filtro gaussiano y otra imagen con un filtro laplaciano.

img1: imagen a la que se le aplica el filtro gaussiana
img2: imagen a la que se le aplica el filtro laplaciano
sigma1: desviación típica de las máscaras del filtro gaussiano
sigma1: desviación típica de las máscaras del filtro laplaciano
peso1: valor por el que se multiplica la imagen 1 filtrada antes de sumarla
peso1: valor por el que se multiplica la imagen 2 filtrada antes de sumarla
flag_dibujar: True si se quieren dibujar las tres imágenes en una misma ventana
"""
def imagenHibrida(img1, img2, sigma1, sigma2, peso1=1, peso2=1, flag_dibujar=True):
  if img1.shape != img2.shape:
    print("Error: las imágenes no tienen las mismas dimensiones")
    exit(-1)

  #Aplicar filtro de paso bajo a imagen 1
  masc_gauss = mascaraGaussiana1D(sigma1, True, 0)
  img1_frec_bajas = correlacionMascaras1D(img1, masc_gauss, masc_gauss, 2)

  #Aplicar filtro de paso alto a imagen 2
  img2_frec_altas = laplaciana(img2, sigma2, True, 2)

  #Normalizar las dos imágenes al intervalo [0,1]
  img1_frec_bajas = normalizarIntervalo01(img1_frec_bajas)
  img2_frec_altas = normalizarIntervalo01(img2_frec_altas)

  #Crear la imagen híbrida y normalizarla al intervalo [0,1]
  img_hibrida = peso1*img1_frec_bajas + peso2*img2_frec_altas
  img_hibrida = normalizarIntervalo01(img_hibrida)

  #Mostrar las tres imágenes en una misma ventana
  if flag_dibujar == True:
    plt.imshow(np.hstack((img1_frec_bajas,img_hibrida,img2_frec_altas)), cmap="gray")
    plt.show()

  return img_hibrida

"""
Función que opera con imágenes a color.
Devuelve la imagen híbrida que resulta de hacer la suma de una imagen con
un filtro gaussiano y otra imagen con un filtro laplaciano.

img1: imagen a color a la que se le aplica el filtro gaussiana
img2: imagen a color a la que se le aplica el filtro laplaciano
sigma1: desviación típica de las máscaras del filtro gaussiano
sigma1: desviación típica de las máscaras del filtro laplaciano
peso1: valor por el que se multiplica la imagen 1 filtrada antes de sumarla
peso1: valor por el que se multiplica la imagen 2 filtrada antes de sumarla
"""
def imagenHibridaColor(img1, img2, sigma1, sigma2, peso1=1, peso2=1):
  if img1.shape != img2.shape:
    print("Error: las imágenes no tienen las mismas dimensiones")
    exit(-1)

  img_hibrida = np.zeros(img1.shape)
  for i in range(3):
    componente_img1 = img1[:,:,i]
    componente_img2 = img2[:,:,i]
    img_hibrida[:,:,i] = imagenHibrida(
      componente_img1, componente_img2, sigma1, sigma2, peso1, peso2, False )

  return img_hibrida

if __name__ == "__main__":
  print("///// APARTADO 1A /////")
  print()

  sigma = 3.0
  print("SIGMA =", sigma)
  print()
  cv2.waitKey(0)

  mascara_gauss = mascaraGaussiana1D(sigma, True, 0)
  print("Máscara de la gaussiana:\n", mascara_gauss)
  plt.figure(figsize=(5,5))
  plt.plot(np.arange(-(len(mascara_gauss)-1)/2, (len(mascara_gauss)-1)/2+1),mascara_gauss,'o',markersize=2)
  plt.show()
  print()
  cv2.waitKey(0)

  mascara_gauss_1deriv = mascaraGaussiana1D(sigma, True, 1)
  print("Máscara de la primera derivada de la gaussiana:\n", mascara_gauss_1deriv)
  plt.figure(figsize=(5,5))
  plt.plot(np.arange(-(len(mascara_gauss_1deriv)-1)/2, (len(mascara_gauss_1deriv)-1)/2+1),mascara_gauss_1deriv,'o',markersize=2)
  plt.show()
  print()
  cv2.waitKey(0)

  mascara_gauss_2deriv = mascaraGaussiana1D(sigma, True, 2)
  print("Máscara de la segunda derivada de la gaussiana:\n", mascara_gauss_2deriv)
  plt.figure(figsize=(5,5))
  plt.plot(np.arange(-(len(mascara_gauss_2deriv)-1)/2, (len(mascara_gauss_2deriv)-1)/2+1),mascara_gauss_2deriv,'o',markersize=2)
  plt.show()
  print()
  cv2.waitKey(0)

  tam = 15
  print("TAMAÑO MÁSCARA =", tam)
  print()
  cv2.waitKey(0)

  mascara_gauss = mascaraGaussiana1D(tam, False, 0)
  print("Máscara de la gaussiana:\n", mascara_gauss)
  plt.figure(figsize=(5,5))
  plt.plot(np.arange(-(len(mascara_gauss)-1)/2, (len(mascara_gauss)-1)/2+1),mascara_gauss,'o',markersize=2)
  plt.show()
  print()
  cv2.waitKey(0)

  mascara_gauss_1deriv = mascaraGaussiana1D(tam, False, 1)
  print("Máscara de la primera derivada de la gaussiana:\n", mascara_gauss_1deriv)
  plt.figure(figsize=(5,5))
  plt.plot(np.arange(-(len(mascara_gauss_1deriv)-1)/2, (len(mascara_gauss_1deriv)-1)/2+1),mascara_gauss_1deriv,'o',markersize=2)
  plt.show()
  print()
  cv2.waitKey(0)

  mascara_gauss_2deriv = mascaraGaussiana1D(tam, False, 2)
  print("Máscara de la segunda derivada de la gaussiana:\n", mascara_gauss_2deriv)
  plt.figure(figsize=(5,5))
  plt.plot(np.arange(-(len(mascara_gauss_2deriv)-1)/2, (len(mascara_gauss_2deriv)-1)/2+1),mascara_gauss_2deriv,'o',markersize=2)
  plt.show()
  print()
  cv2.waitKey(0)

  print("///// FIN APARTADO 1A /////")
  print()
  print()
  print()
  cv2.waitKey(0)

  print("///// APARTADO 1B /////")
  print()

  tam = 15
  print("TAMAÑO MÁSCARA =", tam)
  print()

  print("Imagen original: ")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  plt.imshow(img, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)

  print("Imagen convolucionada: ")
  mascara_gauss = mascaraGaussiana1D(tam, False, 0)
  img_conv = correlacionMascaras1D(img, mascara_gauss, mascara_gauss, 0)
  plt.imshow(img_conv, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)

  print("Imagen convolucionada con GaussianBlur: ")
  img_conv_gaussianblur = cv2.GaussianBlur(img, (tam,tam), (tam-1)/6.0)
  plt.imshow(img_conv_gaussianblur, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)

  print("///// FIN APARTADO 1B /////")
  print()
  print()
  print()
  cv2.waitKey(0)

  print("///// APARTADO 1C /////")
  print()

  for tam in range(3, 15, 4):
    print("TAMAÑO MÁSCARA =", tam)
    mascara_gauss = mascaraGaussiana1D(tam,False,0)
    mascara_gauss1deriv = mascaraGaussiana1D(tam,False,1)
    mascaras_cv2 = cv2.getDerivKernels(1, 0, tam)
    mascara_gauss1deriv_cv2 = mascaras_cv2[0].transpose()[0]
    mascara_gauss_cv2 = mascaras_cv2[1].transpose()[0]
    print("Gaussiana:")
    print("Máscara del apartado 1A:")
    print(mascara_gauss)
    plt.figure(figsize=(5,5))
    plt.plot(np.arange(-(tam-1)/2, (tam-1)/2+1),mascara_gauss,'o',markersize=2)
    plt.show()
    print("Máscara de cv2.getDerivKernels:", )
    print(mascara_gauss_cv2)
    plt.figure(figsize=(5,5))
    plt.plot(np.arange(-(tam-1)/2, (tam-1)/2+1),mascara_gauss_cv2,'o',markersize=2)
    plt.show()

    print()
    cv2.waitKey(0)

    print("Derivada de la gaussiana:")
    print("Máscara del apartado 1A:")
    print(mascara_gauss1deriv)
    plt.figure(figsize=(5,5))
    plt.plot(np.arange(-(tam-1)/2, (tam-1)/2+1),mascara_gauss1deriv,'o',markersize=2)
    plt.show()
    print("Máscara de cv2.getDerivKernels:", )
    print(mascara_gauss1deriv_cv2)
    plt.figure(figsize=(5,5))
    plt.plot(np.arange(-(tam-1)/2, (tam-1)/2+1),mascara_gauss1deriv_cv2,'o',markersize=2)
    plt.show()

    print()
    cv2.waitKey(0)

  for sigma in range(1, 6, 2):
    print("SIGMA =", sigma)
    tam = 2*math.ceil(3*sigma)+1
    mascara_gauss = mascaraGaussiana1D(sigma,True,0)
    mascara_gauss1deriv = mascaraGaussiana1D(sigma,True,1)
    mascaras_cv2 = cv2.getDerivKernels(1, 0, tam)
    mascara_gauss1deriv_cv2 = mascaras_cv2[0].transpose()[0]
    mascara_gauss_cv2 = mascaras_cv2[1].transpose()[0]
    print("Gaussiana:")
    print("Máscara del apartado 1A:")
    print(mascara_gauss)
    plt.figure(figsize=(5,5))
    plt.plot(np.arange(-(tam-1)/2, (tam-1)/2+1),mascara_gauss,'o',markersize=2)
    plt.show()
    print("Máscara de cv2.getDerivKernels:", )
    print(mascara_gauss_cv2)
    plt.figure(figsize=(5,5))
    plt.plot(np.arange(-(tam-1)/2, (tam-1)/2+1),mascara_gauss_cv2,'o',markersize=2)
    plt.show()

    print()
    cv2.waitKey(0)

    print("Derivada de la gaussiana:")
    print("Máscara del apartado 1A:")
    print(mascara_gauss1deriv)
    plt.figure(figsize=(5,5))
    plt.plot(np.arange(-(tam-1)/2, (tam-1)/2+1),mascara_gauss1deriv,'o',markersize=2)
    plt.show()
    print("Máscara de cv2.getDerivKernels:", )
    print(mascara_gauss1deriv_cv2)
    plt.figure(figsize=(5,5))
    plt.plot(np.arange(-(tam-1)/2, (tam-1)/2+1),mascara_gauss1deriv_cv2,'o',markersize=2)
    plt.show()

    print()
    cv2.waitKey(0)

  print("///// FIN APARTADO 1C /////")
  print()
  print()
  print()
  cv2.waitKey(0)

  print("///// APARTADO 1D /////")
  print()

  sigma = 3.0

  # Calcular la máscara de la gaussiana
  mascara_gauss = mascaraGaussiana1D(sigma, True, 0)
  print("Máscara de la gaussiana con sigma =", sigma, ":\n", mascara_gauss)
  plt.figure(figsize=(5,5))
  plt.plot(np.arange(-(len(mascara_gauss)-1)/2, (len(mascara_gauss)-1)/2+1),mascara_gauss,'o',markersize=2)
  plt.show()

  # Calcular la máscara normalizada de la segunda derivada de la gaussiana
  mascara_gauss_2deriv = (sigma*sigma)*mascaraGaussiana1D(sigma, True, 2)
  print("Máscara normalizada de la segunda derivada de la gaussiana con sigma =", sigma, ":\n", mascara_gauss_2deriv)
  plt.figure(figsize=(5,5))
  plt.plot(np.arange(-(len(mascara_gauss_2deriv)-1)/2, (len(mascara_gauss_2deriv)-1)/2+1),mascara_gauss_2deriv,'o',markersize=2)
  plt.show()
  print()
  cv2.waitKey(0)

  # Laplaciana sigma=1.0, bordes cero
  print("Laplaciana con sigma = 1.0 y bordes cero: ")
  plt.imshow(laplaciana(img, 1.0, True, 0), cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  # Laplaciana sigma=1.0, bordes replicados
  print("Laplaciana con sigma = 1.0 y bordes replicados: ")
  plt.imshow(laplaciana(img, 1.0, True, 2), cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  # Laplaciana sigma=3.0, bordes cero
  print("Laplaciana con sigma = 3.0 y bordes cero: ")
  plt.imshow(laplaciana(img, 3.0, True, 0), cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  # Laplaciana sigma=3.0, bordes replicados
  print("Laplaciana con sigma = 3.0 y bordes replicados: ")
  plt.imshow(laplaciana(img, 3.0, True, 2), cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)

  print("///// FIN APARTADO 1D /////")
  print()
  print()
  print()
  cv2.waitKey(0)

  print("///// APARTADO 2A /////")
  print()

  print("Pirámide gaussiana con tamaño de máscara = 5:")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  piramide = piramideGaussiana(img, 5, False)
  plt.imshow(piramide, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)

  print("Pirámide gaussiana con tamaño de máscara = 11:")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  piramide = piramideGaussiana(img, 11, False)
  plt.imshow(piramide, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)

  print("Pirámide gaussiana con sigma = 1.0:")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  piramide = piramideGaussiana(img, 1.0, True)
  plt.imshow(piramide, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)

  print("Pirámide gaussiana con sigma = 3.0:")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  piramide = piramideGaussiana(img, 3.0, True)
  plt.imshow(piramide, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)

  print("///// FIN APARTADO 2A /////")
  print()
  print()
  print()
  cv2.waitKey(0)

  print("///// APARTADO 2B /////")
  print()

  print("Pirámide laplaciana con tamaño de máscara = 5:")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  piramide = piramideLaplaciana(img, 5, False)
  plt.imshow(piramide, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Pirámide laplaciana con tamaño de máscara = 11:")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  piramide = piramideLaplaciana(img, 11, False)
  plt.imshow(piramide, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Pirámide laplaciana con sigma = 1.0:")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  piramide = piramideLaplaciana(img, 1.0, True)
  plt.imshow(piramide, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Pirámide laplaciana con sigma = 3.0:")
  img = cv2.imread('./imagenes/bicycle.bmp', 0)
  piramide = piramideLaplaciana(img, 3.0, True)
  plt.imshow(piramide, cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("///// FIN APARTADO 2B /////")
  print()
  print()
  print()
  cv2.waitKey(0)

  print("///// APARTADO 3 /////")
  print()

  print("Imagen híbrida bicicleta-moto:")
  sigma1, sigma2 = 2.0, 33/20
  img1 = cv2.imread('./imagenes/motorcycle.bmp', 0)
  img2 = cv2.imread('./imagenes/bicycle.bmp', 0)
  img_hibrida = imagenHibrida(img1, img2, sigma1, sigma2, 4, 3)
  plt.imshow(piramideGaussiana(img_hibrida, 7, False), cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Imagen híbrida gato-perro:")
  sigma1, sigma2 = 3.0, 33/20
  img1 = cv2.imread('./imagenes/cat.bmp', 0)
  img2 = cv2.imread('./imagenes/dog.bmp', 0)
  img_hibrida = imagenHibrida(img1, img2, sigma1, sigma2, 4, 3)
  plt.imshow(piramideGaussiana(img_hibrida, 7, False), cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Imagen híbrida Marilyn-Einstein:")
  sigma1, sigma2 = 1.0, 3.0
  img1 = cv2.imread('./imagenes/marilyn.bmp', 0)
  img2 = cv2.imread('./imagenes/einstein.bmp', 0)
  img_hibrida = imagenHibrida(img1, img2, sigma1, sigma2, 4, 5)
  plt.imshow(piramideGaussiana(img_hibrida, 7, False), cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Imagen híbrida submarino-pez:")
  sigma1, sigma2 = 3.0, 11/20
  img1 = cv2.imread('./imagenes/submarine.bmp', 0)
  img2 = cv2.imread('./imagenes/fish.bmp', 0)
  img_hibrida = imagenHibrida(img1, img2, sigma1, sigma2, 1, 1)
  plt.imshow(piramideGaussiana(img_hibrida, 7, False), cmap='gray')
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("///// FIN APARTADO 3 /////")
  print()
  print()
  print()
  cv2.waitKey(0)

  print("///// BONUS 1 /////")
  print()

  print("Imagen híbrida bicicleta-moto a color:")
  sigma1, sigma2 = 2.0, 1.0
  img1 = cv2.imread('./imagenes/motorcycle.bmp')
  img2 = cv2.imread('./imagenes/bicycle.bmp')
  img_hibrida = imagenHibridaColor(img1, img2, sigma1, sigma2, 4, 3)
  plt.imshow( cv2.cvtColor((255*img_hibrida).astype(np.uint8), cv2.COLOR_BGR2RGB) )
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Imagen híbrida gato-perro a color:")
  sigma1, sigma2 = 3.0, 33/20
  img1 = cv2.imread('./imagenes/cat.bmp')
  img2 = cv2.imread('./imagenes/dog.bmp')
  img_hibrida = imagenHibridaColor(img1, img2, sigma1, sigma2, 4, 3)
  plt.imshow( cv2.cvtColor((255*img_hibrida).astype(np.uint8), cv2.COLOR_BGR2RGB) )
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Imagen híbrida submarino-pez a color:")
  sigma1, sigma2 = 3.0, 11/20
  img1 = cv2.imread('./imagenes/submarine.bmp')
  img2 = cv2.imread('./imagenes/fish.bmp')
  img_hibrida = imagenHibridaColor(img1, img2, sigma1, sigma2, 1, 1)
  plt.imshow( cv2.cvtColor((255*img_hibrida).astype(np.uint8), cv2.COLOR_BGR2RGB) )
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("///// FIN BONUS 1 /////")
  print()
  print()
  print()
  cv2.waitKey(0)

  print("///// BONUS 2 /////")
  print()

  sigma1, sigma2 = 3.0, 61/30
  peso1, peso2 = 4, 3

  img1 = cv2.imread('./imagenes/monte_cervino.jpg', 0)
  img2 = cv2.imread('./imagenes/torre_eiffel.jpg', 0)

  img1 = img1[20:583,0:800]
  img2 = img2[:,290:690]

  print("Imagen monte Cervino restringida: ")
  plt.imshow(img1, cmap="gray")
  plt.show()
  print()
  print("Imagen torre Eiffel restringida: ")
  plt.imshow(img2, cmap="gray")
  plt.show()
  print()

  masc_gauss = mascaraGaussiana1D(1.0, True, 0)
  img1_auxiliar = correlacionMascaras1D(img1, masc_gauss, masc_gauss, 2)
  img1 = np.zeros((img1_auxiliar.shape[0], int(img1_auxiliar.shape[1]/2)))
  for j in range(img1.shape[1]):
    img1[:,j] = img1_auxiliar[:,2*j]

  print("Imagen monte Cervino restringida y estrechada: ")
  plt.imshow(img1, cmap="gray")
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Imagen híbrida: ")
  img_hibrida = imagenHibrida(img1, img2, sigma1, sigma2, peso1, peso2, False)
  plt.imshow(img_hibrida, cmap="gray")
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Pirámide gaussiana: ")
  plt.imshow(piramideGaussiana(img_hibrida, 7, False), cmap='gray')
  plt.show()
  print()
  print()
  print()
  cv2.waitKey(0)

  img1 = cv2.imread('./imagenes/monte_cervino.jpg')
  img2 = cv2.imread('./imagenes/torre_eiffel.jpg')

  img1 = img1[20:583,0:800,:]
  img2 = img2[:,290:690,:]

  print("Imagen monte Cervino restringida: ")
  plt.imshow( cv2.cvtColor(img1, cv2.COLOR_BGR2RGB) )
  plt.show()
  print()
  print("Imagen torre Eiffel restringida: ")
  plt.imshow( cv2.cvtColor(img2, cv2.COLOR_BGR2RGB) )
  plt.show()
  print()

  masc_gauss = mascaraGaussiana1D(1.0, True, 0)
  img1_auxiliar = np.zeros(img1.shape)
  for i in range(3):
    img1_auxiliar[:,:,i] = correlacionMascaras1D(img1[:,:,i], masc_gauss, masc_gauss, 2)
  img1 = np.zeros((img1_auxiliar.shape[0], int(img1_auxiliar.shape[1]/2), 3))
  for j in range(img1.shape[1]):
    img1[:,j,:] = img1_auxiliar[:,2*j,:]

  print("Imagen monte Cervino restringida y estrechada: ")
  plt.imshow( cv2.cvtColor(img1.astype(np.uint8), cv2.COLOR_BGR2RGB) )
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("Imagen híbrida: ")
  img_hibrida = imagenHibridaColor(img1, img2, sigma1, sigma2, peso1, peso2)
  plt.imshow( cv2.cvtColor((255*img_hibrida).astype(np.uint8), cv2.COLOR_BGR2RGB) )
  plt.show()
  print()
  cv2.waitKey(0)
  print()

  print("///// FIN BONUS 2 /////")
  print()
  print()
  print()
  cv2.waitKey(0)


from scipy import signal
from scipy.signal import find_peaks
import matplotlib.pyplot as plt
import numpy as np

from scipy.fftpack import dct, idct

if __name__ == "__main__":
    t = np.linspace(0, 1, 500)
    tri = signal.sawtooth(2 * np.pi * 5 * t, 0.5)

    hs = dct(tri, type=2)
    fs = (0.5 + np.arange(len(hs))) / 2

    compressed_len = 40

    # inverse dct, lossy compression
    hs_compressed = hs[0:compressed_len]
    tri_compressed = idct(hs_compressed, type=2)
    # scale it back to -1, 1
    tri_compressed = tri_compressed / 500
    t_compressed = np.linspace(0, 1, len(tri_compressed))

    peaks, _ = find_peaks(-hs, height=10)

    plt.figure()
    plt.subplot(411)
    plt.plot(t, tri)
    plt.subplot(412)
    plt.plot(fs, hs)
    plt.plot(peaks, hs[peaks])
    plt.subplot(413)
    plt.plot(fs[:compressed_len], hs[:compressed_len])
    plt.subplot(414)
    plt.plot(t_compressed, tri_compressed)
    plt.show()


# Lab 5 Logistic Regression Classifier
import tensorflow as tf
import numpy as np
tf.set_random_seed(777)  # seed 지정

# 데이터 read
xy = np.loadtxt('data-03-diabetes.csv', delimiter=',', dtype=np.float32)
x_data = xy[:, 0:-1]    # predictor
y_data = xy[:, [-1]]    # response

print(x_data.shape, y_data.shape)   # (759, 8) (759, 1)

# placeholder 변수 지정
X = tf.placeholder(tf.float32, shape=[None, 8])
Y = tf.placeholder(tf.float32, shape=[None, 1])

W = tf.Variable(tf.random_normal([8, 1]), name='weight')
b = tf.Variable(tf.random_normal([1]), name='bias')

# logistic regression model(post.prob에 modeling)
hypothesis = tf.sigmoid(tf.matmul(X, W) + b)

# cost/loss function
cost = -tf.reduce_mean(Y * tf.log(hypothesis) + (1 - Y) *
                       tf.log(1 - hypothesis))

# Minimize
train = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(cost)

# Accuracy 계산
# post.prob > 0.5 => 1
# post.prob < 0.5 => 0
predicted = tf.cast(hypothesis > 0.5, dtype=tf.float32)
# predicted == Y (True)이면 1
# predicted != Y (False)이면 0
accuracy = tf.reduce_mean(tf.cast(tf.equal(predicted, Y), dtype=tf.float32))

# Session 만들고 실행
with tf.Session() as sess:
    # 변수 초기화
    sess.run(tf.global_variables_initializer())

    for step in range(10001):
        cost_val, _ = sess.run([cost, train], feed_dict={X: x_data, Y: y_data})
        if step % 200 == 0:
            print(step, cost_val)

    # training된 model로 predict, accuracy 계산
    h, c, a = sess.run([hypothesis, predicted, accuracy],
                       feed_dict={X: x_data, Y: y_data})
    print("\nHypothesis: ", h, "\nCorrect (Y): ", c, "\nAccuracy: ", a)

'''
0 0.82794
200 0.755181
400 0.726355
600 0.705179
800 0.686631
...
9600 0.492056
9800 0.491396
10000 0.490767

...

 [ 1.]
 [ 1.]
 [ 1.]]
Accuracy:  0.762846
'''


import numpy as np
from sklearn.preprocessing import LabelBinarizer

class GaussianNB(object):
    """
    朴素贝叶斯分类器，适用于连续型数据。
    """

    @staticmethod
    def gaussfunc(x, mu, singma):
        """高斯函数
        :param x: 数据集
        :param mu: 均值
        :param singma: 方差
        :return:
        """
        sqsingma = singma @ singma
        numerator = -np.exp(np.sum((x - mu) ** 2, axis=1) / (2 * sqsingma))
        return numerator / np.sqrt(2 * np.pi * sqsingma)

    def fit(self, X, y):
        """
        :param X_: shape = [n_samples, n_features]
        :param y: shape = [n_samples]
        :return: self
        """
        self.classes, self.classes_count = np.unique(y, return_counts=True)
        self.mean = np.zeros((self.classes_count.shape[0],
                              X.shape[1]), dtype=np.float64)
        self.var = np.zeros((self.classes_count.shape[0],
                             X.shape[1]), dtype=np.float64)
        for i, label in enumerate(self.classes):
            x_i = X[y == label]
            self.mean[i, :] = np.mean(x_i, axis=0)
            self.var[i, :] = np.var(x_i, axis=0)

        return self

    def predict(self, X):
        """
        :param X: shape = [n_samples, n_features]
        :return: shape = [n_samples]
        """
        likelihood = []
        for i in range(self.classes.shape[0]):
            likelihood.append(self.classes_count[i] *
                              GaussianNB.gaussfunc(X, self.mean[i, :],
                                                   self.var[i, :]))
        likelihood = np.array(likelihood).T
        return np.argmax(likelihood, axis=1)

class MultinomialNB(object):
    """
    朴素贝叶斯分类器，适用于离散型数据。
    """

    def __init__(self, alpha=1.0):
        self.alpha = alpha

    def fit(self, X, y):
        """
        :param X_: shape = [n_samples, n_features]
        :param y: shape = [n_samples]
        :return: self
        """
        labelbin = LabelBinarizer()
        Y = labelbin.fit_transform(y)
        self.classes = labelbin.classes_
        self.class_count = np.zeros(Y.shape[1], dtype=np.float64)
        self.feature_count = np.zeros((Y.shape[1], X.shape[1]),
                                      dtype=np.float64)

        self.feature_count += Y.T @ X
        self.class_count += Y.sum(axis=0)
        smoothed_fc = self.feature_count + self.alpha
        smoothed_cc = smoothed_fc.sum(axis=1)

        self.feature_log_prob = (np.log(smoothed_fc) -
                                 np.log(smoothed_cc.reshape(-1, 1)))

    def predict(self, X):
        """
        :param X: shape = [n_samples, n_features]
        :return: shape = [n_samples]
        """
        likelihood = X @ self.feature_log_prob.T
        return np.argmax(likelihood, axis=1)


import math as m
import numpy as np
import matplotlib.pyplot as plt

def get_parallel_L_C(C_v_min, C_v_max, C_s, f_min, f_max):

    # Circuit schematic
    schematic = """
     -----------------------
     |       |        |
    C        {0} C_v    |
    C  L     |        = C_p
    C        |        |
    C        = C_s    |
     |       |        |
     -----------------------
     |
     _
     -
    """.format(u"\u2260")

    # Variable capacitance range
    C_v_t_min = (1/C_s+1/C_v_min)**-1
    C_v_t_max = (1/C_s+1/C_v_max)**-1

    # Calculate required parallel capacitance and inductance
    C_v = C_v_t_max-C_v_t_min
    r = (f_min/f_max)**2
    C_f = r/(1-r)*C_v
    C_p = C_f-C_v_t_min
    L = 1/(4*np.pi**2*f_min**2*(C_v_t_max+C_p))

    return C_p, L, schematic

class ProgressiveVFO:
    """VFO from Hayward and Lawson "A Progressive Communications Receiver" QST 1981-11."""

    def __init__(self, L, C1, Cv, f_min, f_max):

        # Store params
        self.L = L
        self.C1 = C1
        self.Cv = Cv

        # Circuit schematic
        self.schematic = """
         ---------------------------
         |       |        |
        C        = C3  C1 =
        C  L     |        |_____
        C        |        |    |
        C        |     C2 =    {0} Cv
         |       |        |    |
         ---------------------------
         |
         _
         -
        """.format(u"\u2260")

        # Calculate required capacitances
        wl = 2.0*np.pi*f_min
        wu = 2.0*np.pi*f_max
        Cl = 1.0/(wl**2*L)
        Cu = 1.0/(wu**2*L)
        dC = Cl-Cu
        x = 2.0*C1+Cv
        y = C1*Cv+C1**2*(1-Cv/dC)
        self.C2 = 0.5*(-x+np.sqrt(x**2-4*y))
        self.C3 = Cv-1.0/(1.0/C1+1.0/self.C2)

    def plot_tuning(self):
        """Plots the tuning range of the VFO over a linear variation in Cv."""

        # Calculate frequencies
        Cv_range = np.linspace(0.0, self.Cv, 1000)
        C = self.C3+1.0/(1.0/self.C1+1.0/(self.C2+Cv_range))
        f = 1.0/(2.0*np.pi*np.sqrt(self.L*C))

        # PLot
        plt.figure()
        plt.plot(Cv_range*1e12, f*1e6)
        plt.xlabel('$C_v$ [pF]')
        plt.ylabel('f [MHz]')
        plt.show()

if __name__=="__main__":

    vfo = ProgressiveVFO(0.784e-6, 15.0e-12, 145.0e-12, 14.225e6, 14.350e6)
    vfo.plot_tuning()

    ## Get parameters
    #f_min = float(input("Minimum desired frequency in MHz?"))*1e6
    #f_max = float(input("Maximum desired frequency in MHz?"))*1e6
    #C_v_min = float(input("Minimum variable capacitance in pF?"))*1e-12
    #C_v_max = float(input("Maximum variable capacitance in pF?"))*1e-12

    ## Series capacitances
    #C_s = np.logspace(-12, -10, 10)

    #plt.figure()
    #print("{0:<20}{1:<20}{2:<20}".format("Series C [pF]", "Parallel C [pF]", "Parallel L [uH]"))
    #print("".join(["-"]*60))

    #for i in range(10):
    #    C_p, L, schematic = get_parallel_L_C(C_v_min, C_v_max, C_s[i], f_min, f_max)
    #    print("{0:<20}{1:<20}{2:<20}".format(C_s[i]*1e12, C_p*1e12, L*1e6))

    #    # Get frequency over range of C_v
    #    C_v = np.linspace(C_v_min, C_v_max, 1000)
    #    C = (1/C_s[i]+1/C_v)**-1+C_p
    #    f = 1/(2.0*m.pi*np.sqrt(L*C))
    #    plt.plot(C_v*1e12, f*0.000001, label=str(C_s[i]*1e12))

    #print(schematic)
    #plt.legend(title="Series C [pF]")
    #plt.xlabel('Variable Capacitance [pF]')
    #plt.ylabel('Frequency [MHz]')
    #plt.show()


import math
import numpy as np

def reduce_dimension(x, Orig, method='Mean'):
    '''
    ## Reduce the dimension of the metrix
    ### Parameters
        x: the input ndarray.
        method: choose the method used to reduce dimension of x.
    ### Method:\n
        1. Mean: Get the abs value of a row, and use the mean of the row to replace the row.
        2. Euclidean:
        3. Manhattan:
        4. Chebyshev:
        5. Cosine:
    '''
    ny, nx = x.shape
    Xrd = abs(x)
    if method == 'Mean':
        Xrd = abs(Orig-x)
        Xrd = Xrd.sum(axis=1)

    elif method == 'Euclidean':
        Xrd = [np.linalg.norm(Orig[t]-x[t]) for t in range(ny)]

    elif method == 'Manhattan':
        Xrd = [np.linalg.norm(Orig[t]-x[t], ord=1) for t in range(ny)]

    elif method == 'Chebyshev':
        Xrd = [np.linalg.norm(Orig[t]-x[t], ord=np.inf) for t in range(ny)]

    elif method == 'Cosine':
        Xrd = [np.dot(Orig[t], x[t])/(np.linalg.norm(Orig[t]) *
                                      (np.linalg.norm(x[t]))) for t in range(ny)]

    return Xrd

# from sklearn.preprocessing import normalize

def norm(x, method='linear'):
    '''
    ## Normalized
    ### Parameters
        x: the input vector
        method: ...
    ### Methods:
        1. linear
        2. z-score
        3. atan
        4. sigmod
        5. tanh
    '''
    if method == 'linear':
        maxVal = max(x)
        minVal = min(x)
        dis = maxVal - minVal
        x = (x-minVal)/dis

    elif method == 'z-score':
        x = (x-np.mean(x))/np.std(x)

    elif method == 'atan':
        y = [math.atan(t)*2/math.pi for t in x]
        x = y

    elif method == 'sigmod':
        x = [1 / (1 + np.exp(-t)) for t in x]

    elif method == 'tanh':
        s1 = [np.exp(t) - np.exp(-t) for t in x]
        s2 = [np.exp(t) + np.exp(-t) for t in x]
        x = [s1[t] / s2[t] for t in range(len(s1))]

    return x


import numpy as np
import cmath

def directFT(x):
    # direct discrete Fourier transform
    N = x.size
    n = np.arange(N)
    k = n.reshape((N, 1))
    M = np.exp(-2j * np.pi * k * n / N)
    return np.dot(M, x)

def recursiveFT(x):
    # recursive FFT
    N = x.size

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

def bitreversalFT(vector, inverse=False):
    # Returns the integer whose value is the reverse of the lowest 'bits' bits of the integer 'x'.
    def reverse(x, bits):
        y = 0
        for i in range(bits):
            y = (y << 1) | (x & 1)
            x >>= 1
        return y

    # Initialization
    n = len(vector)
    levels = 0
    while True:
        if 1 << levels == n:
            break
        elif 1 << levels > n:
            raise ValueError("Length is not a power of 2")
        else:
            levels += 1
    # Now, levels = log2(n)
    exptable = [cmath.exp((2j if inverse else -2j) * cmath.pi * i / n) for i in range(n // 2)]
    vector = [vector[reverse(i, levels)] for i in range(n)]  # Copy with bit-reversed permutation

    # Radix-2 decimation-in-time FFT
    size = 2
    while size <= n:
        halfsize = size // 2
        tablestep = n // size
        for i in range(0, n, size):
            k = 0
            for j in range(i, i + halfsize):
                temp = vector[j + halfsize] * exptable[k]
                vector[j + halfsize] = vector[j] - temp
                vector[j] += temp
                k += tablestep
        size *= 2
    return vector


import numpy as np
from scipy import polyfit

from podostemaceous.constants import kB
from podostemaceous.molecule import VHSMolecule

def reference_diameter_VHS(mass, eta_ref, T_ref, omega):
    """
    Calculates the reference diameter for a VHS model so that
        eta(T_ref) == eta_ref

    Parameters:
        mass: in kilograms
        eta_ref: viscosity at the reference temperature, in Pa s
        T_ref: reference temperature, in Kelvin
        omega: Unitless. Previous best fit to viscosity: eta ~ T^{omega}

    Returns:
        Reference diameter in meters.

    Reference:
        Bird, G. A., The DSMC Method, Version 1.2, 2013.
        Chapter 2, Equation (43)
    Formula:
        Note that the reference uses $\mu$ for viscosity; here we use $\eta$.
        $$d_{\mathrm{ref}} = \left(\frac{15(m k_B T_{\mathrm{ref}} / \pi)^{1/2}}{2 (5 - 2 \omega)(7 - 2 \omega) \eta_{\mathrm{ref}}}\right)^{1/2}$$

    """
    numerator = 15 * (mass * kB * T_ref / np.pi) ** (1 / 2)
    denominator = 2 * (5 - 2 * omega) * (7 - 2 * omega) * eta_ref
    d_ref = (numerator / denominator) ** (1 / 2)
    return d_ref

def VHS_model_from_viscosity(visc_func, mass, T_min, T_max, T_ref=0):
    """
    Calculates the reference diameter for a VHS model.

    Parameters:
        visc_func: a function of one variable, temperature in Kelvin,
            returns viscosity at that temperature in Pa s.
        mass: mass of the particle, in kilograms
        T_min, T_max: range over which to perform the fit.
        T_ref: reference temperature, in Kelvin.
            Default is the mean of T_min and T_max.
    Returns:
        A dictionary:
            mass : in kilograms
            T_ref: in Kelvin
            omega: Unitless. Best fit to viscosity: eta ~ T^{omega}
            d_ref: Best fit for the reference diameter, in meters.
                Note that because this is a best fit, the fit VHS model's
                viscosity at the reference temperature is likely different
                from that of the supplied viscosity function.
    """
    NUM_POINTS_TO_FIT_OVER = 100 # bignum
    if T_ref == 0:
        T_ref = (T_min + T_max) / 2
    TK = np.linspace(T_min, T_max, NUM_POINTS_TO_FIT_OVER)
    eta = visc_func(TK)
    # fit in log - logspace
    x = np.log(TK)
    y = np.log(eta)
    omega, b = polyfit(x, y, deg=1)

    # Calculate d_ref
    A_best = np.exp(b) # eta = A_best T^omega
    numerator = 15 * np.sqrt(mass * kB * T_ref / np.pi)
    denominator = 2 * A_best * (7 - 2 * omega) * (5 - 2 * omega) * T_ref ** omega
    d_best = (numerator / denominator)**(1/2)
    return VHSMolecule('best_fit', mass, d_best, omega=omega, t_ref=T_ref)


import math
import numpy as np
from tabulate import tabulate
from numpy.linalg import solve

#Вычисленный u(x) при z(s)=1
def u(x):
    return (math.exp(3*x) - 1) / 3 * x

#Ядро:
def K(x, s):
    return math.exp(3 * x * s)

#Вычисление узлов для формулы средних прямоугольников:
def build_nodes(N):
    result = []
    for i in range(N):
        result.append(i/N+1/(2*N))
    return result

#Построение матрицы С:
def build_matrix(N):
    result = np.zeros((N,N))
    nodes = build_nodes(N)
    for i in range(N):
        for j in range(N):
            result[i,j] = K(nodes[i],nodes[j])*(1/N)
    return result

#Построение значений u(x) в узлах квадратурной формулы средних прямоугольников при z(s)=1
def build_right_part(N):
    result = np.zeros((N, 1))
    nodes = build_nodes(N)
    for i in range(N):
            result[i] = u(nodes[i])
    return result

#Решение СЛАУ методом регуляризации матрицы c параметром а:
def build_solution(N, a):
    C = build_matrix(N)
    right = build_right_part(N)
    return solve(np.transpose(C)@C+a*np.identity(N), np.transpose(C)@right)

#Рассмотрим погрешность при различных параметрах N (число узлов) и a (параметр метода регуляризации) и запишем результат в файл в виде таблицы.
N = 10
ans = np.zeros((N,N+1))
for i in range(1,N+1):
    ans[i-1,0] = i*10
headers = [r'n\a']
for j in range(6,6+N):
    headers.append(10**(-j))
residuals = []
for i in range(1,1+N):
    u0 = np.ones((N*i,1))
    residuals.append(N*i)
    for j in range(6,6+N):
        solution = build_solution(N*i,10**(-j))
        ans[i-1,j-5] = abs(u0-solution).max()

res_table = tabulate(ans,headers=headers,
               tablefmt='github',numalign="right")
with open('result3.txt','w') as file:
    file.write(res_table)

#Посмотрим на индекс минимального элемента таблицы. Таким образом, наименьшая погрешность достигается при числе узлов N=100 и параметре а=1e-10, и именно они являются оптимальными параметрами для данной задачи.
ind = np.unravel_index(np.argmin(ans, axis=None), ans.shape)
ind
#Сама погрешность:

ans[ind]


import numpy as np

# TODO: Mapping features from linear to polynomial regressions
# Return: New X with "p" polynomial features
# X_poly[i, :] = [X[i], X[i]**2, X[i]**3, ..., X[i]**p]
def poly_features(X, p):      # X: (m,)
    # Initialize parameter
    X_poly = np.zeros((X.shape[0], p))

    # Mapping features
    for i in range(p):
        X_poly[:, i] = X**(i+1)
    return X_poly  # (m, p)

# TODO: Normalize the features in X using Z-score normalization
# Return: Normalize version of X where mean of feature = 0; std of features = 1
def feature_normalize(X):  # X: (m, p)
    # Compute norm, mean, std of each feature in array
    norm = np.linalg.norm(X, axis=0)
    mean = np.mean(X, axis=0)
    std = np.std(X, axis=0, ddof=1)

    # Normalize feature of X
    # X_normal = X / norm
    X_normal = (X-mean) / std
    return X_normal, mean, std


from numpy import fabs, arange

class Integration:
    EPS = 1e-6

    area_domain = list()
    area_codomain = list()

    @staticmethod
    def rectangles(func, a, b, iterations=180):
        delta_x = (b - a) / iterations
        x = [a + 0.5 * delta_x]
        y = [func(x[0])]
        current_area = 0.0
        for iteration in range(iterations):
            prev_area = current_area
            current_area += func(x[iteration]) * delta_x
            if fabs(current_area - prev_area) < Integration.EPS:
                break
            x.append(x[iteration] + delta_x)
            y.append(func(x[iteration+1]))
        # plt.plot(x, y, 'bs', color='red', label='Rectangles')
        return current_area

    @staticmethod
    def trapezoids(func, a, b, iterations=150):
        delta_x = (b - a) / iterations
        x = [a + 0.5 * delta_x]
        y = [func(x[0])]
        current_area = 0.0
        for iteration in range(iterations):
            current_area += func(x[iteration])
            x.append(x[iteration] + delta_x)
            y.append(func(x[iteration + 1]))
        # plt.plot(x, y, 'g^', color='yellow', label='Rectangles')
        return delta_x * ((func(a) + func(b)) / 2 + current_area)

    @staticmethod
    def simpson(func, a, b, iterations=50):
        delta_x = (b - a) / iterations
        Integration.area_domain.extend(arange(a, b, delta_x))
        x = a
        current_area = func(a) + func(b)
        for iteration in range(iterations):
            x += delta_x
            if iteration % 2:
                current_area += 2.0 * func(x)
            else:
                current_area += 4.0 * func(x)
            Integration.area_codomain.append((delta_x / 3) * current_area)
        return (delta_x/3.0)*current_area


# -*- coding: utf-8 -*-
import numpy as np

def pca(X, k):
    '''X: m*n,m个样本，每个样本有n个特征
       k: 降到k维
    '''
    m, n = X.shape
    if k>n: return X
    X = X - X.mean(axis=0) ## 中心化
    cov = np.cov(X,rowvar=False,ddof=m-1) ## 协方差矩阵系数为1
    val, vec = np.linalg.eig(cov) ## 特征值+特征向量
    vec = vec[:,np.argpartition(-val,k-(k==n))[:k]] ## 取前k个最大特征值对应的特征向量
    return X.dot(vec) ## 返回降维后的X

if __name__=='__main__':

    import matplotlib.pyplot as plt
    X = np.array([(2.5,2.4), (0.5,0.7), (2.2,2.9), (1.9,2.2), (3.1,3.0),
                  (2.3, 2.7), (2, 1.6), (1, 1.1), (1.5, 1.6), (1.1, 0.9)])
    X1 = pca(X,1)
    X2 = pca(X,2)
    plt.scatter(X[:,0],X[:,1], marker='o',c='red')
    plt.scatter(X1,np.zeros(len(X1)), marker='o',c='blue')
    plt.scatter(X2[:,0],X2[:,1], marker='*',c='blue')


import numpy as np
import itertools
import matplotlib.pyplot as plt
import csv
import sys
import math
import geopy.distance

def create_nodes_array(N, seed=None):
    """
    Creates array of random points of size N.
    """
    if seed:
        print("seed", seed)
        np.random.seed(seed)

    nodes_list = []
    for i in range(N):
        nodes_list.append(np.random.rand(2) * 10)
    return np.array(nodes_list)

def readInstance(instance):
    """
    Creates array from a TSP instance.
    """
    try:
        reader = csv.reader(open(instance), delimiter=' ')
    except FileNotFoundError:
        sys.exit("Fichero param.config no encontrado")

    nodes_list = []

    nodes=False

    for row in reader:

        if row[0]=="EOF":
            break
        if nodes==True:
            node = [float(row[1]),float(row[2])]
            nodes_list.append(node)

        if row[0]=="NODE_COORD_SECTION":
           nodes=True

    return np.array(nodes_list)

def get_tsp_matrix(nodes_array):
    """
    Creates distance matrix out of given coordinates.

    """

    number_of_nodes = len(nodes_array)
    matrix = np.zeros((number_of_nodes, number_of_nodes))
    for i in range(number_of_nodes):
        for j in range(i, number_of_nodes):
            #matrix[i][j] = distance_between_points(nodes_array[i], nodes_array[j])
            matrix[i][j] = distance_two_coordinates(nodes_array[i], nodes_array[j])
            matrix[j][i] = matrix[i][j]
    return matrix

def distance_between_points(point_A, point_B):
    return np.sqrt((point_A[0] - point_B[0])**2 + (point_A[1] - point_B[1])**2)

def distance_two_coordinates(point_A, point_B):

    lat1 = math.pi * ((point_A[0] // 1) + 5.0 * (point_A[0] % 1) / 3.0) / 180.0
    lon1 = math.pi * ((point_A[1] // 1) + 5.0 * (point_A[1] % 1) / 3.0) / 180.0
    lat2 = math.pi * ((point_B[0] // 1) + 5.0 * (point_B[0] % 1) / 3.0) / 180.0
    lon2 = math.pi * ((point_B[1] // 1) + 5.0 * (point_B[1] % 1) / 3.0) / 180.0

    q1       = math.cos( lon1 - lon2 )
    q2       = math.cos( lat1 - lat2 )
    q3       = math.cos( lat1 + lat2 )
    radius   = 6378.388

    distance = radius * math.acos (1/2 * ((1 + q1) * q2 - (1 - q1) * q3)) + 1

    if distance==1.0:
        return 0

    return(int(distance))

    #return round(distance)
    #return round(geopy.distance.great_circle(point_A, point_B).km)

def solve_tsp_brute_force(nodes_array):
    number_of_nodes = len(nodes_array)
    initial_order = range(0, number_of_nodes)
    all_permutations = [list(x) for x in itertools.permutations(initial_order)]
    cost_matrix = get_tsp_matrix(nodes_array)
    best_permutation = all_permutations[0]
    best_cost = calculate_cost(cost_matrix, all_permutations[0])
    for permutation in all_permutations:
        current_cost = calculate_cost(cost_matrix, permutation)
        if current_cost < best_cost:
            best_permutation = permutation
            best_cost = current_cost
    print("Brute force:", best_permutation, best_cost)
    return best_permutation

def solve_tsp_brute_force_from_given_node(nodes_array, starting_node):
    number_of_nodes = len(nodes_array)
    initial_order = range(0, number_of_nodes)
    all_permutations = [list(x) for x in itertools.permutations(initial_order)]
    cost_matrix = get_tsp_matrix(nodes_array)
    best_permutation = all_permutations[0]
    best_cost = calculate_cost(cost_matrix, all_permutations[0])*1000
    for permutation in all_permutations:
        if permutation[0] != starting_node:
            continue
        current_cost = calculate_cost(cost_matrix, permutation)
        if current_cost < best_cost:
            best_permutation = permutation
            best_cost = current_cost
    print("Brute force:", best_permutation, best_cost)
    return best_permutation

def calculate_cost(cost_matrix, solution):
    cost = 0
    for i in range(len(solution)):
        a = i%len(solution)
        b = (i+1)%len(solution)
        cost += cost_matrix[solution[a]][solution[b]]

    return cost

def plot_solution(name, node_array, solution):
    nodes_array = np.array(node_array)
    plt.scatter(nodes_array[:, 0], nodes_array[:, 1], s=200)
    for i in range(len(nodes_array)):
        plt.annotate(i, (nodes_array[i, 0] + 0.15, nodes_array[i, 1] + 0.15), size=16, color='r')

    plt.xlim([min(nodes_array[:, 0]) - 1, max(nodes_array[:, 0]) + 1])
    plt.ylim([min(nodes_array[:, 1]) - 1, max(nodes_array[:, 1]) + 1])
    for i in range(len(solution)):
        a = i%len(solution)
        b = (i+1)%len(solution)
        A = solution[a]
        B = solution[b]
        plt.plot([nodes_array[A, 0], nodes_array[B, 0]], [nodes_array[A, 1], nodes_array[B, 1]], c='r')

    cost = calculate_cost(get_tsp_matrix(nodes_array), solution)
    title_string = "Cost:" + str(cost)
    title_string += "\n" + str(solution)
    plt.title(title_string)
    plt.savefig('figures/' + name + '.png')
    plt.clf()

def points_order_to_binary_state(points_order):
    """
    Transforms the order of points from the standard representation: [0, 1, 2],
    to the binary one: [1,0,0,0,1,0,0,0,1]
    """
    number_of_points = len(points_order)
    binary_state = np.zeros((len(points_order) - 1)**2)
    for j in range(1, len(points_order)):
        p = points_order[j]
        binary_state[(number_of_points - 1) * (j - 1) + (p - 1)] = 1
    return binary_state

def binary_state_to_points_order(binary_state):
    """
    Transforms the the order of points from the binary representation: [1,0,0,0,1,0,0,0,1],
    to the binary one: [0, 1, 2]
    """
    points_order = []
    number_of_points = int(np.sqrt(len(binary_state)))
    for p in range(number_of_points):
        for j in range(number_of_points):
            if binary_state[(number_of_points) * p + j] == 1:
                points_order.append(j)
    return points_order


import numpy as np
from bootstrap_analyses.boostrap_statistical_analyses.one_sample.submodules.permutation import draw_perm_reps

def diff_between_series(series1, series2, reducer=np.mean):

    return reducer(series1) - reducer(series2)

def frac_of_cases(series1, series2=None):
    """
    Description:

        Compute fraction True cases from given data series

    Params:
        series1: an array of True and False cases:

        series2: it is not used. It is only there,
                 so that the draw_perm_reps can work without major changes

    """
    frac = series1[series1==True].size / (series1.size)
    return frac

def diff_frac(data_A, data_B):
    frac_A = frac_of_cases(data_A)
    frac_B = frac_of_cases(data_B)

    return frac_B - frac_A

def eval_permutation_pvalue(series_A, series_B,
                            reducer=diff_frac,
                            n_samples=10000):

    diff_frac_obs = reducer(series_A, series_B)

    perm_replicates = np.empty(n_samples)

    for i in range(n_samples):
        perm_replicates[i] = draw_perm_reps(series_A,
                                            series_B,
                                            reducer,
                                            size=1)

    p_value = np.mean(perm_replicates >= diff_frac_obs)

    return {'p_value':p_value, 'perm_replicates':perm_replicates,'empirical_diff':diff_frac_obs}

if '__main__' == __name__:
    print(''' example case of AB test:

        You measure the number of people who click on an ad on your
        company's website before and after changing its color.

        prior: the series of clicks (True, False) prior to the change

        after: the series of clicks (True, False) after the change

        The sum of True represents the amount of clicks on the ad during the
        time evaluated.

        A True represents someone that did click on your ad
        A False represents someone that did not click on your ad

         ''')

    prior = np.array([True] * 153 + [False] * 91)
    after = np.array([True] * 136 + [False] * 35)

    results = eval_permutation_pvalue(prior, after, reducer=diff_frac)

    print('p-value =', results['p_value'])

    ################

    n = '\n'*2 + '-'*40 + '\n'*2
    print(n, """Second case:

              Assume you measure the values of a given variable before and after a given
              update in your sistem.

              To measure the probability of that change having any effect over
              the variable of interest, let's say:
                  The amount of time a person lasts in your website

              We could do a permutation pvalue analyzes as follows:

                  series 1: an array of entries, which each entry represents the amount of time a given
                  person spent in your website prior to a given change

                  series 2: an array of entries, which each entry represents the amout of time
                  that a given person spent after the given change.

              For this particular case, since it is no longer a True/False condition,
              the function to evaluate the potential difference between the two series
              would be something like: diff_between_series case, in which one
              could evaluate the difference of a given parameter of interest
              between the series:

         """,n)

    series1 = np.random.normal(1,2,size=100)
    series2 = np.random.normal(3, 4, size=100)

    reducers = [np.mean, np.std, np.var, lambda x: np.percentile(x, 25)]

    for reducer in reducers:

        results = eval_permutation_pvalue(prior, after, reducer=diff_frac)

        print(reducer.__name__, 'pvalue: {0:.3f}'.format( results['p_value']))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 13 15:39:22 2019

@author: alankar
"""

import numpy as np
import matplotlib.pyplot as plt
from basic_units import radians, sin

"""
u = np.random.uniform(low=0.,high=1.,size=1000)
x = np.piecewise(u,[u<=0.5],[lambda u: np.arccos(1-2*u),lambda u: np.pi-np.arccos(2*u-1)])
plt.hist(x,normed=True)
x = np.linspace(0,np.pi,100)
plt.plot(x,0.5*np.sin(x))
"""

def azim_draw(N):#radians
    u = np.random.uniform(low=0.,high=2*np.pi,size=N)
    return u

def polar_draw(N):#radians
    u = np.random.uniform(low=0.,high=1.,size=N)
    x = np.piecewise(u,[u<=0.5],[lambda u: np.arccos(1-2*u),lambda u: np.pi-np.arccos(2*u-1)])
    return x

phi = [val*radians for val in np.arange(0, 2*np.pi, 0.01)]
theta = [val*radians for val in np.arange(0, np.pi, 0.01)]
N = int(1e5)

plt.figure(figsize=(13,10))
plt.plot(phi,(1/(2*np.pi))*np.ones(len(phi)),xunits=radians,color='black')
plt.hist(azim_draw(N),normed=True,bins=20,color='orange')
plt.grid()
plt.ylabel(r'Probability',size=18)
plt.xlabel(r'Angle (rad)',size=20)
plt.title(r'Distribution of Azimuthal Angle', size=21)
plt.tick_params(axis='both', which='major', labelsize=18)
plt.tick_params(axis='both', which='minor', labelsize=15)
plt.savefig('10_1.png')
plt.show()

plt.figure(figsize=(13,10))
plt.plot(theta,np.array(sin(theta))/2,xunits=radians,color='black')
plt.hist(polar_draw(N),normed=True,bins=20,color='orange')
plt.grid()
plt.ylabel(r'Probability',size=18)
plt.xlabel(r'Angle (rad)',size=20)
plt.title(r'Distribution of Polar Angle', size=21)
plt.tick_params(axis='both', which='major', labelsize=18)
plt.tick_params(axis='both', which='minor', labelsize=15)
plt.savefig('10_2.png')
plt.show()


# poly
# The poly tool returns the coefficients of a polynomial with the given sequence of roots.
# print numpy.poly([-1, 1, 1, 10])        #Output : [  1 -11   9  11 -10]

# roots
# The roots tool returns the roots of a polynomial with the given coefficients.
# print numpy.roots([1, 0, -1])           #Output : [-1.  1.]

# polyint
# The polyint tool returns an antiderivative (indefinite integral) of a polynomial.
# print numpy.polyint([1, 1, 1])          #Output : [ 0.33333333  0.5         1.          0.        ]

# polyder
# The polyder tool returns the derivative of the specified order of a polynomial.
# print numpy.polyder([1, 1, 1, 1])       #Output : [3 2 1]

# polyval
# The polyval tool evaluates the polynomial at specific value.
# print numpy.polyval([1, -2, 0, 2], 4)   #Output : 34

# polyfit
# The polyfit tool fits a polynomial of a specified order to a set of data using a least-squares approach.
# print numpy.polyfit([0,1,-1, 2, -2], [0,1,1, 4, 4], 2)
# #Output : [  1.00000000e+00   0.00000000e+00  -3.97205465e-16]

# Task :
# You are given the coefficients of a polynomial.
# Your task is to find the value of at point .

import numpy as np

P = list(map(float, input().split()))
P = np.array(P)
x = int(input())
print(np.polyval(P, x), end="")

# Sample Input
# 1.1 2 3
# 0

# Sample Output
# 3.0


import numpy as np
import matplotlib.pyplot as plt

class SolveMinProbl:
    def __init__(self, y, A):  # 111 and identity matrix
        self.matr = A  # matrix A allocating memory for parameters ST we don't need to call
        self.Np = y.shape[0]  # rows shape tell us the shape of the matrix
        self.Nf = A.shape[1]  # columns
        self.vect = y  # col vector
        self.sol = np.zeros((self.Nf, 1), dtype=float)  # column vector w solution
        self.min = 0.0
        self.err = 0
        return
    def plot_w(self,title = 'solution'):
        w = self.sol  # already initialized self.sol in the previous method
        n = np.arange(self.Nf)
        plt.figure()
        plt.plot(n, w)
        plt.xlabel('n')
        plt.ylabel('w(n)')
        plt.grid()
        plt.title(title)
        plt.show()
        return
    def print_result(self , title):
        print(title, ' :')
        print('the optimum weight vector is: ')
        print(self.sol)  # w
        return
    def plot_err(self, title='Square error', logy=1, logx=0):
        err = self.err
        plt.figure()
        if(logy == 0) & (logx == 0):
            plt.plot(err[:, 0], err[:, 1])
        if (logy == 1) & (logx == 0):
            plt.semilogy(err[:, 0], err[:, 1])
        if (logy == 0) & (logx == 1):
            plt.semilogx(err[:, 0], err[:, 1])
        if (logy == 1) & (logx == 1):
            plt.loglog(err[:, 0], err[:, 1])
        plt.xlabel('n')
        plt.ylabel('e(n)')
        plt.title(title)
        plt.margins(0.01, 0.1)
        plt.grid()
        plt.show()
        return
class SolveLLS(SolveMinProbl):  # this class belongs to SolveMinProbl
    def run(self):  # the inpu
        A = self.matr
        y = self.vect
        w = np.dot(np.dot(np.linalg.inv(np.dot(A.T, A)), A.T), y)
        self.sol = w
        self.min = np.linalg.norm(np.dot(A, w)-y)
class SolveGrad(SolveMinProbl):
    def run(self, gamma, Nit):
        self.err = np.zeros((Nit, 2), dtype=float)
        A = self.matr
        y = self.vect
        w = np.random.rand(self.Nf, 1)  # uniform pdf random var range 0,1
        for it in range(Nit):
            grad = 2*np.dot(A.T, (np.dot(A, w)-y))
            w = w - gamma * grad
            self.err[it, 0] = it
            self.err[it, 1] = np.linalg.norm(np.dot(A, w)-y)**2/self.Np
        self.sol = w
        self.min = self.err[it, 1]
class SolveSteepDesc(SolveMinProbl):
    def run(self, Nit):
        self.err = np.zeros((Nit, 2), dtype=float)
        A = self.matr
        y = self.vect
        w = np.random.rand(self.Nf, 1)  # uniform pdf random var range 0,1
        it = 0
        for it in range(Nit):
            grad = 2 * np.dot(A.T, (np.dot(A, w) - y))
            H = 4 * np.dot(A.T, A)
            gamma2 = (np.linalg.norm(grad)**2)/np.dot(np.dot(grad.T, H), grad)
            w = w - gamma2 * grad
            self.err[it, 0] = it
            self.err[it, 1] = np.linalg.norm(np.dot(A, w) - y)**2/self.Np
        self.sol = w
        self.min = self.err[it, 1]
class SolveStoch(SolveMinProbl):
    def run(self, Nit, gamma):
        self.err = np.zeros((Nit, 2), dtype=float)
        A = self.matr
        y = self.vect
        w = np.random.rand(self.Nf, 1)  # uniform pdf random var range 0,1
        it = 0
        row = np.zeros((1, self.Nf), dtype=float)
        for it in range(Nit):
            for i in range(self.Np):
                for j in range(self.Nf):
                    row[0, j] = A[i, j]
                grad = 2 * np.dot(row.T,(np.dot(row, w) - y[i]))
                w = w - gamma * grad
            self.err[it, 0] = it
            self.err[it, 1] = np.linalg.norm(np.dot(A, w) - y)**2/self.Np
        self.sol = w
        self.min = self.err[it, 1]
class SolveMini(SolveMinProbl):
    def run(self, Nit, N, gamma): #N is number of minibatches
        if (N > self.Np) | (self.Np % N != 0):
            print("function not valid")
        self.err = np.zeros((Nit, 2), dtype=float)
        A = self.matr
        y = self.vect
        w = np.random.rand(self.Nf, 1)  # uniform pdf random var range 0,1
        it = 0
        m = int(self.Np/N)
        for it in range(Nit):
            for i in range(self.Nf):
                for j in range(N):
                    grad = 2 * np.dot(A[j:(j*m+m), :].T, (np.dot(A[j:(j*m+m), :], w) - y[j:(j*m+m)]))
                    w = w - gamma * grad
            self.err[it, 0] = it
            self.err[it, 1] = np.linalg.norm(np.dot(A, w) - y)**2/self.Np
        self.sol = w
        self.min = self.err[it, 1]
class SolveConj(SolveMinProbl):
    def run(self):
        self.err = np.zeros((self.Nf, 2), dtype=float)
        A = self.matr
        y = self.vect
        w = np.zeros((self.Nf, 1), dtype=float)
        it = 0
        Q = 2 * np.dot(A.T, A)
        b = 2 * np.dot(A.T, y)
        g = -b
        d = -g
        for it in range(self.Nf):
            #if np.dot(np.dot(d.T, Q), d) == 0:
            #   break
            a = -1 * np.dot(d.T, g)/np.dot(np.dot(d.T, Q), d)
            w = w + a * d
            g = g + a * np.dot(Q, d)
            beta = np.dot(np.dot(g.T, Q), d)/np.dot(np.dot(d.T, Q), d)
            d = -1 * g + beta*d
            self.err[it, 0] = it
            self.err[it, 1] = np.linalg.norm(np.dot(A, w) - y)**2/self.Np
        self.sol = w
        self.min = self.err[it, 1]

class SolveRidge(SolveMinProbl):
    def run(self, lamb=0.5):
        A = self.matr
        y = self.vect
        I = np.eye(self.Nf)
        w = np.dot(np.dot(np.linalg.inv(np.dot(A.T, A)+lamb*I), A.T), y)
        self.sol = w
        self.min = np.linalg.norm(np.dot(A, w) - y)


import numpy as np
from scipy.spatial.distance import cdist
import random

random.seed(25)

class KMeans:
    def __init__(
            self,
            k: int,
            metric: str = "euclidean",
            tol: float = 1e-6,
            max_iter: int = 100):
        """
        inputs:
            k: int
                the number of centroids to use in cluster fitting
            metric: str
                the name of the distance metric to use
            tol: float
                the minimum error tolerance from previous error during optimization to quit the model fit
            max_iter: int
                the maximum number of iterations before quitting model fit
        """
        self.k = k
        self.metric = metric
        self.tol = tol
        self.max_iter = max_iter
        self.centroids = [] # Stores centroids
        self.labels = [] # Stores cluster labels for all points

        assert k>1, "Number of clusters to form (k) must be at least 2"

    def _reinit_labels(self):
        """
        resets cluster labels for all points

        """

        self.labels = []

        return

    def _assign(self, num_obsv: int, dists: np.ndarray, mat: np.ndarray):
        """
        assigns observations in dataset to their closest cluster by referencing a
        precalculated matrix of distances between cluster centroids and observations

        inputs:
            num_obsv: int
                The number of observations in dataset

            dists: np.ndarray
                A 2D matrix where the rows are observations and columns are clusters.
                The i,jth entry represents the Euclidean distance between observation i
                and the centroid of cluster j

            mat: np.ndarray
                A 2D matrix where the rows are observations and columns are features.
        """
        for m in range(num_obsv):

            # Retrieve distances between observation m and current centroids
            m_dist = dists[m].tolist()

            # Figure out which centroid is currently closest to observation m
            c = m_dist.index(min(m_dist))

            # Label observation m as belonging to the cluster of the centroid to which it's closest
            self.labels.append(c)

        return

    def _recalc_centroids(self, num_fts: int, mat: np.ndarray):
        """
        helper function for the kmeans clustering algorithm that computes new centroids
        based on current cluster assignments

        inputs:

            num_fts: int
                The number of features in each observation

            mat: np.ndarray
                A 2D matrix where the rows are observations and columns are features.

        outputs:
            np.ndarray
                a 2D array with updated centroids for the model
        """
        new_centroids = np.empty(shape=(self.k, num_fts))

        for c in range(self.k):

            points_indices = np.where(np.asarray(self.labels) == c)[0]

            # If no points have been assigned to this cluster, the centroid doesn't
            # have to be updated.
            if len(points_indices) == 0:
                new_centroids[c] = self.centroids[c]

            else:

                # Calculate the new centroid in each cluster
                points = mat[points_indices]
                cluster_size = points.shape[0]
                new_centroids[c] = np.sum(points, axis=0)/cluster_size

        # Return the new centroids
        return new_centroids

    def fit(self, mat: np.ndarray):
        """
        fits the kmeans algorithm onto a provided 2D matrix

        inputs:
            mat: np.ndarray
                A 2D matrix where the rows are observations and columns are features
        """

        self._reinit_labels() # Reinitialize empty labels in case function is run multiple times

        num_obsv = mat.shape[0]
        num_fts = mat.shape[1]

        assert self.k <= num_obsv, "Number of clusters to form exceeds number of observations in dataset"

        # Initialize an empty matrix (k X m) to store centroid locations
        self.centroids = np.empty(shape=(self.k, num_fts))

        # Randomly initialize k centroids
        for i in range(self.k):
            self.centroids[i] = mat[random.randint(0, num_obsv-1)]

        iter_counter = 0

        while True:

            # Compute initial error
            old_error = self.get_error(mat)

            # Reset labels
            self._reinit_labels()

            # Calculate Euclidean distance between all points and existing clusters.
            dists = cdist(mat, self.centroids, self.metric)

            # Iterate through observations, assigning each to the closest cluster
            self._assign(num_obsv, dists, mat)

            # Recalculate centroids after reassigning all points
            self.centroids = self._recalc_centroids(num_fts, mat)

            iter_counter+=1

            # Once we have iterated a maximum number of times OR stopped improving, end loop.
            if (iter_counter >= self.max_iter) or ((old_error - self.get_error(mat)) < self.tol):
                break

        return

    def predict(self, mat: np.ndarray) -> np.ndarray:
        """
        predicts the cluster labels for a provided 2D matrix

        inputs:
            mat: np.ndarray
                A 2D matrix where the rows are observations and columns are features

        outputs:
            np.ndarray
                a 1D array with the cluster label for each of the observations in `mat`
        """
        assert self.centroids.size != 0, "Model must be fit before running predict."

        predictions = []

        # Compute Euclidean distance from each point to the model's existing centroids
        dists = cdist(mat, self.centroids, self.metric)

        num_points = mat.shape[0]

        # Iterate through points, assigning each to the closest cluster
        for m in range(num_points):
            m_dists = dists[m].tolist()
            c = m_dists.index(min(m_dists))
            predictions.append(c)

        return np.array(predictions)

    def get_error(self, mat) -> float:
        """
        returns the final mean-squared error of the fit model

        inputs:
            mat: np.ndarray
                A 2D matrix where the rows are observations and columns are features

        outputs:
            float
                the mean-squared error of the fit model
        """
        errors = []

        for i in range(self.k):

            points_indices = np.where(np.asarray(self.labels) == i)[0]

            # If no points are assigned to the cluster, don't add anything to the error
            if len(points_indices) == 0:
                pass

            else:
                points = mat[points_indices]
                cluster_size = points.shape[0]
                centroid = np.reshape(np.sum(points, axis=0)/cluster_size, (1, mat.shape[1]))

                # Calculate the distance from each point to its corresponding centroid.
                dists = cdist(points, centroid, self.metric)

                # Square and store the distances
                errors = errors + [x**2 for x in dists.flatten().tolist()]

        # If points have not been assigned to clusters, return a large error
        if len(errors) == 0:
            return np.inf

        else:
            # Average the squared distances.
            return sum(errors)/len(errors)

    def get_centroids(self) -> np.ndarray:
        """
        returns the centroid locations of the fit model

        outputs:
            np.ndarray
                a `k x m` 2D matrix representing the cluster centroids of the fit model
        """
        return self.centroids


import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
import seaborn as sns

def get_kernels():
    kernels = []
    kernels.append(('Identity',
                     np.array([[0, 0, 0],
                               [0, 1, 0],
                               [0, 0, 0]])))
    kernels.append(('Edge Detection1',
                     np.array([[1, 0, -1],
                               [0, 0, 0],
                               [-1, 0, 1]])))
    kernels.append(('Edge Detection2',
                     np.array([[0, 1, 0],
                               [1, -4, 1],
                               [0, 1, 0]])))
    kernels.append(('Edge Detection3',
                     np.array([[-1, -1, -1],
                               [-1, 8, -1],
                               [-1, -1, -1]])))
    kernels.append(('Sharpen',
                     np.array([[0, -1, 0],
                               [-1, 5, -1],
                               [0, -1, 0]])))
    kernels.append(('Box Blur',
                     np.array([[1/9, 1/9, 1/9],
                             [1/9, 1/9, 1/9],
                             [1/9, 1/9, 1/9]])))
    kernels.append(('Gaussian Blur',
                     np.array([[1/16, 1/8, 1/16],
                               [1/8, 1/4, 1/8],
                               [1/16, 1/8, 1/16]])))
    return kernels

def pad_img(img, kernel):
    pad_height = (kernel.shape[0] - 1) // 2
    pad_width = (kernel.shape[1] - 1) // 2

    if len(img.shape) == 2:
        return np.pad(img, ((pad_height, pad_height),
                            (pad_width, pad_width)), 'constant')
    return np.pad(img, ((pad_height, pad_height),
                        (pad_width, pad_width),
                        (0, 0)), 'constant')

def convolve2D(img, kernel):
    is_gray_scale = len(img.shape) == 2

    if is_gray_scale:
        img = np.expand_dims(img, axis=2)

    img = pad_img(img, kernel)

    height, width = img.shape[:2]

    new_img = []

    for i in range(height - kernel.shape[0] + 1):
        row = []
        for j in range(width - kernel.shape[1] + 1):
            channels = []
            for k in range(img.shape[2]):
                slice = img[i:i+kernel.shape[0], j:j+kernel.shape[1], k]
                channels.append(np.expand_dims(np.sum(slice * kernel, keepdims=True), axis=0))
            row.append(np.concatenate(channels, axis=2))
        new_img.append(np.concatenate(row, axis=1))

    res = np.maximum(np.concatenate(new_img, axis=0), 0).astype('uint8')

    if is_gray_scale:
        return res[:, :, 0]
    return res

def plot_with_kernels(img):
    kernels = get_kernels()
    n_sub_plots = len(kernels)

    plt.figure('kernels', figsize=(20, 20))

    for i, kernel in enumerate(kernels):
        plt.subplot(n_sub_plots, 3, (i*3) + 1)
        plt.text(0.5, 0.5, kernel[0],
                 horizontalalignment='center',
                 verticalalignment='center',
                 fontsize=15)
        plt.axis('off')

        plt.subplot(n_sub_plots, 3, (i * 3) + 2)
        sns.heatmap(kernel[1], annot=True, cmap='YlGnBu')
        plt.axis('off')

        plt.subplot(n_sub_plots, 3, (i+1) * 3)

        img_ = convolve2D(img, kernel[1])

        if len(img_.shape) == 2:
            plt.imshow(img_, cmap='gray')
        else:
            plt.imshow(img_)

        plt.axis('off')
    plt.show()

def imshow(img):
    plt.imshow(img)
    plt.axis('off')
    plt.show()

def get_activations():
    acts = []
    acts.append(('Sigmoid', lambda x: 1/ (1 + np.exp(-x))))
    acts.append(('Hyperbolic Tangent', lambda x: np.tanh(x)))
    acts.append(('Rectified Linear Unit', lambda x: np.maximum(x, 0)))
    return acts

def plot_activations():
    acts = get_activations()
    x = np.arange(-20, 20, 0.01)

    n_sub_plots = len(acts)
    plt.figure('activations', figsize=(20, 15))
    for i in range(n_sub_plots):
        plt.subplot(n_sub_plots, 3, (i*3) + 1)
        plt.text(0.5, 0.5, acts[i][0],
                 horizontalalignment='center',
                 verticalalignment='center',
                 fontsize=15)
        plt.axis('off')

        plt.subplot(n_sub_plots, 3, (i * 3) + 2)
        eq = np.array(Image.open('images/' + acts[i][0] + '.jpg'))
        plt.imshow(eq)
        plt.axis('off')

        plt.subplot(n_sub_plots, 3, (i+1) * 3)
        plt.plot(x, acts[i][1](x))
        plt.xlabel('x')
        plt.ylabel('y')
    plt.show()


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

def SHM(t, A, omega, phi, d):
    return A*np.cos(omega*t + phi) + d

t, y = np.loadtxt("SHM-200g.txt", unpack=True)
sig = np.zeros(len(y))
sig.fill(0.001)

A = (y.max() - y.min())/2.0

cs = CubicSpline(t, y)
pairs = Bisection.find_sign_changes(cs, 0.1, t.min(), t.max())
zeros = Bisection.bisection(cs, pairs, 1E-10, 100)

T = 0

for i in range(1, len(zeros)):
    T += zeros[i] - zeros[i-1]
T = 2*T/(len(zeros) - 1)
print(T)

phi = np.arccos(y[0]/A)
omega = 2*np.pi/T
print(A, omega, phi)

theta_0 = [A, omega, phi, 0.0]
theta, cov = curve_fit(SHM, t, y, p0=theta_0, sigma=sig)
print(theta)
print(np.sqrt(np.diag(cov)))
cor = np.zeros_like(cov)

for i in range(0, len(theta)):
    for j in range(0, len(theta)):
        cor[i][j] = cov[i][j]/np.sqrt(cov[i][i]*cov[j][j])

print(cor)

t_m = np.linspace(t.min(), t.max(), 1000)
y_m = SHM(t_m, theta[0], theta[1], theta[2], theta[3])

plt.errorbar(t, y, sig, fmt = ".")
plt.plot(t_m, y_m)
plt.show()


import numpy as np

from modpy.design._design_util import _scale_factorial_design

def full_factorial_design(joint, level=2, scale=False):
    """
    Create a full factorial design sampling 2 ** n values, one for each vertex of the hypercube.

    Parameters
    ----------
    joint : JointDistribution
        Class JointDistribution.
    level : int
        Level of investigation, 2: min & max, 3: min, mode & max
    scale : boolean
        If true, scale design to the support (and possibly reference value) of the distributions

    Returns
    -------
    design : array_like, shape (2 ** n, n)
        Design matrix of the form 'number of runs' x 'number of variables'.
    """

    if level not in (2, 3):
        raise ValueError('`level` must be either 2 or 3.')

    D = ff2n(len(joint.marginals))

    if scale:
        D = _scale_factorial_design(D, joint, level=level)

    return D

def fullfact(levels):
    """
    Create a full factorial design sampling an arbitrary number of levels per factor.

    NOTE: Inspired from https://github.com/tirthajyoti/Design-of-experiment-Python/blob/master/doe_factorial.py

    Parameters
    ----------
    levels : array_like
        Array of levels at which to sample each factor

    Returns
    -------
    D : array_like
        Design matrix
    """

    k = len(levels)  # number of factors
    ne = np.prod(levels)  # number of experiments
    D = np.zeros((ne, k))

    lvl_rep = 1
    ran_rep = ne
    for i in range(k):

        ran_rep //= levels[i]  # integer floor division
        lvl = []

        for j in range(levels[i]):
            lvl += [j] * lvl_rep

        D[:, i] = lvl * ran_rep
        lvl_rep *= levels[i]

    return D

def ff2n(n):
    """
    Create a full factorial design with 2 levels sampling `n` variables.

    Parameters
    ----------
    n : int
        Number of variables in the experiment

    Returns
    -------
    D : array_like
        Design matrix
    """

    return 2 * fullfact([2] * n) - 1


# -*- coding: utf-8 -*-
"""
Created on Mon Nov  6 22:42:14 2017

@author: SunyoolChae
"""

import pandas as pd
import numpy as np

df = pd.read_csv(
    filepath_or_buffer='https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data',
    header=None,
    sep=',')

df.columns = ['sepal_len', 'sepal_wid', 'petal_len', 'petal_wid', 'class']

df.head()

X = df.iloc[:,0:4].values
y = df.iloc[:,4].values

#define kernel matrix
def kernel_matrix(x, kernel=None, d=3, sigma=None, C=1.):

    n = x.shape[0]
    if sigma is None:
        sigma = 1./n

    xxt = x.dot(x.T)
    if kernel == 'polynomial':
        return (C + xxt)**d
    elif kernel == 'sigmoid':
        return np.tanh(sigma*xxt + C)
    elif kernel == 'rbf':
        A = x.dot(x.T)
        B = np.repeat(np.diag(xxt), n).reshape(n, n)
        return np.exp(-(B.T - 2*A + B)/(2*sigma**2))
    else:
        return xxt

K = kernel_matrix(X, kernel='polynomial', sigma=0.2)
print(K.shape)
print(K)

n = K.shape[0]
one_mat = np.repeat(1/n, n**2).reshape(n, n)
gram = K - one_mat.dot(K) - K.dot(one_mat) + one_mat.dot(K).dot(one_mat)

eigen_vals, eigen_vecs = np.linalg.eigh(gram)

print("eigen_values \n{}".format(eigen_vals))
print("eigen_vectors \n{}".format(eigen_vecs))

eigen_pairs = [(eigen_vals[i], eigen_vecs[:,i]) for i in range(len(eigen_vals))]
eigen_pairs.sort(key = lambda x: x[0], reverse=True)

from sklearn.decomposition import KernelPCA
kpca0 = KernelPCA(n_components=3, kernel='poly')
kpca0.fit(X)

kpca0_eigen_vecs = kpca0.alphas_
eigen_vecs_for_comparison = np.vstack([eigen_pairs[0][1], eigen_pairs[1][1], eigen_pairs[2][1]]).T

import matplotlib.pyplot as plt
%matplotlib inline

#first eigenvector
plt.subplot(2,1,1)
plt.plot(kpca0_eigen_vecs[:,0], c='blue')
plt.subplot(2,1,2)
plt.plot(eigen_vecs_for_comparison[:,0], c='green')

#Second eigenvector
plt.subplot(2,1,1)
plt.plot(kpca0_eigen_vecs[:,1], c='blue')
plt.subplot(2,1,2)
plt.plot(eigen_vecs_for_comparison[:,1], c='green')

#Third eigenvector
plt.subplot(2,1,1)
plt.plot(kpca0_eigen_vecs[:,2], c='blue')
plt.subplot(2,1,2)
plt.plot(eigen_vecs_for_comparison[:,2], c='green')

# transform data
n_components = 2

transformed_data = []
for j in range(n):
    loc = np.zeros(n_components)
    for k in range(n_components):
        inner_prod_sum = 0.
        for i in range(n):
            inner_prod_sum += eigen_pairs[k][1][i] * gram[j,i]
        loc[k] = inner_prod_sum/np.sqrt(eigen_pairs[k][0])
    transformed_data.append(loc)

transformed_data = np.array(transformed_data)

label = df['class'].unique()
print(label)

with plt.style.context("seaborn-darkgrid"):
    for l in zip(label):
        plt.scatter(transformed_data[y==l,0], transformed_data[y==l,1],
                    label=l)
    plt.xlabel("PC 1")
    plt.ylabel("PC 2")
    plt.legend()
    plt.show()

kpca0 = KernelPCA(n_components=2, kernel='poly')
Y = kpca0.fit_transform(X)

with plt.style.context("seaborn-darkgrid"):
    for l in label:
        plt.scatter(Y[y==l,0], Y[y==l,1],label=l)
    plt.xlabel("PC 1")
    plt.ylabel("PC 2")
    plt.legend()
    plt.show()

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
Y_ = pca.fit_transform(X)

with plt.style.context("seaborn-darkgrid"):
    for l in label:
        plt.scatter(Y_[y==l,0], Y_[y==l,1],label=l)
    plt.xlabel("PC 1")
    plt.ylabel("PC 2")
    plt.legend()
    plt.show()

02 Kernel-based Learning/Tutorial 07 - Kernel PCA/KPCA_Chae_1.py


import openmesh as om
from math import sin, cos
import numpy as np

def translate(mesh, dx=0, dy=0, dz=0):
	for vh in mesh.vertices():
		mesh.point(vh)[0] += dx
		mesh.point(vh)[1] += dy
		mesh.point(vh)[2] += dz
	return mesh

def escalate(mesh, Sx=1, Sy=1, Sz=1):
	for vh in mesh.vertices():
		mesh.point(vh)[0] *= Sx
		mesh.point(vh)[1] *= Sy
		mesh.point(vh)[2] *= Sz
	return mesh

def getGeometricCenter(mesh):
	maxCoord = np.copy(mesh.point(next(mesh.vertices())))
	minCoord = np.copy(maxCoord)

	for vh in mesh.vertices():
		coord = mesh.point(vh)

		if coord[0] < minCoord[0]: minCoord[0] = coord[0]
		if coord[1] < minCoord[1]: minCoord[1] = coord[1]
		if coord[2] < minCoord[2]: minCoord[2] = coord[2]
		if coord[0] > maxCoord[0]: maxCoord[0] = coord[0]
		if coord[1] > maxCoord[1]: maxCoord[1] = coord[1]
		if coord[2] > maxCoord[2]: maxCoord[2] = coord[2]

	return np.array([
		(maxCoord[0] + minCoord[0])/2,
		(maxCoord[1] + minCoord[1])/2,
		(maxCoord[2] + minCoord[2])/2
		])

def rotX(meshIn, angle):

	GC = getGeometricCenter(meshIn)
	mesh = translate(meshIn, -GC[0], -GC[1], -GC[2])

	for vh in mesh.vertices():
		coord = np.copy(mesh.point(vh))
		mesh.point(vh)[1] = coord[1]*cos(angle) - coord[2]*sin(angle)
		mesh.point(vh)[2] = coord[1]*sin(angle) + coord[2]*cos(angle)
		coord = mesh.point(vh)

	return translate(mesh, GC[0], GC[1], GC[2])

def rotY(meshIn, angle):

	GC = getGeometricCenter(meshIn)
	mesh = translate(meshIn, -GC[0], -GC[1], -GC[2])

	for vh in mesh.vertices():
		coord = np.copy(mesh.point(vh))
		mesh.point(vh)[0] = coord[0]*cos(angle) + coord[2]*sin(angle)
		mesh.point(vh)[2] = coord[2]*cos(angle) - coord[0]*sin(angle)

	return translate(mesh, GC[0], GC[1], GC[2])

def rotZ(meshIn, angle):

	GC = getGeometricCenter(meshIn)
	mesh = translate(meshIn, -GC[0], -GC[1], -GC[2])

	for vh in mesh.vertices():
		coord = np.copy(mesh.point(vh))
		mesh.point(vh)[0] = coord[0]*cos(angle) - coord[1]*sin(angle)
		mesh.point(vh)[1] = coord[0]*sin(angle) + coord[1]*cos(angle)

	return translate(mesh, GC[0], GC[1], GC[2])