sdfrust 0.6.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sdfrust-0.6.0 → sdfrust-0.7.0}/.gitignore +1 -0
- sdfrust-0.7.0/CHANGELOG.md +241 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/Cargo.lock +185 -357
- {sdfrust-0.6.0 → sdfrust-0.7.0}/Cargo.toml +6 -3
- {sdfrust-0.6.0 → sdfrust-0.7.0}/PKG-INFO +2 -2
- {sdfrust-0.6.0 → sdfrust-0.7.0}/README.md +55 -4
- sdfrust-0.7.0/benches/mol2_parse_benchmark.rs +208 -0
- sdfrust-0.7.0/benches/roundtrip_benchmark.rs +277 -0
- sdfrust-0.7.0/benches/sdf_parse_benchmark.rs +221 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/pyproject.toml +3 -3
- sdfrust-0.7.0/scripts/gen_element_table.py +48 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/Cargo.toml +2 -2
- sdfrust-0.7.0/sdfrust-python/benchmarks/compare_tools.py +695 -0
- sdfrust-0.7.0/sdfrust-python/benchmarks/reports/after_phase1.md +290 -0
- sdfrust-0.7.0/sdfrust-python/benchmarks/reports/after_phase2.md +157 -0
- sdfrust-0.7.0/sdfrust-python/benchmarks/reports/baseline_v0.6.0.md +317 -0
- sdfrust-0.7.0/sdfrust-python/benchmarks/reports/pdbbind2024_sample2000.md +185 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/src/atom.rs +55 -1
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/src/error.rs +4 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/src/molecule.rs +35 -9
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/tests/test_basic.py +11 -1
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/tests/test_ml_validation.py +96 -167
- sdfrust-0.7.0/sdfrust-python/tests/test_regressions.py +65 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/atom.rs +31 -1
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/aromaticity.rs +216 -263
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/chirality.rs +56 -25
- sdfrust-0.7.0/src/descriptors/conjugation.rs +290 -0
- sdfrust-0.7.0/src/descriptors/element_table.rs +456 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/elements.rs +92 -218
- sdfrust-0.7.0/src/descriptors/gasteiger.rs +444 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/hybridization.rs +80 -112
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/mod.rs +9 -2
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/molecular.rs +54 -17
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/rings.rs +127 -321
- sdfrust-0.7.0/src/descriptors/stereo.rs +214 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/topological.rs +97 -73
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/valence.rs +65 -87
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/error.rs +3 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/featurize/ogb.rs +142 -58
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/fingerprints/ecfp.rs +173 -94
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/molecule.rs +41 -29
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/parser/compression.rs +5 -5
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/parser/mol2.rs +86 -64
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/parser/sdf.rs +275 -146
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/parser/sdf_v3000.rs +124 -74
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/parser/xyz.rs +17 -29
- sdfrust-0.7.0/src/writer/sdf.rs +279 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/writer/sdf_v3000.rs +9 -9
- sdfrust-0.6.0/CHANGELOG.md +0 -84
- sdfrust-0.6.0/src/descriptors/conjugation.rs +0 -325
- sdfrust-0.6.0/src/descriptors/gasteiger.rs +0 -443
- sdfrust-0.6.0/src/writer/sdf.rs +0 -178
- {sdfrust-0.6.0 → sdfrust-0.7.0}/LICENSE +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/examples/basic_usage.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/examples/benchmark.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/examples/ml_features.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/examples/parse_molecules.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/python/sdfrust/__init__.py +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/python/sdfrust/py.typed +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/.gitignore +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/LICENSE +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/README.md +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/README.md +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/basic_usage.py +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/batch_analysis.py +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/data/cholesterol.sdf +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/data/dopamine.sdf +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/data/drug_library.sdf +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/data/ibuprofen.sdf +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/format_conversion.py +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/geometry_analysis.py +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/examples/ml_features.py +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/src/bond.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/src/iterators.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/src/lib.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/src/parsing.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/src/writing.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/tests/test_geometry.py +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/sdfrust-python/tests/test_gzip.py +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/bond.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/collection.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/descriptors/bond_inference.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/featurize/mod.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/fingerprints/mod.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/geometry/angles.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/geometry/distance.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/geometry/mod.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/geometry/neighbor_list.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/geometry/rmsd.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/geometry/transform.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/graph.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/lib.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/parser/mod.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/sgroup.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/stereogroup.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/writer/mod.rs +0 -0
- {sdfrust-0.6.0 → sdfrust-0.7.0}/src/writer/mol2.rs +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.7.0] - 2026-10-14
|
|
11
|
+
|
|
12
|
+
Correctness release. sdfrust was audited and every feature that has an
|
|
13
|
+
equivalent in an established toolkit was compared against RDKit 2025.09,
|
|
14
|
+
OpenBabel 3.1 and the `ogb` package with
|
|
15
|
+
`sdfrust-python/benchmarks/compare_tools.py`. The comparison used 4,553
|
|
16
|
+
molecules (NCI first 5k, EGFR and CDK2 ligands, heavy-atom-only and explicit-H;
|
|
17
|
+
113k atoms) plus 2,000 PDBbind 2024 ligands. Each fix has a regression test.
|
|
18
|
+
|
|
19
|
+
Agreement with RDKit before → after:
|
|
20
|
+
|
|
21
|
+
| Feature | 0.6.0 | 0.7.0 |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| Molecular weight / exact mass / formula | 54% | 100% |
|
|
24
|
+
| Aromatic atoms / bonds | 97.8% / 97.2% | 100% / 100% |
|
|
25
|
+
| Hybridization | 98.8% | >99.99% |
|
|
26
|
+
| Conjugated bonds | 98.4% | >99.99% |
|
|
27
|
+
| Gasteiger charges | 46% | 100% |
|
|
28
|
+
| Rotatable bonds | 68% | 100% |
|
|
29
|
+
| SSSR ring sizes | 99.8% | 100% |
|
|
30
|
+
| OGB bond stereo | 99.4% | 100% |
|
|
31
|
+
| ECFP bits identical to RDKit Morgan | ~0% | 100% |
|
|
32
|
+
|
|
33
|
+
Parser/writer edge cases (V3000 via V2000 entry points, charge/isotope/radical
|
|
34
|
+
round-trips, malformed records, non-ASCII input, ...): 4/17 → 17/17. OGB
|
|
35
|
+
features against `ogb.utils.features`: atomic number 0% → 100%, hybridization
|
|
36
|
+
0.9% → >99.99%.
|
|
37
|
+
|
|
38
|
+
Also checked on 2,000 PDBbind 2024 ligands (3D, explicit H; 123k atoms):
|
|
39
|
+
every property agrees with RDKit at ≥99.9%. All 27,670 PDBbind 2024 ligand
|
|
40
|
+
files still parse.
|
|
41
|
+
- The only ECFP or formal-charge differences are two N-oxides that RDKit
|
|
42
|
+
rewrites while sanitizing (N=O becomes [n+][O-]); sdfrust keeps the charges
|
|
43
|
+
as written in the file.
|
|
44
|
+
- 0.08% of chiral tags differ, on RDKit's ring-stereo centers (dependent
|
|
45
|
+
stereocenters across rings), which aren't perceived yet.
|
|
46
|
+
|
|
47
|
+
### Changed (breaking): file formats and OGB encoding
|
|
48
|
+
|
|
49
|
+
- **OGB features now use the real `ogb.utils.features` encoding.** Values were
|
|
50
|
+
previously raw values on a different scale, which is incompatible with OGB's
|
|
51
|
+
`AtomEncoder`/`BondEncoder`. Models trained on 0.6 features must be retrained.
|
|
52
|
+
- atomic number is `Z - 1` (misc = 118)
|
|
53
|
+
- hybridization is `[SP, SP2, SP3, SP3D, SP3D2, misc]` (S → misc)
|
|
54
|
+
- degree is total degree including implicit H
|
|
55
|
+
- radicals are counted as electrons
|
|
56
|
+
- bond stereo uses `[NONE, Z, E, CIS, TRANS, ANY]` (wedge flags are no longer read as E/Z)
|
|
57
|
+
- out-of-range values go to the `misc` bucket
|
|
58
|
+
- `Hybridization::to_ogb_index()` returns OGB indices (SP = 0 … SP3D2 = 4, S/Other = 5).
|
|
59
|
+
- MOL2: bond type `am` (amide) is now `Single`, not `Aromatic`.
|
|
60
|
+
- MOL2: partial charges are stored in the new `Atom::partial_charge` instead of
|
|
61
|
+
being rounded into `formal_charge`. `N.4` atoms get formal charge +1.
|
|
62
|
+
- Isotopes from `M ISO` / V3000 `MASS=` are stored as absolute mass numbers in the
|
|
63
|
+
new `Atom::isotope: Option<u16>`. Previously they went into `mass_difference: i8`,
|
|
64
|
+
which also corrupted masses ≥ 128. `mass_difference` now only holds the V2000
|
|
65
|
+
atom-block column.
|
|
66
|
+
- `write_sdf` (V2000) returns `SdfError::Unwritable` for molecules it cannot
|
|
67
|
+
represent: more than 999 atoms/bonds, coordinates beyond ±9999.9999, or long atom
|
|
68
|
+
symbols. It previously wrote a corrupt file. `write_sdf_auto` falls back to V3000
|
|
69
|
+
in these cases.
|
|
70
|
+
- Writers return `SdfError::InvalidAtomIndex` for bonds or groups that reference
|
|
71
|
+
missing atoms. They previously panicked or wrote invalid files.
|
|
72
|
+
- `MaybeGzReader::Gzip` wraps `MultiGzDecoder`.
|
|
73
|
+
|
|
74
|
+
### Changed (breaking): chemistry perception (values change for the same input)
|
|
75
|
+
|
|
76
|
+
- `molecular_weight`, `exact_mass` and `Molecule::formula` count implicit
|
|
77
|
+
hydrogens, so heavy-atom-only files give the full molecule. They also use
|
|
78
|
+
isotope masses for labelled atoms, and exact mass corrects for the electron
|
|
79
|
+
mass of ions. Formulas use RDKit's style: C, H, then alphabetical, with a
|
|
80
|
+
charge suffix (`C2H3O2-`). A molecule built from atoms without bonds now
|
|
81
|
+
gets implicit H on every atom; add the bonds, or set `Atom::no_implicit`.
|
|
82
|
+
- Implicit hydrogens follow RDKit's valence model: a charged atom takes the
|
|
83
|
+
valences of its isoelectronic neutral element (N⁺ like C, B⁻² like N).
|
|
84
|
+
Radicals and the molfile valence field are honoured.
|
|
85
|
+
- Protonated/quaternary N⁺ and sulfoxide/sulfonium S can be tetrahedral
|
|
86
|
+
stereocenters (the lone pair counts as the fourth substituent).
|
|
87
|
+
- Aromaticity uses per-atom π-electron counts and fused-ring unions (RDKit's
|
|
88
|
+
model). Kekulé naphthalene, indole, azulene, purines, pyridones and
|
|
89
|
+
tropylium are recognized; sulfoxide rings are not.
|
|
90
|
+
- Hybridization comes from σ bonds plus lone pairs, and conjugation follows
|
|
91
|
+
RDKit's rules. `atom_hybridization` and `all_hybridizations` now agree.
|
|
92
|
+
- Gasteiger charges are a port of RDKit's implementation: same parameter
|
|
93
|
+
table, 12 iterations, implicit H as virtual atoms, H χ⁺ = 20.02, and formal
|
|
94
|
+
charge split over conjugated atoms. Atoms without parameters keep their
|
|
95
|
+
formal charge.
|
|
96
|
+
- `rotatable_bond_count` uses RDKit's default (strict) definition, which
|
|
97
|
+
excludes amide/ester C–X bonds, CF3/CCl3/CBr3, tert-butyl and bonds next
|
|
98
|
+
to triple bonds. Aspirin now has 2 rotatable bonds, not 3.
|
|
99
|
+
- `sssr` returns a true minimum cycle basis (Horton) with deterministic order.
|
|
100
|
+
- ECFP/Morgan fingerprints are bit-identical to RDKit's
|
|
101
|
+
`GetMorganGenerator(radius, fpSize)` with default options.
|
|
102
|
+
- Chiral tags (CW/CCW) are relative to bond order, as in RDKit. Their docs no
|
|
103
|
+
longer call them R/S.
|
|
104
|
+
- OGB bond stereo is perceived from coordinates: Z = 1, E = 2, unknown = 5.
|
|
105
|
+
- The element table covers all 118 elements, with isotope masses. MOL2 and
|
|
106
|
+
XYZ atoms are flagged `no_implicit`.
|
|
107
|
+
|
|
108
|
+
### Added
|
|
109
|
+
|
|
110
|
+
- `Atom::isotope`, `Atom::partial_charge`, `Atom::radical_electrons()`. Python
|
|
111
|
+
`Atom` exposes `isotope`, `partial_charge`, `radical` and `radical_electrons`.
|
|
112
|
+
- `SdfParser::skip_to_record_end()`, `SdfV3000Parser::skip_to_record_end()`.
|
|
113
|
+
- `sdfrust-python/benchmarks/compare_tools.py`: a differential comparison harness
|
|
114
|
+
against RDKit, OpenBabel and OGB, with Markdown/JSON reports.
|
|
115
|
+
- `tests/regression_tests.rs` and `sdfrust-python/tests/test_regressions.py`.
|
|
116
|
+
- The Python OGB validation tests compare against `ogb.utils.features` itself, and
|
|
117
|
+
CI installs RDKit and OGB so they run.
|
|
118
|
+
- `descriptors::stereo::double_bond_stereo`: E/Z from coordinates.
|
|
119
|
+
- `gasteiger_charges_with_hydrogens`: also returns implicit-H charges.
|
|
120
|
+
- `element_by_atomic_number`, `isotope_mass`; `ElementData::outer_electrons`
|
|
121
|
+
and `ElementData::valences`.
|
|
122
|
+
- `Atom::no_implicit`, also exposed in Python.
|
|
123
|
+
- `scripts/gen_element_table.py`, which regenerates the periodic table from RDKit.
|
|
124
|
+
- `compare_tools.py --rdkit-data/--smiles/--extra` for larger corpora, and
|
|
125
|
+
exact ECFP bit comparison.
|
|
126
|
+
|
|
127
|
+
### Fixed
|
|
128
|
+
|
|
129
|
+
- `parse_sdf_*` / `iter_sdf_file` silently returned an empty molecule for V3000
|
|
130
|
+
records. V3000 records are now detected per record, so mixed V2000/V3000 files
|
|
131
|
+
parse correctly.
|
|
132
|
+
- A trailing blank line after the final `$$$$` made `*_multi` fail on the whole file.
|
|
133
|
+
- Iterators (SDF V2000/V3000, MOL2) now resume at the next record after a malformed
|
|
134
|
+
one, instead of stopping, as the crate docs described.
|
|
135
|
+
- Panics on non-ASCII text in V2000 fixed-width lines. Invalid UTF-8 (e.g. Latin-1
|
|
136
|
+
property values) is now replaced instead of aborting the file.
|
|
137
|
+
- Huge declared atom counts no longer abort with an allocation failure.
|
|
138
|
+
- A missing `M END` no longer swallows the following record(s).
|
|
139
|
+
- V2000 charges: all nonzero charges are written to `M CHG`. The parser lets
|
|
140
|
+
`M CHG`/`M RAD` supersede atom-block charges, as the CTfile spec requires.
|
|
141
|
+
Previously a molecule with a +1 and a +4 charge lost the +1 when read by RDKit.
|
|
142
|
+
- Radicals: atom-block charge code 4 and `M RAD` are parsed and `M RAD` is
|
|
143
|
+
written. Radicals no longer force V3000 output.
|
|
144
|
+
- `M CHG`/`M ISO` were written using `atom.index` instead of the atom's position.
|
|
145
|
+
- Data items now end at a blank line. Property output is sorted by name, so it is
|
|
146
|
+
deterministic.
|
|
147
|
+
- MOL2: non-contiguous atom ids are resolved through an id map; element case is
|
|
148
|
+
normalized (`CL` → `Cl`); `NO_CHARGES` is honored.
|
|
149
|
+
- XYZ: atomic numbers 1–118 are supported (previously ~25 elements).
|
|
150
|
+
- `ecfp(n_bits = 0)` no longer panics (returns an empty fingerprint; Python raises
|
|
151
|
+
`ValueError`). Python `Molecule.add_bond` raises `IndexError` for bad atom indices.
|
|
152
|
+
- Gzip: multi-member (concatenated / bgzip) files are read completely.
|
|
153
|
+
- The V2000 parser and writer handle stereo code 3 ("cis/trans unknown") on
|
|
154
|
+
double bonds.
|
|
155
|
+
|
|
156
|
+
### Packaging
|
|
157
|
+
|
|
158
|
+
- Python wheels include the `geometry` feature (RMSD, transforms, neighbor
|
|
159
|
+
lists, angles), so the documented geometry API works from PyPI.
|
|
160
|
+
- macOS and Windows wheels are built for Python 3.9–3.13; 0.6.0 shipped only
|
|
161
|
+
3.12 wheels on those platforms.
|
|
162
|
+
- The Python sdist installs from source again. 0.6.0's failed because the
|
|
163
|
+
crate's `[[bench]]` targets were excluded from the package. CI now builds
|
|
164
|
+
and imports the sdist.
|
|
165
|
+
|
|
166
|
+
## [0.6.0] - 2026-02-19
|
|
167
|
+
|
|
168
|
+
### Added
|
|
169
|
+
|
|
170
|
+
- Graph adjacency infrastructure (`AdjacencyList`) with O(1) neighbor lookups and degree computation
|
|
171
|
+
- Atom valence and hydrogen count: `atom_degree()`, `implicit_hydrogen_count()`, `total_hydrogen_count()` with default valence table for ~20 elements
|
|
172
|
+
- SSSR ring perception via spanning-tree + GF(2) linear independence: `sssr()`, `ring_sizes()`, `smallest_ring_size()`
|
|
173
|
+
- Hybridization inference from bond topology: `atom_hybridization()`, `all_hybridizations()` (S/SP/SP2/SP3/SP3D/SP3D2)
|
|
174
|
+
- Aromaticity perception: two-stage (trust file annotations + Hückel 4n+2 rule on SSSR rings) with support for pyrrole, furan, thiophene
|
|
175
|
+
- Conjugation detection: aromatic bonds, SP2-SP2 single bonds, unsaturated bond chains
|
|
176
|
+
- OGB-compatible GNN featurizer: `ogb_atom_features()` [N, 9], `ogb_bond_features()` [E, 3], `ogb_graph_features()` with directed edge index
|
|
177
|
+
- ECFP/Morgan fingerprints: `ecfp()` bit vector, `ecfp_counts()` hash map, Tanimoto similarity — first pure-Rust implementation
|
|
178
|
+
- Gasteiger-Marsili partial charges: PEOE iterative algorithm with ~15 atom type parameters
|
|
179
|
+
- CIP chirality perception: `atom_chirality()`, `all_chiralities()` with R/S determination from 3D signed volume
|
|
180
|
+
- Bond inference from 3D coordinates: `infer_bonds()`, `infer_bonds_with_config()` using covalent radii
|
|
181
|
+
- Cutoff-based neighbor list for 3D GNNs: `neighbor_list()` with directed edges and distances (geometry feature)
|
|
182
|
+
- Bond angle and dihedral angle computation: `bond_angle()`, `dihedral_angle()`, `all_bond_angles()`, `all_dihedral_angles()` (geometry feature)
|
|
183
|
+
- Python bindings for all new ML features with NumPy array outputs (`get_ogb_atom_features_array()`, `get_ecfp_array()`, `get_gasteiger_charges_array()`)
|
|
184
|
+
- Python examples: format conversion, batch analysis, geometry operations, ML features
|
|
185
|
+
- Rust example: `examples/ml_features.rs`
|
|
186
|
+
|
|
187
|
+
## [0.5.0] - 2026-02-17
|
|
188
|
+
|
|
189
|
+
### Added
|
|
190
|
+
|
|
191
|
+
- Transparent gzip decompression for all file parsers (optional `gzip` feature)
|
|
192
|
+
- XYZ format parser with streaming support, atomic number input, and case normalization
|
|
193
|
+
- XYZ auto-detection in `parse_auto_*()` and `iter_auto_file()`
|
|
194
|
+
- Python bindings for XYZ parsing and gzip support
|
|
195
|
+
- PDBbind 2024 large-scale SDF benchmark test (27,670 files, 100% success rate, ~14k files/sec)
|
|
196
|
+
|
|
197
|
+
## [0.4.0] - 2026-01-30
|
|
198
|
+
|
|
199
|
+
### Added
|
|
200
|
+
|
|
201
|
+
- Python bindings via PyO3 (`sdfrust-python/` crate) with full API coverage
|
|
202
|
+
- NumPy integration for coordinate and atomic number arrays
|
|
203
|
+
- Streaming iterators for Python (`iter_sdf_file`, `iter_mol2_file`)
|
|
204
|
+
- Workspace configuration for multi-crate build
|
|
205
|
+
|
|
206
|
+
## [0.3.0] - 2026-01-30
|
|
207
|
+
|
|
208
|
+
### Added
|
|
209
|
+
|
|
210
|
+
- Molecular descriptors module: molecular weight, exact mass, heavy atom count, bond type counts
|
|
211
|
+
- Topological descriptors: ring count, ring atoms/bonds detection, rotatable bond count
|
|
212
|
+
- Element data table (~30 elements with IUPAC 2021 atomic weights)
|
|
213
|
+
- Convenience descriptor methods on `Molecule`
|
|
214
|
+
|
|
215
|
+
## [0.2.0] - 2025-01-29
|
|
216
|
+
|
|
217
|
+
### Added
|
|
218
|
+
|
|
219
|
+
- SDF V3000 parser and writer with stereogroups, SGroups, and collections
|
|
220
|
+
- Automatic V2000/V3000 format detection and auto-format writing
|
|
221
|
+
- MOL2 (TRIPOS) parser with streaming support
|
|
222
|
+
- Performance benchmarks (~220k mol/s, 4-7x faster than RDKit)
|
|
223
|
+
|
|
224
|
+
## [0.1.0] - 2025-01-25
|
|
225
|
+
|
|
226
|
+
### Added
|
|
227
|
+
|
|
228
|
+
- SDF V2000 parser with atom/bond/property blocks and M CHG/M ISO lines
|
|
229
|
+
- SDF V2000 writer with property preservation and multi-molecule output
|
|
230
|
+
- Core types: `Molecule`, `Atom`, `Bond`, `BondOrder`, `BondStereo`
|
|
231
|
+
- Molecule methods: `formula()`, `centroid()`, `translate()`, `neighbors()`, `element_counts()`
|
|
232
|
+
- Memory-efficient streaming iterator for large multi-molecule files
|
|
233
|
+
|
|
234
|
+
[Unreleased]: https://github.com/HFooladi/sdfrust/compare/v0.7.0...HEAD
|
|
235
|
+
[0.7.0]: https://github.com/HFooladi/sdfrust/compare/v0.6.0...v0.7.0
|
|
236
|
+
[0.6.0]: https://github.com/HFooladi/sdfrust/compare/v0.5.0...v0.6.0
|
|
237
|
+
[0.5.0]: https://github.com/HFooladi/sdfrust/compare/v0.4.0...v0.5.0
|
|
238
|
+
[0.4.0]: https://github.com/HFooladi/sdfrust/compare/v0.3.0...v0.4.0
|
|
239
|
+
[0.3.0]: https://github.com/HFooladi/sdfrust/compare/v0.2.0...v0.3.0
|
|
240
|
+
[0.2.0]: https://github.com/HFooladi/sdfrust/compare/v0.1.0...v0.2.0
|
|
241
|
+
[0.1.0]: https://github.com/HFooladi/sdfrust/releases/tag/v0.1.0
|