{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "b9fd7c5e-f3b8-4f0d-8632-821d601af52c",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Ejecutar trabajos en una sesión\"\n",
        "description: \"Cómo ejecutar un trabajo de computación cuántica en una sesión de Qiskit Runtime.\"\n",
        "---\n",
        "\n",
        "<span id=\"run-jobs-in-a-session\" />\n",
        "\n",
        "# Ejecutar trabajos en una sesión\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "817c70e7-29dd-4f70-85ad-6487d7c4b010",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "{/*\n",
        "  DO NOT EDIT THIS CELL!!!\n",
        "  This cell's content is generated automatically by a script. Anything you add\n",
        "  here will be removed next time the notebook is run. To add new content, create\n",
        "  a new cell before or after this one.\n",
        "  */}\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Versiones del paquete\">\n",
        "    El código de esta página se ha desarrollado siguiendo los siguientes requisitos.\n",
        "    Recomendamos utilizar estas versiones o posteriores.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.3.1\n",
        "    qiskit-ibm-runtime~=0.45.0\n",
        "    scipy~=1.17.1\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "be06e0ee-e593-4fe7-aa98-4f6e0dcbaa9f",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"Nota\">\n",
        "  Los usuarios de Open Plan no pueden enviar trabajos de sesión. Las cargas de trabajo deben ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n",
        "Utilice las sesiones cuando necesite un acceso dedicado y exclusivo a la QPU.\n",
        "\n",
        "<span id=\"set-up-to-use-sessions\" />\n",
        "\n",
        "## Configurar para usar sesiones\n",
        "\n",
        "Antes de iniciar una sesión, debes [configurar Qiskit Runtime](/docs/guides/install-qiskit) e inicializarlo como servicio:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "117d0471-1f98-489c-906f-8aab836cd877",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    Session,\n",
        "    SamplerV2 as Sampler,\n",
        "    EstimatorV2 as Estimator,\n",
        "    Executor,\n",
        ")\n",
        "\n",
        "service = QiskitRuntimeService()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e1cd9a3-7f36-4f42-8924-7f4dac202788",
      "metadata": {},
      "source": [
        "<span id=\"open-a-session\" />\n",
        "\n",
        "## Abrir una sesión\n",
        "\n",
        "Puede abrir una sesión en tiempo de ejecución utilizando el gestor de contexto `with Session(...)` o inicializando la clase `Session` clase Al iniciar una sesión, debe especificar una QPU pasando un objeto `backend` . La sesión se inicia cuando comienza la ejecución de su primer trabajo.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Si abre una sesión pero no le envía ningún trabajo durante 30 minutos, la sesión se cierra automáticamente.\n",
        "</Admonition>\n",
        "\n",
        "**Clase de sesión**\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2694bd4c-2011-4671-9625-3e7e941dd381",
      "metadata": {},
      "outputs": [],
      "source": [
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "session = Session(backend=backend)\n",
        "estimator = Estimator(mode=session)\n",
        "sampler = Sampler(mode=session)\n",
        "executor = Executor(mode=session)\n",
        "# Close the session because no context manager was used.\n",
        "session.close()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e0460f64-f5a8-4663-834a-a4c15c9ad82b",
      "metadata": {},
      "source": [
        "**Gestor de contexto**\n",
        "\n",
        "El gestor contextual abre y cierra automáticamente la sesión.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "192e5e57-2f9d-4f8e-b3e7-1dd31907e8a6",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import (\n",
        "    Session,\n",
        "    SamplerV2 as Sampler,\n",
        "    EstimatorV2 as Estimator,\n",
        "    Executor,\n",
        ")\n",
        "\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "with Session(backend=backend):\n",
        "    estimator = Estimator()\n",
        "    sampler = Sampler()\n",
        "    executor = Executor()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e4219769-6348-4d0c-8e13-830177a4e7b8",
      "metadata": {},
      "source": [
        "<span id=\"specify-length\" />\n",
        "\n",
        "<span id=\"session-length\" />\n",
        "\n",
        "## Duración de la sesión\n",
        "\n",
        "El tiempo máximo de vida de la sesión (TTL) determina cuánto tiempo puede durar una sesión.  Puedes fijar este valor con el parámetro `max_time` . Esto debería superar el tiempo de ejecución del trabajo más largo.\n",
        "\n",
        "Este temporizador se inicia cuando comienza la sesión.  Cuando se alcanza el valor, se cierra la sesión.  Los trabajos que se estén ejecutando finalizarán, pero los trabajos que sigan en cola fallarán.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n",
        "```python\n",
        "with Session(backend=backend, max_time=\"25m\"):\n",
        "  ...\n",
        "```\n",
        "\n",
        "También hay un valor de tiempo de vida interactivo (TTL interactivo) que no se puede configurar.  Si no hay trabajos de sesión en cola dentro de esa ventana, la sesión se desactiva temporalmente.\n",
        "\n",
        "Valores predeterminados:\n",
        "\n",
        "| Tipo de instancia (Plan Open o Premium)                                                           | TTL interactivo | TTL máximo |\n",
        "| ------------------------------------------------------------------------------------------------- | --------------- | ---------- |\n",
        "| Plan Premium                                                                                      | 60 sec\\*        | 8 h\\*      |\n",
        "| *\\* Algunas instancias del Plan Premium pueden estar configuradas para tener un valor diferente.* |                 |            |\n",
        "\n",
        "Para determinar el TTL máximo o el TTL interactivo de una sesión, siga las instrucciones de [Determinar los detalles de la sesión](#session-details) y busque el valor `max_time`o `interactive_timeout` , respectivamente.\n",
        "\n",
        "<span id=\"ends\" />\n",
        "\n",
        "<span id=\"end-a-session\" />\n",
        "\n",
        "## Finalizar una sesión\n",
        "\n",
        "Una sesión finaliza en las siguientes circunstancias:\n",
        "\n",
        "* Se alcanza el valor máximo de tiempo de espera (TTL), lo que provoca la cancelación de todos los trabajos en cola.\n",
        "* La sesión se cancela manualmente, lo que provoca la cancelación de todos los trabajos en cola.\n",
        "* La sesión se cierra manualmente.  La sesión deja de aceptar nuevos trabajos pero sigue ejecutando los trabajos en cola con prioridad.\n",
        "* Si utiliza Session como gestor de contexto, es decir, `with Session()`, la sesión se cierra automáticamente cuando finaliza el contexto (el mismo comportamiento que utilizando `session.close()`).\n",
        "\n",
        "<span id=\"close\" />\n",
        "\n",
        "<span id=\"close-a-session\" />\n",
        "\n",
        "### Cerrar sesión\n",
        "\n",
        "Una sesión se cierra automáticamente al salir del gestor de contexto. Cuando se sale del gestor de contexto de sesión, la sesión pasa al estado \"En curso, no acepta nuevos trabajos\". Esto significa que la sesión termina de procesar todos los trabajos en ejecución o en cola hasta que se alcanza el valor máximo de tiempo de espera. Una vez finalizados todos los trabajos, la sesión se cierra inmediatamente. Esto permite al programador ejecutar el siguiente trabajo sin esperar a que se agote el tiempo de espera de la sesión interactiva, reduciendo así el tiempo medio de espera de los trabajos. No puede presentar trabajos a puerta cerrada.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "c6646bed-3064-488f-b2ff-da9ba793775a",
      "metadata": {},
      "outputs": [],
      "source": [
        "with Session(backend=backend) as session:\n",
        "    estimator = Estimator()\n",
        "    sampler = Sampler()\n",
        "    job1 = estimator.run([estimator_pub])\n",
        "    job2 = sampler.run([sampler_pub])\n",
        "\n",
        "# The session is no longer accepting jobs but the submitted job will run to completion.\n",
        "result = job1.result()\n",
        "result2 = job2.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "20eb23f3-fc34-4f38-8ad7-85278ff4c823",
      "metadata": {},
      "source": [
        "<Admonition type=\"tip\">\n",
        "  Si no utiliza un gestor de contexto, cierre manualmente la sesión para evitar costes no deseados. Puedes cerrar una sesión en cuanto hayas terminado de enviarle trabajos. Cuando una sesión se cierra con `session.close()`, ya no acepta nuevos trabajos, pero los trabajos ya enviados seguirán ejecutándose hasta su finalización y se podrán recuperar sus resultados.\n",
        "</Admonition>\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "861cf6b3-9695-41eb-9d3d-d5293880ca75",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result1: PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(3, 2), dtype=float64>), stds=np.ndarray(<shape=(3, 2), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(3, 2), dtype=float64>), shape=(3, 2)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result2: PrimitiveResult([SamplerPubResult(data=DataBin(meas=BitArray(<shape=(3, 2), num_shots=4096, num_bits=2>), meas0=BitArray(<shape=(3, 2), num_shots=4096, num_bits=156>), shape=(3, 2)), metadata={'circuit_metadata': {}})], metadata={'execution': {'execution_spans': ExecutionSpans([DoubleSliceSpan(<start='2026-03-15 07:33:44', stop='2026-03-15 07:33:51', size=24576>)])}, 'version': 2})\n"
          ]
        }
      ],
      "source": [
        "session = Session(backend=backend)\n",
        "\n",
        "# If using qiskit-ibm-runtime earlier than 0.24.0, change `mode=` to `session=`\n",
        "estimator = Estimator(mode=session)\n",
        "sampler = Sampler(mode=session)\n",
        "job1 = estimator.run([estimator_pub])\n",
        "job2 = sampler.run([sampler_pub])\n",
        "print(f\"Result1: {job1.result()}\")\n",
        "print(f\"Result2: {job2.result()}\")\n",
        "\n",
        "# Manually close the session. Running and queued jobs will run to completion.\n",
        "session.close()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aad804ed-027f-49c3-9f7d-8851a4f3de3a",
      "metadata": {},
      "source": [
        "<span id=\"session-status\" />\n",
        "\n",
        "<span id=\"check-session-status\" />\n",
        "\n",
        "## Comprobar el estado de la sesión\n",
        "\n",
        "Puede consultar el estado de una sesión para conocer su estado actual utilizando `session.status()` o consultando la página [Cargas de trabajo](/workloads).\n",
        "\n",
        "El estado de la sesión puede ser uno de los siguientes\n",
        "\n",
        "* `Pending`: La sesión no se ha iniciado o se ha desactivado. El siguiente trabajo de sesión debe esperar en la cola como los demás trabajos.\n",
        "* `In progress, accepting new jobs`: La sesión está activa y acepta nuevos trabajos.\n",
        "* `In progress, not accepting new jobs`: La sesión está activa pero no acepta nuevos trabajos. Se rechaza el envío de trabajos a la sesión, pero los trabajos de sesión pendientes se ejecutarán hasta su finalización. La sesión se cierra automáticamente cuando finalizan todos los trabajos.\n",
        "* `Closed`: Se ha alcanzado el tiempo máximo de espera de la sesión o se ha cerrado explícitamente.\n",
        "\n",
        "<span id=\"session-details\" />\n",
        "\n",
        "<span id=\"determine-session-details\" />\n",
        "\n",
        "## Determinar los detalles de la sesión\n",
        "\n",
        "Para obtener una visión general de la configuración y el estado de una sesión, utilice la página `session.details() method`.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "82a2599e-3e32-4e01-9a36-b77a56af7647",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{'id': 'a9fd2f9d-6239-4451-a19c-9b45aa6a0618', 'backend_name': 'ibm_torino', 'interactive_timeout': 60, 'max_time': 28800, 'active_timeout': 28800, 'state': 'open', 'accepting_jobs': True, 'last_job_started': None, 'last_job_completed': None, 'started_at': None, 'closed_at': None, 'activated_at': None, 'mode': 'dedicated', 'usage_time': None}\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    Session,\n",
        "    EstimatorV2 as Estimator,\n",
        ")\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    print(session.details())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7db97311-25cb-4b81-9dba-494264c554c6",
      "metadata": {},
      "source": [
        "<span id=\"usage-patterns\" />\n",
        "\n",
        "## Patrones de uso\n",
        "\n",
        "Las sesiones son especialmente útiles para algoritmos que requieren una comunicación frecuente entre recursos clásicos y cuánticos.\n",
        "\n",
        "Ejemplo: Ejecute una carga de trabajo iterativa que utilice el optimizador clásico SciPy para minimizar una función de coste. En este modelo, SciPy utiliza la salida de la función de coste para calcular su próxima entrada.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "77cc7c96-e8c0-4970-b05e-361ddf74d707",
      "metadata": {},
      "outputs": [],
      "source": [
        "from scipy.optimize import minimize\n",
        "from qiskit.circuit.library import efficient_su2\n",
        "\n",
        "\n",
        "def cost_func(params, ansatz, hamiltonian, estimator):\n",
        "    # Return estimate of energy from estimator\n",
        "\n",
        "    energy = sum(\n",
        "        estimator.run([(ansatz, hamiltonian, params)]).result()[0].data.evs\n",
        "    )\n",
        "    return energy\n",
        "\n",
        "\n",
        "hamiltonian = SparsePauliOp.from_list(\n",
        "    [(\"YZ\", 0.3980), (\"ZI\", -0.3980), (\"ZZ\", -0.0113), (\"XX\", 0.1810)]\n",
        ")\n",
        "su2_ansatz = efficient_su2(hamiltonian.num_qubits)\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=3)\n",
        "ansatz = pm.run(su2_ansatz)\n",
        "mapped_hamiltonian = [\n",
        "    operator.apply_layout(ansatz.layout) for operator in hamiltonian\n",
        "]\n",
        "\n",
        "num_params = ansatz.num_parameters\n",
        "x0 = 2 * np.pi * np.random.random(num_params)\n",
        "\n",
        "session = Session(backend=backend)\n",
        "\n",
        "\n",
        "# If using qiskit-ibm-runtime earlier than 0.24.0, change `mode=` to `session=`\n",
        "estimator = Estimator(mode=session, options={\"default_shots\": int(1e4)})\n",
        "res = minimize(\n",
        "    cost_func,\n",
        "    x0,\n",
        "    args=(ansatz, mapped_hamiltonian, estimator),\n",
        "    method=\"cobyla\",\n",
        "    options={\"maxiter\": 25},\n",
        ")\n",
        "\n",
        "# Close the session because no context manager was used.\n",
        "session.close()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8c03c721-f040-4066-a9d2-f2dd39777789",
      "metadata": {},
      "source": [
        "<span id=\"two-vqe\" />\n",
        "\n",
        "<span id=\"run-two-vqe-algorithms-in-a-session-by-using-threading\" />\n",
        "\n",
        "## Ejecuta dos algoritmos VQE en una sesión utilizando subprocesos\n",
        "\n",
        "Puedes sacar más partido a una sesión ejecutando varias cargas de trabajo simultáneamente. El siguiente ejemplo muestra cómo puede ejecutar dos algoritmos VQE, cada uno con un optimizador clásico diferente, simultáneamente dentro de una misma sesión. Las etiquetas de trabajo también se utilizan para diferenciar los trabajos de cada carga de trabajo.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  El siguiente bloque de código devolverá un error para los usuarios en el Open Plan porque utiliza sesiones. Las cargas de trabajo en el Open Plan sólo pueden ejecutarse en [modo trabajo](/docs/guides/execution-modes#job-mode) o [por lotes](/docs/guides/execution-modes#batch-mode).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e83f7d60-13f0-4df1-9766-6fa67c970362",
      "metadata": {},
      "outputs": [],
      "source": [
        "from concurrent.futures import ThreadPoolExecutor\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "\n",
        "\n",
        "def minimize_thread(estimator, method):\n",
        "    return minimize(\n",
        "        cost_func,\n",
        "        x0,\n",
        "        args=(ansatz, mapped_hamiltonian, estimator),\n",
        "        method=method,\n",
        "        options={\"maxiter\": 25},\n",
        "    )\n",
        "\n",
        "\n",
        "with Session(backend=backend), ThreadPoolExecutor() as executor:\n",
        "    estimator1 = Estimator()\n",
        "    estimator2 = Estimator()\n",
        "\n",
        "    # Use different tags to differentiate the jobs.\n",
        "    estimator1.options.environment.job_tags = [\"cobyla\"]\n",
        "    estimator2.options.environment.job_tags = [\"nelder-mead\"]\n",
        "\n",
        "    # Submit the two workloads.\n",
        "    cobyla_future = executor.submit(minimize_thread, estimator1, \"cobyla\")\n",
        "    nelder_mead_future = executor.submit(\n",
        "        minimize_thread, estimator2, \"nelder-mead\"\n",
        "    )\n",
        "\n",
        "    # Get workload results.\n",
        "    cobyla_result = cobyla_future.result()\n",
        "    nelder_mead_result = nelder_mead_future.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0194fb4b-0917-45d7-aeb1-9fdff38cc5c5",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximos pasos\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendaciones\">\n",
        "  * Pruebe un ejemplo en el tutorial [Algoritmo de optimización aproximada cuántica (QAOA)](/docs/tutorials/quantum-approximate-optimization-algorithm).\n",
        "  * Revise la referencia [Session API](/docs/api/qiskit-ibm-runtime/session).\n",
        "  * Comprender los [límites del](/docs/guides/job-limits) trabajo al enviar un trabajo a una QPU de IBM®.\n",
        "  * Revisar [los modos de ejecución Preguntas frecuentes](/docs/guides/execution-modes-faq).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}