Skip to content

Commit

Permalink
Commit the nAUTy-like canonical checker
Browse files Browse the repository at this point in the history
  • Loading branch information
gilleain committed Dec 7, 2015
1 parent a7a96fe commit 3ebaa2a
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 0 deletions.
121 changes: 121 additions & 0 deletions src/branch/NautyLikeCanonicalChecker.java
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);
}

}
96 changes: 96 additions & 0 deletions src/test/branch/TestNautyLikeCanonicalChecker.java
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);
}

}

0 comments on commit 3ebaa2a

Please sign in to comment.