cosmolkit-core 0.2.13

Redesigned COSMolKit core with value-style molecule state and explicit topology operation contracts
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SupportStatus {
    Supported,
    SupportedWithRdkitParity { rdkit_version: &'static str },
    PreservedOnly,
    Experimental,
    Unsupported { reason: &'static str },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeatureCategory {
    Core,
    TopologyOperation,
    Io,
    Fingerprint,
    Drawing,
    Stereo,
    Valence,
    Batch,
    BioHierarchy,
    BioCoordinate,
    BioSelection,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FeatureSpec {
    pub name: &'static str,
    pub category: FeatureCategory,
    pub status: SupportStatus,
    pub parity_sensitive: bool,
    pub docs: &'static str,
}

impl FeatureSpec {
    #[must_use]
    pub const fn unsupported(
        name: &'static str,
        category: FeatureCategory,
        parity_sensitive: bool,
        reason: &'static str,
        docs: &'static str,
    ) -> Self {
        Self {
            name,
            category,
            status: SupportStatus::Unsupported { reason },
            parity_sensitive,
            docs,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("unsupported feature {feature}: {reason}")]
pub struct UnsupportedFeatureError {
    pub feature: &'static str,
    pub reason: &'static str,
}

impl UnsupportedFeatureError {
    #[must_use]
    pub const fn from_spec(feature: &'static FeatureSpec) -> Self {
        let reason = match feature.status {
            SupportStatus::Unsupported { reason } => reason,
            _ => "feature is not available in this build",
        };
        Self {
            feature: feature.name,
            reason,
        }
    }
}

pub const SMILES_PARSE_FEATURE: FeatureSpec = FeatureSpec {
    name: "smiles.parse",
    category: FeatureCategory::Io,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Parse SMILES into Molecule with registered sanitize, valence, aromaticity, kekulize, ring, hydrogen, stereo, and supported CX postprocessing. The documented parse scope is checked against pinned RDKit graph-state golden data; separately scoped upstream extensions are not advertised as part of this boundary.",
};

pub const SMILES_WRITE_FEATURE: FeatureSpec = FeatureSpec {
    name: "smiles.write",
    category: FeatureCategory::Io,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Write canonical, noncanonical, rooted, isomeric, kekule, explicit-bond, explicit-hydrogen, dative, atom-map, and supported CX SMILES branches. The documented branch matrix is checked exactly against pinned RDKit output; other upstream writer extensions are separate capability boundaries.",
};

pub const MOLBLOCK_IO_FEATURE: FeatureSpec = FeatureSpec {
    name: "molblock.io",
    category: FeatureCategory::Io,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Read and write the documented V2000/V3000 MolBlock and SDF branches with sanitize, remove-H, strict-parsing, coordinate-dimension, stereo, SGroup, RGroup, alias, value-line, and aromatic-bond handling. Covered fields and output branches are checked against pinned RDKit; unrelated CTAB extensions are outside this named boundary.",
};

pub const MOL2_READ_FEATURE: FeatureSpec = FeatureSpec {
    name: "mol2.read",
    category: FeatureCategory::Io,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Read the exposed Tripos MOL2 profile with sanitize, remove-H, CORINA-variant, and cleanup-substructure controls. Topology, atom and bond fields, coordinates, chirality, and SMILES output are checked against pinned RDKit.",
};

pub const HYDROGENS_FEATURE: FeatureSpec = FeatureSpec {
    name: "molecule.hydrogens",
    category: FeatureCategory::TopologyOperation,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Value-style and explicit in-place AddHs/RemoveHs operations with parameterized removal, atom and coordinate remapping, isotope tracking, stereo maintenance, and operation-contract checks. The documented state model is covered by graph-state parity and focused source tests.",
};

pub const COORDINATE_2D_FEATURE: FeatureSpec = FeatureSpec {
    name: "coordinates.2d",
    category: FeatureCategory::TopologyOperation,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Generate 2D coordinates through registered value-style and in-place operations, including the local RDKit coordinate path, templates, constrained depiction, normalization, and straightening used by drawing and MolBlock workflows. Prepared coordinates and final depiction outputs are checked against pinned RDKit; the separate CoordGen runtime is outside this local-coordinate boundary.",
};

pub const COORDINATE_EDIT_FEATURE: FeatureSpec = FeatureSpec {
    name: "coordinates.edit",
    category: FeatureCategory::TopologyOperation,
    status: SupportStatus::Supported,
    parity_sensitive: true,
    docs: "Edit 2D and 3D coordinate blocks through registered value-style and explicit in-place operations, including replacement, clearing, and appending conformers with operation-contract validation.",
};

pub const CONFORMER_GENERATION_FEATURE: FeatureSpec = FeatureSpec {
    name: "coordinates.3d.conformer_generation",
    category: FeatureCategory::TopologyOperation,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Generate one or multiple 3D conformers through the exposed DG/KDG/ETDG/ETKDG presets and parameter controls, including explicit seeds, pruning, coordMap, CPCI, custom bounds matrices, stereo checks, macrocycles, and small-ring torsions. Deterministic covered branches are compared with pinned RDKit coordinates and status fields.",
};

pub const SANITIZE_FEATURE: FeatureSpec = FeatureSpec {
    name: "molecule.sanitize",
    category: FeatureCategory::TopologyOperation,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Run modeled RDKit sanitize stages as registered weak topology-state operations, including cleanup, properties, rings, kekulization, radicals, aromaticity, conjugation, hybridization, chirality cleanup, and hydrogen adjustment. End-to-end graph parity and focused stage tests cover the documented state and option boundary.",
};

pub const KEKULIZE_FEATURE: FeatureSpec = FeatureSpec {
    name: "molecule.with_kekulized_bonds",
    category: FeatureCategory::TopologyOperation,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Rewrite modeled aromatic systems through registered value-style and in-place kekulization, including fragment filtering, fused-ring candidate selection, backtracking, dummy-query permutations, clear-aromatic-flags behavior, and restoration on failure. Covered output branches are checked against pinned RDKit.",
};

pub const FINGERPRINT_FEATURE: FeatureSpec = FeatureSpec {
    name: "fingerprint.morgan",
    category: FeatureCategory::Fingerprint,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Provide the enumerated Morgan sparse-count, sparse-bit, hashed-count, explicit-bit, AdditionalOutput, and MACCS raw/public projection branches with exact pinned-RDKit parity. Other fingerprint families have separate feature specifications and do not silently fall back to Morgan.",
};

pub const ATOM_PAIR_FINGERPRINT_FEATURE: FeatureSpec = FeatureSpec {
    name: "fingerprint.atom_pair",
    category: FeatureCategory::Fingerprint,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Provide the source-backed AtomPair sparse-count, folded-count, sparse-bit, explicit-bit, AdditionalOutput, 2D/3D distance, chirality, selector, custom-invariant, count-simulation, metadata/JSON, and ordered batch branches through one generator core. Exact validation covers every mutually parseable ChEMBL 37 record across ten profiles and 118,809,964 comparisons against pinned RDKit.",
};

pub const TOPOLOGICAL_TORSION_FINGERPRINT_FEATURE: FeatureSpec = FeatureSpec {
    name: "fingerprint.topological_torsion",
    category: FeatureCategory::Fingerprint,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Provide the modern Topological Torsion sparse-count, sparse-bit, folded-count, explicit-bit, AdditionalOutput, JSON, scalar, ordered bulk, and Rust batch branches plus the three legacy compatibility adapters. Focused and maintained 5,000-row validation covers modern and legacy options, selections, chirality, path behavior, provenance, collisions, and errors. The complete ChEMBL 37 audit matches every mutually parseable record across nine profiles and 127,503,376 exact vector/provenance comparisons against pinned RDKit. RDKFingerprint, Atom Pair fingerprints, and unrelated fingerprint families remain separate capabilities.",
};

pub const AVALON_FINGERPRINT_FEATURE: FeatureSpec = FeatureSpec {
    name: "fingerprint.avalon",
    category: FeatureCategory::Fingerprint,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Provide the source-backed Avalon/REACCS explicit-bit path for n_bits, is_query, and bit_flags, including source byte rounding, aromaticity passes, query branches, and non-SSS feature families. The pinned RDKit adapter matches exactly across the maintained 5,000-row, 23-profile validation matrix; count/string overloads and unrelated Avalon APIs remain out of scope.",
};

pub const DESCRIPTORS_FEATURE: FeatureSpec = FeatureSpec {
    name: "descriptors.molecular",
    category: FeatureCategory::Core,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "RDKit-aligned molecular descriptors exposed through the Rust API and Python bindings. The supported surface covers average and exact molecular weight (including only-heavy mode), formula options, H-bond donor/acceptor counts, fraction Csp3, every exposed Crippen include-H/force combination, every exposed TPSA force/S/P combination, aromatic-ring count, rotatable-bond modes, and QED. Supported corpus rows are compared field-by-field and bit-for-bit against pinned RDKit golden data, while inputs rejected by RDKit are required to fail in COSMolKit as well.",
};

pub const SUBSTRUCTURE_FEATURE: FeatureSpec = FeatureSpec {
    name: "substructure.match",
    category: FeatureCategory::Core,
    status: SupportStatus::Experimental,
    parity_sensitive: true,
    docs: "Experimental molecule-query substructure matching through the current VF2 surface. Direct SMARTS-query parity and broader RDKit query semantics are separate future capability boundaries and are not part of this feature's current parity claim.",
};

pub const DRAWING_FEATURE: FeatureSpec = FeatureSpec {
    name: "drawing.depiction",
    category: FeatureCategory::Drawing,
    status: SupportStatus::Supported,
    parity_sensitive: true,
    docs: "Render the documented molecule depiction surface as SVG and PNG. Covered SVG output is compared exactly with pinned RDKit after normalizing only tool identifiers; PNG is deterministic local rasterization of that SVG through usvg and resvg, not a Cairo or Qt byte-parity claim. Unsupported annotation branches fail explicitly.",
};

pub const STEREO_FEATURE: FeatureSpec = FeatureSpec {
    name: "stereo.perception",
    category: FeatureCategory::Stereo,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Inspect modeled atom and bond stereo state and assign atom ChiralTag values from a selected 3D conformer through the registered with_chiral_tags_from_structure operation. This stable 3D assignment surface has exact full-state parity with pinned RDKit 2026.03.1 assignChiralTypesFrom3D across all 77 fixed oracle records, covering default or explicit conformer selection, replacement control, tetrahedral C/S/Se centers, environment-enabled square-planar/trigonal-bipyramidal/octahedral centers, property updates, no-op paths, and source-defined errors. It preserves topology and coordinates and commits no partial state on error. The broader assignStereochemistryFrom3D workflow, 3D double-bond direction/E-Z assignment, CIP orchestration, and distinct-substituent validation are separate capabilities and are not claimed by this feature.",
};

pub const VALENCE_FEATURE: FeatureSpec = FeatureSpec {
    name: "valence.assignment",
    category: FeatureCategory::Valence,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Assign modeled RDKit valence, implicit hydrogens, property-cache state, and radicals through registered operations used by parsing and sanitization. Covered graph fields and focused source branches match pinned RDKit; unmodeled query or dative states fail explicitly.",
};

pub const RINGS_FEATURE: FeatureSpec = FeatureSpec {
    name: "rings.symm_sssr",
    category: FeatureCategory::TopologyOperation,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Provide modeled SSSR, symmetrized SSSR, fast ring traversal, and URF-backed ring-family and relevant-cycle perception. Ring membership is covered by pinned-RDKit graph parity and focused source tests cover the supported SSSR and ring-family graph states.",
};

pub const AROMATICITY_FEATURE: FeatureSpec = FeatureSpec {
    name: "aromaticity.assignment",
    category: FeatureCategory::TopologyOperation,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Assign modeled RDKit aromatic atom and bond state through registered operations. Covered graph states match pinned RDKit and unsupported aromaticity models or source states fail explicitly.",
};

pub const INCHI_FEATURE: FeatureSpec = FeatureSpec {
    name: "inchi.scalar",
    category: FeatureCategory::Io,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "The four audited scalar APIs (MolToInchi, MolToInchiKey, InchiToInchiKey, and MolFromInchi) reproduce pinned RDKit 2026.03.1 and official InChI v1.07.5 behavior exactly where the official C source defines behavior. The NormalizeAndCompare initial-buffer allocation-failure path executes undefined C behavior, so COSMolKit returns a deterministic structured allocation error. Other official InChI API families are outside this public feature.",
};

pub const BATCH_FEATURE: FeatureSpec = FeatureSpec {
    name: "batch.operations",
    category: FeatureCategory::Batch,
    status: SupportStatus::Supported,
    parity_sensitive: false,
    docs: "Stable batch construction from SMILES lists, ordered transformations via registered molecule operations, \
           error modes (Strict/KeepErrors), valid mask, filter valid, SMILES export with params, \
           PNG image export, configurable progress reporting, and deterministic ordered parallel \
           execution through Rayon with batch-level or per-call worker-count selection.",
};

pub const BIO_STRUCTURE_FEATURE: FeatureSpec = FeatureSpec {
    name: "bio.structure",
    category: FeatureCategory::BioHierarchy,
    status: SupportStatus::Supported,
    parity_sensitive: false,
    docs: "Flat-row BioStructure hierarchy and coordinate storage for protein, PDB, mmCIF, mmJSON, and chem-comp workflows. Public access is read-only; mutation goes through crate-internal builders or registered BioStructure operations.",
};

pub const BIO_PDB_READ_FEATURE: FeatureSpec = FeatureSpec {
    name: "bio.pdb.read",
    category: FeatureCategory::Io,
    status: SupportStatus::Supported,
    parity_sensitive: true,
    docs: "Read the documented Gemmi-aligned PDB structural record scope into BioStructure, including decimal and hybrid-36 identifiers, coordinates, models, anisotropic data, hierarchy, entities, connections, selected metadata, crystallography, and NCS transforms.",
};

pub const BIO_MMCIF_READ_FEATURE: FeatureSpec = FeatureSpec {
    name: "bio.mmcif.read",
    category: FeatureCategory::Io,
    status: SupportStatus::Supported,
    parity_sensitive: true,
    docs: "Read the documented Gemmi-aligned mmCIF, mmJSON, and chem-comp structural scope into BioStructure, including atom sites, entities and sequences, references, connectivity, assemblies, SIFTS mappings, crystallography, and NCS transforms. Unmodeled structural categories fail explicitly where required.",
};

pub const BIO_MMCIF_WRITE_FEATURE: FeatureSpec = FeatureSpec {
    name: "bio.mmcif.write",
    category: FeatureCategory::Io,
    status: SupportStatus::Supported,
    parity_sensitive: true,
    docs: "Serialize BioStructure as canonical Gemmi-aligned mmCIF with source-defined category groups and CIF formatting options. The writer emits state represented by BioStructure and belongs only to BioStructure, not Protein or Molecule.",
};

#[deprecated(note = "use BIO_PDB_READ_FEATURE")]
pub const BIO_PDB_COORDINATE_SUBSET_READ_FEATURE: FeatureSpec = BIO_PDB_READ_FEATURE;

#[deprecated(note = "use BIO_MMCIF_READ_FEATURE")]
pub const BIO_MMCIF_ATOM_SITE_SUBSET_READ_FEATURE: FeatureSpec = BIO_MMCIF_READ_FEATURE;

pub const PUBLIC_FEATURES: &[&FeatureSpec] = &[
    &SMILES_PARSE_FEATURE,
    &SMILES_WRITE_FEATURE,
    &MOLBLOCK_IO_FEATURE,
    &MOL2_READ_FEATURE,
    &HYDROGENS_FEATURE,
    &COORDINATE_2D_FEATURE,
    &COORDINATE_EDIT_FEATURE,
    &CONFORMER_GENERATION_FEATURE,
    &SANITIZE_FEATURE,
    &KEKULIZE_FEATURE,
    &FINGERPRINT_FEATURE,
    &ATOM_PAIR_FINGERPRINT_FEATURE,
    &TOPOLOGICAL_TORSION_FINGERPRINT_FEATURE,
    &AVALON_FINGERPRINT_FEATURE,
    &DESCRIPTORS_FEATURE,
    &SUBSTRUCTURE_FEATURE,
    &DRAWING_FEATURE,
    &STEREO_FEATURE,
    &VALENCE_FEATURE,
    &RINGS_FEATURE,
    &AROMATICITY_FEATURE,
    &INCHI_FEATURE,
    &BATCH_FEATURE,
    &DG_BOUNDS_FEATURE,
    &BIO_STRUCTURE_FEATURE,
    &BIO_PDB_READ_FEATURE,
    &BIO_MMCIF_READ_FEATURE,
    &BIO_MMCIF_WRITE_FEATURE,
    &BIO_SELECTION_FEATURE,
];

pub const DG_BOUNDS_FEATURE: FeatureSpec = FeatureSpec {
    name: "distgeom.bounds_matrix",
    category: FeatureCategory::Core,
    status: SupportStatus::SupportedWithRdkitParity {
        rdkit_version: "2026.03.1",
    },
    parity_sensitive: true,
    docs: "Generate the documented RDKit-style distance-geometry bounds matrix, including triangle smoothing, topological 1-2 through 1-5 bounds, ring and macrocycle handling, and VDW lower bounds. Matrix shape and every covered lower and upper entry are compared with pinned RDKit.",
};

pub const BIO_SELECTION_FEATURE: FeatureSpec = FeatureSpec {
    name: "bio.selection",
    category: FeatureCategory::BioSelection,
    status: SupportStatus::Experimental,
    parity_sensitive: false,
    docs: "Experimental BioStructure selection and filtering operations (e.g. remove_waters).",
};

#[cfg(test)]
mod tests {
    use super::{ATOM_PAIR_FINGERPRINT_FEATURE, PUBLIC_FEATURES, SupportStatus};

    #[test]
    fn atom_pair_support_metadata_records_the_validated_rdkit_boundary() {
        assert_eq!(
            ATOM_PAIR_FINGERPRINT_FEATURE.status,
            SupportStatus::SupportedWithRdkitParity {
                rdkit_version: "2026.03.1",
            }
        );
        assert!(
            PUBLIC_FEATURES
                .iter()
                .any(|feature| std::ptr::eq(*feature, &ATOM_PAIR_FINGERPRINT_FEATURE))
        );
    }
}