{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "10ab20cd",
   "metadata": {},
   "source": [
    "# How to retrieve and display data with Boulder Opal\n",
    "\n",
    "**Fetch job results, navigate execution history, and inspect virtual device state using the Scale Up client**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10abbody",
   "metadata": {},
   "source": [
    "Inspecting past results is a routine part of working with a virtual device. After a calibration routine completes, you often want to revisit individual job outputs, compare runs across time, or confirm the current state of the underlying device model before queuing new work. Boulder Opal Scale Up exposes dedicated client methods for each of these needs, together with display helpers that render the data in notebook-friendly form.\n",
    "\n",
    "In this user guide, you will learn how to fetch full results and summaries for individual jobs, list and filter jobs across the device history, review device-level state through both summary and detailed views, and access the gate calibrations and readout classifiers stored in the device data.\n",
    "\n",
    "This user guide assumes you have a Boulder Opal Scale Up client session, a virtual device with at least one completed job, and the job IDs you want to inspect. If you are starting from scratch, complete the [get started tutorial](https://docs.q-ctrl.com/boulder-opal/autocalibration/get-started-with-boulder-opal-autocalibration) first."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20cd30ef",
   "metadata": {},
   "source": [
    "## 1. Set the device context\n",
    "\n",
    "Select the target device."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30ef40ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "await client.set_current_device(\"<your-device-name>\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5bd2aff",
   "metadata": {},
   "source": [
    "## 2. Navigate job history\n",
    "\n",
    "Use `get_jobs()` to list recent jobs on the current device. You can filter by device name or job name, control the page size with `limit`, and paginate through older results with `page`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ad48880",
   "metadata": {},
   "outputs": [],
   "source": [
    "recent_jobs = await client.get_jobs(limit=5)\n",
    "client.display(recent_jobs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c152f9c0",
   "metadata": {},
   "source": [
    "Filtering by name is useful when you want to compare all runs of a specific experiment or routine across the device history."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8384dbe5",
   "metadata": {},
   "outputs": [],
   "source": [
    "rabi_jobs = await client.get_jobs(job_name=\"power_rabi\", limit=10)\n",
    "client.display(rabi_jobs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60ef70ab",
   "metadata": {},
   "source": [
    "## 3. Get a job summary\n",
    "\n",
    "A job summary provides metadata without the full result payload, which is useful when you only need to check status or timing rather than inspect the underlying data. Key attributes include `id`, `name`, `status`, and `created_at`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "70ab80cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "job_summary = await client.get_job_summary(\"job_id\")\n",
    "client.display(job_summary)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40ab50cd",
   "metadata": {},
   "source": [
    "## 4. Retrieve job data\n",
    "\n",
    "Call `get_job_data()` with a job ID to fetch the full results of a completed experiment or routine. The returned `JobData` object contains results, fitted parameters, and plot data. Pass the result to `client.display()` to render it inline."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50cd60ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "job_id = \"<your-job-id>\"\n",
    "\n",
    "job_data = await client.get_job_data(job_id)\n",
    "client.display(job_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0ef30ab",
   "metadata": {},
   "source": [
    "## 5. Review device state\n",
    "\n",
    "Inspect the current state of the virtual device at three levels of detail. Use `get_device_summary()` for a high-level overview, `get_device_data()` for the full data object, and `display_device_data_sheet()` for a rendered summary table. The rendered sheet accepts an optional `node_name` argument to focus on a single qubit or component."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0ab40cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "device_summary = await client.get_device_summary()\n",
    "client.display(device_summary)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0ef60ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "await client.display_device_data_sheet(node_name=\"q0\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1ab70cd",
   "metadata": {},
   "source": [
    "## 6. Inspect calibrations\n",
    "\n",
    "QPU component data, gate calibration programs, and readout classifiers are stored as part of the device data. Call `get_component(\"<component>\")` on the `device_data`'s `qpu` attribute, replacing \"<component>\" with the component you want to inspect.  Call `get_defcal()` on the `DeviceData` object to retrieve the calibration program for a specific gate and address, and `get_classifier()` to retrieve the readout classifier for a given classifier type and address."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1cd80ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "device_data = await client.get_device_data()\n",
    "\n",
    "transmon_data = device_data.qpu.get_component(\"q0\")\n",
    "anharmonicity = transmon_data.anharm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "232d059e",
   "metadata": {},
   "outputs": [],
   "source": [
    "defcal = device_data.get_defcal(gate=\"x\", addr=\"q0\")\n",
    "defcal.program"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1ef90ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "classifier = device_data.get_classifier(dtype=\"linear_iq\", addr=\"q0\")\n",
    "classifier"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1cdb0ef",
   "metadata": {},
   "source": [
    "## Next steps\n",
    "\n",
    "- Run new experiments: see [How to run a custom experiment](https://docs.q-ctrl.com/boulder-opal/autocalibration/apply/how-to-run-a-custom-experiment-with-boulder-opal).\n",
    "- Manage device snapshots: see [How to create and manage virtual devices](https://docs.q-ctrl.com/boulder-opal/autocalibration/apply/how-to-create-and-manage-virtual-devices-with-boulder-opal).\n",
    "- Diagnose failed routine runs: see [How to debug failed routines](https://docs.q-ctrl.com/boulder-opal/autocalibration/apply/how-to-debug-a-failed-routine-with-boulder-opal)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.12.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
