All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.actelion.research.chem.Mutator Maven / Gradle / Ivy

There is a newer version: 2024.11.2
Show newest version
/*
 * Copyright (c) 1997 - 2016
 * Actelion Pharmaceuticals Ltd.
 * Gewerbestrasse 16
 * CH-4123 Allschwil, Switzerland
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the the copyright holder nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Thomas Sander
 */

package com.actelion.research.chem;

import com.actelion.research.chem.coords.CoordinateInventor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;

public class Mutator {
	public static final int MUTATION_GROW = Mutation.MUTATION_ADD_ATOM
										  | Mutation.MUTATION_INSERT_ATOM;
	public static final int MUTATION_SHRINK = Mutation.MUTATION_CUTOUT_ATOM
											| Mutation.MUTATION_DELETE_ATOM
											| Mutation.MUTATION_DELETE_SUBSTITUENT
											| Mutation.MUTATION_CUTOUT_SFRAGMENT;
	public static final int MUTATION_KEEP_SIZE = Mutation.MUTATION_CHANGE_ATOM
											   | Mutation.MUTATION_CLOSE_RING
											   | Mutation.MUTATION_CHANGE_BOND
											   | Mutation.MUTATION_DELETE_BOND
											   | Mutation.MUTATION_CHANGE_RING
											   | Mutation.MUTATION_CLOSE_RING_AND_AROMATIZE
											   | Mutation.MUTATION_TOGGLE_AMID_SULFONAMID
											   | Mutation.MUTATION_MIGRATE
											   | Mutation.MUTATION_SWAP_SUBSTITUENT
											   | Mutation.MUTATION_INVERT_PARITY;

	public static final int MUTATION_ANY = MUTATION_GROW
										 | MUTATION_SHRINK
										 | MUTATION_KEEP_SIZE;

	private static final int cMinRingClosureSize = 3;
	private static final int cMaxRingClosureSize = 7;

	private static final int cDefaultMinAtoms = 4;
	private static final int cDefaultOptAtoms = 9;
	private static final int cDefaultMaxAtoms = 24;
	private static final double cProbabilityFactor = 1.0f;	// larger/smaller values than 1.0 over/under-express natural probabilities

	// Increase back&forth probabilities to better represent natural equilibriums where mutation path to one of two states is unlikely
	private static final double BOOST_CHANGE_RING = 1.0;
	private static final double BOOST_TOGGLE_AMID_SULFONAMID = 10.f;

	private static final double BOOST_CLOSE_RING = 1.0f;

	private static final double cMinEductProbability = 0.00001f;

	private Random mRandom;

	private StereoMolecule	    mMolCopy 	= new StereoMolecule();
	private StereoMolecule	    mMol		= new StereoMolecule();
	private AtomTypeList		mAtomTypeList;
    private Canonizer           mCanonizer;
    private boolean[]           mIsPrimaryAtom;
	private int					mMinAtoms,mOptAtoms,mMaxAtoms;
	private double				mGrowBoost;
	private MutationBiasProvider mBiasProvider;

	/**
	 * Creates a new Mutator that calculates probabilities of individual mutations from
	 * a given atom type file. For every potential mutation the algorithm calculates
	 * a probability considering the frequencies of broken and formed atom types during the conversion.
* If no type list is given, all possible mutations are considered equally likely.
* New type files can be created from an sdf or dwar file like this:
* new AtomTypeList("/somepath/chembl14.dwar", AtomTypeCalculator.cPropertiesForMutator).writeTypeFile("/somepath/chembl14.typ");
* This would cause a file /somepath/chembl14.typ to be created with the statistics taken from chembl14.dwar. * @param filename null or name of a atomTypeList file ('*.typ') */ public Mutator(String filename) { if (filename != null) { try { mAtomTypeList = new AtomTypeList(filename, AtomTypeCalculator.cPropertiesForMutator); mAtomTypeList.calculateProbabilities(); } catch (Exception e) { e.printStackTrace(); } } mRandom = new Random(); mGrowBoost = 1.0; mMinAtoms = cDefaultMinAtoms; mOptAtoms = cDefaultOptAtoms; mMaxAtoms = cDefaultMaxAtoms; } /** * Creates a new Mutator that calculates probabilities of individual mutations from * a given atom type list. For every potential mutation the algorithm calculates * a probability considering the frequencies of broken and formed atom types during the conversion.
* If no type list is given, all possible mutations are considered equally likely.
* New type files can be created from an sdf or dwar file like this:
* new AtomTypeList().create("/somepath/chembl14.dwar", AtomTypeCalculator.cPropertiesForMutator);
* This would cause a file /somepath/chembl14.typ to be created with the statistics taken from chembl14.dwar. * Instantiating multiple Mutator objects from the same AtomTypeList is thread-safe. * @param atomTypeList null or name of a atomTypeList file ('*.typ') */ public Mutator(AtomTypeList atomTypeList) { mAtomTypeList = atomTypeList; mAtomTypeList.calculateProbabilities(); mRandom = new Random(); mGrowBoost = 1.0; mMinAtoms = cDefaultMinAtoms; mOptAtoms = cDefaultOptAtoms; mMaxAtoms = cDefaultMaxAtoms; } public void setBiasProvider(MutationBiasProvider mbp) { mBiasProvider = mbp; } public void setGrowBoost(double boost) { mGrowBoost = boost; } public void setPreferredSize(int minAtoms, int preferredAtoms, int maxAtoms) { mMinAtoms = minAtoms; mOptAtoms = preferredAtoms; mMaxAtoms = maxAtoms; } public StereoMolecule[] getMutatedSet(StereoMolecule mol, int mutationType, boolean regulateSize, int count) { ArrayList mutationList = generateMutationList(mol, mutationType, regulateSize); if (count > mutationList.size()) count = mutationList.size(); StereoMolecule[] set = new StereoMolecule[count]; for (int i=0; i mutate(StereoMolecule mol) { return mutate(mol, MUTATION_ANY, true); } /** * Does an in-place mutation of the molecule allowing the defined mutation kinds at any of the molecules unselected atoms. * @param mol * @param mutationType MUTATION_ANY, MUTATION_GROW, MUTATION_KEEP_SIZE, or MUTATION_SHRINK or other combination of allowed mutations types * @param regulateSize whether to regulate the molecule size within 4 to 24 non-H atoms or what was defined by setPreferredSize() * @return list of all not-used mutations or null if no mutation was done because no possible mutation was found */ public ArrayList mutate(StereoMolecule mol, int mutationType, boolean regulateSize) { ArrayList ml = generateMutationList(mol, mutationType, regulateSize); if (ml.size() == 0) { System.out.println("no possible mutation found. ID-Code:"+new Canonizer(mol, Canonizer.ENCODE_ATOM_SELECTION).getIDCode()); return null; } mutate(mol, ml); return ml; } /** * Selects a likely mutation from the list, performs the mutation and removes it from the list. * If the mutation list is empty, then no mutation is performed. * @param mol * @param mutationList */ public void mutate(StereoMolecule mol, ArrayList mutationList) { if (!mutationList.isEmpty()) { Mutation mutation = selectLikelyMutation(mutationList); if (mutation != null) { performMutation(mol, mutation); } } } /** * Creates a list of possible mutations and their probabilities * @param mol * @param mutationType MUTATION_ANY, MUTATION_GROW, MUTATION_KEEP_SIZE, or MUTATION_SHRINK or other combination of allowed mutations types * @param regulateSize if true keeps non-H atoms between 4 and 24 with an optimum at 9 or what was defined by setPreferredSize(). */ public ArrayList generateMutationList(StereoMolecule mol, int mutationType, boolean regulateSize) { mMol = mol; if (mBiasProvider != null) mBiasProvider.setBiasReference(mol); detectSymmetry(); ArrayList mutationList = new ArrayList(); ArrayList substituentList = createSubstituentList(); if (!regulateSize || (mRandom.nextDouble() > (float)(mMol.getAtoms() - mOptAtoms) / (float)(mMaxAtoms - mOptAtoms))) { if ((mutationType & Mutation.MUTATION_ADD_ATOM) != 0) addProbabilitiesForAddAtom(mutationList); if ((mutationType & Mutation.MUTATION_INSERT_ATOM) != 0) addProbabilitiesForInsertAtom(mutationList); } if ((mutationType & Mutation.MUTATION_CHANGE_ATOM) != 0) addProbabilitiesForChangeAtom(mutationList); if (!regulateSize || (mRandom.nextDouble() < (float)(mMol.getAtoms() - mMinAtoms) / (float)(mOptAtoms - mMinAtoms))) { if ((mutationType & Mutation.MUTATION_DELETE_ATOM) != 0) addProbabilitiesForDeleteAtom(mutationList); if ((mutationType & Mutation.MUTATION_CUTOUT_ATOM) != 0) addProbabilitiesForCutOutAtom(mutationList); if ((mutationType & Mutation.MUTATION_DELETE_SUBSTITUENT) != 0) addProbabilitiesForDeleteSubstituent(mutationList, substituentList); if ((mutationType & Mutation.MUTATION_CUTOUT_SFRAGMENT) != 0) addProbabilitiesForCutOutFragment(mutationList); } if ((mutationType & (Mutation.MUTATION_CLOSE_RING | Mutation.MUTATION_CLOSE_RING_AND_AROMATIZE)) != 0) addProbabilitiesForCloseRing(mutationList, mutationType); if ((mutationType & (Mutation.MUTATION_TOGGLE_AMID_SULFONAMID)) != 0) addProbabilitiesForToggleAmidSulfonamid(mutationList); if ((mutationType & Mutation.MUTATION_CHANGE_BOND) != 0) addProbabilitiesForChangeBond(mutationList); if ((mutationType & Mutation.MUTATION_DELETE_BOND) != 0) addProbabilitiesForDeleteBond(mutationList); if ((mutationType & Mutation.MUTATION_CHANGE_RING) != 0) addProbabilitiesForChangeRing(mutationList); if ((mutationType & Mutation.MUTATION_MIGRATE) != 0) addProbabilitiesForMigrate(mutationList); if ((mutationType & Mutation.MUTATION_SWAP_SUBSTITUENT) != 0) addProbabilitiesForSwapSubstituent(mutationList, substituentList); if ((mutationType & Mutation.MUTATION_INVERT_PARITY) != 0) addProbabilitiesForInvertParity(mutationList); if (cProbabilityFactor != 1) tweakProbabilities(mutationList); return mutationList; } private void tweakProbabilities(ArrayList mutationList) { for (Mutation mutation:mutationList) mutation.mProbability = Math.pow(mutation.mProbability, cProbabilityFactor); } private void detectSymmetry() { mCanonizer = new Canonizer(mMol, Canonizer.CREATE_SYMMETRY_RANK); mIsPrimaryAtom = new boolean[mMol.getAtoms()]; // here we count unselected atoms per rank int[] rankCount = new int[1+mMol.getAtoms()]; for (int atom=0; atom mutationList){ for (int atom=0; atom 3) ? 3 : freeValence; if( maxBondOrder > 0) { mMol.copyMolecule(mMolCopy); int newAtom = mMolCopy.addAtom(6); int newBond = mMolCopy.addBond(atom, newAtom, Molecule.cBondTypeSingle); //loop for single float or triple bond to add for (int bondOrder=1; bondOrder<=maxBondOrder; bondOrder++) { //check if the atom to add has enough bonds boolean allowed = false; for (int i=0; i 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_ADD_ATOM, atom, -1, atomicNo, bondType, p)); } } } } } } } private void addProbabilitiesForInsertAtom(ArrayList mutationList) { for (int bond=0; bond 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_INSERT_ATOM, bond, -1, atomicNo, -1, p)); } } } } } } } private void addProbabilitiesForChangeAtom(ArrayList mutationList) { for (int atom=0; atom 1 && (proposedAtomicNo == 9 || proposedAtomicNo == 17 || proposedAtomicNo == 35 || proposedAtomicNo == 53)) continue; if (valences > 2 && proposedAtomicNo == 8) continue; if (valences > 3 && proposedAtomicNo == 5) continue; if (valences > 4 && (proposedAtomicNo == 6 || proposedAtomicNo == 7)) continue; if (valences > 5 && proposedAtomicNo == 15) continue; mMol.copyMolecule(mMolCopy); mMolCopy.setAtomicNo(atom, proposedAtomicNo); mMolCopy.ensureHelperArrays(Molecule.cHelperRings); double f_Educt = Math.max(cMinEductProbability, getFrequency(mMol,atom)); double f_Product = getFrequency(mMolCopy,atom); for (int j=0; j 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_CHANGE_ATOM, atom, -1, proposedAtomicNo, -1, p)); } } } } } private void addProbabilitiesForDeleteAtom(ArrayList mutationList) { for (int atom=0; atom 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_DELETE_ATOM, atom, -1, -1, -1, p)); } } } } } } private void addProbabilitiesForCutOutAtom(ArrayList mutationList) { for (int atom=0; atom 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_CUTOUT_ATOM, atom, -1, -1, -1, p)); } } } }//end_deleting private void addProbabilitiesForToggleAmidSulfonamid(ArrayList mutationList) { int[] oxygen = new int[4]; for (int atom=0; atom 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_TOGGLE_AMID_SULFONAMID, atom, oxygen[1], -1, -1, p)); } } } } } private void addProbabilitiesForCloseRing(ArrayList mutationList, int mode) { for (int atom1=0; atom1= cMaxRingClosureSize) break; for (int i=0; i 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_CLOSE_RING_AND_AROMATIZE, atom1, atom2, ringSize, ringAtom, p)); } } } } } if ((mode & Mutation.MUTATION_CLOSE_RING) != 0 && order == 1) { mMol.copyMolecule(mMolCopy); mMolCopy.addBond(atom1, atom2, getBondTypeFromOrder(order)); double f_Educt1 = Math.max(cMinEductProbability, getFrequency(mMol, atom1)); double f_Educt2 = Math.max(cMinEductProbability, getFrequency(mMol, atom2)); double f_Product1 = getFrequency(mMolCopy, atom1); double f_Product2 = getFrequency(mMolCopy, atom2); double p = (mAtomTypeList == null) ? 1.0f : mAtomTypeList.getRingSizeAdjust(graphLevel[atom2]); p *= BOOST_CLOSE_RING * Math.sqrt(f_Product1 * f_Product2 / (f_Educt1 * f_Educt2)); if (p > 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_CLOSE_RING, atom1, atom2, getBondTypeFromOrder(order), -1, p)); } } } } } } } private void addProbabilitiesForChangeBond(ArrayList mutationList) { for (int bond=0; bond mMol.getFreeValence(atom2)) minFreeValence = mMol.getFreeValence(atom2); int maxBondOrder = mMol.getBondOrder(bond) + minFreeValence; if (maxBondOrder > 3) maxBondOrder = 3; if (mMol.isAromaticBond(bond)) { if (mMol.getBondOrder(bond) == 1) continue; maxBondOrder = 2; } if (mMol.isSmallRingBond(bond)) { if (mMol.getBondOrder(bond) == 1 && mMol.getAtomPi(atom1)+mMol.getAtomPi(atom2) != 0) maxBondOrder = 1; else if (maxBondOrder > 2) maxBondOrder = 2; } if (maxBondOrder == 2 && (mMol.getAtomicNo(atom1) < 5 || (mMol.getAtomicNo(atom1) > 8 && mMol.getAtomicNo(atom1) != 15 && mMol.getAtomicNo(atom1) != 16) || mMol.getAtomicNo(atom2) < 5 || (mMol.getAtomicNo(atom2) > 8 && mMol.getAtomicNo(atom2) != 15 && mMol.getAtomicNo(atom2) != 16))) maxBondOrder = 1; for (int bondOrder=1; bondOrder<=maxBondOrder; bondOrder++) { if (bondOrder == mMol.getBondOrder(bond)) continue; mMol.copyMolecule(mMolCopy); mMolCopy.setBondType(bond, getBondTypeFromOrder(bondOrder)); double f_Educt1 = Math.max(cMinEductProbability, getFrequency(mMol, atom1)); double f_Educt2 = Math.max(cMinEductProbability, getFrequency(mMol, atom2)); double f_Product1 = getFrequency(mMolCopy, atom1); double f_Product2 = getFrequency(mMolCopy, atom2); double p = Math.sqrt(f_Product1 * f_Product2 / (f_Educt1 * f_Educt2)); if (p > 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_CHANGE_BOND, bond, -1, getBondTypeFromOrder(bondOrder), -1, p)); } } } } } } } private void addProbabilitiesForDeleteBond(ArrayList mutationList) { for (int bond=0; bond 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_DELETE_BOND, bond, -1, -1, -1, p)); } } } } } } } private void addProbabilitiesForChangeRing(ArrayList mutationList) { mMol.ensureHelperArrays(Molecule.cHelperRings); RingCollection ringSet = mMol.getRingSet(); for (int ring=0; ring 0 && (ringSize == 6 || ringSet.isAromatic(ring))) break; if (ringSize == 5 && mMol.getAtomicNo(ringAtom[heteroPosition]) != 7 && mMol.getAtomicNo(ringAtom[heteroPosition]) != 8 && mMol.getAtomicNo(ringAtom[heteroPosition]) != 16) continue; mMol.copyMolecule(mMolCopy); mMolCopy.ensureHelperArrays(Molecule.cHelperRings); if (!changeAromaticity(mMolCopy, ring, heteroPosition)) continue; double f_Educt = 1.0; double f_Product = 1.0; for (int atom=0; atom 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_CHANGE_RING, ring, -1, heteroPosition, -1, p)); } } } } } } private void addProbabilitiesForMigrate(ArrayList mutationList) { for (int atom=0; atom 2) { for (int i=0; i 0) { mMol.copyMolecule(mMolCopy); for (int k=0; k<2; k++) if (mMolCopy.getBondAtom(k, migratingBond) == atom) mMolCopy.setBondAtom(k, migratingBond, destinationAtom); double f_Educt = Math.max(cMinEductProbability, getFrequency(mMol, atom)) * Math.max(cMinEductProbability, getFrequency(mMol, migratingAtom)) * Math.max(cMinEductProbability, getFrequency(mMol, destinationAtom)); double f_Product = getFrequency(mMolCopy, atom) * getFrequency(mMolCopy, migratingAtom) * getFrequency(mMolCopy, destinationAtom); double p = Math.sqrt(f_Product / f_Educt); // slight boost if (p > 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_MIGRATE, migratingBond, -1, atom, destinationAtom, p)); } } } } } } } } } private void addProbabilitiesForSwapSubstituent(ArrayList mutationList, ArrayList substituentList) { for (MutatorSubstituent s1:substituentList) { if (mIsPrimaryAtom[s1.firstAtom]) { for (MutatorSubstituent s2:substituentList) { if (mIsPrimaryAtom[s2.firstAtom] && s1.coreAtom != s2.coreAtom && s1.bond != s2.bond) { mMol.copyMolecule(mMolCopy); for (int k=0; k<2; k++) { if (mMolCopy.getBondAtom(k, s1.bond) == s1.firstAtom) mMolCopy.setBondAtom(k, s1.bond, s2.firstAtom); if (mMolCopy.getBondAtom(k, s2.bond) == s2.firstAtom) mMolCopy.setBondAtom(k, s2.bond, s1.firstAtom); } double f_Educt = Math.max(cMinEductProbability, getFrequency(mMol, s1.coreAtom)) * Math.max(cMinEductProbability, getFrequency(mMol, s1.firstAtom)) * Math.max(cMinEductProbability, getFrequency(mMol, s2.coreAtom)) * Math.max(cMinEductProbability, getFrequency(mMol, s2.firstAtom)); double f_Product = getFrequency(mMolCopy, s1.coreAtom) * getFrequency(mMolCopy, s1.firstAtom) * getFrequency(mMolCopy, s2.coreAtom) * getFrequency(mMolCopy, s2.firstAtom); double p = Math.sqrt(f_Product / f_Educt); // slight boost if (p > 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_SWAP_SUBSTITUENT, s1.coreAtom, s2.coreAtom, s1.firstAtom, s2.firstAtom, p)); } } } } } } private void addProbabilitiesForDeleteSubstituent(ArrayList mutationList, ArrayList substituentList) { for (MutatorSubstituent s:substituentList) { if (mIsPrimaryAtom[s.coreAtom]) { boolean[] isMemberAtom = new boolean[mMol.getAllAtoms()]; mMol.getSubstituent(s.coreAtom, s.firstAtom, isMemberAtom, null, null); boolean selectedAtomFound = false; for (int atom=0; atom 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_DELETE_SUBSTITUENT, s.coreAtom, -1, s.firstAtom, -1, p)); } } } } } private void addProbabilitiesForCutOutFragment(ArrayList mutationList) { for (int atom=0; atom 2) { int ringBondCount = 0; for (int i=0; i 0.0 && isValidStructure(mMolCopy)) { if (mBiasProvider != null) p *= mBiasProvider.getBiasFactor(mMolCopy); mutationList.add(new Mutation(Mutation.MUTATION_CUTOUT_SFRAGMENT, atom, -1, atom1, atom2, p)); } } } } } } } } } private void addProbabilitiesForInvertParity(ArrayList mutationList) { for (int atom=0; atom createSubstituentList() { ArrayList substituentList = new ArrayList(); boolean[] isCentralAtom = ScaffoldHelper.findMurckoScaffold(mMol); if (isCentralAtom != null) { for (int bond=0; bond mutationList) { double probabilitySum = 0.0f; for (Mutation m:mutationList) probabilitySum += m.mProbability; double selector = mRandom.nextDouble() * probabilitySum; probabilitySum = 0.0f; for (Mutation m:mutationList) { probabilitySum += m.mProbability; if (selector < probabilitySum) { mutationList.remove(m); return m; } } return null; } /** * Performs the given mutation on the molecule, updates atom coordinates, * and updates stereo bonds to reflect lost or new stereo centers. * @param mol * @param mutation */ public void performMutation(StereoMolecule mol, Mutation mutation) { mol.ensureHelperArrays(Molecule.cHelperParities); switch (mutation.mMutationType) { case Mutation.MUTATION_ADD_ATOM: int newAtom = mol.addAtom(mutation.mSpecifier1); mol.addBond(mutation.mWhere1, newAtom, mutation.mSpecifier2); break; case Mutation.MUTATION_INSERT_ATOM: int atom1 = mol.getBondAtom(0, mutation.mWhere1); int atom2 = mol.getBondAtom(1, mutation.mWhere1); mol.deleteBond(mutation.mWhere1); newAtom = mol.addAtom(mutation.mSpecifier1); mol.addBond(atom1, newAtom, Molecule.cBondTypeSingle); mol.addBond(atom2, newAtom, Molecule.cBondTypeSingle); break; case Mutation.MUTATION_CHANGE_ATOM: mol.setAtomicNo(mutation.mWhere1, mutation.mSpecifier1); break; case Mutation.MUTATION_DELETE_ATOM: mol.deleteAtom(mutation.mWhere1); break; case Mutation.MUTATION_CUTOUT_ATOM: atom1 = mol.getConnAtom(mutation.mWhere1, 0); atom2 = mol.getConnAtom(mutation.mWhere1, 1); mol.addBond(atom1, atom2, Molecule.cBondTypeSingle); mol.deleteAtom(mutation.mWhere1); break; case Mutation.MUTATION_CLOSE_RING: mol.addBond(mutation.mWhere1, mutation.mWhere2, mutation.mSpecifier1); break; case Mutation.MUTATION_CLOSE_RING_AND_AROMATIZE: mol.addBond(mutation.mWhere1, mutation.mWhere2, mutation.mSpecifier1); aromatizeRing(mol, mutation.mAtomList, mutation.mSpecifier1); break; case Mutation.MUTATION_TOGGLE_AMID_SULFONAMID: if (mol.getAtomicNo(mutation.mWhere1) == 6) { mol.setAtomicNo(mutation.mWhere1, 16); mol.addBond(mutation.mWhere1, mol.addAtom(8), Molecule.cBondTypeDouble); } else { mol.setAtomicNo(mutation.mWhere1, 6); mol.deleteAtom(mutation.mWhere2); } break; case Mutation.MUTATION_CHANGE_BOND: mol.setBondType(mutation.mWhere1, mutation.mSpecifier1); break; case Mutation.MUTATION_DELETE_BOND: mol.deleteBond(mutation.mWhere1); break; case Mutation.MUTATION_CHANGE_RING: changeAromaticity(mol, mutation.mWhere1, mutation.mSpecifier1); break; case Mutation.MUTATION_MIGRATE: for (int i=0; i<2; i++) if (mol.getBondAtom(i, mutation.mWhere1) == mutation.mSpecifier1) mol.setBondAtom(i, mutation.mWhere1, mutation.mSpecifier2); break; case Mutation.MUTATION_DELETE_SUBSTITUENT: boolean[] atomMask = new boolean[mol.getAtoms()]; mol.getSubstituent(mutation.mWhere1, mutation.mSpecifier1, atomMask, null, null); mol.deleteAtoms(atomMask); break; case Mutation.MUTATION_CUTOUT_SFRAGMENT: int rootAtom = mutation.mWhere1; atom1 = mutation.mSpecifier1; atom2 = mutation.mSpecifier2; int bond1 = -1; int bond2 = -1; for (int i=0; i 0 && ((mMol.isAromaticAtom(firstMaxConsecutiveRingAtom) || mMol.isAromaticAtom(lastMaxConsecutiveRingAtom)) || (mMol.getAtomPi(firstMaxConsecutiveRingAtom) > 0 && mMol.getAtomPi(lastMaxConsecutiveRingAtom) > 0))) penalty++; if (ringSize - penalty < 3) return false; return true; } /** * Runs a rule based check for bicyclic systems with unacceptable strain. * Checks any bridge between two different atoms of one ring * connected to different atoms of one ring. * @param mol * @return */ private boolean isValidStructure(StereoMolecule mol) { // The largest bridge length (bonds) between two direct ring neighbours // that would still cause an unacceptable ring strain. // (this is the smallest allowed chain (bond count) from ring atom to ring atom minus 3!!!) // First index is ring size, 2nd index is number of bonds between attachment points // We distinguish 3 types of rings: // - aromatic or both attachment points sp2 // - one attachment point sp2 // - both attachment points sp3 final int[][] MAX_FORBIDDEN_BRIDGE_LENGTH_AROM = { null, null, null, { -1, 2 }, { -1, 2, 6 }, { -1, 1, 5 }, { -1, 1, 4, 6 }, { -1, 1, 4, 6 } }; final int[][] MAX_FORBIDDEN_BRIDGE_LENGTH_PI = { null, null, null, { -1, 1 }, { -1, 1, 5 }, { -1, 1, 4 }, { -1, 0, 3, 4 }, { -1, 0, 2, 3 } }; final int[][] MAX_FORBIDDEN_BRIDGE_LENGTH_ALIPH = { null, null, null, { -1, 0 }, { -1, -1, 3 }, { -1, -1, 0 }, { -1, -1, 0, -1 }, { -1, -1, 0, -1 } }; mol.ensureHelperArrays(Molecule.cHelperRings); RingCollection ringSet = mol.getRingSet(); boolean[] neglectAtom = new boolean[mol.getAtoms()]; for (int ring=0; ring 2) { for (int ci=0; ci 2) { for (int cj=0; cj 1)) return true; } } return false; } private boolean changeAromaticity(StereoMolecule mol, int ring, int heteroPosition) { mol.ensureHelperArrays(Molecule.cHelperRings); RingCollection ringSet = mol.getRingSet(); int ringSize = ringSet.getRingSize(ring); int ringAtom[] = ringSet.getRingAtoms(ring); int ringBond[] = ringSet.getRingBonds(ring); if (ringSet.isAromatic(ring)) { for (int i=0; i 4) doubleBondPosition1 -= 5; if (doubleBondPosition2 > 4) doubleBondPosition2 -= 5; mol.setBondType(doubleBondPosition1, Molecule.cBondTypeDouble); mol.setBondType(doubleBondPosition2, Molecule.cBondTypeDouble); return true; } if (ringSize == 6) { for (int i=0; i= ringSize) doubleBondIndex -= ringSize; mol.setBondType(ringBond[doubleBondIndex], Molecule.cBondTypeDouble); doubleBondIndex += 2; } return true; } public void printMutationList(ArrayList mutationList, boolean sortByPriority) { Mutation[] mutation = mutationList.toArray(new Mutation[0]); if (sortByPriority) Arrays.sort(mutation, new Comparator() { @Override public int compare(Mutation m1, Mutation m2) { return m1.mProbability > m2.mProbability ? -1 : m1.mProbability < m2.mProbability ? 1 : 0; } } ); System.out.println("Mutation list ("+mutationList.size()+" mutations):"); for (Mutation m:mutation) System.out.println(m.toString()); } class MutatorSubstituent { public int coreAtom; public int firstAtom; public int bond; public int[] atoms; // public int size; public MutatorSubstituent(int coreAtom, int firstAtom, int bond) { this.coreAtom = coreAtom; this.firstAtom = firstAtom; this.bond = bond; boolean[] isMember = new boolean[mMol.getAllAtoms()]; int count = mMol.getSubstituent(coreAtom, firstAtom, isMember, null, null); atoms = new int[count]; for (int atom=0, i=0; atom