Source code for matmelt.core.seeds

"""Seed-id generation: the element set -> deterministic combination ids (3.0).

Pure stdlib -- no science imports, no store, no I/O. This is the n-ary knob's
engine: the user configures ``run.elements`` plus ``run.combination_sizes``
(``[1]`` for single elements, ``[2]`` for binary, ``[2, 3]`` to add ternary,
...; omitted = all sizes 1..N, expanded by the config schema), and every run
enumerates the same combinations in the same order, so seed ids are stable
across runs and machines (``enqueue`` idempotency then makes re-seeding a
no-op).

A seed id is the sorted element symbols joined with ``-`` (``"Cl-Na"``,
``"Cl-Na-O"``): filesystem-safe, human-readable, and unambiguous, since
element symbols never contain ``-``.
"""

from __future__ import annotations

import itertools
from collections.abc import Sequence


[docs] def element_combinations(elements: Sequence[str], sizes: Sequence[int]) -> list[str]: """Enumerate the seed ids for ``elements`` at each combination size. Parameters ---------- elements Element symbols from ``run.elements``. Deduplicated and sorted before anything is enumerated, so the result does not depend on config order. sizes Combination sizes to enumerate, deduplicated and sorted. Size 1 yields the bare element symbols (elemental seeds). A size larger than the element set yields nothing for that size -- enumeration stays silent and total, and the preflight is what reports such no-ops. Returns ------- list of str Seed ids, grouped by ascending size and lexicographic within each size. Raises ------ ValueError If any size is below 1. The config schema enforces the same floor; this guard is for direct callers. Examples -------- >>> element_combinations(["Na", "Cl"], [2]) ['Cl-Na'] >>> element_combinations(["O", "Na", "Cl"], [1, 3]) ['Cl', 'Na', 'O', 'Cl-Na-O'] >>> element_combinations(["Na", "Cl"], [3]) # more elements than we have [] """ symbols = sorted(set(elements)) out: list[str] = [] for size in sorted(set(sizes)): if size < 1: raise ValueError(f"combination size must be >= 1, got {size}") out.extend("-".join(combo) for combo in itertools.combinations(symbols, size)) return out