{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ab1f991f",
   "metadata": {},
   "source": [
    "# Getting started with resource estimation\n",
    "\n",
    "## High-level overview\n",
    "\n",
    "The quantum resource estimator takes two inputs: an **application model** that describes the quantum computation (e.g., a Q# program), and an **architecture model** that describes the target quantum hardware (e.g., gate-based superconducting qubits with specific gate times and error rates). From the application, it generates one or more *traces*, compact representations of the instruction sequences applied to qubits. From the architecture, it derives a physical *instruction set* (ISA) specifying each gate's time, qubit cost, and error rate. Between these two layers sit configurable *transforms*: trace transforms such as gate decomposition and layout routines reshape the application trace, while ISA transforms such as quantum error correction codes and magic state factories build higher-fidelity logical instruction sets from the architecture's physical primitives.\n",
    "\n",
    "<img src=\"overview.svg\" title=\"Resource estimator overview\" style=\"max-width: 1000px; width: 100%\" />\n",
    "\n",
    "Because there are many valid choices at each layer (rooted in different QEC code distances, different factory protocols, different decomposition parameters), the estimator faces a large combinatorial design space. Rather than evaluating a single fixed configuration, it systematically explores the product of all trace and ISA variants, evaluates physical qubit counts, runtimes, and accumulated error rates for each combination, and then filters the results down to a **Pareto-optimal frontier**: the set of configurations where no other result is simultaneously better in both qubits and runtime while staying within a user-specified error budget.\n",
    "\n",
    "In practice, a typical estimation call composes these pieces in a single line, like `estimate(app, arch, isa_query=SurfaceCode.q() * RoundBasedFactory.q(), max_error=0.01)`, and returns an `EstimationTable` of Pareto-optimal results that can be inspected, extended with custom columns, or plotted. The remainder of this notebook walks through each step: setting up an application from Q#, choosing an architecture, running the estimation, and interpreting the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "a73b026c",
   "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": [
    "# Imports\n",
    "from pathlib import Path\n",
    "\n",
    "import qdk\n",
    "from qdk import qsharp\n",
    "from qdk.qre import estimate, property_name, plot_estimates\n",
    "from qdk.qre.models import GateBased, SurfaceCode, RoundBasedFactory\n",
    "from qdk.qre.application import QSharpApplication\n",
    "\n",
    "from qdk.qre.instruction_ids import T, LATTICE_SURGERY\n",
    "from qdk.qre.property_keys import NUM_TS_PER_ROTATION"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f317aee",
   "metadata": {},
   "source": [
    "## Setting up an application\n",
    "\n",
    "In this example, we use `QSharpApplication` to define the application we want to\n",
    "estimate using a Q# entry point.  Other application loaders are available to\n",
    "[read quantum programs from other langauges and frameworks](1_qre_input.ipynb).\n",
    "First, we load an existing Q# program from the samples directory and evaluate\n",
    "it.  Alternatively, we could load a Q# project or evaluate the Q# program inline\n",
    "in a notebook cell.  We create the application instance using the `Main()` entry\n",
    "point of the Q# program."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5ecb972d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluate the Q# application into the scope\n",
    "qsharp.eval(Path(\"../algorithms/Ising/Simple1dIsingOrder1.qs\").read_text(encoding=\"utf-8\"))\n",
    "\n",
    "# The entry point in the Q# code above is called Main\n",
    "app = QSharpApplication(qdk.code.Main)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35416b3e",
   "metadata": {},
   "source": [
    "We will soon use this application instance with the `estimate` method.  This\n",
    "will enumerate all possible traces given the application parameters and the\n",
    "trace transform.  But just to see some information of a trace, we can get the\n",
    "first trace that is enumerated from the application and look at its required ISA\n",
    "to implement it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "53268325",
   "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>encoding</th>\n",
       "      <th>arity</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>id</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>RZ</th>\n",
       "      <td>LOGICAL</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>MEAS_Z</th>\n",
       "      <td>LOGICAL</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       encoding  arity\n",
       "id                    \n",
       "RZ      LOGICAL      1\n",
       "MEAS_Z  LOGICAL      1"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Get one of the generated traces (in fact, this application only generates one trace, but in general there could be multiple)\n",
    "trace = next(app.enumerate_traces())\n",
    "\n",
    "trace.required_isa.as_frame()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "242a1b9b",
   "metadata": {},
   "source": [
    "The output shows the logical instructions required by this trace. In this case, the algorithm needs `RZ` (arbitrary Z-rotation) and `MEAS_Z` (measurement in the Z basis), both operating on single qubits at the logical level. The resource estimator's job is to determine how many physical qubits and how much time are needed to implement these logical operations, given the target architecture and the chosen error correction scheme.\n",
    "\n",
    "Note that the `QSharpApplication` only computes logical counts required for Pauli-based computation (PSSPC)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2383f33",
   "metadata": {},
   "source": [
    "## Choosing a target architecture\n",
    "\n",
    "The architecture model describes the physical quantum hardware. `GateBased` defines a gate-based architecture with configurable gate and measurement times (in nanoseconds) and a physical error rate (which defaults to $10^{-4}$). Calling `provided_isa()` on the architecture returns its physical instruction set: the set of native gates the hardware supports, along with each gate's encoding, arity, qubit cost, time, and error rate."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "490eeeab",
   "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>encoding</th>\n",
       "      <th>arity</th>\n",
       "      <th>space</th>\n",
       "      <th>time</th>\n",
       "      <th>error</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>id</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>PAULI_I</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQRT_SQRT_X</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQRT_SQRT_Y_DAG</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQRT_X_DAG</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>MEAS_X</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>500</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RZ</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>T_DAG</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>PAULI_Y</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQRT_SQRT_Y</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>MEAS_Z</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>500</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>CNOT</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQRT_Y_DAG</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQRT_X</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>H</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RY</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>T</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>PAULI_X</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQRT_SQRT_X_DAG</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>MEAS_Y</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>500</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S_DAG</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>SQRT_Y</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>CZ</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RX</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>PAULI_Z</th>\n",
       "      <td>PHYSICAL</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>100</td>\n",
       "      <td>0.0001</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                 encoding  arity  space  time   error\n",
       "id                                                   \n",
       "PAULI_I          PHYSICAL      1      1   100  0.0001\n",
       "SQRT_SQRT_X      PHYSICAL      1      1   100  0.0001\n",
       "SQRT_SQRT_Y_DAG  PHYSICAL      1      1   100  0.0001\n",
       "S                PHYSICAL      1      1   100  0.0001\n",
       "SQRT_X_DAG       PHYSICAL      1      1   100  0.0001\n",
       "MEAS_X           PHYSICAL      1      1   500  0.0001\n",
       "RZ               PHYSICAL      1      1   100  0.0001\n",
       "T_DAG            PHYSICAL      1      1   100  0.0001\n",
       "PAULI_Y          PHYSICAL      1      1   100  0.0001\n",
       "SQRT_SQRT_Y      PHYSICAL      1      1   100  0.0001\n",
       "MEAS_Z           PHYSICAL      1      1   500  0.0001\n",
       "CNOT             PHYSICAL      2      2   100  0.0001\n",
       "SQRT_Y_DAG       PHYSICAL      1      1   100  0.0001\n",
       "SQRT_X           PHYSICAL      1      1   100  0.0001\n",
       "H                PHYSICAL      1      1   100  0.0001\n",
       "RY               PHYSICAL      1      1   100  0.0001\n",
       "T                PHYSICAL      1      1   100  0.0001\n",
       "PAULI_X          PHYSICAL      1      1   100  0.0001\n",
       "SQRT_SQRT_X_DAG  PHYSICAL      1      1   100  0.0001\n",
       "MEAS_Y           PHYSICAL      1      1   500  0.0001\n",
       "S_DAG            PHYSICAL      1      1   100  0.0001\n",
       "SQRT_Y           PHYSICAL      1      1   100  0.0001\n",
       "CZ               PHYSICAL      2      2   100  0.0001\n",
       "RX               PHYSICAL      1      1   100  0.0001\n",
       "PAULI_Z          PHYSICAL      1      1   100  0.0001"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Define a gate-based architecture: physical error rate 1e-4, gate time 100 ns, measurement 500 ns\n",
    "arch = GateBased(error_rate=1e-4, gate_time=100, measurement_time=500)\n",
    "arch.provided_isa(arch.context()).as_frame()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "436bd2b5",
   "metadata": {},
   "source": [
    "The table above shows the physical gates provided by this architecture. All gates operate at the `PHYSICAL` encoding level. Single-qubit gates (e.g., `H`, `T`) each use 1 physical qubit and take 100 ns, while two-qubit gates (e.g., `CNOT`, `CZ`) use 2 qubits and also take 100 ns. Measurement (`MEAS_Z`) is slower at 500 ns. All gates share the same physical error rate of $10^{-4}$."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "476a9aa1",
   "metadata": {},
   "source": [
    "## Estimate\n",
    "\n",
    "The `estimate` function takes the application and architecture as inputs, along with an ISA query that specifies which quantum error correction codes and magic state factories to explore. The `SurfaceCode.q() * RoundBasedFactory.q()` ISA query enumerates the Cartesian product of surface code configurations (varying code distances) and round-based magic state distillation factories (varying distillation protocols). The `max_error` parameter sets the maximum tolerable error probability for the overall computation.\n",
    "\n",
    "The function returns an `EstimationTable` containing only Pareto-optimal results. These are configurations where no other result is simultaneously better in both physical qubits and runtime while staying within the error budget. The default columns show the total physical qubit count, the total runtime, and the accumulated error for each result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffa0d10b",
   "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>qubits</th>\n",
       "      <th>runtime</th>\n",
       "      <th>error</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>29036</td>\n",
       "      <td>0 days 00:00:00.005213</td>\n",
       "      <td>0.003162</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>30268</td>\n",
       "      <td>0 days 00:00:00.004411</td>\n",
       "      <td>0.003162</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>32928</td>\n",
       "      <td>0 days 00:00:00.003609</td>\n",
       "      <td>0.003162</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>37996</td>\n",
       "      <td>0 days 00:00:00.002807</td>\n",
       "      <td>0.003166</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>52332</td>\n",
       "      <td>0 days 00:00:00.002005</td>\n",
       "      <td>0.003499</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   qubits                runtime     error\n",
       "0   29036 0 days 00:00:00.005213  0.003162\n",
       "1   30268 0 days 00:00:00.004411  0.003162\n",
       "2   32928 0 days 00:00:00.003609  0.003162\n",
       "3   37996 0 days 00:00:00.002807  0.003166\n",
       "4   52332 0 days 00:00:00.002005  0.003499"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Run estimation over surface code × round-based factory configurations with 1% error budget\n",
    "results = estimate(app, arch, isa_query=SurfaceCode.q() * RoundBasedFactory.q(), max_error=0.01)\n",
    "\n",
    "# Returns a pandas DataFrame that can be used for further analysis or visualization\n",
    "results.as_frame()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d41b7893",
   "metadata": {},
   "source": [
    "Each row in the table represents a Pareto-optimal configuration. The first row uses the fewest physical qubits but has the longest runtime, while the last row is the fastest but requires the most qubits. All results stay within the specified `max_error=0.01` error budget.\n",
    "\n",
    "We can inspect the estimation statistics to see how large the design space was and how many configurations were explored to produce these Pareto-optimal results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "5354ea4a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "EstimationTableStats(num_traces=32, num_isas=1248, total_jobs=17424, successful_estimates=353, pareto_results=5)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "results.stats"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06a3c26b",
   "metadata": {},
   "source": [
    "### Adding pre-configured columns to the table\n",
    "\n",
    "The `EstimationTable` provides convenience methods to add commonly used columns. The method `add_qubit_partition_column()` breaks down the total physical qubit count into three components: compute qubits (for logical operations), factory qubits (for magic state distillation), and memory qubits (for data storage). The method `add_factory_summary_column()` adds a human-readable summary of the magic state factories used in each result (e.g., `20×T` meaning 20 copies of a T-state factory). These numbers can help to understand whether space-time trade-offs are possible. For example, if the amount of factory qubits is relatively high compared to the total number of qubits and many factory copies are used, than it's possible to reduce the total number of qubits by letting the algorithm run longer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "621904f7",
   "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>qubits</th>\n",
       "      <th>runtime</th>\n",
       "      <th>error</th>\n",
       "      <th>physical_compute_qubits</th>\n",
       "      <th>physical_factory_qubits</th>\n",
       "      <th>physical_memory_qubits</th>\n",
       "      <th>factories</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>29036</td>\n",
       "      <td>0 days 00:00:00.005213</td>\n",
       "      <td>0.003162</td>\n",
       "      <td>9436</td>\n",
       "      <td>19600</td>\n",
       "      <td>0</td>\n",
       "      <td>20×T</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>30268</td>\n",
       "      <td>0 days 00:00:00.004411</td>\n",
       "      <td>0.003162</td>\n",
       "      <td>6748</td>\n",
       "      <td>23520</td>\n",
       "      <td>0</td>\n",
       "      <td>24×T</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>32928</td>\n",
       "      <td>0 days 00:00:00.003609</td>\n",
       "      <td>0.003162</td>\n",
       "      <td>4508</td>\n",
       "      <td>28420</td>\n",
       "      <td>0</td>\n",
       "      <td>29×T</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>37996</td>\n",
       "      <td>0 days 00:00:00.002807</td>\n",
       "      <td>0.003166</td>\n",
       "      <td>2716</td>\n",
       "      <td>35280</td>\n",
       "      <td>0</td>\n",
       "      <td>36×T</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>52332</td>\n",
       "      <td>0 days 00:00:00.002005</td>\n",
       "      <td>0.003499</td>\n",
       "      <td>1372</td>\n",
       "      <td>50960</td>\n",
       "      <td>0</td>\n",
       "      <td>52×T</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   qubits                runtime     error  physical_compute_qubits  \\\n",
       "0   29036 0 days 00:00:00.005213  0.003162                     9436   \n",
       "1   30268 0 days 00:00:00.004411  0.003162                     6748   \n",
       "2   32928 0 days 00:00:00.003609  0.003162                     4508   \n",
       "3   37996 0 days 00:00:00.002807  0.003166                     2716   \n",
       "4   52332 0 days 00:00:00.002005  0.003499                     1372   \n",
       "\n",
       "   physical_factory_qubits  physical_memory_qubits factories  \n",
       "0                    19600                       0      20×T  \n",
       "1                    23520                       0      24×T  \n",
       "2                    28420                       0      29×T  \n",
       "3                    35280                       0      36×T  \n",
       "4                    50960                       0      52×T  "
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Break total qubits into compute, factory, and memory components\n",
    "results.add_qubit_partition_column()\n",
    "# Add a human-readable factory summary (e.g., \"20×T\")\n",
    "results.add_factory_summary_column()\n",
    "\n",
    "results.as_frame()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48cac642",
   "metadata": {},
   "source": [
    "### Querying properties of selected results\n",
    "\n",
    "Individual results can be accessed by index (e.g., `results[0]` for the first Pareto-optimal result). Each result exposes:\n",
    "- `properties`: a dictionary of named metrics such as qubit counts and algorithm parameters,\n",
    "- `source`: a mapping from instruction IDs to their implementation details, including error rates and timing, and\n",
    "- `factories`: a mapping from instruction IDs to their magic state factory details, including the number of states produced."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "e1df03eb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'LOGICAL_MEMORY_QUBITS': 0, 'EVALUATION_TIME': 2032086, 'NUM_TS_PER_ROTATION': 13, 'ALGORITHM_COMPUTE_QUBITS': 9, 'PHYSICAL_COMPUTE_QUBITS': 9436, 'ALGORITHM_MEMORY_QUBITS': 0, 'LOGICAL_COMPUTE_QUBITS': 28, 'PHYSICAL_FACTORY_QUBITS': 19600}\n",
      "2.1303500000000005e-07\n",
      "1547\n"
     ]
    }
   ],
   "source": [
    "# Print all properties for the first result, converting numeric keys to human-readable names\n",
    "print(dict((property_name(k), v) for k, v in results[0].properties.items()))\n",
    "\n",
    "# Get the logical error rate of the T gate instruction in the first result\n",
    "print(results[0].source[T].instruction.error_rate())\n",
    "\n",
    "# Get the total number of T states produced by the magic state factory\n",
    "print(results[0].factories[T].states)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf9c7111",
   "metadata": {},
   "source": [
    "### Adding custom columns to the table\n",
    "\n",
    "Beyond the pre-configured columns, you can add arbitrary custom columns using `add_column(name, function)` or populate a column from a property key using `add_property_column(key)`. With `add_column`, the function receives an `EstimationTableEntry` and returns the value for that column. With `add_property_column`, the column is automatically populated from the entry's `properties` dictionary using the given key. These methods are useful for surfacing specific metrics, such as per-instruction error rates, factory state counts, or algorithm-level parameters, directly in the results table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "e1fcc7d3",
   "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>qubits</th>\n",
       "      <th>runtime</th>\n",
       "      <th>error</th>\n",
       "      <th>physical_compute_qubits</th>\n",
       "      <th>physical_factory_qubits</th>\n",
       "      <th>physical_memory_qubits</th>\n",
       "      <th>factories</th>\n",
       "      <th>t_error_rate</th>\n",
       "      <th>logical_error_rate</th>\n",
       "      <th>t_states</th>\n",
       "      <th>num_ts_per_rotation</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>29036</td>\n",
       "      <td>0 days 00:00:00.005213</td>\n",
       "      <td>0.003162</td>\n",
       "      <td>9436</td>\n",
       "      <td>19600</td>\n",
       "      <td>0</td>\n",
       "      <td>20×T</td>\n",
       "      <td>2.130350e-07</td>\n",
       "      <td>3.000000e-16</td>\n",
       "      <td>1547</td>\n",
       "      <td>13</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>30268</td>\n",
       "      <td>0 days 00:00:00.004411</td>\n",
       "      <td>0.003162</td>\n",
       "      <td>6748</td>\n",
       "      <td>23520</td>\n",
       "      <td>0</td>\n",
       "      <td>24×T</td>\n",
       "      <td>2.130350e-07</td>\n",
       "      <td>3.000000e-14</td>\n",
       "      <td>1547</td>\n",
       "      <td>13</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>32928</td>\n",
       "      <td>0 days 00:00:00.003609</td>\n",
       "      <td>0.003162</td>\n",
       "      <td>4508</td>\n",
       "      <td>28420</td>\n",
       "      <td>0</td>\n",
       "      <td>29×T</td>\n",
       "      <td>2.130350e-07</td>\n",
       "      <td>3.000000e-12</td>\n",
       "      <td>1547</td>\n",
       "      <td>13</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>37996</td>\n",
       "      <td>0 days 00:00:00.002807</td>\n",
       "      <td>0.003166</td>\n",
       "      <td>2716</td>\n",
       "      <td>35280</td>\n",
       "      <td>0</td>\n",
       "      <td>36×T</td>\n",
       "      <td>2.130350e-07</td>\n",
       "      <td>3.000000e-10</td>\n",
       "      <td>1547</td>\n",
       "      <td>13</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>52332</td>\n",
       "      <td>0 days 00:00:00.002005</td>\n",
       "      <td>0.003499</td>\n",
       "      <td>1372</td>\n",
       "      <td>50960</td>\n",
       "      <td>0</td>\n",
       "      <td>52×T</td>\n",
       "      <td>2.130350e-07</td>\n",
       "      <td>3.000000e-08</td>\n",
       "      <td>1547</td>\n",
       "      <td>13</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   qubits                runtime     error  physical_compute_qubits  \\\n",
       "0   29036 0 days 00:00:00.005213  0.003162                     9436   \n",
       "1   30268 0 days 00:00:00.004411  0.003162                     6748   \n",
       "2   32928 0 days 00:00:00.003609  0.003162                     4508   \n",
       "3   37996 0 days 00:00:00.002807  0.003166                     2716   \n",
       "4   52332 0 days 00:00:00.002005  0.003499                     1372   \n",
       "\n",
       "   physical_factory_qubits  physical_memory_qubits factories  t_error_rate  \\\n",
       "0                    19600                       0      20×T  2.130350e-07   \n",
       "1                    23520                       0      24×T  2.130350e-07   \n",
       "2                    28420                       0      29×T  2.130350e-07   \n",
       "3                    35280                       0      36×T  2.130350e-07   \n",
       "4                    50960                       0      52×T  2.130350e-07   \n",
       "\n",
       "   logical_error_rate  t_states  num_ts_per_rotation  \n",
       "0        3.000000e-16      1547                   13  \n",
       "1        3.000000e-14      1547                   13  \n",
       "2        3.000000e-12      1547                   13  \n",
       "3        3.000000e-10      1547                   13  \n",
       "4        3.000000e-08      1547                   13  "
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Error rate of the T instruction (fixed arity, so no argument needed)\n",
    "results.add_column(\"t_error_rate\", lambda r: r.source[T].instruction.error_rate())\n",
    "\n",
    "# Error rate of the LATTICE_SURGERY instruction.  This instruction has variable\n",
    "# arity (it operates on a varying number of qubits), so we must pass an arity\n",
    "# value to evaluate the error rate function.  Arity 1 is the canonical single-\n",
    "# qubit evaluation point used throughout the estimator for comparison.\n",
    "results.add_column(\"logical_error_rate\", lambda r: r.source[LATTICE_SURGERY].instruction.error_rate(1))\n",
    "\n",
    "# Number of T states produced by the magic state factory\n",
    "results.add_column(\"t_states\", lambda r: r.factories[T].states)\n",
    "\n",
    "# Add a column directly from a property key\n",
    "results.add_property_column(NUM_TS_PER_ROTATION)\n",
    "\n",
    "results.as_frame()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a7569b8",
   "metadata": {},
   "source": [
    "### Plotting the results\n",
    "\n",
    "The `plot` method produces a log-log scatter plot of the Pareto frontier, with runtime on the x-axis and physical qubits on the y-axis. The `runtime_unit` parameter controls the x-axis scale (e.g., `\"ms\"`, `\"s\"`, `\"hours\"`), and `figsize` sets the figure dimensions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "dd244938",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 16056 characters]",
      "text/plain": [
       "<Figure size 600x400 with 1 Axes>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Log-log Pareto frontier: runtime (x) vs physical qubits (y)\n",
    "plot_estimates(results, figsize=(6, 4), runtime_unit=\"ms\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ebb0739c",
   "metadata": {},
   "source": [
    "The plot visualizes the Pareto frontier: each point represents a configuration that is optimal in the sense that no other configuration achieves both fewer qubits *and* a shorter runtime. Moving left along the x-axis trades qubits for speed, while moving down trades speed for fewer qubits."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab667cbc",
   "metadata": {},
   "source": [
    "## Compare multiple estimations\n",
    "\n",
    "A common workflow is to compare resource estimates across different hardware assumptions. Here we sweep over three physical error rates and run a separate estimation for each. The `name` parameter labels each run so the results can be distinguished in the plot. Passing the list of `EstimationTable` objects to `plot_estimates` overlays them on a single figure with a legend."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "c2e31f33",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 19460 characters]",
      "text/plain": [
       "<Figure size 600x400 with 1 Axes>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Compare estimates across different physical error rates\n",
    "results = []\n",
    "\n",
    "for error_rate in [1e-3, 1e-4, 1e-5]:\n",
    "    arch = GateBased(error_rate=error_rate, gate_time=100, measurement_time=500)\n",
    "    results.append(estimate(app, arch, isa_query=SurfaceCode.q() * RoundBasedFactory.q(), max_error=0.01, name=f\"p = {error_rate:.0e}\"))\n",
    "\n",
    "# Overlay multiple estimation runs on a single plot\n",
    "plot_estimates(results, figsize=(6, 4), runtime_unit=\"ms\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bb282a2",
   "metadata": {},
   "source": [
    "## Next steps\n",
    "\n",
    "This notebook covered the basic workflow for quantum resource estimation. To explore further:\n",
    "\n",
    "- Try different Q# applications from the `samples/` directory\n",
    "- Adjust the architecture parameters (e.g., different `error_rate` or `gate_time` values) to see how hardware assumptions affect the estimates\n",
    "- Explore different ISA queries, such as varying the surface code distance range with `SurfaceCode.q(distance=range(3, 30, 2))`\n",
    "- Compare results across multiple estimation runs using `plot_estimates`\n",
    "\n",
    "To learn how to estimate quantum programs written in various languages and frameworks, check out the [Importing quantum programs into QRE](1_qre_input.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
}
