{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "33f4bb3c",
      "metadata": {},
      "source": [
        "---\n",
        "title: Visualize circuit timing\n",
        "description: Visualize the timing of generated circuits by generating a figure that you can view, download, or both.\n",
        "---\n",
        "\n",
        "{/* cspell:ignore mactex, backgroundcolor, lightgreen */}\n",
        "\n",
        "# Visualize circuit timing\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bfada4f0",
      "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=\"Package versions\">\n",
        "    The code on this page was developed using the following requirements.\n",
        "    We recommend using these versions or newer.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.3.0\n",
        "    qiskit-ibm-runtime~=0.43.1\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c3240c62",
      "metadata": {},
      "source": [
        "In addition to [visualizing instructions on a circuit](/docs/guides/visualize-circuits), you might want to visualize a circuit's scheduling by using the Qiskit [`timeline_drawer`](/docs/api/qiskit/qiskit.visualization.timeline_drawer) method. This visualization could help you to quickly spot idling time on qubits, for example. However, this method does not return accurate results for dynamic circuits.  To visualize dynamic circuit scheduling, use the `draw_circuit_schedule_timing` method, as described in the [Qiskit Runtime support](#qr-support) section.\n",
        "\n",
        "## Examples\n",
        "\n",
        "To visualize a scheduled circuit program, you can call this function with a set of control arguments. Most of the  output image's appearance can be modified by a stylesheet, but this is not required.\n",
        "\n",
        "### Draw with the default stylesheet\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "5b21b8c1",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/visualize-circuit-timing/extracted-outputs/5b21b8c1-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.visualization.timeline import draw\n",
        "from qiskit.providers.fake_provider import GenericBackendV2\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "qc = QuantumCircuit(2)\n",
        "qc.h(0)\n",
        "qc.cx(0, 1)\n",
        "\n",
        "backend = GenericBackendV2(5)\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n",
        "isa_circuit = pm.run(qc)\n",
        "\n",
        "draw(isa_circuit, target=backend.target)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "37f62ecd",
      "metadata": {},
      "source": [
        "### Draw with a stylesheet suited for program debugging\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "907dc46c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/visualize-circuit-timing/extracted-outputs/907dc46c-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.visualization.timeline import draw, IQXDebugging\n",
        "from qiskit.providers.fake_provider import GenericBackendV2\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "qc = QuantumCircuit(2)\n",
        "qc.h(0)\n",
        "qc.cx(0, 1)\n",
        "qc.measure_all()\n",
        "\n",
        "backend = GenericBackendV2(5)\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n",
        "isa_circuit = pm.run(qc)\n",
        "draw(isa_circuit, style=IQXDebugging(), target=backend.target)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5406fe18",
      "metadata": {},
      "source": [
        "You can create custom generator or layout functions and update an existing stylesheet with the custom functions. This way, you can control most of the appearance of the output image without modifying the codebase of the scheduled circuit drawer.  See the [`timeline_drawer`](/docs/api/qiskit/qiskit.visualization.timeline_drawer) API reference for more examples.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad24e3a0",
      "metadata": {},
      "source": [
        "<span id=\"qr-support\" />\n",
        "\n",
        "## Qiskit Runtime support\n",
        "\n",
        "While the timeline drawer built in to Qiskit is useful for static circuits, it might not accurately reflect the timing of [dynamic circuits](/docs/guides/classical-feedforward-and-control-flow) because of implicit operations such as broadcasting and branch determination. As part of dynamic circuit support, Qiskit Runtime returns the accurate circuit timing information inside the job results when requested.\n",
        "\n",
        "<Admonition type=\"note\" title=\"Notes\">\n",
        "  * This is an experimental function. It is in preview release status and is therefore subject to change.\n",
        "  * This function only applies to Qiskit Runtime Sampler jobs.\n",
        "  * Although the total circuit time is returned in the \"compilation\" metadata, this is NOT the time used for billing (quantum time).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "995a99e8",
      "metadata": {},
      "source": [
        "### Enable timing data retrieval\n",
        "\n",
        "To enable timing data retrieval, set the experimental `scheduler_timing` flag to `True` when running the primitive job.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "ab8c6c23",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=1)\n",
        "isa_circuit = pm.run(qc)\n",
        "\n",
        "sampler = SamplerV2(backend)\n",
        "sampler.options.experimental = {\n",
        "    \"execution\": {\n",
        "        \"scheduler_timing\": True,\n",
        "    },\n",
        "}\n",
        "\n",
        "sampler_job = sampler.run([isa_circuit])\n",
        "result = sampler_job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "531b2abd",
      "metadata": {},
      "source": [
        "### Access the circuit timing data\n",
        "\n",
        "When requested, the circuit timing data for each PUB is returned in the job result metadata, under `[\"compilation\"][\"scheduler_timing\"][\"timing\"]`. This field contains the raw timing information. To display the timing information, use the built-in visualization tool, as described in the [Visualize the timings](#visualize-timings) section.\n",
        "\n",
        "Use the following code to access the circuit timing data for the first PUB:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b6958d99",
      "metadata": {},
      "outputs": [],
      "source": [
        "job_result = sampler_job.result()\n",
        "circuit_schedule = job_result[0].metadata[\"compilation\"][\"scheduler_timing\"]\n",
        "circuit_schedule_timing = circuit_schedule[\"timing\"]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "20400401",
      "metadata": {},
      "source": [
        "#### Understand the raw timing data\n",
        "\n",
        "While visualizing the circuit timing data by using the `draw_circuit_schedule_timing` method is the most common use case, it might be useful to understand the structure of the raw timing data returned. This could help you, for example, to extract information programmatically.\n",
        "\n",
        "The timing data returned in `[\"compilation\"][\"scheduler_timing\"][\"timing\"]` is a list of strings. Each string represents a single instruction on some channel and is comma-separated into the following data types:\n",
        "\n",
        "* `Branch` - Determines whether the instruction is in a control flow (then / else) or a main branch.\n",
        "* `Instruction` - The gate and the qubit to operate on.\n",
        "* `Channel` - The channel that is being assigned with the instruction. It can be one of the following:\n",
        "  * `Qubit x` - The drive channel for qubit *x*.\n",
        "  * `AWGRx_y` (arbitrary waveform generator readout) - Used by readout channels to communicate when measuring qubits. The *x* and *y* arguments correspond to the readout instrument ID and the qubit number, respectively.\n",
        "* `T0` - The instruction start time within the complete schedule\n",
        "* `Duration` - The instruction's duration, in units of *dt* seconds,  where 1 dt = 1 scheduling cycle. You can find the `dt` value of a backend by using [`backend.dt`](/docs/api/qiskit/qiskit.providers.BackendV2#dt).\n",
        "* `Pulse` - The type of pulse operation being used.\n",
        "\n",
        "Example:\n",
        "\n",
        "```python\n",
        "main,barrier,Qubit 0,7,0,barrier # A barrier on the main branch on qubit 0 at time 7 with 0 duration\n",
        "main,reset_0,Qubit 0,7,64,play # A reset instruction on the main branch on qubit 0 at time 7 with duration 64 and a play operation\n",
        "...\n",
        "```\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9d34100f",
      "metadata": {},
      "source": [
        "<span id=\"visualize-timings\" />\n",
        "\n",
        "### Visualize the timings\n",
        "\n",
        "With `qiskit-ibm-runtime` v0.43.0 or later, you can visualize circuit timings. To visualize the timings, you first need to convert the result metadata to `fig` by using the [`draw_circuit_schedule_timing` method.](https://github.com/Qiskit/qiskit-ibm-runtime/blob/3d1bf1e1d49e5123841639fce259859c90ce9314/qiskit_ibm_runtime/visualization/draw_circuit_schedule_timings.py#L26) This method returns a `plotly` figure, which you can display directly, save to a file, or both.  For more information about the `plotly` commands to use, see [`fig.show()`](https://plotly.com/python-api-reference/generated/plotly.io.show.html) and  [`fig.write_image(\"<path.format>\")`.](https://plotly.com/python-api-reference/generated/plotly.io.write_image.html)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "4ad3acfb",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime.visualization import draw_circuit_schedule_timing\n",
        "\n",
        "# Create a figure from the metadata\n",
        "fig = draw_circuit_schedule_timing(\n",
        "    circuit_schedule=circuit_schedule_timing,\n",
        "    included_channels=None,\n",
        "    filter_readout_channels=False,\n",
        "    filter_barriers=False,\n",
        "    width=1000,\n",
        ")\n",
        "\n",
        "# Uncomment the following line to display the figure\n",
        "# fig.show(renderer=\"notebook\")\n",
        "\n",
        "# Save to a file\n",
        "# fig.write_html(\"scheduler_timing.html\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2788e992",
      "metadata": {},
      "source": [
        "![Hovering over the output shows information such as the start, finish, and duration.](https://quantum.cloud.ibm.com/docs/images/guides/visualize-circuit-timing/image_1.svg \"Example of a generated figure\")\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "323a7d38",
      "metadata": {},
      "source": [
        "#### Understand the generated figure\n",
        "\n",
        "The image of the circuit timing data output by `draw_circuit_schedule_timing` conveys the following information:\n",
        "\n",
        "* X axis is time in units of *dt* seconds,  where 1 dt = 1 scheduling cycle. You can find the `dt` value of a backend by using [`backend.dt`](/docs/api/qiskit/qiskit.providers.BackendV2#dt).\n",
        "* Y axis is the channel (think of channels as instruments that emit pulses).\n",
        "  * `Receive channel` - The only channel that isn't an instrument by itself. It is an instruction played on all channels that are part of a communication procedure with the hub at that time.\n",
        "  * `Qubit x` - The drive channel for qubit x.\n",
        "  * `AWGRx_y` (arbitrary waveform generator readout) - Used by readout channels to communicate when measuring qubits. The *x* and *y* arguments correspond to the readout instrument ID and the qubit number, respectively.\n",
        "  * `Hub` - Controls broadcasting.\n",
        "\n",
        "Additionally, each instruction has the format of *X\\_Y*, where *X* is the name of the instruction and *Y* is the pulse type. A `play` type applies control pulses, and a `capture` records the qubit's state. You can also hover over each instruction to get more details. For example, the previous figure shows a control pulse for the X gate applied to qubit 10 at 1161 dt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "86af09ca",
      "metadata": {},
      "source": [
        "### End-to-end example\n",
        "\n",
        "This example shows you how to enable the option, get the circuit timing information from the metadata, and display it as an image.\n",
        "\n",
        "First, set up the environment, define the circuits and convert them to ISA circuits, and define and run the jobs.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "b46908a0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            ">>>  Job ID:    d6huid73o3rs73cbhaeg (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(operational=True, simulator=False)\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",
        "# 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",
        "sampler.options.experimental = {\"execution\": {\"scheduler_timing\": True}}\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": "1bd5993c",
      "metadata": {},
      "source": [
        "Next, get the circuit schedule timing:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "7e943812",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'main,rz_0,Qubit 0,929,0,shift_phase\\nmain,sx_0,Qubit 0,929,8,play\\nmain,sx_0,Qubit 0,933,0,shift_phase\\nmain,rz_0,Qubit 0,937,0,shift_phase\\nmain,barrier,Qubit 0,937,0,barrier\\nmain,measure_0,Qubit 0,937,64,play\\nmain,measure_0,Qubit 0,1001,72,play\\nmain,measure_0,AWGR13_0,1048,240,capture\\nmain,measure_0,Qubit 0,1073,64,play\\nmain,barrier,Qubit 0,1489,0,barrier\\nmain,broadcast,Hub,1048,441,broadcast\\nmain,receive,Receive,1489,7,receive\\nthen,x_0,Qubit 0,1504,8,play\\nmain,barrier,Qubit 0,1521,0,barrier\\nmain,measure_0,Qubit 0,1521,64,play\\nmain,measure_0,Qubit 0,1585,72,play\\nmain,measure_0,AWGR13_0,1632,240,capture\\nmain,measure_0,Qubit 0,1657,64,play\\nmain,barrier,Qubit 0,1911,0,barrier\\nmain,barrier,Qubit 0,1911,0,barrier\\n'"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the circuit schedule timing\n",
        "result[0].metadata[\"compilation\"][\"scheduler_timing\"][\"timing\"]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "04db1f41",
      "metadata": {},
      "source": [
        "Finally, you can visualize and save the timing:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "9d1901c3",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime.visualization import draw_circuit_schedule_timing\n",
        "\n",
        "circuit_schedule = result[0].metadata[\"compilation\"][\"scheduler_timing\"][\n",
        "    \"timing\"\n",
        "]\n",
        "fig = draw_circuit_schedule_timing(\n",
        "    circuit_schedule=circuit_schedule,\n",
        "    included_channels=None,\n",
        "    filter_readout_channels=False,\n",
        "    filter_barriers=False,\n",
        "    width=1000,\n",
        ")\n",
        "\n",
        "# Uncomment the following line to display the figure\n",
        "# fig.show(renderer=\"notebook\")\n",
        "\n",
        "# Save to a file\n",
        "# fig.write_html(\"scheduler_timing.html\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9daa7cf7",
      "metadata": {},
      "source": [
        "## Next steps\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommendations\">\n",
        "  * [Classical feedforward and control flow](/docs/guides/classical-feedforward-and-control-flow) (dynamic circuits)\n",
        "  * [Visualize circuits](/docs/guides/visualize-circuits)\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": 5
}