-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Commit the nAUTy-like canonical checker
- Loading branch information
Showing
2 changed files
with
217 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package branch; | ||
|
||
import group.BondDiscretePartitionRefiner; | ||
import group.Permutation; | ||
import group.PermutationGroup; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
import org.openscience.cdk.interfaces.IAtom; | ||
import org.openscience.cdk.interfaces.IAtomContainer; | ||
import org.openscience.cdk.interfaces.IBond; | ||
import org.openscience.cdk.interfaces.IChemObjectBuilder; | ||
|
||
import setorbit.BruteForcer; | ||
import setorbit.SetOrbit; | ||
|
||
/** | ||
* Canonical check using the procedure described in the nAUTy User Guide. | ||
* | ||
* For each edge color (bond order), make a layer in a new graph (molecule!) and make edges | ||
* between vertices in these layers according to the colors of edges in the original. | ||
* | ||
* @author maclean | ||
* | ||
*/ | ||
public class NautyLikeCanonicalChecker { | ||
|
||
public static boolean isCanonical(IAtomContainer atomContainer, Set<Integer> augmentedBonds) { | ||
IAtomContainer transformedContainer = transform(atomContainer); | ||
Set<Integer> transformedAugmentedBonds = transformBonds(atomContainer, transformedContainer, augmentedBonds); | ||
BondDiscretePartitionRefiner bondRefiner = new BondDiscretePartitionRefiner(); | ||
PermutationGroup autGE = bondRefiner.getAutomorphismGroup(transformedContainer); | ||
Permutation p = bondRefiner.getBest(); | ||
Set<Integer> chosen = chosen(transformedContainer, p); | ||
return inOrbit(transformedAugmentedBonds, chosen, autGE); | ||
} | ||
|
||
private static Set<Integer> transformBonds( | ||
IAtomContainer atomContainer, IAtomContainer transformedContainer, Set<Integer> augmentedBonds) { | ||
Set<Integer> chosen = new HashSet<Integer>(); | ||
|
||
// bit of a hack! | ||
int numberOfLayers = transformedContainer.getAtomCount() / atomContainer.getAtomCount(); | ||
|
||
for (int bondIndex : augmentedBonds) { | ||
IBond bond = atomContainer.getBond(bondIndex); | ||
int order = bond.getOrder().numeric(); | ||
int a0i = transformedIndex(atomContainer, bond.getAtom(0), numberOfLayers, order); | ||
int a1i = transformedIndex(atomContainer, bond.getAtom(1), numberOfLayers, order); | ||
IAtom tA0 = transformedContainer.getAtom(a0i); | ||
IAtom tA1 = transformedContainer.getAtom(a1i); | ||
chosen.add(transformedContainer.getBondNumber(tA0, tA1)); | ||
} | ||
return chosen; | ||
} | ||
|
||
private static boolean inOrbit(Set<Integer> augmentedBonds, Set<Integer> chosen, PermutationGroup g) { | ||
List<Integer> setList = new ArrayList<Integer>(); | ||
for (int a : augmentedBonds) { setList.add(a); } | ||
SetOrbit orbit = new BruteForcer().getInOrbit(setList, g); | ||
for (List<Integer> o : orbit) { | ||
if (o.equals(chosen)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private static Set<Integer> chosen(IAtomContainer ac, Permutation p) { | ||
Set<Integer> chosen = new HashSet<Integer>(); | ||
|
||
return chosen; | ||
} | ||
|
||
public static IAtomContainer transform(IAtomContainer atomContainer) { | ||
IChemObjectBuilder builder = atomContainer.getBuilder(); | ||
IAtomContainer transformed = builder.newInstance(IAtomContainer.class); | ||
|
||
int numberOfLayers = 1; | ||
for (IBond bond : atomContainer.bonds()) { | ||
int o = bond.getOrder().numeric(); | ||
if (o > numberOfLayers) { | ||
numberOfLayers = o; | ||
} | ||
} | ||
|
||
for (IAtom atom : atomContainer.atoms()) { | ||
IAtom prev = null; | ||
for (int layerIndex = 0; layerIndex < numberOfLayers; layerIndex++) { | ||
IAtom newAtom = builder.newInstance(IAtom.class, atom.getSymbol()); | ||
transformed.addAtom(newAtom); | ||
if (prev != null) { | ||
transformed.addBond(builder.newInstance(IBond.class, prev, newAtom)); | ||
} | ||
prev = newAtom; | ||
} | ||
} | ||
|
||
for (IBond bond : atomContainer.bonds()) { | ||
int o = bond.getOrder().numeric(); | ||
int a0t = transformedIndex(atomContainer, bond.getAtom(0), numberOfLayers, o); | ||
int a1t = transformedIndex(atomContainer, bond.getAtom(1), numberOfLayers, o); | ||
transformed.addBond(a0t, a1t, IBond.Order.SINGLE); | ||
} | ||
|
||
return transformed; | ||
} | ||
|
||
private static int transformedIndex(IAtomContainer ac, IAtom atom, int numberOfLayers, int order) { | ||
return transformedIndex(ac, ac.getAtomNumber(atom), numberOfLayers, order); | ||
} | ||
|
||
private static int transformedIndex(IAtomContainer ac, int originalIndex, int numberOfLayers, int order) { | ||
// System.out.println(String.format("(%s * %s) + %s)", originalIndex, numberOfLayers, order - 1)); | ||
return (originalIndex * numberOfLayers) + (order - 1); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package test.branch; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertTrue; | ||
import io.AtomContainerPrinter; | ||
|
||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import org.junit.Test; | ||
import org.openscience.cdk.interfaces.IAtom; | ||
import org.openscience.cdk.interfaces.IAtomContainer; | ||
import org.openscience.cdk.interfaces.IBond; | ||
import org.openscience.cdk.interfaces.IChemObjectBuilder; | ||
import org.openscience.cdk.silent.SilentChemObjectBuilder; | ||
|
||
import branch.NautyLikeCanonicalChecker; | ||
|
||
public class TestNautyLikeCanonicalChecker { | ||
|
||
private IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance(); | ||
|
||
@Test | ||
public void testA() { | ||
IAtomContainer ac = AtomContainerPrinter.fromString("C0C1C2 0:1(1),1:2(2)", builder); | ||
IAtomContainer transformed = NautyLikeCanonicalChecker.transform(ac); | ||
assertEquals("Should have 6 vertices", 6, transformed.getAtomCount()); | ||
AtomContainerPrinter.print(transformed); | ||
|
||
// 'backbone' edges | ||
assertEdge(transformed, 0, 1); | ||
assertEdge(transformed, 2, 3); | ||
assertEdge(transformed, 4, 5); | ||
|
||
// bond order edges | ||
assertEdge(transformed, 0, 2); // single bond | ||
assertEdge(transformed, 3, 5); // double bond | ||
} | ||
|
||
@Test | ||
public void testB() { | ||
IAtomContainer ac = AtomContainerPrinter.fromString("C0C1C2C3 0:1(1),0:3(2),1:2(2),2:3(1)", builder); | ||
IAtomContainer transformed = NautyLikeCanonicalChecker.transform(ac); | ||
assertEquals("Should have 8 vertices", 8, transformed.getAtomCount()); | ||
|
||
assertEdge(transformed, 0, 2); | ||
assertEdge(transformed, 1, 7); | ||
assertEdge(transformed, 3, 5); | ||
assertEdge(transformed, 4, 6); | ||
} | ||
|
||
@Test | ||
public void testC() { | ||
IAtomContainer ac = AtomContainerPrinter.fromString("C0C1C2C3 0:1(2),0:2(1),1:2(1),2:3(3)", builder); | ||
IAtomContainer transformed = NautyLikeCanonicalChecker.transform(ac); | ||
assertEquals("Should have 12 vertices", 12, transformed.getAtomCount()); | ||
|
||
assertEdge(transformed, 0, 6); | ||
assertEdge(transformed, 1, 4); | ||
assertEdge(transformed, 3, 6); | ||
assertEdge(transformed, 8, 11); | ||
} | ||
|
||
@Test | ||
public void testBPair() { | ||
IAtomContainer ac1 = AtomContainerPrinter.fromString("C0C1C2C3 0:1(1),0:3(2),1:2(2),2:3(1)", builder); | ||
IAtomContainer transformed1 = NautyLikeCanonicalChecker.transform(ac1); | ||
NautyLikeCanonicalChecker.isCanonical(transformed1, set(0, 2)); | ||
|
||
IAtomContainer ac2 = AtomContainerPrinter.fromString("C0C1C2C3 0:1(1),0:2(2),1:3(2),2:3(1)", builder); | ||
IAtomContainer transformed2 = NautyLikeCanonicalChecker.transform(ac2); | ||
NautyLikeCanonicalChecker.isCanonical(transformed2, set(1, 2)); | ||
} | ||
|
||
private Set<Integer> set(int... ints) { | ||
Set<Integer> set = new HashSet<Integer>(); | ||
for (int i : ints){ | ||
set.add(i); | ||
} | ||
return set; | ||
} | ||
|
||
private void assertEdge(IAtomContainer ac, int a0i, int a1i) { | ||
boolean found = false; | ||
for (IBond bond : ac.bonds()) { | ||
IAtom a0 = bond.getAtom(0); | ||
IAtom a1 = bond.getAtom(1); | ||
int a0j = ac.getAtomNumber(a0); | ||
int a1j = ac.getAtomNumber(a1); | ||
found = (a0i == a0j && a1i == a1j) || (a1i == a0j && a0i == a1j); | ||
if (found) break; | ||
} | ||
assertTrue(String.format("No edge (%s, %s)", a0i, a1i), found); | ||
} | ||
|
||
} |