{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multiplicar matrices, y qué está calculando de verdad\n",
    "\n",
    "Por qué la multiplicación de matrices es esa y no la obvia, cómo leer las formas de un vistazo, y por qué el orden sí importa.\n",
    "\n",
    "Cuaderno de soluciones del capítulo 5 de **Matemática para machine learning**, de Miss Yera.\n",
    "\n",
    "Corre de arriba abajo. Si lo abres en Google Colab no necesitas instalar nada.\n",
    "\n",
    "Capítulo completo: https://missyera.com/guias/matematica-para-machine-learning/multiplicacion-de-matrices/\n",
    "\n",
    "Este es el cuaderno de **soluciones**. Trae el código de cada ejercicio, la\n",
    "explicación de la trampa y la respuesta del quiz. Si vienes del cuaderno de\n",
    "práctica sin haberlo intentado, vuelve 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Antes de empezar"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# Este capitulo abre el archivo por su nombre, sin URL, que es lo que se hace\n",
    "# cuando lo tienes al lado. Aqui se baja primero para que exista.\n",
    "for nombre in ['ventas-miss-yera.csv']:\n",
    "    urllib.request.urlretrieve(f\"https://missyera.com/static/datasets/{nombre}\", nombre)\n",
    "    print(nombre, \"lista\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La operación que parece rara hasta que ves para qué sirve 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Multiplicar matrices es lo que más gente abandona en el colegio, porque te lo\n",
    "enseñan como un procedimiento sin decirte para qué. Así que empecemos al revés:\n",
    "primero para qué sirve, y después cómo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Volvemos a abrir el archivo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cada capítulo arranca de cero, así que estas líneas se repiten. Son las\n",
    "mismas del capítulo 1 y las vas a ver en todos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "df = pd.read_csv('ventas-miss-yera.csv')\n",
    "for c in ('unidades', 'monto', 'satisfaccion'):\n",
    "    df[c] = pd.to_numeric(df[c], errors='coerce')\n",
    "X = df[['unidades', 'monto', 'satisfaccion']].dropna().to_numpy()\n",
    "print('X.shape:', X.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Para qué sirve: dos escenarios a la vez"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el capítulo 4 pusiste un juego de pesos y sacaste una predicción\n",
    "por venta. Ahora quieres **dos** juegos: el tuyo, y otro donde solo\n",
    "importan las unidades y la satisfacción."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "W = np.array([[12.0,   1.0],\n",
    "              [ 0.35,  0.0],\n",
    "              [40.0, 100.0]])\n",
    "print('W.shape:', W.shape)\n",
    "\n",
    "S = X @ W\n",
    "print('S.shape:', S.shape)\n",
    "print(np.round(S[:3], 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2.238 filas por 2 escenarios. La primera columna es el escenario de siempre y\n",
    "la segunda es el nuevo, y salieron los dos en una línea.\n",
    "\n",
    "Eso es multiplicar matrices: **muchos productos punto de golpe**.\n",
    "Cada columna de W es un juego de pesos, cada fila de X es una venta, y cada\n",
    "casilla del resultado es el producto punto de una con otra."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(AB)ij=∑k=1naikbkj\n",
    "\n",
    "cada casilla del resultado es el producto punto de una fila del primero con una columna del segundo, y por eso el número de columnas del primero tiene que ser el de filas del segundo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Símbolo | Qué es |\n",
    "|---|---|\n",
    "| **(AB)ij** | la casilla del resultado en la fila i, columna j |\n",
    "| **aik** | la fila i del primero, recorrida entera |\n",
    "| **bkj** | la columna j del segundo, recorrida entera |\n",
    "| **k** | el índice que barre las dos a la vez y después desaparece |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La regla de las formas, que es lo único que hay que memorizar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(n×d)·(d×k)=(n×k)\n",
    "\n",
    "la d del medio se gasta en la suma y no aparece en el resultado, así que si las dos des no coinciden la multiplicación no existe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tapa con el dedo los dos números del medio. Lo que te queda es la forma del\n",
    "resultado. Si los dos que tapaste no son iguales, la operación no existe.\n",
    "\n",
    "Con nuestro caso: `(2238, 3)` por `(3, 2)` da\n",
    "`(2238, 2)`. El 3 se gastó."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## No es elemento a elemento, y aquí se ve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A = X[:2][:, [0, 2]]\n",
    "B = X[2:4][:, [0, 2]]\n",
    "print('A ='); print(A)\n",
    "print('B ='); print(B)\n",
    "print('A * B ='); print(A * B)\n",
    "print('A @ B ='); print(A @ B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mira la primera casilla de cada uno. Con `*` sale 130, que es\n",
    "10 por 13 y nada más. Con `@` sale 142, que es\n",
    "10 por 13 más 4 por 3.\n",
    "\n",
    "El `*` es **elemento a elemento** y pide que las dos\n",
    "tengan la misma forma. El `@` es el producto de matrices y pide que\n",
    "encajen. Confundirlos es el error número uno, y el peor de todos es cuando las\n",
    "dos formas resultan compatibles con las dos operaciones: ahí no hay error, hay\n",
    "un número equivocado."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El orden importa"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Esto revienta a propósito.** Se ejecuta dentro de un `try` para que puedas seguir con \"ejecutar todo\" y aun así ver la queja."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    W @ X\n",
    "except Exception as e:\n",
    "    print(f'{type(e).__name__}: {e}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2238 is different from 2)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`X @ W` funciona y `W @ X` no. Con vectores el orden\n",
    "daba igual; acá no.\n",
    "\n",
    "Y cuando las dos direcciones sí funcionan, tampoco dan lo mismo. Es la\n",
    "diferencia entre \"aplica estos pesos a estos datos\" y \"aplica estos datos a\n",
    "esos pesos\", que ni siquiera significa nada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Por qué esta operación y no otra"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La pregunta honesta es: si multiplicar casilla con casilla es más fácil, ¿por\n",
    "qué la de verdad es esta?\n",
    "\n",
    "Porque esta es la que **encadena transformaciones**. Si A\n",
    "convierte tus datos en otra cosa y B convierte esa otra cosa en una tercera,\n",
    "entonces `A @ B` es la que hace las dos de una vez.\n",
    "\n",
    "Eso es exactamente lo que es una red neuronal: una capa multiplica por una\n",
    "matriz, la siguiente multiplica por otra, y la red entera es esa cadena. Cuando\n",
    "llegues a deep learning y veas `W1 @ W2 @ W3`, esto es lo que\n",
    "significa 🐣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Cada casilla del resultado es un producto punto de una fila con una\n",
    "columna.\n",
    "\n",
    "- La regla de las formas: tapa los dos del medio, lee lo que queda.\n",
    "\n",
    "- `*` es elemento a elemento y `@` es matricial. No son\n",
    "lo mismo ni por casualidad.\n",
    "\n",
    "- El orden importa: AB casi nunca es BA.\n",
    "\n",
    "- Multiplicar matrices es encadenar transformaciones, y eso es una red por\n",
    "dentro."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Multiplicas A de forma (2238, 3) por B de forma (3, 5). ¿Qué forma tiene el resultado?\n",
    "\n",
    "a) (2238, 5)\n",
    "\n",
    "b) (3, 3)\n",
    "\n",
    "c) (2238, 3), la misma que A\n",
    "\n",
    "d) Da error, porque las formas son distintas\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la a.**\n",
    "\n",
    "*b)* Las que sobreviven son las de los extremos, no las del medio. El 3 es justamente el que desaparece.\n",
    "\n",
    "*c)* Esa sería la forma si multiplicaras elemento a elemento con el asterisco, y para eso B tendría que ser también (2238, 3).\n",
    "\n",
    "*d)* Tienen que ser distintas: lo que hace falta es que el 3 de A sea el mismo 3 de B, y lo es. Si coincidieran del todo sería otra operación.\n",
    "\n",
    "Tapa con el dedo los dos números del medio y lee lo que queda: (2238, ~~3~~) por (~~3~~, 5) deja (2238, 5) 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Una casilla a mano\n",
    "\n",
    "Saca la casilla de arriba a la izquierda tú mismo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A = X[:2][:, [0, 2]]\n",
    "B = X[2:4][:, [0, 2]]\n",
    "\n",
    "a_mano = A[0, 0] * B[0, 0] + A[0, 1] * B[1, 0]\n",
    "print('a mano :', a_mano)\n",
    "print('numpy  :', (A @ B)[0, 0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "a mano : 142.0\n",
    "numpy  : 142.0\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fila 0 de A contra columna 0 de B. Si haces esto una vez con papel, la\n",
    "fórmula de la sigma deja de dar miedo para siempre."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Comprueba que la fila 0 del resultado es lo de siempre\n",
    "\n",
    "El producto de matrices no inventó nada nuevo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "W = np.array([[12.0,   1.0],\n",
    "              [ 0.35,  0.0],\n",
    "              [40.0, 100.0]])\n",
    "S = X @ W\n",
    "print('con matrices :', np.round(S[0], 2))\n",
    "print('a mano col 0 :', round(float(X[0] @ W[:, 0]), 2))\n",
    "print('a mano col 1 :', round(float(X[0] @ W[:, 1]), 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "con matrices : [448.13 410.  ]\n",
    "a mano col 0 : 448.13\n",
    "a mano col 1 : 410.0\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cada casilla es el producto punto que ya sabías hacer. La multiplicación de\n",
    "matrices solo los organiza."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. El error de forma, provócalo tú\n",
    "\n",
    "Antes de correrlo, di en voz alta si va a funcionar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "W = np.array([[12.0,   1.0],\n",
    "              [ 0.35,  0.0],\n",
    "              [40.0, 100.0]])\n",
    "print('X.shape', X.shape, ' W.shape', W.shape)\n",
    "print('X @ W ->', (X @ W).shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "X.shape (2238, 3)  W.shape (3, 2)\n",
    "X @ W -> (2238, 2)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y ahora al revés:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "W @ X"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2238 is different from 2)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuando este error te salga en un proyecto, no adivines: imprime las dos\n",
    "formas y tapa los del medio con el dedo. En treinta segundos sabes cuál de las\n",
    "dos hay que trasponer, y trasponer es el capítulo que viene."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Cómo se multiplican dos matrices?Cada número del resultado es el producto punto de una fila de la primera con una columna de la segunda. Solo se puede si las columnas de la primera son tantas como las filas de la segunda.\n",
    "\n",
    "¿Qué es el producto de matrices?La operación de arriba, y no es multiplicar número a número. Esa otra existe, se llama producto elemento a elemento, y confundirlas es de los errores que no dan error.\n",
    "\n",
    "¿Qué es el álgebra lineal?La parte de la matemática que trabaja con vectores y matrices. Es la que hay debajo de casi todo el machine learning, y hace falta bastante menos de la que se enseña en la universidad.\n",
    "\n",
    "¿La multiplicación de matrices es conmutativa?No. A por B casi nunca es igual a B por A, y muchas veces una de las dos ni siquiera se puede calcular."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 5 de **Matemática para machine learning**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/matematica-para-machine-learning/multiplicacion-de-matrices/\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
