aamutils 0.0.3__tar.gz → 0.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aamutils-0.0.3 → aamutils-0.0.5}/PKG-INFO +5 -5
- {aamutils-0.0.3 → aamutils-0.0.5}/README.md +4 -4
- aamutils-0.0.5/aamutils/__init__.py +1 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/aamutils/algorithm/ilp.py +47 -2
- aamutils-0.0.5/aamutils/mapping.py +55 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/aamutils/utils.py +64 -2
- {aamutils-0.0.3 → aamutils-0.0.5}/pyproject.toml +1 -1
- {aamutils-0.0.3 → aamutils-0.0.5}/test/algorithm/test_ilp.py +1 -25
- aamutils-0.0.5/test/test_mapping.py +16 -0
- aamutils-0.0.3/test/algorithm/__init__.py +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/.github/workflows/publish-package.yml +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/.github/workflows/test-and-lint.yml +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/.gitignore +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/LICENSE +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/Makefile +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/aamutils/__main__.py +0 -0
- {aamutils-0.0.3/aamutils → aamutils-0.0.5/aamutils/algorithm}/__init__.py +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/aamutils/algorithm/aaming.py +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/aamutils/cmd/__init__.py +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/aamutils/cmd/expanding.py +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/benchmark.py +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/conf.py +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/data/test_dataset.json +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/doc/getting_started.rst +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/doc/reference.rst +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/index.rst +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/lint.sh +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/make.bat +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/requirements.txt +0 -0
- {aamutils-0.0.3/aamutils/algorithm → aamutils-0.0.5/test}/__init__.py +0 -0
- {aamutils-0.0.3/test → aamutils-0.0.5/test/algorithm}/__init__.py +0 -0
- {aamutils-0.0.3 → aamutils-0.0.5}/test/algorithm/test_aaming.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: aamutils
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Collection of atom-atom-mapping utility functions.
|
|
5
5
|
Project-URL: homepage, https://github.com/klausweinbauer/AAMUtils
|
|
6
6
|
Project-URL: source, https://github.com/klausweinbauer/AAMUtils
|
|
@@ -78,18 +78,18 @@ reported results can be reproduced by running the following commands:
|
|
|
78
78
|
### (1) Extend partial reaction center (50%, 75% and 100% missing atoms)
|
|
79
79
|
|
|
80
80
|
```
|
|
81
|
-
python3 benchmark.py --remove-mode rc --remove-ratio 0.5
|
|
81
|
+
python3 benchmark.py --remove-mode rc --remove-ratio 0.5 --seed 42
|
|
82
82
|
```
|
|
83
83
|
```
|
|
84
|
-
python3 benchmark.py --remove-mode rc --remove-ratio 0.75
|
|
84
|
+
python3 benchmark.py --remove-mode rc --remove-ratio 0.75 --seed 42
|
|
85
85
|
```
|
|
86
86
|
```
|
|
87
|
-
python3 benchmark.py --remove-mode rc --remove-ratio 1
|
|
87
|
+
python3 benchmark.py --remove-mode rc --remove-ratio 1 --seed 42
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
### (2) Extend partial AAM with fully mapped RC
|
|
91
91
|
```
|
|
92
|
-
python3 benchmark.py --remove-mode keep_rc --remove-ratio 1
|
|
92
|
+
python3 benchmark.py --remove-mode keep_rc --remove-ratio 1 --seed 42
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
|
|
@@ -56,18 +56,18 @@ reported results can be reproduced by running the following commands:
|
|
|
56
56
|
### (1) Extend partial reaction center (50%, 75% and 100% missing atoms)
|
|
57
57
|
|
|
58
58
|
```
|
|
59
|
-
python3 benchmark.py --remove-mode rc --remove-ratio 0.5
|
|
59
|
+
python3 benchmark.py --remove-mode rc --remove-ratio 0.5 --seed 42
|
|
60
60
|
```
|
|
61
61
|
```
|
|
62
|
-
python3 benchmark.py --remove-mode rc --remove-ratio 0.75
|
|
62
|
+
python3 benchmark.py --remove-mode rc --remove-ratio 0.75 --seed 42
|
|
63
63
|
```
|
|
64
64
|
```
|
|
65
|
-
python3 benchmark.py --remove-mode rc --remove-ratio 1
|
|
65
|
+
python3 benchmark.py --remove-mode rc --remove-ratio 1 --seed 42
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
### (2) Extend partial AAM with fully mapped RC
|
|
69
69
|
```
|
|
70
|
-
python3 benchmark.py --remove-mode keep_rc --remove-ratio 1
|
|
70
|
+
python3 benchmark.py --remove-mode keep_rc --remove-ratio 1 --seed 42
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .mapping import Mapper
|
|
@@ -57,19 +57,48 @@ def _indicator_constraint(problem, D, G, S, k):
|
|
|
57
57
|
for j in range(m):
|
|
58
58
|
problem += (
|
|
59
59
|
D[i, j] <= k * G[i, j],
|
|
60
|
-
"
|
|
60
|
+
"g_indicator_constraint1_{}-{}".format(i, j),
|
|
61
|
+
)
|
|
62
|
+
problem += (
|
|
63
|
+
D[i, j] >= -k + G[i, j] * (k + 1),
|
|
64
|
+
"g_indicator_constraint2_{}-{}".format(i, j),
|
|
61
65
|
)
|
|
62
66
|
problem += (
|
|
63
67
|
-D[i, j] <= k * S[i, j],
|
|
64
|
-
"
|
|
68
|
+
"s_indicator_constraint1_{}-{}".format(i, j),
|
|
69
|
+
)
|
|
70
|
+
problem += (
|
|
71
|
+
-D[i, j] >= -k + S[i, j] * (k + 1),
|
|
72
|
+
"s_indicator_constraint2_{}-{}".format(i, j),
|
|
65
73
|
)
|
|
66
74
|
|
|
67
75
|
|
|
76
|
+
def _expected_rc_atoms_constraint(problem, G, S, expected_rc_atoms):
|
|
77
|
+
m = int(np.sqrt(len(G.keys())))
|
|
78
|
+
assert m == int(np.sqrt(len(S.keys())))
|
|
79
|
+
|
|
80
|
+
lp_io = lp.LpVariable.dicts("io", [i for i in range(m)], cat=lp.LpInteger)
|
|
81
|
+
lp_rc = lp.LpVariable.dicts("rc", [i for i in range(m)], cat=lp.LpBinary)
|
|
82
|
+
for i in range(m):
|
|
83
|
+
problem += (
|
|
84
|
+
lp_io[i] == lp.lpSum([G[i, j] + S[i, j] for j in range(m)]),
|
|
85
|
+
"idx_io_sum_{}".format(i),
|
|
86
|
+
)
|
|
87
|
+
problem += (lp_io[i] <= m * lp_rc[i], "is_rc_indicator1_{}".format(i))
|
|
88
|
+
problem += (lp_io[i] >= lp_rc[i], "is_rc_indicator_{}".format(i))
|
|
89
|
+
|
|
90
|
+
problem += (
|
|
91
|
+
expected_rc_atoms == lp.lpSum([lp_rc[i] for i in range(m)]),
|
|
92
|
+
"rc_atom_constraint",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
68
96
|
def expand_partial_aam_balanced(
|
|
69
97
|
G: nx.Graph,
|
|
70
98
|
H: nx.Graph,
|
|
71
99
|
beta_map: None | list[tuple[int, int, int]] = None,
|
|
72
100
|
bond_key="bond",
|
|
101
|
+
expected_rc: None | tuple[None | int, None | int] = None,
|
|
73
102
|
) -> tuple[np.ndarray, str, float]:
|
|
74
103
|
"""Function to extend the partial atom-atom map beta of the balanced
|
|
75
104
|
reaction G \u2192 H to a full atom-atom map based on the Minimal Chemical
|
|
@@ -81,6 +110,14 @@ def expand_partial_aam_balanced(
|
|
|
81
110
|
specified the existing mapping in G and H will be used.
|
|
82
111
|
:param bond_key: (optional) The edge label in G and H which encodes the
|
|
83
112
|
bond order.
|
|
113
|
+
:param expected_rc: (optional) Configuration for the expected reaction
|
|
114
|
+
center. The first parameter in the tuple is the number of atoms and
|
|
115
|
+
the second parameter is the number of bonds. The atoms and bonds are
|
|
116
|
+
only constraint if the value is not None. It is possible to constraint
|
|
117
|
+
only atoms or only bonds. Constraining the expected reaction center can
|
|
118
|
+
help to get correct AAM for reactions where the Minimal Chemical
|
|
119
|
+
Distance is unsuitable, e.g. for a Diels-Alder reaction you can set
|
|
120
|
+
expected_rc to (6, 6).
|
|
84
121
|
|
|
85
122
|
:returns: A 3-tuple. The first element is the mapping matrix X. The second
|
|
86
123
|
element is the status string of the ILP solver (e.g. 'Optimal') and the
|
|
@@ -134,6 +171,14 @@ def expand_partial_aam_balanced(
|
|
|
134
171
|
lp_f == lp.lpSum([lp_G[i, j] + lp_S[i, j] for i in range(m) for j in range(m)]),
|
|
135
172
|
"objective",
|
|
136
173
|
)
|
|
174
|
+
if expected_rc is not None:
|
|
175
|
+
if not isinstance(expected_rc, tuple) or len(expected_rc) != 2:
|
|
176
|
+
raise ValueError("Argument expected_rc must be a tuple of size 2.")
|
|
177
|
+
expected_rc_atoms, expected_rc_bonds = expected_rc
|
|
178
|
+
if expected_rc_bonds is not None:
|
|
179
|
+
problem += (lp_f == 2 * expected_rc_bonds, "rc_bond_constraint")
|
|
180
|
+
if expected_rc_atoms is not None:
|
|
181
|
+
_expected_rc_atoms_constraint(problem, lp_G, lp_S, expected_rc_atoms)
|
|
137
182
|
|
|
138
183
|
_bijection_constraint(problem, lp_X)
|
|
139
184
|
_beta_constraint(problem, lp_X, beta_map)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import rdkit.Chem.rdmolops as rdmolops
|
|
2
|
+
import rdkit.Chem.rdmolfiles as rdmolfiles
|
|
3
|
+
|
|
4
|
+
from aamutils.utils import graph_to_mol, mol_to_graph, set_aam
|
|
5
|
+
from aamutils.algorithm.ilp import expand_partial_aam_balanced
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Mapper:
|
|
9
|
+
def __init__(self, include_H=False):
|
|
10
|
+
self.include_H = include_H
|
|
11
|
+
|
|
12
|
+
def _map_rxn(self, rxn_smiles: str, expected_rc):
|
|
13
|
+
g_smiles, h_smiles = rxn_smiles.split(">>")
|
|
14
|
+
g_mol = rdmolfiles.MolFromSmiles(g_smiles)
|
|
15
|
+
h_mol = rdmolfiles.MolFromSmiles(h_smiles)
|
|
16
|
+
if self.include_H:
|
|
17
|
+
g_mol = rdmolops.AddHs(g_mol, 0)
|
|
18
|
+
h_mol = rdmolops.AddHs(h_mol, 0)
|
|
19
|
+
G = mol_to_graph(g_mol)
|
|
20
|
+
H = mol_to_graph(h_mol)
|
|
21
|
+
|
|
22
|
+
M, status, e_diff = expand_partial_aam_balanced(G, H, expected_rc=expected_rc)
|
|
23
|
+
|
|
24
|
+
set_aam(G, H, M)
|
|
25
|
+
|
|
26
|
+
g_mol = graph_to_mol(G)
|
|
27
|
+
h_mol = graph_to_mol(H)
|
|
28
|
+
|
|
29
|
+
aam_smiles = "{}>>{}".format(
|
|
30
|
+
rdmolfiles.MolToSmiles(g_mol), rdmolfiles.MolToSmiles(h_mol)
|
|
31
|
+
)
|
|
32
|
+
return aam_smiles
|
|
33
|
+
|
|
34
|
+
def get_aam(self, smiles: str | list[str], expected_rc) -> str | list[str]:
|
|
35
|
+
return_list = True
|
|
36
|
+
if isinstance(smiles, str):
|
|
37
|
+
smiles = [smiles]
|
|
38
|
+
return_list = False
|
|
39
|
+
if expected_rc is None:
|
|
40
|
+
expected_rc = [None for _ in range(len(smiles))]
|
|
41
|
+
elif not isinstance(expected_rc, list):
|
|
42
|
+
expected_rc = [expected_rc]
|
|
43
|
+
if len(smiles) != len(expected_rc):
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"Argument min_bonds must be of same length ({}) as smiles.".format(
|
|
46
|
+
len(smiles)
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
aam_smiles = []
|
|
50
|
+
print(expected_rc)
|
|
51
|
+
for smiles, exp_rc in zip(smiles, expected_rc):
|
|
52
|
+
aam_smiles.append(self._map_rxn(smiles, expected_rc=exp_rc))
|
|
53
|
+
if not return_list:
|
|
54
|
+
return aam_smiles[0]
|
|
55
|
+
return aam_smiles
|
|
@@ -3,6 +3,7 @@ import numpy as np
|
|
|
3
3
|
import networkx as nx
|
|
4
4
|
import rdkit.Chem as Chem
|
|
5
5
|
import rdkit.Chem.rdmolfiles as rdmolfiles
|
|
6
|
+
import rdkit.Chem.rdDepictor as rdDepictor
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def mol_to_graph(mol: Chem.rdchem.Mol) -> nx.Graph:
|
|
@@ -26,6 +27,16 @@ def mol_to_graph(mol: Chem.rdchem.Mol) -> nx.Graph:
|
|
|
26
27
|
return g
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
def smiles_to_graph(smiles: str) -> nx.Graph | tuple[nx.Graph, nx.Graph]:
|
|
31
|
+
if ">>" in smiles:
|
|
32
|
+
smiles_token = smiles.split(">>")
|
|
33
|
+
g = mol_to_graph(rdmolfiles.MolFromSmiles(smiles_token[0]))
|
|
34
|
+
h = mol_to_graph(rdmolfiles.MolFromSmiles(smiles_token[1]))
|
|
35
|
+
return g, h
|
|
36
|
+
else:
|
|
37
|
+
return mol_to_graph(rdmolfiles.MolFromSmiles(smiles))
|
|
38
|
+
|
|
39
|
+
|
|
29
40
|
def graph_to_mol(
|
|
30
41
|
G: nx.Graph, symbol_key="symbol", aam_key="aam", bond_type_key="bond"
|
|
31
42
|
) -> Chem.rdchem.Mol:
|
|
@@ -37,13 +48,16 @@ def graph_to_mol(
|
|
|
37
48
|
1.5: Chem.rdchem.BondType.AROMATIC,
|
|
38
49
|
}
|
|
39
50
|
rw_mol = Chem.rdchem.RWMol()
|
|
51
|
+
idx_map = {}
|
|
40
52
|
for n, d in G.nodes(data=True):
|
|
41
53
|
idx = rw_mol.AddAtom(Chem.rdchem.Atom(d[symbol_key]))
|
|
54
|
+
idx_map[n] = idx
|
|
42
55
|
if aam_key in d.keys() and d[aam_key] >= 0:
|
|
43
56
|
rw_mol.GetAtomWithIdx(idx).SetAtomMapNum(d[aam_key])
|
|
44
|
-
assert n == idx
|
|
45
57
|
for n1, n2, d in G.edges(data=True):
|
|
46
|
-
|
|
58
|
+
idx1 = idx_map[n1]
|
|
59
|
+
idx2 = idx_map[n2]
|
|
60
|
+
rw_mol.AddBond(idx1, idx2, bond_order_map[d[bond_type_key]])
|
|
47
61
|
return rw_mol.GetMol()
|
|
48
62
|
|
|
49
63
|
|
|
@@ -123,3 +137,51 @@ def print_graph(graph):
|
|
|
123
137
|
)
|
|
124
138
|
)
|
|
125
139
|
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def its2mol(its: nx.Graph, aam_key="aam", bond_key="bond") -> Chem.rdchem.Mol:
|
|
143
|
+
_its = its.copy()
|
|
144
|
+
for n in _its.nodes:
|
|
145
|
+
_its.nodes[n][aam_key] = n
|
|
146
|
+
for u, v in _its.edges():
|
|
147
|
+
_its[u][v][bond_key] = 1
|
|
148
|
+
return graph_to_mol(_its)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def plot_its(
|
|
152
|
+
its, ax, bond_key="bond", aam_key="aam", symbol_key="symbol", use_mol_coords=True
|
|
153
|
+
):
|
|
154
|
+
bond_char = {None: "∅", 1: "—", 2: "=", 3: "≡"}
|
|
155
|
+
mol = its2mol(its, aam_key=aam_key, bond_key=bond_key)
|
|
156
|
+
|
|
157
|
+
if use_mol_coords:
|
|
158
|
+
positions = {}
|
|
159
|
+
conformer = rdDepictor.Compute2DCoords(mol)
|
|
160
|
+
for i, atom in enumerate(mol.GetAtoms()):
|
|
161
|
+
aam = atom.GetAtomMapNum()
|
|
162
|
+
apos = mol.GetConformer(conformer).GetAtomPosition(i)
|
|
163
|
+
positions[aam] = [apos.x, apos.y]
|
|
164
|
+
else:
|
|
165
|
+
positions = nx.spring_layout(its)
|
|
166
|
+
|
|
167
|
+
ax.axis("equal")
|
|
168
|
+
ax.axis("off")
|
|
169
|
+
|
|
170
|
+
nx.draw_networkx_edges(its, positions, edge_color="#000000", ax=ax)
|
|
171
|
+
nx.draw_networkx_nodes(its, positions, node_color="#FFFFFF", node_size=500, ax=ax)
|
|
172
|
+
|
|
173
|
+
labels = {n: "{}:{}".format(d[symbol_key], n) for n, d in its.nodes(data=True)}
|
|
174
|
+
edge_labels = {}
|
|
175
|
+
for u, v, d in its.edges(data=True):
|
|
176
|
+
bc1 = d[bond_key][0]
|
|
177
|
+
bc2 = d[bond_key][1]
|
|
178
|
+
if bc1 == bc2:
|
|
179
|
+
continue
|
|
180
|
+
if bc1 in bond_char.keys():
|
|
181
|
+
bc1 = bond_char[bc1]
|
|
182
|
+
if bc2 in bond_char.keys():
|
|
183
|
+
bc2 = bond_char[bc2]
|
|
184
|
+
edge_labels[(u, v)] = "({},{})".format(bc1, bc2)
|
|
185
|
+
|
|
186
|
+
nx.draw_networkx_labels(its, positions, labels=labels, ax=ax)
|
|
187
|
+
nx.draw_networkx_edge_labels(its, positions, edge_labels=edge_labels, ax=ax)
|
|
@@ -34,26 +34,6 @@ def test_example1():
|
|
|
34
34
|
np.testing.assert_array_equal(X, exp_X)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def test_example1_without_aam():
|
|
38
|
-
# The tested mapping is not correct. This is to show equal solutions based
|
|
39
|
-
# on the implemented optimization criterion. The correct mapping is in
|
|
40
|
-
# test_example1()
|
|
41
|
-
mol_G = rdmolfiles.MolFromSmiles("CCC[Cl].[N]")
|
|
42
|
-
mol_H = rdmolfiles.MolFromSmiles("CCC[N].[Cl]")
|
|
43
|
-
exp_X = np.zeros((5, 5))
|
|
44
|
-
exp_X[0, 2] = 1
|
|
45
|
-
exp_X[1, 1] = 1
|
|
46
|
-
exp_X[2, 0] = 1
|
|
47
|
-
exp_X[3, 4] = 1
|
|
48
|
-
exp_X[4, 3] = 1
|
|
49
|
-
G = mol_to_graph(mol_G)
|
|
50
|
-
H = mol_to_graph(mol_H)
|
|
51
|
-
X, status, v = expand_partial_aam_balanced(G, H)
|
|
52
|
-
assert 4 == v
|
|
53
|
-
assert "Optimal" == status
|
|
54
|
-
np.testing.assert_array_equal(X, exp_X)
|
|
55
|
-
|
|
56
|
-
|
|
57
37
|
def test_example2_m1():
|
|
58
38
|
mol_G = rdmolfiles.MolFromSmiles("[C:1][C:2]=[C:3][C:4]")
|
|
59
39
|
mol_H = rdmolfiles.MolFromSmiles("[C:1].[C:2]=[C:4][C:3]")
|
|
@@ -73,11 +53,7 @@ def test_example2_m1():
|
|
|
73
53
|
def test_example2_m2():
|
|
74
54
|
mol_G = rdmolfiles.MolFromSmiles("CC=CC")
|
|
75
55
|
mol_H = rdmolfiles.MolFromSmiles("C.C=CC")
|
|
76
|
-
exp_X = np.
|
|
77
|
-
exp_X[0, 3] = 1
|
|
78
|
-
exp_X[1, 2] = 1
|
|
79
|
-
exp_X[2, 1] = 1
|
|
80
|
-
exp_X[3, 0] = 1
|
|
56
|
+
exp_X = np.eye(4)
|
|
81
57
|
G = mol_to_graph(mol_G)
|
|
82
58
|
H = mol_to_graph(mol_H)
|
|
83
59
|
X, status, v = expand_partial_aam_balanced(G, H)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from aamutils.mapping import Mapper
|
|
2
|
+
from aamutils.algorithm.aaming import get_its, get_rc
|
|
3
|
+
from aamutils.utils import smiles_to_graph
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_mapper_with_expected_rc():
|
|
7
|
+
# Diels-Alder reaction example
|
|
8
|
+
smiles = "C1=CC=CC1.C1=CC=CC1>>C1=CC2CC1C3C2C=CC3"
|
|
9
|
+
mapper = Mapper()
|
|
10
|
+
aam_smiles = mapper.get_aam(smiles, expected_rc=(6, 6))
|
|
11
|
+
assert isinstance(aam_smiles, str)
|
|
12
|
+
g, h = smiles_to_graph(aam_smiles)
|
|
13
|
+
its = get_its(g, h)
|
|
14
|
+
rc = get_rc(its)
|
|
15
|
+
assert len(rc.nodes) == 6
|
|
16
|
+
assert len(rc.edges) == 6
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|