qat.middleend.decompositions.base module

class DecompositionBase(end_nodes=None, extend_end_nodes=None)

Bases: object

Decomposition objects implement the rules for decomposing gates into native gates.

The object should be equipped with a decompose_op() method that is implemented using single dispatch. This allows us to register a decomposition for each relevant gate / operation type. Note that decompositions should be implemented carefully, and form a directed acyclic graph (DAG) that terminates at NativeGate nodes.

Assumming all relevant nodes are implemented, an operation can be decomposed to a series of native gates / operations using the decompose() method, which will recursively call decompose_op().

As a rule of thumb, try not to use logic on gate angles to define the decomposition. They should be kept general. If there are instances where a choice of angle(s) can be decomposed more effeciently, a transformation pass should be used to substitute the gate. This is done to allow predictable behaviour, and has parameterised circuits in mind.

Parameters:
  • end_nodes (Optional[list[QubitInstruction]]) – Optionally specify the nodes to end the decomposition recursion at. Defaults to decompositions.end_nodes.

  • extend_end_nodes (Optional[list[QubitInstruction]]) – By default the decomposition will recurse until all gates are a NativeGate. You can optionally add additional (non NativeGate) gates to terminate at. This argument should only be specified if end_nodes=None.

decompose(gate, *args)

Recursively decomposes an instruction into a product of native gates.

Parameters:

gate (QubitInstruction) – Gate to decompose.

decompose_op(gate, *args)

Implements the definition of a decomposition of a qubit operation.

The definition does not have to be in terms of end nodes, but decompositions must form a DAG. For example,

CNOT -> {ECR, X, Z} -> {ZX_pi_4, Z_phase, X_pi_2}
U -> {x_pi_2, Z_phase}
end_nodes = ()