def is_stereo_compatible(tra, sgr1, sgr2): """ is this transformation compatible with the reactant/product stereo assignments? """ cgr1 = without_stereo_parities(sgr1) cgr2 = without_stereo_parities(sgr2) atm_key_dct = full_isomorphism(apply(tra, cgr1), cgr2) # determine the stereo centers which are preserved in the transformation sgr1 = _relabel(sgr1, atm_key_dct) atm_keys = sorted(atom_stereo_keys(sgr1) & atom_stereo_keys(sgr2)) bnd_keys = sorted(bond_stereo_keys(sgr1) & bond_stereo_keys(sgr2)) atm_pars1 = dict_.values_by_key(atom_stereo_parities(sgr1), atm_keys) atm_pars2 = dict_.values_by_key(atom_stereo_parities(sgr2), atm_keys) bnd_pars1 = dict_.values_by_key(bond_stereo_parities(sgr1), bnd_keys) bnd_pars2 = dict_.values_by_key(bond_stereo_parities(sgr2), bnd_keys) atm_ngb_keys_dct1 = atom_neighbor_keys(sgr1) atm_ngb_keys_dct2 = atom_neighbor_keys(sgr2) ret = True for atm_key, par1, par2 in zip(atm_keys, atm_pars1, atm_pars2): atm_ngb_keys1 = stereo_sorted_atom_neighbor_keys( sgr1, atm_key, atm_ngb_keys_dct1[atm_key]) atm_ngb_keys2 = stereo_sorted_atom_neighbor_keys( sgr2, atm_key, atm_ngb_keys_dct2[atm_key]) if _permutation_parity(atm_ngb_keys1, atm_ngb_keys2): ret &= (par1 == par2) else: ret &= (par1 != par2) for bnd_key, par1, par2 in zip(bnd_keys, bnd_pars1, bnd_pars2): atm1_key, atm2_key = bnd_key atm1_ngb_key1 = stereo_sorted_atom_neighbor_keys( sgr1, atm1_key, atm_ngb_keys_dct1[atm1_key] - {atm2_key})[0] atm2_ngb_key1 = stereo_sorted_atom_neighbor_keys( sgr1, atm2_key, atm_ngb_keys_dct1[atm2_key] - {atm1_key})[0] atm1_ngb_key2 = stereo_sorted_atom_neighbor_keys( sgr2, atm1_key, atm_ngb_keys_dct2[atm1_key] - {atm2_key})[0] atm2_ngb_key2 = stereo_sorted_atom_neighbor_keys( sgr2, atm2_key, atm_ngb_keys_dct2[atm2_key] - {atm1_key})[0] if not ((atm1_ngb_key1 != atm1_ngb_key2) ^ (atm2_ngb_key1 != atm2_ngb_key2)): ret &= (par1 == par2) else: ret &= (par1 != par2) return ret
def set_stereo_from_geometry(gra, geo, geo_idx_dct=None): """ set graph stereo from a geometry (coordinate distances need not match connectivity -- what matters is the relative positions at stereo sites) """ gra = without_stereo_parities(gra) last_gra = None atm_keys = sorted(atom_keys(gra)) geo_idx_dct = (geo_idx_dct if geo_idx_dct is not None else {atm_key: idx for idx, atm_key in enumerate(atm_keys)}) # set atom and bond stereo, iterating to self-consistency atm_keys = set() bnd_keys = set() while last_gra != gra: last_gra = gra atm_keys.update(stereogenic_atom_keys(gra)) bnd_keys.update(stereogenic_bond_keys(gra)) gra = _set_atom_stereo_from_geometry(gra, atm_keys, geo, geo_idx_dct) gra = _set_bond_stereo_from_geometry(gra, bnd_keys, geo, geo_idx_dct) return gra
def stereomers(gra): """ all stereomers, ignoring this graph's assignments """ bool_vals = (False, True) def _expand_atom_stereo(sgr): atm_ste_keys = stereogenic_atom_keys(sgr) nste_atms = len(atm_ste_keys) sgrs = [set_atom_stereo_parities(sgr, dict(zip(atm_ste_keys, atm_ste_par_vals))) for atm_ste_par_vals in itertools.product(bool_vals, repeat=nste_atms)] return sgrs def _expand_bond_stereo(sgr): bnd_ste_keys = stereogenic_bond_keys(sgr) nste_bnds = len(bnd_ste_keys) sgrs = [set_bond_stereo_parities(sgr, dict(zip(bnd_ste_keys, bnd_ste_par_vals))) for bnd_ste_par_vals in itertools.product(bool_vals, repeat=nste_bnds)] return sgrs last_sgrs = [] sgrs = [without_stereo_parities(gra)] while sgrs != last_sgrs: last_sgrs = sgrs sgrs = list(itertools.chain(*map(_expand_atom_stereo, sgrs))) sgrs = list(itertools.chain(*map(_expand_bond_stereo, sgrs))) return tuple(sorted(sgrs, key=frozen))
def _stereo_corrected_geometry(sgr, geo, geo_idx_dct): """ correct the stereo parities of a geometry (works iterately to handle cases of higher-order stereo) """ assert sgr == explicit(sgr) gra = without_stereo_parities(sgr) if has_stereo(sgr): full_atm_ste_par_dct = atom_stereo_parities(sgr) full_bnd_ste_par_dct = bond_stereo_parities(sgr) atm_keys = set() bnd_keys = set() last_gra = None while last_gra != gra: last_gra = gra atm_keys.update(stereogenic_atom_keys(gra)) bnd_keys.update(stereogenic_bond_keys(gra)) atm_ste_par_dct = { atm_key: full_atm_ste_par_dct[atm_key] for atm_key in atm_keys } bnd_ste_par_dct = { bnd_key: full_bnd_ste_par_dct[bnd_key] for bnd_key in bnd_keys } geo, gra = _atom_stereo_corrected_geometry(gra, atm_ste_par_dct, geo, geo_idx_dct) geo, gra = _bond_stereo_corrected_geometry(gra, bnd_ste_par_dct, geo, geo_idx_dct) return geo
def _have_no_stereo_assignments(gras): return all(gra == without_stereo_parities(gra) for gra in gras)