{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "691824b4",
   "metadata": {},
   "source": [
    "# Setting up Boulder Opal with QuantumMachines OPX controllers\n",
    "\n",
    "**Connect a Quantum Machines OPX+ system to Boulder Opal**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6918body",
   "metadata": {},
   "source": [
    "Quantum Machines OPX controllers handle drive, readout, and flux on a single programmable platform; the Octave companion module provides microwave up- and down-conversion. The `boulderopalscaleup` package connects to an OPX+ system through a `Runtime` object that manages the controller connection.\n",
    "\n",
    "In this tutorial, you will configure a connection to an OPX+ system, register it with the Boulder Opal runtime, instantiate the client, and create a virtual device from a YAML configuration.\n",
    "\n",
    "You will need a Q-CTRL account with an API key and organization slug, an OPX+ cluster reachable on your network with any required Octaves powered and addressable, and a device configuration YAML provided by Q-CTRL."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sec01-md",
   "metadata": {},
   "source": [
    "## 1. Connect to the OPX+ system\n",
    "\n",
    "Describe the OPX with an `OPXSystemInfo` object that carries the cluster name, host IP address, and port number. Replace the placeholder strings with the ones that apply to your device.\n",
    "\n",
    "Once the system is described, you create the runtime with `Runtime.new()` and register the hardware with `runtime.add_system(opx_system)`.\n",
    "\n",
    "**Note:** It is important to configure the `OPXSystemSystemInfo` object properly to reflect your controller's setup. Read the reference documentation for a complete explanation of all its parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "664fc506",
   "metadata": {},
   "outputs": [],
   "source": [
    "from boulderopalscaleup.runtime import Runtime\n",
    "from boulderopalscaleup.runtime.systems.quantum_machines import OPXSystemInfo\n",
    "\n",
    "opx_system = OPXSystemInfo(\n",
    "    host=\"<cluster IP address>\",\n",
    "    port=int(\"<cluster port>\"),\n",
    "    cluster_name=\"<cluster name>\",\n",
    ")\n",
    "\n",
    "runtime = Runtime.new()\n",
    "runtime.add_system(opx_system)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sec02-md",
   "metadata": {},
   "source": [
    "## 2. Initialize the client\n",
    "\n",
    "Create the client and pass it the runtime you just configured along with your Q-CTRL API key and organization slug. Replace the placeholder strings with the credentials issued by Q-CTRL."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b034d7da",
   "metadata": {},
   "outputs": [],
   "source": [
    "from boulderopalscaleup import QctrlScaleUpClient\n",
    "\n",
    "client = QctrlScaleUpClient(\n",
    "    runtime=runtime,\n",
    "    api_key=\"<your API key>\",\n",
    "    organization_slug=\"<your organization slug>\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sec03-md",
   "metadata": {},
   "source": [
    "## 3. Create and select the virtual device\n",
    "\n",
    "Before running any programs on your physical device, you must create a virtual device in the Q-CTRL environment. The `create_device()` call uploads a YAML configuration file that describes the layout of your QPU and the wiring of the control hardware that drives it, and registers the new virtual device under your organization.  During this step, make sure you use the correct configuration files that are specifically for Quantum Machines controllers. This ensures the setup complies with the respective Quantum Machines syntax and support modules.\n",
    "\n",
    "**Note:** Q-CTRL provides a template YAML for each supported QPU model and supported controller. Replace `./device_config.yaml` with the name of the file provided to you."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cdb8c974",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "# The YAML file defines the wiring and other configurations for your device\n",
    "device_yaml_path = Path(\"./device_config.yaml\")\n",
    "\n",
    "# Give the virtual device a unique name\n",
    "device_name = \"device_name\"\n",
    "\n",
    "# Add this named virtual device to the Q-CTRL environment, if not already present\n",
    "if device_name not in [device.name for device in await client.get_devices()]:\n",
    "    await client.create_device(device_name, device_config=device_yaml_path)\n",
    "\n",
    "# Mark that device as the current device to use in subsequent routines and experiments\n",
    "await client.set_current_device(device_name)"
   ]
  }
 ],
 "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.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
