<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Parte 1
Step1:  2. Realizar y verificar la descomposición svd.
Step2:  3. Usar la descomposición para dar una aproximación de grado <code>k</code> de la imagen.</li>
4. Para alguna imagen de su elección, elegir distintos valores de aproximación a la imagen original.
Step3:  Contestar, ¿qué tiene que ver este proyecto con compresión de imágenes?
En este proyecto se planteó una aplicación de la descomposición de una imagen mediante svd y se pudo comprender que esta técnica permite la compresión de información mediante reducción de componentes principales, es decir reducción de columnas en la matriz U, filas o columnas de la matriz S (componentes principales) y filas de la matriz $V^{-1} $
Ejercicio2
Funciones del ejercicio2
Step4:  1. Programar una función que dada cualquier matriz devuelva la pseudoinversa usando la descomposición SVD. Hacer otra función que resuelva cualquier sistema de ecuaciones de la forma Ax=b usando esta pseudoinversa.
Step6:  2. Jugar con el sistema Ax=b donde A=[[1,1],[0,0]] y b puede tomar distintos valores.
(a) Observar que pasa si b esta en la imagen de A (contestar cuál es la imagen) y si no está (ej. b = [1,1]). 
(b) Contestar, ¿la solución resultante es única? Si hay más de una solución, investigar que carateriza a la solución devuelta. 
(c) Repetir cambiando A=[[1,1],[0,1e-32]], ¿En este caso la solucíon es única? ¿Cambia el valor devuelto de x en cada posible valor de b del punto anterior?
Step7:  Ejercicio3
1. Leer el archivo study_vs_sat.csv y almacenearlo como un data frame de pandas.
Step8:  2. Pleantear como un problema de optimización que intente hacer una aproximación de la forma sat_score ~ alpha + beta*study_hours minimizando la suma de los errores de predicción al cuadrado, ¿Cuál es el gradiente de la función que se quiere optimizar (hint
Step9:  3. Programar una función que reciba valores de alpha, beta y el vector study_hours y devuelva un vector array de numpy de predicciones alpha + beta*study_hours_i, con un valor por cada individuo
Step10:  4. Definan un numpy array X de dos columnas, la primera con unos en todas sus entradas y la segunda con la variable study_hours. Observen que X[alpha,beta] nos devuelve alpha + beta * study_hours_i en cada entrada y que entonces el problema se vuelve sat_score ~ X[alpha,beta]
Step11:  5. Calculen la pseudoinversa  X^+ de X y computen (X^+)*sat_score para obtener alpha y beta soluciones.
Step12:  6. Comparen la solución anterior con la de la fórmula directa de solución exacta (alpha,beta)=(X^tX)^(-1)X^t*sat_score
Step13:  La comparación de este método con el anterior es que ambos regresan el ajuste a mínimos cuadrados
7. (Avanzado) Usen la libreria matplotlib para visualizar las predicciones  con alpha y beta solución contra los valores reales de sat_score.
<ASSISTANT_TASK:>
Python Code:
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
#url = sys.argv[1]
url = 'Mario.png'
img = Image.open(url)
imggray = img.convert('LA')
Explanation: Parte 1: Teoría de Algebra Lineal y Optimización
1.  ¿Por qué una matriz equivale a una transformación lineal entre espacios vectoriales?<br>
R: Porque al llevar a cabo la multiplicación de un vector con esta matriz, al vector original se le aplican escalamientos, reducciones, rotaciones y anulamientos en sus componentes transformándolo en otro vector de distintas proporciones y dimensiones, es decir lleva a cabo un mapeo del vector original en su espacio vectorial original hacia otro vector en otro espacio vectorial.<br><br>
2.  ¿Cuál es el efecto de transformación lineal de una matriz diagonal y el de una matriz ortogonal?<br>
R: La transformación lineal que aplica una matriz diagonal hacia un vector columna o una matriz es el escalamiento de cada uno de los vectores columna y se va a dar de acuerdo a la posición de los elementos de la diagonal. 
La transformación lineal que aplica una matriz ortogonal hacia un vector columna o una matriz, es una transformación isométrica que puede ser cualquiera de tres tipos: traslación, espejeo y rotación. Tienen determinante +-1<br><br>
3.  ¿Qué es la descomposición en valores singulares de una matriz?<br>
R: Es la factorización de una matriz en la multiplicación de tres matrices fundamentales que son la matriz de eigenvectores, la matriz de componentes singulares y la matriz transpuesta de eigenvectores. La descomposicion se lleva a cabo de la siguiente manera: $$ A = U \Sigma V^{T} $$
En donde U = matriz de eigenvectores, $\Sigma$ = matriz de valores singulares o raices cuadradas de eigenvalores de $A^{T}A$  y $V^{T}$ = matriz transpuesta de eigenvectores por derecha. 
La descomposición svd tiene muchas aplicaciones en las áreas de reducción de dimensiones, análisis de componentes principales (PCA), compresión de imágenes, aproximacion a solucion de sistemas de ecuaciones sin soluciones unicas mediante minimos cuadrados. <br><br>
4.  ¿Qué es diagonalizar una matriz y que representan los eigenvectores?<br>
R: La diagonalización de una matriz es la factorización de la misma en la multiplicación de tres matrices básicas que son la matriz de eigenvectores, la matriz de eigenvalores y la matriz inversa de los eigenvectores de la misma. De la siguiente manera: $$A = PDP^{-1}$$En donde P = matriz de eigenvectores, D = Matriz diagonal de eigenvalores y $P^{-1}$ = matriz inversa de eigenvectores. 
Los eigenvectores de una transformación lineal son vectores que cumplen con la condición de que el resultado de multiplicarlos por la matriz de transformación equivale a multiplicarlos por un escalar llamado eigenvalor. De la siguiente manera: $$ Ax = \lambda x $$ Esto quiere decir que a estos vectores al aplicarles la transformación lineal (multiplicarlos por la matriz de transformación) no cambian su dirección solamente cambian su longitud o su sentido, en caso de que haya eigenvalor con signo negativo. <br><br>
5.  ¿Intuitivamente que son los eigenvectores?<br>
R: Se pueden interpretar como ejes cartesianos de la transformación lineal, cumpliendo el teorema espectral que afirma:  $$ \tau \vec (v) = \lambda_{1}(\vec v_{1} \bullet v)v_{1} + \lambda_{2}(\vec v_{2} \bullet v)v_{2} + .... + \lambda_{n}(\vec v_{n} \bullet v)v_{n} $$ <br><br>
6.  ¿Cómo interpretas la descomposición en valores singulares como una composición de tres tipos de transformaciones lineales simples?<br>
R: Las tres matrices que conforman la SVD se pueden ver como transformaciones simples que son una rotación inicial, un escalamiento a lo largo de los ejes principales (valores singulares) y una rotación final. <br><br>
7.  ¿Qué relación hay entre la descomposición en valores singulares y la diagonalización?<br>
R: La descomposición svd es una generalización de la diagonalización de una matriz. Cuando una matriz no es cuadrada, no es diagonalizable, sin embargo si se puede descomponer mediante svd. Así mismo si se utiliza la descomposición svd de una matriz, es posible resolver sistemas de ecuaciones lineales que no tengan una única solución sino que la solución devuelta es el ajuste a mínimos cuadrados, (obviamente si tienen solución, regresa la solución al sistema).  <br><br>
8.  ¿Cómo se usa la descomposición en valores singulares para dar una aproximación de rango menor a una matriz? <br>
R: En la descomposición svd de una matriz mxn, se obtiene una descomposición de tres matrices cuya multiplicación resulta en la matriz original completa, sin embargo dadas las propiedades de la descomposición, se pueden utilizar solamente las componentes principales de mayor relevancia desechando columnas de la matriz $U_{mxr}$, filas o columnas de la matriz de componentes principales $\Sigma {rxr} $ y filas de la matriz $V{rxn}^{T}$ La multiplicación de estas resulta en el tamaño mxn de la matriz original pero con un determinado error y a medida que se consideran más componentes principales, columnas y vectores de las matrices U,S,VT , mejor será la reconstrucción de la matriz original. Esta descomposición es muy útil para comprimir información y análisis de componentes principales (PCA).<br><br>
9.  Describe el método de minimización por descenso gradiente<br>
R: Es un método iterativo de primer orden de minimización de una función dado su gradiente. Conociendo la función a optimizar, se calcula el gradiente (vector de derivadas parciales), posteriormente se considera un punto aleatorio de inicio, se substituye la coordenada en el vector gradiente, se analiza en cuál de las componentes del vector gradiente (x, y, o z)  es el valor más negativo y se da un pequeño incremento en esa dirección de manera que se acerca al mínimo local, y así sucesivamente hasta llegar a un punto de convergencia (mínimo local). Entre sus aplicaciones están: Encontrar mínimos locales de funciones, Solución de sistemas de ecuaciones lineales y solución de sistemas de ecuaciones no lineales. <br><br> 
10. Menciona 4 ejemplos de problemas de optimización(dos con restricciones y dos sin restricciones) que te parezcan interesantes como científico de datos<br>
R: Con restricciones: Optimización de la eficiencia energética en el diseño de un motor eléctrico dadas restricciones geométricas, termodinámicas, electromagnéticas, etc. Algunas de las variables a considerar en el diseño de un motor eléctrico son: número de pares de polos, proporción del diámetro interno del estator contra el diámetro externo, porcentaje de la profundidad total del rotor para la sección principal céntrica del rotor, entre otras, todas estas variables forman un vector, este vector es la entrada a un conjunto de ecuaciones objetivo y las restricciones también se modelan como ecuaciones. <br>
Este tipo de optimización es del tipo multi-objetivo y el tipo de métodos más adecuados para implementar son la optimización de Pareto. 
Vector con variables optimas:
$ \vec x = [x_{1},x_{2},x_{1},....,x_{D} ]  \vec x \in R^{D}$
Restricciones de frontera $x_{i}^{L} \leq x_{i} \leq x_{i}^{U}, i=1, ...., D$
Restricciones de ecuaciones $g_{j}(\vec x ) \leq 0, j=1,....,m$
Función objetivo $ f( \vec x ) = [f_{1}( \vec x), f_{2}( \vec x), .... f_{k}( \vec x)]$
Referencia: http://www.adept-itn.eu/images/Publications/Optimization_in_Design_of_Electric_Machines_Methodology_and_Workflow.pdf
B.  Optimización de la red eléctrica inteligente (Smart Grid): 
La red eléctrica inteligente es la evolución de la red eléctrica actual, en donde se integran nuevas fuentes de generación eléctrica (energías renovables), productores caseros, nuevos equipos de transmisión y almacenamiento, medidores, sensores, infraestructura de comunicaciones, cómputo y algoritmos de gestión energética. La red eléctrica inteligente tiene tres objetivos principales que son: 
1.  Mejorar la confiabilidad del servicio eléctrico (menos fallas del servicio).
2.  Reducir demandas pico (Tener un consumo lo más estable posible).
3.  Reducir el consumo total de energía (Tener un consumo lo más bajo posible). <br>
El primer objetivo plantea un problema de maximización del tiempo de servicio así como maximización de la calidad de la energía.
El segundo objetivo se plantea como una minimización de demanda pico.
El tercer objetivo también es un problema de minimización. 
Referencia: https://file.scirp.org/pdf/AJOR_2013013014361193.pdf <br>
Sin restricciones:
C.  Encontrar el consumo energético máximo de una fábrica en kwh teniendo registros de consumo energético en medios digitales generados por medidores eléctricos inteligentes.
D.  Encontrar la menor cantidad de unidades de producto para satisfacer una demanda con forma paraboloide (logística).
Parte2: Aplicaciones en Python
1. Recibir el path de un archivo de una imagen y convertirlo en una matriz numérica que represente la versión en blanco y negro de la imagen
End of explanation
imggrayArray = np.array(list(imggray.getdata(band=0)), float)
imggrayArray.shape = (imggray.size[1], imggray.size[0])
imggrayArray = np.matrix(imggrayArray)
plt.imshow(imggray)
plt.show()
u, s, v = np.linalg.svd(imggrayArray)
print("La matriz U es: ")
print(u)
print("La matriz S es: ")
print(s)
print("La matriz V es: ")
print(v)
Explanation: 2. Realizar y verificar la descomposición svd.
End of explanation
for i in range(1,50,10):
    reconstimg = np.matrix(u[:, :i]) * np.diag(s[:i]) * np.matrix(v[:i,:])
    plt.imshow(reconstimg, cmap='gray')
    plt.show()
Explanation: 3. Usar la descomposición para dar una aproximación de grado <code>k</code> de la imagen.</li>
4. Para alguna imagen de su elección, elegir distintos valores de aproximación a la imagen original.
End of explanation
def lin_solve_pseudo(A,b): 
    pseudoinv = pseudoinverse(A)
    return np.matmul(pseudoinv,b)
def pseudoinverse(A): 
    u,s,v = np.linalg.svd(A)            
    diagonal = np.diag(s)
    if v.shape[0] > diagonal.shape[1]:       
        print("Agregando columnas a la sigma")
        vector = np.array([[0 for x in range(v.shape[0] - diagonal.shape[1])] for y in range(diagonal.shape[0])])                      
        diagonal = np.concatenate((diagonal, vector), axis=1)
    elif u.shape[1] > diagonal.shape[0]:
        print("Agregando renglones a la sigma")
        vector = np.array([[0 for x in range(diagonal.shape[0])] for y in range(u.shape[1]-diagonal.shape[0])])
        diagonal = np.concatenate((diagonal, vector), axis=0)
        
    for a in range(diagonal.shape[0]):
        for b in range(diagonal.shape[1]):
            if diagonal[a][b] != 0:
                diagonal[a][b] = 1/diagonal[a][b]
                
    resultante = np.dot(np.transpose(v),np.transpose(diagonal))
    resultante = np.dot(resultante,np.transpose(u))
    return resultante
    
Explanation: Contestar, ¿qué tiene que ver este proyecto con compresión de imágenes?
En este proyecto se planteó una aplicación de la descomposición de una imagen mediante svd y se pudo comprender que esta técnica permite la compresión de información mediante reducción de componentes principales, es decir reducción de columnas en la matriz U, filas o columnas de la matriz S (componentes principales) y filas de la matriz $V^{-1} $
Ejercicio2
Funciones del ejercicio2
End of explanation
A = np.array([[1,1,1],[1,1,3],[2,4,4]])
b = np.array([[18,30,68]])
solve = lin_solve_pseudo(A,np.transpose(b))
print(solve)
Explanation: 1. Programar una función que dada cualquier matriz devuelva la pseudoinversa usando la descomposición SVD. Hacer otra función que resuelva cualquier sistema de ecuaciones de la forma Ax=b usando esta pseudoinversa.
End of explanation
print("(a)")
print("La imagen de A es cualquier vector de dos coordenadas en donde la segunda componente siempre sea cero")
print("Vector b en imagen de A")
A = np.array([[1,1],[0,0]])
b = np.array([[12,0]])
solve = lin_solve_pseudo(A, np.transpose(b))
print(solve)
print("Cuando b esta en la imagen, la funcion lin_solve_pseudo devuelve la solucion unica a su sistema")
print("Vector b no en imagen de A")
b = np.array([[12,8]])
solve = lin_solve_pseudo(A, np.transpose(b))
print(solve)
print("Cuando b no esta en la imagen, devuelve la solucion mas aproximada a su sistema")
print("(c)")
A = np.array([[1,1],[0,1e-32]])
b = np.array([[12,9]])
solve = lin_solve_pseudo(A, np.transpose(b))
print(solve)
cadena = En este caso, la solucion devuelta siempre es el valor de la segunda coordenada del vector b por e+32\
             y es el valor de ambas incognitas, solo que con signos contrarios ej(x1=-9.0e+32, x2=9.0e+32) \
             esto debido a que cualquier numero entre un numero muy pequenio tiende a infinito, de manera que la \
             coordenada dos del vector tiene mucho peso con referencia a la coordenada uno del vector
         
print(cadena)
Explanation: 2. Jugar con el sistema Ax=b donde A=[[1,1],[0,0]] y b puede tomar distintos valores.
(a) Observar que pasa si b esta en la imagen de A (contestar cuál es la imagen) y si no está (ej. b = [1,1]). 
(b) Contestar, ¿la solución resultante es única? Si hay más de una solución, investigar que carateriza a la solución devuelta. 
(c) Repetir cambiando A=[[1,1],[0,1e-32]], ¿En este caso la solucíon es única? ¿Cambia el valor devuelto de x en cada posible valor de b del punto anterior?
End of explanation
import pandas as pd
import matplotlib.pyplot as plt
data = pd.read_csv("./study_vs_sat.csv", sep=',')
print(data)
Explanation: Ejercicio3
1. Leer el archivo study_vs_sat.csv y almacenearlo como un data frame de pandas.
End of explanation
hrs_studio = np.array(data["study_hours"])
sat_score = np.array(data["sat_score"])
A = np.vstack([hrs_studio, np.ones(len(hrs_studio))]).T
m,c = np.linalg.lstsq(A,sat_score)[0]
print("Beta y alfa: ")
print(m,c)
Explanation: 2. Pleantear como un problema de optimización que intente hacer una aproximación de la forma sat_score ~ alpha + beta*study_hours minimizando la suma de los errores de predicción al cuadrado, ¿Cuál es el gradiente de la función que se quiere optimizar (hint: las variables que queremos optimizar son alpha y beta)?
End of explanation
def predict(alfa, beta, study_hours):
    study_hours_i=[]
    for a in range(len(study_hours)):
        study_hours_i.append(alfa + beta*np.array(study_hours[a]))
    return study_hours_i
print("prediccion")
print(predict(353.165, 25.326, hrs_studio))
Explanation: 3. Programar una función que reciba valores de alpha, beta y el vector study_hours y devuelva un vector array de numpy de predicciones alpha + beta*study_hours_i, con un valor por cada individuo
End of explanation
unos = np.ones((len(hrs_studio),1))
hrs_studio = [hrs_studio]
hrs_studio = np.transpose(hrs_studio)
x = np.hstack((unos, hrs_studio))
print("La prediccion es: ")
print(np.matmul(x,np.array([[353.165],[25.326]])))
Explanation: 4. Definan un numpy array X de dos columnas, la primera con unos en todas sus entradas y la segunda con la variable study_hours. Observen que X[alpha,beta] nos devuelve alpha + beta * study_hours_i en cada entrada y que entonces el problema se vuelve sat_score ~ X[alpha,beta]
End of explanation
X_pseudo = pseudoinverse(x)
print("Las alfas y betas son: ")
print(np.matmul(X_pseudo,sat_score))
Explanation: 5. Calculen la pseudoinversa  X^+ de X y computen (X^+)*sat_score para obtener alpha y beta soluciones.
End of explanation
def comparacion(X, sat_score):
    x_transpose = np.transpose(X)    
    return np.matmul(np.linalg.inv(np.matmul(x_transpose,X)), np.matmul(x_transpose,sat_score))
#x = np.array([[1,1],[1,2],[1,3]])
#y = np.array([5,6,7])
x = np.hstack((unos, hrs_studio))
print(comparacion(x, sat_score))
Explanation: 6. Comparen la solución anterior con la de la fórmula directa de solución exacta (alpha,beta)=(X^tX)^(-1)X^t*sat_score
End of explanation
plt.plot(hrs_studio, sat_score, 'o', label='Original data', markersize=10)
plt.plot(hrs_studio, m*hrs_studio + c, 'r', label='Fitted line')
plt.legend()
plt.show()
Explanation: La comparación de este método con el anterior es que ambos regresan el ajuste a mínimos cuadrados
7. (Avanzado) Usen la libreria matplotlib para visualizar las predicciones  con alpha y beta solución contra los valores reales de sat_score.
End of explanation
<END_TASK>