# Compilation¶

class trueq.compilation.Direction

Defines the two directions that Patterns may be applied to trueq.Circuit

See descrption of trueq.compilation.base.Pattern for more details

class trueq.compilation.Justify(direction=<Direction.FORWARD: 1>, **_)

Pattern that takes two Cycles, and moves gates preferentially to one side.

This method is inherently limited, because it only deals with 2 Cycles at a time, it can encounter “traffic jams”, IE: If you have two Cycles on qubit 0, followed by a series of empty cycles, the first Cycles Gate cannot move because of the second Cycle, but then the second cycle’s Gate will iteratively progress forward through the empty Cycles. This can be combated by repeating the Justify several times.

This class skips immutable cycles.

Returns 2 Cycles.

Parameters

direction (trueq.compilation.base.Direction) – Specifies the direction to preferentially move gates, either trueq.compilation.base.Direction.FORWARD or BACK

property direction

This must return the direction that the pattern will be applied.

Direction must be one of the two enums of trueq.compilation.base.Direction

This requires some discussion, the way that the Compiler functions is that it incrementally goes through a circuit, and passes a set of Cycles to a Pattern. There are two ways that it can increment through the Circuit, Forward or Backward.

apply_cycles(cycles)

This must accept n_input_cycles number of Cycles and return a list of Cycles.

This function must be over-written in the subclass, and should be where the substitutions should be calculated and performed.

Return type

list(Cycles)

class trueq.compilation.Merge(n_labels=1, merge_immutable=False, **_)

Pattern that takes two cycles, finds compatabile labels and gates, then merges them to a single gate as much as is possible.

Identity gates are removed, and gates are automatically split into the smallest number of individual gates possible. For example, if the merging results in a two qubit gate that turns out to be the kronecker product of two single qubit gates, then it will automatically be broken apart into the two single qubit gates.

This class skips immutable cycles unless specified.

Returns 2 Cycles.

Params n_labels

How many labels to merge at any given time, if only single qubit reductions are desired, then n_labels=1, two qubit reductions would be n_labels=2 etc. This defaults to single qubit reductions.

Parameters

merge_immutable (bool) – If set to True, immutable cycles will be merged, if set to False, then immutable cycles are skipped.

apply_cycles(cycles)

This must accept n_input_cycles number of Cycles and return a list of Cycles.

This function must be over-written in the subclass, and should be where the substitutions should be calculated and performed.

Return type

list(Cycles)

class trueq.compilation.RemoveId(skip_immutable=True, **_)

Pattern that removes identity gates from 1 trueq.Cycle

This is a good class to use as an example for more complex classes.

Returns a list containing 1 Cycle.

Params skip_immutable

Determines if identity gates should be removed from immutable cycles. If True, immutable cycles are not altered.

apply_cycles(cycles)

Removes all identity gates in a cycle if the cycle is mutable or skip_immutable is False.

class trueq.compilation.Relabel(permutation, **_)

Pattern which relabels all the labels and keys in a circuit.

This searches through and relabels/reorders entries in the following key entries:

• analyze_decays

• compiled_pauli

• cycle

• measurement_basis

• targeted_errors

• twirl

Note that this does not function when the circuit has results present.

# Swapping the labels on qubit 0 and 1, 2 stays
permutation = {0: 1, 1: 0, 2: 2}

pat = Relabel(permuatation)
new_circ = pat.apply_cycles(old_circ)

Parameters

permutation (dict) – A dictionary where the keys are the current label and the values are the new labels.

apply_cycles(circuit)

This must accept n_input_cycles number of Cycles and return a list of Cycles.

This function must be over-written in the subclass, and should be where the substitutions should be calculated and performed.

Return type

list(Cycles)

class trueq.compilation.CycleSandwich(target, before=None, after=None, ignore_imm=True, ignore_id=True)

Pattern that searches circuits for a specific cycle and when found, sandwiches it between two other (optional) cycles.

target = tq.Cycle({(0, 1): tq.Gate.CNOT})
before = tq.Cycle(0: tq.Gate.from_generators("Z", 4))
after = tq.Cycle(1: tq.Gate.from_generators("X", -8.2))
pattern = CycleSandwich(target, before=before, after=after)
new_circ = pattern.apply_cycles(old_circuit)


Note

This pattern makes deep copies of before and after before returning them.

Parameters
• target (Cycle) – Which cycle to match on.

• before (Cycle) – A cycle to place immediately before every occurrence of the provided target.

• after (Cycle) – A cycle to place immediately after every occurrence of the provided target.

• ignore_imm (bool) – Whether to apply this pattern when the target and a cycle have differing values of trueq.Cycle.immutable. Default is True.

• ignore_id (bool) – Whether to treat all identity gates as though they are not present when comparing cycles. Default is True.

apply_cycles(cycles)

This must accept n_input_cycles number of Cycles and return a list of Cycles.

This function must be over-written in the subclass, and should be where the substitutions should be calculated and performed.

Return type

list(Cycles)

class trueq.compilation.Compiler(patterns, repeat=1)

Substitutes cycles in a Circuit using known substitution Patterns.

In classical computing this would formally be called a Peephole Optimizer.

This stores a list of trueq.compilation.base.Pattern objects which are rules for how to replace Gates in sets of cycles.

These Pattern objects take a set of cycles at a time, and return an altered set of cycles. A trivial example is the RemoveId() pattern, which accepts 1 cycle at a time, and removes any identity Gates from the cycle, before returning it.

Each Pattern object requires a certain number of cycles at a time; in the example above, only 1 cycle is passed. In general this is not the case, for example to simplify single qubit operations. In this case the minimum number of cycles needed is 2. The Pattern looks for single qubit gates in both of the cycles, and computes the single equivalent Gate, which is put back instead of the two original Gates.

In traversing a Circuit, the compiler can either iterate forward, or backward. This traversal direction is specified by the Patterns themselves, as it may be useful to have certain patterns apply themselves backwards while others are strictly forward.

To build a Compiler which knows all possible simplification rules would result in an overly complex and rigid tool, which would would be difficult to generalize for all hardware implementations. By making the compiler itself very small, and allowing custom rulesets (Patterns in this case), very complex compilation instructions can be expressed in simple and readable fashion.

An example of a set of patterns which converts a Circuit into hardware aware Gates:

Compiler([Justify(),

Native2Q(config=config), Merge(), RemoveId(), Native1Q(config=config), Justify() ])

This set of patterns performs these operations:

Justify - move gates preferentially to one side of the circuit, for example,

A circuit containing an X90 Gate at the beginning, but then 10 empty cycles. Justify(Direction.FORWARD) would move the X90 gate all the way to the other end of the circuit.

Native2Q - Convert all 2 qubit operations found in the circuit into NativeGate’s

which can be run on a hardware as specified by the config object. This Pattern does NOT decompose the single qubit gates, so in the process it adds single qubit Gate objects to some Cycles.

Merge - This simplifies any neighboring single qubit operations and reduces

them to a single Gate.

RemoveId - Since the simplify step may have introduced single qubit gate which

are the identity gate, this Pattern removes all Id Gates.

Native1Q - This converts all 1 qubit operations into NativeGates which can be

run on hardware as specified by the config object.

Justify - Just for good measure, make sure everything is moved as far forward

in the Circuit as Possible.

Parameters
• patterns (list) – A list of trueq.compilation.base.Pattern, see above.

• repeat (int) – The number of times to repeat the pattern list, may be helpful in certain instances.

compile(circ)

Apply all of the patterns in the compiler in order to a given trueq.Circuit or trueq.CircuitCollection

Parameters

circ (trueq.Circuit | trueq.CircuitCollection) – A circuit that the Pattern list should be applied to.

property patterns

A list of all Patterns applied by this compiler.

Return type

list

trueq.compilation.count_streaks(circuit)

Iterates through a circuit, finding all multi-qudit gates and counting the number of times that there are repeated operations on the same pair of qudits.

Repeated operations on a pair of qudits can often be merged and do not require extra swaps to map a circuit onto a specific chip topology. Therefore we want to count “streaks”, where a “streak” on a pair of qudits is a series of cycles in a circuit where the qudit pair of interest do not interact with any other qudits.

For example: Given a circuit of 6 cnot gates, where there are 2 cnots in a row on (0, 1) followed by a cnot on (1, 2), then 3 more cnots in a row on (0, 1).

On labels (0, 1) there is 1 streak of 2 in a row and 1 of 3 in a row. On labels (1, 2) there is 1 streak of length 1.

circ = tq.Circuit({(0, 1): tq.Gate.cx})

count_streaks(circ)
# {(0, 1): {2: 1, 3: 1}, (1, 2): {1: 1}}


This function returns a nested dictionary whose keys are pairs of qudits that have a multi-qudit gate acting on them, and the values are the numbers of streaks of each length. In the above example, (0, 1) has a value of {2: 1, 3: 1}, meaning it found 1 streak of length 2 and 1 streak of length 3.

Note that single qudit operations will not break a streak, and operations may happen in parallel and those will be counted as independent streaks.

The keys of the dictionary can be used to define the connectivity graph of the circuit itself, and are useful for validation of matching circuit topology to chip topology.

Parameters

circuit (trueq.Circuit) – The circuit of which to calculate the topology and streaks.

Return type

dict

class trueq.compilation.Native1Q(config)

Pattern which expands arbitrary single qubit gates into the decomposition mode provided in a given Config object.

Basics of operation:

1. When asked to decompose a qubit gate on a given label, looks through the config object to find GateFactory objects that apply to that qubit. Checks if these factories are sufficient to build arbitrary single qubit gates according to the mode of the config. This list of factories is stashed against the label.

2. Next, these factories are combined with QubitMode to decompose into 3 or 5 cycles of NativeGates.

This class does NOT skip immutable cycles.

Returns 3 or 5 cycles.

Parameters

config (trueq.Config) – Config object which defines available gates on the system

apply_cycles(cycles)

This must accept n_input_cycles number of Cycles and return a list of Cycles.

This function must be over-written in the subclass, and should be where the substitutions should be calculated and performed.

Return type

list(Cycles)

class trueq.compilation.Native2Q(config, max_depth=3, rounding=5, tol=1e-06)

Series of numerical SU(4) decomposition methods.

Decomposes a target gate into a series of SU(2) gates between non-parameterized SU(4) gates found in the config. This uses the trueq.math.decomposition.decompose_su4() method found below.

This class does NOT skip immutable cycles.

Returns 1 to 2 * max_depth + 1 Cycles.

Parameters
• config (trueq.Config) – Config object which defines available gates on the system

• max_depth (int) – The maximum number of SU(4) gates to use.

• rounding (int) – How much rounding should be performed on the KAK fitting, this is how many decimal places to round to in terms of degrees, IE: 0 means nearest whole degree, 1 means nearest 0.1 degree etc.

apply_cycles(cycles)

Decomposes all SU(4) gates in a given cycle into gates defined by the config.

Parameters

cycles (list) – A list containing 1 trueq.Cycles

Raises

tq.math.DecompError – If there is a gate in the cycle which is not decomposable using the config.

trueq.compilation.compile(config, circuit, patterns=None)

Compile from arbitrary True-Qᵀᴹ representation of circuits, into a representation which is compatable with a given config.

By default, this performs a standard set of patterns, and can be used as a template for more advanced compiler definitions. See trueq.compilation.DEFAULT_PATTERNS for a list of the default patterns which are applied.

Parameters
• config (trueq.Config) – A trueq.Config which has 2-qubit gates defined.

• circuit (trueq.Circuit) – A circuit which will be compiled into the config compatable format.

• patterns (tuple) – A tuple of trueq.compilation.Pattern to be applied in order. These must be uninstantiated classes which must accept a config as a keyword argument, see trueq.compilation.DEFAULT_PATTERNS for an example. If None is provided, then the default list is used.

trueq.compilation.get_transpiler

Get a trueq.Compiler which can transpile from arbitrary True-Qᵀᴹ representation of circuits, into a representation which is compatable with a given config.

By default, this performs a standard set of patterns, and can be used as a template for more advanced compiler definitions. See trueq.compilation.DEFAULT_PATTERNS for a list of the default patterns which are applied.

Parameters
• config (trueq.Config) – A trueq.Config which has 2-qubit gates defined.

• patterns (tuple) – A tuple of trueq.compilation.Pattern to be applied in order. These must be uninstantiated classes which must accept a config as a keyword argument, see trueq.compilation.DEFAULT_PATTERNS for an example. If None is provided, then the default list is used.