Sign outOpen workspaceSign in

MethodLayer 1

Grow the circuit a layer at a time while training it

Do not settle the circuit before optimising it. Start shallow, train what is there, then hold most of it fixed and add the next layer on top — so every step of the search runs on a shallow circuit with few free parameters, which is where a gradient is still large enough to follow.

Takes

A parameterised circuit family; an objective function of its parameters, evaluated only through estimates bought with a finite shot budget; a starting point; and a stopping rule — a tolerance, an iteration cap, or an exhausted budget.

Returns

A preparation routine for the state at the parameters the search stopped at, and the total number of objective evaluations it consumed. The routine is returned whether or not the search found a minimum; that it stopped is not evidence that it converged.

Same contract as the slot it fills.

This one, drawn

From Parameterised circuit family 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

  • Minimise the objective over the parameters

    Search the parameters of a fixed circuit family for the ones that minimise a chosen objective, given that every evaluation of that objective is a noisy estimate someone paid shots for. The output is a routine that prepares one particular state — the family collapsed to a member.

When it applies

Skolik et al. state the strategy in one sentence — "the circuit depth is incrementally grown during optimization, and only subsets of parameters are updated in each training step" — and then hedge the benefit they claim from it: "when considering sampling noise, this strategy can help avoid the problem of barren plateaus of the error surface". *Can help*, and under sampling noise; the reason offered is structural rather than incidental — "the low depth of circuits, low number of parameters trained in one step, and larger magnitude of gradients compared to training the full circuit". The evidence is the part to read before carrying the numbers anywhere, because it is *not chemistry*. The demonstration is "an image-classification task on handwritten digits", and there layerwise learning "attains an 88% lower generalization error on average in comparison to standard learning schemes for training quantum circuits of the same size", with "the percentage of runs that reach lower test errors ... up to 4040% larger compared to training the full circuit". Both figures are relative to full-circuit training of the same size on that one benchmark, so neither is a claim about the molecular energies the rest of this region is drawn from.

Requires

These do not move the route along. The method needs each of them alongside its own work, and the cost of getting them is part of what the method costs.

  • Choose a parameterised trial state

    Fix the gate structure of a circuit family and leave its rotation angles open. What comes back is not a circuit but the set of states the later optimisation is allowed to search — which is why this is a slot of its own and not a paragraph in one method's write-up.

    Layerwise training reaches for this slot once per growth step, not once up front, so what it asks for is not a circuit but a layer it can keep appending. Eq. 4 gives the one it uses: Ul(θl)=i=1nexp(iθl,iVi)U_l(\vec\theta_l)=\prod_{i=1}^{n}\exp(-i\theta_{l,i}V_i) over the nn qubits, with single-qubit generators ViV_i drawn from the Pauli operators XX, YY, ZZ. One angle per qubit and none in the fixed WW, so a returned layer adds exactly nn free parameters, and since Ul(0)=IU_l(\vec 0)=I, a layer appended at zero angles contributes only its fixed WW until training moves them. assumption: the generators are sampled rather than designed — the paper builds "circuits that consist of layers of randomly chosen gates" after McClean et al., so nothing in this family argues that the target state lies inside it. The reported circuits stack those rotations with CZ gates "coupling arbitrary pairs of qubits" into all-to-all connected layers (Fig. 2).

    assumption

Example

given  a parameterised circuit family built as a product of layers,
       l_i(theta_i) = U_i(theta_i) W                                 (Eq. 2)
       with U_i(theta_i) = prod_{k=1..n} exp(-i theta_i,k V_k),
       one generator V_k in {X,Y,Z} per qubit                        (Eq. 4)
       and W a fixed two-qubit coupling layer (CZ gates here,
       all-to-all connectivity) held constant for the whole run      (Sec III, Fig. 2)
       an objective L(theta) (binary cross-entropy in the paper's
       demonstration) whose gradient is bought by the parameter-
       shift rule, two device calls per trained parameter            (Eq. 5, Eq. 9)
       hyperparameters s (initial layers), p (layers added per
       growth step), q (freeze horizon -- how far back from the
       current layer a layer may sit and still be trained), el
       (epochs per training step), r (fraction of the final-depth
       circuit's layers trained per block in phase two)

requires  the layer structure itself can be "arbitrary, as long as
    they allow successively increasing the number of layers"; the
    Pauli-rotation-plus-CZ form given above is this paper's own
    numerical choice for its demonstration, not a requirement of
    the strategy                                                     (Sec III)

# --- phase one: grow the circuit, train only the newest window -----------
L = s
theta_1 .. theta_s = 0                                                (Eq. 1)
    # zero init "provides additional degrees of freedom for the
    #   optimization routine without perturbing the current
    #   solution" -- growth does not undo what phase one already
    #   found                                                        (Sec III)
trainable = { theta_1 .. theta_s }

repeat:
    for el epochs:
        for each theta_i in trainable:
            g_i = parameter_shift_grad(theta_i)
                # E(theta) = <psi| U(theta)^dagger M U(theta) |psi>  (Eq. 7)
                # dE/dtheta_i = r * ( E(theta + s_shift*e_i)
                #                     - E(theta - s_shift*e_i) )     (Eq. 9)
                #   with r = 0.5 and s_shift = pi/(4r) = pi/2. The
                #   prefactor r is part of the estimator, not only
                #   of the shift: the raw difference of the two
                #   shifted expectation values is twice dE/dtheta_i.
                #   This r is Eq. 9's shift-rule constant, a
                #   DIFFERENT r from phase two's block fraction below
                # each such evaluation costs measurements: cumulative
                #   count r_dev_i = r_dev_{i-1} + 2*|trainable|*m*b   (Eq. 10)
            theta_i <- update(theta_i, g_i)
                # paper's demonstration uses Adam; not specific to
                #   the layerwise strategy itself                    (Sec IV A)

    if L has reached the target depth, or the last growth step
            did not improve the objective:
        break                                                        (Sec III)

    append p new layers  theta_{L+1} .. theta_{L+p} = 0
    L = L + p
    trainable = { theta_i : i >= L - q }
        # the paper defines q by prose, not by a formula: "layers
        #   more than [q] back from the current layer are frozen",
        #   illustrated with p = 2, q = 4. Read literally that
        #   leaves layers L-q .. L trainable, i.e. q+1 of them, and
        #   the predicate above is that reading                      (Sec III)
        # UNSETTLED, and the paper does not resolve it: the reported
        #   experiment sets p = q = 2 "with one initial layer that is
        #   always active during training" and states "three layers
        #   are trained at once". Three is q+1 with no always-active
        #   layer counted, or q plus that always-active layer -- so
        #   the window is q+1 or q depending on how the sentence is
        #   parsed. If you implement this, fix the convention and
        #   check it against the paper's three-layer figure          (Sec III, Sec IV D)
        # the reported experiment also keeps its initial layer
        #   trainable for the whole of phase one, outside the
        #   sliding window                                           (Sec IV D)

# --- phase two: retrain in larger contiguous blocks, to full depth -------
# phase one already gives "a sufficiently non-random initialization",
#   which the paper gives as the reason it is now safe to widen the
#   trainable window                                                  (Sec III)
partition theta_1 .. theta_L into contiguous blocks of size
    round(r * L)   # e.g. a quarter or a half of the circuit's layers (Sec III)
repeat, alternating over the blocks:
    unfreeze one block, freeze every other layer
    for el epochs:
        for each theta_i in the unfrozen block:
            g_i = parameter_shift_grad(theta_i)                      (Eq. 9)
            theta_i <- update(theta_i, g_i)
until the objective stops improving
    # no more permanent freezing here -- this is what lets phase two
    #   eventually move every parameter, unlike phase one            (Sec III)

return  theta_1 .. theta_L and the cumulative device measurement
        count r_dev_L                                                (Eq. 10)

# MEASURED, not proved, and only in the noisy regime the paper adds
#   on purpose: on one 8-qubit, 21-layer, fully-connected MNIST
#   six-vs-nine instance with finite shots (m=10, b=20, 100 runs),
#   the best layerwise configuration reaches a lower average test
#   error, and about half the cumulative measurements, of the
#   highest-success-probability full-circuit run                     (Sec IV D, Fig. 3, Fig. 4)
# under exact gradients (m = infinity) the paper reports "all tuned
#   training strategies seem to perform similarly" -- the advantage
#   above is a shot-noise-regime result, not a property that
#   distinguishes the two ansatz-growth strategies on their own      (Sec III, Appendix)
# the authors expect the advantage mainly with LOCAL cost functions;
#   a global cost function hits barren plateaus even at shallow
#   depth, which layerwise growth does not fix                       (Sec III)

Cost, as the source states it

Skolik, McClean, Mohseni, van der Smagt and Leib count device measurements, not gates: cumulatively ri=ri1+2npmbr_i = r_{i-1} + 2 n_p m b (Eq. 10) — two shifted evaluations per trained parameter by the parameter-shift rule (Eq. 9), mm shots per expectation value (O(1/ε2)O(1/\varepsilon^2) under operator averaging), bb samples per batch. A layer carries one angle per qubit (Eq. 4), so layerwise training shrinks npn_p per step at the price of more epochs, and nothing bounds the step count: layers are added until they stop improving the objective or a chosen depth is reached, then phase two sweeps "until the algorithm converges". Measured, not proved: on one 8-qubit, 21-layer, fully connected MNIST six-versus-nine instance (m=10m=10, b=20b=20, 100 runs, diverged runs excluded from the averages), the best layerwise configuration converges in about half the cumulative measurements — Fig. 3 plots that count as runtime at an assumed 10 kHz — of the highest-success-probability CDL configuration, which the paper notes is also its slowest. With exact gradients (m=m=\infty, b=100b=100) the appendix reports QPU calls almost equal. The authors expect an advantage mostly with local cost functions.

Implementations

  • Skolik's own notebook on TensorFlow Quantum's `research` branch

    This is the method's first author writing the method down in code: the notebook's third cell, the second of its markdown cells, reads "Author : Andrea Skolik", "Contributors : Masoud Mohseni", "Created : 2019", "Last updated : 2020-Jun-29", and the paper it belongs to closes by thanking "the TensorFlow Quantum team for providing early access to the library, which was used to perform the simulations in this work". The branch says what the directory is for — the `research` branch README is a heading, "Research", and one sentence, "Each directory in this branch corresponds to an example application in the TensorFlow Quantum whitepaper", whose last three words are a markdown link to arXiv:2003.02989 — and the whitepaper's own section V B 2, "Layerwise quantum circuit learning", claims that "a complete implementation of both phases can be found in the accompanying notebook" and prints the path `research/layerwise_learning/layerwise_learning.ipynb`. Skolik's companion post on the TensorFlow blog, "Layerwise learning for Quantum Neural Networks" of 10 August 2020, "Posted by Andrea Skolik, Volkswagen AG and Leiden University", sends a reader to the same file: "If you'd like to play with the code for this example yourself, check out the notebook on layerwise learning in the TFQ research repository, where we train a QNN on a simulated quantum computer!". The problem statement the notebook opens with is the paper's — "we successively add layers to a QNN during training, which does not only make training faster, but also ensures a better signal-to-noise ratio compared to training the full circuit when done on real hardware" — and it names the constraint the strategy is aimed at: "As the gradients produced by circuits grow smaller, we need more and more measurements from a quantum device to accurately estimate them."

    A layer is `create_layer(qubits, layer_id)`, which picks one of `cirq.rx`, `cirq.ry`, `cirq.rz` per qubit by `random.choice(gate_set)` and follows them with `cirq.CZ(control, target)` over `zip(qubits, qubits[1:])`, and returns three things — the gates bound to the literal angle `0`, the same gates carrying `sympy.Symbol` angles, and the symbols. Phase one is a bare loop over `range(n_layer_steps)` that appends `n_layers_to_add` fresh layers, rebuilds the whole circuit as `circuit += symbol_layers`, appends `cirq.X(readout)`, `cirq.H(readout)`, `cirq.X(readout)` and measures `cirq.Z(readout)`, then wraps it in `tfq.layers.PQC(model_circuit=circuit, operators=readout_op, differentiator=tfq.differentiators.ParameterShift(), initializer=tf.keras.initializers.Zeros)` inside a `tf.keras.Sequential`, compiles with `tf.keras.losses.squared_hinge` and `tf.keras.optimizers.Adam(learning_rate=0.01)`, carries the previous step's angles forward by `model.set_weights([np.pad(weights, (0, n_qubits*n_layers_to_add))])` so the new layer starts at zero, and fits for 20 epochs. **The freezing is the only departure from the paper the notebook flags in its own prose, and it is not the only departure.** The markdown cell above the loop says "We are not going to freeze the previous layers in phase one in this implementation, but simply grow the circuit incrementally", where the paper's phase one trains only a sliding window and the blog post describes the reported experiment as "we add two layers in each step, and freeze the parameters of all previous layers, except the start layer, such that we only train three layers in each step". Three more go unflagged: the `cirq.CZ` ladder against the paper's "circuit with fully-connected layers as described in section III" (chosen there "for our numerical investigations" although "not realistic on NISQ hardware"), `squared_hinge` against "We use the binary cross-entropy as the training objective", and `n_qubits = 6` at ten layers against "we pick a circuit with 8 qubits and 21 layers for our experiments" — and the notebook credits the ladder elsewhere anyway, "a ladder of CZ gates that connect them. This is the same structure as used in [2]", its reference 2 being McClean, Boixo, Smelyanskiy, Babbush and Neven on barren plateaus rather than the layerwise paper. The digit pair and the data encoding are two further departures, recorded in `data`. **The notebook also contradicts itself about the freezing, and a reader meets the wrong side first**: the markdown cell that introduces the two phases still says "we add another set of layers and freeze the parameters of the previous step's layers", nine cells above the cell that says it does not. The Keras summaries printed in the saved output settle which sentence the code follows: `Total params` runs 12, 24, 36, 48, 60 with `Non-trainable params: 0` at every step, so every parameter present is trained at every step and phase one here is layer growth with no freezing in it at all. Phase two does freeze. `train_partition(circuit, trained_weights)` is handed a circuit assembled from `symbol_layers` for the half being trained and, for the other half, layers whose gates have been rebuilt with numeric angles — the notebook's own comment is "We can't alter a gate's parameter directly after it was initialized, so we simply initialize a new gate with the new parameter by using a slightly ugly hack to determine the gate type", and the hack is `if str(g)[:2] == 'Rx':` and its two siblings, popping angles off a reversed copy of the weight vector. With `partition_percentage = 0.5` and `n_sweeps = 2` it alternates the two halves four times, and the summaries read `Total params: 30`, exactly half of 60.

    MNIST, loaded by `tf.keras.datasets.mnist.load_data()` and cut down hard. The pair is threes against sixes — `y in [3, 6]`, with `convert_label` sending a 3 to `1.0` and everything else to `-1.0` — which is not the pair the paper reports; the paper's circuit "learns to distinguish between the numbers six and nine". `reduce_image` resizes each image to 4×44\times4 by `tf.image.resize` and divides by 255; `remove_contradicting` then drops images whose downsampled greyscale array maps to both labels, keying on `str(x)` for the float array `reduce_image` returned rather than on any bitstring, so it runs before thresholding and does not catch two images that agree bit for bit but differ in grey value; and `convert_to_circuit` puts a `cirq.X` on qubit ii for every pixel above 0.5, so what the circuit finally sees is a binary bitstring rather than the paper's principal-component angles ("we use qubit encoding in combination with principal component analysis (PCA)"). The saved output records 11520 filtered training examples and 1906 filtered test examples, of which `NUM_EXAMPLES = 128` are kept for training. Two things a reader should check before quoting the counts. The notebook prints its own second count wrong — `print("Number of original test examples:", len(x_train))` — so the two 60000s in the output are the same number twice. And the data circuits and the model circuit are not built on the same register: `convert_to_circuit` lays the 16 flattened pixels on `cirq.GridQubit.rect(1, len(values))`, that is qubits (0,0)(0,0) to (0,15)(0,15), while the trained circuit lives on `cirq.GridQubit.rect(1, n_qubits)` with `n_qubits = 6` and the observable is `cirq.Z` on `cirq.GridQubit(0, 5)`, so ten of the sixteen pixel qubits carry a data gate and are then touched by no layer and by no measurement.

    https://github.com/tensorflow/quantum, branch `research` — not `master`, which does not contain this directory at all — at path `layerwise_learning/layerwise_learning.ipynb`. Python in a Colab notebook, Apache License 2.0: the branch carries a top-level `LICENSE` whose first two lines are "Apache License" and "Version 2.0, January 2004", the first markdown cell reads "Copyright 2020 The TensorFlow Quantum Authors." under five hash marks, and the first code cell is the header itself, its two opening comment lines reading "Licensed under the Apache License, Version 2.0 (the "License");" and "you may not use this file except in compliance with the License." Read 2026-08-27 at blob `a45dbbbe42c6d57561efe1408696633150dafc6f`: 166,906 bytes, 1,167 lines of notebook JSON, 22 cells of which 12 are code and 10 markdown, and 254 lines of Python across the code cells. There are no classes and no importable module — the things to open are `create_layer(qubits, layer_id)`, the phase-one `for layer_id in range(n_layer_steps)` loop, and `train_partition(circuit, trained_weights)`, and phase two reads the notebook globals `layers`, `symbol_layers`, `weights`, `readout` and `readout_op` that phase one left behind, so the cells only run in order. **The content is Skolik's, and the branch has been frozen since 2022.** The file arrived on 2020-02-23 as "Added initial notebook from Andrea"; everything this entry describes as phase two, `train_partition` included, arrived with commit `d905f64c08d5` of 2020-07-01, `+745 -35`, whose message is "Updated LL example" for pull request 279 followed by "@askolik Sent along an update for the layerwise learning notebook to more closely reflect what was done in their new publication: https://arxiv.org/abs/2006.14904" — so the git author, Michael Broughton, is the conduit rather than the author. The three commits after it are version bumps, the last being `92c952f9d29b` of 2022-03-04 by Michael Broughton, "Bump research branch to tf 2.7.0."; the `research` head is `8756d82c1a2c110670919d80fee8e611bd00961e` of the same day, and the notebook still opens with `!pip install --upgrade tensorflow==2.7.0` and `!pip install tensorflow-quantum`. Meanwhile `master` was last committed 2026-07-14, its newest release is `v0.7.6` published 2026-02-25, and `tensorflow-quantum` 0.7.6 was uploaded to PyPI on 2026-02-18 requiring Python `>=3.10`. No tag contains this file, so there is no released version of the notebook to pin.

    Everything is simulated — `tfq.layers.PQC` on the default TFQ simulator, no device anywhere in the file — and the only numbers are the squared-hinge losses the saved outputs recorded, because `model.compile` is called with no `metrics=` argument, so `model.evaluate` returns loss alone and no accuracy is computed at any point. Phase one's five `model.evaluate` calls on the 1906 held-out examples read 1.0000, 0.8751, 0.8434, 0.8435 and 0.8098 as the circuit grows from 2 to 10 layers, so the fourth step made it very slightly worse; phase two's four partition trainings read 0.7363, 0.7006, 0.6960 and 0.6798 on the same held-out set. Phase one therefore moved the held-out loss by 0.19, from 1.0000 to 0.8098, across five growth steps, and phase two took it a further 0.13 on a circuit that had stopped growing. Two `plt.plot(training_history)` cells are the only figures, and only the second mixes the phases: `training_history` holds phase one's five entries when the first of them runs and all nine when the second does. None of this reproduces the paper's comparison: there is no complete-depth-learning arm, no repetition count, no shot budget — the paper's own claim is stated for 100 runs at m=10m=10 measurements per expectation value on an 8-qubit, 21-layer instance — and the notebook's closing cell says as much about the task itself, "a classical neural network is hard to beat on a simple learning task like this, especially with a basic data encoding scheme as used above".

  • Layerwise learning rebuilt on Qiskit primitives and PyTorch, in `Gopal-Dahale/ILearnQuantum`

    A second implementation of the same paper on a different stack, written up as a Qiskit blog article rather than as a paper. The repository README states the goal and the scope in three sentences, the first of which carries a markdown link to arXiv:2006.14904 where the word "paper" stands here: "The paper introduces the concept of layerwise learning to train quantum circuits step-by-step by gradually adding layers and training only a subset of parameters. In this article, we implemented the layerwise learning algorithm using Qiskit and PyTorch. We also talked about barren plateaus and the effectiveness of layerwise learning to overcome them." The notebook's own first cell is "Layerwise learning for Quantum Neural Networks with Qiskit and PyTorch" and "Author: Gopal Ramesh Dahale", and the directory README offers three Colab badges — `barren_plateaus.ipynb`, `barren_plateaus_visualization.ipynb` and `ll_qiskit_pytorch.ipynb` — so the barren-plateau motivation is not asserted here but measured in a sibling file. It is worth recording next to the author's own TensorFlow Quantum notebook precisely because it makes the opposite choice on the one thing that notebook skips: this one freezes.

    The layer is `TwoLocal(n_qubits, ['ry', 'rz'], 'cz', skip_final_rotation_layer=True, entanglement='linear', reps=1).decompose()`, so a growth step contributes rotations plus a linear chain of `cz` rather than the paper's all-to-all coupling, and the data layer is a separate `TwoLocal(n_qubits, ['rx'], parameter_prefix='x', skip_final_rotation_layer=True, reps=1).decompose()` with one `rx` per qubit, matching the local-X data layer the paper uses. Phase one loops over `range(n_layer_steps)`, gives each of the two layers added per step its own `ParameterVector` of `num_weights` entries, initialises the step's new parameters to `np.zeros(temp_params.shape) + 0.001` — a deliberate offset from the paper's exact zeros, flagged in the code as "we perform a small deviation from all zeros" — composes them onto `main_qc`, and then freezes: under `if layer_id >= n_layers_to_train:` it takes `layer_params[-n_layers_to_train - 1]`, the growth step that has just fallen out of the window, and calls `main_qc = main_qc.bind_parameters(dict(zip(freeze_params, freeze_param_values)))`, which substitutes numbers for those symbols so they are no longer parameters of the circuit at all. With `n_layers_to_train = 2` and `n_layers_to_add = 2` the trainable set is `np.asarray(layer_params[-n_layers_to_train:]).flatten()`, the two most recent growth steps. The circuit reaches PyTorch through `EstimatorQNN(estimator=estimator, circuit=main_qc, input_params=input_params, weight_params=initial_params, gradient=gradient, input_gradients=False)` wrapped by `TorchConnector` inside `class Net(torch.nn.Module)`, whose `forward` rescales the expectation value with `torch.clamp((self.qnn(x) + 1) / 2, min=0.0, max=1.0)` and whose `loss_func` is `torch.nn.BCELoss()`; the optimiser is `torch.optim.Adam(model.parameters(), lr=0.01)` for 20 epochs per step. Phase two starts from `residual_qc`, the copy that was never bound, splits `layer_param_values` at `int(len(layer_param_values) * partition_percentage)` with `partition_percentage = 0.5`, and alternates: `train_qc = qc.bind_parameters(p2_weights)` to train the first half, `qc.bind_parameters(p1_weights)` to train the second, for `n_sweeps = 2`, this time with `input_gradients=True`. Expectation values and their gradients are bought from `QulacsEstimator()` and `QulacsEstimatorGradient(estimator)` out of `qiskit-qulacs`, installed in the first cell straight from git; the Qiskit-native `Estimator()` and `ReverseEstimatorGradient(estimator)` are imported and then left commented out, "One can use the Qiskit's primitives by uncommenting the lines below".

    MNIST again, but a different pair and a different encoding from the TensorFlow Quantum notebook. `torchvision.datasets.MNIST` is filtered to labels 0 and 1 by `np.where(X_train.targets == 0)[0][:n_train_samples // 2]` and its sibling for label 1, with `n_train_samples = 200` and `n_test_samples = 2000`, then shuffled with `random_state=seed` and `seed = 42`. Each 28×2828\times28 image is flattened to 784 values, scaled by 1/2551/255, reduced by `sklearn.decomposition.PCA(n_components)` with `n_components = 8`, one component per qubit, and mapped into [0,2π][0, 2\pi] by `min_max_scaling(x, 0, 2 * np.pi)` — the paper's own scheme, "scaling the component values to lie within [0,2π)[0, 2\pi), and using the scaled values to parametrize a data layer consisting of local X-gates", with the endpoint reached here because `min_max_scaling` sends the maximum onto it. Eight components out of 784 carry 69.22405% of the training set's variance, and the saved output prints that one figure twice: the second `print` re-reads `pca.explained_variance_ratio_`, which `pca.transform(x_test)` does not change, and is labelled "on train" as well, so the notebook reports no explained-variance figure for the test split at all. Batches are 16, from `DataLoader(..., shuffle=True, batch_size=batch_size, num_workers=2)`. The training split really is 200 examples, 100 of each label; the test split is not 2000, because MNIST's test set holds only 980 zeros against the 1000 the slice asks for, so 1980 examples go into `test_dataloader` and `results` reads the accuracy with that denominator.

    https://github.com/Gopal-Dahale/ILearnQuantum at path `layerwise_learning_with_qiskit_and_pytorch/ll_qiskit_pytorch.ipynb`, branch `main`. Python in a Colab notebook. **There is no licence.** The repository has no `LICENSE` file, the GitHub repository record returns `"license": null` and the licence endpoint returns 404 for it, so nothing here grants a reader permission to reuse the code, and that is worth knowing before copying from it. Read 2026-08-27 at blob `8a114532e2408f1654bb7767a27a89a68de39d46`: 662,470 bytes, 1,204 lines of notebook JSON, 25 cells of which 20 are code, and 430 lines of Python across the code cells — most of the size is saved plot images. The two commits that produced the directory are `011c0d0eed63`, 2023-08-24, "Layerwise learning with qiskit and pytorch", and `a5fe68502e3c`, 2023-08-25, "Added readme for layerwise learning"; nothing has touched it since, and the repository carries no tags or releases. Beside the notebook sit `barren_plateaus.ipynb`, `barren_plateaus_visualization.ipynb` and `gradient_variance.csv`, the last a 45-row table of `layers,qubits,variance` over layer counts 1, 3, 5, 10, 15, 20, 50, 100, 200 and qubit counts 2, 4, 6, 8, 10, whose 10-qubit column falls from 0.33654903429237054 at one layer to 0.01893045301547359 at two hundred. The names to open are `class Net(torch.nn.Module)`, the phase-one loop with its `bind_parameters` freeze, and the phase-two sweep loop. **Check the imports before believing the notebook runs.** Its own version table stamps the run as qiskit 0.44.1, qiskit-terra 0.25.1, `qiskit_qulacs` 0.0.1, `qiskit_machine_learning` 0.6.1, Python 3.10.12, "Thu Aug 24 16:25:54 2023 UTC", and the first import cell asks for `from qiskit.algorithms.gradients import ReverseEstimatorGradient`, `from qiskit.utils import algorithm_globals` and `from qiskit.primitives import Estimator`, while the last cell does `import qiskit.tools.jupyter`. Against Qiskit as it stands today — release 2.5.2, published 2026-08-13 — `qiskit/algorithms` and `qiskit/tools` are both absent from the tree, `algorithm_globals` appears nowhere in `qiskit/utils/__init__.py`, `qiskit/primitives/__init__.py` imports no `Estimator` and states "There are currently no implementations of the legacy ``EstimatorV1`` interface in Qiskit", and `qiskit/circuit/quantumcircuit.py` defines `assign_parameters` and no `bind_parameters`, which is the call the freezing is built on. `qiskit-qulacs` itself is alive and Apache-2.0, last pushed 2025-12-15, and `TorchConnector` is still exported by `qiskit_machine_learning.connectors` at release 0.9.1 of 2026-08-19.

    A classical simulator throughout — `QulacsEstimator` from `qiskit-qulacs` — with no device and no shot noise, so none of the sampling-noise regime the paper's claim is stated in is exercised here. **Every loss the notebook reports is a training loss.** Both phase loops iterate `for batch in train_dataloader` alone, average `loss.item()` over that epoch's batches and print the average; no loss is ever computed on the held-out split, and `evaluate` returns accuracy rather than loss. The training-set binary cross-entropy at the end of each 20-epoch block runs, across phase one's eight growth steps, 0.3859, 0.3200, 0.3208, 0.3225, 0.3319, 0.3182, 0.3223 and 0.3210 — it improved once and then sat still for six steps — and across phase two's four partition trainings 0.3182, 0.3117, 0.2534 and 0.2056. None of those twelve numbers is comparable with the held-out losses in the entry above. Wall-clock is printed for each phase, "Duration 223.5977 s" and "Duration 354.7637 s". The only held-out figure in the file is an accuracy: the second-to-last cell evaluates the last phase-two model and returns the tuple `(0.9711538553237915, 0.9529569745063782)`, train and test. Read both with their denominators, because `accuracy` returns the fraction correct within a batch and `evaluate` takes `torch.stack(batch_accs).mean()`, an unweighted mean over batches. The training figure averages 13 batches of which the last holds 8 of the 200 examples; the test figure averages 124 batches over 1980 examples rather than the 2000 `n_test_samples` names, of which the last holds 12. One more thing the figures get wrong: the phase-two cell appends each sweep's first-partition loss history to `losses`, which is phase one's list, and only the second partition's to `losses2`, so the side-by-side plot drawn after phase two puts ten blocks under the title "Phase I Training Convergence" and two of them are phase two. There is no complete-depth-learning arm anywhere in the notebook, so nothing here compares layerwise training against training the whole circuit — the comparison the paper's 88% and 4040% figures are about.

What it needs

Every step this method names is listed under Requires above. It walks its own span in one hop and calls out to the rest — that is a fact about the recorded route, not a claim that the span is simple.

Other ways to fill the same slot

Different approaches

  • Conditional-value-at-risk objective

    Change what the classical loop is minimising rather than how it minimises. Instead of averaging every measurement outcome into an expectation value, keep only the best tail of them and average that — which is defensible precisely when the answer is a single good bitstring rather than a physical average.

  • Analytic-gradient parameter search

    Get the gradient of the objective exactly, rather than by finite differences, by running the same circuit again at shifted parameter values. The direction is then not an estimate of a slope taken from two noisy numbers; it is the slope, estimated to whatever precision the shots allow.

  • Minimise the energy variance

    Minimise how much the energy fluctuates rather than the energy itself. Any eigenstate has zero variance, so the objective's own value tells you whether you have arrived — which the energy never does, since a low number is only low relative to a minimum nobody knows.

  • Follow the steepest descent in the state's own geometry

    Take the step that moves the state fastest, not the one that moves the parameters fastest. The two differ because equal changes in parameters do not make equal changes in the state, and the metric measuring that difference has to be estimated before every step.

  • Simultaneous-perturbation optimization

    Perturb every parameter at once, in one random direction, and take the difference of two objective evaluations as the gradient estimate. The estimate is bad in any single round and unbiased across rounds, so the cost of a step stops growing with the number of parameters.

In the Atlas

  • Layerwise VQE training

    Circuit depth grows in stages so each newly introduced layer can be initialized and optimized locally.

Sources