code wiki / (root) / nx_chem_morgan.nx

nx_chem_morgan.nx

buildroot/runtime/nx_chem_morgan.nx

14255 B379 linesdepth 7pulls 8 transitivereach 4 importersview sourcekind librarytopic chem
docsdependenciesstructsconstsfunctions

about

nx_chem_morgan.nx -- C2.3c milestone: Morgan extended-connectivity canonical atom ranking. Implements Weisfeiler-Lehman (1-WL) partition refinement to compute a canonical rank per atom that is invariant under graph isomorphism. Two MolGraphs representing the same molecule produce the same set of canonical ranks (modulo atom-index relabeling), so canonical SMILES emit using these ranks yields byte-identical output for graph-equivalent inputs. Algorithm (after Weininger 1989 + modern WL formulation): 1. Initial invariant per atom: packed (z, degree, charge, isotope, h_count, aromaticity) into i64. 2. Sort by initial invariant, assign DENSE class indices 0..k-1. All atoms with same packed invariant get same class. 3. Refinement step: for each atom, signature = (current_class, sorted_neighbor_classes). Sort by signature, assign new dense class indices. 4. Iterate refinement until class count stable (or MAX_ITER). 5. Final rank = position in sorted (final_class, atom_index) order. EXCEED axis hit (landscape doc): E1 -- bit-reproducible canonical form across versions/platforms. The Morgan algorithm here is content-addressed via versioned initial-invariant packing + deterministic signature sort. RDKit's BackwardsIncompatibleChanges.html documents cross-release canonical drift; this substrate's canonical is pinned to nx_chem_morgan_canonical_rank_v1 semantics (hash-pinned at the function level by source identity). Honest gaps (deferred): - Bond orders not yet used in invariants (so cyclohexane vs cyclohexene atoms could collide on initial invariant; one WL refinement step resolves it via neighbor distinctness) - Stereo not yet folded into canonicalization (C2.3d) - Tie-breaking for true graph automorphisms uses (final_class, atom_idx); not a true automorphism canonicalizer but adequate for graph-equivalent input alternatives

dependencies 2 imports · 2 importers

nx_chem.nx nx_chem_molecule.nx nx_chem_morgan.nx nx_chem_morgan_test.nx nx_chem_smiles_emit.nx

imports: nx_chem.nxnx_chem_molecule.nx

imported by: nx_chem_morgan_test.nxnx_chem_smiles_emit.nx

structs

none

consts

43const NX_MORGAN_MAX_DEG: nx_int = 7 // max neighbors per atom (organic typical <= 4)
44const NX_MORGAN_MAX_ITER: nx_int = 32 // safety cap for refinement loop
45const NX_MORGAN_SIG_LEN: nx_int = 8 // 1 (own class) + 7 (neighbor classes) = 8 nx_int per row

functions

57func nx_chem_morgan_initial_invariant(m: *MolGraph, atom_idx: nx_int) -> nx_int
93func nx_chem_morgan_get_neighbors(m: *MolGraph, atom_idx: nx_int, out: *nx_int) -> nx_int
118func nx_chem_morgan_sort_asc(arr: *nx_int, n: nx_int) -> nx_int
144func nx_chem_morgan_signature(
174func nx_chem_morgan_sig_cmp(sig_a: *nx_int, sig_b: *nx_int) -> nx_int
189func nx_chem_morgan_sort_indices_by_sig(indices: *nx_int, n: nx_int, sigs: *nx_int) -> nx_int
219func nx_chem_morgan_refine(
255func nx_chem_morgan_densify_initial(
303func nx_chem_morgan_canonical_rank(m: *MolGraph, rank_out: *nx_int) -> nx_int