{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a1b2c3d4",
   "metadata": {},
   "source": [
    "# How to create and manage virtual devices with Boulder Opal\n",
    "\n",
    "**Create, inspect, and snapshot virtual devices that serve as a digital representation of your quantum hardware**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1b2body",
   "metadata": {},
   "source": [
    "A virtual device in Boulder Opal Scale Up acts as a digital representation of your quantum processor. You can create a device from a YAML configuration file, inspect its calibration data, and save or restore snapshots to preserve known-good states.\n",
    "\n",
    "In this user guide, you will learn how to set up the Scale Up client, create and select virtual devices, inspect device data at both the full-device and component level, and manage snapshots for reproducible calibration states.\n",
    "\n",
    "You will need an authenticated client (see documentation specific to [Qblox](https://docs.q-ctrl.com/boulder-opal/autocalibration/discover/setting-up-boulder-opal-with-qblox-controllers) and [Quantum Machines](https://docs.q-ctrl.com/boulder-opal/autocalibration/discover/setting-up-boulder-opal-with-quantummachines-opx-controllers)) and a device configuration YAML file for your quantum processor. 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": "d4e5f6a7",
   "metadata": {},
   "source": [
    "## 1. Create a new virtual device\n",
    "\n",
    "Each virtual device corresponds to a digital instance of a physical quantum processor, and you can have multiple virtual devices for each physical quantum processor to support different users or different calibration tasks over time. To create one, supply a unique name and the path to a YAML configuration file that describes the hardware layout. The returned job ID lets you track the creation process."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5f6a7b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "job_id = await client.create_device(\n",
    "    device_name=\"<your-device-name>\", device_config=Path(\"<path-to-your-config>.yaml\")\n",
    ")\n",
    "await client.set_current_device(\"<your-device-name>\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6a7b8c9",
   "metadata": {},
   "source": [
    "## 2. List and select devices\n",
    "\n",
    "The `get_devices()` method returns all virtual devices in your organization. Once you identify the device you want to work with, call `set_current_device()` to mark it as the active target for all subsequent\n",
    "operations in this session."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a7b8c9d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "devices = await client.get_devices()\n",
    "client.display(devices)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b8c9d0e1",
   "metadata": {},
   "outputs": [],
   "source": [
    "await client.set_current_device(\"<your-device-name>\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2a3b4c5",
   "metadata": {},
   "source": [
    "## 3. Save a snapshot\n",
    "\n",
    "Snapshots allow you to persist the current device state, capturing all calibration parameters, gate definitions, and classifier configurations at a single point in time. These device snapshots are specifically for resetting or recreating a virtual device to a given, known state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a3b4c5d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "await client.save_device_snapshot(output=Path(\"./snapshot.json\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4c5d6e7",
   "metadata": {},
   "source": [
    "## 4. Restore from snapshot\n",
    "\n",
    "Loading a previously saved snapshot restores the device to the state recorded in that file. This operation overwrites all current calibration data on the virtual device, so any calibration updates made after the snapshot was taken will be lost. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5d6e7f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "await client.load_device_snapshot(snapshot=Path(\"./snapshot.json\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8a9b0c1",
   "metadata": {},
   "source": [
    "## Next steps\n",
    "\n",
    "- [How to run a custom experiment](https://docs.q-ctrl.com/boulder-opal/autocalibration/apply/how-to-run-a-custom-experiment-with-boulder-opal): running\n",
    "  experiments on your virtual device\n",
    "- [How to update device parameters](https://docs.q-ctrl.com/boulder-opal/autocalibration/apply/how-to-manually-update-device-parameters-with-boulder-opal):\n",
    "  updating calibration values after manual tuning\n",
    "- [The device data model](https://docs.q-ctrl.com/boulder-opal/autocalibration/apply/how-to-retrieve-and-display-data-with-boulder-opal):\n",
    "  structure and organization of the device data object"
   ]
  }
 ],
 "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.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
