ClassicalShadow_Application_EN.ipynb 30.2 KB
Notebook
Newer Older
Q
Quleaf 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4f560a55",
   "metadata": {},
   "source": [
    "# Estimation of Quantum State Properties Based on the Classical Shadow"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a894cbba",
   "metadata": {},
   "source": [
    "<em> Copyright (c) 2021 Institute for Quantum Computing, Baidu Inc. All Rights Reserved. </em>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4cdaa03",
   "metadata": {},
   "source": [
    "## Overview"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b3ea48a",
   "metadata": {},
   "source": [
    "In [The Classical Shadow of Unknown Quantum States](./ClassicalShadow_Intro_EN.ipynb), we introduced some theoretical knowledge of the classical shadow and showed how to construct the classical shadow of a quantum state $\\rho$. According to the theoretical derivation of the classical shadow, it is very suitable for estimating the linear properties of quantum states. At present, its basic applications are: quantum fidelity estimation, entanglement verification, the estimation of local observables' expectation values, and the estimation of global observables' expectation values [1]. Among them, the estimation of observables' expectation values widely appears in current quantum algorithms, such as [Variational Quantum Eigensolver](./VQE_EN.ipynb)  (VQE) for estimating the ground state energy of complex molecular Hamiltonian. Next, we will focus on the estimation of observables' expectation values algorithms based on the classical shadow and show how to use the shadow function in Paddle Quantum to do so."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db8b954f",
   "metadata": {},
   "source": [
    "## Estimation of Observables' Expectation Values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2869ea5",
   "metadata": {},
   "source": [
    "### Problem description"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6adea50",
   "metadata": {},
   "source": [
    "In the field of quantum chemistry, one of the core tasks is to solve for ground state energy of the Hamiltonian $\\hat{H}$ on a closed physical system on a quantum scale and its corresponding ground state. The main method is to prepare a parameterized trial wave function $|\\Psi(\\theta)\\rangle$. Then, the parameter $\\theta$ is being continuously adjusted and optimized to minimize the expected value $\\langle\\Psi(\\theta)|\\hat{H}| \\Psi(\\theta)\\rangle$ using classical optimization algorithms (such as gradient descent method). The principle of this scheme is based on Rayleigh-Ritz variational principle,\n",
    "\n",
    "$$\n",
    "E_{0}=\\min _{\\theta}\\langle\\Psi(\\theta)|\\hat{H}| \\Psi(\\theta)\\rangle \\tag{1}\n",
    "$$\n",
    "\n",
    "where $E_{0}$ represents the ground state energy of the system. Numerically, the problem can be understood as solving for the minimum eigenvalue $\\lambda_{\\min }$ of a discretized Hamiltonian $\\hat{H}$ (Hermitian matrix) and its corresponding eigenvector $\\left|\\Psi_{0}\\right\\rangle$. Where the classical shadow comes into play is to calculate the $\\langle\\Psi(\\theta)|\\hat{H}| \\Psi(\\theta)\\rangle = \\operatorname{tr}(\\hat{H}\\rho )$ part in every optimization iteration where $ \\rho = | \\Psi(\\theta)\\rangle\\langle\\Psi(\\theta)|$.\n",
    "\n",
    "The problem is then transformed into: for a quantum state $\\rho$ of $n$ qubits and an observable (Hamiltonian) $\\hat{H}$ that can be written as a linear combination of a set of Pauli operators $\\{I, X, Y, Z\\}^{\\otimes n}$,\n",
    "\n",
    "$$\n",
    "\\hat{H}=\\sum_{Q \\in\\{I, X, Y, Z\\} ^{\\otimes n}} \\alpha_{Q} Q \\quad \\text{where} \\quad \\alpha_{Q} \\in \\mathbb{R}, \\tag{2}\n",
    "$$\n",
    "\n",
    "how to estimate the observable's expectation value $\\operatorname{tr}(\\hat{H}\\rho )$ using the classical shadow?\n",
    "\n",
    "\n",
    "The most intuitive method is to use each term of the Hamiltonian as a measurement base, and the corresponding Pauli measurements are made for the quantum state $\\rho$. A certain number of repetitions are made for each of the measurements, and then the measurement results are being processed to obtain the estimation value. Here we refer to this method as the item-by-item measurement method.\n",
    "\n",
    "Readers can see that when both $n$ and the number of terms of the Hamiltonian $\\hat{H}$ are small, we can get $\\operatorname{tr}(\\hat{H}\\rho )$ through the item-by-item measurement method. However, when $n$ is large and the number of terms of $\\hat{H}$ increases, the cost of the this method will increase significantly. The classical-shadow-based method that will be introduced can obtain the same precision estimation of $\\operatorname{tr}(\\hat{H}\\rho )$ with less cost."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff8af318",
   "metadata": {},
   "source": [
    "### The improved algorithm based on the classical shadow"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3db3328",
   "metadata": {},
   "source": [
    "When constructing the classical shadow, a critical step is to uniformly and randomly sample the unitary transformation from the fixed set. In [The Classical Shadow of Unknown Quantum States](./ClassicalShadow_Intro_EN.ipynb), we showed the case when the selected set is the Clifford group. When the selected set is a Clifford group on single qubit, the process of sampling and measurement is equivalent to making Pauli measurements on the quantum states. The classical shadow algorithm using random Pauli measurements (CS) is provided in Paddle Quantum. Briefly, in the CS algorithm, we repeatedly choose a Pauli basis for each qubit uniformly at random to measure the quantum state $\\rho$ and estimate the observable's expectation value based on the measurement results, and the reader may refer to [1-2] to learn the details of the principle. Further, when the Pauli measurement base is no longer selected uniformly and randomly, improved algorithms are proposed [2-3]. Relevant algorithm functions are also provided in Paddle Quantum: Locally-biased classical shadows (LBCS) [2], Adaptive Pauli shadows (APS) [3]. Readers can refer to [1-3] to learn these algorithms in detail."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b772de7",
   "metadata": {},
   "source": [
    "## Paddle Quantum Implementation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e094e752",
   "metadata": {},
   "source": [
    "In Paddle Quantum, we provide the shadow function, which mainly includes two parts to use the above three algorithms to estimate the expectation value of the observable and obtain the classical shadow data of unknown quantum states. Next, we will show how to implement finding the ground state energy estimation of $H_{2}$ and $LiH$ based on the shadow function in Paddle Quantum."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "efc769b4",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Import all the dependencies\n",
    "import numpy as np\n",
    "from numpy import pi as PI\n",
    "import paddle\n",
    "from paddle_quantum.circuit import UAnsatz\n",
    "from paddle_quantum.VQE.chemistrysub import H2_generator\n",
    "from paddle_quantum.utils import Hamiltonian"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a58e8871",
   "metadata": {},
   "source": [
    "###  Estimate the ground state energy of hydrogen molecule  ($H_{ 2}$)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ebda5aeb",
   "metadata": {},
   "source": [
    "Import the Hamiltonian of $H_2$ with 4 qubits (for details, please refer to [Variational Quantum Eigensolver](./VQE_CN.ipynb) tutorial to obtain $H_2$ molecular Hamiltonian)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "70b8fdef",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "H2 hamiltonian =  -0.04207897647782277 I0\n",
      "0.17771287465139946 Z0\n",
      "0.1777128746513994 Z1\n",
      "-0.2427428051314046 Z2\n",
      "-0.24274280513140462 Z3\n",
      "0.17059738328801055 Z0, Z1\n",
      "0.04475014401535163 Y0, X1, X2, Y3\n",
      "-0.04475014401535163 Y0, Y1, X2, X3\n",
      "-0.04475014401535163 X0, X1, Y2, Y3\n",
      "0.04475014401535163 X0, Y1, Y2, X3\n",
      "0.12293305056183797 Z0, Z2\n",
      "0.1676831945771896 Z0, Z3\n",
      "0.1676831945771896 Z1, Z2\n",
      "0.12293305056183797 Z1, Z3\n",
      "0.1762764080431959 Z2, Z3\n"
     ]
    }
   ],
   "source": [
    "# Set up the Hamiltonian of hydrogen molecule\n",
    "H2_pauli_str, H2_qubit = H2_generator()\n",
    "# Construct a Hamiltonian class instance using the H2_pauli_str\n",
    "H2_hamiltonian = Hamiltonian(H2_pauli_str)\n",
    "print('H2 hamiltonian = ', H2_hamiltonian)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d6a6532",
   "metadata": {},
   "source": [
    "To show how to estimate the ground state energy using the classical-shadow-based algorithm, we first get the estimated ground state of $H_{2}$ using the VQE algorithm in Paddle Quantum."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1bc042c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "def U_theta(theta, hamiltonian, N, D):\n",
    "    \"\"\"\n",
    "    Quantum Neural Network\n",
    "    \"\"\"\n",
    "    \n",
    "    # Initialize the quantum neural network according to the number of qubits N\n",
    "    cir = UAnsatz(N)\n",
    "    \n",
    "    # Built-in {R_y + CNOT} circuit template\n",
    "    cir.real_entangled_layer(theta[:D], D)\n",
    "    \n",
    "    # Lay R_y gates in the last row\n",
    "    for i in range(N):\n",
    "        cir.ry(theta=theta[D][i][0], which_qubit=i)\n",
    "        \n",
    "    # The quantum neural network acts on the default initial state |0000>\n",
    "    cir.run_state_vector()\n",
    "    \n",
    "    # Calculate the expected value of a given Hamiltonian\n",
    "    expectation_val = cir.expecval(hamiltonian)\n",
    "\n",
    "    return expectation_val, cir\n",
    "\n",
    "class StateNet(paddle.nn.Layer):\n",
    "    \"\"\"\n",
    "    Construct the model net\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(self, shape, dtype=\"float64\"):\n",
    "        super(StateNet, self).__init__()\n",
    "        \n",
    "        # Assign the theta parameter list to be the trainable parameter list of the circuit\n",
    "        self.theta = self.create_parameter(shape=shape, \n",
    "                                           default_initializer=paddle.nn.initializer.Uniform(low=0.0, high=2*PI),\n",
    "                                           dtype=dtype, is_bias=False)\n",
    "        \n",
    "    # Define loss function and forward propagation mechanism\n",
    "    def forward(self, hamiltonian, N, D):\n",
    "        \n",
    "        # Calculate the loss function/expected value\n",
    "        loss, cir = U_theta(self.theta, hamiltonian, N, D)\n",
    "\n",
    "        return loss, cir"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c53b204",
   "metadata": {},
   "source": [
    "After building the quantum neural network of the VQE algorithm and defining the loss function, we can estimate the ground state energy of $H_{2}$ and the quantum circuit corresponding to the ground state by training the quantum neural network."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "40036ae4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iter: 20 loss: -1.0865\n",
      "iter: 20 Ground state energy: -1.0865 Ha\n",
      "iter: 40 loss: -1.1284\n",
      "iter: 40 Ground state energy: -1.1284 Ha\n",
      "iter: 60 loss: -1.1355\n",
      "iter: 60 Ground state energy: -1.1355 Ha\n",
      "iter: 80 loss: -1.1360\n",
      "iter: 80 Ground state energy: -1.1360 Ha\n",
      "\n",
      "The trained circuit:\n",
      "--Ry(-1.57)----*--------------x----Ry(4.713)----*--------------x----Ry(3.129)--\n",
      "               |              |                 |              |               \n",
      "--Ry(5.091)----x----*---------|----Ry(1.768)----x----*---------|----Ry(3.711)--\n",
      "                    |         |                      |         |               \n",
      "--Ry(0.997)---------x----*----|----Ry(4.696)---------x----*----|----Ry(3.180)--\n",
      "                         |    |                           |    |               \n",
      "--Ry(4.753)--------------x----*----Ry(4.701)--------------x----*----Ry(-0.37)--\n",
      "                                                                               \n"
     ]
    }
   ],
   "source": [
    "ITR = 80  # Set the number of optimization iterations\n",
    "LR = 0.4   # Set the learning rate\n",
    "D = 2      # Set the depth of the repetitive calculation module in QNN\n",
    "N = H2_hamiltonian.n_qubits \n",
    "\n",
    "# Determine the parameter dimension of the network\n",
    "net = StateNet(shape=[D + 1, N, 1])\n",
    "\n",
    "# Generally speaking, we use Adam optimizer to obtain relatively good convergence,\n",
    "# You can change it to SGD or RMS prop.\n",
    "opt = paddle.optimizer.Adam(learning_rate=LR, parameters=net.parameters())\n",
    "\n",
    "# Record optimization results\n",
    "summary_iter, summary_loss = [], []\n",
    "\n",
    "# Optimization loop\n",
    "for itr in range(1, ITR + 1):\n",
    "\n",
    "    # Forward propagation to calculate loss function\n",
    "    loss, cir = net(H2_hamiltonian, N, D)\n",
    "\n",
    "    # Use back propagation to minimize the loss function\n",
    "    loss.backward()\n",
    "    opt.minimize(loss)\n",
    "    opt.clear_grad()\n",
    "\n",
    "    # Record optimization results\n",
    "    summary_loss.append(loss.numpy())\n",
    "    summary_iter.append(itr)\n",
    "\n",
    "    # Print result\n",
    "    if itr % 20 == 0:\n",
    "        print(\"iter:\", itr, \"loss:\", \"%.4f\" % loss.numpy())\n",
    "        print(\"iter:\", itr, \"Ground state energy:\", \"%.4f Ha\" \n",
    "                                            % loss.numpy())\n",
    "    if itr == ITR:\n",
    "        print(\"\\nThe trained circuit:\") \n",
    "        print(cir)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6c9d23f",
   "metadata": {},
   "source": [
    "#### Introduction to shadow function"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9891733",
   "metadata": {},
   "source": [
    "At this point, we've obtained the quantum circuit for generating the ground state of $H_{2}$. We can run the `shadow_trace` function on this circuit to obtain the estimated ground state energy. In the `shadow_trace` function, our inputs are the Hamiltonian to be estimated, the number of samples, and the method selected. You can choose the sampling mode you want by specifying the parameter `method`.  Among them, CS has a broader application range and the fastest speed, but its estimation accuracy may be slightly poor; LBCS has a higher accuracy, but it runs slower as the number of terms of Hamiltonian grows; APS also has higher accuracy, but it runs slower when the number of qubits is large."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3b31e57d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "H2 ground state energy =  -1.1360331063822373\n",
      "H2 ground state energy CS=  -1.1652093330967717\n",
      "H2 ground state energy LBCS=  -1.1213982275348622\n",
      "H2 ground state energy APS=  -1.137720214050516\n"
     ]
    }
   ],
   "source": [
    "# The actual energy value corresponding to the estimated ground state\n",
    "H2_energy = cir.expecval(H2_hamiltonian).numpy()[0]\n",
    "\n",
    "# Sampling times\n",
    "sample = 1500 \n",
    "# Three algorithms are used to estimate the expectation value of observable.\n",
    "H2_energy_CS = cir.shadow_trace(H2_hamiltonian, sample, method=\"CS\")\n",
    "H2_energy_LBCS = cir.shadow_trace(H2_hamiltonian, sample, method=\"LBCS\")\n",
    "H2_energy_APS = cir.shadow_trace(H2_hamiltonian, sample, method=\"APS\")\n",
    "\n",
    "print('H2 ground state energy = ', H2_energy)\n",
    "print('H2 ground state energy CS= ', H2_energy_CS)\n",
    "print('H2 ground state energy LBCS= ', H2_energy_LBCS)\n",
    "print('H2 ground state energy APS= ', H2_energy_APS)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cb856da",
   "metadata": {},
   "source": [
    "Now let's use the item-by-item measurement method to estimate the ground state energy of $H_{2}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "3ae0242c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "H2 ground state energy traditional =  -1.1367622727687419\n"
     ]
    }
   ],
   "source": [
    "# Use the item-by-item measurement method to estimate the ground state energy\n",
    "H2_energy_traditional = cir.expecval(H2_hamiltonian, shots=100).numpy()[0]\n",
    "print('H2 ground state energy traditional = ', H2_energy_traditional)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2e1ad02",
   "metadata": {},
   "source": [
    "We can see that under 1500 samples, the estimated ground state energy by these three algorithms are very close to the energy of the estimated ground state by VQE. The item-by-item measurement method is to make 100 measurements for each item of the Hamiltonian, and there are 15 items of the Hamiltonian of $H_{2}$, which is equivalent to 1500 measurements in total. The difference between the obtained results and the energy of the estimated ground state by VQE is also tiny. In this small-scale situation, the classical-shadow-based algorithms do not show significant advantages over the item-by-item measurement method. But in large-scale qubits scenarios, this algorithm requires only constant-level growth of the number of the Hamiltonian terms. In contrast, the item-by-item measurement method or some other methods require polynomial-level or even exponential-level growth in the number of samples to get the same accuracy[1]. In fact, it is pointed out in [2] that for CS algorithm and LBCS algorithm, the average error of estimation $\\epsilon$, variance $\\operatorname {var}(\\nu)$ and number of samples $S$ are related as follows:\n",
    "\n",
    "$$\n",
    "S = O(\\epsilon^{-2} \\operatorname{var}(\\nu) ),  \\tag{3}\n",
    "$$\n",
    "\n",
    "where the variance $\\operatorname{var} (\\nu) $ is independent of the number of samples, and the variance is related to the number of terms of the Hamiltonian. Therefore, knowing our expected precision, we can calculate the required number of samples. Similarly, our average error can also be defined according to the number of samples: \n",
    "\n",
    "$$\n",
    "\\epsilon = \\sqrt{\\frac{\\operatorname{var}}{S}}.  \\tag{4}\n",
    "$$\n",
    "\n",
    "We can be seen that in the above experiments, the errors obtained by the CS algorithm and LBCS algorithm are within the accuracy of theoretical estimation in reference [3]."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e0e174d",
   "metadata": {},
   "source": [
    "At the same time, Paddle Quantum provides a sampling function ` shadow_sample`, which supports pre-sampling of unknown quantum states, which is convenient for readers to explore other applications of the classical shadow. The specific usage is as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "fc6b465c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('yxxz', '0010'), ('xyzx', '0101'), ('yxyx', '1010'), ('yxyy', '1101'), ('yyyx', '0101'), ('xyxz', '0010'), ('zyxz', '1010'), ('xzyz', '0110'), ('zzzx', '1101'), ('xyxx', '0101')]\n"
     ]
    }
   ],
   "source": [
    "from paddle_quantum.shadow import shadow_sample\n",
    "\n",
    "# Run the circuit in the vector form\n",
    "H2_rho = np.array(cir.run_state_vector())\n",
    "# Get the data of classical shadow and output it in the form of a list\n",
    "H2_sample_data_CS = shadow_sample(H2_rho, H2_qubit, sample_shots=10, mode='state_vector', \n",
    "                                  hamiltonian=H2_hamiltonian, method='CS')\n",
    "print(H2_sample_data_CS)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e74ffd18",
   "metadata": {},
   "source": [
    "###  Estimate the ground state energy of lithium hydride  ($LiH$)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4480cb6",
   "metadata": {},
   "source": [
    "Next, we consider the ground state energy of $LiH$. First, we load from a pre-computed file to generate the molecular Pauli Hamiltonian of $LiH$ with 12 qubits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6916c8a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('./LiH_hamiltonian.txt', 'r') as lih_file:\n",
    "    unprocessed_pauli_str = lih_file.read()\n",
    "    LiH_pauli_str = [term.split(maxsplit=1) for term in unprocessed_pauli_str.split('\\n')]\n",
    "    LiH_pauli_str = [[float(term[0]), term[1]] for term in LiH_pauli_str]\n",
    "    LiH_hamiltonian = Hamiltonian(LiH_pauli_str)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61d8fd59",
   "metadata": {},
   "source": [
    "Then, we also use the VQE algorithm to obtain the ground state energy and the ground state of $LiH $ and then use the classical shadow algorithm to estimate the ground state energy. Due to the large size of the $LiH$ molecular Hamiltonian, it will take a long time to train the VQE circuit. Hence we provide a set of pre-trained parameters, using which the users could test the classical-shadow-based algorithms on $LiH$ Hamiltonian directly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "fe326409",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The pre-trained VQE gets a ground state energy of: -7.7720 \n"
     ]
    }
   ],
   "source": [
    "# Load the pre-trained parameters\n",
    "pretrained_parameters = paddle.load('LiH_VQE_parameters.pdtensor')\n",
    "N = LiH_hamiltonian.n_qubits\n",
    "# Run the VQE circuit with pre-trained parameters\n",
    "energy, cir = U_theta(pretrained_parameters, LiH_hamiltonian, N, D)\n",
    "print('The pre-trained VQE gets a ground state energy of: %.4f ' % energy.numpy())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f8ff501",
   "metadata": {},
   "source": [
    "Once we have the circuit corresponding to the $LiH$ ground state, we can directly use the `shadow_trace` function to  perform random measurements. Also, since this molecular Hamiltonian has 631 terms, we specify `sample = 1262` for the function `shadow_trace` and `shots = 2` for the function `expecval` in order to ensure that the number of measurements is the same for both types of methods.\n",
    "\n",
    "Since ground state of $LiH$ has 12 qubits, there are $3^{12}$ possible combinations of measurements when doing different Pauli measurements on the ground state of $LiH$. So it is too random to just perform 1262 samples to get the valuation. Thus, we run each of the above four methods 20 times. Then take the mean of these 20 samples of data as the estimation for each algorithm, and calculate the sample variance for a simple comparison of the algorithms.(It may take at least 1 hour to run the following code blocks.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "8f3684ce",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LiH ground state energy =  -7.771980394176657\n",
      "ave LiH ground state energy CS =  -7.835791570579005\n",
      "ave LiH ground state energy LBCS =  -7.7622296662623445\n",
      "ave LiH ground state energy APS =  -7.762836542787509\n",
      "ave LiH ground state energy traditional =  -7.8964746269601465\n",
      "time =  4206.216086864471\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "begin = time.time()\n",
    "estimator_CS = []\n",
    "estimator_LBCS = []\n",
    "estimator_APS = []\n",
    "estimator_traditional = []\n",
    "\n",
    "# The actual energy value corresponding to the estimated ground state\n",
    "LiH_energy = cir.expecval(LiH_hamiltonian).numpy()[0]\n",
    "\n",
    "# Number of repetition times\n",
    "n = 20 \n",
    "\n",
    "for i in range(n):\n",
    "    LiH_energy_CS = cir.shadow_trace(LiH_hamiltonian, 1262, method=\"CS\")\n",
    "    LiH_energy_LBCS = cir.shadow_trace(LiH_hamiltonian, 1262, method=\"LBCS\")\n",
    "    LiH_energy_APS = cir.shadow_trace(LiH_hamiltonian, 1262, method=\"APS\")\n",
    "    LiH_energy_traditional = cir.expecval(LiH_hamiltonian, shots=2).numpy()[0]\n",
    "\n",
    "    estimator_CS.append(LiH_energy_CS) \n",
    "    estimator_LBCS.append(LiH_energy_LBCS) \n",
    "    estimator_APS.append(LiH_energy_APS) \n",
    "    estimator_traditional.append(LiH_energy_traditional) \n",
    "\n",
    "ave_LiH_energy_CS = np.mean(estimator_CS)\n",
    "ave_LiH_energy_LBCS = np.mean(estimator_LBCS)\n",
    "ave_LiH_energy_APS = np.mean(estimator_APS)\n",
    "ave_LiH_energy_traditional = np.mean(estimator_traditional)\n",
    "end = time.time() \n",
    "\n",
    "print(\"LiH ground state energy = \", LiH_energy)\n",
    "print(\"ave LiH ground state energy CS = \", ave_LiH_energy_CS)\n",
    "print(\"ave LiH ground state energy LBCS = \", ave_LiH_energy_LBCS)\n",
    "print(\"ave LiH ground state energy APS = \", ave_LiH_energy_APS)\n",
    "print('ave LiH ground state energy traditional = ', ave_LiH_energy_traditional)\n",
    "print('time = ', end-begin)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89469273",
   "metadata": {},
   "source": [
    "From the results, the mean values obtained by the classical-shadow-based algorithms are closer to the energy of the estimated ground state of $LiH$ by VQE than the item-by-item measurement method, and the errors of the algorithms are all within the accuracy of theoretical estimation in reference [3]. So, what about the sample variance of each algorithm?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "742f4f15",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LiH variance CS =  0.034596840359755784\n",
      "LiH variance LBCS =  0.016602696085670984\n",
      "LiH variance APS =  0.0016603026356630662\n",
      "LiH variance traditional =  0.13200055652163223\n"
     ]
    }
   ],
   "source": [
    "# Calculate the sample variance\n",
    "variance_CS = []\n",
    "variance_LBCS = []\n",
    "variance_APS = []\n",
    "variance_traditional = []\n",
    "\n",
    "for i in range(n):\n",
    "    variance_CS.append((estimator_CS[i] - ave_LiH_energy_CS) ** 2)\n",
    "    variance_LBCS.append((estimator_LBCS[i] - ave_LiH_energy_LBCS) ** 2)\n",
    "    variance_APS.append((estimator_APS[i] - ave_LiH_energy_APS) ** 2)\n",
    "    variance_traditional.append((estimator_traditional[i] - ave_LiH_energy_traditional) ** 2)\n",
    "\n",
    "var_CS = sum(variance_CS)/(n-1)\n",
    "var_LBCS = sum(variance_LBCS)/(n-1)\n",
    "var_APS = sum(variance_APS)/(n-1)\n",
    "var_traditional = sum(variance_traditional)/(n-1)\n",
    "\n",
    "print('LiH variance CS = ', var_CS)\n",
    "print('LiH variance LBCS = ', var_LBCS)\n",
    "print('LiH variance APS = ', var_APS)\n",
    "print('LiH variance traditional = ', var_traditional)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d40ef806",
   "metadata": {},
   "source": [
    "It can be seen that the APS algorithm has the lowest sample variance, followed by the LBCS algorithm, then the CS algorithm, and finally the item-by-item measurement method. Accordingly, we can find that after the increase in the number of terms of the Hamiltonian, the classical-shadow-based algorithm has higher accuracy and more stability at the same cost than the item-by-item measurement method. Among them, the APS algorithm is the most stable one.\n",
    "\n",
    "It is worth mentioning that for the classical shadow algorithm, the scene of 12 qubits still can not show its significant advantages compared with some existing algorithms. In large-scale systems with more qubits, its advantages can be better demonstrated [1]."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa9e6a26",
   "metadata": {},
   "source": [
    "## Conclusion"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "91a60770",
   "metadata": {},
   "source": [
    "This tutorial discusses how to use the classical-shadow-based algorithms to estimate the observable's expectation value and shows how to use the `shadow` function in Paddle Quantum. It can be seen that the improved algorithm based on the classical shadow can get a good estimate of the observable's expectation value. Compared with the item-by-item measurement method, when the number of samples are the same, its estimated value is more accurate and the algorithm is more stable. In the large-scale qubit scenario, the number of samples required by the classical shadow method is only a constant increase in some tasks. So its role in the NISQ (noisy intermediate-scale quantum) era will continue to be explored."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fcdcad3",
   "metadata": {},
   "source": [
    "_______\n",
    "\n",
    "## References"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "947b52db",
   "metadata": {},
   "source": [
    "[1] Huang, Hsin-yuan, R. Kueng and J. Preskill. “Predicting many properties of a quantum system from very few measurements.” [Nature Physics (2020): 1-8.](https://www.nature.com/articles/s41567-020-0932-7?proof=t)\n",
    "\n",
    "[2] Hadfield, Charles, et al. \"Measurements of quantum hamiltonians with locally-biased classical shadows.\" [arXiv preprint arXiv:2006.15788 (2020).](https://arxiv.org/abs/2006.15788)\n",
    "\n",
    "[3] Hadfield, Charles. \"Adaptive Pauli Shadows for Energy Estimation.\" [arXiv preprint arXiv:2105.12207 (2021).](https://arxiv.org/abs/2105.12207)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.7.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}