def union(gra1, gra2): """ a union of two graphs """ assert not atom_keys(gra1) & atom_keys(gra2) atm_dct = {} atm_dct.update(atoms(gra1)) atm_dct.update(atoms(gra2)) bnd_dct = {} bnd_dct.update(bonds(gra1)) bnd_dct.update(bonds(gra2)) return _create.from_atoms_and_bonds(atm_dct, bnd_dct)
def add_atom_explicit_hydrogen_keys(gra, atm_exp_hyd_keys_dct): """ add explicit hydrogens by atom """ assert set(atm_exp_hyd_keys_dct.keys()) <= atom_keys(gra), ( '{} !<= {}'.format(set(atm_exp_hyd_keys_dct.keys()), atom_keys(gra))) for atm_key, atm_exp_hyd_keys in atm_exp_hyd_keys_dct.items(): assert not set(atm_exp_hyd_keys) & atom_keys(gra) atm_exp_hyd_bnd_keys = { frozenset({atm_key, atm_exp_hyd_key}) for atm_exp_hyd_key in atm_exp_hyd_keys } atm_exp_hyd_sym_dct = dict_.by_key({}, atm_exp_hyd_keys, fill_val='H') gra = add_atoms(gra, atm_exp_hyd_sym_dct) gra = add_bonds(gra, atm_exp_hyd_bnd_keys) return gra
def add_atoms(gra, sym_dct, imp_hyd_vlc_dct=None, ste_par_dct=None): """ add atoms to this molecular graph, setting their keys """ atm_keys = atom_keys(gra) atm_sym_dct = atom_symbols(gra) atm_imp_hyd_vlc_dct = atom_implicit_hydrogen_valences(gra) atm_ste_par_dct = atom_stereo_parities(gra) keys = set(sym_dct.keys()) imp_hyd_vlc_dct = {} if imp_hyd_vlc_dct is None else imp_hyd_vlc_dct ste_par_dct = {} if ste_par_dct is None else ste_par_dct assert not keys & atm_keys assert set(imp_hyd_vlc_dct.keys()) <= keys assert set(ste_par_dct.keys()) <= keys atm_sym_dct.update(sym_dct) atm_imp_hyd_vlc_dct.update(imp_hyd_vlc_dct) atm_ste_par_dct.update(ste_par_dct) atm_dct = _create.atoms_from_data( atom_symbols=atm_sym_dct, atom_implicit_hydrogen_valences=atm_imp_hyd_vlc_dct, atom_stereo_parities=atm_ste_par_dct) bnd_dct = bonds(gra) gra = _create.from_atoms_and_bonds(atoms=atm_dct, bonds=bnd_dct) return gra
def standard_keys_for_sequence(gras): """ assigns non-overlapping keys to a sequence of graphs (returns a series of key maps for each) """ atm_key_dcts = [] shift = 0 for gra in gras: natms = atom_count(gra, with_dummy=True, with_implicit=False) atm_key_dct = { atm_key: idx + shift for idx, atm_key in enumerate(sorted(atom_keys(gra))) } atm_key_dcts.append(atm_key_dct) shift += natms gras = [ relabel(gra, atm_key_dct) for gra, atm_key_dct in zip(gras, atm_key_dcts) ] return gras, atm_key_dcts
def longest_chain(gra): """ longest chain in the graph """ atm_keys = atom_keys(gra) max_chain = max((atom_longest_chain(gra, atm_key) for atm_key in atm_keys), key=len) return max_chain
def without_stereo_parities(gra): """ graph with stereo assignments wiped out """ atm_ste_par_dct = dict_.by_key({}, atom_keys(gra), fill_val=None) bnd_ste_par_dct = dict_.by_key({}, bond_keys(gra), fill_val=None) gra = set_atom_stereo_parities(gra, atm_ste_par_dct) gra = set_bond_stereo_parities(gra, bnd_ste_par_dct) return gra
def bond_induced_subgraph(gra, bnd_keys): """ the subgraph induced by a subset of the bonds """ atm_keys = set(itertools.chain(*bnd_keys)) bnd_keys = set(bnd_keys) assert atm_keys <= atom_keys(gra) atm_dct = dict_.by_key(atoms(gra), atm_keys) bnd_dct = dict_.by_key(bonds(gra), bnd_keys) return _create.from_atoms_and_bonds(atm_dct, bnd_dct)
def subgraph(gra, atm_keys): """ the subgraph induced by a subset of the atoms """ atm_keys = set(atm_keys) assert atm_keys <= atom_keys(gra) bnd_keys = set(filter(lambda x: x <= atm_keys, bond_keys(gra))) atm_dct = dict_.by_key(atoms(gra), atm_keys) bnd_dct = dict_.by_key(bonds(gra), bnd_keys) return _create.from_atoms_and_bonds(atm_dct, bnd_dct)
def atom_longest_chains(gra): """ longest chains, by atom """ atm_keys = atom_keys(gra) long_chain_dct = { atm_key: atom_longest_chain(gra, atm_key) for atm_key in atm_keys } return long_chain_dct
def remove_atoms(gra, atm_keys, check=True): """ remove atoms from the molecular graph """ all_atm_keys = atom_keys(gra) atm_keys = set(atm_keys) if check: assert atm_keys <= all_atm_keys atm_keys_left = all_atm_keys - atm_keys return subgraph(gra, atm_keys_left)
def atom_neighborhoods(gra): """ neighborhood subgraphs, by atom """ bnd_keys = bond_keys(gra) def _neighborhood(atm_key): nbh_bnd_keys = set(filter(lambda x: atm_key in x, bnd_keys)) return bond_induced_subgraph(gra, nbh_bnd_keys) atm_keys = list(atom_keys(gra)) atm_nbh_dct = dict(zip(atm_keys, map(_neighborhood, atm_keys))) return atm_nbh_dct
def atom_bond_valences(gra, bond_order=True): """ bond count (bond valence), by atom """ atm_keys = list(atom_keys(gra)) gra = explicit(gra) if not bond_order: gra = without_bond_orders(gra) atm_nbhs = dict_.values_by_key(atom_neighborhoods(gra), atm_keys) atm_bnd_vlcs = [sum(bond_orders(nbh).values()) for nbh in atm_nbhs] atm_bnd_vlc_dct = dict_.transform_values(dict(zip(atm_keys, atm_bnd_vlcs)), int) return atm_bnd_vlc_dct
def atom_unsaturated_valences(gra, bond_order=True): """ unsaturated valences, by atom element valences minus bonding valences = pi sites and radical electrons """ atm_keys = list(atom_keys(gra)) if not bond_order: gra = without_bond_orders(gra) atm_bnd_vlcs = dict_.values_by_key(atom_bond_valences(gra), atm_keys) atm_tot_vlcs = dict_.values_by_key(atom_element_valences(gra), atm_keys) atm_rad_vlcs = numpy.subtract(atm_tot_vlcs, atm_bnd_vlcs) atm_unsat_vlc_dct = dict_.transform_values( dict(zip(atm_keys, atm_rad_vlcs)), int) return atm_unsat_vlc_dct
def frozen(gra): """ hashable, sortable, immutable container of graph data """ atm_keys = sorted(atom_keys(gra)) bnd_keys = sorted(bond_keys(gra), key=sorted) # make it sortable by replacing Nones with -infinity atm_vals = numpy.array(dict_.values_by_key(atoms(gra), atm_keys)) bnd_vals = numpy.array(dict_.values_by_key(bonds(gra), bnd_keys)) atm_vals[numpy.equal(atm_vals, None)] = -numpy.inf bnd_vals[numpy.equal(bnd_vals, None)] = -numpy.inf frz_atms = tuple(zip(atm_keys, map(tuple, atm_vals))) frz_bnds = tuple(zip(bnd_keys, map(tuple, bnd_vals))) return (frz_atms, frz_bnds)
def from_graph(gra): """ networkx graph object from a molecular graph """ nxg = networkx.Graph() nxg.add_nodes_from(atom_keys(gra)) nxg.add_edges_from(bond_keys(gra)) networkx.set_node_attributes(nxg, atom_symbols(gra), 'symbol') networkx.set_node_attributes(nxg, atom_implicit_hydrogen_valences(gra), 'implicit_hydrogen_valence') networkx.set_node_attributes(nxg, atom_stereo_parities(gra), 'stereo_parity') networkx.set_edge_attributes(nxg, bond_orders(gra), 'order') networkx.set_edge_attributes(nxg, bond_stereo_parities(gra), 'stereo_parity') return nxg
def explicit(gra, atm_keys=None): """ make the hydrogens at these atoms explicit """ atm_keys = backbone_keys(gra) if atm_keys is None else atm_keys atm_keys = sorted(atm_keys) atm_imp_hyd_vlc_dct = dict_.by_key(atom_implicit_hydrogen_valences(gra), atm_keys) atm_exp_hyd_keys_dct = {} next_atm_key = max(atom_keys(gra)) + 1 for atm_key in atm_keys: imp_hyd_vlc = atm_imp_hyd_vlc_dct[atm_key] atm_exp_hyd_keys_dct[atm_key] = set( range(next_atm_key, next_atm_key + imp_hyd_vlc)) next_atm_key += imp_hyd_vlc gra = set_atom_implicit_hydrogen_valences( gra, dict_.by_key({}, atm_keys, fill_val=0)) gra = add_atom_explicit_hydrogen_keys(gra, atm_exp_hyd_keys_dct) return gra
def add_bonded_atom(gra, sym, bnd_atm_key, imp_hyd_vlc=None, atm_ste_par=None, bnd_ord=None, bnd_ste_par=None): """ add a single atom with a bond to an atom already in the graph """ atm_keys = atom_keys(gra) atm_key = max(atm_keys) + 1 sym_dct = {atm_key: sym} imp_hyd_vlc_dct = ({ atm_key: imp_hyd_vlc } if imp_hyd_vlc is not None else None) atm_ste_par_dct = ({ atm_key: atm_ste_par } if atm_ste_par is not None else None) gra = add_atoms(gra, sym_dct, imp_hyd_vlc_dct=imp_hyd_vlc_dct, ste_par_dct=atm_ste_par_dct) bnd_key = frozenset({atm_key, bnd_atm_key}) bnd_ord_dct = {bnd_key: bnd_ord} if bnd_ord is not None else None bnd_ste_par_dct = ({ bnd_key: bnd_ste_par } if bnd_ste_par is not None else None) gra = add_bonds(gra, [bnd_key], ord_dct=bnd_ord_dct, ste_par_dct=bnd_ste_par_dct) return gra, atm_key
def _neighbor_keys(atm_key, atm_nbh): return frozenset(atom_keys(atm_nbh) - {atm_key})
def branch_atom_keys(gra, atm_key, bnd_key): """ atom keys for branch extending along `bnd_key` away from `atm_key` """ bnch_atm_keys = atom_keys(branch(gra, atm_key, bnd_key)) return bnch_atm_keys - {atm_key}
def transform_keys(gra, atm_key_func): """ transform atom keys with a function """ atm_keys = atom_keys(gra) atm_key_dct = dict(zip(atm_keys, map(atm_key_func, atm_keys))) return relabel(gra, atm_key_dct)
def standard_keys(gra): """ replace the current atom keys with standard indices, counting from zero """ atm_key_dct = dict(map(reversed, enumerate(sorted(atom_keys(gra))))) return relabel(gra, atm_key_dct)
def backbone_keys(gra): """ backbone atom keys """ bbn_keys = atom_keys(gra) - explicit_hydrogen_keys(gra) return bbn_keys