Skip to content

Commit

Permalink
update sssr (#156)
Browse files Browse the repository at this point in the history
* update sssr

* refactored

Co-authored-by: stsouko <nougmanoff@protonmail.com>
  • Loading branch information
salikhovi4 and stsouko committed Dec 22, 2020
1 parent cfb18b3 commit 55d9ed9
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 21 deletions.
114 changes: 93 additions & 21 deletions CGRtools/algorithms/sssr.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
#
from CachedMethods import cached_property
from collections import defaultdict
from itertools import chain, combinations, product
from itertools import chain, combinations
from logging import warning
from operator import itemgetter
from typing import Any, Dict, Set, Tuple, Union
from typing import Any, Dict, Set, Tuple, Union, TYPE_CHECKING, Type, List
from ..exceptions import ImplementationError


if TYPE_CHECKING:
from CGRtools.containers.common import Graph


class SSSR:
""" SSSR calculation. based on idea of PID matrices from:
Lee, C. J., Kang, Y.-M., Cho, K.-H., & No, K. T. (2009).
Expand All @@ -35,7 +39,7 @@ class SSSR:
__slots__ = ()

@cached_property
def sssr(self) -> Tuple[Tuple[int, ...], ...]:
def sssr(self: 'Graph') -> Tuple[Tuple[int, ...], ...]:
"""
Smallest Set of Smallest Rings.
Expand All @@ -46,59 +50,127 @@ def sssr(self) -> Tuple[Tuple[int, ...], ...]:
return ()

@classmethod
def _sssr(cls, bonds: Dict[int, Union[Set[int], Dict[int, Any]]], n_sssr: int) -> Tuple[Tuple[int, ...], ...]:
def _sssr(cls: Type[Union['Graph', 'SSSR']], bonds: Dict[int, Union[Set[int], Dict[int, Any]]], n_sssr: int) -> \
Tuple[Tuple[int, ...], ...]:
"""
Smallest Set of Smallest Rings of any adjacency matrix.
Number of rings required.
"""
bonds = cls._skin_graph(bonds)
return cls.__rings_filter(cls.__c_set(*cls.__make_pid(bonds)), n_sssr, bonds)
paths = cls.__bfs(bonds)
pid1, pid2, dist = cls.__make_pid(paths)
return cls.__rings_filter(cls.__c_set(pid1, pid2, dist), n_sssr, bonds)

@staticmethod
def __make_pid(bonds):
lb = len(bonds)
def __bfs(bonds):
atoms = set(bonds)
terminated = []
tail = atoms.pop()
next_stack = {x: [tail, x] for x in bonds[tail]}

while True:
next_front = set()
found_odd = set()
stack, next_stack = next_stack, {}
for tail, path in stack.items():
neighbors = bonds[tail] & atoms
next_front.add(tail)

if len(neighbors) == 1:
n = neighbors.pop()
if n in found_odd:
if len(path) != 1:
terminated.append(tuple(path)) # save second ring closure
next_stack[n] = [n] # maybe we have another path?
else:
path.append(n)
if n in stack: # odd rings
found_odd.add(tail)
terminated.append(tuple(path)) # found ring closure. save path.
elif n in next_stack: # even rings
terminated.append(tuple(path))
if len(next_stack[n]) != 1: # prevent bicycle case
terminated.append(tuple(next_stack[n]))
next_stack[n] = [n]
else:
next_stack[n] = path # grow must go on
elif neighbors:
if len(path) != 1:
terminated.append(tuple(path)) # save path.
for n in neighbors:
if n in found_odd:
next_stack[n] = [n]
else:
path = [tail, n]
if n in stack: # odd rings
found_odd.add(tail)
terminated.append(tuple(path))
elif n in next_stack: # even rings
terminated.append(tuple(path))
if len(next_stack[n]) != 1: # prevent bicycle case
terminated.append(tuple(next_stack[n]))
next_stack[n] = [n]
else:
next_stack[n] = path

atoms.difference_update(next_front)
if not atoms:
break
elif not next_stack:
tail = atoms.pop()
next_stack = {x: [tail, x] for x in bonds[tail] & atoms}
return terminated

@staticmethod
def __make_pid(paths: List[List[int]]):
pid1 = defaultdict(lambda: defaultdict(dict))
pid2 = defaultdict(lambda: defaultdict(dict))
distances = defaultdict(lambda: defaultdict(lambda: lb))
for n, ms in bonds.items():
dn = distances[n]
pn = pid1[n]
for m in ms:
pn[m][(m, n)] = (n, m)
dn[m] = 1
distances = defaultdict(lambda: defaultdict(lambda: 1e9))
chains = sorted(paths, key=len)
for c in chains:
di = len(c) - 1
n, m = c[0], c[-1]
nn, mm = c[1], c[-2]
if n in distances and m in distances[n] and distances[n][m] != di:
pid2[n][m][(nn, mm)] = c
pid2[m][n][(mm, nn)] = c[::-1]
else:
pid1[n][m][(nn, mm)] = c
pid1[m][n][(mm, nn)] = c[::-1]
distances[n][m] = distances[m][n] = di

for k in bonds:
for k in pid1:
new_distances = defaultdict(dict)
dk = distances[k]
ndk = new_distances[k]
for i in bonds:
for i in pid1:
if i == k:
continue
di = distances[i]
ndi = new_distances[i]
ndk[i] = ndi[k] = di[k]
for j in bonds:
for j in pid1:
if j == k or j == i:
continue
ij = di[j]
ikj = di[k] + dk[j]
if ij - ikj == 1: # A new shortest path == previous shortest path - 1
pid2[i][j] = pid1[i][j]
pid1[i][j] = {(ni, mj): ip[:-1] + jp for ((ni, _), ip), ((_, mj), jp) in
product(pid1[i][k].items(), pid1[k][j].items())}
zip(pid1[i][k].items(), pid1[k][j].items())}
ndi[j] = ikj
elif ij > ikj: # A new shortest path
pid2[i][j] = {}
pid1[i][j] = {(ni, mj): ip[:-1] + jp for ((ni, _), ip), ((_, mj), jp) in
product(pid1[i][k].items(), pid1[k][j].items())}
zip(pid1[i][k].items(), pid1[k][j].items())}
ndi[j] = ikj
elif ij == ikj: # Another shortest path
pid1[i][j].update({(ni, mj): ip[:-1] + jp for ((ni, _), ip), ((_, mj), jp) in
product(pid1[i][k].items(), pid1[k][j].items())})
zip(pid1[i][k].items(), pid1[k][j].items())})
ndi[j] = ij
elif ikj - ij == 1: # Shortest+1 path
pid2[i][j].update({(ni, mj): ip[:-1] + jp for ((ni, _), ip), ((_, mj), jp) in
product(pid1[i][k].items(), pid1[k][j].items())})
zip(pid1[i][k].items(), pid1[k][j].items())})
ndi[j] = ij
else:
ndi[j] = ij
Expand Down
117 changes: 117 additions & 0 deletions test/cycle.sdf
Original file line number Diff line number Diff line change
@@ -1,4 +1,121 @@

Mrv2019 11162016092D

21 24 0 0 0 0 999 V2000
0.7505 1.9966 0.0000 C 0 0 0 0 0 0 0 0 0 1 0 0
-0.0885 2.1429 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 0
-0.5280 1.4583 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 0
-0.2233 0.6981 0.0000 C 0 0 0 0 0 0 0 0 0 4 0 0
0.5925 0.5241 0.0000 C 0 0 0 0 0 0 0 0 0 5 0 0
1.0922 1.2133 0.0000 C 0 0 0 0 0 0 0 0 0 6 0 0
-0.1641 -1.0140 0.0000 C 0 0 0 0 0 0 0 0 0 7 0 0
-0.5372 -0.2910 0.0000 C 0 0 0 0 0 0 0 0 0 8 0 0
-1.3479 -0.1748 0.0000 C 0 0 0 0 0 0 0 0 0 9 0 0
-1.9065 -0.7943 0.0000 C 0 0 0 0 0 0 0 0 0 10 0 0
-1.5595 -1.5716 0.0000 C 0 0 0 0 0 0 0 0 0 11 0 0
-0.7103 -1.6673 0.0000 C 0 0 0 0 0 0 0 0 0 12 0 0
-1.5416 1.2356 0.0000 C 0 0 0 0 0 0 0 0 0 13 0 0
-2.0476 0.5916 0.0000 C 0 0 0 0 0 0 0 0 0 14 0 0
-2.8602 0.6300 0.0000 C 0 0 0 0 0 0 0 0 0 15 0 0
-3.1529 1.4297 0.0000 C 0 0 0 0 0 0 0 0 0 16 0 0
-2.6455 2.1173 0.0000 C 0 0 0 0 0 0 0 0 0 17 0 0
-1.7988 2.0291 0.0000 C 0 0 0 0 0 0 0 0 0 18 0 0
-0.9571 2.3072 0.0000 S 0 0 0 0 0 0 0 0 0 19 0 0
0.4125 -0.3439 0.0000 S 0 0 0 0 0 0 0 0 0 20 0 0
-2.5682 -0.2044 0.0000 S 0 0 0 0 0 0 0 0 0 21 0 0
1 2 1 0 0 0 0
2 3 2 0 0 0 0
3 4 1 0 0 0 0
4 5 2 0 0 0 0
5 6 1 0 0 0 0
1 6 2 0 0 0 0
7 8 1 0 0 0 0
8 9 2 0 0 0 0
9 10 1 0 0 0 0
10 11 2 0 0 0 0
11 12 1 0 0 0 0
7 12 2 0 0 0 0
13 14 1 0 0 0 0
14 15 2 0 0 0 0
15 16 1 0 0 0 0
16 17 2 0 0 0 0
17 18 1 0 0 0 0
13 18 2 0 0 0 0
19 2 1 0 0 0 0
5 20 1 0 0 0 0
20 7 1 0 0 0 0
10 21 1 0 0 0 0
19 18 1 0 0 0 0
21 15 1 0 0 0 0
M END
$$$$

Mrv2019 11162015272D

28 32 0 0 0 0 999 V2000
-0.2787 -1.9567 0.0000 C 0 0 0 0 0 0 0 0 0 54 0 0
-0.6957 -1.2520 0.0000 C 0 0 0 0 0 0 0 0 0 2 0 0
-1.5026 -1.2200 0.0000 C 0 0 0 0 0 0 0 0 0 3 0 0
-1.9605 -1.9032 0.0000 C 0 0 0 0 0 0 0 0 0 4 0 0
-1.5624 -2.6379 0.0000 C 0 0 0 0 0 0 0 0 0 5 0 0
-0.7375 -2.6520 0.0000 C 0 0 0 0 0 0 0 0 0 6 0 0
0.1941 -0.2827 0.0000 C 0 0 0 0 0 0 0 0 0 7 0 0
0.8775 -0.7406 0.0000 C 0 0 0 0 0 0 0 0 0 8 0 0
1.6120 -0.3424 0.0000 C 0 0 0 0 0 0 0 0 0 9 0 0
1.6261 0.4825 0.0000 C 0 0 0 0 0 0 0 0 0 10 0 0
0.9308 0.9412 0.0000 C 0 0 0 0 0 0 0 0 0 11 0 0
0.2261 0.5242 0.0000 C 0 0 0 0 0 0 0 0 0 12 0 0
-0.2853 2.0974 0.0000 C 0 0 0 0 0 0 0 0 0 13 0 0
-0.7432 1.4141 0.0000 C 0 0 0 0 0 0 0 0 0 14 0 0
-1.5501 1.4460 0.0000 C 0 0 0 0 0 0 0 0 0 15 0 0
-1.9671 2.1507 0.0000 C 0 0 0 0 0 0 0 0 0 16 0 0
-1.5083 2.8461 0.0000 C 0 0 0 0 0 0 0 0 0 17 0 0
-0.6834 2.8319 0.0000 C 0 0 0 0 0 0 0 0 0 18 0 0
-3.1766 -0.7471 0.0000 C 0 0 0 0 0 0 0 0 0 19 0 0
-3.8719 -0.2884 0.0000 C 0 0 0 0 0 0 0 0 0 20 0 0
-3.8578 0.5365 0.0000 C 0 0 0 0 0 0 0 0 0 21 0 0
-3.1232 0.9346 0.0000 C 0 0 0 0 0 0 0 0 0 22 0 0
-2.4399 0.4767 0.0000 C 0 0 0 0 0 0 0 0 0 23 0 0
-2.4719 -0.3302 0.0000 C 0 0 0 0 0 0 0 0 0 24 0 0
-2.6583 -1.4208 0.0000 S 0 0 0 0 0 0 0 0 0 25 0 0
0.6744 -1.6713 0.0000 S 0 0 0 0 0 0 0 0 0 1 0 0
0.4125 1.6149 0.0000 S 0 0 0 0 0 0 0 0 0 26 0 0
-2.6407 1.6324 0.0000 S 0 0 0 0 0 0 0 0 0 27 0 0
1 2 1 0 0 0 0
2 3 2 0 0 0 0
3 4 1 0 0 0 0
4 5 2 0 0 0 0
5 6 1 0 0 0 0
1 6 2 0 0 0 0
7 8 1 0 0 0 0
8 9 2 0 0 0 0
9 10 1 0 0 0 0
10 11 2 0 0 0 0
11 12 1 0 0 0 0
7 12 2 0 0 0 0
13 14 1 0 0 0 0
14 15 2 0 0 0 0
15 16 1 0 0 0 0
16 17 2 0 0 0 0
17 18 1 0 0 0 0
13 18 2 0 0 0 0
19 20 1 0 0 0 0
20 21 2 0 0 0 0
21 22 1 0 0 0 0
22 23 2 0 0 0 0
23 24 1 0 0 0 0
19 24 2 0 0 0 0
19 25 1 0 0 0 0
25 4 1 0 0 0 0
1 26 1 0 0 0 0
26 8 1 0 0 0 0
11 27 1 0 0 0 0
27 13 1 0 0 0 0
16 28 1 0 0 0 0
22 28 1 0 0 0 0
M END
$$$$

Mrv1641802071913382D

3 3 0 0 0 0 999 V2000
Expand Down

0 comments on commit 55d9ed9

Please sign in to comment.