qml.workflow.get_transform_program

get_transform_program(qnode, level='device', gradient_fn='unset')[source]

Extract a transform program at a designated level.

Warning

Using level=None is deprecated and will be removed in a future release. Please use level='device' to include all transforms.

Parameters:
  • qnode (QNode) – the qnode to get the transform program for.

  • level (None, str, int, slice) –

    An indication of what transforms to use from the full program.

    • None or "device": Uses the entire transformation pipeline.

    • "top": Ignores transformations and returns the original tape as defined.

    • "user": Includes transformations that are manually applied by the user.

    • "gradient": Extracts the gradient-level tape.

    • int: Can also accept an integer, corresponding to a number of transforms in the program. level=0 corresponds to the start of the program.

    • slice: Can also accept a slice object to select an arbitrary subset of the transform program.

  • gradient_fn (None, str, TransformDispatcher) – The processed gradient fn for the workflow.

Returns:

the transform program corresponding to the requested level.

Return type:

TransformProgram

The transforms are organized as:

../../_images/transforms_order.png

where transform1 is first applied to the QNode followed by transform2. First, user transforms are run on the tapes, followed by the gradient expansion, followed by the device expansion. “Final” transforms, like param_shift and metric_tensor, always occur at the end of the program, despite being part of user transforms. Note that when requesting a level by name (e.g. “gradient” or “device”), the preceding levels would be applied as well.

dev = qml.device('default.qubit')

@qml.metric_tensor # final transform
@qml.transforms.merge_rotations # transform 2
@qml.transforms.cancel_inverses # transform 1
@qml.qnode(dev, diff_method="parameter-shift", shifts=np.pi / 4)
def circuit():
    return qml.expval(qml.Z(0))

By default, we get the full transform program. This can be explicitly specified by level="device".

>>> qml.workflow.get_transform_program(circuit)
TransformProgram(cancel_inverses, merge_rotations, _expand_metric_tensor,
_expand_transform_param_shift, validate_device_wires, defer_measurements,
decompose, validate_measurements, validate_observables, metric_tensor)

The "user" transforms are the ones manually applied to the qnode, cancel_inverses(), merge_rotations() and metric_tensor().

>>> qml.workflow.get_transform_program(circuit, level="user")
TransformProgram(cancel_inverses, merge_rotations, _expand_metric_tensor, metric_tensor)

The _expand_transform_param_shift is the "gradient" transform. This expands all trainable operations to a state where the parameter shift transform can operate on them. For example, it will decompose any parametrized templates into operators that have generators. Note how metric_tensor is still present at the very end of resulting program.

>>> qml.workflow.get_transform_program(circuit, level="gradient")
TransformProgram(cancel_inverses, merge_rotations, _expand_metric_tensor, _expand_transform_param_shift, metric_tensor)

"top" and 0 both return empty transform programs.

>>> qml.workflow.get_transform_program(circuit, level="top")
TransformProgram()
>>> qml.workflow.get_transform_program(circuit, level=0)
TransformProgram()

The level can also be any integer, corresponding to a number of transforms in the program.

>>> qml.workflow.get_transform_program(circuit, level=2)
TransformProgram(cancel_inverses, merge_rotations)

level can also accept a slice object to select out any arbitrary subset of the transform program. This allows you to select different starting transforms or strides. For example, you can skip the first transform or reverse the order:

>>> qml.workflow.get_transform_program(circuit, level=slice(1,3))
TransformProgram(merge_rotations, _expand_transform_param_shift)
>>> qml.workflow.get_transform_program(circuit, level=slice(None, None, -1))
TransformProgram(metric_tensor, validate_observables, validate_measurements,
decompose, defer_measurements, validate_device_wires, _expand_transform_param_shift,
_expand_metric_tensor, merge_rotations, cancel_inverses)

You can get creative and pick a single category of transforms as follows, excluding any preceding transforms (and the final transform if it exists):

>>> user_prog = qml.workflow.get_transform_program(circuit, level="user")
>>> grad_prog = qml.workflow.get_transform_program(circuit, level="gradient")
>>> dev_prog = qml.workflow.get_transform_program(circuit, level="device")
>>> grad_prog[len(user_prog) - 1 : -1]
TransformProgram(_expand_transform_param_shift)
>>> dev_prog[len(grad_prog) - 1 : -1]
TransformProgram(validate_device_wires, mid_circuit_measurements, decompose, validate_measurements, validate_observables)