diff --git a/samples/azure-quantum/resource-estimation/estimation-chemistry.ipynb b/samples/azure-quantum/resource-estimation/estimation-chemistry.ipynb index 66a8081bf710..2689cf6f49d9 100644 --- a/samples/azure-quantum/resource-estimation/estimation-chemistry.ipynb +++ b/samples/azure-quantum/resource-estimation/estimation-chemistry.ipynb @@ -188,7 +188,7 @@ "source": [ "## Analyzing the results\n", "\n", - "Now that the results have been computed, we display them in a summary table. For this purpose we are creating a reusable `dashboard` function that is creating an HTML display from a pandas data frame and the resource estimation tables." + "Finally, we are presenting the experimental results using a summary table." ] }, { @@ -199,52 +199,7 @@ "source": [ "labels = [\"Gate-based µs, 10⁻³\", \"Gate-based µs, 10⁻⁴\", \"Gate-based ns, 10⁻³\", \"Gate-based ns, 10⁻⁴\", \"Majorana ns, 10⁻⁴\", \"Majorana ns, 10⁻⁶\"]\n", "\n", - "def dashboard(results):\n", - " def get_row(result):\n", - " # Extract raw data from result dictionary\n", - " logical_qubits = result[\"physicalCounts\"][\"breakdown\"][\"algorithmicLogicalQubits\"]\n", - " logical_depth = result[\"physicalCounts\"][\"breakdown\"][\"logicalDepth\"]\n", - " num_tstates = result[\"physicalCounts\"][\"breakdown\"][\"numTstates\"]\n", - " code_distance = result[\"logicalQubit\"][\"codeDistance\"]\n", - " num_tfactories = result[\"physicalCounts\"][\"breakdown\"][\"numTfactories\"]\n", - " tfactory_fraction = (result[\"physicalCounts\"][\"breakdown\"][\"physicalQubitsForTfactories\"] / result[\"physicalCounts\"][\"physicalQubits\"]) * 100\n", - " physical_qubits = result[\"physicalCounts\"][\"physicalQubits\"]\n", - " runtime = result[\"physicalCounts\"][\"runtime\"]\n", - "\n", - " # Format some entries\n", - " logical_depth_formatted = f\"{logical_depth:.1e}\"\n", - " num_tstates_formatted = f\"{num_tstates:.1e}\"\n", - " tfactory_fraction_formatted = f\"{tfactory_fraction:.1f}%\"\n", - " physical_qubits_formatted = f\"{physical_qubits / 1e6:.2f}M\"\n", - "\n", - " # Make runtime human readable; we find the largest units for which the\n", - " # runtime has a value that is larger than 1.0. For that unit we are\n", - " # rounding the value and append the unit suffix.\n", - " units = [(\"nanosecs\", 1), (\"microsecs\", 1000), (\"millisecs\", 1000), (\"secs\", 1000), (\"mins\", 60), (\"hours\", 60), (\"days\", 24), (\"years\", 365)]\n", - " runtime_formatted = runtime\n", - " for idx in range(1, len(units)):\n", - " if runtime_formatted / units[idx][1] < 1.0:\n", - " runtime_formatted = f\"{round(runtime_formatted) % units[idx][1]} {units[idx - 1][0]}\"\n", - " break\n", - " else:\n", - " runtime_formatted = runtime_formatted / units[idx][1]\n", - "\n", - " # special case for years\n", - " if isinstance(runtime_formatted, float):\n", - " runtime_formatted = f\"{round(runtime_formatted)} {units[-1][0]}\"\n", - "\n", - " # Append all extracted and formatted data to data array\n", - " return (logical_qubits, logical_depth_formatted, num_tstates_formatted, code_distance, num_tfactories, tfactory_fraction_formatted, physical_qubits_formatted, runtime_formatted)\n", - "\n", - " data = [get_row(results.data(index)) for index in range(len(results))]\n", - "\n", - " # Create data frame with explicit column names and configuration names extracted from array\n", - " import pandas as pd\n", - " df = pd.DataFrame(data, columns=[\"Logical qubits\", \"Logical depth\", \"T states\", \"Code distance\", \"T factories\", \"T factory fraction\", \"Physical qubits\", \"Physical runtime\"], index=labels)\n", - "\n", - " return df\n", - "\n", - "dashboard(results)" + "results.summary_data_frame(labels=labels)" ] }, { @@ -303,6 +258,12 @@ "source": [ "In this notebook, you've estimated the quantum computing requirements to calculate the energy of a Hamiltonian. Nice job! 👏🏽\n", "\n", + "The numbers for the XVIII-cas4-fb-64e-56o instance roughly match the numbers in\n", + "the paper [Assessing requirements for scaling quantum computers to real-world\n", + "impact](https://aka.ms/AQ/RE/Paper), as we incorporated a few improvements in\n", + "the implementation of the double-factorized chemistry algorithm as compared to\n", + "the version used when the paper was published.\n", + "\n", "We hope that this notebook was helpful to you. Here are some suggestions for next steps:\n", "* Try to estimate some custom FCIDUMP files\n", "* Investigate the details of resource estimation by exploring the detailed resource estimation tables\n", @@ -327,10 +288,10 @@ } ], "metadata": { - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 1 + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 1 } diff --git a/samples/azure-quantum/resource-estimation/estimation-dynamics.ipynb b/samples/azure-quantum/resource-estimation/estimation-dynamics.ipynb index f8b8ce0698d8..ded643f595c3 100644 --- a/samples/azure-quantum/resource-estimation/estimation-dynamics.ipynb +++ b/samples/azure-quantum/resource-estimation/estimation-dynamics.ipynb @@ -20,10 +20,9 @@ "metadata": {}, "outputs": [], "source": [ - "import qsharp.azure\n", - "targets = qsharp.azure.connect(\n", - " resourceId=\"\",\n", - " location=\"\")" + "from azure.quantum import Workspace\n", + "from azure.quantum.target.microsoft import MicrosoftEstimator, QubitParams, QECScheme\n", + "import qsharp" ] }, { @@ -32,8 +31,19 @@ "metadata": {}, "outputs": [], "source": [ - "qsharp.packages.add(\"Microsoft.Quantum.Numerics\")\n", - "qsharp.azure.target(\"microsoft.estimator\")" + "workspace = Workspace (\n", + " resource_id = \"\",\n", + " location = \"\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "qsharp.packages.add(\"Microsoft.Quantum.Numerics\")" ] }, { @@ -226,21 +236,7 @@ "- `N`: size of the square lattice.\n", "- `totTime`: the number of Trotter steps.\n", "- `dt` : the step size for the simulation, sometimes denoted as $\\Delta$.\n", - "- `eps`: the precision for arbitrary rotations.\n", - "\n", - "The last parameter `noops` is used in a way to implicitly slow down the\n", - "algorithm. When estimating physical resources, the Resource Estimator is\n", - "computing the number of T factory invocations in a way that the total runtime of\n", - "all T factories does not exceed the algorithm runtime. In order to achieve the\n", - "required number of T states, the T factories are copied sufficiently many times.\n", - "In order to obtain fewer copies, we can extend the algorithm runtime. One way\n", - "to do this, without increasing the number of T states, is by adding single qubit\n", - "measurements. Therefore we model such no-operations (NoOps) in the program by\n", - "measuring one qubit `noops` many times at the end of the program.\n", - "\n", - "ℹ️ In upcoming releases of the Resource Estimator we will improve the\n", - "configuration of optimization objectives for T factories and the overall\n", - "algorithm." + "- `eps`: the precision for arbitrary rotations." ] }, { @@ -252,7 +248,7 @@ "%%qsharp\n", "open Microsoft.Quantum.Math;\n", "\n", - "operation IsingModel2DSim(N : Int, J : Double, g : Double, totTime : Double, dt : Double, eps : Double, noops : Int) : Unit {\n", + "operation IsingModel2DSim(N : Int, J : Double, g : Double, totTime : Double, dt : Double, eps : Double) : Unit {\n", " use qs = Qubit[N * N];\n", " let len = Length(qs);\n", "\n", @@ -274,11 +270,6 @@ " }\n", " }\n", " }\n", - "\n", - " // apply `noops` NoOps to extend algorithm runtime\n", - " for _ in 1..noops {\n", - " Ignore(M(qs[0]));\n", - " }\n", "}" ] }, @@ -288,7 +279,12 @@ "source": [ "## Running the experiment\n", "\n", - "Next, we are estimating the physical resource estimates to simulate the Ising model Hamiltonian for a $10 \\times 10$ lattice with $J = g = 1.0$, total time $20$, step size $0.25$, and `eps` ${}=0.001$. To do this, we first create a Q# operation for this instance that takes as single input argument the number of NoOps, which will be job parameter dependent." + "Next, we are estimating the physical resource estimates to simulate the Ising\n", + "model Hamiltonian for a $10 \\times 10$ lattice with $J = g = 1.0$, total time\n", + "$20$, step size $0.25$, and `eps` ${}=0.001$. As configurations for the\n", + "experiment we use all six pre-defined qubit parameters. As pre-defined QEC\n", + "scheme we are using `surface_code` with gate-based qubit parameters (default),\n", + "and `floquet_code` with Majorana based qubit parameters." ] }, { @@ -297,20 +293,32 @@ "metadata": {}, "outputs": [], "source": [ - "%%qsharp\n", - "operation Ising10(noops : Int) : Unit {\n", - " IsingModel2DSim(10, 1.0, 1.0, 20.0, 0.25, 0.001, noops);\n", - "}" + "estimator = MicrosoftEstimator(workspace)\n", + "\n", + "labels = [\"Gate-based µs, 10⁻³\", \"Gate-based µs, 10⁻⁴\", \"Gate-based ns, 10⁻³\", \"Gate-based ns, 10⁻⁴\", \"Majorana ns, 10⁻⁴\", \"Majorana ns, 10⁻⁶\"]\n", + "\n", + "params = estimator.make_params(num_items=6)\n", + "params.arguments[\"N\"] = 10\n", + "params.arguments[\"J\"] = 1.0\n", + "params.arguments[\"g\"] = 1.0\n", + "params.arguments[\"totTime\"] = 20.0\n", + "params.arguments[\"dt\"] = 0.25\n", + "params.arguments[\"eps\"] = 0.001\n", + "params.items[0].qubit_params.name = QubitParams.GATE_US_E3\n", + "params.items[1].qubit_params.name = QubitParams.GATE_US_E4\n", + "params.items[2].qubit_params.name = QubitParams.GATE_NS_E3\n", + "params.items[3].qubit_params.name = QubitParams.GATE_NS_E4\n", + "params.items[4].qubit_params.name = QubitParams.MAJ_NS_E4\n", + "params.items[4].qec_scheme.name = QECScheme.FLOQUET_CODE\n", + "params.items[5].qubit_params.name = QubitParams.MAJ_NS_E6\n", + "params.items[5].qec_scheme.name = QECScheme.FLOQUET_CODE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "As configurations for the experiment we use all six pre-defined qubit\n", - "parameters. As pre-defined QEC scheme we are using `surface_code` with\n", - "gate-based qubit parameters, and `floquet_code` with Majorana based qubit\n", - "parameters." + "We are submitting a resource estimation job with all target parameter configurations." ] }, { @@ -319,25 +327,15 @@ "metadata": {}, "outputs": [], "source": [ - "labels = [\"Gate-based µs, 10⁻³\", \"Gate-based µs, 10⁻⁴\", \"Gate-based ns, 10⁻³\", \"Gate-based ns, 10⁻⁴\", \"Majorana ns, 10⁻⁴\", \"Majorana ns, 10⁻⁶\"]\n", - "\n", - "target_params = [\n", - " {\"qubitParams\": {\"name\": \"qubit_gate_us_e3\"}},\n", - " {\"qubitParams\": {\"name\": \"qubit_gate_us_e4\"}},\n", - " {\"qubitParams\": {\"name\": \"qubit_gate_ns_e3\"}},\n", - " {\"qubitParams\": {\"name\": \"qubit_gate_ns_e4\"}},\n", - " {\"qecScheme\": {\"name\": \"floquet_code\"}, \"qubitParams\": {\"name\": \"qubit_maj_ns_e4\"}},\n", - " {\"qecScheme\": {\"name\": \"floquet_code\"}, \"qubitParams\": {\"name\": \"qubit_maj_ns_e6\"}}\n", - "]" + "job = estimator.submit(IsingModel2DSim, input_params=params)\n", + "results = job.get_results()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We are submitting a resource estimation job with all target parameter\n", - "configurations. We create corresponding item objects by setting the input\n", - "argument `noops` to `0` for all items." + "Finally, we are presenting the experimental results using a summary table." ] }, { @@ -346,20 +344,40 @@ "metadata": {}, "outputs": [], "source": [ - "items = [{\"arguments\": [{\"name\": \"noops\", \"value\": 0, \"type\": \"Int\"}], **params} for params in target_params]\n", - "results = qsharp.azure.execute(Ising10, jobParams={\"items\": items})" + "results.summary_data_frame(labels=labels)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, we present the estimation results. First, we explicitly extract some\n", - "of the results in a summary table. Some values are using a user-defined\n", - "formatting. Then, we display full details for all qubit parameters in tables\n", - "that are constructed using the built-in resource estimation table feature. You\n", - "can re-use this `dashboard` function in other Q# + Python notebooks to display\n", - "similar tables your experiments." + "From the results we can observe that a large fraction of physical qubits is used\n", + "for the T factories. To understand why, it's important to remark that the\n", + "overall algorithm runtime is determined based on the number of logical\n", + "operations (also called logical cycles or logical depth). The runtime limits\n", + "the number of invocations of a single T factory. The total number of T factory\n", + "copies is computed based on the total number of required T states divided by the\n", + "number of possible invocations. Therefore, if the algorithm would run longer, a\n", + "T factory can be invoked more often, which may allow to compute all required T\n", + "states with less T factory copies.\n", + "\n", + "Since T factory fraction is high, while at the same time the physical runtime is\n", + "relatively small, this is a good opportunity for a space-time optimization based\n", + "on the logical depth. We can make an algorithm run longer, by inserting no-op\n", + "(no operation or idle) operations. We do this using the `logical_depth_factor`\n", + "constraint. For example, a value of 2 means that the number of cycles should be\n", + "twice as much, i.e., one no-op per operation; or, a value of 1.5 means that the\n", + "number of cycles is 50% more, i.e., one no-op for every two operations.\n", + "\n", + "Please note that the algorithm runtime may increase by a larger factor than the\n", + "`logical_depth_factor`. This is because no-ops also can incur logical errors,\n", + "and therefore the required logical error rate is lower, which in turn may\n", + "increase the required code distance, therefore leading a to a longer execution\n", + "time of a logical cycle.\n", + "\n", + "In the balanced implementation that is described in the paper, we increase the\n", + "logical depth by a factor of 10. We do this by updating the `params` variable.\n", + "All other parameters remain unchanged." ] }, { @@ -368,69 +386,17 @@ "metadata": {}, "outputs": [], "source": [ - "def dashboard(results):\n", - " def get_row(result):\n", - " # Extract raw data from result dictionary\n", - " logical_qubits = result[\"physicalCounts\"][\"breakdown\"][\"algorithmicLogicalQubits\"]\n", - " logical_depth = result[\"physicalCounts\"][\"breakdown\"][\"logicalDepth\"]\n", - " num_tstates = result[\"physicalCounts\"][\"breakdown\"][\"numTstates\"]\n", - " code_distance = result[\"logicalQubit\"][\"codeDistance\"]\n", - " num_tfactories = result[\"physicalCounts\"][\"breakdown\"][\"numTfactories\"]\n", - " tfactory_fraction = (result[\"physicalCounts\"][\"breakdown\"][\"physicalQubitsForTfactories\"] / result[\"physicalCounts\"][\"physicalQubits\"]) * 100\n", - " physical_qubits = result[\"physicalCounts\"][\"physicalQubits\"]\n", - " runtime = result[\"physicalCounts\"][\"runtime\"]\n", - "\n", - " # Format some entries\n", - " logical_depth_formatted = f\"{logical_depth:.1e}\"\n", - " num_tstates_formatted = f\"{num_tstates:.1e}\"\n", - " tfactory_fraction_formatted = f\"{tfactory_fraction:.1f}%\"\n", - " physical_qubits_formatted = f\"{physical_qubits / 1e6:.2f}M\"\n", - "\n", - " # Make runtime human readable; we find the largest units for which the\n", - " # runtime has a value that is larger than 1.0. For that unit we are\n", - " # rounding the value and append the unit suffix.\n", - " units = [(\"nanosecs\", 1), (\"microsecs\", 1000), (\"millisecs\", 1000), (\"secs\", 1000), (\"mins\", 60), (\"hours\", 60), (\"days\", 24), (\"years\", 365)]\n", - " runtime_formatted = runtime\n", - " for idx in range(1, len(units)):\n", - " if runtime_formatted / units[idx][1] < 1.0:\n", - " runtime_formatted = f\"{round(runtime_formatted) % units[idx][1]} {units[idx - 1][0]}\"\n", - " break\n", - " else:\n", - " runtime_formatted = runtime_formatted / units[idx][1]\n", - "\n", - " # special case for years\n", - " if isinstance(runtime_formatted, float):\n", - " runtime_formatted = f\"{round(runtime_formatted)} {units[-1][0]}\"\n", - "\n", - " # Append all extracted and formatted data to data array\n", - " return (logical_qubits, logical_depth_formatted, num_tstates_formatted, code_distance, num_tfactories, tfactory_fraction_formatted, physical_qubits_formatted, runtime_formatted)\n", - "\n", - " data = [get_row(results.data(index)) for index in range(len(results))]\n", - "\n", - " # Create data frame with explicit column names and configuration names extracted from array\n", - " import pandas as pd\n", - " df = pd.DataFrame(data, columns=[\"Logical qubits\", \"Logical depth\", \"T states\", \"Code distance\", \"T factories\", \"T factory fraction\", \"Physical qubits\", \"Physical runtime\"], index=labels)\n", - "\n", - " from IPython.display import HTML\n", - "\n", - " html = f\"\"\"\n", - "