{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Custimize the pulse-level simulation\n", "Author: Boxi Li (etamin1201@gmail.com)\n", "\n", "In this note, we demonstrate examples of customizing the pulse-level simulator in qutip-qip.The notebook is divided into three parts:\n", "1. Customizing the Hamiltonian model\n", "2. Customizing the compiler\n", "3. Customizing the noise" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# imports\n", "import numpy as np\n", "\n", "from qutip import sigmax, sigmay, sigmaz, tensor, fidelity\n", "from qutip import basis\n", "\n", "from qutip_qip.pulse import Pulse\n", "from qutip_qip.device import ModelProcessor, Model\n", "from qutip_qip.circuit import QubitCircuit\n", "from qutip_qip.compiler import GateCompiler, Instruction, SpinChainCompiler\n", "from qutip_qip.noise import Noise\n", "\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Customizing the Hamiltonian model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We start from the customization of the Hamiltonian model, which is represented by a class instance of Model.A minimal requirement for a processor model is specifying the hardware parameters and the control Hamiltonians of the physical system. The former will be provided to the compiler for computing the pulse strength. The latter will be accessed by the Processor class via the method get_control." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "class MyModel(Model):\n", " def __init__(self, num_qubits, dims=None, h_x=1., h_z=1., g=0.1, t1=None, t2=None):\n", " super().__init__(num_qubits, dims=dims)\n", " self.params = {\n", " \"sz\": [h_z] * num_qubits,\n", " \"sx\": [h_x] * num_qubits,\n", " \"g\": [g] * num_qubits,\n", " \"t1\": t1, # Will be accessed by the noise module.\n", " \"t2\": t2,\n", " }\n", " # Control Hamiltonians\n", " _two_qubit_operator = tensor([sigmax(), sigmax()]) + tensor([sigmay(), sigmay()])\n", " self.controls = {}\n", " self.controls.update(\n", " {f\"sx{n}\": (2 * np.pi * sigmax(), n) for n in range(num_qubits)})\n", " self.controls.update(\n", " {f\"sz{n}\": (2 * np.pi * sigmaz(), n) for n in range(num_qubits)}),\n", " self.controls.update(\n", " {f\"g{n}\": (2 * np.pi * _two_qubit_operator, [n, n+1]) for n in range(num_qubits - 1)}),\n", " \n", " def get_control(self, label):\n", " \"\"\"\n", " The mandatory method. It Returns a pair of Qobj and int representing the control Hamiltonian and the target qubit.\n", " \"\"\"\n", " return self.controls[label]\n", "\n", " def get_control_labels(self):\n", " \"\"\"\n", " It returns all the labels of availble controls.\n", " \"\"\"\n", " return self.controls.keys()\n", "\n", " def get_control_latex(self):\n", " \"\"\"\n", " The function returns a list of dictionaries, corresponding to the latex representation of each control.\n", " This is used in the plotting. Controls in each dictionary will be plotted in a different colour.\n", " See examples later in this notebook.\n", " \"\"\"\n", " return ([\n", " {f\"sx{n}\": r\"$\\sigma_x^%d$\" % n for n in range(num_qubits)},\n", " {f\"sy{n}\": r\"$\\sigma_z^%d$\" % n for n in range(num_qubits)},\n", " {f\"g{n}\": r\"$g_{%d}$\" % (n) for n in range(num_qubits - 1)}])\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is a quantum system of $n$ qubits arranged in a chain (same as the [spin chain model](https://qutip-qip.readthedocs.io/en/stable/apidoc/qutip_qip.device.html?highlight=spinchain#qutip_qip.device.SpinChainModel)), where we have control over three Hamiltonian: $\\sigma_x$, $\\sigma_z$ on each qubit, and neighbouring-qubits interaction $\\sigma_x\\sigma_x+\\sigma_y\\sigma_y$:\n", "\n", "$$\n", "H = \\sum_{j=0}^{n-1} c_{1,j}(t) \\cdot h_x^{j}\\sigma_x^{j} + \\sum_{j=0}^{n-1} c_{2,j}(t) \\cdot h_z^{j}\\sigma_z^{j}\n", "+ \\sum_{j=0}^{n-2} c_{3,j}(t)\\cdot g^{j}(\\sigma_x^{j}\\sigma_x^{j+1}+\\sigma_y^{j}\\sigma_y^{j+1})\n", "$$\n", "\n", "where $h_x$, $h_z$, $g$ are the hardware parameters and $c_{i,j}(t)$ are the time-dependent control pulse coefficients. This Hamiltonian is the same as the one for the linear spin chain model in QuTiP. In general, the hardware parameters will not be identical for each qubit, but here, for simplicity, we represent them by three numbers: $h_x$, $h_z$ and $g$.\n", "\n", "To simulate a custom quantum device, we provide the model to ModelProcessor, which is used for simulators based on a concrete physics model (in contrast to optimal control for arbitrary Hamiltonians). In this way, we inherit the necessary methods from ModelProcessor used in the simulation. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "num_qubits = 2\n", "processor = ModelProcessor(model=MyModel(num_qubits, h_x=1.0, h_z=1.0, g=0.1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In set_up_ops, we define the control Hamiltonians of the quantum system and use them to initialize the control pulses. We can get the map between the pulse labels and the pulse location with" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dict_keys(['sx0', 'sx1', 'sz0', 'sz1', 'g0'])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "processor.get_control_labels()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or access a control Hamiltonian by its label:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n", " Qobj data =\n", " [[0. 6.28318531]\n", " [6.28318531 0. ]],\n", " 0)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sx0 = processor.get_control(\"sx0\")\n", "sx0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In qutip-qip 0.1 version, the model is directly defined in the Processor. This can still be achieved in version 0.2 as shown in the following cell. This is equivalent to the definition above. An instance of Model is automatically created and saved in the processor." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "class MyProcessor(ModelProcessor):\n", " \"\"\"\n", " Custom processor built using ModelProcessor as the base class.\n", " This custom processor will inherit all the methods of the base class\n", " such as setting up of the T1 and T2 decoherence rates in the simulations.\n", "\n", " In addition, it is possible to write your own functions to add control pulses.\n", "\n", " Args:\n", " num_qubits (int): Number of qubits in the processor.\n", " t1, t2 (float or list): The T1 and T2 decoherence rates for the\n", " \"\"\"\n", " def __init__(self, num_qubits, h_x, h_z, g, t1=None, t2=None):\n", " super(MyProcessor, self).__init__(num_qubits, t1=t1, t2=t2) # call the parent class initializer\n", " self.pulse_mode = \"discrete\" # The control pulse is discrete or continous.\n", " self.model.params.update({\"sz\": [h_z] * num_qubits, # can also be different for each qubit\n", " \"sx\": [h_x] * num_qubits,\n", " \"g\": [g] * num_qubits})\n", " self.model.dims = [2] * num_qubits # The dimension of each controllable quantum system, here num_qubits qubits.\n", " self.num_qubits = num_qubits\n", " self.set_up_ops() # set up the available Hamiltonians\n", "\n", " def set_up_ops(self):\n", " \"\"\"\n", " Sets up the control operators.\n", " \"\"\"\n", " for m in range(self.num_qubits):\n", " # sigmax pulse on m-th qubit with the corresponding pulse \n", " self.add_control(2 * np.pi * sigmax(), m, label=\"sx\" + str(m))\n", " # sz\n", " for m in range(self.num_qubits):\n", " self.add_control(2 * np.pi * sigmaz(), m, label=\"sz\" + str(m))\n", " # interaction operator\n", " operator = tensor([sigmax(), sigmax()]) + tensor([sigmay(), sigmay()])\n", " for m in range(self.num_qubits - 1):\n", " self.add_control(2 * np.pi * operator, [m, m+1], label=\"g\" + str(m))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Load and compile the circuit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "We first define a quantum circuit. Here we use a two-qubit circuit with two X gates. Acting on a $|00\\rangle$ state, this results in a $|11\\rangle$ state." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "circuit = QubitCircuit(num_qubits)\n", "circuit.add_gate(\"X\", targets=1)\n", "circuit.add_gate(\"X\", targets=0)\n", "circuit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For circuit plotting, see [this notebook](quantum-gates.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To convert a quantum circuit into the Hamiltonian model, we need a compiler. The custom definition of a compiler will be discussed in details in the next section. Because we used the Hamiltonian model of the spin chain, we here simply \"borrow\" the compiler of the spin chain model." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/latex": [ "Quantum object: dims = [[2, 2], [1, 1]], shape = (4, 1), type = ket\\begin{equation*}\\left(\\begin{array}{*{11}c}3.450\\times10^{-07}\\\\1.624\\times10^{-08}j\\\\1.624\\times10^{-08}j\\\\-1.000\\\\\\end{array}\\right)\\end{equation*}" ], "text/plain": [ "Quantum object: dims = [[2, 2], [1, 1]], shape = (4, 1), type = ket\n", "Qobj data =\n", "[[ 3.44966416e-07+0.00000000e+00j]\n", " [ 0.00000000e+00+1.62381269e-08j]\n", " [ 0.00000000e+00+1.62381269e-08j]\n", " [-1.00000000e+00+0.00000000e+00j]]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "processor = ModelProcessor(model=MyModel(num_qubits, h_x=1.0, h_z=1.0, g=0.1))\n", "processor.native_gates = [\"ISWAP\", \"RX\", \"RZ\"]\n", "\n", "# processor.num_qubits, processor.params access directly the information in the model.\n", "compiler = SpinChainCompiler(processor.num_qubits, processor.params)\n", "\n", "processor.load_circuit(circuit, compiler=compiler)\n", "result = processor.run_state(init_state = basis([2,2], [0,0]))\n", "result.states[-1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The compiled pulse coefficients are now saved in the processor and can be accessed by:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1.]\n", "[0. 0.25]\n" ] } ], "source": [ "sx1_pulse = processor.find_pulse(\"sx1\")\n", "print(sx1_pulse.coeff)\n", "print(sx1_pulse.tlist)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is a rectangular pulse that starts from time 0 and ends at time 0.25.\n", "\n", "#### Note\n", "\n", "For discrete pulse, the time sequence is one element shorter than the pulse coefficient because we need to specify the start and the end of the pulse. If two sequences are of the same length, the last element of coeff will be neglected. Later, we will see continuous pulse where coeff and tlist have the same length.\n", "\n", "To give an intuitive illustration of the control pulses, we give each pulse a latex label by defining a method get_operators_labels and then plot the compiled pulses." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "image/png": "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YAEAAMySwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaCCwAAIAmAgsAAKCJwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaCCwAAIAmAgsAAKCJwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaCCwAAIAmAgsAAKCJwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaCCwAAIAmAgsAAKCJwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaCCwAAIAmAgsAAKCJwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaTBRYVfWuqnq8qp6oqj+rqv+Y9WAAAACLZtvAqqp9Sf4uyR+PMW5Icl2SJ2c9GAAAwKLZN8F3jib51hjj3zaen0ryvaq6LMmDSc4meWyMMaMRAQAAFsMkWwRvSHLioucjG89HkzwyxvjDJO9rnwwAAGDBTBJYp5McTpKq+pUkf5DkW0kOJTm18Z1zsxgOAABgkUwSWF9O8mtVdTLJ3Um+O8Z4Nsl/53xkTfo7AAAAS22SMHpljPEbY4zrkzyX5B82Pv/7JO+vqs8meXRWAwIAACyK2u5yiqr68yS/n2Q1yb8k+ZMxxqtbfNUtFwAAwF5RW37YePvfXAXWJ499Mk+feXq3xwAAAKbwzivemQ/9+od2e4ytbBlYzk4BAAA0WdoVLAAAgBmyggUAADBLAgsAAKCJwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaCCwAAIAmAgsAAKCJwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaCCwAAIAmAgsAAKCJwAIAAGgisAAAAJoILAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgicACAABoIrAAAACaCCwAAIAmAgsAAKCJwAIAAGiyr/G3qvG3AAAAFo4VLAAAgCYCCwAAoInAAgAAaCKwAAAAmggsAACAJgILAACgyf8DcdDG+sxWT4wAAAAASUVORK5CYII=\n", "text/plain": [ "