microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
be82821236a00686b004bc7fe619ad16904f7997

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

samples/qre/1_qre_input.ipynb

651lines · modepreview

{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1db6c3f9",
   "metadata": {},
   "source": [
    "# Importing quantum programs into QRE\n",
    "\n",
    "The Quantum Resource Estimator (QRE) can accept applications written in several quantum programming languages and frameworks. This notebook demonstrates how to import programs from **Q#**, **Cirq**, **QIR** (Quantum Intermediate Representation), and **OpenQASM**, and then run resource estimation on each of them.\n",
    "\n",
    "After running estimation on all four input formats, we go a step further and build a **custom application model**. This shows how to define your own `Application` subclass with configurable *trace parameters* (algorithmic hyperparameters that QRE varies automatically during estimation) and how to enrich results with custom columns derived from trace properties."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "138685c1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n// This file provides CodeMirror syntax highlighting for Q# magic cells\n// in classic Jupyter Notebooks. It does nothing in other (Jupyter Notebook 7,\n// VS Code, Azure Notebooks, etc.) environments.\n\n// Detect the prerequisites and do nothing if they don't exist.\nif (window.require && window.CodeMirror && window.Jupyter) {\n  // The simple mode plugin for CodeMirror is not loaded by default, so require it.\n  window.require([\"codemirror/addon/mode/simple\"], function defineMode() {\n    let rules = [\n      {\n        token: \"comment\",\n        regex: /(\\/\\/).*/,\n        beginWord: false,\n      },\n      {\n        token: \"string\",\n        regex: String.raw`^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)`,\n        beginWord: false,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(namespace|open|as|operation|function|body|adjoint|newtype|controlled|internal)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(if|elif|else|repeat|until|fixup|for|in|return|fail|within|apply)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(Adjoint|Controlled|Adj|Ctl|is|self|auto|distribute|invert|intrinsic)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(let|set|use|borrow|mutable)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"operatorKeyword\",\n        regex: String.raw`(not|and|or)\\b|(w/)`,\n        beginWord: true,\n      },\n      {\n        token: \"operatorKeyword\",\n        regex: String.raw`(=)|(!)|(<)|(>)|(\\+)|(-)|(\\*)|(/)|(\\^)|(%)|(\\|)|(&&&)|(~~~)|(\\.\\.\\.)|(\\.\\.)|(\\?)`,\n        beginWord: false,\n      },\n      {\n        token: \"meta\",\n        regex: String.raw`(Int|BigInt|Double|Bool|Qubit|Pauli|Result|Range|String|Unit)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"atom\",\n        regex: String.raw`(true|false|Pauli(I|X|Y|Z)|One|Zero)\\b`,\n        beginWord: true,\n      },\n    ];\n    let simpleRules = [];\n    for (let rule of rules) {\n      simpleRules.push({\n        token: rule.token,\n        regex: new RegExp(rule.regex, \"g\"),\n        sol: rule.beginWord,\n      });\n      if (rule.beginWord) {\n        // Need an additional rule due to the fact that CodeMirror simple mode doesn't work with ^ token\n        simpleRules.push({\n          token: rule.token,\n          regex: new RegExp(String.raw`\\W` + rule.regex, \"g\"),\n          sol: false,\n        });\n      }\n    }\n\n    // Register the mode defined above with CodeMirror\n    window.CodeMirror.defineSimpleMode(\"qsharp\", { start: simpleRules });\n    window.CodeMirror.defineMIME(\"text/x-qsharp\", \"qsharp\");\n\n    // Tell Jupyter to associate %%qsharp magic cells with the qsharp mode\n    window.Jupyter.CodeCell.options_default.highlight_modes[\"qsharp\"] = {\n      reg: [/^%%qsharp/],\n    };\n\n    // Force re-highlighting of all cells the first time this code runs\n    for (const cell of window.Jupyter.notebook.get_cells()) {\n      cell.auto_highlight();\n    }\n  });\n}\n",
      "text/plain": []
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from dataclasses import dataclass, field\n",
    "from pathlib import Path\n",
    "from typing import Any\n",
    "\n",
    "import cirq\n",
    "import qdk\n",
    "\n",
    "from qiskit.circuit.library import RGQFTMultiplier\n",
    "from qiskit.qasm3.exporter import Exporter\n",
    "\n",
    "from qdk.qre import estimate, plot_estimates, Application\n",
    "from qdk.qre.interop import trace_from_entry_expr\n",
    "from qdk.qre.models import GateBased, RoundBasedFactory, SurfaceCode, TwoDimensionalYokedSurfaceCode\n",
    "from qdk.qre.application import QSharpApplication, QIRApplication, CirqApplication, OpenQASMApplication\n",
    "from qdk.qre.property_keys import custom_properties"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54a49066",
   "metadata": {},
   "source": [
    "## From Q#\n",
    "\n",
    "Here we use the `%%qsharp` cell magic to define an inline Q# operation that performs ripple-carry addition on two registers of qubits. We then wrap the compiled entry point in a `QSharpApplication`, passing the register size as a runtime argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "082a75fc",
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "import Std.Arithmetic.*;\n",
    "\n",
    "// Ripple-carry addition on two n-qubit registers\n",
    "operation Program(n : Int) : Unit {\n",
    "    use a = Qubit[n];\n",
    "    use b = Qubit[n];\n",
    "\n",
    "    RippleCarryCGIncByLE(a, b);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f9a84bf7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Wrap the compiled Q# entry point with a register size of 10 qubits\n",
    "qsharp_app = QSharpApplication(qdk.code.Program, args=(10,))  # type: ignore"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d5625e1",
   "metadata": {},
   "source": [
    "## From Cirq\n",
    "\n",
    "`CirqApplication` accepts any `cirq.Circuit` directly. Here we build a 10-qubit Quantum Fourier Transform (QFT) circuit using Cirq's built-in `qft` helper and wrap it for resource estimation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b81536f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Build a 10-qubit QFT circuit and wrap it for estimation\n",
    "circuit = cirq.Circuit(cirq.qft(*cirq.LineQubit.range(10)))\n",
    "cirq_app = CirqApplication(circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2f4db19",
   "metadata": {},
   "source": [
    "## From QIR\n",
    "\n",
    "Quantum Intermediate Representation (QIR) is an LLVM-based IR for quantum programs. `QIRApplication` accepts QIR as an LLVM IR string (or bitcode). Here we load a pre-compiled QIR file for a Hidden Shift algorithm."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24dad0bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load a pre-compiled QIR file for the Hidden Shift algorithm\n",
    "qir_file = (\n",
    "    Path.cwd().parent.parent\n",
    "    / \"source\" / \"qdk_package\" / \"tests-integration\" / \"resources\"\n",
    "    / \"adaptive_ri\" / \"output\" / \"HiddenShiftNISQ.ll\"\n",
    ")\n",
    "qir_app = QIRApplication(qir_file.read_text(encoding=\"utf-8\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95eccf1d",
   "metadata": {},
   "source": [
    "## From OpenQASM (via Qiskit)\n",
    "\n",
    "`OpenQASMApplication` accepts an OpenQASM 3 source string directly. Here we use Qiskit to generate an OpenQASM 3 program for a 4-qubit RGQFT multiplier and wrap it for resource estimation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "4f628862",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Export a Qiskit circuit to OpenQASM 3\n",
    "source = Exporter().dumps(RGQFTMultiplier(num_state_qubits=4))\n",
    "qasm_app = OpenQASMApplication(source)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d684e434",
   "metadata": {},
   "source": [
    "## Running resource estimation\n",
    "\n",
    "With all four applications defined, we run resource estimation on each one to illustrate that any of the supported input formats can be used interchangeably. We use a `GateBased` architecture with 100 ns gate time and 500 ns measurement time, and query over `SurfaceCode` distances crossed with `RoundBasedFactory` protocols. The `max_error` parameter caps the total error probability at 1%.\n",
    "\n",
    "Each call to `estimate` returns an `EstimationTable` of Pareto-optimal results: configurations where no other result achieves both fewer physical qubits *and* a shorter runtime within the error budget. Note that the four applications implement entirely different algorithms, so the results are not directly comparable; the purpose here is to show that QRE handles all input formats uniformly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "0171b84b",
   "metadata": {},
   "outputs": [],
   "source": [
    "estimates = []\n",
    "\n",
    "# Define the target architecture: gate-based with specified timing (ns)\n",
    "arch = GateBased(gate_time=100, measurement_time=500)\n",
    "\n",
    "# Explore surface code distances × round-based magic state factories\n",
    "isa_query = SurfaceCode.q() * RoundBasedFactory.q()\n",
    "\n",
    "for name, app in [(\"Q# adder\", qsharp_app), (\"cirq QFT\", cirq_app), (\"QIR HiddenShift\", qir_app), (\"OpenQASM multiplier\", qasm_app)]:\n",
    "    estimates.append(estimate(app, arch, isa_query, max_error=0.01, name=name))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00938f1d",
   "metadata": {},
   "source": [
    "We can plot the Pareto frontiers for each application. Since the four programs implement different algorithms, the plots are not meant for cross-algorithm comparison; they simply confirm that each input format produces valid estimation results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "08fc2a22",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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1AhTpkxB/fr1a6l6IiIiqgnRg4hcLkdwcDDi4uIqXb5582bExsZi1qxZOH/+PIKDg9GtWzdkZmYq13F2dsbFixchk8mwceNGZGRk1Fb5REREVAOiB5Hu3btj7ty56NevX6XLFy1ahLfeegvDhw9HUFAQli1bBjs7O6xatUptXXd3dwQHB+Po0aNV7q+wsBC5ubkqDyIiIhKH6EGkOkVFRTh37hy6dOmibDMzM0OXLl1w8uRJAEBGRgYePXoEAMjJycGRI0fQrFmzKrc5b948SKVS5cPb29uwb4KIiIiq9EwHkfv370OhUMDd3V2l3d3dHenp6QCA27dvIyIiAsHBwYiIiMB7772HFi1aVLnN6dOnIycnR/lITU016HsgIiKiqtX5y3fbtGmDxMREjde3traGtbW14QoiIiIijT3TPSL169eHubm52uDTjIwMeHh4iFQVERER6cszHUSsrKzQunVrxMfHK9tKS0sRHx+P9u3bi1gZERER6YPop2by8vLw119/KZ/LZDIkJibCxcUFPj4+iI2NRXR0NMLCwtCmTRssWbIEcrkcw4cPF7FqIiIi0gfRg8jZs2cRGRmpfB4bGwsAiI6Oxpo1azBo0CDcu3cPM2fORHp6OkJCQrBnzx61AazaiouLQ1xcHBQKRY22Q0RERLqTCCY+H3pubi6kUilycnLg5OQkdjlERER1hj4+Q5/pMSJERERk3BhEiIiISDQMIkRERCQaBhEiIiISDYMIERERicZkg0hcXByCgoIQHh4udilEREQmi5fv8vJdIiIinfDyXSIiIqrTGESIiIhINAwiREREJBoGESIiIhINgwgRERGJxmSDCC/fJSIiEh8v3+Xlu0RERDrh5btERERUpzGIEBERkWgYRIiIiEg0DCJEREQkGgYRIiIiEg2DCBEREYmGQYSIiIhEY7JBhBOaERERiY8TmnFCMyIiIp1wQjMiIiKq0xhEiIiISDQMIkRERCQaBhEiIiISDYMIERERiYZBhIiIiETDIEJERESiYRAhIiIi0ZhsEOHMqkREROLjzKqcWZWIiEgnnFmViIiI6jQGESIiIhINgwgRERGJhkGEiIiIRMMgQkRERKJhECEiIiLRMIgQERGRaBhEiIiISDQMIkRERCQaBhEiIiISDYMIERERicZkgwhvekdERCQ+3vSON70jIiLSCW96R0RERHUagwgRERGJhkGEiAzmUdEjpMvTK12WLk/Ho6JHtVwRET1rGESIyCAeFT3C6P2jMXzPcLUwki5Px/A9wzF6/2iGESITxyBCRAYhL5YjqyALd/LuqISR8hByJ+8OsgqyIC+Wi1wpEYmJQYSIDMLD3gOro1ajkUMjZRhJzExUhpBGDo2wOmo1POw9xC6ViETEy3d5+S6RQVXsASnHEEJkHHj5LhE98zzsPTAvYp5K27yIeQwhRASAQYSIDCxdno7pR6ertE0/Or3Kq2mIyLQwiBCRwVQ8LdPIoRHWd1+vMmaEYYSIGESIyCCeDCGro1YjpEGI2gBWhhEi08YgQkQGYW9pDxdbF7WBqRWvpnGxdYG9pb3IlRKRmHjVDK+aITKYR0WPIC+WVzowNV2eDntLezhaOYpQGRHpgz4+Qy30XBMRkZKjlWOVQYNXzRARYMKnZuLi4hAUFITw8HCxSyEiIjJZPDXDUzNEREQ64YRmREREVKcxiBAREZFoGESIiIhINAwiREREJBqTv3y3fKxubm6uyJUQERHVLeWfnTW57sXkg8iDBw8AAN7e3iJXQkREVDc9ePAAUqlUp9eafBBxcXEBAKSkpOj0TQwPD8eZM2d0XqeyZU+2VXyuydc1UZP3o8l7ebKtrr8fTf6vyv/Nzc2Ft7c3UlNTdbrMjcda9e081nis6VLr09bhsVb9sZaTkwMfHx/lZ6kuTD6ImJmVDZORSqU6/cCam5s/9XXVrVPZsifbKj7X5OuaqMn70eS9PNlW19+PJv9XT67j5OTEY03D7fBY47FWF/5vnmyr6+9Hl2Ot/LNUFxysWkNjxoyp0TqVLXuyreJzTb6uiZq8H03ey5Ntdf39aPJ/9Sy8l6qW8VirO++Hx5rueKxV3y7msQZwZlXOrEq1hsca1RYea1RbOLOqHlhbW2PWrFmwtrYWuxQycjzWqLbwWKPaoo9jzeR7RIiIiEg8Jt8jQkREROJhECEiIiLRMIgQERGRaBhEiIiISDQmF0QKCgqQn5+vfH779m0sWbIEe/fuFbEqMkY81kgsCoUCiYmJePjwodilkJE5f/48Ll++rHy+c+dO9O3bFx988AGKiop02qbJBZE+ffpg3bp1AIDs7Gy0bdsWCxcuRJ8+ffDtt9+KXB0ZEx5rVFsmTJiAlStXAigLIR07dkSrVq3g7e2NQ4cOiVscGZW3334b169fBwDcvHkTgwcPhp2dHX788Ue8//77Om3T5ILI+fPnERERAQDYunUr3N3dcfv2baxbtw5ff/21yNWRMeGxRrVl69atCA4OBgD88ssvkMlk+PPPPzFx4kTMmDFD5OrImFy/fh0hISEAgB9//BEvvfQSNm7ciDVr1mDbtm06bdPkgkh+fj4cHR0BAHv37sUrr7wCMzMztGvXDrdv3xa5OjImPNaotty/fx8eHh4AgF27dmHAgAEICAjAiBEjVLrRiWpKEASUlpYCAPbv348ePXoAKLuD/f3793XapskFEX9/f+zYsQOpqan4/fff8fLLLwMAMjMzORUy6RWPNaot7u7uuHr1KhQKBfbs2YOuXbsCKAvD5ubmIldHxiQsLAxz587F+vXrcfjwYfTs2RMAIJPJ4O7urtM2TS6IzJw5E5MnT4afnx/atm2L9u3bAyj7izU0NFTk6siY8Fij2jJ8+HAMHDgQzZs3h0QiQZcuXQAAp06dwvPPPy9ydWRMlixZgvPnz2Ps2LGYMWMG/P39AZSdHvzXv/6l0zZNcor39PR0pKWlITg4WHnr4tOnT8PJyYk/tKRXVR1rUqkUzZo1E7k6MiZbt25FamoqBgwYgEaNGgEA1q5dC2dnZ/Tp00fk6sjYPX78GObm5rC0tNT6tSYZRIjEdOvWLSxYsABLly4VuxQiItGZXBCRy+WYP38+4uPjkZmZqRx0U+7mzZsiVUbGJjIyEhKJRK09LS0NaWlpyM7Orv2iyGidOXMGBw8erPT32qJFi0SqiujpLMQuoLa9+eabOHz4MIYOHQpPT89KPyiI9KH8ErdyCoUCN2/exF9//YU1a9aIUhMZp88++wwffvghmjVrBnd3d5Xfa/wdR886k+sRcXZ2xm+//YYOHTqIXQqZqBUrVmDlypU4efKk2KWQkXB3d8fnn3+OmJgYsUsh0prJXTVTr149uLi4iF0GmbDOnTsjMTFR7DLIiJiZmfGPK6qzTC6IfPLJJ5g5c6bKPUCIatOBAwcQGRkpdhlkRCZOnIi4uDixyyATlpqaihEjRuj0WpM7NRMaGoobN25AEAT4+fmpXWp0/vx5kSojY/PKK6+otWVkZODUqVOIjIxUzroKAD/99FNtlkZGprS0FD179sT169cRFBSk9nuNxxcZ2sWLF9GqVSsoFAqtX2tyg1X79u0rdglkIqRSaaVtAQEBIlRDxmzcuHE4ePAgIiMj4erqygGqpHc///xztctrcsWpyfWIEBEZG0dHR2zatEk53TaRvpmZmUEikaC6yCCRSHTqETG5MSJERMbGxcUFTZo0EbsMMmKenp746aefUFpaWumjJsMaGESIiOq42bNnY9asWRyETwbTunVrnDt3rsrlT+stqQ5PzRAR1XEchE+GdvToUcjlckRFRVW6XC6X4+zZs+jYsaPW2za5wapERMaGg/DJ0CIiIqpdbm9vr1MIAdgjQkRERCLiGJH/qclkLETa4LFGRPQP9oj8T00mYyHSBo81IqJ/mMwYEUNOxkJUEY81IiLNmUyPiCEnYyGqiMcaEZHmTGaMiCEnYyGqiMcaEZHmTCaIGHIyFqKKeKzRs4IDo6kuMJlTM4acjIWoIh5r9KzgwGiqC0wmiBARGRtNBkZPmjSJQYSeaQwiRER1FAdGkzEwmTEiRETGhgOjyRgwiBAR1VEcGE3GwGQmNCMiMjZTpkyBXC6vcrm/vz8OHjxYixURaY9jRIiIiEg0PDVDREREomEQISIiItEwiBAREZFoGESIiIhINAwiRFSndOrUCRMmTBC7DCLSEwYRItJaTEwMJBIJJBIJLC0t0bhxY7z//vt4/Pix3vZx6NAhSCQSZGdnq7T/9NNP+OSTT/S2HyISF+cRISKdREVFYfXq1SguLsa5c+cQHR0NiUSCzz//3KD7dXFxMej2iah2sUeEiHRibW0NDw8PeHt7o2/fvujSpQv27dsHAPDz88OSJUtU1g8JCcHs2bOVzyUSCVasWIF+/frBzs4OTZs2Vd7E7datW4iMjAQA1KtXDxKJBDExMQDUT834+flh7ty5GDZsGBwcHODr64uff/4Z9+7dQ58+feDg4ICWLVvi7NmzKvUcO3YMERERsLW1hbe3N8aNG1ft5GBEZBgMIkRUY1euXMGJEydgZWWl1es+/vhjDBw4EJcuXUKPHj0wZMgQZGVlwdvbG9u2bQMAXLt2DWlpafjqq6+q3M7ixYvRoUMHXLhwAT179sTQoUMxbNgwvPHGGzh//jyaNGmCYcOGKac7v3HjBqKiotC/f39cunQJmzdvxrFjxzB27FjdvwlEpBMGESLSya+//goHBwfY2NigRYsWyMzMxJQpU7TaRkxMDF577TX4+/vjs88+Q15eHk6fPg1zc3PlKZgGDRrAw8MDUqm0yu306NEDb7/9Npo2bYqZM2ciNzcX4eHhGDBgAAICAjB16lQkJSUhIyMDADBv3jwMGTIEEyZMQNOmTfGvf/0LX3/9NdatW6fXcS5E9HQcI0JEOomMjMS3334LuVyOxYsXw8LCAv3799dqGy1btlR+bW9vDycnJ2RmZmpdS8XtuLu7AwBatGih1paZmQkPDw9cvHgRly5dwoYNG5TrCIKA0tJSyGQyBAYGal0DEemGQYSIdGJvbw9/f38AwKpVqxAcHIyVK1di5MiRMDMzU7vra3Fxsdo2LC0tVZ5LJBKUlpZqXUvF7Ugkkirbyredl5eHt99+G+PGjVPblo+Pj9b7JyLdMYgQUY2ZmZnhgw8+QGxsLF5//XW4ubkhLS1NuTw3NxcymUyrbZaPN1EoFHqtFQBatWqFq1evKoMUEYmHY0SISC8GDBgAc3NzxMXF4d///jfWr1+Po0eP4vLly4iOjoa5ublW2/P19YVEIsGvv/6Ke/fuIS8vT2+1Tp06FSdOnMDYsWORmJiI5ORk7Ny5k4NViUTAIEJEemFhYYGxY8fiiy++wLRp09CxY0f06tULPXv2RN++fdGkSROtttewYUN8/PHHmDZtGtzd3fUaElq2bInDhw/j+vXriIiIQGhoKGbOnAkvLy+97YOINCMRnjyRS0RERFRL2CNCREREomEQISIiItEwiBAREZFoGESIiIhINAwiREREJBoGESIiIhINgwgRERGJhkGEiIiIRMMgQkRERKJhECEiIiLRMIgQERGRaBhEiIiISDT/D9tiuywMiI6vAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 600x400 with 1 Axes>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "plot_estimates(estimates, figsize=(6, 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8dfc16d9",
   "metadata": {},
   "source": [
    "## Custom application model\n",
    "\n",
    "The built-in application wrappers (`QSharpApplication`, `CirqApplication`, etc.) are convenient for importing existing circuits, but sometimes you need finer control. By subclassing `Application`, you can define a *custom application model* that separates two kinds of parameters:\n",
    "\n",
    "- **Instance parameters** define *which* problem you are solving. They are fixed for a given estimation run. For example, the bit-size of an adder or the key length of an encryption scheme.\n",
    "- **Trace parameters** are algorithmic hyperparameters that control *how* the problem is solved. QRE varies these automatically and explores all combinations during estimation. For example, the choice of adder circuit, the compute-to-memory ratio, or the eviction strategy.\n",
    "\n",
    "Below we build a configurable adder application that lets QRE explore different adder implementations, compute fractions, and memory-management strategies for a given bit-size, and then compare the Pareto-optimal results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "932122cc",
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "import Std.Arithmetic.*;\n",
    "import Std.Convert.*;\n",
    "import Std.Math.*;\n",
    "import Std.ResourceEstimation.*;\n",
    "\n",
    "// An adder operation that we will estimate together with instance and trace parameters\n",
    "operation Adder(n : Int, adder: (Qubit[], Qubit[]) => Unit, computeFraction: Double, strategy: Int) : Unit {\n",
    "    use a = Qubit[n];\n",
    "    use b = Qubit[n];\n",
    "\n",
    "    if computeFraction > 0.0 {\n",
    "        // Enables an automatic memory/compute architecture with a fixed number of compute qubits\n",
    "        // (computed as a fraction of the total number of qubits).  The strategy controls how\n",
    "        // compute qubits are deallocated if free compute qubits are needed (either least recently\n",
    "        // used or least frequently used).\n",
    "        EnableMemoryComputeArchitecture(Ceiling(computeFraction * IntAsDouble(n)), strategy);\n",
    "    }\n",
    "\n",
    "    // Run the adder\n",
    "    adder(a, b);\n",
    "\n",
    "    // Reset all qubits (if code is used in simulation)\n",
    "    ResetAll(a + b);\n",
    "}\n",
    "\n",
    "// Re-export adders and strategies to use in Python, together with strings to use in displaying the results\n",
    "function CGAdder(): ((Qubit[], Qubit[]) => Unit, String) { (RippleCarryCGIncByLE, \"CG\") }\n",
    "function TTKAdder(): ((Qubit[], Qubit[]) => Unit, String) { (RippleCarryTTKIncByLE, \"TTK\") }\n",
    "function LRU(): (Int, String) { (LeastRecentlyUsed(), \"LRU\") }\n",
    "function LFU(): (Int, String) { (LeastFrequentlyUsed(), \"LFU\") }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df9bb728",
   "metadata": {},
   "source": [
    "Trace parameters are defined as a `dataclass` with `kw_only=True`. Only keyword-only fields are treated as enumerable parameters by QRE; regular fields are considered fixed. Each keyword field must specify a `domain` in its `metadata` dict; this tells QRE which values to explore during estimation.\n",
    "\n",
    "In the `AdderParameters` class below:\n",
    "\n",
    "- **`adder`** selects the adder implementation: the Craig Gidney (CG) ripple-carry adder or the Takahashi–Tani–Kunihiro (TTK) ripple-carry adder.\n",
    "- **`compute_fraction`** controls the ratio of qubits allocated to active computation versus memory, passed to `EnableMemoryComputeArchitecture` in the Q# operation.\n",
    "- **`strategy`** chooses the eviction policy when compute capacity is exceeded: Least Recently Used (LRU) or Least Frequently Used (LFU).\n",
    "\n",
    "Each field carries a default value (used when calling the application directly) and a domain (used when QRE enumerates all parameter combinations)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "cd26a6b7",
   "metadata": {},
   "outputs": [],
   "source": [
    "@dataclass(kw_only=True)\n",
    "class AdderParameters:\n",
    "    adder: tuple[Any, str] = field(default = qdk.code.CGAdder(), metadata={\"domain\": [qdk.code.CGAdder(), qdk.code.TTKAdder()]})\n",
    "    compute_fraction: float = field(default=1.0, metadata={\"domain\": [0.5, 1.0, 1.5, 2.0]})\n",
    "    strategy: tuple[int, str] = field(default=qdk.code.LRU(), metadata={\"domain\": [qdk.code.LRU(), qdk.code.LFU()]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d8f3f9d",
   "metadata": {},
   "source": [
    "The `Adder` class subclasses `Application[AdderParameters]`, binding the trace-parameter type. Its single instance parameter, `bitsize`, is a regular (non-keyword) dataclass field that defines which problem instance to estimate.\n",
    "\n",
    "Key details:\n",
    "\n",
    "- **`get_trace(parameters)`** is the core method that QRE calls once for every combination of trace parameters. It invokes the Q# `Adder` operation with the current `bitsize` and the selected `adder`, `compute_fraction`, and `strategy` values, producing a resource-estimation trace.\n",
    "- **`set_property`** attaches custom metadata (the adder name, compute fraction, and strategy name) to each trace. These properties are carried through estimation and can later be surfaced as custom columns in the results table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "fe5f3ef6",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Property keys are integers; we can use the custom_properties function to\n",
    "# define custom properties that do not conflict with existing properties.\n",
    "ADDER, COMPUTE_FRACTION, STRATEGY = custom_properties(3)\n",
    "\n",
    "@dataclass\n",
    "class Adder(Application[AdderParameters]):\n",
    "    bitsize: int\n",
    "\n",
    "    def __post_init__(self):\n",
    "        # Disable parallel trace generation since passing the Q# adder operations is not thread-safe.\n",
    "        self.disable_parallel_traces()\n",
    "\n",
    "    def get_trace(self, parameters: AdderParameters):\n",
    "        # obtain the adder function and its name\n",
    "        adder_fn, adder_name = parameters.adder\n",
    "        # obtain the memory/compute strategy and its name\n",
    "        strategy, strategy_name = parameters.strategy\n",
    "        # generate a trace from the Q# entry point with the specified parameters\n",
    "        trace = trace_from_entry_expr(qdk.code.Adder, self.bitsize, adder_fn, parameters.compute_fraction, strategy)\n",
    "\n",
    "        # Set trace properties for later analysis and display\n",
    "        trace.set_property(ADDER, adder_name)\n",
    "        trace.set_property(COMPUTE_FRACTION, parameters.compute_fraction)\n",
    "        trace.set_property(STRATEGY, strategy_name)\n",
    "\n",
    "        return trace"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a466e2f2",
   "metadata": {},
   "source": [
    "Because the Q# operation enables a memory/compute architecture, the estimation must account for *memory qubits* in addition to compute and factory qubits. We extend the ISA query with `TwoDimensionalYokedSurfaceCode`, which models yoked surface codes for memory qubits on top of the lattice-surgery layer provided by `SurfaceCode`.\n",
    "\n",
    "We also enrich the results table with additional columns:\n",
    "\n",
    "- **`add_factory_summary_column()`** adds a human-readable summary of the magic-state factories used (e.g., `2×T, 1×CCZ`).\n",
    "- **`add_qubit_partition_column()`** breaks the total physical qubit count into compute, factory, and memory partitions.\n",
    "- Three **custom property columns** (`Adder`, `Compute fraction`, `Strategy`) surface the trace properties we attached earlier, so each row shows which algorithmic configuration produced that result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "de782d73",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>name</th>\n",
       "      <th>qubits</th>\n",
       "      <th>runtime</th>\n",
       "      <th>error</th>\n",
       "      <th>factories</th>\n",
       "      <th>physical_compute_qubits</th>\n",
       "      <th>physical_factory_qubits</th>\n",
       "      <th>physical_memory_qubits</th>\n",
       "      <th>adder</th>\n",
       "      <th>compute_fraction</th>\n",
       "      <th>strategy</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Configurable adder</td>\n",
       "      <td>14048</td>\n",
       "      <td>0 days 00:00:00.007940</td>\n",
       "      <td>0.009500</td>\n",
       "      <td>5×T</td>\n",
       "      <td>7448</td>\n",
       "      <td>4900</td>\n",
       "      <td>1700</td>\n",
       "      <td>TTK</td>\n",
       "      <td>1.0</td>\n",
       "      <td>LRU</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Configurable adder</td>\n",
       "      <td>15028</td>\n",
       "      <td>0 days 00:00:00.006545</td>\n",
       "      <td>0.007920</td>\n",
       "      <td>6×T</td>\n",
       "      <td>7448</td>\n",
       "      <td>5880</td>\n",
       "      <td>1700</td>\n",
       "      <td>TTK</td>\n",
       "      <td>1.0</td>\n",
       "      <td>LFU</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Configurable adder</td>\n",
       "      <td>15233</td>\n",
       "      <td>0 days 00:00:00.006205</td>\n",
       "      <td>0.008384</td>\n",
       "      <td>2×T</td>\n",
       "      <td>3969</td>\n",
       "      <td>680</td>\n",
       "      <td>10584</td>\n",
       "      <td>CG</td>\n",
       "      <td>0.5</td>\n",
       "      <td>LRU</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Configurable adder</td>\n",
       "      <td>17729</td>\n",
       "      <td>0 days 00:00:00.006180</td>\n",
       "      <td>0.008911</td>\n",
       "      <td>6×T</td>\n",
       "      <td>10829</td>\n",
       "      <td>5880</td>\n",
       "      <td>1020</td>\n",
       "      <td>TTK</td>\n",
       "      <td>1.5</td>\n",
       "      <td>LRU</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Configurable adder</td>\n",
       "      <td>18709</td>\n",
       "      <td>0 days 00:00:00.005010</td>\n",
       "      <td>0.007249</td>\n",
       "      <td>7×T</td>\n",
       "      <td>10829</td>\n",
       "      <td>6860</td>\n",
       "      <td>1020</td>\n",
       "      <td>TTK</td>\n",
       "      <td>1.5</td>\n",
       "      <td>LFU</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Configurable adder</td>\n",
       "      <td>19781</td>\n",
       "      <td>0 days 00:00:00.004375</td>\n",
       "      <td>0.008869</td>\n",
       "      <td>4×T</td>\n",
       "      <td>14161</td>\n",
       "      <td>3920</td>\n",
       "      <td>1700</td>\n",
       "      <td>CG</td>\n",
       "      <td>2.0</td>\n",
       "      <td>LFU</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>Configurable adder</td>\n",
       "      <td>20761</td>\n",
       "      <td>0 days 00:00:00.003805</td>\n",
       "      <td>0.007727</td>\n",
       "      <td>5×T</td>\n",
       "      <td>14161</td>\n",
       "      <td>4900</td>\n",
       "      <td>1700</td>\n",
       "      <td>CG</td>\n",
       "      <td>2.0</td>\n",
       "      <td>LRU</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>Configurable adder</td>\n",
       "      <td>31801</td>\n",
       "      <td>0 days 00:00:00.001890</td>\n",
       "      <td>0.003385</td>\n",
       "      <td>18×T</td>\n",
       "      <td>14161</td>\n",
       "      <td>17640</td>\n",
       "      <td>0</td>\n",
       "      <td>TTK</td>\n",
       "      <td>2.0</td>\n",
       "      <td>LRU</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                 name  qubits                runtime     error factories  \\\n",
       "0  Configurable adder   14048 0 days 00:00:00.007940  0.009500       5×T   \n",
       "1  Configurable adder   15028 0 days 00:00:00.006545  0.007920       6×T   \n",
       "2  Configurable adder   15233 0 days 00:00:00.006205  0.008384       2×T   \n",
       "3  Configurable adder   17729 0 days 00:00:00.006180  0.008911       6×T   \n",
       "4  Configurable adder   18709 0 days 00:00:00.005010  0.007249       7×T   \n",
       "5  Configurable adder   19781 0 days 00:00:00.004375  0.008869       4×T   \n",
       "6  Configurable adder   20761 0 days 00:00:00.003805  0.007727       5×T   \n",
       "7  Configurable adder   31801 0 days 00:00:00.001890  0.003385      18×T   \n",
       "\n",
       "   physical_compute_qubits  physical_factory_qubits  physical_memory_qubits  \\\n",
       "0                     7448                     4900                    1700   \n",
       "1                     7448                     5880                    1700   \n",
       "2                     3969                      680                   10584   \n",
       "3                    10829                     5880                    1020   \n",
       "4                    10829                     6860                    1020   \n",
       "5                    14161                     3920                    1700   \n",
       "6                    14161                     4900                    1700   \n",
       "7                    14161                    17640                       0   \n",
       "\n",
       "  adder  compute_fraction strategy  \n",
       "0   TTK               1.0      LRU  \n",
       "1   TTK               1.0      LFU  \n",
       "2    CG               0.5      LRU  \n",
       "3   TTK               1.5      LRU  \n",
       "4   TTK               1.5      LFU  \n",
       "5    CG               2.0      LFU  \n",
       "6    CG               2.0      LRU  \n",
       "7   TTK               2.0      LRU  "
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isa_query = SurfaceCode.q() * RoundBasedFactory.q() * TwoDimensionalYokedSurfaceCode.q(source=SurfaceCode.q())\n",
    "\n",
    "results = estimate(Adder(64), arch, isa_query, max_error=0.01, name=\"Configurable adder\")\n",
    "results.add_factory_summary_column()\n",
    "results.add_qubit_partition_column()\n",
    "results.add_property_column(ADDER, \"adder\")\n",
    "results.add_property_column(COMPUTE_FRACTION, \"compute_fraction\")\n",
    "results.add_property_column(STRATEGY, \"strategy\")\n",
    "results.as_frame()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e0ae55d",
   "metadata": {},
   "source": [
    "The table above shows the Pareto-optimal configurations for a 64-bit adder. Each row represents a point on the Pareto frontier where no other configuration achieves both fewer physical qubits *and* a shorter runtime within the 1% error budget. The custom columns make it easy to see which adder implementation, compute fraction, and eviction strategy each result uses.\n",
    "\n",
    "We can also visualize the Pareto frontier as a scatter plot of physical qubits versus runtime:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "7a33193a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 400x400 with 1 Axes>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results.plot(figsize=(4, 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57b74ab4",
   "metadata": {},
   "source": [
    "## Conclusion\n",
    "\n",
    "This notebook demonstrated two ways to use the Quantum Resource Estimator:\n",
    "\n",
    "1. **Importing existing programs.** QRE accepts quantum programs from Q#, Cirq, QIR, and OpenQASM through dedicated application wrappers, making it straightforward to estimate resources for circuits written in any of these frameworks.\n",
    "2. **Building a custom application model.** By subclassing `Application` and defining trace parameters as a dataclass, you can let QRE automatically explore algorithmic hyperparameters (adder choice, compute fraction, eviction strategy) and compare Pareto-optimal results across all combinations.\n",
    "\n",
    "For more details on inspecting and visualizing estimation results, see the [Analysing Resource Estimation Results](2_analysing_results.ipynb) notebook."
   ]
  }
 ],
 "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.11.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}