Skip to content

Parameter tiers and resuming

Whether a run can be resumed depends on which parameters changed between the checkpoint and the new kd.Model.

The parameter table on every algorithm page has a "Resume" column, and every parameter carries a badge there: resume-safe or init-only. The algorithm declares that badge itself, and the badge determines whether a new value takes effect on a resume.

Tier Meaning
resume-safe the new value takes effect when resuming from a checkpoint, and the stored search state stays valid
init-only the new value applies to a fresh fit only; resuming with the change raises

A third tier is not shown in the tables. Fields that change the scientific identity of the run, such as the candidate library and derivative semantics, are identity-breaking: changing one makes the two segments a different experiment, and the resume raises.

Any config field with no declared tier is treated as init-only. The default refuses because the two mistakes have different consequences. A field that should have been declared resume-safe requires one line of edit to correct, while a resume that succeeded across two different configurations leaves the error in the result.

The tables use the config field names. Two fields are spelled differently as kd.Model keywords: num is population, and use_autograd is derivatives.

Checkpoint writing

Setting checkpoint_dir to a directory enables checkpointing. checkpoint_every sets the interval in iterations, and a checkpoint_final.pt is written when the run ends. The directory is managed by a manifest.json and cannot be reused: pointing at a non-empty directory raises, so each run needs a fresh directory. checkpoint_keep_last bounds how many periodic checkpoints are retained, and the final one is neither counted nor pruned.

import kd

dataset = kd.load_kdv()
model = kd.Model(
    algorithm="sga", generations=4, seed=42, verbose=False,
    checkpoint_dir="out/kdv-ckpt", checkpoint_every=2,
)
model.fit(dataset)

The manifest

kd.load_checkpoint_manifest returns the directory's entries in write order. It is read-only and performs no repair or cleanup. Select a checkpoint from these entries rather than globbing the directory.

for entry in kd.load_checkpoint_manifest("out/kdv-ckpt"):
    print(entry.filename, entry.kind, entry.final_status, entry.iteration, entry.best_score)
checkpoint_000000.pt periodic None 0 -18.37657775848694
checkpoint_000002.pt periodic None 2 -18.37657775848694
checkpoint_final.pt final completed 3 -18.37657775848694

An entry also carries best_expression, algorithm, seed, created_at, kd_version and more. kind separates periodic from final checkpoints, and final_status is set on the final one only: completed for a run that finished, crashed for the final checkpoint a crashed run left behind, so the two are distinguishable. Both values have exported constants, kd.KIND_FINAL and kd.FINAL_STATUS_COMPLETED.

Resume a run

fit takes a checkpoint path as resume_from. A resume restores the search state: the population, the controller weights, and the current best. The number of generations comes from this kd.Model, and the parameters are judged by the tiers above. seed is not in the tier table, so it takes the unregistered-field default of init-only. A plain resume keeps the seed the checkpoint was written with, and a different seed is rejected. Passing reseed=True to fit turns the resume into a branch: it keeps the restored search state and re-derives the random streams from this model's seed, so a changed seed is accepted. Branching is available for the algorithms whose segmentation.reseed declaration is true, today sga, dlga and discover.

resumed = kd.Model(algorithm="sga", generations=8, seed=42, verbose=False, population=25)
resumed.fit(dataset, resume_from="out/kdv-ckpt/checkpoint_final.pt")
print(resumed.best_expr_)
u_t = -0.0025*diff2_x(diff_x(u)) - 1*mul(u_x, u)

population is resume-safe, so the resume accepts the new value. With the init-only depth instead:

changed = kd.Model(algorithm="sga", generations=8, seed=42, verbose=False, depth=5)
changed.fit(dataset, resume_from="out/kdv-ckpt/checkpoint_final.pt")
ValueError: resume config mismatch for algorithm 'sga': init_only field(s) changed
vs the checkpoint: depth (stored 4 -> live 5). These fields can only be set on a
fresh fit; drop resume_from or revert them.

And with an identity-breaking change, the derivative source (derivatives="autograd", the config field use_autograd):

ValueError: resume config mismatch for algorithm 'sga': identity_breaking field(s)
changed vs the checkpoint: use_autograd (stored False -> live True). A science-identity
change starts a new lineage: run a fresh fit in a new checkpoint directory; this
checkpoint cannot be resumed under the new config.

Every message names the field and both of its values.

Add generations

generations is not a config field. For the iterative algorithms it maps to the iteration budget, so extending a run means raising generations on the resuming model. PySR stores its populations and hall of fame in the checkpoint, so raising generations on a resume extends that segment's PySR search. PySINDy solves in one pass, so for it generations cannot be raised on a resume.

The two manifests

The manifest.json above belongs to a checkpoint directory and lists what is in it. A finished run carries a second, unrelated manifest at model.result_.manifest. It records the facts needed to reproduce that run and excludes anything that varies between two identical runs: the dataset fingerprint, the KD version, the seed, the term library for the algorithms that take one, and, when the run continued an earlier one, where it resumed from.