{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Running MCFE on target circuits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pygsti\n",
    "from collections import defaultdict\n",
    "import numpy as np\n",
    "import time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create pyGSTi circs\n",
    "unmapped_circs = [pygsti.circuits.Circuit([[\"Gxpi2\", \"Q0\"], [\"Gypi2\", \"Q1\"]]),pygsti.circuits.Circuit([[\"Gypi2\", \"Q0\"], [\"Gxpi2\", \"Q1\"]])]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Map circuits to device connectivity and U3-CX gate set"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This step will be different depending on what architecture you are using. For this example, we are using an IBM device. You need to end up with pyGSTi circuits in a U3-CX gate set so that circuit mirroring can be performed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/ndsieki/pygsti_development/src/pygsti/pygsti/circuits/circuit.py:4154: UserWarning: pyGSTi circuit mapping discards classical registers.\n",
      "  _warnings.warn('pyGSTi circuit mapping discards classical registers.')\n",
      "/home/ndsieki/pygsti_development/src/pygsti/pygsti/circuits/circuit.py:4210: UserWarning: skipping measure\n",
      "  _warnings.warn('skipping measure')\n"
     ]
    }
   ],
   "source": [
    "mapped_circs = defaultdict(list)\n",
    "\n",
    "import qiskit\n",
    "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager as _pass_manager\n",
    "from qiskit_ibm_runtime.fake_provider import FakeSherbrooke, FakeAthensV2\n",
    "\n",
    "fake_backend = FakeAthensV2()\n",
    "\n",
    "pm = _pass_manager(coupling_map=fake_backend.coupling_map, basis_gates=['u3', 'cx'], optimization_level=0)\n",
    "\n",
    "\n",
    "for i, circ in enumerate(unmapped_circs):\n",
    "    # Convert from pyGSTi to Qiskit\n",
    "    # Comment these lines out and do qiskit_circ = circ if passing in Qiskit\n",
    "    pygsti_openqasm_circ = circ.convert_to_openqasm(block_between_layers=True, include_delay_on_idle=False)\n",
    "    # print(pygsti_openqasm_circ)\n",
    "    qiskit_circ = qiskit.QuantumCircuit.from_qasm_str(pygsti_openqasm_circ)\n",
    "\n",
    "    # print(qiskit_circ.draw())\n",
    "\n",
    "    mapped_qiskit_circ = pm.run(qiskit_circ)\n",
    "\n",
    "    # print(mapped_qiskit_circ.draw())\n",
    "    pygsti_circ, _ = pygsti.circuits.Circuit.from_qiskit(mapped_qiskit_circ)\n",
    "    # print(pygsti_circ)\n",
    "\n",
    "    mapped_circ = pygsti_circ\n",
    "\n",
    "    metadata = {'width': len(mapped_circ.line_labels), 'depth': mapped_circ.depth, 'dropped_gates': 0, 'id': i}\n",
    "    mapped_circs[mapped_circ] += [metadata]\n",
    "\n",
    "\n",
    "unmirrored_design = pygsti.protocols.FreeformDesign(mapped_circs)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Mirror circuit generation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We use Pauli random compiling (`pauli_rc`) here. Central Pauli (`central_pauli`) is also an option."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "using provided edesign for both reference and test compilations\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Sampling mirror circuits: 100%|##########| 2/2 [00:00<00:00,  3.54it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sampling reference circuits for width 5 with 1 subsets\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Sampling reference circuits for subset ('Q0', 'Q1', 'Q2', 'Q3', 'Q4'): 100%|##########| 100/100 [00:00<00:00, 4501.20it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mirroring time: 0.6060056686401367\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "# Highly recommended to seed all RNG\n",
    "mcfe_rand_state = np.random.RandomState(20240718)\n",
    "\n",
    "start = time.time()\n",
    "mirror_design = pygsti.protocols.mirror_edesign.make_mirror_edesign(\n",
    "    unmirrored_design,\n",
    "    account_for_routing=False,\n",
    "    num_mcs_per_circ=100,\n",
    "    num_ref_per_qubit_subset=100,\n",
    "    mirroring_strategy='pauli_rc',\n",
    "    rand_state=mcfe_rand_state)\n",
    "print(f'Mirroring time:', time.time() - start)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have created the MCFE experiment design."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Run the Edesign"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This example will run the edesign on a fake IBM backend, but this is not strictly required. This step needs to generate a `ProtocolData(edesign=mirror_edesign, dataset=circuit_counts_data)` where `mirror_edesign` is the variable defined earlier and `circuit_counts_data` is a `DataSet` that contains the outcomes for each circuit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.1233065128326416\n"
     ]
    }
   ],
   "source": [
    "from pygsti.extras.devices import ExperimentalDevice\n",
    "from pygsti.extras import devices, ibmq\n",
    "\n",
    "device = ExperimentalDevice.from_qiskit_backend(fake_backend)\n",
    "pspec = device.create_processor_spec(['Gc{}'.format(i) for i in range(24)] + ['Gcnot'])\n",
    "\n",
    "start = time.time()\n",
    "exp = ibmq.IBMQExperiment(mirror_design, pspec, circuits_per_batch=300, num_shots=1024, seed=20240718, checkpoint_override=True)\n",
    "print(time.time() - start)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "transpiling to basis gates ['measure', 'id', 'rz', 'reset', 'delay', 'sx', 'x', 'cx']\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 2/2 [00:13<00:00,  6.84s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total transpilation time: 13.707915306091309\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "from qiskit_aer import AerSimulator\n",
    "\n",
    "sim_backend = AerSimulator.from_backend(fake_backend)\n",
    "\n",
    "qiskit_convert_kwargs={}\n",
    "\n",
    "start = time.time()\n",
    "exp.transpile(sim_backend, direct_to_qiskit=True, qiskit_convert_kwargs=qiskit_convert_kwargs)\n",
    "end = time.time()\n",
    "print(f'Total transpilation time: {end - start}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Submitting batch 1\n",
      "  - Job ID is 7a324892-5a58-4fb1-a545-3183ba678622\n",
      "Submitting batch 2\n",
      "  - Job ID is e6888670-fe9d-4cf1-bea8-6b047927b9ec\n"
     ]
    }
   ],
   "source": [
    "exp.submit(sim_backend)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Querying IBMQ for results objects for batch 1...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Querying IBMQ for results objects for batch 2...\n",
      "11.363933324813843\n"
     ]
    }
   ],
   "source": [
    "start = time.time()\n",
    "exp.batch_results = []\n",
    "exp.retrieve_results()\n",
    "end = time.time()\n",
    "print(end - start)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = exp.data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Compute process fidelity for each circuit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Calculating effective polarizations:  67%|######7   | 336/500 [00:00<00:00, 3355.23it/s]"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Calculating effective polarizations: 100%|##########| 500/500 [00:00<00:00, 3217.16it/s]\n"
     ]
    }
   ],
   "source": [
    "from pygsti.protocols.vbdataframe import VBDataFrame\n",
    "\n",
    "df = VBDataFrame.from_mirror_experiment(unmirrored_design, data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you used Central Pauli instead, you can swap `'RC Process Fidelity'` for `'CP Process Fidelity'` in the cell below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "process_fidelities = df.dataframe['RC Process Fidelity']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0.9973274623920335, 0.9969363722899848]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "process_fidelities.tolist()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "qiskit2_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.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
