{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "2ac3bafa",
      "metadata": {
        "id": "2ac3bafa"
      },
      "source": [
        "# QBronze 1.4 — Entrelazamiento y protocolos\n",
        "\n",
        "Este cuaderno desarrolla los ejercicios centrales sobre estados entrelazados, codificación superdensa y teleportación cuántica.\n",
        "\n",
        "Se trabaja con amplitudes reales y con vectores de estado en la base computacional. La notación será explícita:\n",
        "\n",
        "$$\n",
        "|ab\\rangle=|a\\rangle\\otimes |b\\rangle.\n",
        "$$\n",
        "\n",
        "Para tres qubits usaremos el orden lógico:\n",
        "\n",
        "$$\n",
        "|\\text{primer qubit},\\text{segundo qubit},\\text{tercer qubit}\\rangle.\n",
        "$$\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "66f8b945",
      "metadata": {
        "id": "66f8b945"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "from math import sqrt, pi, cos, sin, log2, floor\n",
        "from collections import Counter\n",
        "\n",
        "np.set_printoptions(precision=6, suppress=True)\n",
        "\n",
        "ket0 = np.array([1.0, 0.0])\n",
        "ket1 = np.array([0.0, 1.0])\n",
        "\n",
        "I = np.eye(2)\n",
        "X = np.array([[0.0, 1.0], [1.0, 0.0]])\n",
        "Z = np.array([[1.0, 0.0], [0.0, -1.0]])\n",
        "H = (1 / sqrt(2)) * np.array([[1.0, 1.0], [1.0, -1.0]])\n",
        "\n",
        "def tensor(*vectors_or_matrices):\n",
        "    \"\"\"Producto tensorial en el orden escrito: A ⊗ B ⊗ C.\"\"\"\n",
        "    result = np.array([1.0])\n",
        "    for item in vectors_or_matrices:\n",
        "        result = np.kron(result, item)\n",
        "    return result\n",
        "\n",
        "def norm2(state):\n",
        "    \"\"\"Norma al cuadrado: suma |amplitud|^2.\"\"\"\n",
        "    return float(np.vdot(state, state).real)\n",
        "\n",
        "def probs(state):\n",
        "    \"\"\"Probabilidades de medición en la base computacional.\"\"\"\n",
        "    return np.abs(state) ** 2\n",
        "\n",
        "def basis_state(bitstring):\n",
        "    \"\"\"Vector base |bitstring⟩ en orden lógico de izquierda a derecha.\"\"\"\n",
        "    n = len(bitstring)\n",
        "    index = int(bitstring, 2)\n",
        "    v = np.zeros(2**n)\n",
        "    v[index] = 1.0\n",
        "    return v\n",
        "\n",
        "def bitstrings(n):\n",
        "    return [format(i, f\"0{n}b\") for i in range(2**n)]\n",
        "\n",
        "def show_state(state, n=None, tol=1e-10):\n",
        "    \"\"\"Representación textual compacta de un vector de estado.\"\"\"\n",
        "    if n is None:\n",
        "        n = int(round(log2(len(state))))\n",
        "    terms = []\n",
        "    for amp, bits in zip(state, bitstrings(n)):\n",
        "        if abs(amp) > tol:\n",
        "            terms.append(f\"{amp:+.6g}|{bits}⟩\")\n",
        "    return \" \".join(terms) if terms else \"0\"\n",
        "\n",
        "def sample_counts(state, shots=1024, seed=7, qiskit_order=False):\n",
        "    \"\"\"Muestreo de una distribución de medición. Si qiskit_order=True, invierte las etiquetas.\"\"\"\n",
        "    rng = np.random.default_rng(seed)\n",
        "    p = probs(state)\n",
        "    n = int(round(log2(len(state))))\n",
        "    outcomes = rng.choice(2**n, p=p/p.sum(), size=shots)\n",
        "    labels = []\n",
        "    for i in outcomes:\n",
        "        bits = format(i, f\"0{n}b\")\n",
        "        labels.append(bits[::-1] if qiskit_order else bits)\n",
        "    return dict(sorted(Counter(labels).items()))\n",
        "\n",
        "def is_valid_quantum_state(v, tol=1e-9):\n",
        "    return abs(norm2(np.asarray(v, dtype=float)) - 1.0) < tol\n",
        "\n",
        "\n",
        "def cnot_matrix(control=0, target=1, n=2):\n",
        "    size = 2**n\n",
        "    M = np.zeros((size, size))\n",
        "    for i, bits in enumerate(bitstrings(n)):\n",
        "        b = list(bits)\n",
        "        if b[control] == '1':\n",
        "            b[target] = '0' if b[target] == '1' else '1'\n",
        "        j = int(''.join(b), 2)\n",
        "        M[j, i] = 1.0\n",
        "    return M\n",
        "\n",
        "def apply_single_qubit_gate(state, gate, qubit, n):\n",
        "    ops = [gate if k == qubit else I for k in range(n)]\n",
        "    return tensor(*ops) @ state\n",
        "\n",
        "def coefficient_matrix_two_qubits(state):\n",
        "    \"\"\"Convierte [a00,a01,a10,a11] en matriz [[a00,a01],[a10,a11]].\"\"\"\n",
        "    return np.array([[state[0], state[1]], [state[2], state[3]]])\n",
        "\n",
        "def is_entangled_two_qubit(state, tol=1e-9):\n",
        "    \"\"\"Para dos qubits puros: está entrelazado si la matriz de coeficientes tiene rango 2.\"\"\"\n",
        "    M = coefficient_matrix_two_qubits(np.asarray(state, dtype=float))\n",
        "    return np.linalg.matrix_rank(M, tol=tol) > 1\n",
        "\n",
        "def density_matrix(state):\n",
        "    state = np.asarray(state, dtype=complex)\n",
        "    return np.outer(state, np.conjugate(state))\n",
        "\n",
        "def partial_trace_second_qubit(rho):\n",
        "    \"\"\"Traza parcial sobre el segundo qubit de un sistema de dos qubits.\"\"\"\n",
        "    rho4 = rho.reshape(2, 2, 2, 2)  # a,b,a',b'\n",
        "    return np.einsum('abcb->ac', rho4)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b296d727",
      "metadata": {
        "id": "b296d727"
      },
      "source": [
        "## 1. Preparar un estado de Bell\n",
        "\n",
        "Partimos de\n",
        "\n",
        "$$\n",
        "|00\\rangle.\n",
        "$$\n",
        "\n",
        "Primero aplicamos $H$ al primer qubit:\n",
        "\n",
        "$$\n",
        "(H\\otimes I)|00\\rangle\n",
        "=\n",
        "\\frac{|0\\rangle+|1\\rangle}{\\sqrt{2}}\\otimes |0\\rangle\n",
        "=\n",
        "\\frac{|00\\rangle+|10\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "Luego aplicamos CNOT con el primer qubit como control y el segundo como objetivo:\n",
        "\n",
        "$$\n",
        "|00\\rangle\\mapsto |00\\rangle,\n",
        "\\qquad\n",
        "|10\\rangle\\mapsto |11\\rangle.\n",
        "$$\n",
        "\n",
        "Por tanto:\n",
        "\n",
        "$$\n",
        "|00\\rangle\n",
        "\\xrightarrow{H\\otimes I}\n",
        "\\frac{|00\\rangle+|10\\rangle}{\\sqrt{2}}\n",
        "\\xrightarrow{CNOT}\n",
        "\\frac{|00\\rangle+|11\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "Este estado no puede escribirse como producto de dos estados de un qubit; por eso está entrelazado."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "953152d1",
      "metadata": {
        "id": "953152d1"
      },
      "outputs": [],
      "source": [
        "state = basis_state(\"00\")\n",
        "state = tensor(H, I) @ state\n",
        "state = cnot_matrix(control=0, target=1, n=2) @ state\n",
        "print(\"Estado de Bell:\", show_state(state, n=2))\n",
        "print(\"¿Está entrelazado?\", is_entangled_two_qubit(state))\n",
        "print(\"Matriz de coeficientes:\")\n",
        "print(coefficient_matrix_two_qubits(state))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f8dcc498",
      "metadata": {
        "id": "f8dcc498"
      },
      "source": [
        "## 2. Circuito GHZ de tres qubits\n",
        "\n",
        "Consideramos el circuito:\n",
        "\n",
        "1. Iniciar en $|000\\rangle$.\n",
        "2. Aplicar $H$ al primer qubit.\n",
        "3. Aplicar CNOT del primer qubit al segundo.\n",
        "4. Aplicar CNOT del primer qubit al tercero.\n",
        "\n",
        "Paso a paso:\n",
        "\n",
        "$$\n",
        "|000\\rangle\n",
        "\\xrightarrow{H_1}\n",
        "\\frac{|000\\rangle+|100\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "Primer CNOT:\n",
        "\n",
        "$$\n",
        "|100\\rangle\\mapsto |110\\rangle.\n",
        "$$\n",
        "\n",
        "Segundo CNOT:\n",
        "\n",
        "$$\n",
        "|110\\rangle\\mapsto |111\\rangle.\n",
        "$$\n",
        "\n",
        "Resultado:\n",
        "\n",
        "$$\n",
        "\\frac{|000\\rangle+|111\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "Al medir muchas veces, sólo deben aparecer $000$ y $111$, aproximadamente mitad y mitad."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5c8e9a9e",
      "metadata": {
        "id": "5c8e9a9e"
      },
      "outputs": [],
      "source": [
        "state = basis_state(\"000\")\n",
        "state = apply_single_qubit_gate(state, H, qubit=0, n=3)\n",
        "state = cnot_matrix(control=0, target=1, n=3) @ state\n",
        "state = cnot_matrix(control=0, target=2, n=3) @ state\n",
        "print(\"Estado GHZ:\", show_state(state, n=3))\n",
        "print(\"Conteos simulados:\", sample_counts(state, shots=1000, seed=10))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a19a9b57",
      "metadata": {
        "id": "a19a9b57"
      },
      "source": [
        "## 3. Identificar estados entrelazados de dos qubits\n",
        "\n",
        "Para un estado puro de dos qubits\n",
        "\n",
        "$$\n",
        "|\\psi\\rangle=a_{00}|00\\rangle+a_{01}|01\\rangle+a_{10}|10\\rangle+a_{11}|11\\rangle,\n",
        "$$\n",
        "\n",
        "formamos la matriz de coeficientes\n",
        "\n",
        "$$\n",
        "A=\\begin{pmatrix}\n",
        "a_{00}&a_{01}\\\\\n",
        "a_{10}&a_{11}\n",
        "\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "El estado es separable si la matriz tiene rango 1. Es entrelazado si tiene rango 2.\n",
        "\n",
        "Ejemplos típicos entrelazados:\n",
        "\n",
        "$$\n",
        "\\frac{|01\\rangle-|10\\rangle}{\\sqrt{2}},\n",
        "\\qquad\n",
        "\\frac{|00\\rangle-|11\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "Ejemplo no entrelazado:\n",
        "\n",
        "$$\n",
        "\\frac{|00\\rangle+|01\\rangle+|10\\rangle+|11\\rangle}{2}\n",
        "=\\left(\\frac{|0\\rangle+|1\\rangle}{\\sqrt{2}}\\right)\n",
        "\\otimes\n",
        "\\left(\\frac{|0\\rangle+|1\\rangle}{\\sqrt{2}}\\right).\n",
        "$$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "485fde2e",
      "metadata": {
        "id": "485fde2e"
      },
      "outputs": [],
      "source": [
        "states = {\n",
        "    \"(|01>-|10>)/sqrt(2)\": (basis_state(\"01\") - basis_state(\"10\")) / sqrt(2),\n",
        "    \"(|00>-|11>)/sqrt(2)\": (basis_state(\"00\") - basis_state(\"11\")) / sqrt(2),\n",
        "    \"(|00>+|01>+|10>+|11>)/2\": (basis_state(\"00\") + basis_state(\"01\") + basis_state(\"10\") + basis_state(\"11\")) / 2,\n",
        "    \"(|0>+|1>)/sqrt(2) no es de dos qubits\": None,\n",
        "}\n",
        "\n",
        "for name, v in states.items():\n",
        "    if v is None:\n",
        "        print(f\"{name}: no se evalúa como estado de dos qubits\")\n",
        "    else:\n",
        "        print(f\"{name:38s}  entrelazado = {is_entangled_two_qubit(v)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2fb525f6",
      "metadata": {
        "id": "2fb525f6"
      },
      "source": [
        "## 4. Leer correlaciones en un estado de tres qubits\n",
        "\n",
        "Sea\n",
        "\n",
        "$$\n",
        "|\\psi\\rangle=\\frac{|000\\rangle+|001\\rangle+|111\\rangle}{\\sqrt3}.\n",
        "$$\n",
        "\n",
        "Los únicos resultados posibles al medir en la base computacional son:\n",
        "\n",
        "$$\n",
        "000,\n",
        "\\quad\n",
        "001,\n",
        "\\quad\n",
        "111.\n",
        "$$\n",
        "\n",
        "Cada uno tiene probabilidad\n",
        "\n",
        "$$\n",
        "\\left(\\frac1{\\sqrt3}\\right)^2=\\frac13.\n",
        "$$\n",
        "\n",
        "Consecuencias:\n",
        "\n",
        "- Si el primer qubit se mide como $0$, los resultados compatibles son $000$ y $001$. En ambos, el segundo qubit es $0$.\n",
        "- Si el primer qubit se mide como $1$, el único resultado compatible es $111$. Entonces el tercer qubit es $1$ con probabilidad 1."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c3cd45be",
      "metadata": {
        "id": "c3cd45be"
      },
      "outputs": [],
      "source": [
        "psi = (basis_state(\"000\") + basis_state(\"001\") + basis_state(\"111\")) / sqrt(3)\n",
        "print(\"Estado:\", show_state(psi, n=3))\n",
        "\n",
        "p = probs(psi)\n",
        "for bits, prob in zip(bitstrings(3), p):\n",
        "    if prob > 1e-12:\n",
        "        print(bits, prob)\n",
        "\n",
        "def conditional_probability(state, condition, event):\n",
        "    n = int(round(log2(len(state))))\n",
        "    p_total_condition = 0.0\n",
        "    p_event_and_condition = 0.0\n",
        "    for bits, prob in zip(bitstrings(n), probs(state)):\n",
        "        if condition(bits):\n",
        "            p_total_condition += prob\n",
        "            if event(bits):\n",
        "                p_event_and_condition += prob\n",
        "    return p_event_and_condition / p_total_condition\n",
        "\n",
        "print(\"P(segundo=0 | primero=0) =\", conditional_probability(psi, lambda b: b[0]=='0', lambda b: b[1]=='0'))\n",
        "print(\"P(tercero=1 | primero=1) =\", conditional_probability(psi, lambda b: b[0]=='1', lambda b: b[2]=='1'))\n",
        "print(\"P(tercero=1 | primero=0) =\", conditional_probability(psi, lambda b: b[0]=='0', lambda b: b[2]=='1'))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "73bd5f58",
      "metadata": {
        "id": "73bd5f58"
      },
      "source": [
        "## 5. Recursos en codificación superdensa\n",
        "\n",
        "En codificación superdensa, un par entrelazado permite transmitir 2 bits clásicos mediante el envío físico de 1 qubit, siempre que el par entrelazado haya sido compartido antes.\n",
        "\n",
        "Supongamos que el mensaje es `1302`, y que la codificación de cada dígito es:\n",
        "\n",
        "$$\n",
        "0\\mapsto 00,\n",
        "\\quad\n",
        "1\\mapsto 01,\n",
        "\\quad\n",
        "2\\mapsto 10,\n",
        "\\quad\n",
        "3\\mapsto 11.\n",
        "$$\n",
        "\n",
        "El mensaje tiene 4 dígitos. Cada dígito corresponde a 2 bits. Por tanto se usa el protocolo 4 veces.\n",
        "\n",
        "Cada uso consume 1 par entrelazado. Entonces se necesitan:\n",
        "\n",
        "$$\n",
        "4\\text{ pares entrelazados}=8\\text{ qubits en total}.\n",
        "$$\n",
        "\n",
        "No se reutiliza el mismo par, porque el entrelazamiento se consume durante el protocolo."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f185b51c",
      "metadata": {
        "id": "f185b51c"
      },
      "outputs": [],
      "source": [
        "message = \"1302\"\n",
        "encoding = {\"0\": \"00\", \"1\": \"01\", \"2\": \"10\", \"3\": \"11\"}\n",
        "encoded = [encoding[d] for d in message]\n",
        "print(\"mensaje:\", message)\n",
        "print(\"bloques de 2 bits:\", encoded)\n",
        "print(\"número de usos del protocolo:\", len(encoded))\n",
        "print(\"pares entrelazados necesarios:\", len(encoded))\n",
        "print(\"qubits totales en esos pares:\", 2 * len(encoded))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1d9139c",
      "metadata": {
        "id": "c1d9139c"
      },
      "source": [
        "## 6. Operaciones de Alice en codificación superdensa\n",
        "\n",
        "Usamos el estado inicial\n",
        "\n",
        "$$\n",
        "|\\Phi^+\\rangle=\\frac{|00\\rangle+|11\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "Una convención común es:\n",
        "\n",
        "$$\n",
        "00\\mapsto I,\n",
        "\\qquad\n",
        "01\\mapsto X,\n",
        "\\qquad\n",
        "10\\mapsto Z,\n",
        "\\qquad\n",
        "11\\mapsto XZ.\n",
        "$$\n",
        "\n",
        "Si Alice quiere enviar `01`, aplica $X$ a su qubit.\n",
        "\n",
        "Verificación:\n",
        "\n",
        "$$\n",
        "(X\\otimes I)|\\Phi^+\\rangle\n",
        "=\\frac{|10\\rangle+|01\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "Bob, al decodificar con CNOT y luego $H$ sobre el primer qubit, obtiene los bits clásicos correspondientes."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "df8767ec",
      "metadata": {
        "id": "df8767ec"
      },
      "outputs": [],
      "source": [
        "phi_plus = (basis_state(\"00\") + basis_state(\"11\")) / sqrt(2)\n",
        "encoded_01 = tensor(X, I) @ phi_plus\n",
        "print(\"Estado después de X en el qubit de Alice:\", show_state(encoded_01, n=2))\n",
        "\n",
        "# Decodificación: CNOT primer->segundo, luego H en el primero.\n",
        "decoded = tensor(H, I) @ (cnot_matrix(control=0, target=1, n=2) @ encoded_01)\n",
        "print(\"Estado decodificado:\", show_state(decoded, n=2))\n",
        "print(\"Resultado medido con probabilidad 1:\", max(zip(bitstrings(2), probs(decoded)), key=lambda x: x[1]))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b5b1682c",
      "metadata": {
        "id": "b5b1682c"
      },
      "source": [
        "## 7. Por qué Bob no puede recuperar el mensaje si pierde su qubit\n",
        "\n",
        "En codificación superdensa, Bob necesita dos cosas:\n",
        "\n",
        "1. El qubit que Alice le envía.\n",
        "2. Su propio qubit, que estaba entrelazado con el de Alice.\n",
        "\n",
        "Si Bob pierde su qubit, sólo conserva un subsistema. Ese subsistema por sí solo no contiene información suficiente para distinguir todos los mensajes.\n",
        "\n",
        "Matemáticamente, para los cuatro mensajes, el estado reducido del qubit de Alice es el mismo:\n",
        "\n",
        "$$\n",
        "\\rho_A=\\frac12 I.\n",
        "$$\n",
        "\n",
        "Si los estados reducidos son iguales, ninguna medición local sobre ese único qubit puede recuperar el mensaje completo."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "85375972",
      "metadata": {
        "id": "85375972"
      },
      "outputs": [],
      "source": [
        "ops = {\n",
        "    \"00: I\": I,\n",
        "    \"01: X\": X,\n",
        "    \"10: Z\": Z,\n",
        "    \"11: XZ\": X @ Z,\n",
        "}\n",
        "\n",
        "for label, op in ops.items():\n",
        "    state = tensor(op, I) @ phi_plus\n",
        "    rho = density_matrix(state)\n",
        "    rho_alice = partial_trace_second_qubit(rho)\n",
        "    print(label)\n",
        "    print(np.real_if_close(rho_alice))\n",
        "    print(\"---\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "459e75ba",
      "metadata": {
        "id": "459e75ba"
      },
      "source": [
        "## 8. Operaciones condicionadas por mediciones clásicas\n",
        "\n",
        "Considérese la lógica:\n",
        "\n",
        "1. Iniciar dos qubits en $|00\\rangle$.\n",
        "2. Aplicar $X$ a $q[0]$, de modo que $q[0]=1$.\n",
        "3. Medir $q[0]$ en $c[0]$, por lo que $c[0]=1$.\n",
        "4. Aplicar $H$ a $q[1]$ sólo si el registro clásico completo vale 0.\n",
        "\n",
        "Como el registro clásico ya contiene $c[0]=1$, la condición `c == 0` no se cumple. Por tanto no se aplica $H$ a $q[1]$.\n",
        "\n",
        "El estado medido permanece con $q[1]=0$ y $q[0]=1$. La cadena impresa tipo $c[1]c[0]$ es `01`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4dd44235",
      "metadata": {
        "id": "4dd44235"
      },
      "outputs": [],
      "source": [
        "# Simulación clásica del flujo lógico descrito.\n",
        "q0 = 0\n",
        "q1 = 0\n",
        "q0 = 1 - q0        # X en q0\n",
        "c0 = q0            # medición de q0 en c0\n",
        "c1 = 0             # aún no medido\n",
        "c_register_value = c0 + 2*c1\n",
        "\n",
        "if c_register_value == 0:\n",
        "    # Se habría aplicado H a q1, pero no entra aquí.\n",
        "    q1_state = \"superposición\"\n",
        "else:\n",
        "    q1_state = q1\n",
        "\n",
        "c1 = 0 if q1_state != \"superposición\" else \"0 o 1\"\n",
        "print(\"valor del registro clásico después de medir q0:\", c_register_value)\n",
        "print(\"¿se aplica H a q1?\", c_register_value == 0)\n",
        "print(\"cadena final esperada c1c0:\", \"01\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "776a7952",
      "metadata": {
        "id": "776a7952"
      },
      "source": [
        "## 9. Afirmaciones conceptuales sobre protocolos\n",
        "\n",
        "Afirmaciones correctas:\n",
        "\n",
        "- En codificación superdensa se pueden transmitir 2 bits clásicos mediante el envío físico de 1 qubit, usando además un par entrelazado previamente compartido.\n",
        "\n",
        "Afirmaciones incorrectas:\n",
        "\n",
        "- El entrelazamiento no permite comunicación más rápida que la luz.\n",
        "- La teleportación no crea múltiples copias del mismo qubit; no viola el teorema de no clonación.\n",
        "- No existe un único estado entrelazado; existen muchos estados entrelazados.\n",
        "- En codificación superdensa, las operaciones que dependen del mensaje las aplica Alice, no Bob."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c49a8a59",
      "metadata": {
        "id": "c49a8a59"
      },
      "outputs": [],
      "source": [
        "statements = {\n",
        "    \"Entrelazamiento permite comunicación más rápida que la luz\": False,\n",
        "    \"En codificación superdensa Alice codifica el mensaje\": True,\n",
        "    \"Hay un único estado entrelazado\": False,\n",
        "    \"Teleportación crea copias múltiples\": False,\n",
        "    \"Codificación superdensa envía 2 bits clásicos usando 1 qubit transmitido y entrelazamiento previo\": True,\n",
        "}\n",
        "\n",
        "for s, val in statements.items():\n",
        "    print(f\"{val!s:5s}  {s}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bf2a552c",
      "metadata": {
        "id": "bf2a552c"
      },
      "source": [
        "## 10. Correcciones de Bob en teleportación cuántica\n",
        "\n",
        "Sea el estado desconocido de Alice\n",
        "\n",
        "$$\n",
        "|\\psi\\rangle=\\alpha|0\\rangle+\\beta|1\\rangle.\n",
        "$$\n",
        "\n",
        "Después de las operaciones de Alice y de su medición, Bob puede quedar en uno de cuatro estados:\n",
        "\n",
        "$$\n",
        "\\begin{array}{c|c|c}\n",
        "\\text{medición de Alice} & \\text{estado de Bob antes de corrección} & \\text{corrección}\\\\\n",
        "\\hline\n",
        "00 & \\alpha|0\\rangle+\\beta|1\\rangle & I\\\\\n",
        "01 & \\alpha|1\\rangle+\\beta|0\\rangle & X\\\\\n",
        "10 & \\alpha|0\\rangle-\\beta|1\\rangle & Z\\\\\n",
        "11 & \\alpha|1\\rangle-\\beta|0\\rangle & ZX\n",
        "\\end{array}\n",
        "$$\n",
        "\n",
        "Si Alice informa `10`, Bob aplica $Z$."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "11265200",
      "metadata": {
        "id": "11265200"
      },
      "outputs": [],
      "source": [
        "alpha = 0.6\n",
        "beta = sqrt(1 - alpha**2)\n",
        "psi = np.array([alpha, beta])\n",
        "\n",
        "bob_before = {\n",
        "    \"00\": np.array([alpha, beta]),\n",
        "    \"01\": np.array([beta, alpha]),\n",
        "    \"10\": np.array([alpha, -beta]),\n",
        "    \"11\": np.array([-beta, alpha]),  # equivale a aplicar X y luego Z hasta fase global según convención\n",
        "}\n",
        "correction = {\n",
        "    \"00\": I,\n",
        "    \"01\": X,\n",
        "    \"10\": Z,\n",
        "    \"11\": Z @ X,\n",
        "}\n",
        "\n",
        "for outcome in [\"00\", \"01\", \"10\", \"11\"]:\n",
        "    corrected = correction[outcome] @ bob_before[outcome]\n",
        "    # Comparamos hasta fase global: si difiere por -1, físicamente es el mismo estado.\n",
        "    same_direct = np.allclose(corrected, psi)\n",
        "    same_global_minus = np.allclose(corrected, -psi)\n",
        "    print(outcome, \"antes:\", bob_before[outcome], \"después:\", corrected, \"recupera ψ:\", same_direct or same_global_minus)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e83801d5",
      "metadata": {
        "id": "e83801d5"
      },
      "source": [
        "## 11. Interpretación del vector de estado tras la medición de Alice\n",
        "\n",
        "En una simulación de vector de estado con tres qubits, los estados base se ordenan como\n",
        "\n",
        "$$\n",
        "|000\\rangle, |001\\rangle, |010\\rangle, |011\\rangle,\n",
        "|100\\rangle, |101\\rangle, |110\\rangle, |111\\rangle.\n",
        "$$\n",
        "\n",
        "Si todas las entradas son cero excepto las últimas dos, entonces el estado vive en el subespacio generado por\n",
        "\n",
        "$$\n",
        "|110\\rangle, |111\\rangle.\n",
        "$$\n",
        "\n",
        "En ambos casos, los dos primeros bits son `11`. Por tanto, la medición de Alice fue `11`.\n",
        "\n",
        "Al final del protocolo, Bob conserva un único qubit: el qubit que contiene el estado teleportado."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5a7eb714",
      "metadata": {
        "id": "5a7eb714"
      },
      "outputs": [],
      "source": [
        "# Vector con entradas no nulas sólo en |110> y |111>.\n",
        "state = np.zeros(8)\n",
        "state[6] = 0.6\n",
        "state[7] = 0.8\n",
        "print(\"Estado:\", show_state(state, n=3))\n",
        "nonzero = [bits for bits, amp in zip(bitstrings(3), state) if abs(amp) > 1e-12]\n",
        "print(\"bases no nulas:\", nonzero)\n",
        "print(\"primeros dos bits en todas ellas:\", sorted(set(bits[:2] for bits in nonzero)))\n",
        "print(\"qubits finales de Bob:\", 1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "519a9538",
      "metadata": {
        "id": "519a9538"
      },
      "source": [
        "## 12. Resumen operativo del módulo\n",
        "\n",
        "Ideas esenciales:\n",
        "\n",
        "$$\n",
        "|\\Phi^+\\rangle=\\frac{|00\\rangle+|11\\rangle}{\\sqrt{2}}\n",
        "$$\n",
        "\n",
        "es un recurso entrelazado.\n",
        "\n",
        "La codificación superdensa usa:\n",
        "\n",
        "$$\n",
        "\\text{1 qubit transmitido} + \\text{1 par entrelazado previo}\n",
        "\\quad\\Longrightarrow\\quad\n",
        "\\text{2 bits clásicos comunicados}.\n",
        "$$\n",
        "\n",
        "La teleportación usa:\n",
        "\n",
        "$$\n",
        "\\text{2 bits clásicos transmitidos} + \\text{1 par entrelazado previo}\n",
        "\\quad\\Longrightarrow\\quad\n",
        "\\text{1 qubit teleportado}.\n",
        "$$\n",
        "\n",
        "El entrelazamiento se consume como recurso. La teleportación no clona el estado y no permite comunicación superlumínica."
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.x"
    },
    "title": "QBronze 1.4 — Entrelazamiento y protocolos",
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}