MethodLayer 2
Uniformly controlled rotations
Prepare an arbitrary state with one layer of uniformly controlled (multiplexed) Ry and Rz rotations per qubit, the angles computed analytically from the amplitude list. This is the exact, assumption-free method most software stacks emit by default.
A description of — an explicit list of amplitudes, an analytic density, a list of nonzero entries, or a low-bond-dimension tensor network — plus a target .
An -qubit circuit, possibly using ancillas, with a stated gate count, depth, ancilla count, and — where the circuit is not deterministic — a success probability.
Same contract as the slot it fills.
This one, drawn
From Vector to load to State you can prepare
A circle is an object you are holding. This method is drawn heavier, opened into its own steps; the other lines between the same two ends are the alternatives recorded for the same slot. Circles are named on hover, and each one is a link.
Nothing drawn here has a recorded way through it that this figure leaves shut. See it on the map
What it fills
- Prepare an input state
Map to a state whose amplitudes are proportional to a specified vector , to within . The cost is set by which description of you hold, not by the algorithm that consumes it.
When it applies
Applies to any state, with no structural assumption — which is exactly why it cannot beat the exponential bound. Ancilla-free in its basic form.
Requires
Every step this method names moves its route along, so there is nothing it needs alongside them.
Example
given n qubits and the amplitude list of the arbitrary state to prepare
for each of the n qubits:
apply one layer of uniformly controlled (multiplexed) Ry rotations
apply one layer of uniformly controlled (multiplexed) Rz rotations
# every angle in those layers is computed analytically from the amplitude
# list; Mottonen, Vartiainen, Bergholm and Salomaa give the analytic
# expression, which is not transcribed here
# the state is prepared exactly -- this is the assumption-free method most
# software stacks emit by default
# their count, ancilla-free in this basic form: 2^(n+2) - 4n - 4 CNOT gates
# and 2^(n+2) - 5 one-qubit rotations
# Yuan and Zhang settle the ancilla-assisted case: depth Theta(n + 2^n/(n+m))
# and size Theta(2^n), for any number m of ancillary qubits
# applies to any state, with no structural assumption -- which is exactly
# why it cannot beat the exponential boundCost, as the source states it
Möttönen, Vartiainen, Bergholm and Salomaa give CNOT gates and one-qubit rotations, with an analytic expression for the angles. Yuan and Zhang settle the ancilla-assisted case, pinning the depth complexity to and the size complexity to for any number of ancillary qubits. Li and Luo independently give size for an -qubit -sparse state with unlimited ancillas; the substitution that turns this into is arithmetic we performed, not a statement in their paper.
Implementations
PennyLane `qml.MottonenStatePreparation` template
Xanadu's PennyLane ships this method as a named built-in template, and names the paper in the class docstring: it "Prepares an arbitrary state on the given wires using a decomposition into gates developed by Möttönen et al. (2004)", and "The state is prepared via a sequence of uniformly controlled rotations." The docstring gives the order of the two cascades for the inverse direction: "In the work of Möttönen et al., inverse state preparation is executed by first equalizing the phases of the state vector via uniformly controlled Z rotations, and then rotating the now real state vector into the direction of the state via uniformly controlled Y rotations." That is the order the paper itself gives in its Sec. III, where the algorithm for taking to is stated as two steps: first a cascade of uniformly controlled -rotations , "rendering the vector real up to the global phase", then a similar cascade of uniformly controlled -rotations onto . The file also records where it came from — "This code is adapted from code written by Carsten Blank for PennyLane-Qiskit" — and carries one standing caveat: "Due to non-trivial classical processing of the state vector, this template is not always fully differentiable."
The two angle helpers write out, in their own docstrings, the same two expressions this record's theory carries. `_get_alpha_z` gives and `_get_alpha_y` gives . `compute_decomposition` splits the input into `a = qp.math.abs(state_vector)` and `omega = qp.math.angle(state_vector)`, reverses the wire order ("change ordering of wires, since original code was written for IBM machines"), and runs the cascade first — `for k in range(len(wires_reverse), 0, -1)`, target `wires_reverse[k - 1]`, controls `wires_reverse[k:]` — which is the dagger of the docstring's order. The cascade, and with it the single `global_phase = -1 * qp.math.sum(omega, axis=-1) / qp.math.shape(state_vector)[-1]` that closes it, runs unless the phases are all zero and the input is neither abstract nor differentiable: the guard is `qp.math.is_abstract(omega) or qp.math.requires_grad(omega) or not qp.math.allclose(omega, 0)`, so under jit or under autodiff the cascade is emitted even for an all-real state. Each uniformly controlled rotation is compiled by Gray code rather than by nested controls: `_apply_uniform_rotation_dagger` sets `theta = compute_theta(alpha, num_qubits=gray_code_rank)`, then `code = gray_code(gray_code_rank)` and `control_indices = np.log2(code ^ np.roll(code, -1)).astype(int)`, and emits one rotation and one `qp.CNOT` per index — except at the end of each cascade, where the control list is empty, `gray_code_rank == 0` takes an early return, and at most one rotation and no CNOT is emitted. `compute_theta`'s docstring names the identity that makes this cheap: "This function uses the fact that the transformation given by Eq. (3) in Möttönen et al. (2004) is equal to a Walsh-Hadamard transform followed by some permutations, which can be expressed as a ladder of CNOT gates applied to the angles, when interpreting them as a quantum state." A rotation whose angle falls below `_ATOL = np.finfo(qp.math.get_dtype_name(theta)).eps` is dropped, but never when the angles are abstract or differentiable: `skip_none` starts as `qp.math.is_abstract(theta) or qp.math.requires_grad(theta)` and is then set true again when every angle already exceeds `_ATOL`. Read at tag `v0.45.1`. The `master` branch read the same day has diverged on one point: `_get_alpha_y` there gains a leaf-level branch, , so that a real-valued state carries its signs in the angles and skips the cascade and the global phase entirely. Neither that branch nor the `is_real` test exists in `v0.45.1`.
No dataset — the template takes a state vector, and the module runs nothing else. The docstring's worked example is three wires on `qp.device('default.qubit', wires=3)`, PennyLane's state-vector simulator, with `state = np.array([1, 2j, 3, 4j, 5, 6j, 7, 8j])` divided by its own norm.
`pennylane/templates/state_preparations/mottonen.py` in https://github.com/PennyLaneAI/pennylane, Python, Apache License 2.0. The class is `MottonenStatePreparation`, reached as `qml.MottonenStatePreparation`; the module-level helpers are `gray_code`, `compute_theta`, `_get_alpha_y`, `_get_alpha_z`, `_apply_uniform_rotation_dagger` and `_uniform_rotation_dagger_ops`. Read at tag `v0.45.1`, released 2026-06-26.
No runtime, fidelity or hardware figure appears in the module. What it does execute is its own docstring: `>>> print(np.allclose(state, circuit(state)))` returns `True` on three wires, a device-level drawing of that same circuit is printed beside it, and `compute_decomposition`'s docstring runs a second two-wire example whose printed op list opens `[RY(tensor(1.5708, dtype=torch.float64), wires=['a']),`. The gate counts the file carries are declared, not measured: `_mottonen_resources(num_wires)` sets `n = 2**num_wires - 1` and returns `{qp.GlobalPhase: 1, qp.RY: n, qp.RZ: n, qp.CNOT: 2 * (n - 1)}`, attached with `qp.register_resources(_mottonen_resources, MottonenStatePreparation.compute_decomposition, exact=False)`. PennyLane documents that flag, in `pennylane/decomposition/decomposition_rule.py`, as "whether the resources are computed exactly (``True``, default) or estimated heuristically (``False``)" — it marks the count as a heuristic and states no direction for the error. Where the gap shows is the docstring's own drawn circuit: counted off that drawing here, the three-wire case draws seven `RY` and twelve `CNOT`, matching the declared and , but a single `RZ(1.57)` against a declared seven.
Qiskit `UCRYGate` / `UCRZGate` and the `UCGate` multiplexer
- Quantum circuits with uniformly controlled one-qubit gates
- Quantum Multiplexer Simplification for State Preparation
Qiskit ships the uniformly controlled rotation as a first-class library gate rather than only as a state-preparation subroutine, so it can be read as the construction itself. But the claim that this is what a software stack emits by default needs a date attached to it, because Qiskit's own state preparation stopped emitting it: the 1.2 release note records "Replacing the internal synthesis algorithm of :class:`~.library.StatePreparation` and :class:`~.library.Initialize` of Shende et al. by the algorithm given in :class:`~.library.Isometry` of Iten et al." At tag 2.5.2, `StatePreparation._define` branches three ways — from a label, from an integer, and otherwise into `_define_synthesis_isom`, the state-vector path, which builds `Isometry(self.params, 0, 0)`; that isometry disentangles one qubit at a time with `UCGate`, the multiplexed-single-qubit-unitary generalisation, and never constructs a `UCRYGate` or a `UCRZGate`. The two rotation gates remain in the library and remain used in-tree — `ExactReciprocalGate._define` appends `UCRYGate(angles)` directly, and `DiagonalGate._define` appends `UCRZGate(angles_rz)`.
`UCPauliRotGate` takes a "List of rotation angles " and an axis that "Must be either of ``"X"``, ``"Y"`` or ``"Z"``"; `UCRYGate` and `UCRZGate` are subclasses whose entire body is the one line that fixes the axis, `super().__init__(angle_list, "Y")`. `_dec_ucrot` first rewrites the angles in place with the recursion `_dec_uc_rotations`, whose base step is `_update_angles`, returning `(angle1 + angle2) / 2.0, (angle1 - angle2) / 2.0` — the pair's average and its half-difference — and documented as "Calculate the new rotation angles according to Shende's decomposition"; the module header credits the structure to "Emanuel Malvetti's semester thesis at ETH in 2018, which was supervised by Raban Iten and Prof. Renato Renner." It then places the CNOTs by a trailing-zero rule rather than by an explicit Gray code — the comment reads "Determine the index of the qubit we want to control the C-NOT gate. Note that it corresponds to the number of trailing zeros in the binary representation of i+1" — with the final iteration special-cased to `q_contr_index = len(q_controls) - 1`. A rotation is emitted only when `np.abs(angle) > _EPS` with `_EPS = 1e-10`, while `circuit.cx(...)` is appended on every one of the loop's `len(angles)` iterations, so the CNOT count does not thin out with the angles. For the X axis each CNOT is wrapped in `ry(np.pi / 2)` and `ry(-np.pi / 2)`, which the file explains as changing "the basis of the NOT operation, such that the decomposition of for uniformly controlled X rotations works correctly by symmetry with the decomposition of uniformly controlled Z or Y rotations". `UCGate` in `uc.py` is the general block-diagonal case, "The decomposition is based on Ref. [1]" — Bergholm et al. — with `_dec_ucg_help` pointing at "https://arxiv.org/pdf/quant-ph/0410066.pdf" and delegating the numerics to Rust through `from qiskit._accelerate import uc_gate`; it reuses the same trailing-zero CNOT rule. Since 2.0 it also carries `mux_simp`, default `True`, whose release note says it "enables the search for simplifications of Carvalho et al., implemented in :meth:`~.library.UCGate._simplify`. This optimization, enabled by default, identifies and removes unnecessary controls from the multiplexer, reducing the number of CX gates and circuit depth, especially in separable state preparation with :class:`~.library.Initialize`." Carvalho et al. confirm the same handover from their side: "The simplification proposed in this work is applied by default in the UCGate of qiskit version 2.0.0rc1."
No dataset — `UCPauliRotGate` takes an angle list, `UCGate` a list of unitaries. Neither module executes a circuit; both only construct one.
https://github.com/Qiskit/qiskit, Python, Apache License 2.0. `qiskit/circuit/library/generalized_gates/uc_pauli_rot.py` holds `UCPauliRotGate` with `_dec_ucrot`, `_dec_uc_rotations` and `_update_angles`; `ucry.py` and `ucrz.py` hold the axis-fixed subclasses `UCRYGate` and `UCRZGate`; `uc.py` holds `UCGate` with `_dec_ucg`, `_dec_ucg_help` and `_simplify`. The state-preparation path that no longer uses them is `qiskit/circuit/library/data_preparation/state_preparation.py` (`StatePreparation._define_synthesis_isom`) into `generalized_gates/isometry.py` (`Isometry`). The in-tree consumers are `qiskit/circuit/library/arithmetic/exact_reciprocal.py` for `UCRYGate` and `generalized_gates/diagonal.py` (`DiagonalGate._define`) for `UCRZGate`. Read at tag `2.5.2`, released 2026-08-13; `uc_pauli_rot.py`, `ucry.py` and `ucrz.py` are each headed "(C) Copyright IBM 2020".
The modules themselves report no measurement. The only quantitative statement on this path is the 1.2 release note's claim for the algorithm that displaced the multiplexer route: "The new algorithm reduces the number of CX gates and the circuit depth by a factor of 2." The 2.0 note claims `mux_simp` reduces CX count and depth but attaches no figure to it, and Carvalho et al.'s own figures are plotted rather than tabulated.
qclib `TopDownInitialize` and the `multiplexor` it calls
- Transformation of quantum states using uniformly controlled rotations
- Configurable sublinear circuits for quantum state preparation
qclib is a Qiskit-based library whose stated focus is this problem — "Qclib is a quantum computing library implemented using qiskit. The focus of qclib is on preparing quantum states, but it is not limited to that." `TopDownInitialize` is its direct realisation of this method, and it says so without prose: the module docstring of `topdown.py` is nothing but the two URLs it implements, `https://arxiv.org/abs/quant-ph/0407010` and `https://arxiv.org/abs/2108.10182`, and the class docstring repeats both under the heading "Top-down state preparation". It is one of the sixteen initialisers exported from `qclib/state_preparation/__init__.py`, so the library carries the multiplexor route as one option among many.
On its default `lib='qclib'` path — the alternative, `opt_params={'lib': 'qiskit'}`, returns a circuit built by `circuit.initialize(self.params)` and touches none of the machinery below — `_define_initialize` builds an amplitude tree and then an angle tree before it emits any gate: `state_tree = state_decomposition(self.num_qubits, data)`, `angle_tree = create_angles_tree(state_tree)`, then `add_register(circuit, angle_tree, 0)` and `top_down(angle_tree, circuit, 0)`. `state_decomposition` stores each leaf as `abs(k.amplitude)` and `cmath.phase(k.amplitude)` and folds parents upward as `mag = math.sqrt(nodes[k].mag ** 2 + nodes[k + 1].mag ** 2)` and `arg = (nodes[k].arg + nodes[k + 1].arg) / 2`. `create_angles_tree` then reads each node's pair of angles off the child-to-parent ratio and the phase difference: `mag = state_tree.right.mag / state_tree.mag`, `arg = state_tree.right.arg - state_tree.arg`, `angle_y = 2 * math.asin(mag)` and `angle_z = 2 * arg` — with `angle_y` set to or outright, rather than through `asin`, when `mag` falls below or above ("Avoid out-of-domain value due to numerical error"). `top_down` emits at most one Ry layer and one Rz layer per tree level, each behind its own `if any(...)` guard so that an all-zero layer is skipped, controlled on the qubits of the levels already visited: `angles_y = [node.angle_y for node in target_nodes]`, `ucry = multiplexor(RYGate, angles_y, last_control=not any(angles_z))`, and the Rz layer appended as `ucrz.reverse_ops()`. That reversal is a deliberate saving the file states outright: "If both multiplexors are used (RY and RZ), we can save two CNOTs. That is why the RZ multiplexor is reversed." The global phase is corrected by default with `circuit.global_phase += sum(np.angle(self.params)) / len(self.params)`. The multiplexer itself is the module-level function `multiplexor` in `qclib/gates/ucr.py` — `tree_walk.py` imports and calls that function, never the `Ucr` gate class in the same file — and its docstring names a different source than the state-preparation module does, "Synthesis of Quantum Logic Circuits" with the URL `https://arxiv.org/abs/quant-ph/0406176`. It recurses by applying `angle_multiplexor = np.kron([[0.5, 0.5], [0.5, -0.5]], np.identity(2 ** (n_qubits - 2)))` to the angle list, calling itself on each half with `last_control=False`, reversing the second half's operations, and composing one `c_gate()` between the two — a parameter `c_gate: Union[Type[CXGate], Type[CZGate]] = CXGate`, left at its `CXGate` default on the `top_down` path — with the closing one emitted only `if last_control`. The comment gives the cancellation this relies on: "Figure 2 from Synthesis of Quantum Logic Circuits: The recursive decomposition of a multiplexed Rz gate. The boxed CNOT gates may be canceled." The `Ucr` gate class in the same file wraps `multiplexor` and offers `'multiplexor'` (its default), `'mcg'` and `'auto'` as build methods, plus a `simplify` path, off by default, which groups control strings whose angles agree to `np.isclose(..., atol=0.0, rtol=1e-07)`, minimises each group's control patterns into don't-care strings with SymPy's `simplify_logic(..., form='dnf', force=True, deep=False)`, and then searches for separability — "qubits not used after simplification" — before choosing between the multiplexer and multi-controlled gates.
No fixed dataset. The README's comparison runs "the same random 15-qubit state", which the notebook it links builds unseeded as `np.random.rand(2 ** n_qubits) + np.random.rand(2 ** n_qubits) * 1j` divided by its norm. The README's own quick-start example is a 3-qubit vector seeded with `np.random.RandomState(42)`, but it calls `LowRankInitialize`, not `TopDownInitialize`.
https://github.com/qclib/qclib, Python on top of Qiskit, Apache License 2.0. `qclib/state_preparation/topdown.py` holds `TopDownInitialize`; `qclib/gates/ucr.py` holds the `multiplexor` function and the `Ucr` gate; the tree helpers are `qclib/state_preparation/util/state_tree_preparation.py` (`state_decomposition`), `angle_tree_preparation.py` (`create_angles_tree`), `tree_register.py` (`add_register`) and `tree_walk.py` (`top_down`, and a `bottom_up` alternative that swaps the multiplexers for plain `ry`/`rz` plus a `cswap` cascade in `_apply_cswaps`). The repository publishes no tags; read at commit `cf06981` on `master`, dated 2026-04-07.
The README carries a table for one random 15-qubit state, transpiled by its linked notebook to `basis_gates=['u', 'cx']` at `optimization_level=0`. Two rows price the Qiskit routes — `multiplexor | qiskit | 15 | 65504 | 131025` and `isometry | qiskit | 15 | 32752 | 65505`, columns qubits, cnots, depth — and qclib's own uniformly-controlled-gate initialiser matches the second at `ucg | qclib | 15 | 32752 | 65505`. Three limits on reading that table: it has no `top-down` row, so it does not price `TopDownInitialize` itself; its `multiplexor` row is produced by `circuit_mul.initialize(state)`, which is the Qiskit call whose internal algorithm Qiskit 1.2 replaced; and neither the README nor the notebook states which Qiskit version produced the numbers.
What it needs
Nothing below this — it bottoms out here.
Other ways to fill the same slot
Different approaches
- Grover-Rudolph bisection preparation
Prepare a discrete approximation to a probability density by recursive bisection: at layer a uniformly controlled rotation splits each current interval's probability mass between its two halves, so only rotation layers are needed.
- Sparse state preparation
When only of the amplitudes are nonzero, build the computational-basis strings directly instead of rotating through the whole binary tree, so the cost tracks and rather than .
In the Atlas
No record in the Atlas covers this yet. The catalogue is circuits and primitives; this part of the literature is not in it.