{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "7e952871",
      "metadata": {
        "id": "7e952871"
      },
      "source": [
        "# QBronze 1.2 — Sistemas cuánticos básicos\n",
        "\n",
        "Este cuaderno estudia, con cálculo explícito y Python, los ejercicios centrales del módulo sobre sistemas cuánticos básicos.\n",
        "\n",
        "La idea operativa es siempre la misma:\n",
        "\n",
        "1. Un estado cuántico se representa como un vector de amplitudes.\n",
        "2. Una amplitud no es una probabilidad. La probabilidad se obtiene elevando al cuadrado el valor absoluto de la amplitud.\n",
        "3. Para amplitudes reales, si\n",
        "\n",
        "$$\n",
        "|\\psi\\rangle = \\alpha |0\\rangle + \\beta |1\\rangle,\n",
        "$$\n",
        "\n",
        "entonces\n",
        "\n",
        "$$\n",
        "P(0)=\\alpha^2,\\qquad P(1)=\\beta^2,\\qquad \\alpha^2+\\beta^2=1.\n",
        "$$\n",
        "\n",
        "\n",
        "El cuaderno usa simulaciones sencillas con `numpy`, sin depender de Qiskit, para que todos los pasos matemáticos sean visibles.\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "420722c0",
      "metadata": {
        "id": "420722c0"
      },
      "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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ab917ea2",
      "metadata": {
        "id": "ab917ea2"
      },
      "source": [
        "## 1. Vectores que sí representan estados cuánticos\n",
        "\n",
        "Un vector representa un estado cuántico válido si su norma al cuadrado es exactamente 1:\n",
        "\n",
        "$$\n",
        "\\sum_i |\\alpha_i|^2=1.\n",
        "$$\n",
        "\n",
        "No se exige que las amplitudes sean positivas. Las amplitudes pueden ser negativas. Lo que debe ser no negativo son las probabilidades, porque se calculan como cuadrados.\n",
        "\n",
        "Ejemplo:\n",
        "\n",
        "$$\n",
        "v=\\left(\\frac12,\\frac12,\\frac12,\\frac12\\right).\n",
        "$$\n",
        "\n",
        "Entonces\n",
        "\n",
        "$$\n",
        "\\|v\\|^2 = \\left(\\frac12\\right)^2+\\left(\\frac12\\right)^2+\\left(\\frac12\\right)^2+\\left(\\frac12\\right)^2\n",
        "=\\frac14+\\frac14+\\frac14+\\frac14=1.\n",
        "$$\n",
        "\n",
        "Por tanto, es un estado cuántico válido."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "389fac7e",
      "metadata": {
        "id": "389fac7e"
      },
      "outputs": [],
      "source": [
        "vectors = {\n",
        "    \"(1/2, 1/2, 1/2, 1/2)\": np.array([1/2, 1/2, 1/2, 1/2]),\n",
        "    \"(2/3, 1/3, 2/3, 0)\": np.array([2/3, 1/3, 2/3, 0]),\n",
        "    \"(1/2, 0, 1/2, 0)\": np.array([1/2, 0, 1/2, 0]),\n",
        "    \"(1/sqrt(2), -1/sqrt(2), 0, 0)\": np.array([1/sqrt(2), -1/sqrt(2), 0, 0]),\n",
        "    \"(1/sqrt(2), -1/sqrt(2), -1/sqrt(2), -1/sqrt(2))\": np.array([1/sqrt(2), -1/sqrt(2), -1/sqrt(2), -1/sqrt(2)]),\n",
        "    \"(1/2, -1/2, -1/2, 1/2)\": np.array([1/2, -1/2, -1/2, 1/2]),\n",
        "}\n",
        "\n",
        "for name, v in vectors.items():\n",
        "    print(f\"{name:58s} norma^2 = {norm2(v):.6f}  válido = {is_valid_quantum_state(v)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "49dd5cf1",
      "metadata": {
        "id": "49dd5cf1"
      },
      "source": [
        "## 2. Estados que no son válidos\n",
        "\n",
        "Para descartar un vector, basta con calcular\n",
        "\n",
        "$$\n",
        "\\sum_i |\\alpha_i|^2.\n",
        "$$\n",
        "\n",
        "Si el resultado no es 1, el vector no está normalizado. Esto no significa que el vector sea inútil: puede normalizarse dividiéndolo entre su norma. Pero como está escrito, no representa directamente un estado físico normalizado."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "dd1668af",
      "metadata": {
        "id": "dd1668af"
      },
      "outputs": [],
      "source": [
        "not_candidates = {\n",
        "    \"(1/4, -1/4, -1/4, 1/4)\": np.array([1/4, -1/4, -1/4, 1/4]),\n",
        "    \"(2/3, -1/3, 2/3, 0)\": np.array([2/3, -1/3, 2/3, 0]),\n",
        "    \"(1/sqrt(2), -1/sqrt(2), 0, 0)\": np.array([1/sqrt(2), -1/sqrt(2), 0, 0]),\n",
        "    \"(1/sqrt(2), -1/sqrt(2), -1/sqrt(2), -1/sqrt(2))\": np.array([1/sqrt(2), -1/sqrt(2), -1/sqrt(2), -1/sqrt(2)]),\n",
        "    \"(1/2, 0, 1/2, 0)\": np.array([1/2, 0, 1/2, 0]),\n",
        "    \"(0, -1, 0, 0)\": np.array([0, -1, 0, 0]),\n",
        "}\n",
        "\n",
        "for name, v in not_candidates.items():\n",
        "    print(f\"{name:62s} norma^2 = {norm2(v):.6f}  no válido = {not is_valid_quantum_state(v)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "74c3bc17",
      "metadata": {
        "id": "74c3bc17"
      },
      "source": [
        "## 3. La compuerta Hadamard sobre $|1\\rangle$\n",
        "\n",
        "La matriz de Hadamard es\n",
        "\n",
        "$$\n",
        "H=\\frac{1}{\\sqrt{2}}\n",
        "\\begin{pmatrix}\n",
        "1 & 1\\\\\n",
        "1 & -1\n",
        "\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "El estado $|1\\rangle$ es\n",
        "\n",
        "$$\n",
        "|1\\rangle=\\begin{pmatrix}0\\\\1\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "Multiplicamos sin saltar pasos:\n",
        "\n",
        "$$\n",
        "H|1\\rangle\n",
        "=\\frac{1}{\\sqrt{2}}\n",
        "\\begin{pmatrix}\n",
        "1 & 1\\\\\n",
        "1 & -1\n",
        "\\end{pmatrix}\n",
        "\\begin{pmatrix}0\\\\1\\end{pmatrix}\n",
        "=\\frac{1}{\\sqrt{2}}\n",
        "\\begin{pmatrix}\n",
        "1\\cdot 0 + 1\\cdot 1\\\\\n",
        "1\\cdot 0 + (-1)\\cdot 1\n",
        "\\end{pmatrix}\n",
        "=\\frac{1}{\\sqrt{2}}\n",
        "\\begin{pmatrix}1\\\\-1\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "Por definición,\n",
        "\n",
        "$$\n",
        "|-\\rangle=\\frac{|0\\rangle-|1\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "Entonces:\n",
        "\n",
        "$$\n",
        "H|1\\rangle=|-\\rangle.\n",
        "$$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "477c7c10",
      "metadata": {
        "id": "477c7c10"
      },
      "outputs": [],
      "source": [
        "result = H @ ket1\n",
        "print(\"H|1> =\", result)\n",
        "print(\"Forma ket:\", show_state(result, n=1))\n",
        "print(\"Probabilidades:\", probs(result))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c380255e",
      "metadata": {
        "id": "c380255e"
      },
      "source": [
        "## 4. Qubit real parametrizado por un ángulo\n",
        "\n",
        "Para un qubit real se usa frecuentemente la parametrización\n",
        "\n",
        "$$\n",
        "|\\psi(x)\\rangle=\\cos(x)|0\\rangle+\\sin(x)|1\\rangle.\n",
        "$$\n",
        "\n",
        "La amplitud de $|0\\rangle$ es $\\cos(x)$. La probabilidad de observar $|0\\rangle$ no es $\\cos(x)$, sino\n",
        "\n",
        "$$\n",
        "P(0)=|\\cos(x)|^2=\\cos^2(x).\n",
        "$$\n",
        "\n",
        "Análogamente,\n",
        "\n",
        "$$\n",
        "P(1)=\\sin^2(x).\n",
        "$$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5d80b0ea",
      "metadata": {
        "id": "5d80b0ea"
      },
      "outputs": [],
      "source": [
        "for x in [0, pi/6, pi/4, pi/3, pi/2]:\n",
        "    state = np.array([cos(x), sin(x)])\n",
        "    print(f\"x = {x:.4f} rad\")\n",
        "    print(\"estado =\", state)\n",
        "    print(\"P(0) = cos^2(x) =\", cos(x)**2)\n",
        "    print(\"P(1) = sin^2(x) =\", sin(x)**2)\n",
        "    print(\"suma =\", cos(x)**2 + sin(x)**2)\n",
        "    print(\"---\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8c1f18c6",
      "metadata": {
        "id": "8c1f18c6"
      },
      "source": [
        "## 5. Amplitud frente a probabilidad\n",
        "\n",
        "Si un qubit está en el estado\n",
        "\n",
        "$$\n",
        "|\\psi\\rangle=0.43|0\\rangle-0.90|1\\rangle,\n",
        "$$\n",
        "\n",
        "entonces la amplitud asociada a $|1\\rangle$ es $-0.90$. La probabilidad de observar $|1\\rangle$ es\n",
        "\n",
        "$$\n",
        "P(1)=|-0.90|^2=0.81.\n",
        "$$\n",
        "\n",
        "La pregunta por la amplitud y la pregunta por la probabilidad no son equivalentes."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9346a8b3",
      "metadata": {
        "id": "9346a8b3"
      },
      "outputs": [],
      "source": [
        "psi = np.array([0.43, -0.90])\n",
        "print(\"amplitud de |0>:\", psi[0])\n",
        "print(\"amplitud de |1>:\", psi[1])\n",
        "print(\"probabilidad de |0>:\", psi[0]**2)\n",
        "print(\"probabilidad de |1>:\", psi[1]**2)\n",
        "print(\"norma^2 aproximada:\", norm2(psi))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fa0afd10",
      "metadata": {
        "id": "fa0afd10"
      },
      "source": [
        "## 6. Compuerta NOT cuántica: la compuerta $X$\n",
        "\n",
        "La compuerta NOT cuántica es la matriz\n",
        "\n",
        "$$\n",
        "X=\\begin{pmatrix}\n",
        "0&1\\\\\n",
        "1&0\n",
        "\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "Actúa así:\n",
        "\n",
        "$$\n",
        "X|0\\rangle=|1\\rangle,\\qquad X|1\\rangle=|0\\rangle.\n",
        "$$\n",
        "\n",
        "Por tanto, si el circuito empieza en $|0\\rangle$, se aplica $X$, y luego se mide, el resultado debe ser $1$ con probabilidad 1."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "3628f332",
      "metadata": {
        "id": "3628f332"
      },
      "outputs": [],
      "source": [
        "state = ket0.copy()\n",
        "state = X @ state\n",
        "print(\"Estado después de X|0>:\", show_state(state, n=1))\n",
        "print(\"Probabilidades:\", {\"0\": probs(state)[0], \"1\": probs(state)[1]})\n",
        "print(\"Muestreo:\", sample_counts(state, shots=1024, seed=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6038dcbf",
      "metadata": {
        "id": "6038dcbf"
      },
      "source": [
        "## 7. Preparar $|-\\rangle$ con $X$ seguido de $H$\n",
        "\n",
        "Partimos de $|0\\rangle$. Primero aplicamos $X$:\n",
        "\n",
        "$$\n",
        "X|0\\rangle=|1\\rangle.\n",
        "$$\n",
        "\n",
        "Luego aplicamos $H$:\n",
        "\n",
        "$$\n",
        "H|1\\rangle=\\frac{|0\\rangle-|1\\rangle}{\\sqrt{2}}=|-\\rangle.\n",
        "$$\n",
        "\n",
        "Por tanto, la secuencia es:\n",
        "\n",
        "$$\n",
        "|0\\rangle \\xrightarrow{X} |1\\rangle \\xrightarrow{H} |-\\rangle.\n",
        "$$"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "dfe8725d",
      "metadata": {
        "id": "dfe8725d"
      },
      "outputs": [],
      "source": [
        "state = ket0.copy()\n",
        "state = X @ state\n",
        "state = H @ state\n",
        "print(\"Estado final:\", show_state(state, n=1))\n",
        "print(\"Vector:\", state)\n",
        "print(\"Probabilidades:\", probs(state))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e04ed74b",
      "metadata": {
        "id": "e04ed74b"
      },
      "source": [
        "## 8. Orden de bits al imprimir resultados de dos qubits\n",
        "\n",
        "En muchos entornos de circuitos cuánticos, cuando se mide $q[0]$ en $c[0]$ y $q[1]$ en $c[1]$, la cadena impresa aparece como\n",
        "\n",
        "$$\n",
        "c[1]c[0].\n",
        "$$\n",
        "\n",
        "Por eso, si se aplica $X$ al qubit $q[0]$ y se mide, el estado lógico interno es $q[1]q[0]=01$, y el conteo impreso aparece como `'01'`.\n",
        "\n",
        "La clave práctica es no confundir el índice del qubit con la posición visual de la cadena de salida."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5fe30fef",
      "metadata": {
        "id": "5fe30fef"
      },
      "outputs": [],
      "source": [
        "# Estado lógico en orden |q1 q0>. Aplicar X a q0 produce |01>.\n",
        "state_q1q0 = basis_state(\"01\")\n",
        "print(\"Estado lógico |q1 q0>:\", show_state(state_q1q0, n=2))\n",
        "print(\"Conteo con etiquetas en orden lógico:\", sample_counts(state_q1q0, shots=100, seed=2, qiskit_order=False))\n",
        "\n",
        "# Si ya usamos |q1 q0>, la cadena coincide con c1c0.\n",
        "# El resultado impreso esperado es {'01': 100}."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "691ff54a",
      "metadata": {
        "id": "691ff54a"
      },
      "source": [
        "## 9. Divisor de haz ideal como moneda cuántica\n",
        "\n",
        "En el modelo ideal de moneda cuántica, un fotón que atraviesa un divisor de haz balanceado tiene dos posibles salidas:\n",
        "\n",
        "$$\n",
        "P(\\text{reflejado})=\\frac12,\\qquad P(\\text{transmitido})=\\frac12.\n",
        "$$\n",
        "\n",
        "Una forma abstracta de modelarlo es análoga a aplicar $H$ sobre un estado base: el resultado queda con dos amplitudes de magnitud $1/\\sqrt{2}$, y por la regla de Born las probabilidades son $1/2$ y $1/2$."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f7494740",
      "metadata": {
        "id": "f7494740"
      },
      "outputs": [],
      "source": [
        "state = H @ ket0\n",
        "print(\"Estado abstracto después de H|0>:\", show_state(state, n=1))\n",
        "print(\"Probabilidad salida 0:\", probs(state)[0])\n",
        "print(\"Probabilidad salida 1:\", probs(state)[1])\n",
        "print(\"Muestreo aproximado:\", sample_counts(state, shots=1000, seed=3))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ddf3e6a5",
      "metadata": {
        "id": "ddf3e6a5"
      },
      "source": [
        "## 10. Resumen operativo del módulo\n",
        "\n",
        "Reglas que conviene recordar:\n",
        "\n",
        "$$\n",
        "\\text{estado válido} \\iff \\sum_i |\\alpha_i|^2=1.\n",
        "$$\n",
        "\n",
        "$$\n",
        "P(i)=|\\alpha_i|^2.\n",
        "$$\n",
        "\n",
        "$$\n",
        "H|0\\rangle=|+\\rangle=\\frac{|0\\rangle+|1\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "$$\n",
        "H|1\\rangle=|-\\rangle=\\frac{|0\\rangle-|1\\rangle}{\\sqrt{2}}.\n",
        "$$\n",
        "\n",
        "$$\n",
        "X|0\\rangle=|1\\rangle,\\qquad X|1\\rangle=|0\\rangle.\n",
        "$$"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.x"
    },
    "title": "QBronze 1.2 — Sistemas cuánticos básicos",
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}