{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "609eace0",
      "metadata": {
        "id": "609eace0"
      },
      "source": [
        "# QPrep — Sesión 3: Matrices, producto tensorial y puente a QBronze\n",
        "\n",
        "Esta sesión cubre la segunda mitad de **Basic Math** antes de QBronze: matrices, transpuesta, multiplicación matricial, producto tensorial y una introducción mínima a operadores sobre estados. También se repasan expresiones de Python usadas en preguntas de quiz: indexación, *slicing*, módulo y potencias.\n",
        "\n",
        "**Fuentes curriculares usadas para el diseño:** sección `math` de Bronze-Qiskit, en particular `Math28_Matrices.ipynb`, `Math32_Tensor_Product.ipynb` y `Exercises_Basic_Math.ipynb`. Se añade un puente breve hacia los primeros cuadernos de QBronze sobre sistemas clásicos y cuánticos.\n",
        "\n",
        "**Duración sugerida:** 120 minutos."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0fb9d309",
      "metadata": {
        "id": "0fb9d309"
      },
      "source": [
        "## Objetivos de aprendizaje\n",
        "\n",
        "Al terminar esta sesión, el estudiante debe poder:\n",
        "\n",
        "1. Representar matrices como arreglos de NumPy.\n",
        "2. Calcular transpuesta, suma de elementos y multiplicación matricial.\n",
        "3. Distinguir multiplicación matricial `@` de multiplicación elemento a elemento `*`.\n",
        "4. Calcular producto tensorial o de Kronecker con `np.kron`.\n",
        "5. Usar propiedades simples de suma de elementos en productos tensoriales.\n",
        "6. Relacionar vectores, matrices y operadores con la notación inicial de QBronze."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2d5b837c",
      "metadata": {
        "id": "2d5b837c"
      },
      "outputs": [],
      "source": [
        "from math import sqrt, isclose\n",
        "import numpy as np\n",
        "\n",
        "def _to_list(x):\n",
        "    if isinstance(x, np.ndarray):\n",
        "        return x.tolist()\n",
        "    return x\n",
        "\n",
        "def _igual(a, b, tol=1e-9):\n",
        "    a = _to_list(a)\n",
        "    b = _to_list(b)\n",
        "    if isinstance(a, float) or isinstance(b, float):\n",
        "        try:\n",
        "            return isclose(float(a), float(b), rel_tol=tol, abs_tol=tol)\n",
        "        except Exception:\n",
        "            return False\n",
        "    if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)):\n",
        "        return len(a) == len(b) and all(_igual(x, y, tol) for x, y in zip(a, b))\n",
        "    return a == b\n",
        "\n",
        "def qprep_check(nombre, obtenido, esperado, tol=1e-9):\n",
        "    if obtenido is None:\n",
        "        print(f\"{nombre}: pendiente. Reemplaza None por tu respuesta.\")\n",
        "        return False\n",
        "    if _igual(obtenido, esperado, tol):\n",
        "        print(f\"{nombre}: correcto.\")\n",
        "        return True\n",
        "    print(f\"{nombre}: revisa tu resultado. Obtenido = {obtenido!r}\")\n",
        "    return False"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6266c656",
      "metadata": {
        "id": "6266c656"
      },
      "source": [
        "## 1. Matrices y dimensiones (Michael)\n",
        "\n",
        "Una matriz $m \\times n$ tiene $m$ filas y $n$ columnas. En Python/NumPy, los índices empiezan en cero:\n",
        "\n",
        "- Matemática: elemento $M_{2,1}$ significa segunda fila, primera columna.\n",
        "- Python: ese elemento se lee como `M[1, 0]`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "535cec66",
      "metadata": {
        "id": "535cec66"
      },
      "outputs": [],
      "source": [
        "M = np.array([[1, 2],\n",
        "              [3, 4]])\n",
        "\n",
        "print(\"M =\")\n",
        "print(M)\n",
        "print(\"dimensiones =\", M.shape)\n",
        "print(\"elemento matemático M_{2,1} =\", M[1, 0])\n",
        "print(\"elemento matemático M_{1,2} =\", M[0, 1])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ee72165",
      "metadata": {
        "id": "1ee72165"
      },
      "source": [
        "## 2. Transpuesta, suma de elementos y multiplicación matricial\n",
        "\n",
        "La transpuesta $M^T$ intercambia filas por columnas.\n",
        "\n",
        "La multiplicación matricial se escribe `A @ B` en Python. No debe confundirse con `A * B`, que en NumPy multiplica componente a componente."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "57eed227",
      "metadata": {
        "id": "57eed227"
      },
      "outputs": [],
      "source": [
        "M = np.array([[1, 2],\n",
        "              [3, 4]])\n",
        "\n",
        "print(\"M.T =\")\n",
        "print(M.T)\n",
        "\n",
        "print(\"suma de todos los elementos =\", M.sum())\n",
        "\n",
        "print(\"M @ M =\")\n",
        "print(M @ M)\n",
        "\n",
        "print(\"M * M =\")\n",
        "print(M * M)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "861d53c9",
      "metadata": {
        "id": "861d53c9"
      },
      "source": [
        "### Ejercicio 3.1 — Producto matricial y suma de elementos\n",
        "\n",
        "Para\n",
        "\n",
        "$$M=\\begin{bmatrix}1&2\\\\3&4\\end{bmatrix},$$\n",
        "\n",
        "calcula la suma de todos los elementos de $M M$."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "fdb21f3a",
      "metadata": {
        "id": "fdb21f3a"
      },
      "outputs": [],
      "source": [
        "respuesta_31a = None"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "bc820b9e",
      "metadata": {
        "id": "bc820b9e"
      },
      "outputs": [],
      "source": [
        "# Verificación posterior.\n",
        "M = np.array([[1, 2], [3, 4]])\n",
        "print(M @ M)\n",
        "print((M @ M).sum())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9f75fb4f",
      "metadata": {
        "id": "9f75fb4f"
      },
      "source": [
        "## 3. El orden importa: $AB$ no siempre es $BA$\n",
        "\n",
        "En general, la multiplicación de matrices no es conmutativa. Es decir, puede ocurrir que $AB \\ne BA$."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "732c77c2",
      "metadata": {
        "id": "732c77c2"
      },
      "outputs": [],
      "source": [
        "A = np.array([[1, -1],\n",
        "              [-1, 1]])\n",
        "B = np.array([[1, 2],\n",
        "              [3, 4]])\n",
        "\n",
        "print(\"A @ B =\")\n",
        "print(A @ B)\n",
        "print(\"B @ A =\")\n",
        "print(B @ A)\n",
        "print(\"¿son iguales?\", np.array_equal(A @ B, B @ A))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9998a267",
      "metadata": {
        "id": "9998a267"
      },
      "source": [
        "### Ejercicio 3.2 — Índices matemáticos e índices de Python\n",
        "\n",
        "Si $C=A B$, calcula el elemento matemático $C_{2,2}$ y luego úsalo en una expresión de Python."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "80878ff8",
      "metadata": {
        "id": "80878ff8"
      },
      "outputs": [],
      "source": [
        "respuesta_32a = None  # elemento matemático C_{2,2}\n",
        "respuesta_32b = None  # y = -((respuesta_32a**3 - 6*respuesta_32a + respuesta_32a%2) * 2)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5ad68bd7",
      "metadata": {
        "id": "5ad68bd7"
      },
      "source": [
        "## 4. Matrices compuestas: $M M^T M$\n",
        "\n",
        "Expresiones con transpuesta son frecuentes porque en álgebra lineal y computación cuántica los operadores se componen por multiplicación matricial."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "38f126dc",
      "metadata": {
        "id": "38f126dc"
      },
      "outputs": [],
      "source": [
        "M = np.array([[1, 2],\n",
        "              [3, 4]])\n",
        "\n",
        "resultado = M @ M.T @ M\n",
        "print(resultado)\n",
        "print(\"suma =\", resultado.sum())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c2e3cc21",
      "metadata": {
        "id": "c2e3cc21"
      },
      "source": [
        "### Ejercicio 3.3 — Suma de $M M^T M$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "32ab7c00",
      "metadata": {
        "id": "32ab7c00"
      },
      "outputs": [],
      "source": [
        "respuesta_33a = None"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1870611",
      "metadata": {
        "id": "c1870611"
      },
      "source": [
        "## 5. Producto tensorial o producto de Kronecker\n",
        "\n",
        "El producto tensorial combina sistemas. Si\n",
        "\n",
        "$$A = \\begin{bmatrix}a&b\\\\c&d\\end{bmatrix}, \\quad B \\text{ es una matriz},$$\n",
        "\n",
        "entonces\n",
        "\n",
        "$$A\\otimes B = \\begin{bmatrix}aB & bB\\\\ cB & dB\\end{bmatrix}.$$\n",
        "\n",
        "En NumPy se calcula con `np.kron(A, B)`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "71eb46f1",
      "metadata": {
        "id": "71eb46f1"
      },
      "outputs": [],
      "source": [
        "M = np.array([[1, 2],\n",
        "              [3, 4]])\n",
        "\n",
        "K = np.kron(M, M)\n",
        "print(\"M ⊗ M =\")\n",
        "print(K)\n",
        "print(\"dimensiones =\", K.shape)\n",
        "print(\"suma de elementos =\", K.sum())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cf4e50e3",
      "metadata": {
        "id": "cf4e50e3"
      },
      "source": [
        "### Propiedad útil para cálculo manual\n",
        "\n",
        "La suma de todos los elementos de un producto tensorial satisface:\n",
        "\n",
        "$$\\operatorname{sum}(A\\otimes B)=\\operatorname{sum}(A)\\operatorname{sum}(B).$$\n",
        "\n",
        "Por tanto, si la suma de elementos de $M$ es $10$, entonces la suma de elementos de $M\\otimes M$ es $10\\cdot10=100$."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0c5a2312",
      "metadata": {
        "id": "0c5a2312"
      },
      "source": [
        "### Ejercicio 3.4 — Producto tensorial\n",
        "\n",
        "Para $M=\\begin{bmatrix}1&2\\\\3&4\\end{bmatrix}$, calcula la suma de todos los elementos de $M\\otimes M$."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "131db30e",
      "metadata": {
        "id": "131db30e"
      },
      "outputs": [],
      "source": [
        "respuesta_34a = None"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9cc9a83c",
      "metadata": {
        "id": "9cc9a83c"
      },
      "source": [
        "### Ejercicio 3.5 — Producto tensorial anidado y módulo\n",
        "\n",
        "Sea\n",
        "\n",
        "$$N = M \\otimes ((M\\otimes M)\\otimes M).$$\n",
        "\n",
        "Si $s$ es la suma de todos los elementos de $N$, calcula en Python:\n",
        "\n",
        "```python\n",
        "y = s * 25 % 3\n",
        "```\n",
        "\n",
        "Recuerda que `*` y `%` tienen la misma precedencia y se evalúan de izquierda a derecha."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1b0b45db",
      "metadata": {
        "id": "1b0b45db"
      },
      "outputs": [],
      "source": [
        "respuesta_35a = None  # valor de s\n",
        "respuesta_35b = None  # valor de y"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "32b244ca",
      "metadata": {
        "id": "32b244ca"
      },
      "outputs": [],
      "source": [
        "# Verificación posterior.\n",
        "M = np.array([[1, 2], [3, 4]])\n",
        "N = np.kron(M, np.kron(np.kron(M, M), M))\n",
        "s = N.sum()\n",
        "y = s * 25 % 3\n",
        "print(\"s =\", s)\n",
        "print(\"y =\", y)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6609d065",
      "metadata": {
        "id": "6609d065"
      },
      "source": [
        "## 6. Vectores base y operadores: puente mínimo a QBronze (Ivan)\n",
        "\n",
        "QBronze usa vectores y matrices para representar estados y operaciones. En una versión real elemental:\n",
        "\n",
        "$$|0\\rangle = \\begin{bmatrix}1\\\\0\\end{bmatrix}, \\quad |1\\rangle = \\begin{bmatrix}0\\\\1\\end{bmatrix}.$$\n",
        "\n",
        "La matriz $X$ intercambia $|0\\rangle$ y $|1\\rangle$:\n",
        "\n",
        "$$X=\\begin{bmatrix}0&1\\\\1&0\\end{bmatrix}.$$\n",
        "\n",
        "La matriz de Hadamard $H$ crea combinaciones balanceadas:\n",
        "\n",
        "$$H=\\frac{1}{\\sqrt{2}}\\begin{bmatrix}1&1\\\\1&-1\\end{bmatrix}.$$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "bfc267e4",
      "metadata": {
        "id": "bfc267e4"
      },
      "outputs": [],
      "source": [
        "zero = np.array([1, 0])\n",
        "one = np.array([0, 1])\n",
        "\n",
        "X = np.array([[0, 1],\n",
        "              [1, 0]])\n",
        "\n",
        "H = (1/np.sqrt(2)) * np.array([[1, 1],\n",
        "                               [1, -1]])\n",
        "\n",
        "print(\"X |0> =\", X @ zero)\n",
        "print(\"X |1> =\", X @ one)\n",
        "print(\"H |0> =\", H @ zero)\n",
        "print(\"H |1> =\", H @ one)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "53ce3f5d",
      "metadata": {
        "id": "53ce3f5d"
      },
      "source": [
        "## 7. Estados compuestos por producto tensorial\n",
        "\n",
        "Dos bits/qubits se combinan con producto tensorial:\n",
        "\n",
        "$$|0\\rangle\\otimes|1\\rangle = |01\\rangle.$$\n",
        "\n",
        "Con la convención computacional usual:\n",
        "\n",
        "- $|00\\rangle = [1,0,0,0]$\n",
        "- $|01\\rangle = [0,1,0,0]$\n",
        "- $|10\\rangle = [0,0,1,0]$\n",
        "- $|11\\rangle = [0,0,0,1]$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1e11b2fd",
      "metadata": {
        "id": "1e11b2fd"
      },
      "outputs": [],
      "source": [
        "print(\"|0> ⊗ |0> =\", np.kron(zero, zero))\n",
        "print(\"|0> ⊗ |1> =\", np.kron(zero, one))\n",
        "print(\"|1> ⊗ |0> =\", np.kron(one, zero))\n",
        "print(\"|1> ⊗ |1> =\", np.kron(one, one))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c88eabde",
      "metadata": {
        "id": "c88eabde"
      },
      "source": [
        "### Ejercicio 3.6 — Operador sobre estado base\n",
        "\n",
        "Calcula el resultado de $X|0\\rangle$ y de $H|0\\rangle$ redondeando las componentes de Hadamard a 4 decimales."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ba2e7285",
      "metadata": {
        "id": "ba2e7285"
      },
      "outputs": [],
      "source": [
        "respuesta_36a = None  # X|0>\n",
        "respuesta_36b = None  # H|0> con 4 decimales"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6cdc8201",
      "metadata": {
        "id": "6cdc8201"
      },
      "source": [
        "## 8. Repaso de Python: slicing combinado con potencias\n",
        "\n",
        "El quiz mezcla álgebra lineal con expresiones de Python. Repasemos una traza de lista."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f694bd81",
      "metadata": {
        "id": "f694bd81"
      },
      "outputs": [],
      "source": [
        "l = [1, 2, 3, 4, 5]\n",
        "print(l[-1:-4:-1])\n",
        "print(l[-1:-4:-1][::-1])\n",
        "print(l[-1:-4:-1][::-1][-3] ** 3)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e8e5374",
      "metadata": {
        "id": "5e8e5374"
      },
      "source": [
        "### Ejercicio 3.7 — Slicing"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "e6db84d0",
      "metadata": {
        "id": "e6db84d0"
      },
      "outputs": [],
      "source": [
        "l = [2, 4, 6, 8, 10]\n",
        "respuesta_37a = None  # l[-1:-5:-2]\n",
        "respuesta_37b = None  # l[-1:-5:-2][::-1]\n",
        "respuesta_37c = None  # l[-1:-5:-2][::-1][0] ** 2"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0f9c5859",
      "metadata": {
        "id": "0f9c5859"
      },
      "source": [
        "## 9. Comprobación opcional de Qiskit\n",
        "\n",
        "QPrep no necesita usar Qiskit para resolver el quiz de Python y matemáticas, pero QBronze sí lo usa. Si estás trabajando en el entorno del taller, esta celda permite verificar si Qiskit está disponible."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "e09da4a3",
      "metadata": {
        "id": "e09da4a3"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "    import qiskit\n",
        "    print(\"Qiskit disponible. Versión:\", getattr(qiskit, \"__version__\", \"desconocida\"))\n",
        "except ImportError:\n",
        "    print(\"Qiskit no está instalado en este entorno. Para QBronze, sigue installation.pdf del paquete Bronze-Qiskit.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "81bd3995",
      "metadata": {
        "id": "81bd3995"
      },
      "source": [
        "## Autoevaluación de la sesión 3\n",
        "\n",
        "Resuelve primero a mano."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ac43fba9",
      "metadata": {
        "id": "ac43fba9"
      },
      "outputs": [],
      "source": [
        "# A. Para M=[[1,2],[3,4]], suma de elementos de M @ M\n",
        "s3_A = None\n",
        "\n",
        "# B. Para M=[[1,2],[3,4]], suma de elementos de kron(M,M)\n",
        "s3_B = None\n",
        "\n",
        "# C. Para M=[[1,2],[3,4]], suma de elementos de M @ M.T @ M\n",
        "s3_C = None\n",
        "\n",
        "# D. En Python, (10000 * 25) % 3\n",
        "s3_D = None\n",
        "\n",
        "# E. Resultado de np.kron([0,1], [1,0])\n",
        "s3_E = None"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e6723ba",
      "metadata": {
        "id": "1e6723ba"
      },
      "source": [
        "## Cierre\n",
        "\n",
        "Antes de iniciar QBronze, el estudiante debería poder:\n",
        "\n",
        "- Multiplicar matrices pequeñas manualmente.\n",
        "- Traducir índices matemáticos a índices de Python.\n",
        "- Usar `@` para multiplicación matricial y no confundirlo con `*`.\n",
        "- Calcular productos tensoriales simples.\n",
        "- Interpretar $|0\\rangle$, $|1\\rangle$, $X$ y $H$ como vectores y matrices reales elementales."
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11"
    },
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}