com.actelion.research.chem.Mutator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openchemlib Show documentation
Show all versions of openchemlib Show documentation
Open Source Chemistry Library
/*
* 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