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