Skip to content
This repository has been archived by the owner on Jul 10, 2024. It is now read-only.

Commit

Permalink
Merge pull request #17 from ncats/symmetryHandle
Browse files Browse the repository at this point in the history
fix with some tests for issue #4, #3, #7, needs evaluation
  • Loading branch information
tylerperyea authored May 1, 2019
2 parents 9558e17 + 2926b8b commit 7f66a2a
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 6 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<groupId>gov.nih.ncats</groupId>
<artifactId>lychi</artifactId>
<packaging>jar</packaging>
<version>0.5.1ISOTOPE_FIX</version>
<version>0.5.2</version>
<name>Lychi</name>

<repositories>
Expand Down
138 changes: 137 additions & 1 deletion src/main/java/lychi/LyChIStandardizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,19 @@ public class LyChIStandardizer {

/**
* This static version value must be updated if any changes is made
* to this class that would be imcompatible with earlier results!!!
* to this class that would be incompatible with earlier results!!!
*/
public static final int VERSION = 0x10;



/**
* This flag, when true, checks for "deeper" symmetry by enumerating
* unspecified stereo forms and confirming that they
*/
private static final boolean DEEP_SYMMETRY = true;


static final private boolean DEBUG;
static final private boolean UNMEX; // apply UNM extra rules
static {
Expand Down Expand Up @@ -238,6 +247,7 @@ protected SMIRKS[] initialValue () {
}
};


static class MolComparator implements Comparator<Molecule> {
public int compare (Molecule m1, Molecule m2) {
if (m1 == null && m2 == null) return 0;
Expand Down Expand Up @@ -1149,6 +1159,132 @@ else if (chiral != 0) {
}
}
}

if(DEEP_SYMMETRY){
try{

Map<MolAtom,MolBond> nonChiralStereo = new LinkedHashMap<>();

for(int k=0;k<m.getBondCount();k++){
MolBond b = m.getBond(k);
int parity = b.getFlags() & MolBond.STEREO1_MASK;
MolAtom ma1=b.getAtom1();
if(chirality.get(ma1)==null){
if(parity!=0){
//some other parity assigned here
// if(parity==MolBond.UP)System.out.println("UP");
// if(parity==MolBond.DOWN)System.out.println("DOWN");
nonChiralStereo.put(ma1, b);
}
}
}

if(!nonChiralStereo.isEmpty()){
String igprop=m.getProperty("IGNORE_COMPLEX");

if(!"true".equals(igprop)){
m.setProperty("IGNORE_COMPLEX", "true");

Set<int[]> rings = new HashSet<int[]>();

int[][] sssr=m.getSSSR();
for(MolAtom ma:nonChiralStereo.keySet()){
//need to find all atoms in the ring
int im=m.indexOf(ma);
for(int[] ir:sssr){
for(int i=0;i<ir.length;i++){
if(ir[i]==im){
rings.add(ir);
}
}

}
}

for(int[] rr:rings){
Set<MolAtom> ratoms=Arrays.stream(rr)
.mapToObj(i->m.getAtom(i))
.collect(Collectors.toSet());

MolBond[] bonds=ratoms.stream()
.filter(a->!chirality.containsKey(a))
.flatMap(a->IntStream.range(0, a.getEdgeCount()).mapToObj(i->a.getEdge(i)))
.filter(e->!ratoms.contains(e.getNode1()) || !ratoms.contains(e.getNode2()))
.map(b->(MolBond)b)
.filter(b->b.getType()==1)
.peek(b->{
if(ratoms.contains(b.getAtom1()))b.swap();
})
.toArray(i->new MolBond[i]);

BitSet bs = new BitSet(bonds.length*2);
for(int i=0;i<bonds.length;i++){
MolBond b=bonds[i];
int parity = b.getFlags() & MolBond.STEREO1_MASK;
if(parity==MolBond.UP){
bs.set(i*2);
}else if(parity==MolBond.DOWN){
bs.set(i*2+1);
}else{
bs.set(i*2);
bs.set(i*2+1);
}
}

Set<String> allPossible = new HashSet<String>();
Set<String> currentPossible = new HashSet<String>();

for(int i=0;i<Math.pow(2, bonds.length);i++){
BitSet onOff = new BitSet(bonds.length*2);
for(int j=0;j<bonds.length;j++){
if((i>>j&1)==1){
onOff.set(j*2);
bonds[j].setFlags(MolBond.UP, MolBond.STEREO1_MASK);
}else{
onOff.set(j*2+1);
bonds[j].setFlags(MolBond.DOWN, MolBond.STEREO1_MASK);
}
}
Molecule mclone=m.cloneMolecule();
//(new LyChIStandardizer()).standardize(mclone);
String hash1=LyChIStandardizer.hashKey(mclone);
allPossible.add(hash1);
onOff.or(bs);

if(onOff.cardinality() == bs.cardinality()){
currentPossible.add(hash1);
}
}
if(allPossible.size()==currentPossible.size()){
for(int j=0;j<bonds.length;j++){
bonds[j].setFlags(0, MolBond.STEREO1_MASK);
}
}else{
for(int j=0;j<bonds.length;j++){
boolean isUp=bs.get(j*2);
boolean isDown=bs.get(j*2+1);

if(isUp && ! isDown){
bonds[j].setFlags(MolBond.UP, MolBond.STEREO1_MASK);
}else if(!isUp && isDown){
bonds[j].setFlags(MolBond.DOWN, MolBond.STEREO1_MASK);
}else{
bonds[j].setFlags(0, MolBond.STEREO1_MASK);
}
}
}
}



m.setProperty("IGNORE_COMPLEX", null);
}
}

}catch(Exception e){
logger.warning("Processing symmetry threw an error:" + e.getMessage());
}
}

/*
* and finally adjust the wedge/hash based on the chirality
Expand Down
64 changes: 60 additions & 4 deletions src/test/java/lychi/LychiRegressionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,64 @@ public static List<Object[]> data(){

tests.add(LychiTestInstance.of("CN(C)CCOC(C1=CC=CC=C1)C2=CC=CC=C2","SG1MX4TJL-LRQMG7F9KY-LYVJD4DSRGU-LYU23YRCSQTR").name("test lychi change"));

tests.add(LychiTestInstance.equivalent("CC(C)(C)C1CCC2(CC1)CCN(CCCN3CCOCC3)CC2","NCGC00013953\n" +
" -IDBS- 1129050841\n\n" +
" 24 26 0 0 0 0 0 0 0 0999 V2000\n" +
" 0.2296 -3.5406 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -0.5954 -3.5406 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -1.4204 -3.5406 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -0.5954 -4.3656 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -0.5954 -2.7156 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 0.1191 -2.3031 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 0.1191 -1.4781 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -0.5954 -1.0656 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -1.3099 -1.4781 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -1.3099 -2.3031 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 0.1191 -0.6531 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 0.1191 0.1719 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -0.5954 0.5844 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -0.5954 1.4094 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 0.1191 1.8219 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 0.1191 2.6469 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 0.8335 3.0594 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 1.5480 2.6469 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 2.2625 3.0594 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 2.2625 3.8844 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 1.5480 4.2969 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 0.8335 3.8844 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -1.3099 0.1719 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" -1.3099 -0.6531 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" 1 2 1 0 0 0\n" +
" 2 3 1 0 0 0\n" +
" 2 4 1 0 0 0\n" +
" 5 2 1 1 0 0\n" +
" 5 6 1 0 0 0\n" +
" 6 7 1 0 0 0\n" +
" 8 7 1 6 0 0\n" +
" 8 9 1 0 0 0\n" +
" 9 10 1 0 0 0\n" +
" 5 10 1 0 0 0\n" +
" 8 11 1 0 0 0\n" +
" 11 12 1 0 0 0\n" +
" 12 13 1 0 0 0\n" +
" 13 14 1 0 0 0\n" +
" 14 15 1 0 0 0\n" +
" 15 16 1 0 0 0\n" +
" 16 17 1 0 0 0\n" +
" 17 18 1 0 0 0\n" +
" 18 19 1 0 0 0\n" +
" 19 20 1 0 0 0\n" +
" 20 21 1 0 0 0\n" +
" 21 22 1 0 0 0\n" +
" 17 22 1 0 0 0\n" +
" 13 23 1 0 0 0\n" +
" 23 24 1 0 0 0\n" +
" 8 24 1 0 0 0\n" +
"M END").name("spiro stereo without meaning should not change lychi"));


tests.add(LychiTestInstance.equivalentLayer3("CC(C)(CO)[C@@H](O)C(=O)NCCC(O)=O","CC(C)(CO)[CH](O)C(=O)NCCC(O)=O").name("layer 3 the same when only stereo changes"));
tests.add(LychiTestInstance.equivalentLayer3("CCCCCCCCCCCCCC.CC(C)(CO)[C@@H](O)C(=O)NCCC(O)=O","CCCCCCCCCCCCCC.CC(C)(CO)[CH](O)C(=O)NCCC(O)=O").name("rare salt should be stripped, regardless of stereo"));
//tests.add(LychiTestInstance.equivalentLayer3("CC(C)(CO)[C@@H](O)C(=O)NCCC(O)=O","CC(C)(CO)[CH](O)C(=O)NCCC(O)=O").name("layer 3 the same when only stereo changes"));
//tests.add(LychiTestInstance.equivalentLayer3("CCCCCCCCCCCCCC.CC(C)(CO)[C@@H](O)C(=O)NCCC(O)=O","CCCCCCCCCCCCCC.CC(C)(CO)[CH](O)C(=O)NCCC(O)=O").name("rare salt should be stripped, regardless of stereo"));

tests.add(LychiTestInstance.of("[H][C@@]12[C@@H]3SC[C@]4(NCCC5=C4C=C(OC)C(O)=C5)C(=O)OC[C@H](N1[C@@H](O)[C@@H]6CC7=C([C@H]2N6C)C(O)=C(OC)C(C)=C7)C8=C9OCOC9=C(C)C(OC(C)=O)=C38", "DCLRH149F-FFMPLZ16VC-FC35942KGAU-FCUDSDS2V1NT").name("round trip problem"));

Expand Down Expand Up @@ -418,7 +473,7 @@ public static List<Object[]> data(){

//These are tests that don't pass currently, because they deal
//with complex symmetry, should be uncommented later
/*

tests.add(LychiTestInstance.equivalent("C[C@H]1C[C@@H](C)CC(C)C1","C[C@@H]1C[C@H](C)CC(C)C1")
.name("symmetric half-defined stereo should be the same"));

Expand All @@ -427,7 +482,8 @@ public static List<Object[]> data(){

tests.add(LychiTestInstance.equivalent("C[C@H]1OC(C)O[C@@H](C)O1","CC1OC(C)OC(C)O1")
.name("meaningless stereo with 2 dashed bonds on ring shouldn't be honored"));
*/





Expand Down

0 comments on commit 7f66a2a

Please sign in to comment.