"""Regenerate knapsack_cache/ and knapsack_cache.zip against the currently installed instanovo version. When InstaNovo bumps its Knapsack on-disk format (e.g. v1.1 -> v1.2.2 added a field to parameters.pkl, giving the "not enough values to unpack" error on load), the bundled cache becomes unreadable and the runtime falls back to multi-minute regeneration on every container cold-start. This script does that regeneration once on a workstation so the committed zip stays in sync. Run with: .venv/bin/python scripts/regenerate_knapsack.py Expect a few minutes on CPU. Output: ./knapsack_cache/{parameters.pkl,masses.npy,chart.npy} ./knapsack_cache.zip (the three files above, repacked for HF Spaces) """ from __future__ import annotations import shutil import time import zipfile from pathlib import Path from instanovo.constants import MASS_SCALE, MAX_MASS from instanovo.inference import Knapsack from instanovo.transformer.model import InstaNovo TRANSFORMER_MODEL_ID = "instanovo-v1.2.0" KNAPSACK_DIR = Path(__file__).resolve().parent.parent / "knapsack_cache" KNAPSACK_ZIP = KNAPSACK_DIR.with_suffix(".zip") def main() -> None: print(f"Loading {TRANSFORMER_MODEL_ID} for residue set...") model, _ = InstaNovo.from_pretrained(TRANSFORMER_MODEL_ID) residue_set = model.residue_set residue_masses = dict(residue_set.residue_masses) excluded = set(residue_set.special_tokens) | { k for k, v in residue_masses.items() if v <= 0 } for res in excluded: residue_masses.pop(res, None) if not residue_masses: raise SystemExit("No valid residues with positive mass; cannot regenerate.") print(f"Excluding {sorted(excluded)} from knapsack; {len(residue_masses)} residues remain.") if KNAPSACK_DIR.exists(): print(f"Clearing existing {KNAPSACK_DIR}...") shutil.rmtree(KNAPSACK_DIR) KNAPSACK_DIR.parent.mkdir(parents=True, exist_ok=True) # Knapsack.save() creates KNAPSACK_DIR itself and refuses if it already exists. start = time.time() print("Generating knapsack (this can take several minutes)...") knapsack = Knapsack.construct_knapsack( residue_masses=residue_masses, residue_indices=residue_set.residue_to_index, max_mass=MAX_MASS, mass_scale=MASS_SCALE, ) knapsack.save(str(KNAPSACK_DIR)) print(f"Saved knapsack to {KNAPSACK_DIR} ({time.time() - start:.1f}s).") print(f"Packing {KNAPSACK_ZIP}...") if KNAPSACK_ZIP.exists(): KNAPSACK_ZIP.unlink() with zipfile.ZipFile(KNAPSACK_ZIP, "w", zipfile.ZIP_DEFLATED) as zf: for f in sorted(KNAPSACK_DIR.iterdir()): zf.write(f, arcname=f.name) print(f"Wrote {KNAPSACK_ZIP} ({KNAPSACK_ZIP.stat().st_size / 1e6:.1f} MB).") print("Done. Commit the new zip with git (LFS-tracked) and push to the Space.") if __name__ == "__main__": main()