diff --git a/src/main/org/openscience/cdk/structgen/stochastic/PartialFilledStructureMerger.java b/src/main/org/openscience/cdk/structgen/stochastic/PartialFilledStructureMerger.java index 0d362dff18e..2812cea71a7 100644 --- a/src/main/org/openscience/cdk/structgen/stochastic/PartialFilledStructureMerger.java +++ b/src/main/org/openscience/cdk/structgen/stochastic/PartialFilledStructureMerger.java @@ -25,37 +25,21 @@ package org.openscience.cdk.structgen.stochastic; import org.openscience.cdk.exception.CDKException; -import org.openscience.cdk.graph.ConnectivityChecker; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; +import org.openscience.cdk.interfaces.IAtomContainerSet; import org.openscience.cdk.tools.ILoggingTool; import org.openscience.cdk.tools.LoggingToolFactory; import org.openscience.cdk.tools.SaturationChecker; +import org.openscience.cdk.tools.manipulator.AtomContainerSetManipulator; import org.openscience.cdk.tools.manipulator.BondManipulator; /** - * Randomly generates a single, connected, correctly bonded structure for - * a given molecular formula. - * To see it working run the graphical - * test org.openscience.cdk.test.SingleStructureRandomGeneratorTest - * and add more structures to the panel using the "More" button. - * In order to use this class, use MFAnalyser to get an AtomContainer from - * a molecular formula string. - * + * Randomly generates a single, connected, correctly bonded structure from + * a number of fragments. *

Assign hydrogen counts to each heavy atom. The hydrogens should not be * in the atom pool but should be assigned implicitly to the heavy atoms in * order to reduce computational cost. - * - *

Assign this AtomContainer to the - * PartialFilledStructureMerger and retrieve a randomly generated, but correctly bonded - * structure by using the generate() method. You can then repeatedly call - * the generate() method in order to retrieve further structures. - * - *

Agenda: - *

* * @author steinbeck * @cdk.created 2001-09-04 @@ -67,110 +51,111 @@ public class PartialFilledStructureMerger { private ILoggingTool logger = LoggingToolFactory.createLoggingTool(PartialFilledStructureMerger.class); - IAtomContainer atomContainer; SaturationChecker satCheck; /** * Constructor for the PartialFilledStructureMerger object. */ - public PartialFilledStructureMerger() throws java.lang.Exception + public PartialFilledStructureMerger() { satCheck = new SaturationChecker(); } /** - * Sets the AtomContainer attribute of the PartialFilledStructureMerger object - * - * @param gc The new AtomContainer value + * Randomly generates a single, connected, correctly bonded structure from + * a number of fragments. + * + * @param atomContainers The fragments to generate for. IMPORTANT: The AtomContainers in the set must be connected. + * If an AtomContainer is disconnected, the generated structure is not guaranteed to be connected. + * @return + * @throws CDKException */ - public void setAtomContainer(IAtomContainer gc) - { - this.atomContainer = gc; - } - - public IAtomContainer getAtomContainer() - { - return this.atomContainer; - } - - public IAtomContainer generate() throws CDKException + public IAtomContainerSet generate(IAtomContainerSet atomContainers) throws CDKException { - boolean structureFound = false; - boolean bondFormed; - double order; - double max, cmax1, cmax2; int iteration = 0; - IAtom partner; - IAtom atom; - IAtomContainer backup = atomContainer.getBuilder().newAtomContainer(atomContainer); + boolean structureFound = false; do { iteration++; - - atomContainer = backup; - do - { - bondFormed = false; - for (int f = 0; f < atomContainer.getAtomCount(); f++) - { - atom = atomContainer.getAtom(f); - - if (!satCheck.isSaturated(atom, atomContainer)) - { - partner = getAnotherUnsaturatedNode(atom); - if (partner != null) + boolean bondFormed; + do{ + bondFormed=false; + for(IAtomContainer ac : atomContainers.atomContainers()){ + for(IAtom atom : ac.atoms()){ + if (!satCheck.isSaturated(atom, ac)) { - cmax1 = satCheck.getCurrentMaxBondOrder(atom, atomContainer); - cmax2 = satCheck.getCurrentMaxBondOrder(partner, atomContainer); - max = Math.min(cmax1, cmax2); - order = Math.min(Math.max(1.0, (double)Math.round(Math.random() * max)), 3.0); - logger.debug("cmax1, cmax2, max, order: " + cmax1 + ", " + cmax2 + ", " + max + ", " + order); - - atomContainer.addBond( - atomContainer.getBuilder().newBond( - atom, partner, BondManipulator.createBondOrder(order) - ) - ); - bondFormed = true; - } - } + IAtom partner = getAnotherUnsaturatedNode(atom, atomContainers); + if (partner != null) + { + IAtomContainer toadd = AtomContainerSetManipulator.getRelevantAtomContainer(atomContainers, partner); + double cmax1 = satCheck.getCurrentMaxBondOrder(atom, ac); + double cmax2 = satCheck.getCurrentMaxBondOrder(partner, toadd); + double max = Math.min(cmax1, cmax2); + double order = Math.min(Math.max(1.0, max), 3.0);//(double)Math.round(Math.random() * max) + logger.debug("cmax1, cmax2, max, order: " + cmax1 + ", " + cmax2 + ", " + max + ", " + order); + if(toadd!=ac){ + atomContainers.removeAtomContainer(toadd); + ac.add(toadd); + } + ac.addBond( + ac.getBuilder().newBond( + atom, partner, BondManipulator.createBondOrder(order) + ) + ); + bondFormed = true; + } + } + } } - } while (bondFormed); - if (ConnectivityChecker.isConnected(atomContainer) && satCheck.allSaturated(atomContainer)) + }while(bondFormed); + if (atomContainers.getAtomContainerCount()==1 && satCheck.allSaturated(atomContainers.getAtomContainer(0))) { structureFound = true; } - } while (!structureFound && iteration < 300); - System.out.println("Structure found after " + iteration + " iterations."); - return atomContainer; + } while (!structureFound && iteration < 5); + if (atomContainers.getAtomContainerCount()==1 && satCheck.allSaturated(atomContainers.getAtomContainer(0))) + { + structureFound = true; + } + if(!structureFound) + throw new CDKException("Could not combine the fragments to combine a valid, satured structure"); + return atomContainers; } /** - * Gets the AnotherUnsaturatedNode attribute of the PartialFilledStructureMerger object + * Gets a randomly selected unsaturated atom from the set. If there are any, it will be from another + * container than exclusionAtom. * - * @return The AnotherUnsaturatedNode value + * @return The unsaturated atom. */ - private IAtom getAnotherUnsaturatedNode(IAtom exclusionAtom) throws CDKException + private IAtom getAnotherUnsaturatedNode(IAtom exclusionAtom, IAtomContainerSet atomContainers) throws CDKException { IAtom atom; - int next = (int) (Math.random() * atomContainer.getAtomCount()); - for (int f = next; f < atomContainer.getAtomCount(); f++) - { - atom = atomContainer.getAtom(f); - if (!satCheck.isSaturated(atom, atomContainer) && exclusionAtom != atom && !atomContainer.getConnectedAtomsList(exclusionAtom).contains(atom)) - { - return atom; + for(IAtomContainer ac : atomContainers.atomContainers()){ + if(!ac.contains(exclusionAtom)){ + int next = 0;//(int) (Math.random() * ac.getAtomCount()); + for (int f = next; f < ac.getAtomCount(); f++) + { + atom = ac.getAtom(f); + if (!satCheck.isSaturated(atom, ac) && exclusionAtom != atom && !ac.getConnectedAtomsList(exclusionAtom).contains(atom)) + { + return atom; + } + } } } - for (int f = 0; f < next; f++) - { - atom = atomContainer.getAtom(f); - if (!satCheck.isSaturated(atom, atomContainer) && exclusionAtom != atom && !atomContainer.getConnectedAtomsList(exclusionAtom).contains(atom)) + for(IAtomContainer ac : atomContainers.atomContainers()){ + int next = ac.getAtomCount();//(int) (Math.random() * ac.getAtomCount()); + for (int f = 0; f < next; f++) { - return atom; + atom = ac.getAtom(f); + if (!satCheck.isSaturated(atom, ac) && exclusionAtom != atom && !ac.getConnectedAtomsList(exclusionAtom).contains(atom)) + { + return atom; + } } } return null; diff --git a/src/main/org/openscience/cdk/structgen/stochastic/operator/ChemGraph.java b/src/main/org/openscience/cdk/structgen/stochastic/operator/ChemGraph.java index 89ec75b7d68..c839bd21d76 100644 --- a/src/main/org/openscience/cdk/structgen/stochastic/operator/ChemGraph.java +++ b/src/main/org/openscience/cdk/structgen/stochastic/operator/ChemGraph.java @@ -47,7 +47,7 @@ public class ChemGraph /*Flag: true if atom visited during a traversal*/ protected boolean[] visited; /*Depth first traversal of the graph*/ - protected List subGraph; + protected List subGraph; public ChemGraph(IAtomContainer chrom) { @@ -57,12 +57,12 @@ public ChemGraph(IAtomContainer chrom) contab = ConnectionMatrix.getMatrix(chrom); } - public List pickDFgraph() + public List pickDFgraph() { //depth first search from a randomly selected atom travIndex = 0; - subGraph = new ArrayList(); + subGraph = new ArrayList(); visited = new boolean[dim]; for (int atom = 0; atom < dim; atom++) visited[atom] = false; int seedAtom = RandomNumbersTool.randomInt(0,dim-1); @@ -81,7 +81,7 @@ private void recursiveDFT(int atom) // for (int nextAtom = 0; nextAtom < dim; nextAtom++) //not generalized // if (contab[atom][nextAtom] != 0) recursiveDFT(nextAtom); - List adjSet = new ArrayList(); + List adjSet = new ArrayList(); for (int nextAtom = 0; nextAtom < dim; nextAtom++) { if ((int)contab[atom][nextAtom] != 0) @@ -99,17 +99,17 @@ private void recursiveDFT(int atom) } } - public List pickBFgraph() + public List pickBFgraph() { //breadth first search from a randomly selected atom travIndex = 0; - subGraph = new ArrayList(); + subGraph = new ArrayList(); visited = new boolean[dim]; for (int atom = 0; atom < dim; atom++) visited[atom] = false; int seedAtom = RandomNumbersTool.randomInt(0,dim-1); - List atomQueue = new ArrayList(); + List atomQueue = new ArrayList(); atomQueue.add(Integer.valueOf(seedAtom)); visited[seedAtom] = true; @@ -120,7 +120,7 @@ public List pickBFgraph() atomQueue.remove(0); travIndex++; - List adjSet = new ArrayList(); + List adjSet = new ArrayList(); for (int nextAtom = 0; nextAtom < dim; nextAtom++) { if (((int)contab[foreAtom][nextAtom] != 0)&&(!visited[nextAtom])) @@ -140,12 +140,12 @@ public List pickBFgraph() return subGraph; } - public List getSubgraph() + public List getSubgraph() { return subGraph; } - public void setSubgraph(List subgraph) + public void setSubgraph(List subgraph) { subGraph = subgraph; } diff --git a/src/main/org/openscience/cdk/structgen/stochastic/operator/CrossoverMachine.java b/src/main/org/openscience/cdk/structgen/stochastic/operator/CrossoverMachine.java index 654a27f5597..c28b7904542 100644 --- a/src/main/org/openscience/cdk/structgen/stochastic/operator/CrossoverMachine.java +++ b/src/main/org/openscience/cdk/structgen/stochastic/operator/CrossoverMachine.java @@ -21,14 +21,16 @@ package org.openscience.cdk.structgen.stochastic.operator; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import org.openscience.cdk.exception.CDKException; +import org.openscience.cdk.graph.ConnectivityChecker; +import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; -import org.openscience.cdk.interfaces.IBond; +import org.openscience.cdk.interfaces.IAtomContainerSet; import org.openscience.cdk.math.RandomNumbersTool; import org.openscience.cdk.structgen.stochastic.PartialFilledStructureMerger; +import org.openscience.cdk.tools.SaturationChecker; /** * Modified molecular structures by applying crossover operator on a pair of parent structures @@ -40,10 +42,6 @@ */ public class CrossoverMachine { - private IAtomContainer[] redChild,blueChild; - private List redAtoms,blueAtoms; - List children; - PartialFilledStructureMerger pfsm; /** selects a partitioning mode*/ @@ -62,141 +60,165 @@ public class CrossoverMachine */ public CrossoverMachine() { - redChild = new IAtomContainer[2]; - blueChild = new IAtomContainer[2]; - - redAtoms = new ArrayList(); - blueAtoms = new ArrayList(); - children = new ArrayList(2); - try - { - pfsm = new PartialFilledStructureMerger(); - } - catch(java.lang.Exception ex){ } + pfsm = new PartialFilledStructureMerger(); } /** - * Performs the n point crossover of two MolChromosome - * supplied by the CrossInfo.parents class and stores the resulting - * chromosomes in CrossInfo.children. + * Performs the n point crossover of two IAtomContainer. * - * @return an object storing information generated by the operator. - * @exception IllegalArgumentException if some of the crosspoints defined are - * greater than the size of the corresponding chromosome. + * @return The children. + * @exception CDKException if it was not possible to form offsprings. */ - public List doCrossover(IAtomContainer dad, IAtomContainer mom) throws CDKException + public List doCrossover(IAtomContainer dad, IAtomContainer mom) throws CDKException { - int dim = dad.getAtomCount(); - - /***randomly divide atoms into two parts: redAtoms and blueAtoms.***/ - redAtoms.clear(); - blueAtoms.clear(); - - if (splitMode==SPLIT_MODE_RADNDOM) - { - /*better way to randomly divide atoms into two parts: redAtoms and blueAtoms.*/ - for (int i = 0; i < dim; i++) - redAtoms.add(Integer.valueOf(i)); - for (int i = 0; i < (dim - numatoms); i++) - { int ranInt = RandomNumbersTool.randomInt(0,redAtoms.size()-1); - redAtoms.remove(ranInt); - blueAtoms.add(Integer.valueOf(ranInt)); - } - - } - else - { - /*split graph using depth/breadth first traverse*/ - ChemGraph graph = new ChemGraph(dad); - graph.setNumAtoms(numatoms); - if (splitMode==SPLIT_MODE_DEPTH_FIRST) + int tries=0; + while(true){ + int dim = dad.getAtomCount(); + IAtomContainer[] redChild = new IAtomContainer[2]; + IAtomContainer[] blueChild = new IAtomContainer[2]; + + List redAtoms = new ArrayList(); + List blueAtoms = new ArrayList(); + + /***randomly divide atoms into two parts: redAtoms and blueAtoms.***/ + if (splitMode==SPLIT_MODE_RADNDOM) { - redAtoms = graph.pickDFgraph(); + /*better way to randomly divide atoms into two parts: redAtoms and blueAtoms.*/ + for (int i = 0; i < dim; i++) + redAtoms.add(Integer.valueOf(i)); + for (int i = 0; i < (dim - numatoms); i++) + { int ranInt = RandomNumbersTool.randomInt(0,redAtoms.size()-1); + redAtoms.remove(Integer.valueOf(ranInt)); + blueAtoms.add(Integer.valueOf(ranInt)); + } + } else - redAtoms = graph.pickBFgraph(); - - for (int i = 0; i < dim; i++){ - Integer element = Integer.valueOf(i); - if (!(redAtoms.contains(element))) - { - blueAtoms.add(element); - } - } - } - /*** dividing over ***/ - - - redChild[0] = dad.getBuilder().newAtomContainer(dad); - blueChild[0] = dad.getBuilder().newAtomContainer(dad); - redChild[1] = dad.getBuilder().newAtomContainer(mom); - blueChild[1] = dad.getBuilder().newAtomContainer(mom); - - - for (int j = 0; j < blueAtoms.size(); j++) - { - Iterator bonds = redChild[1].bonds().iterator(); - while (bonds.hasNext()) { - IBond bond = (IBond)bonds.next(); - if (bond.contains(redChild[0].getAtom(((Integer)blueAtoms.get(j)).intValue()))) + { + /*split graph using depth/breadth first traverse*/ + ChemGraph graph = new ChemGraph(dad); + graph.setNumAtoms(numatoms); + if (splitMode==SPLIT_MODE_DEPTH_FIRST) { - redChild[0].removeBond(bond); + redAtoms = graph.pickDFgraph(); } - } - } - - - for (int j = 0; j < blueAtoms.size(); j++) - { - Iterator bonds = redChild[1].bonds().iterator(); - while (bonds.hasNext()) { - IBond bond = (IBond)bonds.next(); - if (bond.contains(redChild[1].getAtom(((Integer)blueAtoms.get(j)).intValue()))) + else { - bonds.remove(); // this removes it from redChild[1] too + //this is SPLIT_MODE_BREADTH_FIRST + redAtoms = graph.pickBFgraph(); + } + + for (int i = 0; i < dim; i++){ + Integer element = Integer.valueOf(i); + if (!(redAtoms.contains(element))) + { + blueAtoms.add(element); + } } + } + /*** dividing over ***/ + redChild[0] = dad.getBuilder().newAtomContainer(dad); + blueChild[0] = dad.getBuilder().newAtomContainer(dad); + redChild[1] = dad.getBuilder().newAtomContainer(mom); + blueChild[1] = dad.getBuilder().newAtomContainer(mom); + + List blueAtomsInRedChild0 = new ArrayList(); + for (int j = 0; j < blueAtoms.size(); j++) + { + blueAtomsInRedChild0.add(redChild[0].getAtom((Integer)blueAtoms.get(j))); } - } - - - for (int j = 0; j < redAtoms.size(); j++) - { - Iterator bonds = blueChild[0].bonds().iterator(); - while (bonds.hasNext()) { - IBond bond = (IBond)bonds.next(); - if (bond.contains(blueChild[0].getAtom(((Integer)redAtoms.get(j)).intValue()))) - { - bonds.remove(); + for (int j = 0; j < blueAtomsInRedChild0.size(); j++) + { + redChild[0].removeAtomAndConnectedElectronContainers(blueAtomsInRedChild0.get(j)); + } + List blueAtomsInRedChild1 = new ArrayList(); + for (int j = 0; j < blueAtoms.size(); j++) + { + blueAtomsInRedChild1.add(redChild[1].getAtom((Integer)blueAtoms.get(j))); + } + for (int j = 0; j < blueAtomsInRedChild1.size(); j++) + { + redChild[1].removeAtomAndConnectedElectronContainers(blueAtomsInRedChild1.get(j)); + } + List redAtomsInBlueChild0 = new ArrayList(); + for (int j = 0; j < redAtoms.size(); j++) + { + redAtomsInBlueChild0.add(blueChild[0].getAtom((Integer)redAtoms.get(j))); + } + for (int j = 0; j < redAtomsInBlueChild0.size(); j++) + { + blueChild[0].removeAtomAndConnectedElectronContainers(redAtomsInBlueChild0.get(j)); + } + List redAtomsInBlueChild1 = new ArrayList(); + for (int j = 0; j < redAtoms.size(); j++) + { + redAtomsInBlueChild1.add(blueChild[1].getAtom((Integer)redAtoms.get(j))); + } + for (int j = 0; j < redAtomsInBlueChild1.size(); j++) + { + blueChild[1].removeAtomAndConnectedElectronContainers(redAtomsInBlueChild1.get(j)); + } + //if the two fragments of one and only one parent have an uneven number + //of attachment points, we need to rearrange them + SaturationChecker satCheck = new SaturationChecker(); + double red1attachpoints=0; + for(int i=0;i children=new ArrayList(2); + for (int f = 0; f < 2; f++) { - bonds.remove(); + try{ + children.add(f, pfsm.generate(newstrucs[f]).getAtomContainer(0)); + //System.err.println("F "+writer.createSMILES(new Molecule(children.get(f))) ); + }catch(Exception ex){ + + } } + if(children.size()==2 && ConnectivityChecker.isConnected(children.get(0)) && ConnectivityChecker.isConnected(children.get(1))) + return children; } - } - - - redChild[0].add(blueChild[1]); - if (children.size()==2) - { - redChild[1].add(blueChild[0]); - } - - - - for (int f = 0; f < children.size(); f++) - { - pfsm.setAtomContainer(redChild[f]); - children.add(f, pfsm.generate()); - } - return children; + tries++; + if(tries>20) + throw new CDKException("Could not mate these properly"); + } } }