{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "90f546a9",
   "metadata": {},
   "source": [
    "# Submitting Qiskit Circuits to Azure Quantum with the QDK\n",
    "\n",
    "This notebook demonstrates two ways to run Qiskit `QuantumCircuit` jobs on Azure Quantum using the QDK: (A) direct submission via `AzureQuantumProvider` for a streamlined workflow, and (B) an explicit OpenQASM 3 → QIR compilation path for transparency and artifact inspection. It also shows how to handle parameterized circuits by binding parameters prior to execution."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b838c23",
   "metadata": {},
   "source": [
    "This workflow demonstrates two methods of submission:\n",
    "\n",
    "### A. Direct submission via `AzureQuantumProvider` (recommended when available)\n",
    "1. Build (or load) a Qiskit `QuantumCircuit`.\n",
    "2. Reference an existing Azure Quantum workspace with `qdk.azure.Workspace`.\n",
    "3. Instantiate `AzureQuantumProvider(workspace)` and pick a backend (`provider.get_backend(<target_name>)`).\n",
    "4. Call `backend.run(circuit, shots, job_name=...)` and fetch results (`job.result().get_counts(circuit)`).\n",
    "\n",
    "### B. Submit via OpenQASM compilation (explicit OpenQASM → QIR → submit)\n",
    "1. Build (or load) a Qiskit `QuantumCircuit`.\n",
    "2. Convert it to OpenQASM 3 text (`qiskit.qasm3.dumps`).\n",
    "3. Compile the OpenQASM 3 source to QIR with `qdk.openqasm.compile` (choose a `TargetProfile`).\n",
    "4. Reference an existing Azure Quantum workspace with `qdk.azure.Workspace`.\n",
    "5. Select a target (e.g. a simulator such as `rigetti.sim.qvm`).\n",
    "6. Submit the QIR payload (`target.submit(qir, job_name, shots)`), then retrieve results (`job.get_results()`)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "004e5982",
   "metadata": {},
   "source": [
    "## Prerequisites\n",
    "\n",
    "This notebook assumes the `qdk`, Azure Quantum integration, and Qiskit packages are installed. You can install everything (including the Azure + Qiskit extras) with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfa160e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install \"qdk[azure,qiskit]\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7f37bcd",
   "metadata": {},
   "source": [
    "This installs:\n",
    "- The base qdk package (compiler, OpenQASM/QIR tooling)\n",
    "- Azure Quantum client dependencies for submission\n",
    "- Qiskit for circuit construction\n",
    "\n",
    "After installing, restart the notebook kernel if it was already running. You can verify installation with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f7446ed1",
   "metadata": {},
   "outputs": [],
   "source": [
    "import qiskit, qdk, qdk.azure  # should import without errors"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f761649",
   "metadata": {},
   "source": [
    "## Submitting a simple Qiskit circuit\n",
    "\n",
    "We start with a minimal circuit that prepares single-qubit superpositions on two qubits and measures them. After constructing the circuit we’ll submit it to an Azure Quantum target."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e91dd2d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import QuantumCircuit\n",
    "\n",
    "circuit = QuantumCircuit(2, 2)\n",
    "\n",
    "circuit.h(0)\n",
    "circuit.measure(0, 0)\n",
    "\n",
    "circuit.h(1)\n",
    "circuit.measure(1, 1)\n",
    "\n",
    "circuit.draw(output=\"text\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "efd50528",
   "metadata": {},
   "source": [
    "## Configure Azure Quantum workspace connection\n",
    "\n",
    "To connect to an Azure workspace replace the following variables with your own values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54206752",
   "metadata": {},
   "outputs": [],
   "source": [
    "subscription_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'\n",
    "resource_group = 'myresourcegroup'\n",
    "workspace_name = 'myworkspace'\n",
    "location = 'westus'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdae59f7",
   "metadata": {},
   "source": [
    "### Approach A: Using Azure Quantum Provider\n",
    "\n",
    "Here we use the recommended submission method with `AzureQuantumProvider` to submit the Qiskit circuit straight to an Azure Quantum backend. This avoids having to explicitly do QASM translation or QIR compilation steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d9a0f59b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from qdk.azure import Workspace\n",
    "from qdk.azure.qiskit import AzureQuantumProvider\n",
    "\n",
    "def submit_qiskit_circuit_to_azure_provider(circuit, target_name, name, shots=100):\n",
    "\n",
    "    workspace = Workspace(\n",
    "        subscription_id=subscription_id,\n",
    "        resource_group=resource_group,\n",
    "        name=workspace_name,\n",
    "        location=location,\n",
    "    )\n",
    "\n",
    "    provider = AzureQuantumProvider(workspace)\n",
    "    backend = provider.get_backend(target_name)\n",
    "    job = backend.run(circuit, shots, job_name=name)\n",
    "    return job.result().get_counts(circuit)\n",
    "\n",
    "provider_counts = submit_qiskit_circuit_to_azure_provider(circuit, \"rigetti.sim.qvm\", \"qiskit-provider-job\")\n",
    "print(provider_counts)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fbf60412",
   "metadata": {},
   "source": [
    "### Approach B: Submit via OpenQASM compilation\n",
    "\n",
    "Below we show the longer path that exposes intermediate artifacts. This is useful if you want to inspect or transform OpenQASM/QIR, or integrate with tooling that consumes QIR directly.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee47d6f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import qasm3\n",
    "from qdk.openqasm import compile\n",
    "from qdk import TargetProfile\n",
    "\n",
    "def submit_qiskit_circuit_to_azure_via_qasm(circuit, target_name, name, shots=100):\n",
    "    qasm3_str = qasm3.dumps(circuit)\n",
    "    qir = compile(qasm3_str, target_profile=TargetProfile.Base)\n",
    "\n",
    "    workspace = Workspace(\n",
    "        subscription_id=subscription_id,\n",
    "        resource_group=resource_group,\n",
    "        name=workspace_name,\n",
    "        location=location,\n",
    "    )\n",
    "    target = workspace.get_targets(target_name)\n",
    "    job = target.submit(qir, name, shots=shots)\n",
    "    return job.get_results()\n",
    "\n",
    "results = submit_qiskit_circuit_to_azure_via_qasm(circuit, \"rigetti.sim.qvm\", \"qiskit-via-qasm-job\")\n",
    "print(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c84df75",
   "metadata": {},
   "source": [
    "## Parameterized Qiskit circuits\n",
    "\n",
    "Many algorithms use symbolic parameters. Before submitting to azure (or before compiling to QIR), all circuit parameters must be bound to numeric values. Below we build a simple entangling ladder, apply a rotation parameterized by `θ` across all qubits, then uncompute the entanglement and measure the first qubit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d766661",
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import QuantumCircuit\n",
    "from qiskit.circuit import Parameter\n",
    "\n",
    "def get_parameterized_circuit(n = 5) -> QuantumCircuit:\n",
    "    theta = Parameter(\"θ\")\n",
    "    qc = QuantumCircuit(n, 1)\n",
    "    qc.h(0)\n",
    "    for i in range(n - 1):\n",
    "        qc.cx(i, i + 1)\n",
    "    qc.rz(theta, range(n))\n",
    "\n",
    "    for i in reversed(range(n - 1)):\n",
    "        qc.cx(i, i + 1)\n",
    "    qc.h(0)\n",
    "    qc.measure(0, 0)\n",
    "    return qc\n",
    "\n",
    "# Build the symbolic (parameterized) circuit\n",
    "parameterized_circuit = get_parameterized_circuit()\n",
    "\n",
    "# Bind θ to a numeric value, then visualize the bound circuit\n",
    "bound_circuit = parameterized_circuit.assign_parameters({\"θ\": 0.5})\n",
    "\n",
    "bound_circuit.draw(output=\"text\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee4d57b4",
   "metadata": {},
   "source": [
    "## Submitting a bound parameterized circuit\n",
    "\n",
    "Here we submit the `bound_circuit` from above using the recommended `AzureQuantumProvider` approach. The printed result shows the measurement counts. Change the value of `θ` (or loop over several values) to explore how the outcome distribution varies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d9bb3eb0",
   "metadata": {},
   "outputs": [],
   "source": [
    "results = submit_qiskit_circuit_to_azure_provider(\n",
    "    bound_circuit,\n",
    "    \"rigetti.sim.qvm\",\n",
    "    \"qiskit-parameterized-job\"\n",
    ")\n",
    "print(results)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
