We will be using PennyLane along with the aformentioned\n ``qsim`` simulator running via our `PennyLane-Cirq plugin\n

- 1,\\end{align}\n\nwhere $n$ is the number of qubits, $P(x_i)$ is the\nprobability of bitstring $x_i$ computed for the ideal quantum\ncircuit, and the average is over the observed bitstrings.\n\nThe idea behind using this fidelity is that it will be close to 1 for\nsamples obtained from random quantum circuits, such as the one we defined\nabove, and close to zero for a uniform probability distribution, which\ncan be effectively sampled from classically. Sampling a bitstring from a\nrandom quantum circuit would follow the distribution\n\n\\begin{align}Pr(p) = (N - 1)(1- p)^{N-2},\\end{align}\n\nwhere $N = 2^n$ is the number of possible bitstrings [#Boixo2018]_.\nThis distribution is approximated well by the Porter-Thomas distribution,\ngiven by $Pr(p) = Ne^{-Np}$, a characteristic property of chaotic quantum\nsystems. From this we can then calculate the expectation value\n$\\left

$ as follows:\n\n\\begin{align}\\left

= \\int_0^1 p^2 N (N-1)(1-p)^{N-2}dp = \\frac{2}{N+1},\\end{align}\n\nwhich leads to the theoretical fidelity\n\n\\begin{align}F_{XEB} = 2^{n}\\left

- 1 = \\frac{2N}{N+1} - 1.\\end{align}\n\nWe implement this fidelity as the function below, where ``samples`` is a\nlist of sampled bitstrings, and ``probs`` is a list with corresponding\nsampling probabilities for the same noiseless circuit.\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def fidelity_xeb(samples, probs):\n sampled_probs = []\n for bitstring in samples:\n # convert each bitstring into an integer\n bitstring_idx = int(bitstring, 2)\n\n # retrieve the corresponding probability for the bitstring\n sampled_probs.append(probs[bitstring_idx])\n\n return 2 ** len(samples[0]) * np.mean(sampled_probs) - 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We set a random seed and use it to calculate the probability for all the\npossible bitstrings. It is then possible to sample from exactly the same\ncircuit by using the same seed. Before calculating the cross-entropy\nbenchmarking fidelity, the Pauli-Z samples need to be converted into\ntheir correponding bitstrings, since we need the samples to be in the\ncomputational basis.\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "seed = np.random.randint(0, 42424242)\nprobs = circuit(seed=seed, return_probs=True)\n\n# transpose the samples to get the shape (shots, wires)\ncircuit_samples = circuit(seed=seed).T\n\n# take the eigenvalues and transform -1 to 1 and 1 to 0\nbitstring_samples = []\nfor sam in circuit_samples:\n bitstring_sample = -(sam - 1) // 2\n bitstring_samples.append(\"\".join(str(bs) for bs in bitstring_sample))\n\nf_circuit = fidelity_xeb(bitstring_samples, probs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, we can sample random bitstrings from a uniform probability\ndistribution by generating all basis states, along with their\ncorresponding bitstrings, and sample directly from them using NumPy.\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "basis_states = dev.generate_basis_states(wires)\nrandom_integers = np.random.randint(0, len(basis_states), size=shots)\nbitstring_samples = []\nfor i in random_integers:\n bitstring_samples.append(\"\".join(str(bs) for bs in basis_states[i]))\n\nf_uniform = fidelity_xeb(bitstring_samples, probs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, let's compare the two different values. Sampling from the\ncircuit's probability distribution should give a fidelity close to 1,\nwhile sampling from a uniform distribution should give a fidelity\nclose to 0.\n\n

The cross-entropy benchmarking fidelity may output\n values that are negative or that are larger than 1, for any finite\n number of samples. This is due to the random nature of the sampling.\n For an infinite amount of samples, or circuit runs, the observed\n values will tend towards the theoretical ones, and will then always\n lie in the 0-to-1 interval.

The following mean fidelity calculations can be interesting to play\n around with. You can change the qubit grid at the top of this demo\n using, e.g., 8 or 4 qubits; change the number of shots used; as well\n as the number of circuit evaluations below. Running the following code\n snippet, the mean fidelity should still tend towards the theoretical\n value (which will be lower for fewer qubits).

For more reading on this, the original paper [#Arute2019]_ is highly\n recommended (along with the suplementary information [#Arute2019sup]_ if you want\n to dive deeper into the math and physics of the experiment). The blog\n post in [#Sohaib2019]_, along with the accompanying GitHub repo, also provides\n a nice introduction to the cross-entropy benchmarking fidelity, and\n includes calculations highlighting the effects of added noise models.