44from dataclasses import dataclass , field
55
66from selfies .bond_constraints import get_bonding_capacity
7- from selfies .constants import AROMATIC_VALENCES
7+ from selfies .constants import AROMATIC_VALENCES , VALENCE_ELECTRONS
88from selfies .utils .matching_utils import find_perfect_matching
99
1010
@@ -254,7 +254,7 @@ def kekulize(self) -> bool:
254254
255255 ds = self ._delocal_subgraph
256256 kept_nodes = set (itertools .filterfalse (self ._prune_from_ds , ds ))
257-
257+
258258 # relabel kept DS nodes to be 0, 1, 2, ...
259259 label_to_node = list (sorted (kept_nodes ))
260260 node_to_label = {v : i for i , v in enumerate (label_to_node )}
@@ -265,7 +265,7 @@ def kekulize(self) -> bool:
265265 label = node_to_label [node ]
266266 for adj in filter (lambda v : v in kept_nodes , ds [node ]):
267267 pruned_ds [label ].append (node_to_label [adj ])
268-
268+
269269 matching = find_perfect_matching (pruned_ds )
270270 if matching is None :
271271 return False
@@ -288,18 +288,33 @@ def _prune_from_ds(self, node):
288288 adj_nodes = self ._delocal_subgraph [node ]
289289 if not adj_nodes :
290290 return True # aromatic atom with no aromatic bonds
291-
291+
292292 atom = self ._atoms [node ]
293293 valences = AROMATIC_VALENCES [atom .element ]
294-
294+
295295 # each bond in DS has order 1.5 - we treat them as single bonds
296296 used_electrons = int (self ._bond_counts [node ] - 0.5 * len (adj_nodes ))
297-
297+
298298 if atom .h_count is None : # account for implicit Hs
299299 assert atom .charge == 0
300300 return any (used_electrons == v for v in valences )
301301 else :
302302 valence = valences [- 1 ] - atom .charge
303303 used_electrons += atom .h_count
304- free_electrons = valence - used_electrons
305- return not ((free_electrons >= 0 ) and (free_electrons % 2 != 0 ))
304+
305+ # count the total number of bound electrons of each atom
306+ bound_electrons = (max (0 , atom .charge ) + atom .h_count
307+ + int (self ._bond_counts [node ])
308+ + int (2 * (self ._bond_counts [node ] % 1 )))
309+
310+ # calculate the number of unpaired electrons of each atom
311+ radical_electrons = (max (0 , VALENCE_ELECTRONS [atom .element ]
312+ - bound_electrons ) % 2 )
313+
314+ # unpaired electrons do not contribute to the aromatic system
315+ free_electrons = valence - used_electrons - radical_electrons
316+
317+ if any (used_electrons == v - atom .charge for v in valences ):
318+ return True
319+ else :
320+ return not ((free_electrons >= 0 ) and (free_electrons % 2 != 0 ))
0 commit comments