qml.Tracker

class Tracker(dev=None, callback=None, persistent=False)[source]

Bases: object

This class stores information about device executions and allows users to interact with that data upon individual executions and batches, even within parameter-shift gradients and optimization steps.

The information is stored in three class attribute dictionaries: totals, history, and latest:

  • latest tracks the last set of information passed to the tracker.

  • history stores a list of values passed for each keyword.

  • totals keeps a running sum per keyword when the values are numeric.

Standard devices will track the number of executions, number of shots, number of batch executions, batch execution length, and results of circuit executions, but plugins may store additional information with no changes to this class.

Information is only stored when the class attribute active is set to True. This attribute can be toggled via a context manager and Python’s with statement. Upon entering a context, the stored information is reset, unless persistent=True. Tracking mode can also be manually triggered by setting tracker.active = True without the use of a context manager.

Parameters
  • dev (Device) – A PennyLane compatible device

  • callback=None (callable or None) – A function of the keywords totals, history and latest. Run on each record call with current values of the corresponding attributes.

  • persistent=False (bool) – Whether to reset stored information upon entering a runtime context.

Example

Using a with statement to toggle the active mode, we can see the number of executions and shots used to calculate a parameter-shift derivative.

dev = qml.device('default.qubit', wires=1, shots=100)

@qml.qnode(dev, diff_method="parameter-shift")
def circuit(x):
    qml.RX(x, wires=0)
    return qml.expval(qml.Z(0))

x = np.array(0.1, requires_grad=True)

with qml.Tracker(dev) as tracker:
    qml.grad(circuit)(x)

You can then access the tabulated information through totals, history, and latest:

>>> tracker.totals
{'batches': 2, 'simulations': 3, 'executions': 3, 'shots': 300}
>>> tracker.latest
{'simulations': 1,
 'executions': 1,
 'results': array(-0.08),
 'shots': 100,
 'resources': Resources(num_wires=1, num_gates=1,
                        gate_types=defaultdict(<class 'int'>, {'RX': 1}),
                        gate_sizes=defaultdict(<class 'int'>, {1: 1}),
                        depth=1,
                        shots=Shots(total_shots=100, shot_vector=(ShotCopies(100 shots x 1),)))}
>>> tracker.history.keys()
dict_keys(['batches', 'simulations', 'executions', 'results', 'shots', 'resources'])
>>> tracker.history['results']
[array(1.), array(0.02), array(-0.08)]
>>> print(tracker.history['resources'][0])
wires: 1
gates: 1
depth: 1
shots: Shots(total=100)
gate_types:
{'RX': 1}
gate_sizes:
{1: 1}

We can see that calculating the gradient of circuit takes three total evaluations: one forward pass and one batch of length two for the derivative of qml.RX.

Note

With backpropagation, this function should take qnode.device instead of the device used to create the QNode.

Users can pass a custom callback function to the callback keyword. This function is run each time the record() method is called, which occurs near the end of a device’s execute and batch_execute methods. Using print or logging, users can monitor completion during a long set of jobs.

The function passed must accept totals, history, and latest as keyword arguments. The dictionary latest will contain different keywords based on whether whether execute or batch_execute last performed an update.

>>> def shots_info(totals, history, latest):
...     if 'shots' in latest:
...         print("Total shots: ", totals['shots'])
>>> x = np.array(0.1, requires_grad=True)
>>> with qml.Tracker(circuit.device, callback=shots_info) as tracker:
...     qml.grad(circuit)(x)
Total shots:  100
Total shots:  200
Total shots:  300

By specifying persistent=False, you can reuse the same tracker across multiple contexts.

>>> with qml.Tracker(circuit.device, persistent=False) as tracker:
...     circuit(0.1)
>>> with tracker:
...     circuit(0.2)
>>> tracker.totals['executions']
2

When used with the null qubit device (eg. dev = qml.device("null.qubit")), we can track the resources used in the circuit without execution!

>>> dev = qml.device("null.qubit", wires=[0], shots=10)
>>> @qml.qnode(dev)
... def circuit(x):
...     qml.RX(x, wires=0)
...     return qml.expval(qml.Z(0))
...
>>> with qml.Tracker(dev) as tracker:
...     circuit(0.1)
...
>>> resources_lst = tracker.history['resources']
>>> print(resources_lst[0])
wires: 1
gates: 1
depth: 1
shots: Shots(total=10)
gate_types:
{"RX": 1}
gate_sizes:
{1: 1}

record()

This method allows users to interact with the stored data.

reset()

Resets stored information.

update(**kwargs)

Store passed keyword-value pairs into totals,``history``, and latest attributes.

record()[source]

This method allows users to interact with the stored data. While it’s intended purpose is monitoring large jobs through print statements or logging, the function is completely flexible and customizable.

If a user provided a callback function during initialization, that function is called with the current totals, history, and latest data variables as keyword arguments.

reset()[source]

Resets stored information.

update(**kwargs)[source]

Store passed keyword-value pairs into totals,``history``, and latest attributes.

There is no restriction on the key-value pairs passed, but in the standard devices, the device execute method will pass executions and shots, and the batch_execute method will pass batches and batch_len.

Only numeric values will be added to totals.

>>> tracker.update(a=1, b=2, c="c")
>>> tracker.latest
{"a":1, "b":2, "c":"c"}
>>> tracker.history
{"a": [1], "b": [2], "c": ["c"]}
>>> tracker.totals
{"a": 1, "b": 2}

Contents

Using PennyLane

Development

API

Internals