/* $Revision$ $Author$ $Date$ * * Copyright (C) 1997-2007 The Chemistry Development Kit (CDK) project * * Contact: cdk-devel@lists.sourceforge.net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * All we ask is that proper credit is given for our work, which includes * - but is not limited to - adding the above copyright notice to the beginning * of your source code files, and to any copyright notice that you may distribute * with programs based on this work. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.openscience.cdk.structgen; 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.IMolecule; import org.openscience.cdk.math.MathTools; import org.openscience.cdk.tools.ILoggingTool; import org.openscience.cdk.tools.LoggingToolFactory; import org.openscience.cdk.tools.manipulator.BondManipulator; /** * RandomGenerator is a generator of Constitutional Isomers. It needs to be * provided with a starting constitution and it makes random moves in * constitutional space from there. * This generator was first suggested by J.-L. Faulon {@cdk.cite FAU96}. * * <p>Unlike the VicinitySampler, this methods does not sample * the full Faulon vicinity. * * @see org.openscience.cdk.structgen.VicinitySampler * * @cdk.githash * @cdk.keyword structure generator */ public class RandomGenerator { ILoggingTool logger = LoggingToolFactory.createLoggingTool(RandomGenerator.class); private IMolecule proposedStructure = null; private IMolecule molecule = null; private IMolecule trial = null; /** * Constructs a RandomGenerator with a given starting structure. * * @param molecule The starting structure */ public RandomGenerator(IMolecule molecule) { setMolecule(molecule); } /** * Proposes a structure which can be accepted or rejected by an external * entity. If rejected, the structure is not used as a starting point * for the next random move in structure space. * * @return A proposed molecule */ public IMolecule proposeStructure() { logger.debug("RandomGenerator->proposeStructure() Start"); do { try { trial = (IMolecule)molecule.clone(); } catch (CloneNotSupportedException e) { logger.error("Could not clone IAtomContainer!" + e.getMessage()); trial = null; } mutate(trial); if(logger.isDebugEnabled()) { String s = "BondCounts: "; for (int f = 0; f < trial.getAtomCount(); f++) { s += trial.getConnectedBondsCount(trial.getAtom(f)) + " "; } logger.debug(s); s = "BondOrderSums: "; for (int f = 0; f < trial.getAtomCount(); f++) { s += trial.getBondOrderSum(trial.getAtom(f)) + " "; } logger.debug(s); } } while(trial == null || !ConnectivityChecker.isConnected(trial)); proposedStructure = trial; return proposedStructure; } /** * Tell the RandomGenerator to accept the last structure that had been proposed. */ public void acceptStructure() { if (proposedStructure != null) { molecule = proposedStructure; } } /** * Randomly chooses four atoms and alters the bonding * pattern between them according to rules described * in "Faulon, JCICS 1996, 36, 731". */ public void mutate(IAtomContainer ac) { logger.debug("RandomGenerator->mutate() Start"); int nrOfAtoms = ac.getAtomCount(); int x1 = 0, x2 = 0, y1 = 0, y2 = 0; double a11 = 0, a12 = 0, a22 = 0, a21 = 0; double b11 = 0, lowerborder = 0, upperborder = 0; IAtom ax1 = null, ax2 = null, ay1 = null, ay2 = null; IBond b1 = null, b2 = null, b3 = null, b4 = null; int[] choices = new int[3]; int choiceCounter = 0; /* We need at least two non-zero bonds in order to be successful */ int nonZeroBondsCounter = 0; do { do { nonZeroBondsCounter = 0; /* Randomly choose four distinct atoms */ do { // this yields numbers between 0 and (nrOfAtoms - 1) x1 = (int)(Math.random() * nrOfAtoms); x2 = (int)(Math.random() * nrOfAtoms); y1 = (int)(Math.random() * nrOfAtoms); y2 = (int)(Math.random() * nrOfAtoms); logger.debug("RandomGenerator->mutate(): x1, x2, y1, y2: " + x1 + ", " + x2 + ", " + y1 + ", " + y2); } while (!(x1 != x2 && x1 != y1 && x1 != y2 && x2 != y1 && x2 != y2 && y1 != y2)); ax1 = ac.getAtom(x1); ay1 = ac.getAtom(y1); ax2 = ac.getAtom(x2); ay2 = ac.getAtom(y2); /* Get four bonds for these four atoms */ b1 = ac.getBond(ax1, ay1); if (b1 != null) { a11 = BondManipulator.destroyBondOrder(b1.getOrder()); nonZeroBondsCounter ++; } else { a11 = 0; } b2 = ac.getBond(ax1, ay2); if (b2 != null) { a12 = BondManipulator.destroyBondOrder(b2.getOrder()); nonZeroBondsCounter ++; } else { a12 = 0; } b3 = ac.getBond(ax2, ay1); if (b3 != null) { a21 = BondManipulator.destroyBondOrder(b3.getOrder()); nonZeroBondsCounter ++; } else { a21 = 0; } b4 = ac.getBond(ax2, ay2); if (b4 != null) { a22 = BondManipulator.destroyBondOrder(b4.getOrder()); nonZeroBondsCounter ++; } else { a22 = 0; } logger.debug("RandomGenerator->mutate()->The old bond orders: a11, a12, a21, a22: " + + a11 + ", " + a12 + ", " + a21 + ", " + a22); }while(nonZeroBondsCounter < 2); /* Compute the range for b11 (see Faulons formulae for details) */ double[] cmax = {0, a11 - a22, a11 + a12 - 3, a11 + a21 - 3}; double[] cmin = {3, a11 + a12, a11 + a21, a11 - a22 + 3}; lowerborder = MathTools.max(cmax); upperborder = MathTools.min(cmin); /* Randomly choose b11 != a11 in the range max > r > min */ logger.debug("*** New Try ***"); logger.debug("a11 = ", a11); logger.debug("upperborder = ", upperborder); logger.debug("lowerborder = ", lowerborder); choiceCounter = 0; for (double f = lowerborder; f <= upperborder; f++) { if (f != a11) { choices[choiceCounter] = (int)f; choiceCounter ++; } } if (choiceCounter > 0) { b11 = choices[(int)(Math.random() * choiceCounter)]; } logger.debug("b11 = " + b11); } while (!(b11 != a11 && (b11 >= lowerborder && b11 <= upperborder))); double b12 = a11 + a12 - b11; double b21 = a11 + a21 - b11; double b22 = a22 - a11 + b11; if (b11 > 0) { if (b1 == null) { b1 = ac.getBuilder().newBond( ax1, ay1, BondManipulator.createBondOrder(b11) ); ac.addBond(b1); } else { b1.setOrder(BondManipulator.createBondOrder(b11)); } } else if (b1 != null) { ac.removeBond(b1); } if (b12 > 0) { if (b2 == null) { b2 = ac.getBuilder().newBond( ax1, ay2, BondManipulator.createBondOrder(b12) ); ac.addBond(b2); } else { b2.setOrder(BondManipulator.createBondOrder(b12)); } } else if (b2 != null) { ac.removeBond(b2); } if (b21 > 0) { if (b3 == null) { b3 = ac.getBuilder().newBond( ax2, ay1, BondManipulator.createBondOrder(b21) ); ac.addBond(b3); } else { b3.setOrder(BondManipulator.createBondOrder(b21)); } } else if (b3 != null) { ac.removeBond(b3); } if (b22 > 0) { if (b4 == null) { b4 = ac.getBuilder().newBond( ax2, ay2, BondManipulator.createBondOrder(b22) ); ac.addBond(b4); } else { b4.setOrder(BondManipulator.createBondOrder(b22)); } } else if (b4 != null) { ac.removeBond(b4); } logger.debug("a11 a12 a21 a22: " + a11 + " " + a12 + " " + a21 + " " + a22); logger.debug("b11 b12 b21 b22: " + b11 + " " + b12 + " " + b21 + " " + b22); } /** * Assigns a starting structure to this generator. * * @param molecule a starting structure for this generator */ public void setMolecule(IMolecule molecule) { this.molecule = molecule; } /** * Returns the molecule which reflects the current state of this * stochastic structure generator. * * @return The molecule */ public IMolecule getMolecule() { return this.molecule; } }