Results
- class trueq.Results(value=None, n_measurements=None, dim=None)
A dictionary-like class which keeps track of measurement results for a single circuit that was run on quantum hardware or a simulator. This is stored as a map from bitstrings to corresponding counts.
Here are some examples:
from trueq import Results # here we have 3 total measurements in the circuit, meaning we # have to have a bitstring of length 3 res = Results() res["000"] = 1000 res["100"] = 5 # unary addition with another (compatible) Results-like object is supported res += {"100": 5, "110": 100} res
Results({'000': 1000, '100': 10, '110': 100})
from trueq import Results # here we have 2 total measurements in the circuit res = Results(n_measurements=2) # we can also index this using the decimal representation of the number # so in the case of 2 total measurements, we can index, 0, 1, 2, 3. # This only works if either measurements are already present, or # n_measurements is specified on instantiation. res[0] = 30 res[3] += 30 res
Results({'00': 30, '11': 30})
Note
This class works for ditstrings as well as bitstrings, for \(d<10\). However, for simplicity, this documentation refers to bitstrings throughout.
Dimension can be specified at instantiation, but this dimension will be silently increased if bitstrings with larger numbers get inserted. For example, putting a bitstring of
'002'
in will mean the dimension will be at least 3, even if it was only 2 previously.- Parameters:
value (
dict
-like) – A dictionary-like object containing bitstrings and counts, see above examples.n_measurements (
int
|NoneType
) – The number of measurements present. This is inferred ifNone
provided. If not provided, the first measurement added sets this value. Using integer-based indexing of the results will not work unless either this number is specified, or has been inferred through another means.dim (
int
|NoneType
) – The dimension of each subsystem. This number will automatically increase if bitstrings with larger values are used. The default valueNone
will result in the dimension ofvalue
if it exists.
- Raises:
ValueError – If the
dim
value provided is greater than 9.
- get(bitstring)
Gets the value associated with a given bitstring, if none is found, returns 0.
Bitstrings must match the number of measurements in the Results, this can be checked with
n_measurements
- Parameters:
bitstring (
int
|str
) – A valid bitstring orint
, such as'101'
or5
.- Return type:
int
- reset()
Resets all counts to 0, but leaves measurement positions intact. Resets
last_modified
toNone
.
- property last_modified
The timestamp of the last modification to these results, which is automatically set whenever the results are modified. The automatically set value is the current UTC time in the ISO string format. This timestamp can also be set manually, in which case any subsequent changes to the results will not cause this timestamp to automatically change. Results that have never had counts added will have a value of
None
.from trueq import Results r = Results() print(r.last_modified is None) # automatically update the timestamp r["00"] = 5 print(r.last_modified) # manually update the timestamp r.last_modified = "2019-10-02T00:00:00.0" print(r.last_modified) # we've adjusted it manually, so it is no longer automatically updated r["01"] = 2 print(r.last_modified)
True 2022-12-20T22:59:47.399985 2019-10-02T00:00:00.0 2019-10-02T00:00:00.0
- Type:
str
|NoneType
- property dim
The system dimension; 2 for qubits, 3 for qutrits, etc.
- Type:
int
- property vec
The vector representation of the results.
from trueq import Results res = Results(n_measurements=2) res[0] = 10 res[3] = 30 res.vec
array([10, 0, 0, 30])
- Type:
numpy.ndarray
- Raises:
ValueError – If the
n_measurements
exceeds theMAX_VEC_SYS
value, which can be manually adjusted.
- property n_measurements
The total number of measurements, i.e. the length of each bitstring.
- Type:
int
- property n_shots
The total number of shots present.
This is useful in the normalization of the results.
from trueq import Results res = Results(n_measurements=2) res[0] = 10 res[3] = 30 res.n_shots
40
- Type:
int
- normalized(total=1, clip_to_zero=True)
Returns a new
Results
object obtained by normalizing the values of these results to have a sum equal tototal
, which is1
by default. By default, the method clips negative values to zero before the normalization.from trueq import Results res = Results({"0110": 15, "0111": 40, "1101": 50, "1111": -5}) print(res.normalized())
Results({'0110': 0.14285714285714288, '0111': 0.380952380952381, '1101': 0.4761904761904762})
- Parameters:
total (
float
|int
) – The sum of values in the returned results.clip_to_zero (
bool
) – IfTrue
, removes negative-valued outcome counts before the normalization.
- Return type:
- Raises:
RuntimeError – If these results have values that sum to zero, which includes the case of no results.
- sort(by_value=True)
Sorts the keys of the results in-place, either by value or by bitstring.
- Parameters:
by_value (
bool
) – If this is true, sort the results with the largest number of counts at the beginning of the results. If this is false, sort the results by the bitstring value, i.e.'00'
,'01'
,'10'
,'11'
.
- marginal(positions)
Returns a new
Results
object obtained by marginalizing this results object over the specified positions. The order of positions is respected, so this method can also be used to create a copy with reordered bitstrings.from trueq import Results res = Results({"0110": 15, "0111": 35}) print(res.marginal([1, 2])) print(res.marginal([0, 3]))
Results({'11': 50}) Results({'00': 15, '01': 35})
- Parameters:
positions (
Iterable
) – A list of bitstring position indexes to keep in the new results object.- Return type:
- from_vec(vec)
Sets the bitstring counts from a given vector having a length which is a power of the system dimension.
from trueq import Results Results().from_vec([0.5, 0, 0, 0.5])
Results({'00': 0.5, '11': 0.5})
- Parameters:
vec (
Iterable
) – A vector whose length is a power of the system dimension.- Returns:
This instance.
- Return type:
- Raises:
ValueError – If the Results has a dimension of
None
and the dimension discerned from the length ofvec
is greater than 9.ValueError – If the
vec
length is incompatible with the number of measurements.
- subsample(n_shots, copy=True)
Randomly chooses a subset of shots. This is done uniformly and without replacement.
- Parameters:
n_shots (
int
) – The number of shots to subsample, must be no bigger than the number of shots present.copy (
bool
) – IfTrue
, a new copy of the results will be made and returned, otherwise, the operation will happen in-place.
- Return type:
- Raises:
ValueError – If the number of shots to subsample exceeds the number of shots.
TypeError – If the Results values are not integers.
- tvd(other, multinomial=None, n_samples=2000)
Computes the total variational distance (TVD) between these results and another
Results
object. Each result object is treated as an empirical probability distribution by dividing each value by the sum of all values. The TVD between two probabality vectors \(p\) and \(q\) is given by\[\operatorname{tvd}(p,q) = \sum_i |p_i - q_i| / 2.\]from trueq import Results r1 = Results({"00": 50, "11": 50}) r2 = Results({"00": 498, "11": 492, "01": 1, "10": 9}) print(r1.tvd(r2)) # here, both results sum to 1 so there is assumed to be no multinomial # sampling error, and hence the variance is reported at 0. r1 = Results({"00": 0.5, "11": 0.5}) r2 = Results({"00": 0.498, "11": 0.492, "01": 0.001, "10": 0.009}) print(r1.tvd(r2))
(0.010000000000000004, 0.030790169589288485) (0.010000000000000004, 0.0)
If one or both of these results are assumed to come from a multinomial distribution sampled from some true underlying probabilties (which will in practice usually be the (noisy) measurement probabilities of some final state of a quantum circuit), then the TVD itself is also a random variable. This function reports an estimate of the variance of the TVD, along with the TVD itself described above, as follows.
First, this result object and/or the
other
result object are determined to be multinomial samples ifn_shots
is different than 1. However, this can be overridden by the argumentmultinomial
.To estimate the variance of the TVD, for this result object and/or the
other
result object (whichever are classified as multinomial by the above criteria),n_samples
samples are drawn from the posterior distribution\[\operatorname{Dirichlet}(x)\]where \(x\) is a vector of result counts, and where the prior distribution in this model is the non-informative improper prior \(\operatorname{Dirichlet}(0)\). The standard deviation returned by this method is the standard deviation of the TVD of these samples with the samples or values of the
other
results object.- Parameters:
other (
Results
) – Another results object to compute the TVD with.multinomial (
Iterable
) – A pair of boolean values(self_multi, other_multi)
which specify whether these results and/or the other results should be treated as multinomial samples, thereby contributing to estimated standard deviation. The default value ofNone
results in the behaviour explained above.n_samples (
int
) – The number of Dirichlet samples to use in the estimation ofstd
.
- Returns:
A pair
(tvd, std)
wheretvd
is the TVD andstd
is an estimate of its standard deviation.- Return type:
tuple
- Raises:
ValueError – If the result objects are not compatible with one another, where either the dimension or number of measurements do not match.
- decompiled_results(weyls)
Returns a new results object where the outcomes are flipped according to the inverse of the given
weyls
. That is, we assume the existing results resulted from a circuit where the givenweyls
were compiled into the circuit before every measurement operator, and this function returns the results as if they were not there.In the case of qubits, this just means flipping the bit whenever an \(X\) or \(Y\) Pauli was compiled before a measurement.
import trueq as tq # make a simple circuit with random gates on each qubit followed by # measurements circuit = tq.Circuit( {0: tq.Gate.random(2), 1: tq.Gate.random(2), 2: tq.Gate.random(2)} ) circuit.measure_all() # compile this circuit with random Pauli operators compiler = tq.Compiler([tq.compilation.CompilePaulis()]) compiled_circuit = compiler.compile(circuit) print( "The circuit was compiled with the following operations:", compiled_circuit.key, ) # create a simulator to compare the original, compiled, and decompiled # results sim = tq.Simulator() original_result = sim.sample(circuit, n_shots=float("inf")) compiled_result = sim.sample(compiled_circuit, n_shots=float("inf")) # decompile the result from the compiled circuit decompiled_result = compiled_result.decompiled_results( compiled_circuit.key.compiled_pauli ) tq.visualization.plot_results( original_result, compiled_result, decompiled_result, labels=["Original", "Compiled", "Decompiled"], )
The circuit was compiled with the following operations: Key(compiled_pauli=Weyls('IIX'))
- Parameters:
weyls (
Weyls
) – A multi-qudit Weyl operator.- Return type:
- Raises:
ValueError – If the dimension of these results and the provided
weyls
do not match.ValueError – If the input
weyls.n_sys
does not matchn_measurements
of these results.ValueError – If the provided
weyls
has more than one row.
- to_dict()
Returns a dictionary representation of a
Results
object. This dictionary representation contains only base Python classes.- Return type:
dict
- plot(sparse_cutoff=True, axis=None, error_bars=False)
Plots a bar graph of these results. See also
plot_results()
for plotting severalResults
together, and for additional details.import trueq as tq # make a random vector of integers and instantiate new Results vec = tq.settings.get_rng().integers(0, 100, 32) tq.Results().from_vec(vec).plot()
- Parameters:
sparse_cutoff (
bool
|float
) – Optional cutoff for plotting small values. By default this isTrue
, and will not plot values bigger than \(1/s^2\), where \(s\) is the number of possible ditstrings for \(s>256\). IfFalse
, no cutoff is applied. The cutoff can be set manually by inputing a float.axis (
matplotlib.Axis
|NoneType
) – An existing axis to plot on, orNone
to create a new one.error_bars (
bool
) – Whether or not to plot shot-noise errorbars.