Collections and Metadata

Keys

True-Q™ uses Keys to store attributes of some objects like Circuits and Estimates. This is done to enable efficient filtering and grouping of similar or dissimilar objects. Keys are lightweight hashable dictionaries; their keys and values must both be hashable. One main feature of keys is their ability to match against patterns:

import trueq as tq

key = tq.Key(fruit="banana", color="yellow", quantity=1)

print(key.match(fruit="apple"))
print(key.match(fruit="banana"))
print(key.match(fruit="banana", color="brown"))
print(key.match(fruit={"apple", "pear"}))
print(key.match(fruit={"apple", "banana"}))
False
True
False
False
True

Various other conveniences are supported:

import trueq as tq

key = tq.Key(fruit="banana", color="yellow", quantity=1)

print("shape in key:   \t", "shape" in key)
print("quantity in key:\t", "quantity" in key)
print("key.quantity:   \t", key.quantity)
print("key['quantity']:\t", key["quantity"])
print("key.names:      \t", key.names)
shape in key:   	 False
quantity in key:	 True
key.quantity:   	 1
key['quantity']:	 1
key.names:      	 ('fruit', 'color', 'quantity')

Special Keywords

True-Q™ uses the following keywords for Keys internally. There are no restrictions on using these keywords for your own purposes, or manually overriding them, but doing so may cause fitting and other features to break.

Keyword

Description

analyze_decays

Stores CB decays to measure.

batch

Used by RCAL to index experiment batches.

compiled_pauli

Which Pauli was compiled into the circuit prior to measurement.

cycle

The cycle of interest, see e.g. CB.

labels

A tuple of qubit labels.

measurement_basis

Which basis change was compiled into the circuit.

n_bodies

Used by KNR to store the gate-body size to probe.

n_random_cycles

The number of independent random cycles in a circuit.

order

Sometimes used to order by key.

protocol

The protocol.

seq_label

Used to distinguish otherwise identical circuits.

targeted_errors

Non-identity errors targeted by CB.

twirl

Stores the twirling group.

Usually, fit() produces a separate set of estimates for every unique combination of custom keywords (i.e. not in the list above). The exceptions to this rule are the following custom keywords, which will be ignored and deleted in the returned fit object:

Keyword

Description

job_id

E.g. store a job execution identifier.

n_shots

E.g. store a recommended number of shots for a circuit.

KeySets

KeySets are special set-like containers for Keys. They exist (instead of using the built-in set) mainly to easily create subsets using the match() functionality of Keys. When we call the subset() of a KeySet, a new KeySet is returned that contains all Keys that match the arguments. KeySets are also useful for getting all unique values of a particular parameter:

import trueq as tq

keys = tq.KeySet(
    tq.Key(fruit="banana", color="yellow"),
    tq.Key(fruit="apple", color="red"),
    tq.Key(fruit="apple", color="yellow"),
    tq.Key(size=1),
)

print(keys.subset(color="yellow"))
print(keys.subset(size=1))
print(keys.subset(shape="triangle"))
print(keys.fruit)
KeySet(
	Key(fruit='banana', color='yellow'),
	Key(fruit='apple', color='yellow'))
KeySet(
	Key(size=1))
KeySet()
{'banana', 'apple'}

Circuit Collections

Collections of circuits are stored in instances of CircuitCollection, and this is the type that will be returned by the functions that create circuits for diagnostic tools. Circuit collections are iterable and maintain insertion order. Multiple protocols can (and usually should) put their circuits into the same collection, so that their results can be analyzed together if applicable. Different types of circuit are differentiated within the collection by their key attribute, and collections of circuits are designed so that accessing subsets of circuits by properties of these keys is convenient.

For example, circuits created with make_srb() have keys with the property protocol="SRB", while circuits created with make_xrb() have keys with the property protocol="XRB". We may access only those circuits with keys having the property protocol="SRB" as follows.

import trueq as tq

circuits = tq.make_srb([0], [4, 100])
circuits += tq.make_xrb([0], [4, 100])

# see all the values of protocol inside keys in the circuit collection
print(circuits.keys().protocol)

print(circuits.subset(protocol='SRB').keys().protocol)
{'SRB', 'XRB'}
{'SRB'}

See the API documentation for CircuitCollection for other key-related convenience functions.

Perhaps most importantly, circuit collections implement the method fit(), which takes all of the available data in the collection and analyzes it, returning an EstimateCollection (see below). Similarly, plot() contains methods to visualize the results of any analysis.

Accessing Estimates and Circuits

Both CircuitCollections and EstimateCollections use flat structures to enable a simple interface between different protocols performed on various subsets of the same quantum device. For example, the parameters estimated by the XRB protocol depend on whether the SRB protocol was also performed, and the make_crosstalk_diagnostics() macro generates SRB circuits in both simultaneous and isolated modes.

This guide supplements the API documentation for methods in the CircuitCollection and EstimateCollection classes. Skip ahead to Circuit Collection Internals or EstimateCollection Internals if you want to skip to useful examples immediately. Throughout this page, we will use the following objects in our examples:

import trueq as tq

# make some varied circuits in a single CircuitCollection
circuits = tq.CircuitCollection()
circuits += tq.make_srb([4, 5], [4, 16])
circuits += tq.make_srb([4], [4, 16])
circuits += tq.make_xrb([4], [4, 12])
circuits += tq.make_cb({(5, 4): tq.Gate.cnot}, [4, 12], n_decays=5)

# simulate them and put their results in an EstimateCollection
tq.Simulator().add_stochastic_pauli(px=0.05).run(circuits)
fit = circuits.fit([4, 5])

Circuit Collection Internals

CircuitCollections are effectively lists of circuits with convenience functions, where each circuit contains its own Key. The protocols built in to True-Q™ typically assign keys such that each key only appears once per generated circuit collection.

If you happen to know the key of the list of circuits you are interested in, you can access it directly.

key = tq.Key(n_random_cycles=4,
             protocol='SRB',
             twirl=(('C', 4),),
             compiled_pauli="I")
len(circuits[key])
0

However, it is typically inconvenient to manually construct keys in this way. There are four convenience functions for accessing circuits:

  1. trueq.CircuitCollection.keys():

    Returns a KeySet of all keys in the collection that match a given filter.

    For example, let’s get all of the keys in the collection that came from the SRB protocol:

    circuits.keys(protocol="SRB")
    
    True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
    KeySet
    List of all the keys in the KeySet
    protocol
    The characterization protocol used to generate a circuit.
    twirl
    The twirling groups used on each subsystem.
    n_random_cycles
    The number of independent random cycles in the circuit.
    compiled_pauli
    The n-qubit Pauli operator that was compiled into the circuit immediately before measurement.
    Key
    Key:
    • compiled_pauli: II
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 II
    Key
    Key:
    • compiled_pauli: IX
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 IX
    Key
    Key:
    • compiled_pauli: IY
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 IY
    Key
    Key:
    • compiled_pauli: IZ
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 IZ
    Key
    Key:
    • compiled_pauli: XI
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 XI
    Key
    Key:
    • compiled_pauli: XX
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 XX
    Key
    Key:
    • compiled_pauli: XY
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 XY
    Key
    Key:
    • compiled_pauli: XZ
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 XZ
    Key
    Key:
    • compiled_pauli: YI
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 YI
    Key
    Key:
    • compiled_pauli: YX
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 YX
    Key
    Key:
    • compiled_pauli: YY
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 YY
    Key
    Key:
    • compiled_pauli: YZ
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 YZ
    Key
    Key:
    • compiled_pauli: ZI
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 ZI
    Key
    Key:
    • compiled_pauli: ZX
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 ZX
    Key
    Key:
    • compiled_pauli: ZY
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 ZY
    Key
    Key:
    • compiled_pauli: ZZ
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 4 ZZ
    Key
    Key:
    • compiled_pauli: II
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 II
    Key
    Key:
    • compiled_pauli: IX
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 IX
    Key
    Key:
    • compiled_pauli: IY
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 IY
    Key
    Key:
    • compiled_pauli: IZ
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 IZ
    Key
    Key:
    • compiled_pauli: XI
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 XI
    Key
    Key:
    • compiled_pauli: XX
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 XX
    Key
    Key:
    • compiled_pauli: YI
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 YI
    Key
    Key:
    • compiled_pauli: YX
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 YX
    Key
    Key:
    • compiled_pauli: ZI
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 ZI
    Key
    Key:
    • compiled_pauli: ZX
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 ZX
    Key
    Key:
    • compiled_pauli: ZY
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 ZY
    Key
    Key:
    • compiled_pauli: ZZ
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4, 5]
    SRB Cliffords on [4, 5] 16 ZZ
    Key
    Key:
    • compiled_pauli: I
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4]
    SRB Cliffords on [4] 4 I
    Key
    Key:
    • compiled_pauli: X
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4]
    SRB Cliffords on [4] 4 X
    Key
    Key:
    • compiled_pauli: Y
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4]
    SRB Cliffords on [4] 4 Y
    Key
    Key:
    • compiled_pauli: Z
    • n_random_cycles: 4
    • protocol: SRB
    • twirl: Cliffords on [4]
    SRB Cliffords on [4] 4 Z
    Key
    Key:
    • compiled_pauli: I
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4]
    SRB Cliffords on [4] 16 I
    Key
    Key:
    • compiled_pauli: X
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4]
    SRB Cliffords on [4] 16 X
    Key
    Key:
    • compiled_pauli: Y
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4]
    SRB Cliffords on [4] 16 Y
    Key
    Key:
    • compiled_pauli: Z
    • n_random_cycles: 16
    • protocol: SRB
    • twirl: Cliffords on [4]
    SRB Cliffords on [4] 16 Z

    Since the keys() method returns a new KeySet, we have access to all of its methods. For example, this is the easiest way to get all sequence lengths present in the SRB protocol:

    circuits.keys(protocol="SRB").n_random_cycles
    
    {4, 16}
    

    This enables convenient looping such as the following:

    for m in circuits.keys(protocol="SRB").n_random_cycles:
        for key in circuits.keys(protocol="SRB", n_random_cycles=m):
            pass
    

    Note that in this case circuits.similar_keys("n_random_cycles", protocol="SRB") (see below) would be even more convenient:

  2. trueq.CircuitCollection.subset():

    Returns an iterable over all circuits whose key attribute matches a given filter. This may seamlessly join together circuits in different circuit lists.

    For example, we can put all XRB circuits of this protocol into a new CircuitCollection like this:

    circuits.subset(protocol="XRB");
    

    We can match on multiple values:

    len(circuits.subset(protocol="XRB", n_random_cycles={4,5}))
    
    90
    
  3. trueq.CircuitCollection.similar_keys():

    Returns an iterable over KeySets. Each KeySet contains keys that match the filter, but are additionally grouped by some equal (or unequal) estimate value.

    It is often useful to both group and filter at the same time. Suppose we want all XRB circuits, and that we want to group them by their n_random_cycles value.

    for keys in circuits.similar_keys("n_random_cycles", protocol="XRB"):
        # keys is a KeySet where protocol=XRB, and where every n_random_cycles is equal
        for key in keys:
            pass
    

    As a convenience for more concise code, we can also group where everything except n_random_cycles is equal. This is done with the invert=True flag. This is particularly useful for seq_label in XRB.

    for keys in circuits.similar_keys("seq_label", invert=True, protocol="XRB"):
        # keys is a KeySet where protocol=XRB, and all other key fields except
        # seq_label are equal
        for key in keys:
            pass
    

EstimateCollection Internals

EstimateCollections are containers of Estimates with fancy pattern matching functionality.

The most convenient way to view the output of a EstimateCollection is its fancy HTML representation in an IPython Notebook. If you want to access specific values then the relevant methods are listed below, followed by some illustrative examples.

  1. trueq.estimate.EstimateCollection.keys()

    Calling syntax: fit.keys(**filter)

  2. trueq.estimate.EstimateCollection.subset()

    Calling syntax: fit.subset(pattern="*", **filter)

Suppose we are after the SRB infidelity of the qubit with label 4. Our first step might be to list all of the keys just to see what sorts of estimates they have:

fit.keys()
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
KeySet
List of all the keys in the KeySet
protocol
The characterization protocol used to generate a circuit.
cycle
A clock cycle containing a collection of operations.
labels twirl
The twirling groups used on each subsystem.
Key
Key:
  • cycle: Cycle((5, 4): Gate.cx, immutable=True)
  • labels: (4,)
  • protocol: CB
  • twirl: Paulis on [4, 5]
CB Cycle((5, 4): Gate.cx, immutable=True) (4,) Paulis on [4, 5]
Key
Key:
  • cycle: Cycle((5, 4): Gate.cx, immutable=True)
  • labels: (5,)
  • protocol: CB
  • twirl: Paulis on [4, 5]
CB Cycle((5, 4): Gate.cx, immutable=True) (5,) Paulis on [4, 5]
Key
Key:
  • labels: (4,)
  • protocol: SRB
  • twirl: Cliffords on [4, 5]
SRB (4,) Cliffords on [4, 5]
Key
Key:
  • labels: (4,)
  • protocol: SRB
  • twirl: Cliffords on [4]
SRB (4,) Cliffords on [4]
Key
Key:
  • labels: (5,)
  • protocol: SRB
  • twirl: Cliffords on [4, 5]
SRB (5,) Cliffords on [4, 5]
Key
Key:
  • labels: (4,)
  • protocol: XRB
  • twirl: Cliffords on [4]
XRB (4,) Cliffords on [4]

Looking at these keys we can choose a filter to pass to keys() and then extract the corresponding estimates:

[estimate.e_F for estimate in fit.subset(protocol="SRB", labels=(4,))]
[EstimateTuple(name='e_F', val=0.04391468613970742, std=0.004810753376252928),
 EstimateTuple(name='e_F', val=0.05572999767047815, std=0.007269806983770489)]

You will notice that there are two entries returned in the list. This is because we added SRB to the circuit collection in two different ways; we are seeing the estimates of \(e_F\) when SRB is isolated on qubit 4, and when SRB is run simultaneously on qubits (4, 5). If this behaviour were undesired, we would have to filter further. For example, we could filter by the twirling group of the Cliffords on qubit 4:

[estimate.e_F for estimate in fit.subset(protocol="SRB", twirl=(("C", 4),))]
[]