Source code for besmarts.core.rulesets

"""
besmarts.core.rulesets

Defines the rules for determining whether a given graph is "valid" where the
current state is whether the graph is a valid molecule/SMILES
"""

from besmarts.core.primitives import primitive_key
from besmarts.core.chem import bechem
from besmarts.core.graph_visitors import graph_visitor
from besmarts.core.graphs import graph
from besmarts.core import arrays


[docs] class visitor_ruleset(graph_visitor): def __init__(self, primitive_codecs): self.primitive_codecs = primitive_codecs self.beg: graph = None self.small_molecule_cutoff = 5 self.hydrogen_rule = ("n_minus", {None: None}) self.noncarbon_rule = ("n_max", {None: None})
[docs] def on_start(self, g: graph): self.beg = g return None
[docs] def on_stop(self, g: graph, result): self.beg: graph = None return None
[docs] def on_graph(self, nodes, edges): codec = self.primitive_codecs[primitive_key.FORMAL_CHARGE] if abs(codec.count_charge_smiles(nodes)) > 2: return False N = len(self.beg.nodes) if len(nodes) == N: # codec = self.primitive_codecs[primitive_key.ELEMENT] # C = codec.count_element_smiles(nodes, n=6) name, rule = self.hydrogen_rule if name == "n_minus": codec = self.primitive_codecs[primitive_key.HYDROGEN] H = codec.count_hydrogen_smiles(nodes) x = rule.get(N, rule[None]) if type(x) is float: # print("H", H, "total", N, (N-H)/(N+H)) if H/N < x: return False elif type(x) is int: if H < N - x: return False if self.noncarbon_rule[0] == "n_max": codec = self.primitive_codecs[primitive_key.ELEMENT] C = codec.count_carbon_smiles(nodes) N = len(self.beg.nodes) calc = len(nodes) - C rule = self.noncarbon_rule[1] x = rule.get(N, rule[None]) # print("Calc:", calc, len(nodes), C, len(self.beg.nodes), x) if x is not None and calc > x: return False return True
[docs] def on_nodes(self, nodes): return True
[docs] def on_node(self, idx, besmarts: bechem, *args, **kwargs): valid = True name = primitive_key.FORMAL_CHARGE codec = self.primitive_codecs[name] arr = besmarts.primitives[name] q = codec.decode_int(arr.on_first()) name = primitive_key.ELEMENT codec = self.primitive_codecs[name] arr = besmarts.primitives[name] e = codec.decode_int(arr.on_first()) name = primitive_key.VALENCE codec = self.primitive_codecs[name] arr = besmarts.primitives[name] valence = codec.decode_int(arr.on_first()) # name = primitive_key.RING_SMALLEST # codec = self.primitive_codecs[name] # arr = besmarts.primitives[name] # r = codec.decode_int(arr.on_first()) # name = primitive_key.CONNECTIVITY_RING # codec = self.primitive_codecs[name] # arr = besmarts.primitives[name] # x = codec.decode_int(arr.on_first()) # name = primitive_key.AROMATIC # codec = self.primitive_codecs[name] # arr = besmarts.primitives[name] # a = codec.decode_int(arr.on_first()) name = primitive_key.HYDROGEN codec = self.primitive_codecs[name] arr = besmarts.primitives[name] h = codec.decode_int(arr.on_first()) if False: pass # elif a == 1 and (x == 0 or r < 2 or e not in [6,7,8,15]): # valid = False elif e == 6: if q != 0: valid = False elif valence != 4: valid = False elif e == 7: if q < 0: valid = False elif valence not in [3]: valid = False elif e == 8: if q > 0: valid = False if h > 2: valid = False elif valence != 2: valid = False elif e in [1, 9, 17, 35, 53]: if q != 0: valid = False elif valence != 1: valid = False elif e in [15]: # if q != 0: # valid = False # if h > 1: # valid = False if valence not in [3,5,6]: valid = False elif e in [16]: if q not in [0, 1]: valid = False # elif h > 1: # valid = False elif valence not in [2,4,6]: valid = False elif e in [5]: if q != 0: valid = False elif h > 0: valid = False elif valence != 3: valid = False elif e in [1]: # if r != 0: # valid = False # elif x != 0: # valid = False if h > 0: valid = False if not valid: return False # if e == 1: # if valence != 1: # continue # if 0: # if c and not frag.primitives["chirality"][0]: # chiral # continue # elif e == 6: # if 0: # if c and not frag.primitives["chirality"][0]: # chiral # if h + n_edges != 4 or h > 1: # continue # else: # not chiral # if (h == 1 and n_edges == 3) or (n_edges == 4 and h == 0): # continue # elif frag.primitives["element"][7]: # if valence != 3: # continue # if 0: # if c and not frag.primitives["chirality"][0]: # chiral # if h + n_edges != 3 or h > 1 or (h + n_edges == 3 and q == 1): # continue # else: # not chiral # if h <= 1 and n_edges >= 2: # continue # elif frag.primitives["element"][8]: # if valence != 2: # continue # if 0: # if c and not frag.primitives["chirality"][0]: # chiral # continue return valid
[docs] def on_node_edges(self, idx, node, edges): bo = 0.0 naro = 0 # has_double = False has_single = 0 has_stereo = False name = primitive_key.FORMAL_CHARGE codec = self.primitive_codecs[name] arr = node.primitives[name] q = codec.decode_int(arr.on_first()) name = primitive_key.VALENCE codec = self.primitive_codecs[name] arr = node.primitives[name] valence = codec.decode_int(arr.on_first()) name = primitive_key.ELEMENT codec = self.primitive_codecs[name] arr = node.primitives[name] sym = codec.decode_int(arr.on_first()) # if len(valence): # valence = valence[0] # else: # if e == 6: # valence = 4 # elif e == 7: # valence = 3 # elif e == 8: # valence = 2 # elif e == 1: # valence = 1 name = primitive_key.HYDROGEN codec = self.primitive_codecs[name] arr = node.primitives[name] h = codec.decode_int(arr.on_first()) if sym == 16: # breakpoint() if len(edges) > 2: return False if len(edges) == 4 and valence == 2: return False elif len(edges) <= 2 and valence > 2: return False elif valence == 2 and len(edges) > 2: return False if sym == 15: # breakpoint() if len(edges) < 3: return False if len(edges) == 4 and valence < 3: return False elif len(edges) <= 3 and valence > 3: return False edges = dict(edges) # has_double = False # for eidx, e in edges.items(): # arr = e.primitives[primitive_key.BOND_ORDER] # if arr[2]: # has_double = True # for eidx, e in edges.items(): # arr = e.primitives[primitive_key.BOND_ORDER] # if arr[1] or h > 0: # has_single = True # for eidx, e in edges.items(): # arr = e.primitives[primitive_key.BOND_ORDER] # if (arr[6] or arr[7]): # if h == 2: # return False # has_stereo = True # if has_stereo and not has_double: # return False # elif has_double and has_stereo and not has_single: # return False # elif has_double and has_single and not has_stereo: # return False for eidx, e in edges.items(): arr = e.primitives[primitive_key.BOND_ORDER] if arr[1]: bo += 1 # if eidx in new_edges: # has_single += 1 # check to see if other bond is set to single already # other = eidx[0] if eidx[0] == idx else eidx[1] # for otheredge, e2 in edge_set: # if otheredge == eidx: # continue # if e2.primitives['bond_order'][1]: # valid = False elif arr[2]: bo += 2 # if eidx not in new_edges: # other = eidx[0] if eidx[1] == idx else eidx[0] # for otheredge in [edge for edge in edges if other in edge]: # if edges[otheredge].primitives['bond_order'][6]: # if has_stereo: # valid = False # continue # has_stereo = True # if edges[otheredge].primitives['bond_order'][7]: # if has_stereo: # valid = False # # continue # has_stereo = True elif arr[3]: bo += 3 if sym == 16 and len(edges) == 1: # no sulfur triple (single) bonds return False elif arr[4]: bo += 4 elif arr[5]: bo += 1.5 naro += 1 elif arr[6]: # if not frag.primitives['element'][6]: # continue bo += 1 # if eidx in new_edges: if has_stereo: valid = False # continue # check to see if other bond is set to stereo already # other = eidx[0] if eidx[0] == idx else eidx[1] # for otheredge, e2 in edges.items(): # if otheredge == eidx: # continue # if e2.primitives[primitive_key.BOND_ORDER][6]: # valid = False # has_stereo = True # # if eidx in new_edges: # other = eidx[0] if eidx[1] == idx else eidx[0] # for otheredge in [edge for edge in edges if other in edge]: # if edges[otheredge].primitives['bond_order'][2]: # has_double = True # elif edges[otheredge].primitives['bond_order'][6]: # valid = False # elif edges[otheredge].primitives['bond_order'][7]: # valid = False # # continue elif arr[7]: bo += 1 if has_stereo: valid = False # continue other = eidx[0] if eidx[0] == idx else eidx[1] for otheredge, e2 in edges.items(): if otheredge == eidx: continue if e2.primitives[primitive_key.BOND_ORDER][7]: valid = False # for otheredge in [edge for edge in edges if other in edge]: # if otheredge == eidx: # continue # if edges[otheredge].primitives['bond_order'][7]: # valid = False # if eidx in new_edges: # other = eidx[0] if eidx[1] == idx else eidx[0] # for otheredge in [edge for edge in edges if other in edge]: # if edges[otheredge].primitives['bond_order'][2]: # has_double = True # elif edges[otheredge].primitives['bond_order'][6]: # valid = False # elif edges[otheredge].primitives['bond_order'][7]: # valid = False # # continue has_stereo = True else: bo += 1 if int(2 * (valence - h + q)) != int(2 * bo): return False return True
[docs] def on_node_exit(self, idx): return None
[docs] def on_edge_enter(self, idx): return None
[docs] def on_edge(self, idx, smarts: bechem, *args, **kwargs): return True
[docs] def on_edge_exit(self, idx): return None