{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f0c52178-5d77-43b2-8a0c-35691f96fdd9",
   "metadata": {},
   "source": [
    "## Memory Qubits in Q#\n",
    "\n",
    "This notebook illustrates an advanced feature in Q#: memory qubits. Memory qubits can store a quantum state, but gates or operations cannot be applied to them. We call regular qubits that do support these operations \"compute qubits.\" This distinction makes it possible to design algorithms that use fewer compute qubits, because memory qubits can use more efficient error-correction codes (see, for example, [yoked surface codes](https://arxiv.org/pdf/2312.04522)).\n",
    "\n",
    "By default, all qubits are allocated as compute qubits. Users can turn a compute qubit into a memory qubit by calling `Std.Memory.Store`; the qubit is then \"stored in memory,\" and no operations can be applied to it. When an operation needs to be performed on a qubit, it must be \"loaded\" from memory by calling `Std.Memory.Load`.\n",
    "\n",
    "To make `Std.Memory.Load` and `Std.Memory.Store` affect resource estimation, call `Std.ResourceEstimation.EnableManualMemoryComputeArchitecture()`. This enables the \"manual\" memory/compute strategy, where users explicitly move qubits between memory and compute.\n",
    "\n",
    "### Example: GHZ state preparation and measurement\n",
    "\n",
    "In the example below, we prepare the [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) on 10 memory qubits. We move qubits to memory as soon as each has been used as the control qubit of a CNOT gate. As a result, this circuit needs only 2 compute qubits. It also uses 10 memory qubits (to store the state), so in total it uses 12 qubits.\n",
    "\n",
    "Then we demonstrate how to measure a memory register by moving qubits from memory to compute one-by-one before measuring them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f596f9b9-0b8b-44cf-8a92-34e6b55ddbe1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from qdk import qsharp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "56f322d4-b77d-452a-a392-9ef7fbc6c04a",
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "// Prepares GHZ state (|0..0> + |1..1>)/sqrt(2) in a memory register of length n.\n",
    "// Uses at most 2 compute qubits.\n",
    "operation PrepareGhzStateInMemory(qs: Qubit[]) : Unit {\n",
    "    let n = Length(qs);\n",
    "    H(qs[0]);\n",
    "    for i in 1..n-1 {\n",
    "        CNOT(qs[i-1], qs[i]);\n",
    "        Std.Memory.Store(qs[i-1]);\n",
    "    }\n",
    "    Std.Memory.Store(qs[n-1]);\n",
    "}\n",
    "\n",
    "// Measures the value in a memory-qubit register, interpreted as a little-endian integer.\n",
    "// Uses a single compute qubit as a buffer.\n",
    "operation MeasureMemory(qs : Qubit[]) : Int {\n",
    "    mutable results = [];\n",
    "    for q in qs {\n",
    "      Std.Memory.Load(q);\n",
    "      set results += [MResetZ(q)];\n",
    "    }\n",
    "    return Std.Convert.ResultArrayAsInt(results);\n",
    "}\n",
    "\n",
    "operation Main() : Int {\n",
    "    Std.ResourceEstimation.EnableManualMemoryComputeArchitecture();\n",
    "    use qs = Qubit[10];\n",
    "    PrepareGhzStateInMemory(qs);\n",
    "    return MeasureMemory(qs);\n",
    "}\n",
    "\n",
    "operation MainNoMemoryCompute() : Int {\n",
    "    use qs = Qubit[10];\n",
    "    PrepareGhzStateInMemory(qs);\n",
    "    return MeasureMemory(qs);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "764ac24b-c5d3-4fee-93ee-e64cdb7a3be6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({1023: 12, 0: 8})"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from collections import Counter\n",
    "Counter(qsharp.run(\"Main()\", shots=20))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "79196eac-7f1e-4f21-9b4e-fecb95c3f5ea",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'numQubits': 12,\n",
       " 'tCount': 0,\n",
       " 'rotationCount': 0,\n",
       " 'rotationDepth': 0,\n",
       " 'cczCount': 0,\n",
       " 'ccixCount': 0,\n",
       " 'measurementCount': 10,\n",
       " 'numComputeQubits': 2,\n",
       " 'readFromMemoryCount': 10,\n",
       " 'writeToMemoryCount': 10}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "qsharp.logical_counts(\"Main()\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74e433db-45ce-4ff4-836b-35d4a24a912e",
   "metadata": {},
   "source": [
    "`logical_counts` reports 2 compute qubits and 12 total qubits, which implies 10 memory qubits, as expected.\n",
    "\n",
    "For comparison, if we do not call `EnableManualMemoryComputeArchitecture`, we get 10 total qubits without a breakdown into memory and compute, which means 10 compute qubits:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cc72bc32-b2bf-4086-aa46-36488cdbe102",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'numQubits': 10,\n",
       " 'tCount': 0,\n",
       " 'rotationCount': 0,\n",
       " 'rotationDepth': 0,\n",
       " 'cczCount': 0,\n",
       " 'ccixCount': 0,\n",
       " 'measurementCount': 10}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "qsharp.logical_counts(\"MainNoMemoryCompute()\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
