About this map
Sections
What this is
Quantum algorithms are not written from scratch. They are assembled from a small number of reusable steps, and almost every published method is a different route through the same handful of them.
This is a map of those routes. Circles are the things an algorithm can be holding. Lines are the steps that carry you from one to the next. A method is a path across.
Nothing here is generated. Every line was read out of a paper and checked against it.
How to read it
- Something you can hold — a state, a matrix, a circuit, an answer.
- The same, in the middle of a step you have opened.
- A step. Someone has published a way through it.
- A step whose way through has not been pinned to one method.
- A step nothing published fills yet.
- A step you have opened. What is drawn inside it is how it was done.
- There is a record in the repository for this one.
How to move around
- Two fingers move the map. Pinch to zoom, or hold ctrl and scroll.
- Click a step to open it in place — everything else stays where it is.
- Click a name to read the full record without leaving the map.
- Arrow keys move, plus and minus zoom, zero puts it back.
What a line is claiming
A solid line means a paper puts those two steps together and we have the citation. A long-dashed line means the route is recorded but no single method has been named for that step. A short-dashed line means nothing published fills it — the step is real, the way through is not written yet.
A count after a step's name — ×T/h, ×O(κ) — means the route walks that step that many times rather than once. It is the source's own symbol, and the card says what it stands for and what one turn costs. A step with no count is a step no source we read said is repeated, which is not the same as one taken once.
A line drawn nested under another, on the soft shaded band behind it, is a narrower version of the line above it: the same construction, re-analysed or re-tuned, filling the same step. It is why two lines can draw the identical interior and still be two entries. Lines outside the band are alternatives to their neighbours, not versions of them.
The map does not hide the gaps. An empty step is drawn as an empty step.
What is not here yet
The map covers the algorithm literature. The repository covers circuits and primitives. They overlap less than you would expect, and where a method has no record we say so on its page rather than leaving the space blank.
Where something named here does have a record, its name links straight to it.
Method
Block-encoding from a Pauli decomposition
Write over Pauli strings; PREPARE loads amplitudes proportional to into an ancilla register, SELECT applies the controlled Pauli strings, and PREPARE unprepares, leaving in the block flagged by the all-zeros ancilla. This is the input model chemistry and lattice Hamiltonians supply for free.
Open the full recordFills the slot: Block-encode a matrix
Efficient only when the number of Pauli terms is and stays small. It does not rescue a general dense matrix: an arbitrary matrix has Pauli coefficients, so this route is a win only where the physics hands you a short decomposition. Note also that a decomposition with many terms of comparable magnitude is expensive even when itself is well-conditioned, because the term count enters the cost through .
An access model for — sparse-access oracles, a Pauli or LCU decomposition, a purification, or an explicit arithmetic description — plus a target precision .
Matrix you can query → Block-encodingsandwich SELECT between PREPARE calls
The block-encoding is . For , the first loads the coefficients onto an ancilla, , and applies the indexed term to the system. assumption: each must be self-inverse — a Pauli string is — because "the self-inverse nature of the operators implies that they are both Hermitian and unitary, which means they can be applied directly to a quantum state". Projecting the ancilla back collapses the sandwich exactly: . approximation: the general statement replaces by any -state-preparation-pair , which need only reproduce the coefficients to , so an imperfectly synthesised preparation degrades the block before any per-term error is counted.
approximationassumption
A unitary on qubits, its subnormalization , and its ancilla/flag count . Because , Gilyén, Su, Low and Wiebe's Definition 43 forces .
- 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.
The slot receives one classical vector and one normalisation: the coefficients of , which Babbush et al. take "real and non-negative, with any phases included in the ", and — their one-norm, never the two-norm. The returned amplitudes normalise against that , which the finished block-encoding inherits as its subnormalisation and which the same authors warn "turns out to have significant ramifications for the overall algorithm complexity". Back must come a circuit on "an empty ancilla register of qubits" carrying amplitude on index — and its inverse too: the preparation is spent once forward, once as its adjoint, per block-encoding. approximation: the ancilla need not come back holding only the index. The same paper's Section III D generalises the requirement to , with "an unspecified junk register entangled with " — the loosening that permits subsampling the coefficient oracle.
approximation
given a decomposition A = Sum_j c_j P_j over Pauli strings
# the input model chemistry and lattice Hamiltonians supply for free
# -- efficient only when the number of Pauli terms is poly(n) and ||c||_1
# stays small. It does not rescue a general dense matrix: an arbitrary
# 2^n x 2^n matrix has 4^n Pauli coefficients, so this route is a win only
# where the physics hands you a short decomposition
PREPARE: load amplitudes proportional to sqrt(|c_j|) into an ancilla
register
# loading the coefficient vector is state preparation: hand it to the
# layer below
SELECT: apply the controlled Pauli strings
PREPARE^dagger: unprepare
# A/||c||_1 is left in the block flagged by the all-zeros ancilla
# Gilyen, Su, Low and Wiebe Lemma 52: with (P_L, P_R) a
# (beta, b, epsilon_1)-state-preparation-pair for the coefficient vector and
# each U_j an (alpha, a, epsilon_2)-block-encoding, the result is an
# (alpha*beta, a+b, alpha*epsilon_1 + alpha*beta*epsilon_2)-block-encoding,
# using a single use each of SELECT, P_R and P_L^dagger
# their Definition 51 requires beta >= ||y||_1; for a Pauli decomposition
# that is the lambda = ||c||_1 of the chemistry literature. So a decomposition
# with many terms of comparable magnitude is expensive even when A itself is
# well-conditioned -- the term count enters the cost through ||c||_1
# b = ceil(log2 L) for L terms is implied by Definition 51's requirement
# 2^b >= L; Lemma 52 does not state it, and this listing does not claim it as
# the lemma's
# Babbush et al. give a SELECT/PREPARE compilation with T-gate complexity
# O(N + log(1/epsilon)) for N orbitals, enabling qubitized phase estimation
# with optimal query complexity O(lambda/epsilon)Gilyén, Su, Low and Wiebe (arXiv:1806.01838) Lemma 52: with a -state-preparation-pair for the coefficient vector and each an -block-encoding, the result is an -block-encoding using a single use each of SELECT, and , with required by their Definition 51 — for a Pauli decomposition this is the of the chemistry literature. Babbush et al. give a SELECT/PREPARE compilation with T-gate complexity for orbitals, enabling qubitized phase estimation with optimal query complexity . The flag-qubit count for terms is implied by Definition 51's requirement ; Lemma 52 does not state it.
None found yet.
None found yet.
Qualtran's `LCUBlockEncoding` and `SelectBlockEncoding` bloqs
- Expressing and Analyzing Quantum Algorithms with Qualtran
Matthew P. Harrigan, Tanuj Khattar, Charles Yuan, Anurudh Peduri, Noureldin Yosri, Fionn D. Malone, Ryan Babbush, Nicholas C. Rubin · 2024
About
Qualtran is Google Quantum AI's library for writing fault-tolerant algorithms down and counting what they cost: its abstract presents it as "an open-source library for representing and analyzing quantum algorithms" and says that "Using appropriate abstractions and data structures, we can simulate and test algorithms, automatically generate information-rich diagrams, and tabulate resource requirements". Block encoding is Section III E, and that section's definition is the record's: "the (s + a)-qubit unitary B[A] is a (α, a, ϵ)-block encoding of A if" , "where α is a normalization constant chosen such that ∥A∥/α ≤ 1, and ϵ ≥ 0 is the precision of the encoding" — the norm is the paper's, and without it the left-hand side would be an operator compared to a scalar. The LCU route is the second of three primitives the section builds — "First, it provides bloqs to construct block encodings from primitive elements such as unitaries, LCU SELECT/PREPARE oracles, and sparse access matrices" — and it is named outright: "any linear combination of unitaries as specified by a pair of SELECT and PREPARE oracles defines a block encoding of A, which can be created by Qualtran's LCUBlockEncoding bloq". What the paper offers for those primitives is a parameter table rather than a run: Table III's caption says of α, a and ϵ that "Rather than manually reasoning about these performance parameters, users can compute them automatically by invoking the bloq implementations".
Methods
The module holds two classes, and they differ in exactly the place this record's `summary` names: which ancilla state flags the block. `LCUBlockEncoding` is the sandwich — its docstring builds "the standard block encoding from an LCU as" and extracts the Hamiltonian by , the all-zeros flag. Its `build_composite_bloq` is that product literally: it adds `self.prepare`, then `self.select`, then `self.prepare.adjoint()`. `SelectBlockEncoding` is the variant that does not unprepare — alone, extracted by against . The contrast is stated in `LCUBlockEncoding`'s docstring, from its own side: "This differs from the `SelectBlockEncoding` which uses Prepare for the signal state, while here it is the identity operator." In code that is `signal_state`, which returns `self.prepare` on `SelectBlockEncoding` and `PrepareIdentity(self.selection_registers)` on `LCUBlockEncoding`. Both classes take the subnormalisation from the preparation rather than computing it: `alpha` returns `self.prepare.l1_norm_of_coeffs`, whose base-class docstring reads "Sum of the absolute values of coefficients . For LCU Hamiltonians, this is usually referred to as in texts" — the record's . Both docstrings state "The ancilla register is at least of size ". The precision slot is not filled: `epsilon` returns `0.0` on both, under a comment naming Qualtran issue 1247 as the open work, so that zero is a placeholder and not a claim that the encoding is exact. On the ancilla convention the file is explicit: "In our implementations we typically split the ancilla registers into selection registers (i.e. the registers above) and junk registers which are extra qubits needed by state preparation but not controlled upon during SELECT." A caller pinning an older Qualtran meets a trap here, because the two names were swapped: at tag v0.4.1 the class called `LCUBlockEncoding` is the SELECT-only one and the sandwich is called `LCUBlockEncodingZeroState`, while from v0.5.0 onward the sandwich is `LCUBlockEncoding` and the SELECT-only one is `SelectBlockEncoding`. The swap landed on 2024-08-08 in pull request 1268, "Rename LCU block encodings", and `LCUBlockEncodingZeroState` no longer exists anywhere in the repository. The same name denotes different objects on either side of that commit — though not on either side of the paper, which was submitted on 6 Sep 2024, four weeks after the rename.
Data
No molecule and no matrix: the inputs are a SELECT bloq and a PREPARE bloq, and the worked instances are lattice models. All four `bloq_example` functions in the file build the same one, commented "# 3x3 hubbard model U/t = 4" with `dim = 3`, `U = 4`, `t = 1`, from `SelectHubbard(x_dim=dim, y_dim=dim)` and `PrepareHubbard(x_dim=dim, y_dim=dim, t=t, u=U)` in `qualtran.bloqs.chemistry.hubbard_model.qubitization`; two of them wrap those in `BlackBoxSelect` and `BlackBoxPrepare` first. The paper's Fig. 10 runs a larger one, `n, t_hubbard, u_hubbard = 4, 1, 4`, introduced as "an example snippet for simulating the 2D Hubbard Hamiltonian [9, Sec. 5] using HamiltonianSimulationByGQSP". Its reference [9] is "R. Babbush, C. Gidney, D. W. Berry, N. Wiebe, J. McClean, A. Paler, A. Fowler, and H. Neven, Encoding electronic spectra in quantum circuits with linear t complexity, Phys. Rev. X 8, 041015 (2018)" — the second of this record's own two citations.
Code
`qualtran/bloqs/block_encoding/lcu_block_encoding.py` in https://github.com/quantumlib/Qualtran — Python, Apache License 2.0, header "Copyright 2023 Google LLC"; 376 lines on `main` as read on 2026-08-26, latest tag v0.7.0. The two block-encoding classes are `SelectBlockEncoding` and `LCUBlockEncoding`, both `attrs.frozen` subclasses of `BlockEncoding` from `qualtran.bloqs.block_encoding.block_encoding_base`, each taking `select: Union[BlackBoxSelect, SelectOracle]` and `prepare: Union[BlackBoxPrepare, PrepareOracle]`; `LCUBlockEncoding` additionally takes `control_val: Optional[int] = None`. Neither class is Pauli-specific — the Pauli case arrives through the SELECT slot, and the SELECT built for Pauli strings is a separate bloq, `SelectPauliLCU` in `qualtran/bloqs/multiplexers/select_pauli_lcu.py`, whose module docstring is "Bloqs for applying SELECT unitary for LCU of Pauli Strings" and which "uses the unary iteration scheme to apply `select_unitaries[selection]` to `target` controlled on the single-bit `control` register", for `select_unitaries` given as `cirq.DensePauliString`s.
Results
The repository itself reports no number for these bloqs: the checked-in `lcu_block_encoding.ipynb` calls `show_call_graph` and `show_counts_sigma` on the 3x3 Hubbard example, but every cell in the committed notebook has an empty `outputs` list, so no count is stored there. The published numbers are in the paper's Fig. 10, and they are the cost of a whole simulation rather than of the block encoding alone: the snippet imports `LCUBlockEncoding` from `qualtran.bloqs.block_encoding.lcu_block_encoding`, builds `block_enc = LCUBlockEncoding(select=select, prepare=prepare)`, wraps it as `walk_op = QubitizationWalkOperator(block_enc)` and passes that to `HamiltonianSimulationByGQSP(walk_op, t=t, precision=eps)` with `t, eps = 5, 1e-5`. The top box of that figure gives Qubits 60, CSwaps 37930, Ands 139476, Cliffords 715012, Rotations 14905, Measurements 139476, captioned "Hamiltonian simulation of 2D Hubbard using GQSP for time t = 5 and precision 1.0 × 10−5, and its corresponding gate costs". Three boxes hang below it, each on an edge labelled 1: `Adjoint(subbloq=PrepareHubbard)` and `PrepareHubbard`, carrying identical figures of Qubits 16, CSwaps 9, Ands 7, Cliffords 50, Rotations 4, Measurements 7, and `GeneralizedQSP` at Qubits 58, CSwaps 37912, Ands 139462, Cliffords 714912, Rotations 14897, Measurements 139462. Those two `PrepareHubbard` boxes are not the block encoding's own PREPARE and PREPARE-dagger: they belong to `HamiltonianSimulationByGQSP`, whose `build_composite_bloq` carries the comment "# PREPARE, GQSP, PREPARE†" around its `bb.add_d(self.gqsp, **soqs)` and whose docstring reads "The obtained GQSP operator can then be used with two calls to the PREPARE oracle to simulate the hamiltonian". The two edges labelled 1 are that same class's `counts[Always(self.walk_operator.prepare)] += 1` and `counts[Always(self.walk_operator.prepare.adjoint())] += 1`. The block encoding's own PREPARE and SELECT sit inside the repeated walk operator and are therefore already counted inside the `GeneralizedQSP` box. The paper's Table III, which tabulates α, ancilla count and ϵ for the block encodings, has no row for `LCUBlockEncoding`; its rows are `Unitary`, `SparseMatrix`, `TensorProduct`, `Product`, `Phase`, `LinearCombination` and `ChebyshevPolynomial`. The LCU α is stated in code instead, as `prepare.l1_norm_of_coeffs`.
- Expressing and Analyzing Quantum Algorithms with Qualtran
pyLIQTR's `PauliStringLCU` encoding
About
pyLIQTR is MIT Lincoln Laboratory's circuit-and-resource-estimation library — "a python library for building quantum circuits derived from quantum algorithms and generating Clifford+T resource estimates", "built extensively atop `cirq` & the recent release of `qualtran`". Its architecture separates the Hamiltonian from the way it is encoded: `pyLIQTR.ProblemInstances` "provides an easy interface (through `pyLIQTR.ProblemInstances.getInstance`) to generate Hamiltonians that capture various physical models of interest", and `pyLIQTR.BlockEncodings` "provides an easy interface (through `pyLIQTR.BlockEncodings.getEncoding`) to generate BlockEncodings that encode the action of a ProblemInstance through various different encodings". The Pauli decomposition is the first member of the `VALID_ENCODINGS` enum, `PauliLCU = 1`, but nothing makes it a default: `getEncoding(encoding, instance=None, prepare_type=None, **kwargs)` gives `encoding` no default value, so a caller must name one. Of the enum's seven members only five are reachable through that function — `PauliLCU`, `FermiHubbardSquare`, `LinearT`, `DoubleFactorized` and `FirstQuantized`, with `Fermionic` and `CarlemanLinearization` falling through to `raise NotImplementedError("Sorry :'(")` — and of those five it is the only one not tied to a particular Hamiltonian family, so it is the route a caller takes when the only structure available is a list of Pauli terms.
Methods
`PauliStringLCU` is the concrete Pauli case of the record's construction, and the two quantities the record names appear in it by name. Term padding comes first, because the index register must be a power of two: `self.n_pad = 2**(int(np.ceil(np.log2(self.n_terms)))) - self.n_terms` and `self.n_tot = self.n_terms + self.n_pad`, after which `selection_bitsize = int(np.ceil(np.log2(self.n_tot)))` — the record's , computed on the padded count. The amplitudes are the record's: `alphas = [np.sqrt(np.abs(t.coefficient)) for t in self.getTerms]`, the square roots of the absolute coefficients. SELECT is delegated to Qualtran — `from qualtran.bloqs.multiplexers.select_pauli_lcu import SelectPauliLCU` — and instantiated as `SelectPauliLCU(selection_bitsize=selection_bitsize, target_bitsize=self.PI.n_qubits(), select_unitaries=self.getTerms, control_val=self._control_val)`. PREPARE is where the class offers a choice. With `prepare_type=None` it uses pyLIQTR's own `prepare_pauli_lcu(selection_bitsize=selection_bitsize, alphas=alphas)`, which wraps `QSP_Prepare` and declares a cost analytically rather than by decomposition: `TComplexity(t=0, clifford=2**(d-1), rotations=2**(d))` for `d = selection_bitsize`, under a comment reading "Note the analytical expression from https://arxiv.org/pdf/1905.10724.pdf A.3.2 seemed to be off by a factor of 1/2". With `prepare_type='AS'` it uses Qualtran's `StatePreparationAliasSampling.from_lcu_probs(lcu_probabilities=[...], probability_epsilon=probability_eps)` with `probability_eps=0.002` by default — and that branch carries a restriction the caller must read, because it is a warning and not an error: "Alias sampling preparation with negative coefficients is not supported yet. Circuits and estimates will assume positive coefficients." The circuit is assembled by the parent class `BlockEncoding_select_prepare`, whose `decompose_from_registers` yields `self._prepare_gate`, then the select gate, then `cirq.inverse(self._prepare_gate...)`, with the outer two gated by constructor flags (`do_prepare=True`, `do_prepare_inverse=True`) and the middle one reversible by a third (`invert_select=False`, which yields `cirq.inverse(self._select_gate...)` when set) — so the sandwich is the default but every stage can be switched. Its `_t_complexity_` charges `2*prepare_cost + select_cost` only when both prepare flags are set and `self._inverse_prepare_workaround is None`; where that attribute is set the doubled term is replaced by `t_complexity(self._inverse_prepare_workaround)`, and where either flag is off the total is `prepare_cost + select_cost`. For `PauliStringLCU` the doubled branch is the one taken: the parent's `__init__` sets `self._inverse_prepare_workaround = None` and this subclass never overrides it.
Data
Lattice spin models, supplied as a `ProblemInstance` rather than as a matrix. The fixture the encoding's own test suite uses is a 3-by-3 Heisenberg model on a square lattice: `N=3`, `J_x = J_y = 0.5`, `J_z = 1.0`, `h_x = 1.0`, `h_y = 0.0`, `h_z = 0.5`, built by `getInstance("Heisenberg", shape=(N,N), J=(J_x,J_y,J_z), h=(h_x,h_y,h_z), cell=SquareLattice)`.
Code
`src/pyLIQTR/BlockEncodings/PauliStringLCU.py` in https://github.com/isi-usc-edu/pyLIQTR — Python, 78 lines, class `PauliStringLCU(BlockEncoding_select_prepare)`, reached through `getEncoding(instance=..., encoding=VALID_ENCODINGS.PauliLCU, prepare_type=...)` in `src/pyLIQTR/BlockEncodings/getEncoding.py`, where `VALID_ENCODINGS` is an `Enum` in the package `__init__.py` with `PauliLCU = 1`. The default PREPARE lives in `src/pyLIQTR/circuits/operators/select_prepare_pauli.py` and the sandwich in `src/pyLIQTR/BlockEncodings/BlockEncoding.py`. Distributed on PyPI as `pyLIQTR`, version 1.4.2 (uploaded 2026-01-05), latest tag v1.4.2. The licence is recorded in three different strings and they disagree. The repository's `LICENSE.txt` is the MIT License text under "Copyright (c) 2024 Massachusetts Institute of Technology", which is what the GitHub API reports; every source file read here opens with "SPDX-License-Identifier: BSD-2-Clause", as does the README; and PyPI's metadata field records the licence as the string "BDS-2", which matches no SPDX identifier. `CITATION.cff` carries both of the first two on its own — the SPDX line sits inside its `message:` block, while its top-level machine-readable key reads `license: MIT` — so it is not evidence for either side. There is no paper: `CITATION.cff` says "Please use this DOI number reference, published on https://zenodo.org. when citing the software: https://doi.org/10.5281/zenodo.7221272".
Results
Nothing numerical is asserted. The six tests in `src/pyLIQTR/BlockEncodings/tests/test_PauliLCU.py` run the 3-by-3 Heisenberg fixture through both PREPARE branches and check only that the machinery executes: two check "gate decomposition existence" by asserting `[operation] != cirq.decompose_once(operation)` and `[operation] != cirq.decompose(operation)`; two check "qasm printing functionality" with `assert qasm is not None`; and the two resource tests are documented as "Tests estimate_resources executes without error" and call `resources = estimate_resources(encoding.circuit)` without asserting anything about the value returned. So this artefact demonstrates that the encoding decomposes to gates and can be costed, and reports no Clifford+T count of its own for that instance.
PennyLane's `qml.PrepSelPrep` template
About
Xanadu's PennyLane ships the sandwich as a single template, and its release note frames it as a convenience over three calls a user was already making: "A new template called `qml.PrepSelPrep` has been added that implements a block-encoding of a linear combination of unitaries", which "acts as a nice wrapper for having to perform `qml.StatePrep`, `qml.Select`, and `qml.adjoint(qml.StatePrep)` in succession, which is quite common in many quantum algorithms (e.g., LCU and block encoding)". The module docstring is the same sentence: "Implements a block-encoding of a linear combination of unitaries." Its input is the decomposition already assembled — the `lcu` argument is typed "Union[.Hamiltonian, .Sum, .Prod, .SProd, .LinearCombination]: The operator written as a linear combination of unitaries" — so it takes over exactly where a Pauli decomposition leaves off.
Methods
Three functions do the work, and the first discharges a condition this record states rather than assumes. `_get_new_terms`, documented "Compute a new sum of unitaries with positive coefficients", takes `coeffs, ops = lcu.terms()`, reads `angles = math.angle(coeffs)`, and returns `math.abs(coeffs)` together with `new_ops = [prod(op, GlobalPhase(-angle, wires=op.wires)) for angle, op in zip(angles, ops, strict=True)]` — every phase, a negative Pauli coefficient included, is moved off the amplitude and onto the operator, so that the square roots loaded next are real. That is the condition the record's `state-preparation` hop puts on the slot, where Babbush et al. take the coefficients "real and non-negative, with any phases included in the ": the template performs in code the move the record requires. Second, `compute_decomposition` returns a single `change_op_basis(AmplitudeEmbedding(math.sqrt(coeffs), normalize=True, pad_with=0, wires=control), Select(ops, control, partial=True))`: amplitudes proportional to the square roots of the absolute coefficients, zero-padded to fill the control register, and a `Select` over the phase-corrected operators. Third, `change_op_basis(compute_op, target_op, uncompute_op=None)` is documented as "the product of the operators provided; particularly a compute-uncompute pattern", with "`None` corresponds to `uncompute_op=qp.adjoint(compute_op)`" — so the unprepare is the adjoint of the same preparation, not a separately specified circuit. The template is honest about a limit that the record does not raise: it sets `grad_method = None` and opens its docstring with a warning, "Derivatives of this operator are not always guaranteed to exist." It also refuses an overlapping register outright, raising `ValueError("Control wires should be different from operation wires.")`.
Data
No dataset — the worked instance is a two-term Pauli operator on one wire, `lcu = qp.dot([0.3, -0.1], [qp.X(2), qp.Z(2)])` with `control = [0, 1]`, run on `qp.device("default.qubit")`, PennyLane's state-vector simulator. The docstring names no hardware device.
Code
`pennylane/templates/subroutines/prepselprep.py` in https://github.com/PennyLaneAI/pennylane — Python, Apache License 2.0, header "Copyright 2018-2024 Xanadu Quantum Technologies Inc.", 245 lines on `master` as read on 2026-08-26. The class is `PrepSelPrep(Operation)`. Its signature depends on which version you hold: the stable API documentation page prints `PrepSelPrep(lcu, control, id=None)` and the source at tag v0.45.1 agrees, `def __init__(self, lcu: SymbolicOp | CompositeOp, control: WiresLike, id=None) -> None:`, while on `master` that trailing argument is gone — `def __init__(self, lcu: SymbolicOp | CompositeOp, control: WiresLike) -> None:` — so `id=` is accepted by the released version and rejected by the development one. The pieces it composes come from the same library: `Select` from `.select`; `StatePrep`, `GlobalPhase`, `prod` and `change_op_basis` from `pennylane.ops`; and `AmplitudeEmbedding` from `pennylane.templates.embeddings`. It first shipped in v0.38.0, added by pull request 5756 and pull request 5987; v0.39.0 then reused it elsewhere, its changelog recording that "The decomposition for `qml.Qubitization` has been improved to use `qml.PrepSelPrep`."
Results
The docstring runs the check the record's `summary` describes and prints both halves of it. Taking the circuit's own matrix, `print(matrix_psp.real[0:2, 0:2])` gives `[[-0.25 0.75]` / `[ 0.75 0.25]]`, and the same array comes back from the operator divided by the one-norm of its coefficients, `qp.matrix(lcu).real / sum(abs(np.array(lcu.terms()[0])))` — the top-left block holds the operator rescaled by , which for the quoted coefficients `[0.3, -0.1]` is 0.4. The v0.38.0 release note reports a second check, of the template against the three explicit calls it replaces: applying `qml.PrepSelPrep(lcu, control)` and then `qml.adjoint(prep_sel_prep)(alphas, unitaries)`, where `prep_sel_prep` is written out as `qml.StatePrep`, `qml.Select` and `qml.adjoint(qml.StatePrep)`, returns `np.round(circuit(...), decimals=2)` equal to `tensor([1.+0.j -0.+0.j -0.+0.j -0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j])` — the identity, which is the sense in which the two are the same circuit. That check is run on a different operator from the docstring's, and the difference is the load-bearing one: its coefficients are `coeffs = [0.3, 0.1]`, both non-negative, and the manual route takes `alphas = (np.sqrt(coeffs) / np.linalg.norm(np.sqrt(coeffs)))` with no phase correction at all. On the docstring's `[0.3, -0.1]` the manual route would take the square root of a negative number, while `PrepSelPrep` survives it, precisely because of the `_get_new_terms` phase move above. The equivalence is reported, and holds as reported, for non-negative coefficients. Both are exact-simulator checks on a two-term operator; neither is a resource count, and the module reports none.
Linear combination of unitaries · Qiskit
From the repository — run, not written up from a paper · native
About
A block-encoding primitive that turns a weighted sum of unitary operations into one larger unitary circuit.
Methods
None found yet.
Data
None found yet.
Code
Qiskit
Results
Two-term PREPARE–SELECT–unprepare circuit checked on a one-qubit target · success branch and coefficient normalization recorded
- Linear combination of unitaries
A block-encoding primitive that turns a weighted sum of unitary operations into one larger unitary circuit.
References
- Quantum singular value transformation and beyond: exponential improvements for quantum matrix arithmetics
András Gilyén, Yuan Su, Guang Hao Low, Nathan Wiebe · 2018
- Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity
Ryan Babbush, Craig Gidney, Dominic W. Berry, Nathan Wiebe, Jarrod McClean, Alexandru Paler, Austin Fowler, Hartmut Neven · 2018
Where the routes meet
11 problems nothing else needs — the places a reader arrives. Open a line to see what is recorded inside it, or click its name to go there.
13 lines have something recorded inside that you have not opened.
Of the routes that have been taken apart, 15 are built entirely from named slots, 15 hand off part of the work and finish the rest themselves, and 20 are one undivided act. None of the three is a defect; they are different things to reuse.
Every line on this figure, in words
The lines on this figure
Solve a nonlinear ODE dy/dt = F(y)
- Embed a nonlinear system into a linear one — opens into 6 · a way across — click it to open it here
- Solve a linear ODE du/dt = A(t)u + b(t) — opens into 9 · a way across — click it to open it here
- Choose a time discretization or propagator approximation → Quantum linear solve — open
- Choose a time discretization or propagator approximation — opens into 6 · a way across — click it to open it here
- Quantum linear solve — opens into 5 · a way across — click it to open it here
- Simulate Hamiltonian evolution → Estimate an observable — open
- Simulate Hamiltonian evolution — opens into 3 · a way across — click it to open it here
- Estimate an observable — opens into 4 · a way across — click it to open it here
Estimate an excited-state energy
- Variational quantum deflation — opens into 3 · a way across — click it to open it here
- Subspace-search variational eigensolver — opens into 3 · a way across — click it to open it here
- Quantum subspace expansion
- Quantum equation of motion
- Folded-spectrum variational eigensolver — opens into 3 · a way across — click it to open it here
- Penalty-constrained variational eigensolver — opens into 3 · a way across — click it to open it here
- Multistate contracted variational eigensolver — opens into 3 · a way across — click it to open it here
Every step you can open
1 of these have an object recorded in the middle; the rest open into the methods that fill them.
- Solve a nonlinear ODE dy/dt = F(y)
- Replace a spatial domain with a finite grid
- Discretize a PDE into one linear system
- Embed a nonlinear system into a linear one
- Solve a linear ODE du/dt = A(t)u + b(t)
- Recast a non-Hermitian generator as Hamiltonian evolution
- Choose a time discretization or propagator approximation
- Quantum linear solve
- Matrix function
- QSP phase factors
- Polynomial approximation
- Block-encode a matrix
- Prepare an input state
- Amplify a success branch
- Simulate Hamiltonian evolution
- Estimate an observable
- Compile a circuit to a specific device
- Satisfy the hardware connectivity constraint
- Approximate a continuous rotation in a discrete gate set
- Recover a noiseless expectation value by post-processing
- Build logical qubits at a target logical error rate
- Estimate a Hamiltonian's ground-state energy
- Choose a parameterised trial state
- Minimise the objective over the parameters
- Estimate an excited-state energy
- Measure what the machine can actually do
- Recover the period of a periodic function
- Estimate the eigenphase of a unitary
- Find the item a check accepts
- Walk a graph to the vertex you want
- Search a cost Hamiltonian for the assignment it minimises
What is on this map, counted
What is here, counted
147 nodes — 31 slots and 116 methods.
76 of the 147 link to a record in the Atlas, between them naming 89 records. The rest name papers and nothing else: this graph describes work the catalogue has not got yet, and the nodes with no record are the list of what a corpus pass has to go and read.
0 slots have no method recorded, and 32 methods have not been taken apart. Both are shown as what they are rather than left blank.
Every claim here rests on a source. This graph cites 140 papers; they and the 172 the Atlas cites alone are registered in one place, with what each reports and everywhere it is cited from. Papers