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
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 .
Open the full recordFills the slot: Prepare an input state
Requires the state to be -sparse in the computational basis with small, and the support to be known in advance. Sparsity is basis-dependent: a state that is sparse in one basis is generally dense in another, so this is a property of the problem's encoding as much as of the state.
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 .
Vector to load → State you can preparemerge two strings, shrink support by one
The circuit is built backwards, from toward , and then inverted. Each pass shrinks the support by exactly one. The current support is repeatedly split on a qubit into and , chosen so the two sizes are "as unequal as possible but neither set is empty", pushing the bit onto a stack until one string is left; the sibling branch is split the same way down to a second string . CNOTs controlled on the differing bit make and "equal on all bits except ", and a two-level gate controlled on the stacked bits merges into a single basis state — and because it is controlled on those bits "it will only be applied to and but no other ". assumption: is a normalised state, ; the merge itself needs nothing of the individual pair.
assumption
An -qubit circuit, possibly using ancillas, with a stated gate count, depth, ancilla count, and — where the circuit is not deterministic — a success probability.
None found yet.
given an n-qubit state with only d of its 2^n amplitudes nonzero, d small,
and its support known in advance
build the d computational-basis strings directly
# not by rotating through the whole binary tree -- that is the whole of the
# difference, and it is why the cost tracks d and n rather than 2^n
# how the d strings are built is not stated on this record and is not
# invented here: Gleinig and Hoefler's Eq. 5 is quoted for its cost only --
# T_CNOT(|S|) in O(|S| n) for a state with |S| nonzero coefficients,
# ancilla-free
# Li and Luo: without ancillas, size O(n d / log n + n),
# asymptotically optimal when d = poly(n); with unlimited ancillas, size
# exactly Theta(n d / log(n d) + n)
# sparsity is basis-dependent: a state that is sparse in one basis is
# generally dense in another, so this is a property of the problem's
# encoding as much as of the stateGleinig and Hoefler's Eq. 5 gives in for an -qubit state with nonzero coefficients, ancilla-free. Li and Luo give matching bounds: without ancillas, size , asymptotically optimal when ; with ancillas, size for any , with a matching lower bound under reasonable assumptions; with unlimited ancillas, size exactly .
None found yet.
None found yet.
qclib's `MergeInitialize`, the merging-states sparse loader (`qclib/state_preparation/merge.py`)
- An Efficient Algorithm for Sparse Quantum State Preparation
Gleinig, Hoefler · 2021
About
qclib describes itself in one line: "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." Its `state_preparation` package exports sixteen initialisers from a single `__init__.py`, and six of those modules import `from qclib.gates.initialize_sparse import InitializeSparse` — `fnpoints.py`, `merge.py`, `cvqram.py`, `cvoqram.py`, `pivot.py` and `mixed.py`, the last of which only uses it in an `issubclass` type check; the other five subclass it. What the base class fixes is the input format: a dictionary of non-zero amplitudes rather than a dense vector, which is what makes those loaders sparse rather than dense ones taking a sparse argument. `merge.py` is the module that realises the construction this record's hop describes, and it says so: its module docstring is the title of the paper this record cites for its cost, "An Efficient Algorithm for Sparse Quantum State Preparation", above the link `https://ieeexplore.ieee.org/document/9586240`. The class docstring states the direction the record's hop states, that the circuit is built backwards and then inverted: "Classical algorithm that creates a quantum circuit C that loads a sparse quantum state, applying a sequence of operations mapping the desired state |sigma> to |0>. And then inverting C to obtain the mapping of |0> to the desired state |sigma>." The input format is given in the same docstring as "A dictionary with the non-zero amplitudes corresponding to each state in format { '000': <value>, ... , '111': <value> }", so the support is supplied by the caller, which is this record's "the support to be known in advance".
Methods
`_define_initialize` is the whole loop and it is short: `while len(b_strings) > 1:` it calls `_select_strings`, then `_preprocess_states`, then `_merge`, and refreshes `b_strings` from the shrunken dictionary; when one string is left it applies an `x` on each of that string's `1` bits and returns `quantum_circuit.reverse_ops()`. That call reverses the order of the instructions and takes no adjoint, so the inversion the class docstring promises is carried half by the ordering and half by the angles, which are computed pre-inverted — the comment above `_compute_angles` reads "there is no minus on the theta because the intetion is to compute the inverse" (the typo is the file's). The `x` and `cx` gates it emits are self-inverse, so reordering suffices for them. The split rule the record's hop quotes as "as unequal as possible but neither set is empty" is two things in the file. `_maximizing_difference_bit_search` is documented as "Searching for the bit_index not in dif_qubits that maximizes the difference between the size of the nonempty t_0 and t_1" and computes `temp_difference = np.abs(len(temp_t0) - len(temp_t1))`; the emptiness half is the guard `if temp_t0 and temp_t1:` (line 120), which simply skips a bit that does not separate the current set. `_bit_string_search` then descends into the *smaller* side — `if len(t_0) < len(t_1): dif_values.append("0"); temp_strings = t_0` — appending each chosen bit to `dif_qubits` until `len(temp_strings)` is one. `_select_strings` runs that descent twice: the first gives , and `dif_qubit = dif_qubits.pop()` peels the last-chosen bit off to be the merge target, leaving the rest as controls; the second is restricted to the sibling branch by `b_strings2.remove(bitstr1)` followed by `_build_bit_string_set(b_strings2, dif_qubits, dif_values)`, which keeps only the strings agreeing with on the already-fixed bits, and gives . `_preprocess_states` is then three steps, not one: it applies an `x` on `dif` when `bitstr1[dif] != "1"`; it calls `_equalize_bit_string_states`, which for every index other than `dif` at which the two strings differ emits `quantum_circuit.cx(dif, b_index)` and rewrites both strings, so the pair ends equal everywhere but `dif`; and it calls `_apply_not_gates_to_qubit_index_list`, which emits an `x` on each stacked bit in `dif_qubits` where the pair reads `0`, so that the controls of the coming merge all read on this pair. `_merge` builds the two-level gate as a single-qubit `UGate(theta, phi, lamb, label="U")` on `dif`. The two angle branches differ in `theta` and not only in the phases: the real branch is `theta = -2 * np.arcsin(amplitude_2 / norm)`, and the complex branch takes a modulus, `theta = -2 * np.arcsin(np.abs(amplitude_2 / norm))`, and additionally sets `lamb = np.log(amplitude_2 / norm).imag` and `phi = np.log(amplitude_1 / norm).imag - lamb`. The gate is controlled on the stacked bits through `Ldmcu.ldmcu(quantum_circuit, gate_definition, dif_qubits, dif)`; with no controls left it appends the bare `UGate`. The support then shrinks by exactly one in the bookkeeping rather than in the circuit: the `"merge"` branch of `_update_state_dict_according_to_operation` computes `norm = np.linalg.norm([...])` over the pair, pops the second string, and writes `new_state_dict[merge_strings[0]] = norm`. **The normalisation the record's hop marks as an assumption is not enforced by this code.** `InitializeSparse._get_num_qubits` tests `if not isclose(sum(np.absolute(list(params.values())) ** 2), 1.0, abs_tol=1e-10):` and its body is `Exception("Sum of amplitudes-squared does not equal one.")` — constructed, never raised — so an unnormalised dictionary is accepted silently.
Data
No molecule, graph or benchmark instance; the repository's top level holds `qclib/`, `test/` and packaging files and no benchmark directory, so what exercises the module is `test/test_merge_initialize.py`. Four of its cases are hand-written 3-qubit vectors — , , , and one with complex entries built from , and — each converted by `build_state_dict` and checked with `np.allclose` against the state read back from the circuit. `test_8qb_sparse` is the only instance written out in full: an 8-qubit dictionary of 26 explicit bit strings and amplitudes, transpiled to `basis_gates=['cx', 'u']`. `test_several_qubit_sizes` is the only sweep: `for n_qubits in range(4, 12)` it draws `scipy.sparse.random(1, 2 ** n_qubits, density=0.1, random_state=42)`, normalises, and asserts the prepared state matches — so 4 to 11 qubits at a fixed 10% density and a fixed seed.
Code
https://github.com/qclib/qclib, Apache License 2.0 (`LICENSE.md`; every file carries the "Copyright 2021 qclib project" Apache header), Python on top of Qiskit. The module is `qclib/state_preparation/merge.py`, 465 lines, and the class is `MergeInitialize(InitializeSparse)`, re-exported as `from .merge import MergeInitialize` in `qclib/state_preparation/__init__.py`; the entry point for a caller is the static `MergeInitialize.initialize(q_circuit, state, qubits=None)`, which appends the gate. The base class is `qclib/gates/initialize_sparse.py`. Its key check raises `Exception("Dictionary keys must be binary strings")`, but the message is stronger than the test it guards: the line is `if not match("([01])+", parameter[0]):`, and `re.match` anchors only at the start while the pattern has no end anchor, so a key such as `'01abc'` passes. The usable reading is the narrower one — a key that does not *begin* with `0` or `1` is rejected — with the file's own wording recorded as written. The multi-controlled gate is `qclib/gates/ldmcu.py`, `Ldmcu`, documented as "Linear Depth Multi-Controlled Unitary" implementing "gate decomposition of a multi-controlled operator in U(2)" per arXiv:2203.11882 / Phys. Rev. A 106, 042602 — so the control cost inside each merge is not Gleinig and Hoefler's own but that decomposition's. `CITATION.cff` names the library itself rather than a paper ("Quantum computing library", version 0.0.21, released 2023-02-23) and carries no DOI. Read at `master`, commit `cf069817b0`, dated 2026-04-07.
Results
None. The module reports no gate counts and the repository asserts none. `test/test_merge_initialize.py` holds seven tests: the four small hand-written vectors and `test_several_qubit_sizes` assert state-vector agreement through `np.allclose`, which is correctness rather than cost; `test_raises_error_input_not_dict` asserts only that a non-dictionary argument raises; and `test_8qb_sparse` transpiles the 26-string 8-qubit circuit to `['cx', 'u']` and then simply ends — no `count_ops`, no threshold, no comparison against another initialiser. The measured C-not figures that do exist for this exact code were produced outside qclib, by the quantum-walks study recorded below, which vendors this file as its "Merging States" baseline.
- An Efficient Algorithm for Sparse Quantum State Preparation
`SparseStatePreparation` in UniversalQCompiler, and the pivoting experiment of Figure 4
- Quantum Circuits for Sparse Isometries
Emanuel Malvetti, Raban Iten, Roger Colbeck · 2020
About
This is the authors' own reference implementation, and the paper it belongs to reaches sparse state preparation as a stepping stone rather than as its subject. The introduction sets the order: "In Section 2 we discuss the case of state preparation, which is a building-block for the later cases. The main idea behind the decomposition there is to use pivoting gates, which permute the entries of the state such that the non-zero entries are grouped together, in effect reducing to state preparation on a smaller system." Section 2 then states the point of the section outright — "In this section we introduce a method for implementing sparse state preparation more efficiently than is possible in the dense case" — and the last section returns to it with counts. The construction is the opposite move from the merging-states one above: instead of shrinking the support one string at a time, it leaves the support alone and permutes it into one contiguous block, then calls an ordinary dense preparation on the small block. The repository's own credit line for the code reads "Emanuel Malvetti developed code for the decomposition of (sparse) isometries using the Householder decomposition."
Methods
Corollary 5 is a C-not bound rather than a construction. After fixing the notation — "Let be a state on qubits and let denote the number of non-zero entries of in the computational basis. Let " — it states , "where denotes the number of C-not gates used to implement pivoting up to a diagonal gate". What the function implements is the circuit in the corollary's proof. That proof gives the direction — "It is sufficient to find a circuit that maps to , since the inverse of this circuit implements state preparation for " — and its Eq. (1) the shape, . Pivoting itself is Lemma 2's Algorithm 1, whose six steps stop "If all non-zero entries are in the target block" and otherwise "Pick a non-zero entry outside the target block and a zero entry inside the target block", choose "a qubit on which" their block indices differ, use "at most C-nots to adjust to such that and differ only on the control qubit", and then fire "one -controlled not (controlling on ) to exchange and " — the step carrying its own invariant, "Note that none of the other entries of the target block are affected by this process", and the proof closing with "Since no non-zero entry ever leaves the target block, the claimed bound follows." **Two choices the lemma leaves open are what the implementation spends its effort on, and Remark 3 says so: "the target block and the order in which to proceed are not fixed and none of these choices affect any of the decompositions used in this work. Making these choices in the right way can reduce the C-not counts."** Section 5.2 fixes them greedily — "If the number of possible splittings is small enough, we try all splittings and choose the one with the largest number of non-zero elements in one column and this will be the target column. Otherwise one can randomly sample a fixed number of splittings and use the best one" — and then takes at each step the cheapest insertion, because "The insertion of one element into the target column can be implemented using one -controlled not gate and C-nots, where is the Hamming distance between the index of the non-zero entry and the index of the target entry." That distance is minimised in two pieces and only one of them is the hypercube search: "The Hamming distance can be written as ", where restricted to the column indices costs per non-zero entry, and over the row indices is computed "for all non-zero entries at the same time in by using breadth first search on the -dimensional hypercube with multiple starting vertices, given by the row indices of the zero entries in the target column". The whole circuit is then computable in classical time. The code follows that same order. `SparseStatePreparation[vec_,action_:Null,OptionsPattern[]]` calls `SparseReverseStatePreparation[SparseArray[vec], ...]`; `gates = Join[gatesPiv,gatesSP]` is the *reverse* preparation, that is Eq. (1)'s dagger, and the next line `gates=InverseGateList[gates]` turns it back into Eq. (1) itself. `SparseReverseStatePreparation` in turn is `{gatesPiv,rowQubits,colQubits,reducedVec,perm} = PivotingDec[vec]` followed by `gatesSP = RemoveAncillaGates[StatePreparation[reducedVec, ...]]` on the reduced vector alone. `PivotingDec` is the greedy loop: `{rowQubits,colQubits} = FindQubitSplitting[vec]`, `tcol = MostOccupiedColumn[stab]`, then `While[PivotingNotDone[stab], nnz = FindClosestNNZ[stab,tcol]; {stab,insertGates,insertPerm} = InsertNnz[...]]`. The of Eq. (1) is the licence Remark 6 takes: "For use in our sparse state preparation decomposition, it is sufficient to decompose the -controlled not gates of Lemma 2 up to a diagonal gate." The same remark bounds what that licence actually buys — a Toffoli falls from six C-nots to three when implemented up to a diagonal, but "We are not currently aware of schemes to decompose not gates with more controls up to diagonal, but, if these were found, our counts would be improved."
Data
No physical instance and no dataset — the states are drawn at random and only their support matters. Figure 4's caption fixes the ensemble: "sparse states on qubits with non-zero entries, whose positions are chosen uniformly at random", with the standing observation that "the actual values of the non-zero entries do not influence the counts". The horizontal axis runs to and there are four sparse curves, , plus a dense one — but the sparse curves do not span the axis. The ancillary per-trial tables carry from , from , from and only from , each running up to . Each column holds 200 trials; the , column is . The sampling shortcut is stated in the text: "we do not consider all possible qubit splittings, but randomly sample splittings and choose the one with the largest number of non-zero elements in one column." The corresponding random-state generators are in the same file as the decomposition — `PickRandomSparsePsi[dim,s]`, documented as "generates a random pure state with dimension dim and with s non-zero elements", alongside `RPickRandomSparsePsi[dim,s]` for "a random real pure state" and `FPickRandomSparsePsi[dim,s,tol]` for "a random analytic real pure state". The per-trial counts behind the figure are published as an ancillary file, "Data for Fig. 4", as histograms of C-not count against frequency for each cell.
Code
https://github.com/Q-Compiler/UniversalQCompiler, Apache License 2.0, Wolfram Mathematica; the Mathematica package the README names, `UniversalQCompiler.m`, is a single 4898-line file. The public symbol is declared at line 153 as `SparseStatePreparation::usage="SparseStatePreparation[vec] returns a circuit implementing sparse state preparation for a sparse state vec."` under the section comment `(*Decompositions for sparse isometries*)` at line 152, with the options at line 4672 and the definition at line 4673. It sits on `SparseReverseStatePreparation` (line 4658) and `PivotingDec` (line 4631); the dense fallback it calls is `StatePreparation`, filed in the same file under `(*State preparation (Plesch and Brukner)*)`, which is the scheme the numerical section names as its comparison — "We use the dense state preparation scheme from [5], which achieves near optimal C-not counts for arbitrary dense states." `SparseStatePreparation` is reused inside the package's isometry decomposition, but only behind a sparsity guard: line 3462 is `If[NumberOfZeros > 2^(n/3), (*To save runtime, we only run the sparse decomposition if the number of zeros is reasonable high*)`, and only inside it does line 3463 read `out1=SparseStatePreparation[u,action,...];If[CNOTCount[out]>=CNOTCount[out1],out=out1]`. So on a vector with too few zeros the sparse route is not computed at all, rather than computed and discarded. Read at `master`, commit `c72cd84a53`, dated 2025-09-25. The ancillary data file is `anc/supplementary_data.pdf` in the arXiv source package of arXiv:2006.00016.
Results
The headline finding is stated with its own counterexample attached, and dropping the second half would reverse it: the results "indicate the advantage we gain by taking into account the sparseness. Note however, that the dense case outperforms the sparse case for fairly dense states (where the cost of pivoting is not compensated by the smaller state preparation)." The second finding is that the implementation beats the paper's own analysis: "the counts found in practice are significantly smaller than our upper bounds", the bound drawn on the figure being " from Table 2, based on clean ancillas", and the caption spelling out why that comparison is not free — "No ancillas were used for our implementation... Although our implementation does not use ancillas, it nevertheless beats this bound." Error bars are "twice the standard error of the mean (about 95% confidence)" over 200 trials, invisible at . The ancillary "Data for Fig. 4" gives the raw spread rather than the mean: at the smallest cells are near-trivial — for , 1 trial of the 200 needed 0 C-nots and the largest count in that column is 9 — while at (sixteen non-zero entries) the tabulated counts run from 228 to 289 for , from 206 to 268 for , and from 199 to 260 for .
- Quantum Circuits for Sparse Isometries
The CTQW sparse state-preparation code and its C-not benchmark against merging states and Qiskit
- Efficient Sparse State Preparation via Quantum Walks
Alvin Gonzales, Rebekah Herrman, Colin Campbell, Igor Gaidai, Ji Liu, Teague Tomesh, Zain H. Saleem · 2024
About
This artefact is recorded for what it measures as much as for what it builds: it drives the merging-states loader of the first entry, the paper's own walk heuristics and Qiskit's built-in preparation through one interface on identical states, and commits the per-state counts alongside the states. The paper builds sparse state preparation out of continuous-time quantum walks on dynamic graphs and then shows the merging-states method is a special case of it: "the state of the art ancilla free sparse state preparation method, referred to as Merging States (MS) method in Ref. [19] fits in the CTQW framework we present in this paper. From examination, we determined that MS typically corresponds to star shaped graphs." Reference [19] is Gleinig and Hoefler. The abstract states the comparison with Qiskit and one condition on it together — the framework "offers an alternative to the uniformly controlled rotation method used by Qiskit by requiring fewer CX gates when the target state has a polynomial number of non-zero amplitudes" — and the body attaches a second condition the abstract leaves off, that this is what the method "is expected" to do "for sufficiently large values of ".
Methods
`StateCircuitGenerator` is an abstract base with one method, `generate_circuit(self, target_state: dict[str, complex]) -> QuantumCircuit`, and every method under comparison is a subclass of it, so the three families are driven through one interface on identical inputs. `MergingStatesGenerator` is four lines — "Generates state preparation circuits via merging states method of Gleinig" — instantiating `MergeInitialize(target_state)`, calling `merger._define_initialize()` and returning `circuit.reverse_bits()`. `QiskitDefaultGenerator` calls `circuit.prepare_state(target_state_vector)`. The paper's own best heuristic is `MHSTreeGeneratorHeuristic`, whose `generate_circuit` is the mirror image of the merging loop: `while len(current_state[0]) > 1:` it calls `select_next_walk`, then `implement_walk`, then applies `qc.x(ind)` on the surviving string's set bits and returns `qc.inverse().reverse_bits()` — a genuine adjoint, unlike the reordering in the first entry. What differs is the choice rule. Instead of the maximally-unequal bit split, `_get_z2_search` computes the pairwise differing-index sets `diffs = [get_different_inds(elem, z2, -1) for z2 in remaining_basis]`, takes `mhs = solve_minimum_hitting_set(diffs)`, and picks as the interaction index the element of that hitting set hit by fewest blocks, `interaction_ind = min(mhs, key=lambda idx: sum([1 for block in diffs if idx in block]))`. `MHSTreeGeneratorExhaustive` overrides `select_next_walk` to consider "all pairs and all interaction indices". The measurement harness is `run_state_preparation.py`'s `prepare_state`, which is where the reported quantity is defined: it transpiles with `basis_gates = ["rx", "ry", "rz", "h", "cx"]` at `optimization_level = 3`, applies `remove_leading_cx_gates` (documented as "Removes leading CX gates whose controls are always false"), and returns `circuit_transpiled.count_ops().get("cx", 0)`. The fidelity check is not part of that count and is not unconditional: it runs after the count and only `if check_fidelity`, asserting `abs(1 - fidelity) < fidelity_tol` with `fidelity_tol: float = 1e-8`.
Data
Random sparse state vectors, generated by `run_generate_states.py` and committed as pickles beside the counts: for each size it draws `qqi.random_statevector(len(all_inds)).data`, zeroes all but randomly chosen indices with `random.sample`, renormalises, and stores `Statevector(state_vector).to_dict()`. The paper's grid is "1000 randomly generated sparse states ( and non-zero amplitude basis states) for every value of from 5 up to 11". The committed `data/` tree on `main` is wider at one end and thinner at the other: it holds `qubits_2` through `qubits_12`, with one to three `m_*` subfolders each, but the folders below `qubits_5` — `qubits_2/m_2`, both under `qubits_3` and all three under `qubits_4` — carry a `states.pkl` and no `cx_counts.csv` at all. From `qubits_5` upward each `m_*` folder does carry a `cx_counts.csv` of 1000 data rows, with `states.pkl` beside it except in `qubits_11/m_1024`, which holds the counts alone. Which columns those CSVs carry varies by folder, and not every column is filled on all 1000 rows.
Code
https://github.com/GaidaiIgor/quantum_walks, Python against Qiskit 1.3.0 per the README. **The repository declares no licence** — the GitHub API returns `"license": null` and there is no `LICENSE` file at the root — so it is readable but not, on its face, reusable. The classes are in `src/state_circuit_generator.py`; the walk primitives in `src/quantum_walks.py`; the harness in `run_state_preparation.py` and `run_generate_states.py`. **The merging-states baseline is not an independent reimplementation: `src/gleinig.py` is qclib's `merge.py`,** carrying the same "Copyright 2021 qclib project" Apache header, the same module docstring naming the same IEEE link, the same `class MergeInitialize(InitializeSparse)`, and importing `qclib.gates.ldmcu.Ldmcu` and `qclib.gates.initialize_sparse.InitializeSparse` from the installed library. A full `diff` against `merge.py` is 30 lines longer and entirely bookkeeping: added `networkx` and `matplotlib` imports, `self.original_basis` / `self.transformed_basis` / `self.path` and the lines that append to them, a commented-out plotting block, two `@staticmethod` decorators commented out, and four docstring typos. No `quantum_circuit` gate call differs, and `_select_strings`, `_equalize_bit_string_states`, `_compute_angles` and `_merge` are otherwise unchanged. So the figures compare the CTQW heuristics against the very code recorded in the first entry above. The paper's code-availability section points here: "The code and data for this research can be found at https://github.com/GaidaiIgor/quantum_walks." The repository has no `master` branch — `main` is the default, beside `dev`, `nature_qi` and `nature_qi_test` — though a `raw.githubusercontent.com/.../master/...` URL nonetheless returns 200 and serves `main`'s bytes. Read at `main`, commit `ab9faea005`, dated 2025-07-21; every quotation from the paper is from the arXiv version at 2405.20273, not from the npj published version.
Results
The paper's summary claim about its own heuristic is narrow and carries its limit in the same breath: "Our method also outperforms MS for the considered cases, but our walk order heuristic seems to excel only in the linear case. From numerical examination, it appears that for the gap increases, but for the case a crossover happens at ." **Both "the gap" and "a crossover" take their referent from the clause before them and are about the walk heuristic against Merging States, not against Qiskit.** The Qiskit comparison is settled one sentence earlier and settled as an expectation with no number on it — the method "is expected to outperform Qiskit's built-in method for any asymptotically sparse state (i.e. ) for sufficiently large values of " — and the paragraph closes "All the discussed CTQWs (including MS) have the same asymptotic scaling." What the paper does report about Qiskit at the sizes it ran is structural: "Qiskit's performance scales exponentially and is very similar regardless of the value of . In fact, for and Qiskit produces circuits with exactly the same number of CX gates regardless of the state being prepared." The committed CSVs bear that out and sharpen it: over the folders the `qiskit` column is constant down all 1000 rows and equals exactly at every size that carries it — 26, 57, 120, 247, 502, 1013, 2036 for to . Over those same files Qiskit's count is the smallest of the three at every one of those sizes, so the asymptotic expectation quoted above has not arrived anywhere inside the tested range: means for the `mhs_nonlinear` and `merging_states` columns are 239.29 and 302.18 against 26.00 at , 1386.73 and 1505.93 against 502.00 at , and 2632.45 and 2718.84 against 2036.00 at . What does move across the range is the distance between the two sparse columns: `merging_states` minus `mhs_nonlinear` runs 62.89, 85.24, 98.01, 113.25, 119.20, 114.16, 86.39 for to , peaking at and closing after it, which is the shape the crossover describes. The largest cell in the repository, `data/qubits_12/m_144/cx_counts.csv`, carries no `qiskit` column at all: there `merging_states` means 3516.55 over 1000 filled rows and `mhs_nonlinear` 3492.17 over the 180 that are filled, so the remaining difference is not a like-for-like comparison. Where both columns are full the spreads differ in kind: at `merging_states` ranges 2405 to 2977 while `mhs_nonlinear` ranges 2135 to 3273, so the column that wins on the mean is the less predictable of the two. Which committed column is which named method is documented nowhere in the repository — `run_state_preparation.py` selects one generator and one `out_col_name` at a time and leaves the alternatives commented out — so all of the above is a claim about the columns as named, not about the paper's own labels. The framework's stated complexity is , "where is the number of qubits and is the number of basis states in the target state", the same order the paper attributes to Ref. [19].
- Efficient Sparse State Preparation via Quantum Walks
None found yet.
References
- Nearly Optimal Circuit Size for Sparse Quantum State Preparation
Lvzhou Li, Jingquan Luo · 2024
- An Efficient Algorithm for Sparse Quantum State Preparation
Gleinig, Hoefler · 2021
Where the routes meet
Every circle is drawn once. This step has no smaller object recorded inside it, so the strands between its two circles are the recorded ways of taking it — one strand per method.
3 recorded ways of doing Prepare an input state. Nothing smaller is recorded inside it, so there is no object in the middle to draw.
Everything on this figure that opens is open.
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
- Every line on this figure is one a recorded source takes.
Open the cardRead the full write-up
Where you are
Path
- Solve a nonlinear ODE dy/dt = F(y)
- Quantum linear solve
- Prepare an input state
Ways through: 3
Routes that skip it
No recorded route avoids this step.
Narrower kinds
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