Skip to content

Commit 638b0bc

Browse files
committed
Substructure matching against structures with stereogroups.
1 parent 8a325e6 commit 638b0bc

File tree

3 files changed

+98
-0
lines changed

3 files changed

+98
-0
lines changed

base/isomorphism/src/main/java/org/openscience/cdk/isomorphism/QueryStereoFilter.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ final class QueryStereoFilter implements Predicate<int[]> {
7373
/** Indices of focus atoms of stereo elements. */
7474
private final int[] queryStereoIndices, targetStereoIndices;
7575

76+
/**
77+
* Indicates the stereo group config for a given atom idx, 0=unsed, 1=stored, -1=inverted.
78+
* Initially all entries start as 0, if we hit a stereo-element in a group &1, &2, or1, or2
79+
* then we check if we have already "set" the group, if not then we "set" the group to make
80+
* the first element match, this means we may choose to flip the group to be the enantiomer.
81+
*/
82+
private int[] groupConfigAdjust;
83+
7684
/**
7785
* Create a predicate for checking mappings between a provided
7886
* {@code query} and {@code target}.
@@ -108,6 +116,11 @@ public QueryStereoFilter(IAtomContainer query, IAtomContainer target) {
108116
*/
109117
@Override
110118
public boolean apply(final int[] mapping) {
119+
120+
// reset augment group config if it was initialised
121+
if (groupConfigAdjust != null)
122+
Arrays.fill(groupConfigAdjust, 0);
123+
111124
for (final int u : queryStereoIndices) {
112125
switch (queryTypes[u]) {
113126
case Tetrahedral:
@@ -176,6 +189,41 @@ private boolean checkTetrahedral(int u, int[] mapping) {
176189
* permutationParity(vs)
177190
* parity(targetElement.getStereo());
178191

192+
int groupInfo = targetElement.getGroupInfo();
193+
194+
if (groupInfo != 0) {
195+
if (groupConfigAdjust == null)
196+
groupConfigAdjust = new int[target.getAtomCount()];
197+
198+
// initialise the group
199+
if (groupConfigAdjust[v] == 0) {
200+
201+
boolean leftOk = ((QueryAtom) queryAtom).getExpression().matches(targetAtom, IStereoElement.LEFT);
202+
boolean rghtOk = ((QueryAtom) queryAtom).getExpression().matches(targetAtom, IStereoElement.RIGHT);
203+
204+
// Note: [C@,Si@@] can not happen since the target atom can't be both
205+
// but [C;@,@@] can in which case we can't 'set' the group based on this
206+
// element so wait till we find the next one
207+
if (leftOk && rghtOk) {
208+
return true;
209+
}
210+
211+
int adjust = 1;
212+
if ((parity == -1 && !leftOk) || (parity == 1 && !rghtOk))
213+
adjust = -1;
214+
215+
for (int idx : targetStereoIndices) {
216+
if (targetElements[idx].getGroupInfo() == groupInfo) {
217+
groupConfigAdjust[idx] = adjust;
218+
}
219+
}
220+
221+
}
222+
223+
// make the adjustment
224+
parity *= groupConfigAdjust[v];
225+
}
226+
179227
if (parity < 0)
180228
return ((QueryAtom) queryAtom).getExpression()
181229
.matches(targetAtom, IStereoElement.LEFT);

base/isomorphism/src/main/java/org/openscience/cdk/isomorphism/StereoMatch.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ final class StereoMatch implements Predicate<int[]> {
7272
/** Indices of focus atoms of stereo elements. */
7373
private final int[] queryStereoIndices, targetStereoIndices;
7474

75+
/**
76+
* Indicates the stereo group config for a given atom idx, 0=unsed, 1=stored, -1=inverted.
77+
* Initially all entries start as 0, if we hit a stereo-element in a group &1, &2, or1, or2
78+
* then we check if we have already "set" the group, if not then we "set" the group to make
79+
* the first element match, this means we may choose to flip the group to be the enantiomer.
80+
*/
81+
private int[] groupConfigAdjust;
82+
7583
/**
7684
* Create a predicate for checking mappings between a provided
7785
* {@code query} and {@code target}.
@@ -106,6 +114,10 @@ public boolean apply(final int[] mapping) {
106114
// n.b. not true for unspecified queries e.g. [C@?H](*)(*)*
107115
if (queryStereoIndices.length > targetStereoIndices.length) return false;
108116

117+
// reset augment group config if it was initialised
118+
if (groupConfigAdjust != null)
119+
Arrays.fill(groupConfigAdjust, 0);
120+
109121
for (final int u : queryStereoIndices) {
110122
switch (queryTypes[u]) {
111123
case Tetrahedral:
@@ -144,6 +156,24 @@ private boolean checkTetrahedral(int u, int[] mapping) {
144156
int p = permutationParity(us) * parity(queryElement.getStereo());
145157
int q = permutationParity(vs) * parity(targetElement.getStereo());
146158

159+
int groupInfo = targetElement.getGroupInfo();
160+
if (groupInfo != 0) {
161+
if (groupConfigAdjust == null)
162+
groupConfigAdjust = new int[target.getAtomCount()];
163+
164+
// 'set' the group either to be 'as stored' or 'flipped'
165+
if (groupConfigAdjust[v] == 0) {
166+
int adjust = p == q ? +1 : -1;
167+
for (int idx : targetStereoIndices) {
168+
if (targetElements[idx].getGroupInfo() == groupInfo)
169+
groupConfigAdjust[idx] = adjust;
170+
}
171+
}
172+
173+
// make the adjustment
174+
q *= groupConfigAdjust[v];
175+
}
176+
147177
return p == q;
148178
}
149179

tool/smarts/src/test/java/org/openscience/cdk/smarts/SubstructureTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,26 @@ public void sulfoxide() throws Exception {
359359
assertMatch(sma("O1.[S@]=1(C)CC"), smi("O=[S@@](C)CC"), 1);
360360
}
361361

362+
@Test
363+
public void stereoGroups() throws Exception {
364+
assertMatch(sma("C[C@H](O)[C@H](O)CC"), smi("C[C@H](O)[C@H](O)CC |r|"), 1);
365+
assertMatch(sma("C[C@@H](O)[C@@H](O)CC"), smi("C[C@H](O)[C@H](O)CC |r|"), 1);
366+
assertMatch(sma("C[C@H](O)[C@H](O)CC"), smi("C[C@@H](O)[C@@H](O)CC |r|"), 1);
367+
assertMatch(sma("C[C@@H](O)[C@@H](O)CC"), smi("C[C@@H](O)[C@@H](O)CC |r|"), 1);
368+
assertMismatch(sma("C[C@H](O)[C@@H](O)CC"), smi("C[C@H](O)[C@H](O)CC |r|"));
369+
assertMismatch(sma("C[C@@H](O)[C@H](O)CC"), smi("C[C@H](O)[C@H](O)CC |r|"));
370+
assertMismatch(sma("C[C@H](O)[C@@H](O)CC"), smi("C[C@@H](O)[C@@H](O)CC |r|"));
371+
assertMismatch(sma("C[C@@H](O)[C@H](O)CC"), smi("C[C@@H](O)[C@@H](O)CC |r|"));
372+
}
373+
374+
@Test
375+
public void stereoGroupsQuery() throws Exception {
376+
assertMatch(sma("C[C;@,@@](O)[C@H](O)[C@H](O)CC"), smi("C[C@H](O)[C@H](O)[C@H](O)CC |r|"), 1);
377+
assertMatch(sma("C[C;@,@@](O)[C@H](O)[C@H](O)CC"), smi("C[C@@H](O)[C@@H](O)[C@@H](O)CC |r|"), 1);
378+
assertMismatch(sma("C[C;@,@@](O)[C@H](O)[C@H](O)CC"), smi("C[C@H](O)[C@@H](O)[C@H](O)CC |r|"));
379+
assertMismatch(sma("C[C;@,@@](O)[C@H](O)[C@H](O)CC"), smi("C[C@H](O)[C@H](O)[C@@H](O)CC |r|"));
380+
}
381+
362382
// doesn't matter if the match takes place but it should not cause and error
363383
// if the query is larger than the target
364384
@Test

0 commit comments

Comments
 (0)