ObservableSplitter : turning observable sampling into qubit sampling
The purpose of the ObservableSplitter
Plugin is to transform jobs
consisting of a circuit
and an observable into a batch of jobs consisting of circuits and qubit sampling tasks.
Consider for instance the following observable:
Sampling such an observable at the end of a quantum circuit boils down to sampling each of its terms individually (assuming that we have a large enough number of samples). The expected value of each of these terms can, in turn, be computed by sampling the relevant set of qubits in the appropriate basis:
the term \(X_0 X_1\) will correspond to sampling qubits \(0\) and \(1\) in the \(XX\) basis
the term \(Z_1 Z_2\) will correspond to sampling qubits \(1\) and \(2\) in the \(ZZ\) basis
Hence, if a QPU does not natively support observable sampling, we can transform a pair \((\mathcal{C},\mathcal{O})\) into a collection of sampling jobs \((\mathcal{C_i},\mathcal{Q_i})\) where \(\mathcal{Q_i}\) is the support of term \(i\) of \(\mathcal{O}\).
ObservableSplitter
automatically splits jobs to perform this
transformation, and automatically aggregates the sampling data of the various circuits
in order to compute the final expected value of the observable.
Here is the detailed documentation of the ObservableSplitter plugin:
- class qat.plugins.ObservableSplitter(splitting_method='naive', x_basis_change=None, y_basis_change=None, **kwargs)
A QPU Plugin that splits jobs that require observable sampling into jobs that only require computational basis measurements.
- Parameters
splitting_method (optional, str) – Specify the splitting algorithm to be used (see below). Defaults to “naive”.
x_basis_change (optional, callable) – a (qbit, nbqbits) -> QRoutine method performing a Z -> X basis change on qubit qbit. The routine must have arity nbqbits. Defaults to a single H gate.
y_basis_change (optional, callable) – a (qbit, nbqbits) -> QRoutine method performing a Z -> Y basis change on qubit qbit. The routine must have arity nbqbits. Defaults to [PH(pi/2); RY(pi/2)].
The plugin implements three different approaches for the splitting:
“naive”: Each term of the observable will be individually sampled using an individual circuit.
“coloring”: Terms will be grouped into trivially commuting subsets using a greedy graph coloring method. Each group will be sampled using a single circuit.
“clifford”: Terms will grouped into (non-trivially) commuting subsets using a greedy graph coloring method. Each group is then co-diagonalized using a Clifford circuit and sampled. This method might add entangling gates to the circuit.
Notes
As of today, the “clifford” co-diagonalization method ignores the x_basis_change and y_basis_change arguments.
The three methods coincide when no two terms commute.
- classmethod addargs(parser)
Add arguments to a parser
- compile(batch, _specs)
Performs the splitting of all the jobs inside task.
- do_post_processing()
Returns True iff we need post-processing of the results
- get_fresh_key()
Returns a fresh key.
- post_process(batch_results)
Performs the post processing of results. Using meta_data to find the key we set up before and retrieve the list of observables we stored in self.keys.
- Returns
batch of results. Each result holds the estimated observable value in the “value” field. The value_data field is a dictionary which may contain an “err” key whose value corresponds to the estimated standard value on the mean.
- Return type