# # Copyright 2021 Quantum Benchmark Inc. # """ Streamlined Randomized Benchmarking (SRB) ========================================= """ #%% # These examples demonstrate how to use SRB, the most basic QCVV protocol, to estimate # the process infidelity of a single-qubit or two-qubit gateset [#f1]_ . See # :tqdoc:`SRB` for more information about this protocol. #%% # Hello World # ----------- # The simplest thing we can do is run SRB on a single-qubit. Qubits are labeled by # non-negative integers, and here we target qubit 0. We choose to use sequence lengths # ``[4, 30, 50]``, and, by default, 30 random circuits are generated per sequence # length. Below, we display the first circuit where the 4 random Clifford gates can be # seen, plus a fifth gate which is the product of their inverses and a random Pauli # matrix, where the additional Pauli matrix is introduced to make the fit more robust. # The empty cycles with non-zero markers delimit the random Cliffords, and more # generally, in other protocols like :meth:`~trueq.make_cb()` and # :meth:`~trueq.make_irb()`, contain interleaved cycles of interest. import trueq as tq import trueq.simulation as tqs circuits = tq.make_srb(0, [4, 30, 50]) circuits[0] #%% # Next, we simulate each of the circuits with the built-in simulator. We can see the # bitstring counts of the first circuit below. tq.Simulator().add_overrotation(0.04).add_depolarizing(0.01).run(circuits) circuits[0].results.plot() #%% # It is important to look at the decay curve to verify that appropriate sequence lengths # were chosen. The most informative sequence length to measure occurs around # :math:`m=1/(1-p)` where the decay is :math:`y=Ap^m`. If :math:`A\approx 1` and # :math:`p\approx 1`, then this corresponds to :math:`y\approx1/e\approx0.37`. # Therefore, in this example, our sequence lengths are a bit too short, and ``[4, 70]`` # would, in retrospect, have been more appropriate. circuits.plot.raw() #%% # Finally, we can look at the values of the estimate: circuits.fit() #%% # Isolated vs. Simultaneous SRB # ----------------------------- # %% # The core function :meth:`~trueq.make_srb()` generates a circuit collection that # implements simultaneous SRB, where gates are placed on all specified qubits of a # quantum device within each circuit. Different random gates are independently chosen # during each cycle. Qubit labels are entered into its first argument and specify which # qubits are acted on simultaneously. You can choose which qubits get single-qubit # twirls, and which qubits get two-qubit twirls, by nesting the qubit labels # appropriately; see the following examples. # simultaneous single-qubit RB on qubits 0 and 1; 60 circuits total circuits = tq.make_srb([0, 1], [4, 100], 30) # isolated single-qubit RB on qubits 0 and 1; 120 circuits total circuits = tq.make_srb([0], [4, 100], 30).append(tq.make_srb([1], [4, 100], 30)) # two-qubit RB on 0 and 1, simultaneous with single-qubit RB on 2; 60 circuits total circuits = tq.make_srb([[0, 1], 2], [4, 100], 30) #%% # The invocations above all use two sequence lengths (the number of random gate cycles # in a circuit), 4 and 100, and produce 30 random circuits per sequence length. Note # that specifying qubit labels ``[0, 1, 2]`` is a convenient short-hand for # ``[[0], [1], [2]]``. This imples, for example, that ``[[0,1]]`` has a much different # meaning than ``[0,1]``. #%% # Isolated SRB # ------------ # In isolated SRB, we characterize qubits or qubit-pairs one at a time with disjoint # experiments. This is done to assess gate quality on a small subregister in the context # of an idling environment. In this example, we build a single circuit collection to # estimate the average gate infidelity of three pairs on a 6 qubit device. # generate a circuit collection circuits = tq.CircuitCollection() for pair in [[0, 1], [2, 3], [4, 5]]: circuits += tq.make_srb([pair], [4, 10, 20]) #%% # Next, we transpile the circuits into native gates and simulate them. # Initialize a simulator sim = tq.Simulator().add_overrotation(0.01, 0.02) sim.add_stochastic_pauli(px=0.01, match=tqs.LabelMatch((0, 1))) sim.add_stochastic_pauli(pz=0.005, match=tqs.LabelMatch((2, 3, 4, 5))) # initialize a transpiler into native gates (U3 and CNOT) cnot_factory = tq.config.GateFactory.from_matrix("cnot", tq.Gate.cnot.mat) config = tq.Config(factories=[tq.config.u3_factory, cnot_factory]) t = tq.Compiler.from_config(config) # transpile and simulate circuits = t.compile(circuits) sim.run(circuits) #%% # Plot the results. circuits.plot.raw([[0, 1], [2, 3], [4, 5]]) circuits.plot.compare_rb() #%% # Simultaneous SRB # ---------------- # In simultaneous SRB, we characterize qubits or qubit-pairs in a single set of # experiments. This captures crosstalk between these subsystems which is usually not # present when other qubits are idle. In this example, we add more circuits to the # circuits from the previous section. # add simultaneous circuits to our old collection simul_circuits = t.compile(tq.make_srb([[0, 1], [2, 3], [4, 5]], [4, 10, 20])) circuits += simul_circuits sim.run(simul_circuits) # plot the results circuits.plot.raw([[0, 1], [2, 3], [4, 5]]) circuits.plot.compare_rb() #%% # Changing the Twirling Group Gateset # ----------------------------------- # The above examples characterize the average gate fidelity of the Clifford group, which # is historically by far the most common group to use. However, any unitary 2-design # will suffice, and so for SRB, the other notable choice is the set of all unitaries # sampled using the Haar measure. We make this alternate choice below. su_circuits = tq.make_srb([[0, 1], [2, 3], [4, 5]], [4, 6, 12], twirl="U") su_circuits = t.compile(su_circuits) sim.run(su_circuits) #%% # Our native gates here are still CNOT and U3. It takes on average 1.5 CNOTs to # synthesize a two-qubit Clifford gate, but it almost always takes 3 CNOTs to # synthesize a Haar-random two-qubit unitary group. Thus we see higher error rates # than in the section above. #%% # Simulate and plot the results. su_circuits.plot.raw([[0, 1], [2, 3], [4, 5]]) su_circuits.plot.compare_rb() #%% # .. rubric:: Footnotes # # .. [#f1] "Streamlined" refers to the method we use to eliminate the constant offset # (the :math:`B` in :math:`Ap^m+B`) that typically appears in standard RB.