Merges operations in a circuit by calling merge_func iteratively on operations.

Used in the notebooks

Used in the tutorials

Two operations op1 and op2 are merge-able if

- There is no other operations between op1 and op2 in the circuit
- is_subset(op1.qubits, op2.qubits) or is_subset(op2.qubits, op1.qubits)

The merge_func is a callable which, given two merge-able operations op1 and op2, decides whether they should be merged into a single operation or not. If not, it should return None, else it should return the single merged operations op.

The method iterates on the input circuit moment-by-moment from left to right and attempts to repeatedly merge each operation in the latest moment with all the corresponding merge-able operations to it's left.

If op1 and op2 are merged, both op1 and op2 are deleted from the circuit and the resulting merged_op is inserted at the index corresponding to the larger of op1/op2. If both op1 and op2 act on the same number of qubits, merged_op is inserted in the smaller moment index to minimize circuit depth.

The number of calls to merge_func is O(N), where N = Total no. of operations, because:

- Every time the `merge_func` returns a new operation, the number of operations in the
    circuit reduce by 1 and hence this can happen at most O(N) times
- Every time the `merge_func` returns None, the current operation is inserted into the
    frontier and we go on to process the next operation, which can also happen at-most
    O(N) times.

circuit Input circuit to apply the transformations on. The input circuit is not mutated.
merge_func Callable to determine whether two merge-able operations in the circuit should be merged. If the operations can be merged, the callable should return the merged operation, else None.
tags_to_ignore Sequence of tags which should be ignored while applying merge_func on tagged operations -- i.e. merge_func(op1, op2) will be called only if both op1 and op2 satisfy set(op.tags).isdisjoint(tags_to_ignore).
deep If true, the transformer primitive will be recursively applied to all circuits wrapped inside circuit operations.

Copy of input circuit with merged operations.

ValueError if the merged operation acts on new qubits outside the set of qubits corresponding to the original operations to be merged.