{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "83596f71-5e63-4aa2-907b-75de8813d85a",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Ejecutar circuitos dinámicos\"\n",
        "description: \"Utiliza Qiskit Runtime para el flujo de control y la transmisión directa clásicos, también conocidos como circuitos dinámicos\"\n",
        "---\n",
        "\n",
        "<span id=\"execute-dynamic-circuits\" />\n",
        "\n",
        "# Ejecutar circuitos dinámicos\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2228a8a2-02b7-4a0b-bbe8-0c2933fbe44a",
      "metadata": {},
      "source": [
        "Los circuitos dinámicos son potentes herramientas que permiten medir los qubits en medio de la ejecución de un circuito cuántico y, a continuación, realizar operaciones lógicas clásicas dentro del circuito, basándose en el resultado de esas mediciones realizadas durante la ejecución.  Este proceso también se conoce como «*feedforward clásico* ». Aunque aún estamos en una fase inicial en lo que respecta a comprender cuál es la mejor manera de aprovechar los circuitos dinámicos, la comunidad de investigación cuántica ya ha identificado varios casos de uso, como los siguientes:\n",
        "\n",
        "* Preparación eficiente de estados cuánticos, como [el estado GHZ](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.5.030339), [el estado W](https://arxiv.org/abs/2403.07604) (para más información sobre el estado W, véase también [«Preparación de estados mediante circuitos poco profundos con propagación hacia adelante»](https://arxiv.org/abs/2307.14840) ) y una amplia clase de [estados de producto matricial](https://arxiv.org/abs/2404.16083)\n",
        "* [Entrelazamiento eficiente a larga distancia](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.5.030339) entre qubits en un mismo chip mediante circuitos de poca profundidad\n",
        "* [Muestreo](https://arxiv.org/pdf/2505.04705) eficiente de circuitos similares a los IQP\n",
        "\n",
        "Sin embargo, estas mejoras que aportan los circuitos dinámicos conllevan algunas desventajas. Las mediciones a mitad de circuito y las operaciones clásicas suelen tener un tiempo de ejecución mayor que las puertas de dos qubits, y este aumento del tiempo podría anular las ventajas de una menor profundidad del circuito. Por lo tanto, la reducción de la duración de las mediciones a mitad de circuito constituye un área prioritaria de mejora ahora que IBM Quantum® lanza la [nueva versión](/docs/guides/latest-updates#new-version-dynamic-circuits) de circuitos dinámicos.  Para conocer otras restricciones relacionadas con el uso de circuitos dinámicos, consulte la tabla de compatibilidad de las funciones [«Estimador»](/docs/guides/estimator-options#options-compatibility-table) y «[Muestreador](/docs/guides/sampler-options#options-compatibility-table) ».\n",
        "\n",
        "[La especificación « OpenQASM e 3»](https://openqasm.com/language/classical.html#looping-and-branching) define varias estructuras de flujo de control, pero Qiskit Runtime actualmente solo admite la instrucción `if` condicional. En Qiskit SDK, esto se corresponde con el método [if\\_test](/docs/api/qiskit/qiskit.circuit.QuantumCircuit#if_test) de [QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit). Este método devuelve un [gestor de contexto](https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers) y suele utilizarse en una `with` instrucción. En esta guía se explica cómo utilizar esta instrucción condicional.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Los ejemplos de código de esta guía utilizan la instrucción de medición estándar para realizar mediciones en medio del circuito. No obstante, se recomienda utilizar la `MidCircuitMeasure` instrucción en su lugar, si el backend la admite. Consulte la [sección «Mediciones en el punto medio del circuito»](#midcircuit) para obtener más información.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7e9dc06d-85fa-4425-9bd1-ce5085be9741",
      "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 teniendo en cuenta los siguientes requisitos.\n",
        "    Recomendamos utilizar estas versiones o posteriores.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.5.0\n",
        "    qiskit-ibm-runtime~=0.47.0\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c4616d43-f296-4a84-a79c-deb37fcdac15",
      "metadata": {},
      "source": [
        "<span id=\"find-backends-that-support-dynamic-circuits\" />\n",
        "\n",
        "## Busca backends que admitan circuitos dinámicos\n",
        "\n",
        "Para encontrar todos los backends a los que tu cuenta tiene acceso y que admiten circuitos dinámicos, ejecuta un código como el siguiente.  En este ejemplo se da por hecho que has [guardado tus credenciales de inicio de sesión](/docs/guides/save-credentials).  También puede [especificar explícitamente las credenciales](/docs/guides/initialize-account#explicit) al inicializar su cuenta de servicio de Qiskit Runtime.  Esto te permitiría ver los backends disponibles en una instancia concreta o en un tipo de plan, por ejemplo.\n",
        "\n",
        "<Admonition type=\"note\" title=\"Notas\">\n",
        "  * Los backends disponibles para la cuenta dependen de la instancia especificada en las credenciales.\n",
        "  * La nueva versión de los circuitos dinámicos ya está disponible para todos los usuarios en todos los servidores. Consulte [el anuncio](/docs/guides/latest-updates#new-dynamic-circuits) para obtener más detalles.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "a63f07b1-9e8a-4086-ab01-552bd444dc9b",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_kingston')>]\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "dc_backends = service.backends(dynamic_circuits=True)\n",
        "print(dc_backends)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1dca7d79-99ea-42bd-86f7-182266907a85",
      "metadata": {},
      "source": [
        "<span id=\"midcircuit\" />\n",
        "\n",
        "<span id=\"mid-circuit-measurements\" />\n",
        "\n",
        "## Mediciones en el punto medio del circuito\n",
        "\n",
        "Antes de « `qiskit-ibm-runtime`v0.43.0 », `measure` era la única instrucción de medición disponible en Qiskit. Sin embargo, las mediciones en el medio del circuito tienen requisitos de ajuste distintos a los de las mediciones *en los extremos* (mediciones que se realizan al final de un circuito). Por ejemplo, hay que tener en cuenta la duración de la instrucción al ajustar una medición en medio del circuito, ya que las instrucciones más largas provocan más ruido en los circuitos. No es necesario tener en cuenta la duración de las instrucciones en las mediciones de terminales, ya que no hay instrucciones después de ellas.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  La `MidCircuitMeasure` instrucción se corresponde con la `measure_2` instrucción indicada en el archivo. del backend `supported_instructions`. Sin embargo, no `measure_2` es compatible con todos los backends. Utiliza `service.backends(filters=lambda b: \"measure_2\" in b.supported_instructions)` para encontrar backends que lo admitan.  Es posible que en el futuro se añadan nuevas medidas, pero esto no está garantizado.\n",
        "</Admonition>\n",
        "\n",
        "<span id=\"midcircuitmeasure-method\" />\n",
        "\n",
        "### Método de `MidCircuitMeasure`\n",
        "\n",
        "En `qiskit-ibm-runtime` v0.43.0 se presentó la `MidCircuitMeasure` instrucción. Como su nombre indica, se trata de una nueva instrucción de medición optimizada para la fase intermedia del circuito en las QPU de IBM®.  Aunque puedes utilizar `QuantumCircuit.measure` para realizar una medición en medio del circuito, debido a su diseño, suele `MidCircuitMeasure` ser una mejor opción.  Por ejemplo, supone una menor carga para el circuito que cuando se utiliza `QuantumCircuit.measure`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "87dfb9c7-1dc2-4752-bdc1-17b9144369f4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "     ┌───┐┌────────────┐\n",
            "q_0: ┤ X ├┤0           ├\n",
            "     └───┘│            │\n",
            "q_1: ─────┤  Measure_2 ├\n",
            "          │            │\n",
            "c_0: ═════╡0           ╞\n",
            "          └────────────┘\n",
            "c_1: ═══════════════════\n",
            "                        \n"
          ]
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime.circuit import MidCircuitMeasure\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, dynamic_circuits=True\n",
        ")\n",
        "\n",
        "circ = QuantumCircuit(2, 2)\n",
        "circ.x(0)\n",
        "circ.append(MidCircuitMeasure(), [0], [0])\n",
        "# circ.measure([0], [0])\n",
        "# circ.measure_all()\n",
        "print(circ.draw(cregbundle=False))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2b603f88-2e0e-4819-8342-8e3662949ff3",
      "metadata": {},
      "source": [
        "<Admonition type=\"attention\" title=\"Notas importantes\">\n",
        "  * Para poder utilizar las mediciones, debe haber al menos un registro clásico.\n",
        "  * La primitiva Sampler requiere mediciones del circuito. Puedes añadir medidas de circuitos con la primitiva «Estimator», pero estas se ignoran.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9401a5cc-49e0-48b6-8deb-88e9b7da5a5a",
      "metadata": {},
      "source": [
        "<span id=\"store\" />\n",
        "\n",
        "## `Store`\n",
        "\n",
        "A partir de `qiskit-ibm-runtime` la versión 0.47.0, puedes utilizar la [`store`](/docs/api/qiskit/circuit#store) instrucción para guardar el resultado de una expresión clásica, si vas a utilizarla repetidamente. Las operaciones se paralelizan automáticamente, lo que hace que tu código sea mucho más eficiente en tiempo de ejecución.\n",
        "\n",
        "Para obtener más información, consulta la guía [sobre control de avance clásico y flujo de control](/docs/guides/classical-feedforward-and-control-flow#store).\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Cuando se utiliza `store` para guardar un valor en un registro clásico en un backend real, dicho valor solo se guarda en memoria durante la ejecución y no se copia ni se devuelve en el resultado del trabajo.\n",
        "\n",
        "  Por ejemplo, en el siguiente código, `temp` tiene el mismo valor que `creg` durante la `if_test` ejecución, y funciona como se esperaba. Sin embargo, una vez finalizado el trabajo, el `temp` objeto `BitArray` devuelto en el resultado del trabajo **no** contiene el valor de `creg`.  Es decir, `job.result()[0].data.temp` es `0`.\n",
        "\n",
        "  ```python\n",
        "  creg = ClassicalRegister(3, \"c\")\n",
        "  temp = ClassicalRegister(3, \"temp\")\n",
        "  ...\n",
        "  qc.store(temp, creg)\n",
        "  with circuit.if_test((temp, 0b001)):\n",
        "      ...\n",
        "  ```\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3f374a74-c331-40de-8594-686e471058a2",
      "metadata": {},
      "source": [
        "<span id=\"full-example\" />\n",
        "\n",
        "## Ejemplo completo\n",
        "\n",
        "El siguiente código crea y ejecuta un circuito dinámico en un dispositivo de hardware de la seri IBM®.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "a7a9c998-4031-432f-978b-83edc040798f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            ">>>  Job ID:    d9bjipbv6alc73ct567g (DONE)\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2, QiskitRuntimeService\n",
        "from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, dynamic_circuits=True\n",
        ")\n",
        "\n",
        "# Create a dynamic circuit\n",
        "\n",
        "qubits = QuantumRegister(1)\n",
        "clbits = ClassicalRegister(1)\n",
        "qc = QuantumCircuit(qubits, clbits)\n",
        "(q0,) = qubits\n",
        "(c0,) = clbits\n",
        "\n",
        "qc.h(q0)\n",
        "qc.measure(q0, c0)\n",
        "with qc.if_test((c0, 1)):\n",
        "    qc.x(q0)\n",
        "qc.measure(q0, c0)\n",
        "\n",
        "\n",
        "# Convert to an ISA circuit for the given backend\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n",
        "isa_circuit = pm.run(qc)\n",
        "\n",
        "# Generate samplers for backend targets\n",
        "sampler = SamplerV2(backend)\n",
        "\n",
        "# Submit jobs\n",
        "sampler_job = sampler.run([isa_circuit])\n",
        "result = sampler_job.result()\n",
        "\n",
        "print(\n",
        "    f\">>> {' Job ID:':<10}  {sampler_job.job_id()} ({sampler_job.status()})\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c5b14dcf-edf4-4fef-bb49-e6b86f06afb0",
      "metadata": {},
      "source": [
        "<span id=\"qiskit-runtime-limitations\" />\n",
        "\n",
        "## Qiskit Runtime limitaciones\n",
        "\n",
        "Ten en cuenta las siguientes restricciones al ejecutar circuitos dinámicos en Qiskit Runtime.\n",
        "\n",
        "* Debido a la memoria física limitada de la electrónica de control, también hay un límite en el número de `if` instrucciones y en el tamaño de sus operandos. Este límite depende del número de transmisiones y del número de bits transmitidos en un trabajo (no en un circuito).\n",
        "\n",
        "  Al procesar una `if` condición, es necesario transferir los datos de medición a la lógica de control para que esta pueda realizar dicha evaluación. Una transmisión es una transferencia de datos clásicos únicos, y los bits transmitidos son el número de bits clásicos que se transfieren. Tenga en cuenta lo siguiente:\n",
        "\n",
        "  ```python\n",
        "  c0 = ClassicalRegister(3)\n",
        "  c1 = ClassicalRegister(5)\n",
        "  ...\n",
        "  with circuit.if_test((c0, 1)) ...\n",
        "  with circuit.if_test((c0, 3)) ...\n",
        "  with circuit.if_test((c1[2], 1)) ...\n",
        "  ```\n",
        "\n",
        "  En el ejemplo de código anterior, los dos primeros `if_test` objetos de `c0` se consideran una sola difusión, ya que el contenido de no `c0` ha cambiado y, por lo tanto, no es necesario volver a difundirlo. La emisión `if_test` de hoy `c1` es una retransmisión. La primera transmite los tres bits, `c0` y la segunda solo transmite un bit, lo que hace un total de cuatro bits transmitidos.\n",
        "\n",
        "  Actualmente, si se transmiten 60 bits cada vez, el trabajo puede tener aproximadamente 300 transmisiones. Sin embargo, si se transmite solo un bit cada vez, la tarea puede llegar a tener 2400 transmisiones.\n",
        "\n",
        "* El operando utilizado en una `if_test` instrucción debe tener 32 bits o menos. Por lo tanto, si estás comparando un entero `ClassicalRegister`, el tamaño de ese entero `ClassicalRegister` debe ser de 32 bits o menos. Sin embargo, si solo estás comparando un único bit de un `ClassicalRegister`, este `ClassicalRegister` puede tener cualquier tamaño (ya que el operando es de un solo bit).\n",
        "\n",
        "  Por ejemplo, el bloque de código «No válido» no funciona porque `cr` tiene más de 32 bits.  No obstante, puedes utilizar un registro clásico de más de 32 bits si solo vas a comprobar un bit, tal y como se muestra en el bloque de código «Válido».\n",
        "\n",
        "  <Tabs>\n",
        "    <TabItem value=\"Not valid\" label=\"Not valid\">\n",
        "      ```python\n",
        "         cr = ClassicalRegister(50)\n",
        "         qr = QuantumRegister(50)\n",
        "         circuit = QuantumCircuit(qr, cr)\n",
        "         ...\n",
        "         circ.measure(qr, cr)\n",
        "         with circ.if_test((cr, 15)):\n",
        "            ...\n",
        "      ```\n",
        "    </TabItem>\n",
        "\n",
        "    <TabItem value=\"Valid\" label=\"Valid\">\n",
        "      ```python\n",
        "         cr = ClassicalRegister(50)\n",
        "         qr = QuantumRegister(50)\n",
        "         circuit = QuantumCircuit(qr, cr)\n",
        "         ...\n",
        "         circ.measure(qr, cr)\n",
        "         with circ.if_test((cr[5], 1)):\n",
        "            ...\n",
        "      ```\n",
        "    </TabItem>\n",
        "  </Tabs>\n",
        "\n",
        "* No se permiten las condiciones anidadas. Por ejemplo, el siguiente fragmento de código no funcionará porque contiene un `if_test` dentro de otro `if_test`:\n",
        "\n",
        "  <Tabs>\n",
        "    <TabItem value=\"Not valid\" label=\"Not valid\">\n",
        "      ```python\n",
        "         c1 = ClassicalRegister(1, \"c1\")\n",
        "         c2 = ClassicalRegister(2, \"c2\")\n",
        "         ...\n",
        "         with circ.if_test((c1, 1)):\n",
        "          with circ.if_test(c2, 1)):\n",
        "           ...\n",
        "      ```\n",
        "    </TabItem>\n",
        "\n",
        "    <TabItem value=\"Valid\" label=\"Valid\">\n",
        "      ```python\n",
        "      cr = ClassicalRegister(2)\n",
        "      ...\n",
        "      with circuit.if_test((cr, 0b11)):\n",
        "        ...\n",
        "      ```\n",
        "    </TabItem>\n",
        "  </Tabs>\n",
        "\n",
        "* No se admite el uso de `reset` o medidas dentro de las condiciones.\n",
        "\n",
        "* No se admiten operaciones aritméticas.\n",
        "\n",
        "* Consulte la [tabla de características de « OpenQASM 3»](/docs/guides/qasm-feature-table) para determinar qué funciones de « OpenQASM 3» son compatibles con Qiskit y Qiskit Runtime.\n",
        "\n",
        "* Cuando se utiliza « OpenQASM 3» (en lugar de `QuantumCircuit`) como formato de entrada para pasar circuitos a las primitivas de « Qiskit Runtime », solo se admiten las instrucciones que se pueden cargar en Qiskit. Las operaciones clásicas, por ejemplo, no son compatibles porque no se pueden cargar en Qiskit. Para obtener más información, consulta «[Importar un programa de `OpenQASM` 3 a Qiskit](/docs/guides/interoperate-qiskit-qasm3#import-an-openqasm-3-program-into-qiskit) ».\n",
        "\n",
        "* Las `switch` instrucciones `for`, `while`, y no son compatibles.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ca6e0493-d364-40bf-9f47-2e3feed9f82c",
      "metadata": {},
      "source": [
        "<span id=\"use-dynamic-circuits-with-estimator\" />\n",
        "\n",
        "## Utiliza circuitos dinámicos con Estimator\n",
        "\n",
        "Dado que Estimator no admite circuitos dinámicos, puedes utilizar [Sampler](/docs/guides/get-started-with-sampler) y crear tus propios circuitos de medición.\n",
        "\n",
        "Para reproducir el comportamiento de Estimator, sigue estos pasos:\n",
        "\n",
        "1. Agrupa los términos de todas las variables observables en una partición.  Esto se puede hacer utilizando, por ejemplo, [`PauliList` API](/docs/api/qiskit/qiskit.quantum_info.PauliList#group_qubit_wise_commuting)el.\n",
        "   <Admonition type=\"note\">\n",
        "     Puedes utilizar el [`BitArray`](/docs/en/api/qiskit/qiskit.primitives.BitArray#expectation_values) atributo «primitive» para calcular los valores esperados de las observables proporcionadas.\n",
        "   </Admonition>\n",
        "2. Realiza un ciclo de cambio de base por cada partición (el cambio de base que sea necesario para cada partición). Consulte el [módulo `measurement_bases`](https://qiskit.github.io/qiskit-addon-utils/apidocs/qiskit_addon_utils.exp_vals.html#qiskit_addon_utils.exp_vals.get_measurement_bases) complementario «Measurement bases» para obtener más información. Para obtener más información, consulta la [documentación](https://qiskit.github.io/qiskit-addon-utils/) del paquete de utilidades del complemento Qiskit.\n",
        "3. Suma los resultados de cada partición.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "07045397-6db8-4151-a1e4-de24a8f39ab6",
      "metadata": {},
      "source": [
        "<span id=\"restrictions\" />\n",
        "\n",
        "## Restricciones\n",
        "\n",
        "Consulte cualquier [tabla de compatibilidad de funciones](/docs/guides/estimator-options#options-compatibility-table) para conocer las restricciones que se aplican al utilizar circuitos dinámicos. Ten en cuenta que la compatibilidad de las funciones no depende de los primitivos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6688e2e3-766b-4194-99db-ae097e9b8bc7",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Próximos pasos\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recomendaciones\">\n",
        "  * Descubre cómo implementar un desacoplamiento dinámico preciso mediante el uso de [stretch](/docs/guides/stretch).\n",
        "  * Consulte la guía [sobre el control directo clásico y el flujo de control](/docs/guides/classical-feedforward-and-control-flow).\n",
        "  * Utiliza [la visualización del esquema del circuito](/docs/guides/qiskit-runtime-circuit-timing) para depurar y optimizar tus circuitos dinámicos.\n",
        "  * No todas las funciones son compatibles con los circuitos dinámicos. Consulta la sección de compatibilidad de funciones de [Sampler](/docs/guides/sampler-options#feature-compatibility) para obtener más detalles.\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
}