{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1d313723",
   "metadata": {},
   "source": [
    "# QSilver: Introduction to Complex Numbers II\n",
    "\n",
    "Este notebook desarrolla la geometría de estados de un qubit, la esfera de Bloch, fases cuánticas, compuertas de Pauli, Hadamard, compuertas de fase y CRZ. Se asume que ya se conocen números complejos, forma polar, conjugación, normalización de vectores complejos y la regla de Born.\n",
    "\n",
    "La notación de Dirac se escribe sin macros personalizadas para maximizar compatibilidad en JupyterLab, Anaconda y Google Colab. Por ejemplo:\n",
    "\n",
    "$$\n",
    "\\left|0\\right\\rangle,\\qquad \\left|1\\right\\rangle,\\qquad \\left|\\psi\\right\\rangle=\\alpha\\left|0\\right\\rangle+\\beta\\left|1\\right\\rangle.\n",
    "$$\n",
    "\n",
    "El notebook usa visualizaciones con Matplotlib y, cuando Qiskit está disponible, visualizaciones nativas de Qiskit."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69fe7097",
   "metadata": {},
   "source": [
    "## Objetivos de aprendizaje\n",
    "\n",
    "Al finalizar el notebook deberías poder convertir un estado de un qubit entre amplitudes complejas y coordenadas de Bloch, distinguir fase global de fase relativa, interpretar Pauli, Hadamard, S, T y CRZ como transformaciones geométricas, y verificar estas ideas mediante Python y Qiskit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e414c687",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys, subprocess, importlib, math\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from mpl_toolkits.mplot3d import Axes3D  # noqa\n",
    "\n",
    "np.set_printoptions(precision=4, suppress=True)\n",
    "\n",
    "# Matrices básicas\n",
    "I = np.eye(2, dtype=complex)\n",
    "X = np.array([[0, 1], [1, 0]], dtype=complex)\n",
    "Y = np.array([[0, -1j], [1j, 0]], dtype=complex)\n",
    "Z = np.array([[1, 0], [0, -1]], dtype=complex)\n",
    "H = (1/np.sqrt(2)) * np.array([[1, 1], [1, -1]], dtype=complex)\n",
    "S = np.array([[1, 0], [0, 1j]], dtype=complex)\n",
    "Sdg = S.conj().T\n",
    "T = np.array([[1, 0], [0, np.exp(1j*np.pi/4)]], dtype=complex)\n",
    "Tdg = T.conj().T\n",
    "\n",
    "zero = np.array([1, 0], dtype=complex)\n",
    "one = np.array([0, 1], dtype=complex)\n",
    "plus = (zero + one) / np.sqrt(2)\n",
    "minus = (zero - one) / np.sqrt(2)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "91dff1c3",
   "metadata": {},
   "source": [
    "## 1. Estado puro de un qubit y esfera de Bloch\n",
    "\n",
    "Un estado puro de un qubit tiene la forma\n",
    "\n",
    "$$\n",
    "\\left|\\psi\\right\\rangle=\\alpha\\left|0\\right\\rangle+\\beta\\left|1\\right\\rangle,\n",
    "\\qquad |\\alpha|^2+|\\beta|^2=1.\n",
    "$$\n",
    "\n",
    "Salvo una fase global, puede escribirse como\n",
    "\n",
    "$$\n",
    "\\left|\\psi\\right\\rangle=\n",
    "\\cos\\frac{\\theta}{2}\\left|0\\right\\rangle+\n",
    "e^{i\\phi}\\sin\\frac{\\theta}{2}\\left|1\\right\\rangle.\n",
    "$$\n",
    "\n",
    "El ángulo $\\theta$ controla probabilidades y $\\phi$ controla fase relativa."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63c57435",
   "metadata": {},
   "outputs": [],
   "source": [
    "def normalize_state(psi):\n",
    "    psi = np.asarray(psi, dtype=complex)\n",
    "    norm = np.linalg.norm(psi)\n",
    "    if norm == 0:\n",
    "        raise ValueError(\"El estado cero no puede normalizarse\")\n",
    "    return psi / norm\n",
    "\n",
    "\n",
    "def state_from_angles(theta, phi):\n",
    "    # Devuelve el vector de estado asociado a theta y phi.\n",
    "    return np.array([np.cos(theta/2), np.exp(1j*phi)*np.sin(theta/2)], dtype=complex)\n",
    "\n",
    "\n",
    "def bloch_vector(psi):\n",
    "    # Convierte un estado normalizado de un qubit en vector de Bloch.\n",
    "    psi = normalize_state(psi)\n",
    "    alpha, beta = psi\n",
    "    x = 2*np.real(np.conjugate(alpha)*beta)\n",
    "    y = 2*np.imag(np.conjugate(alpha)*beta)\n",
    "    z = abs(alpha)**2 - abs(beta)**2\n",
    "    return np.array([x, y, z], dtype=float)\n",
    "\n",
    "\n",
    "def probs_z(psi):\n",
    "    psi = normalize_state(psi)\n",
    "    return {\"0\": float(abs(psi[0])**2), \"1\": float(abs(psi[1])**2)}\n",
    "\n",
    "print(\"Bloch(|0>) =\", bloch_vector(zero))\n",
    "print(\"Bloch(|1>) =\", bloch_vector(one))\n",
    "print(\"Bloch(|+>) =\", bloch_vector(plus))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6b5a6851",
   "metadata": {},
   "outputs": [],
   "source": [
    "def draw_bloch(vectors=None, labels=None, title=\"Esfera de Bloch\", trajectories=None, elev=22, azim=35):\n",
    "    # Dibuja una esfera de Bloch sencilla con vectores y trayectorias opcionales.\n",
    "    fig = plt.figure(figsize=(6, 5))\n",
    "    ax = fig.add_subplot(111, projection=\"3d\")\n",
    "    u = np.linspace(0, 2*np.pi, 80)\n",
    "    v = np.linspace(0, np.pi, 40)\n",
    "    xs = np.outer(np.cos(u), np.sin(v))\n",
    "    ys = np.outer(np.sin(u), np.sin(v))\n",
    "    zs = np.outer(np.ones_like(u), np.cos(v))\n",
    "    ax.plot_surface(xs, ys, zs, alpha=0.07, linewidth=0)\n",
    "    ax.plot_wireframe(xs, ys, zs, color=\"gray\", alpha=0.18, linewidth=0.4)\n",
    "\n",
    "    # ejes\n",
    "    axes = [np.array([1,0,0]), np.array([0,1,0]), np.array([0,0,1])]\n",
    "    names = [\"x\", \"y\", \"|0>\"]\n",
    "    for a, name in zip(axes, names):\n",
    "        ax.quiver(0,0,0,*a, length=1.05, arrow_length_ratio=0.08, linewidth=1)\n",
    "        ax.text(*(1.18*a), name)\n",
    "    ax.text(0,0,-1.2,\"|1>\")\n",
    "\n",
    "    if trajectories:\n",
    "        for traj in trajectories:\n",
    "            traj = np.array(traj)\n",
    "            ax.plot(traj[:,0], traj[:,1], traj[:,2], linewidth=2)\n",
    "    if vectors is not None:\n",
    "        for k, vec in enumerate(vectors):\n",
    "            vec = np.array(vec, dtype=float)\n",
    "            ax.quiver(0,0,0,*vec, length=1.0, arrow_length_ratio=0.12, linewidth=2)\n",
    "            if labels:\n",
    "                ax.text(*(1.12*vec), labels[k])\n",
    "\n",
    "    ax.set_xlim(-1.1,1.1); ax.set_ylim(-1.1,1.1); ax.set_zlim(-1.1,1.1)\n",
    "    ax.set_box_aspect([1,1,1])\n",
    "    ax.set_axis_off()\n",
    "    ax.view_init(elev=elev, azim=azim)\n",
    "    ax.set_title(title)\n",
    "    plt.show()\n",
    "\n",
    "# Visualización básica\n",
    "states = [zero, one, plus, minus]\n",
    "labels = [\"|0>\", \"|1>\", \"|+>\", \"|->\"]\n",
    "draw_bloch([bloch_vector(s) for s in states], labels, \"Estados base y ecuatoriales\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59a5c532",
   "metadata": {},
   "source": [
    "## 2. Fase global y fase relativa\n",
    "\n",
    "Los vectores $\\left|\\psi\\right\\rangle$ y $e^{i\\gamma}\\left|\\psi\\right\\rangle$ representan el mismo estado físico. Esa fase común no altera probabilidades ni posición en la esfera de Bloch.\n",
    "\n",
    "En cambio, cambiar solo la fase relativa entre $\\left|0\\right\\rangle$ y $\\left|1\\right\\rangle$ sí puede cambiar el estado físico. La fase relativa suele volverse visible después de aplicar una compuerta que mezcle amplitudes, como Hadamard."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0247a5cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "psi = state_from_angles(np.pi/2, np.pi/3)\n",
    "psi_global = np.exp(1j*np.pi/2) * psi\n",
    "\n",
    "print(\"psi =\", psi)\n",
    "print(\"i psi =\", psi_global)\n",
    "print(\"Probabilidades psi:\", probs_z(psi))\n",
    "print(\"Probabilidades i psi:\", probs_z(psi_global))\n",
    "print(\"Vector Bloch psi:\", bloch_vector(psi))\n",
    "print(\"Vector Bloch i psi:\", bloch_vector(psi_global))\n",
    "\n",
    "draw_bloch([bloch_vector(psi), bloch_vector(psi_global)], [\"psi\", \"i psi\"], \"Fase global: mismo punto\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "044fffe2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Fase relativa: los puntos se mueven sobre el ecuador.\n",
    "phis = np.linspace(0, 2*np.pi, 100)\n",
    "trajectory = [bloch_vector(state_from_angles(np.pi/2, phi)) for phi in phis]\n",
    "selected = [state_from_angles(np.pi/2, 0), state_from_angles(np.pi/2, np.pi/2), state_from_angles(np.pi/2, np.pi)]\n",
    "labels = [\"phi=0\", \"phi=pi/2\", \"phi=pi\"]\n",
    "draw_bloch([bloch_vector(s) for s in selected], labels, \"Fase relativa en el ecuador\", trajectories=[trajectory])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fd945a14",
   "metadata": {},
   "outputs": [],
   "source": [
    "# La fase relativa no cambia la medición Z inmediata, pero sí cambia después de H.\n",
    "phis = np.linspace(0, 2*np.pi, 300)\n",
    "prob0_direct = []\n",
    "prob0_after_h = []\n",
    "for phi in phis:\n",
    "    psi = state_from_angles(np.pi/2, phi)\n",
    "    prob0_direct.append(probs_z(psi)[\"0\"])\n",
    "    prob0_after_h.append(probs_z(H @ psi)[\"0\"])\n",
    "\n",
    "plt.figure(figsize=(7,4))\n",
    "plt.plot(phis, prob0_direct, label=\"Pr(0) medición directa\")\n",
    "plt.plot(phis, prob0_after_h, label=\"Pr(0) después de H\")\n",
    "plt.xlabel(\"fase relativa phi\")\n",
    "plt.ylabel(\"probabilidad\")\n",
    "plt.title(\"La fase relativa se revela al cambiar de base\")\n",
    "plt.grid(alpha=0.3)\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfa0bb23",
   "metadata": {},
   "source": [
    "## 3. Operadores Pauli y rotaciones de $\\pi$\n",
    "\n",
    "Las compuertas $X$, $Y$ y $Z$ son rotaciones de $\\pi$ alrededor de los ejes $x$, $y$ y $z$ de la esfera de Bloch. Esta interpretación geométrica complementa la multiplicación matricial: muestra qué componente del vector se preserva y cuáles se invierten."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "061285e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "def apply_gate(gate, psi):\n",
    "    return normalize_state(gate @ normalize_state(psi))\n",
    "\n",
    "psi0 = state_from_angles(np.pi/3, np.pi/4)\n",
    "gates = {\"psi\": I, \"X psi\": X, \"Y psi\": Y, \"Z psi\": Z}\n",
    "vectors = [bloch_vector(apply_gate(g, psi0)) for g in gates.values()]\n",
    "draw_bloch(vectors, list(gates.keys()), \"Efecto de X, Y y Z sobre un mismo estado\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f16da98",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Tablas algebraicas mínimas de Pauli\n",
    "for name, gate in [(\"X\", X), (\"Y\", Y), (\"Z\", Z)]:\n",
    "    print(name)\n",
    "    print(gate)\n",
    "    print(\"U dagger U =\")\n",
    "    print(gate.conj().T @ gate)\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67c046ad",
   "metadata": {},
   "source": [
    "### Ejercicio guiado: acción de $Y$\n",
    "\n",
    "Para\n",
    "\n",
    "$$\n",
    "\\left|\\psi\\right\\rangle=\\begin{pmatrix} i/\\sqrt{6} \\ (2+i)/\\sqrt{6} \\end{pmatrix},\n",
    "$$\n",
    "\n",
    "se tiene\n",
    "\n",
    "$$\n",
    "Y\\left|\\psi\\right\\rangle=\n",
    "\\begin{pmatrix}0&-i\\ i&0\\end{pmatrix}\n",
    "\\begin{pmatrix} i/\\sqrt{6} \\ (2+i)/\\sqrt{6} \\end{pmatrix}\n",
    "=\n",
    "\\begin{pmatrix} (1-2i)/\\sqrt{6} \\ -1/\\sqrt{6} \\end{pmatrix}.\n",
    "$$\n",
    "\n",
    "El punto conceptual es que $Y$ no solo intercambia componentes: también introduce fases imaginarias."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af030c1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "psi_y = np.array([1j/np.sqrt(6), (2+1j)/np.sqrt(6)], dtype=complex)\n",
    "result_y = Y @ psi_y\n",
    "print(\"Y psi =\", result_y)\n",
    "print(\"Norma:\", np.linalg.norm(result_y))\n",
    "draw_bloch([bloch_vector(psi_y), bloch_vector(result_y)], [\"psi\", \"Y psi\"], \"Acción de Y\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "955ecdf8",
   "metadata": {},
   "source": [
    "## 4. Hadamard y su eje geométrico\n",
    "\n",
    "Hadamard transforma información entre la dirección $z$ y la dirección $x$. Algebraicamente,\n",
    "\n",
    "$$\n",
    "H\\left|0\\right\\rangle=\\left|+\\right\\rangle,\\qquad\n",
    "H\\left|1\\right\\rangle=\\left|-\\right\\rangle.\n",
    "$$\n",
    "\n",
    "Geométricamente puede entenderse como una rotación de $\\pi$ alrededor del eje proporcional a $(1,0,1)$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "198732a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "axis_h = np.array([1,0,1], dtype=float) / np.sqrt(2)\n",
    "minus_axis_h = -axis_h\n",
    "draw_bloch([axis_h, minus_axis_h], [\"eigen +1\", \"eigen -1\"], \"Eje de Hadamard\")\n",
    "\n",
    "# Verificación del eigenestado asociado al eje +1\n",
    "psi_h_plus = normalize_state(np.array([np.cos(np.pi/8), np.sin(np.pi/8)], dtype=complex))\n",
    "print(\"psi_h_plus =\", psi_h_plus)\n",
    "print(\"H psi_h_plus =\", H @ psi_h_plus)\n",
    "print(\"¿Coinciden?\", np.allclose(H @ psi_h_plus, psi_h_plus))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a8e17e84",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Hadamard convierte fase relativa en diferencia de probabilidad.\n",
    "for state_name, st in [(\"|+>\", plus), (\"|->\", minus), (\"(|0>+i|1>)/sqrt(2)\", np.array([1,1j])/np.sqrt(2))]:\n",
    "    print(state_name)\n",
    "    print(\"Antes de H:\", probs_z(st))\n",
    "    print(\"Después de H:\", probs_z(H @ st))\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4417bd50",
   "metadata": {},
   "source": [
    "## 5. Compuertas de fase: S, S†, T y T†\n",
    "\n",
    "Las compuertas de fase son diagonales en la base computacional. Por tanto, no cambian los módulos de las amplitudes, pero sí cambian la fase relativa.\n",
    "\n",
    "$$\n",
    "S=\\begin{pmatrix}1&0\\0&i\\end{pmatrix},\\qquad\n",
    "T=\\begin{pmatrix}1&0\\0&e^{i\\pi/4}\\end{pmatrix}.\n",
    "$$\n",
    "\n",
    "En la esfera de Bloch, actúan como rotaciones alrededor del eje $z$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95579c9d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Comparar S y T sobre |+>\n",
    "states_phase = [plus, T @ plus, S @ plus, Tdg @ plus, Sdg @ plus]\n",
    "labels_phase = [\"|+>\", \"T|+>\", \"S|+>\", \"Tdg|+>\", \"Sdg|+>\"]\n",
    "draw_bloch([bloch_vector(s) for s in states_phase], labels_phase, \"Efectos de S, T y sus inversas\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "045249c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Trayectoria continua de rotaciones z sobre |+>\n",
    "def rz_matrix(lam):\n",
    "    return np.array([[np.exp(-1j*lam/2), 0], [0, np.exp(1j*lam/2)]], dtype=complex)\n",
    "\n",
    "angles = np.linspace(0, 2*np.pi, 120)\n",
    "traj_rz = [bloch_vector(rz_matrix(a) @ plus) for a in angles]\n",
    "draw_bloch([bloch_vector(plus), bloch_vector(S @ plus), bloch_vector(T @ plus)], [\"|+>\", \"S|+>\", \"T|+>\"], \"Rotación alrededor de z\", trajectories=[traj_rz])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e2d6bc6",
   "metadata": {},
   "source": [
    "### Ejercicio guiado: secuencia $X,H,S$\n",
    "\n",
    "Partiendo de $\\left|0\\right\\rangle$:\n",
    "\n",
    "1. $X\\left|0\\right\\rangle=\\left|1\\right\\rangle$.\n",
    "2. $H\\left|1\\right\\rangle=(\\left|0\\right\\rangle-\\left|1\\right\\rangle)/\\sqrt{2}$.\n",
    "3. $S$ multiplica la componente $\\left|1\\right\\rangle$ por $i$.\n",
    "\n",
    "Por tanto,\n",
    "\n",
    "$$\n",
    "S H X\\left|0\\right\\rangle =\n",
    "\\frac{1}{\\sqrt{2}}\\left|0\\right\\rangle-\n",
    "\\frac{i}{\\sqrt{2}}\\left|1\\right\\rangle.\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c28e2094",
   "metadata": {},
   "outputs": [],
   "source": [
    "state_xhs = S @ H @ X @ zero\n",
    "print(state_xhs)\n",
    "draw_bloch([bloch_vector(zero), bloch_vector(X@zero), bloch_vector(H@X@zero), bloch_vector(state_xhs)],\n",
    "           [\"inicio\", \"X\", \"HX\", \"SHX\"],\n",
    "           \"Trayectoria de la secuencia X, H, S\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b4158cf",
   "metadata": {},
   "source": [
    "## 6. CRZ: rotación de fase controlada\n",
    "\n",
    "La compuerta $CRZ(\\lambda)$ aplica una rotación alrededor de $z$ al qubit objetivo condicionada al estado del control. Su forma matricial convencional es\n",
    "\n",
    "$$\n",
    "CRZ(\\lambda)=\n",
    "\\begin{pmatrix}\n",
    "1&0&0&0\\\n",
    "0&1&0&0\\\n",
    "0&0&e^{-i\\lambda/2}&0\\\n",
    "0&0&0&e^{i\\lambda/2}\n",
    "\\end{pmatrix}.\n",
    "$$\n",
    "\n",
    "La operación es coherente: no mide el control."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1528fce9",
   "metadata": {},
   "outputs": [],
   "source": [
    "def crz_matrix(lam):\n",
    "    return np.diag([1, 1, np.exp(-1j*lam/2), np.exp(1j*lam/2)]).astype(complex)\n",
    "\n",
    "lam = np.pi/2\n",
    "CRZ = crz_matrix(lam)\n",
    "print(CRZ)\n",
    "\n",
    "# Efecto sobre los estados base de dos qubits\n",
    "basis2 = {\n",
    "    \"00\": np.array([1,0,0,0], dtype=complex),\n",
    "    \"01\": np.array([0,1,0,0], dtype=complex),\n",
    "    \"10\": np.array([0,0,1,0], dtype=complex),\n",
    "    \"11\": np.array([0,0,0,1], dtype=complex),\n",
    "}\n",
    "for label, vec in basis2.items():\n",
    "    out = CRZ @ vec\n",
    "    print(label, \"->\", out)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "964dcd8b",
   "metadata": {},
   "source": [
    "## 7. Visualización con Qiskit\n",
    "\n",
    "Las siguientes celdas intentan importar Qiskit. Si no está instalado, se intenta instalar `qiskit` y `qiskit-aer`. En algunos entornos, las visualizaciones 3D pueden depender de paquetes gráficos adicionales; por eso el notebook mantiene también visualizaciones propias con Matplotlib."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca2d268b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import display\n",
    "\n",
    "qiskit_available = False\n",
    "qiskit_viz_ok = False\n",
    "try:\n",
    "    from qiskit import QuantumCircuit\n",
    "    from qiskit.quantum_info import Statevector\n",
    "    try:\n",
    "        from qiskit.visualization import plot_bloch_vector, plot_bloch_multivector\n",
    "        qiskit_viz_ok = True\n",
    "    except Exception as exc:\n",
    "        print(\"Visualización nativa de Qiskit no disponible:\", exc)\n",
    "    qiskit_available = True\n",
    "except Exception as exc:\n",
    "    print(\"Qiskit no está disponible en este entorno:\", exc)\n",
    "    print(\"En Google Colab puede instalarse ejecutando: !pip install qiskit qiskit-aer\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a968a160",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Construcción de estados y visualización con Qiskit\n",
    "if qiskit_available:\n",
    "    qc = QuantumCircuit(1)\n",
    "    qc.h(0)\n",
    "    qc.s(0)\n",
    "    state = Statevector.from_instruction(qc)\n",
    "    print(state)\n",
    "    print(\"Vector de Bloch calculado manualmente:\", bloch_vector(np.array(state.data)))\n",
    "\n",
    "    if qiskit_viz_ok:\n",
    "        display(plot_bloch_multivector(state))\n",
    "\n",
    "    draw_bloch([bloch_vector(np.array(state.data))], [\"S H |0>\"], \"Misma visualización con Matplotlib\")\n",
    "else:\n",
    "    # Alternativa equivalente con NumPy cuando Qiskit no está instalado.\n",
    "    state = S @ H @ zero\n",
    "    print(\"Estado calculado con NumPy:\", state)\n",
    "    print(\"Vector de Bloch:\", bloch_vector(state))\n",
    "    draw_bloch([bloch_vector(state)], [\"S H |0>\"], \"Visualización con Matplotlib\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a6ae0f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Visualización de X, Y, Z, H, S, T\n",
    "if qiskit_available:\n",
    "    for name, instruction in [(\"X\", \"x\"), (\"Y\", \"y\"), (\"Z\", \"z\"), (\"H\", \"h\"), (\"S\", \"s\"), (\"T\", \"t\")]:\n",
    "        qc = QuantumCircuit(1)\n",
    "        qc.h(0)  # estado inicial |+>\n",
    "        getattr(qc, instruction)(0)\n",
    "        st = Statevector.from_instruction(qc)\n",
    "        print(name, np.array(st.data), bloch_vector(np.array(st.data)))\n",
    "else:\n",
    "    print(\"Qiskit no disponible; se muestran resultados equivalentes con NumPy.\")\n",
    "    for name, gate in [(\"X\", X), (\"Y\", Y), (\"Z\", Z), (\"H\", H), (\"S\", S), (\"T\", T)]:\n",
    "        st = gate @ plus\n",
    "        print(name, st, bloch_vector(st))\n",
    "\n",
    "vectors = []\n",
    "labels = []\n",
    "for name, gate in [(\"I\", I), (\"X\", X), (\"Y\", Y), (\"Z\", Z), (\"H\", H), (\"S\", S), (\"T\", T)]:\n",
    "    vectors.append(bloch_vector(gate @ plus))\n",
    "    labels.append(name + \"|+>\")\n",
    "draw_bloch(vectors, labels, \"Compuertas aplicadas a |+>\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bfa87069",
   "metadata": {},
   "outputs": [],
   "source": [
    "# CRZ en Qiskit y matriz equivalente con NumPy\n",
    "lambda_angle = np.pi/2\n",
    "if qiskit_available:\n",
    "    qc2 = QuantumCircuit(2)\n",
    "    qc2.x(0)       # control en 1\n",
    "    qc2.h(1)       # objetivo en superposición\n",
    "    qc2.crz(lambda_angle, 0, 1)\n",
    "    state2 = Statevector.from_instruction(qc2)\n",
    "    print(qc2)\n",
    "    print(state2)\n",
    "else:\n",
    "    # Orden conceptual: |control, objetivo>. Estado inicial |1> \\otimes |+>.\n",
    "    state2_initial = np.kron(one, plus)\n",
    "    state2 = crz_matrix(lambda_angle) @ state2_initial\n",
    "    print(\"Estado de dos qubits calculado con NumPy:\", state2)\n",
    "\n",
    "# El estado de dos qubits no se representa en una sola esfera de Bloch si hay correlaciones.\n",
    "# Aquí observamos que la fase aparece en las componentes con control activado.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e587a68d",
   "metadata": {},
   "source": [
    "## 8. Ejercicios propuestos\n",
    "\n",
    "1. Para $\\left|\\psi\\right\\rangle=\\cos(\\pi/6)\\left|0\\right\\rangle+e^{i\\pi/3}\\sin(\\pi/6)\\left|1\\right\\rangle$, calcule el vector de Bloch y verifíquelo con código.\n",
    "2. Compare $\\left|\\psi\\right\\rangle$ y $-i\\left|\\psi\\right\\rangle$ mediante probabilidades y vector de Bloch.\n",
    "3. Partiendo de $\\left|+\\right\\rangle$, aplique $S$, $T$ y $T^\\dagger$. Explique las posiciones relativas sobre el ecuador.\n",
    "4. Construya en Qiskit la secuencia $X,H,S$ y verifique que produce $(\\left|0\\right\\rangle-i\\left|1\\right\\rangle)/\\sqrt2$.\n",
    "5. Explique por qué aplicar $Z$ a $\\left|1\\right\\rangle$ no produce un estado físicamente distinguible de $\\left|1\\right\\rangle$, pero aplicar $Z$ a $\\left|+\\right\\rangle$ sí cambia el punto de Bloch.\n",
    "6. Use CRZ con $\\lambda=\\pi$ y determine qué componentes del vector de dos qubits adquieren fase."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "038cbcde",
   "metadata": {},
   "source": [
    "## Resumen final\n",
    "\n",
    "La esfera de Bloch surge porque un estado puro de un qubit tiene dos parámetros físicos después de normalizar y eliminar fase global. La latitud describe probabilidades en la base computacional y la longitud describe fase relativa.\n",
    "\n",
    "La fase global no cambia ningún resultado físico; la fase relativa sí puede modificar resultados cuando el circuito cambia de base y produce interferencia. Las compuertas $X$, $Y$ y $Z$ son rotaciones de $\\pi$ alrededor de ejes cartesianos; Hadamard rota alrededor de un eje inclinado; $S$ y $T$ realizan rotaciones discretas alrededor de $z$; CRZ introduce una fase condicionada de forma coherente.\n",
    "\n",
    "La combinación de álgebra, geometría y código permite interpretar estados de un qubit con precisión matemática y con intuición visual."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
