Skip to content

Commit

Permalink
!!Query update!!
Browse files Browse the repository at this point in the history
heteroatoms mark added.
  • Loading branch information
stsouko committed Dec 11, 2020
1 parent ddaf40b commit dba6b93
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 6 deletions.
8 changes: 8 additions & 0 deletions CGRtools/algorithms/smiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def __format__(self, format_spec):
!n - Disable neighbors marks in queries. Returns non-unique signature.
!g - Disable hydrogens marks in queries. Returns non-unique signature.
!c - Disable rings marks in queries. Returns non-unique signature.
!w - Disable heteroatoms marks in queries. Returns non-unique signature.
t - Use aromatic bonds instead aromatic atoms.
m - Set atom mapping.
r - Generate random-ordered smiles.
Expand All @@ -84,6 +85,8 @@ def __format__(self, format_spec):
kwargs['hydrogens'] = False
if '!c' in format_spec:
kwargs['rings'] = False
if '!w' in format_spec:
kwargs['heteroatoms'] = False
if 'r' in format_spec:
def w(x):
return random()
Expand Down Expand Up @@ -405,6 +408,7 @@ def _format_atom(self, n, **kwargs):
neighbors = self._neighbors[n]
hydrogens = self._hydrogens[n]
rings = self._rings_sizes[n]
heteroatoms = self._heteroatoms[n]

if atom.isotope:
smi = ['[', str(atom.isotope), atom.atomic_symbol]
Expand All @@ -430,6 +434,10 @@ def _format_atom(self, n, **kwargs):
smi.append(';Z')
smi.append(''.join(hybridization_str[x] for x in hybridization))

if kwargs.get('heteroatoms', True) and heteroatoms:
smi.append(';W')
smi.append(''.join(str(x) for x in heteroatoms))

if self._radicals[n]:
smi.append(';*')

Expand Down
8 changes: 8 additions & 0 deletions CGRtools/containers/molecule.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ def neighbors(self, n: int) -> int:
"""number of neighbors atoms excluding any-bonded"""
return sum(b.order != 8 for b in self._bonds[n].values())

@cached_args_method
def heteroatoms(self, n: int) -> int:
"""
Number of neighbored heteroatoms (not carbon or hydrogen)
"""
atoms = self._atoms
return sum(atoms[m].atomic_number not in (1, 6) for m in self._bonds[n])

def remap(self, mapping, *, copy=False) -> 'MoleculeContainer':
h = super().remap(mapping, copy=copy)
mg = mapping.get
Expand Down
21 changes: 19 additions & 2 deletions CGRtools/containers/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

class QueryContainer(QueryStereo, Graph, QuerySmiles, StructureComponents, DepictQuery, Calculate2DMolecule):
__slots__ = ('_neighbors', '_hybridizations', '_atoms_stereo', '_cis_trans_stereo', '_allenes_stereo',
'_hydrogens', '_rings_sizes')
'_hydrogens', '_rings_sizes', '_heteroatoms')

def __init__(self):
self._neighbors: Dict[int, Tuple[int, ...]] = {}
Expand All @@ -40,6 +40,7 @@ def __init__(self):
self._cis_trans_stereo: Dict[Tuple[int, int], bool] = {}
self._hydrogens: Dict[int, Tuple[int, ...]] = {}
self._rings_sizes: Dict[int, Tuple[int, ...]] = {}
self._heteroatoms: Dict[int, Tuple[int, ...]] = {}

super().__init__()

Expand All @@ -48,11 +49,13 @@ def add_atom(self, atom: Union[QueryElement, AnyElement, Element, int, str], *ar
hybridization: Union[int, List[int], Tuple[int, ...], None] = None,
hydrogens: Union[int, List[int], Tuple[int, ...], None] = None,
rings_sizes: Union[int, List[int], Tuple[int, ...], None] = None,
heteroatoms: Union[int, List[int], Tuple[int, ...], None] = None,
**kwargs):
neighbors = self._validate_neighbors(neighbors)
hybridization = self._validate_hybridization(hybridization)
hydrogens = self._validate_neighbors(hydrogens)
rings_sizes = self._validate_rings(rings_sizes)
heteroatoms = self._validate_neighbors(heteroatoms)

if not isinstance(atom, (QueryElement, AnyElement)):
if isinstance(atom, Element):
Expand All @@ -69,6 +72,7 @@ def add_atom(self, atom: Union[QueryElement, AnyElement, Element, int, str], *ar
self._hybridizations[_map] = hybridization
self._hydrogens[_map] = hydrogens
self._rings_sizes[_map] = rings_sizes
self._heteroatoms[_map] = heteroatoms
return _map

def add_bond(self, n, m, bond: Union[QueryBond, Bond, int, Tuple[int, ...]]):
Expand Down Expand Up @@ -106,6 +110,7 @@ def delete_atom(self, n):
del self._hybridizations[n]
del self._hydrogens[n]
del self._rings_sizes[n]
del self._heteroatoms[n]

sas = self._atoms_stereo
if n in sas:
Expand Down Expand Up @@ -147,6 +152,7 @@ def remap(self, mapping, *, copy=False) -> 'QueryContainer':
sn = self._neighbors
shg = self._hydrogens
srs = self._rings_sizes
sha = self._heteroatoms

if copy:
hn = h._neighbors
Expand All @@ -156,6 +162,7 @@ def remap(self, mapping, *, copy=False) -> 'QueryContainer':
hcs = h._cis_trans_stereo
hhg = h._hydrogens
hrs = h._rings_sizes
hha = h._heteroatoms
else:
hn = {}
hh = {}
Expand All @@ -164,13 +171,15 @@ def remap(self, mapping, *, copy=False) -> 'QueryContainer':
hcs = {}
hhg = {}
hrs = {}
hha = {}

for n, hyb in self._hybridizations.items():
m = mg(n, n)
hn[m] = sn[n]
hh[m] = hyb
hhg[m] = shg[n]
hrs[m] = srs[n]
hha[m] = sha[n]

for n, stereo in self._atoms_stereo.items():
has[mg(n, n)] = stereo
Expand All @@ -186,6 +195,7 @@ def remap(self, mapping, *, copy=False) -> 'QueryContainer':
self._hybridizations = hh
self._hydrogens = hhg
self._rings_sizes = hrs
self._heteroatoms = hha
self._atoms_stereo = has
self._allenes_stereo = hal
self._cis_trans_stereo = hcs
Expand All @@ -196,6 +206,7 @@ def copy(self, **kwargs) -> 'QueryContainer':
copy._neighbors = self._neighbors.copy()
copy._hybridizations = self._hybridizations.copy()
copy._hydrogens = self._hydrogens.copy()
copy._heteroatoms = self._heteroatoms.copy()
copy._rings_sizes = self._rings_sizes.copy()
copy._atoms_stereo = self._atoms_stereo.copy()
copy._allenes_stereo = self._allenes_stereo.copy()
Expand All @@ -217,11 +228,13 @@ def substructure(self, atoms, **kwargs) -> 'QueryContainer':
sh = self._hybridizations
shg = self._hydrogens
srs = self._rings_sizes
sha = self._heteroatoms

sub._neighbors = {n: sn[n] for n in atoms}
sub._hybridizations = {n: sh[n] for n in atoms}
sub._hydrogens = {n: shg[n] for n in atoms}
sub._rings_sizes = {n: srs[n] for n in atoms}
sub._heteroatoms = {n: sha[n] for n in atoms}

lost = {n for n, a in sa.items() if a.atomic_number != 1} - set(atoms) # atoms not in substructure
not_skin = {n for n in atoms if lost.isdisjoint(sb[n])}
Expand All @@ -244,6 +257,7 @@ def union(self, other, **kwargs) -> 'QueryContainer':
u._atoms_stereo.update(other._atoms_stereo)
u._allenes_stereo.update(other._allenes_stereo)
u._cis_trans_stereo.update(other._cis_trans_stereo)
u._heteroatoms.update(other._heteroatoms)
return u
else:
raise TypeError('QueryContainer expected')
Expand Down Expand Up @@ -322,7 +336,7 @@ def __getstate__(self):
return {'atoms_stereo': self._atoms_stereo, 'allenes_stereo': self._allenes_stereo,
'cis_trans_stereo': self._cis_trans_stereo, 'neighbors': self._neighbors,
'hybridizations': self._hybridizations, 'hydrogens': self._hydrogens,
'rings_sizes': self._rings_sizes, **super().__getstate__()}
'rings_sizes': self._rings_sizes, 'heteroatoms': self._heteroatoms, **super().__getstate__()}

def __setstate__(self, state):
if 'node' in state: # 3.1 compatibility.
Expand Down Expand Up @@ -361,6 +375,8 @@ def __setstate__(self, state):
b._QueryBond__order = bond._Bond__order
bn[m] = b
state['bonds'] = bonds
if 'heteroatoms' not in state: # <4.1.4
state['heteroatoms'] = {n: () for n in state['atoms']}

super().__setstate__(state)
self._atoms_stereo = state['atoms_stereo']
Expand All @@ -370,6 +386,7 @@ def __setstate__(self, state):
self._hybridizations = state['hybridizations']
self._hydrogens = state['hydrogens']
self._rings_sizes = state['rings_sizes']
self._heteroatoms = state['heteroatoms']


__all__ = ['QueryContainer']
7 changes: 7 additions & 0 deletions CGRtools/periodictable/element/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ def total_hydrogens(self) -> int:
except AttributeError:
raise IsNotConnectedAtom

@property
def heteroatoms(self) -> int:
try:
return self._graph().heteroatoms(self._map)
except AttributeError:
raise IsNotConnectedAtom

@property
def neighbors(self) -> int:
try:
Expand Down
31 changes: 28 additions & 3 deletions CGRtools/periodictable/element/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ def hybridization(self, hybridization):
except AttributeError:
raise IsNotConnectedAtom

@property
def heteroatoms(self) -> Tuple[int, ...]:
try:
return self._graph()._heteroatoms[self._map]
except AttributeError:
raise IsNotConnectedAtom

@heteroatoms.setter
def heteroatoms(self, heteroatoms):
try:
g = self._graph()
g._heteroatoms[self._map] = g._validate_neighbors(heteroatoms)
g.flush_cache()
except AttributeError:
raise IsNotConnectedAtom

@property
def in_ring(self) -> bool:
"""
Expand Down Expand Up @@ -189,11 +205,14 @@ def __eq__(self, other):
return False
if self.implicit_hydrogens and other.implicit_hydrogens not in self.implicit_hydrogens:
return False
if self.heteroatoms and other.heteroatoms not in self.heteroatoms:
return False
return True
elif isinstance(other, QueryElement) and self.atomic_number == other.atomic_number and \
self.isotope == other.isotope and self.charge == other.charge and self.is_radical == other.is_radical \
and self.neighbors == other.neighbors and self.hybridization == other.hybridization \
and self.ring_sizes == other.ring_sizes and self.implicit_hydrogens == other.implicit_hydrogens:
and self.ring_sizes == other.ring_sizes and self.implicit_hydrogens == other.implicit_hydrogens \
and self.heteroatoms == other.heteroatoms:
# equal query element has equal query marks
return True
return False
Expand Down Expand Up @@ -261,10 +280,13 @@ def __eq__(self, other):
return False
if self.implicit_hydrogens and other.implicit_hydrogens not in self.implicit_hydrogens:
return False
if self.heteroatoms and other.heteroatoms not in self.heteroatoms:
return False
return True
elif isinstance(other, Query) and self.charge == other.charge and self.is_radical == other.is_radical \
and self.neighbors == other.neighbors and self.hybridization == other.hybridization \
and self.ring_sizes == other.ring_sizes and self.implicit_hydrogens == other.implicit_hydrogens:
and self.ring_sizes == other.ring_sizes and self.implicit_hydrogens == other.implicit_hydrogens \
and self.heteroatoms == other.heteroatoms:
return True
return False

Expand Down Expand Up @@ -303,10 +325,13 @@ def __eq__(self, other):
return False
if self.implicit_hydrogens and other.implicit_hydrogens not in self.implicit_hydrogens:
return False
if self.heteroatoms and other.heteroatoms not in self.heteroatoms:
return False
return True
elif isinstance(other, Query) and self.charge == other.charge and self.is_radical == other.is_radical \
and self.neighbors == other.neighbors and self.hybridization == other.hybridization \
and self.ring_sizes == other.ring_sizes and self.implicit_hydrogens == other.implicit_hydrogens:
and self.ring_sizes == other.ring_sizes and self.implicit_hydrogens == other.implicit_hydrogens \
and self.heteroatoms == other.heteroatoms:
if isinstance(other, ListElement):
return self._numbers == other._numbers
if isinstance(other, AnyElement):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def finalize_options(self):

setup(
name='CGRtools',
version='4.1.3',
version='4.1.4',
packages=['CGRtools', 'CGRtools.algorithms', 'CGRtools.algorithms.components', 'CGRtools.containers',
'CGRtools.files', 'CGRtools.files._mdl', 'CGRtools.periodictable', 'CGRtools.periodictable.element',
'CGRtools.utils', 'CGRtools.attributes'],
Expand Down

0 comments on commit dba6b93

Please sign in to comment.