Batch Workflows¶
MoleculeBatch is an ordered collection for processing many molecules with a
single API call. Valid records keep their original input order through
transform, export, and filtering steps.
import cosmolkit as ck
batch = ck.mols_from_smiles_list(
["CCO", "c1ccccc1", "not-smiles"],
errors=ck.BatchErrorMode.KEEP,
).with_parallel_jobs(8)
prepared = batch.with_hydrogens().with_2d_coordinates()
print(prepared.valid_mask)
print(prepared.errors)
repr() on BatchParams, BatchQueryParams, BatchExportParams and
BatchImageParams displays every registered configuration field and its
current value, including nested configuration. Printing does not execute a
progress callback or modify the configuration.
Error Handling¶
Batch construction, transforms, and exports accept errors to control
per-record failures:
"raise"raises an exception when a record fails."keep"keeps failed records and exposes structured errors. Export methods write valid records and include both existing input errors and new export errors in the returned report.total == success + failedandfailed == len(errors); there is no separate skipped state.
Omitting errors (or passing None) inherits the batch policy for transforms
and exports. New batches default to "raise". An explicit override becomes
the returned batch’s policy without modifying the source batch; error_mode
reports that policy. Slices and filtering preserve it. Default BatchParams
and BatchExportParams therefore display errors=None (inherit).
Important
errors="keep" is not a pipeline-wide guarantee that calls never raise.
Queries reject a batch containing failed records before computing any rows.
The BatchValidationError retains the indexed errors and explains how to
filter valid records before retrying. A new calculation error on a valid
record also raises; no partial query result is returned. The source batch
is unchanged.
batch = ck.mols_from_smiles_list(["CCO", "not-smiles"], errors="keep")
print(batch.errors) # Inspect failures before filtering.
clean = batch[batch.valid_mask] # Or batch.with_valid_records().
print(clean.to_smiles())
# ['CCO'] — filtering preserves survivor order and renumbers the rows.
clean.to_smiles(rooted_at_atom=99)
# Raises BatchValidationError: atom 99 is out of range for CCO.
String modes remain supported, but Python callers can also pass
BatchErrorMode enum members. Per-record BatchError values expose the
input index, operation name, and message:
for error in batch.errors:
print(error.index, error.operation, error.message)
try:
ck.mols_from_smiles_list(["C1CC"], errors=ck.BatchErrorMode.RAISE)
except ck.BatchValidationError as exc:
print(exc.record_errors) # Indexed failures; exc.errors is their count.
The read-only BATCH_ERROR_MODE_MAP converts external string names to enum
members when needed.
Batch Values¶
MoleculeBatch behaves like an ordered Python container. Valid records are
returned as Molecule objects and invalid kept records are returned as
None:
Boolean selection accepts Python boolean lists and one-dimensional NumPy boolean arrays, including strided views. The mask length must match the batch; numeric arrays are not converted to boolean masks.
molecules = prepared.to_list()
first = prepared[0]
tail = prepared[5:]
valid = prepared[prepared.valid_mask]
for molecule in prepared:
if molecule is not None:
print(molecule.to_smiles())
Integer indexing returns Molecule | None because kept invalid records are
represented as None. Slices, integer index lists, and boolean masks return a
MoleculeBatch. Selecting records does not compute molecule results: a slice
may still contain failed records, and queries on that slice will reject them.
Use batch[batch.valid_mask] to select only valid records. Selection preserves
the requested order and the batch-level parallel-job setting.
Retained errors keep their original source indices even after reordering;
use valid_mask to locate valid rows in the current batch. New calculation
errors after filtering refer to the filtered batch’s indices.
Export Images¶
report = prepared.write_images(
"molecule_images",
format="png",
width=300,
height=300,
errors="keep",
filenames=["ethanol.png", "benzene.png", "invalid.png"],
report_path="image_errors.json",
)
print(report.total, report.success, report.failed)
filenames is optional. Entries must match the batch length; None uses
the default zero-padded name for that record. Names are relative to the output
directory, and missing extensions are filled from format.
Export SDF¶
SDF input/output paths, output directories and report paths accept str or
os.PathLike[str], including pathlib.Path. This also applies to
SdfDataset.open(), SdfReader.open() and their configured forms.
Byte paths are rejected; custom path objects must return text from
__fspath__(). Parameter-object and keyword-based calls use the same rules.
report = prepared.write_sdf(
"prepared.sdf",
format="v2000",
errors="keep",
report_path="sdf_errors.csv",
)
Use write_sdf_files() when each valid record should be written to its own SDF
file:
report = prepared.write_sdf_files(
"prepared_records",
format="v2000",
errors="keep",
filenames=["ethanol", "benzene.sdf", "invalid.sdf"],
)
Derived Outputs¶
Remove retained failures explicitly before querying. Keep prepared.errors
if you need the original input indices; filtering does not modify prepared.
prepared = prepared.with_valid_records()
smiles = prepared.to_smiles()
rooted = prepared.to_smiles(rooted_at_atom=0)
explicit = prepared.to_smiles(
all_bonds_explicit=True,
all_hydrogens_explicit=True,
)
svgs = prepared.to_svg(width=300, height=300)
bounds = prepared.dg_bounds_matrix()
fingerprints = prepared.fingerprint_morgan(fp_size=2048)
patterns = prepared.fingerprint_pattern(n_bits=2048, tautomeric=False)
atom_pairs = prepared.fingerprint_atom_pair(fp_size=2048)
Fingerprint method names and algorithm parameters match Molecule. Pass
either a parameter object or algorithm keywords. Execution keywords remain
independent of algorithm configuration:
options = ck.MorganFingerprintParams(radius=2)
fingerprints = prepared.fingerprint_morgan(
options,
n_jobs=4,
)
Morgan, Pattern, and AtomPair batch APIs use the same source-backed cores as
the single-molecule APIs and preserve record order. Pattern batch calls reuse the fixed compile-once
SMARTS table and return one result per input; they are not the separate RDKit
MolBundle intersection operation. Topological Torsion exposes the same
ordered bulk execution through its generator methods. Morgan and AtomPair can also collect
provenance through the following MoleculeBatch conveniences:
results = prepared.fingerprint_morgan_with_output(
ck.MorganFingerprintParams(radius=2, fp_size=2048),
)
for result in results:
if result is not None:
print(result.fingerprint.on_bits)
print(result.additional_output.bit_info_map)
atom_pair_results = prepared.fingerprint_atom_pair_with_output(
ck.AtomPairFingerprintParams(fp_size=2048),
)
for result in atom_pair_results:
if result is not None:
print(result.fingerprint.on_bits)
print(result.additional_output.bit_info_map)
SMILES Options¶
to_smiles() accepts the same output-shaping options for every record:
isomeric_smilesincludes stereochemical and isotopic information.canonicalreturns canonical SMILES when enabled.kekulewrites aromatic systems in Kekule form.clean_stereonormalizes stereo output where possible.all_bonds_explicitwrites explicit bond symbols.all_hydrogens_explicitwrites explicit hydrogens.include_dative_bondsincludes dative bond notation.ignore_atom_map_numbersomits atom map numbers from canonical decisions.rooted_at_atomstarts traversal from a selected atom index.
Batch Chirality¶
Batch SMILES output preserves isomeric chirality by default:
chiral_batch = ck.mols_from_smiles_list(
["F[C@H](Cl)Br", "F[C@@H](Cl)Br"],
errors="raise",
)
print(chiral_batch.to_smiles(isomeric_smiles=True))
print(chiral_batch.to_smiles(isomeric_smiles=False))
Use canonical=False when you want output to stay closer to each record’s
input traversal while keeping the same CW/CCW chiral tag path:
print(chiral_batch.to_smiles(canonical=False))
Parallel Work¶
Batch calls use one worker by default; n_jobs=1 can be omitted. Explicit
n_jobs or a stored with_parallel_jobs() setting enables more workers.
Changing the worker count never changes fingerprint parameters or results.
with_parallel_jobs() returns a new batch with a default worker count for
later parallel operations. Because molecule values use copy-on-write storage,
this configuration step does not duplicate the molecular data.
configured = batch.with_parallel_jobs(8)
prepared = configured.with_2d_coordinates(errors="keep")
smiles = prepared.with_valid_records().to_smiles()
Method-level n_jobs still overrides the batch default for a single call:
svgs = prepared.with_valid_records().to_svg(300, 300, n_jobs=2)
with_progress_bar() returns a new batch with a default Rust-side progress
bar setting. Progress is emitted by Rust to stderr, matching the usual terminal
stream for progress indicators, and method-level progress_bar overrides the
batch default for one call:
tracked = batch.with_parallel_jobs(8).with_progress_bar(True)
prepared = tracked.with_2d_coordinates(errors="keep")
smiles = prepared.with_valid_records().to_smiles(progress_bar=False)
Pass None to clear the stored batch-level override:
default_scheduled = prepared.with_parallel_jobs(None)
quiet = prepared.with_progress_bar(None)