Tessera: Quantum Circuit Transpiler

The Pipeline

When you call transpile(), your circuit passes through a sequence of single-purpose transformation passes. Each pass receives the output of the previous one. The final stage is an optimization loop that wraps two inner passes and can be configured to run a fixed number of iterations or until the gate count stabilizes. This page walks through each stage in order, including what it does, why it exists, and what changes in the circuit as a result.

The full pipeline:

BasisTranslation → Layout → BasicSwapRouter → BasisTranslation → RemoveBarriers → OptimizationLoop

OptimizationLoop = (CancelAdjacent → MergeRotations) repeated N times or until gate count converges

Pass 1: Basis Translation

The first thing Tessera does is decompose every gate in your circuit into the target backend's supported gate set. Different hardware supports different native gates. IBM supports cx, rz, sx, x, u. IonQ supports rx, ry, rz, cx. Rigetti supports rx, rz, cz. Any gate that isn't in the backend's basis set gets replaced with an equivalent sequence of gates that are.

This pass is single-pass. It walks the instruction list once and substitutes each non-basis gate using the backend's decomposition map. Decomposition map entries must produce only basis gate instructions, so no second pass is needed here.

Gates that are already in the basis set pass through unchanged. Measurements and barriers also pass through unchanged.

What changes: Non-basis gates are replaced with their decomposed equivalents. Gate count typically increases.

Pass 2: Layout

Your circuit is written in logical qubit space with qubit 0, qubit 1, qubit 2. Real hardware has a fixed topology where only certain qubit pairs are physically connected. LayoutPass maps each logical qubit to a physical qubit on the target device.

The actual placement logic lives in a layout algorithm. LayoutPass resolves whichever algorithm you selected from the registry (or a custom callable you passed) and delegates to it. Three algorithms ship with Tessera:

  • "dense" (default): greedy placement by interaction frequency
  • "sabre": forward-backward trial routing for higher quality on routing-heavy circuits
  • "trivial": direct logical-to-physical mapping, useful as a baseline

The result is stored in TesseraCircuit.layout as a dict mapping logical → physical qubit indices. For example {0: 3, 1: 5, 2: 6} means logical qubit 0 maps to physical qubit 3, and so on. All subsequent passes work in physical qubit space using this mapping.

For the details of each built-in algorithm and the custom-callable interface, see the Layout Algorithms page.

What changes: No gates are added or removed. The layout field on the circuit is populated.

Pass 3: Swap Router

Even with a good layout, some two-qubit gates may still involve qubits that aren't directly connected on the hardware. BasicSwapRouter resolves this by applying the layout from Pass 2 and inserting SWAP gates to move qubits into adjacent positions before each non-adjacent operation.

The actual swap-selection logic lives in a routing algorithm. BasicSwapRouter resolves whichever algorithm you selected from the registry (or a custom pairwise callable you passed) and delegates to it. Three algorithms ship with Tessera:

  • "bfs" (default): pairwise breadth-first shortest path
  • "a_star": pairwise A* shortest path with an admissible heuristic, ready for noise-aware weighted edges
  • "sabre": whole-circuit swap selection with front-layer lookahead, often produces fewer total SWAPs

For pairwise algorithms, the router walks the instruction list once. For each two-qubit gate it checks whether the two involved physical qubits are connected in the coupling map. If they are, the gate is emitted as-is. If they aren't, the router finds a path between them using the selected algorithm, inserts SWAP gates along that path to bring them adjacent, and emits the gate. The routing state (which logical qubit is at which physical position) is updated after every SWAP so subsequent gates see the correct positions.

SABRE works differently. It maintains a front layer of executable gates and picks each swap by scoring candidates against a heuristic that considers both the immediate gates and a window of upcoming gates, then applies the swap and updates the front layer.

For the details of each built-in algorithm and the custom pairwise callable interface, see the Routing Algorithms page.

What changes: SWAP instructions are inserted where needed. Gate count increases for circuits with non-adjacent two-qubit interactions.

Pass 4: Basis Translation (Second Run)

The SWAP gates inserted by Pass 3 are not basis gates on most backends. A SWAP decomposes into three CX gates on IBM, or a longer sequence on Rigetti. This second run of BasisTranslationPass catches those newly introduced SWAPs and decomposes them into backend basis gates exactly the same way Pass 1 did.

Running basis translation a second time rather than baking SWAP decomposition into the router keeps the router simple and backend-agnostic. It only has to think about connectivity, not gate sets.

What changes: SWAP instructions are replaced with their backend-specific decompositions. Gate count increases.

Pass 5: Remove Barriers

Barriers are structural hints that prevent gate reordering during circuit construction. They have no physical effect on hardware. Before the optimization passes run, RemoveBarriersPass strips all barrier instructions from the circuit so that CancelAdjacentPass and MergeRotationsPass can see across them and find cancellation and merging opportunities they would otherwise miss.

What changes: All barrier instructions are removed. Gate count decreases by the number of barriers present.

Pass 6: Optimization Loop

The final stage is an optimization loop that wraps two inner passes (Cancel Adjacent and Merge Rotations) and can run them more than once. A single round of these passes often unlocks new cancellation and merging opportunities for the other to find on the next round, so iterating can compound the savings.

The number of iterations is controlled by optimization_iterations:

  • 1 (default): a single pass through Cancel Adjacent and Merge Rotations. Matches pre-v1.1 behavior.
  • Any positive integer: run that many iterations regardless of whether the gate count is still changing.
  • -1: convergence mode. The loop runs until the gate count stops decreasing, capped at max_iterations (default 1000) as a safety valve against pathological circuits.

Inside the loop, the two inner passes run in order: Cancel Adjacent first, then Merge Rotations. Both inner passes can also be used standalone outside the loop if you want to compose your own pipeline.

Inner pass: Cancel Adjacent

Some gates are self-inverse. Applying the same gate twice returns the qubit to its original state, so the pair can be removed entirely. CancelAdjacentPass finds and removes these pairs.

The supported self-inverse gates are: x, cx, y, h, cz, swap.

This pass operates in two modes controlled by the strict parameter:

  • Strict mode (default): only cancels gates that are directly adjacent in the instruction list. X(q0) X(q0) cancels; X(q0) H(q1) X(q0) does not.
  • Commutative mode: cancels gates separated by instructions on non-overlapping qubits, since those instructions commute freely. H(q0) X(q1) H(q0) cancels the H gates because nothing between them touches q0.

Inner pass: Merge Rotations

Consecutive rotation gates on the same qubit can be combined into a single rotation since rotation is additive: Rz(a)·Rz(b) = Rz(a+b). MergeRotationsPass finds these pairs and collapses them. If the merged angle is smaller than epsilon (default 1e-9), the gate is dropped entirely since it has negligible effect.

The supported mergeable gates are: rz, rx, ry.

Like CancelAdjacentPass, this pass operates in two modes:

  • Strict mode (default): only merges gates that are directly adjacent. Three consecutive rotation gates will leave one unmerged in a single iteration of this pass; running the optimization loop more than once handles that case.
  • Commutative mode: merges gates separated by instructions on non-overlapping qubits. Rz(0.3, q0) Rx(0.5, q1) Rz(0.5, q0) becomes Rz(0.8, q0) Rx(0.5, q1).
What changes: Self-inverse gate pairs are cancelled. Consecutive rotation pairs are merged into single gates, with near-zero merged rotations dropped. The loop repeats both transformations either a fixed number of times or until gate count stabilizes. Gate count decreases (or stays the same) every iteration.