/* $Revision$ $Author$ $Date$ * * Copyright (C) 2000-2009 Christoph Steinbeck, Stefan Kuhn<shk3@users.sf.net> * * 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. * * 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.stochastic.operator; import java.util.ArrayList; 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.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 * and generate a pair of offspring structures. Each of the two offspring structures inherits * a certain fragments from both of its parents. * * @cdk.module structgen * @cdk.githash */ public class CrossoverMachine { PartialFilledStructureMerger pfsm; /** selects a partitioning mode*/ int splitMode = 2; /** selects a partitioning scale*/ int numatoms = 5; /** Indicates that <code>crossover</code> is using SPLIT_MODE_RADNDOM mode. */ public static final int SPLIT_MODE_RADNDOM = 0; /** Indicates that <code>crossover</code> is using SPLIT_MODE_DEPTH_FIRST mode. */ public static final int SPLIT_MODE_DEPTH_FIRST = 1; /** Indicates that <code>crossover</code> is using SPLIT_MODE_BREADTH_FIRST mode. */ public static final int SPLIT_MODE_BREADTH_FIRST = 2; /** * Constructs a new CrossoverMachine operator. */ public CrossoverMachine() { pfsm = new PartialFilledStructureMerger(); } /** * Performs the n point crossover of two {@link IAtomContainer}. * Precondition: The atoms in the molecules are ordered by properties to * preserve (e. g. atom symbol). Due to its randomized nature, this method * fails in around 3% of all cases. A CDKException with message "Could not * mate these properly" will then be thrown. * * @return The children. * @exception CDKException if it was not possible to form offsprings. */ public List<IAtomContainer> doCrossover(IAtomContainer dad, IAtomContainer mom) throws CDKException { int tries=0; while(true){ int dim = dad.getAtomCount(); IAtomContainer[] redChild = new IAtomContainer[2]; IAtomContainer[] blueChild = new IAtomContainer[2]; List<Integer> redAtoms = new ArrayList<Integer>(); List<Integer> blueAtoms = new ArrayList<Integer>(); /***randomly divide atoms into two parts: redAtoms and blueAtoms.***/ 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(Integer.valueOf(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) { redAtoms = graph.pickDFgraph(); } else { //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<IAtom> blueAtomsInRedChild0 = new ArrayList<IAtom>(); for (int j = 0; j < blueAtoms.size(); j++) { blueAtomsInRedChild0.add(redChild[0].getAtom((Integer)blueAtoms.get(j))); } for (int j = 0; j < blueAtomsInRedChild0.size(); j++) { redChild[0].removeAtomAndConnectedElectronContainers(blueAtomsInRedChild0.get(j)); } List<IAtom> blueAtomsInRedChild1 = new ArrayList<IAtom>(); 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<IAtom> redAtomsInBlueChild0 = new ArrayList<IAtom>(); 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<IAtom> redAtomsInBlueChild1 = new ArrayList<IAtom>(); 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<redChild[0].getAtomCount();i++){ red1attachpoints += satCheck.getCurrentMaxBondOrder(redChild[0].getAtom(i),redChild[0]); } double red2attachpoints=0; for(int i=0;i<redChild[1].getAtomCount();i++){ red2attachpoints += satCheck.getCurrentMaxBondOrder(redChild[1].getAtom(i),redChild[1]); } boolean isok=true; if(red1attachpoints % 2 ==1 ^ red2attachpoints % 2==1){ isok=false; IAtomContainer firstToBalance = redChild[1]; IAtomContainer secondToBalance = blueChild[0]; if(red1attachpoints % 2 == 1){ firstToBalance = redChild[0]; secondToBalance = blueChild[1]; } //we need an atom which has //- an uneven number of "attachment points" and //- an even number of outgoing bonds for(IAtom atom : firstToBalance.atoms()){ if(satCheck.getCurrentMaxBondOrder(atom,firstToBalance)%2==1 && firstToBalance.getBondOrderSum(atom)%2==0){ //we remove this from it's current container and add it to the other one firstToBalance.removeAtomAndConnectedElectronContainers(atom); secondToBalance.addAtom(atom); isok=true; break; } } } //if we have combineable fragments if(isok){ //combine the fragments crosswise IAtomContainerSet[] newstrucs = new IAtomContainerSet[2]; newstrucs[0] = dad.getBuilder().newAtomContainerSet(); newstrucs[0].add(ConnectivityChecker.partitionIntoMolecules(redChild[0])); newstrucs[0].add(ConnectivityChecker.partitionIntoMolecules(blueChild[1])); newstrucs[1] = dad.getBuilder().newAtomContainerSet(); newstrucs[1].add(ConnectivityChecker.partitionIntoMolecules(redChild[1])); newstrucs[1].add(ConnectivityChecker.partitionIntoMolecules(blueChild[0])); //and merge List<IAtomContainer> children=new ArrayList<IAtomContainer>(2); for (int f = 0; f < 2; f++) { try{ children.add(f, pfsm.generate(newstrucs[f])); }catch(Exception ex){ //if children are not correct, the outer loop will repeat, //so we ignore this } } if(children.size()==2 && ConnectivityChecker.isConnected(children.get(0)) && ConnectivityChecker.isConnected(children.get(1))) return children; } tries++; if(tries>20) throw new CDKException("Could not mate these properly"); } } }