def RemoveBond(mol, bond): if bond.HasProp('group'): raise MutateFail(mol, 'Protected bond (in group) passed to RemoveBond.') if not bond.IsInRing(): raise MutateFail(mol, 'Non-ring bond passed to RemoveBond') mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) return mol
def MakeMutations(candidate): ''' The mutations, based on not-aromatic SMILES''' # 1. Kekulize: try: Chem.Kekulize(candidate, True) except ValueError: print "MakeMutation. Kekulize Error:", Chem.MolToSmiles(candidate) raise MutateFail(candidate) # 2. Mutate candidate = SingleMutate(candidate) # 3. SetAromaticity again try: if aromaticity: Chem.SetAromaticity(candidate) candidate = Finalize(candidate, tautomerize=False) except ValueError: print "MakeMutation. SetAromaticity Error:", Chem.MolToSmiles( candidate) raise MutateFail(candidate) return candidate
def AddAroRing(mol): freesinglebonds = GetFreeBonds(mol, order=1, sides=True) freedoublebonds = GetFreeBonds(mol, order=2, notprop='group') triplebonds = filter(lambda bond: bond.GetBondType() == bondorder[3], mol.GetBonds()) correctbonds = freedoublebonds + triplebonds + freesinglebonds if len(correctbonds) < 1: raise MutateFail() bond = random.choice(correctbonds) # if double bond if all(atom.GetImplicitValence() for atom in (bond.GetBeginAtom(), bond.GetEndAtom())): pass elif bond.GetBondType() == bondorder[3]: bond.SetBondType(bondorder[2]) else: print "wrong kind of atom given" raise MutateFail() def AwithLabel(label, idx=True): atom = filter(lambda atom: atom.HasProp(label), mol.GetAtoms())[0] if idx: return atom.GetIdx() else: return atom butadiene = Chem.MolFromSmiles('C=CC=C') butadiene.GetAtomWithIdx(0).SetBoolProp('buta1', True) butadiene.GetAtomWithIdx(3).SetBoolProp('buta2', True) bond.GetBeginAtom().SetBoolProp('ah1', True) bond.GetEndAtom().SetBoolProp('ah2', True) mol = Chem.RWMol(Chem.CombineMols(mol, butadiene)) try: mol.AddBond(AwithLabel('buta1'), AwithLabel('ah1'), bondorder[1]) mol.AddBond(AwithLabel('buta2'), AwithLabel('ah2'), bondorder[1]) except RuntimeError: raise MutateFail() finally: for prop in ['buta1', 'buta2', 'ah1', 'ah2']: AwithLabel(prop, idx=False).ClearProp(prop) return mol
def AddBond(mol): # NB! This function was overloaded and atoms where possible arguments # Not anymore possible to get a cleaner interface natoms = mol.GetNumAtoms() if natoms < 5: raise MutateFail() #don't create 4-member rings #Don't create non-planar graphs (note that this does not prevent #all non-planar graphs, but it does prevent some) if natoms >= 3 and mol.GetNumBonds() >= 3 * natoms - 6: raise MutateFail() # List of atoms that can form new bonds atoms = [ atom for atom in GetAtoms(mol, notprop='protected') if EmptyValence(atom) > 0 ] if len(atoms) < 2: raise MutateFail() for i in range(MAXTRY): atom1 = random.choice(atoms) atom2 = random.choice(atoms) if atom1 == atom2: continue # new rather strict criteria; if atom1.IsInRing() and atom2.IsInRing(): #print "continue because both atoms in ring" continue # Only make rings of size 5-7 if not (5 <= len( Chem.GetShortestPath(mol, atom1.GetIdx(), atom2.GetIdx())) <= 7): continue nfreebonds1 = EmptyValence(atom1) nfreebonds2 = EmptyValence(atom2) if nfreebonds2 < nfreebonds1: nfreebonds1 = nfreebonds2 assert nfreebonds1 > 0 order = random.randrange(1, nfreebonds1 + 1) mol.AddBond(atom1.GetIdx(), atom2.GetIdx(), bondorder[order]) return mol # if here, mutation failed raise MutateFail()
def GetFragment(mol): #print "in get fragment with:", Chem.MolToSmiles(mol) tried = [] for itry in xrange(MAXTRY): frag = Chem.RWMol(mol) #frag.ClearComputedProps() ResetProps(frag) #Cut an acyclic bond to break molecule in 2 # get the ring bond ids. this gives a tuple of tuple for every ring ringbondids = frag.GetRingInfo().BondRings() # flatten this tuple to a 1D set ringbondids = list( set([bond for subset in ringbondids for bond in subset])) # get all the bonds that are NOT ringbonds or already tried bonds = [ bond for bond in mol.GetBonds() if bond.GetIdx() not in tried + ringbondids ] if len(bonds) == 0: break if debug: print "CX0", # choose a bond delbond = random.choice(bonds) tried.append(delbond.GetIdx()) # get atom indices of the bond i, j = (delbond.GetBeginAtomIdx(), delbond.GetEndAtomIdx()) frag.RemoveBond(i, j) # get new fragments as two new normal Mol objects try: frags = Chem.GetMolFrags(frag, asMols=True) except ValueError: print "frag:", Chem.MolToSmiles(frag) print "mol:", Chem.MolToSmiles(mol) break molfrags = [Chem.MolToSmiles(x, True) for x in frags] if len(frags) < 2: #print "only one frag" continue #Check to make sure fragments have at least 2 atoms if any(frag.GetNumAtoms() < 2 for frag in frags): #print "not enough atoms on frag" continue #print molfrags # convert to RWMols? frags = map(Chem.RWMol, frags) return frags # If here, we failed to find a good bond to cut #print "no nice bond to cut" raise MutateFail()
def FlipBond(mol, bond): if bond.HasProp('group'): raise MutateFail(mol, 'In-group bond passed to FlipBond') oldorder = int(bond.GetBondTypeAsDouble()) atom1 = bond.GetBeginAtom() atom2 = bond.GetEndAtom() full = False if EmptyValence(atom1) == 0: full = True if EmptyValence(atom2) == 0: full = True if oldorder == 1 and full: raise MutateFail() elif oldorder == 1: add = 1 elif oldorder == 3: add = -1 elif oldorder == 2 and full: add = -1 else: add = random.choice((-1, 1)) bond.SetBondType(bondorder[oldorder + add]) return mol
def FlipBond(mol): bonds = list(GetBonds(mol, notprop='group')) if len(bonds) == 0: raise MutateFail() bond = random.choice(bonds) oldorder = int(bond.GetBondTypeAsDouble()) atom1 = bond.GetBeginAtom() atom2 = bond.GetEndAtom() full = False if EmptyValence(atom1) == 0: full = True if EmptyValence(atom2) == 0: full = True if oldorder == 1 and full: raise MutateFail() elif oldorder == 1: add = 1 elif oldorder == 3: add = -1 elif oldorder == 2 and full: add = -1 else: add = random.choice((-1, 1)) bond.SetBondType(bondorder[oldorder + add]) return mol
def FlipAtom(mol): atoms = filter(CanChangeAtom, mol.GetAtoms()) if len(atoms) == 0: raise MutateFail() atom = random.choice(atoms) changed = False neighbors = list(atom.GetNeighbors()) valence = atom.GetExplicitValence() + atom.GetNumRadicalElectrons() # Add a halogen, if possible # we only add halogens when neighbor and neighbor-neighbor only C if valence == 1: if (len(halogens) > 0 and random.random() > 0.6 and neighbors[0].GetAtomicNum() == 6): CanAddHalogen = True nextneighbors = neighbors[0].GetNeighbors() for nb in nextneighbors: if (nb.GetAtomicNum() != 6 and nb.GetIdx() != atom.GetIdx()): CanAddHalogen = False break if CanAddHalogen: atom.SetAtomicNum(random.choice(halogens)) return mol # # # # # # # # # # Regular atom switching atnum = atom.GetAtomicNum() elems = [el for el in elements if el != atnum] for itry in range(50): cand = random.choice(elems) if MaxValence[cand] >= valence: atom.SetAtomicNum(cand) return mol if not changed: raise MutateFail() # if here, mutation failed ... return mol
def AddAtom(mol): # 5. add an atom atoms = GetAtoms(mol, notprop='protected') bonds = GetBonds(mol, notprop='group') if len(atoms) < 1: raise MutateFail() obj = random.choice(atoms + bonds) # If passed a bond, insert an atom into it if type(obj) == Chem.Bond: if obj.HasProp('group'): raise MutateFail(mol, 'Grouped bond passed to AddAtom') # note that we really nead the atoms here because the Idx may change # after adding a new atom to the molecule atom1 = obj.GetBeginAtom() atom2 = obj.GetEndAtom() mol.RemoveBond(atom1.GetIdx(), atom2.GetIdx()) newatomid = mol.AddAtom(Chem.Atom(6)) mol.AddBond(atom1.GetIdx(), newatomid, bondorder[1]) mol.AddBond(atom2.GetIdx(), newatomid, bondorder[1]) #Otherwise, make a new sidechain (or add a terminal atom) elif type(obj) == Chem.Atom and EmptyValence(obj) > 0: if obj.HasProp('protected'): raise MutateFatal(mol, 'Trying to add atom to protected atom.') # choose an atom to add atnum = obj.GetAtomicNum() elems = [el for el in elements if el != atnum] if atnum == 6: # only add halogens to carbons elems.extend(halogens) cand = random.choice(elems) # and add it newatomid = mol.AddAtom(Chem.Atom(cand)) mol.AddBond(obj.GetIdx(), newatomid, bondorder[1]) else: raise MutateFail() return mol
def AddFusionRing(mol): try: p = Chem.MolFromSmarts('[h]@&=*(@*)@[h]') matches = mol.GetSubstructMatches(p) except RuntimeError: raise MutateFail() if len(matches) == 0: raise MutateFail() match = random.choice(matches) if mol.GetAtomWithIdx(match[-1]).GetNumRadicalElectrons(): raise MutateFail() def AwithLabel(label, idx=True): atom = filter(lambda atom: atom.HasProp(label), mol.GetAtoms())[0] if idx: return atom.GetIdx() else: return atom propene = Chem.MolFromSmiles('C=CC') propene.GetAtomWithIdx(0).SetBoolProp('propane1', True) propene.GetAtomWithIdx(2).SetBoolProp('propane2', True) mol.GetAtomWithIdx(match[3]).SetBoolProp('ah1', True) mol.GetAtomWithIdx(match[0]).SetBoolProp('ah2', True) mol = Chem.RWMol(Chem.CombineMols(mol, propene)) try: mol.AddBond(AwithLabel('propane1'), AwithLabel('ah1'), bondorder[1]) mol.AddBond(AwithLabel('propane2'), AwithLabel('ah2'), bondorder[1]) except RuntimeError: raise MutateFail() finally: for prop in ['propane1', 'propane2', 'ah1', 'ah2']: AwithLabel(prop, idx=False).ClearProp(prop) return mol
def AddAtom(mol, obj): # If passed a bond, insert an atom into it if type(obj) == Chem.Bond: if obj.HasProp('group'): raise MutateFail(mol, 'Grouped bond passed to AddAtom') # note that we really nead the atoms here because the Idx may change # after adding a new atom to the molecule atom1 = obj.GetBeginAtom() atom2 = obj.GetEndAtom() mol.RemoveBond(atom1.GetIdx(), atom2.GetIdx()) newatomid = mol.AddAtom(Chem.Atom(6)) mol.AddBond(atom1.GetIdx(), newatomid, bondorder[1]) mol.AddBond(atom2.GetIdx(), newatomid, bondorder[1]) #Otherwise, make a new sidechain (or add a terminal atom) elif type(obj) == Chem.Atom and EmptyValence(obj) > 0: if obj.HasProp('protected'): raise MutateFatal(mol, 'Trying to add atom to protected atom.') newatomid = mol.AddAtom(Chem.Atom(6)) mol.AddBond(obj.GetIdx(), newatomid, bondorder[1]) else: raise MutateFail() return mol
def __call__(self, mol): Chem.Kekulize(mol, True) try: mol = self.mutator(mol) except MutateFail as e: raise except RuntimeError as e: print "RuntimeError in Mutator:", self.mutator, self.name print "repr(e):", repr(e) raise MutateFail() except Exception as e: if 'MutateFail' in repr(e): raise MutateFail() else: raise if mol is None: raise MutateFail() mol = Finalize(mol, tautomerize=False, aromatic=False) if type(mol) == Chem.Mol: mol = Chem.RWMol(mol) return mol
def DelBond(mol): bondringids = mol.GetRingInfo().BondRings() # need to flatten bondringids: if len(bondringids) > 0: # fancy manner to flatten a list of tuples with possible duplicates #bondringids = set.intersection(*map(set, bondringids)) bondringids = set.union(*map(set, bondringids)) bonds = GetIBonds(bondringids, mol, notprop='group') else: raise MutateFail() try: bond = random.choice(bonds) except IndexError: print bondringids mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) #print "succesful delbond:", Chem.MolToSmiles(mol) return mol
def Finalize(mol, tautomerize=None, aromatic=aromaticity): ''' This function makes sure that a new mutated/crossover/fixfilter molecule: - corrects the implicit valence - removes data from parent molecules. - optionally converts the molecule to its canonical tautomer - checks if it is a valid molecule, i.e. Sanitize step. (without aromaticity) ''' # 1. try: mol.UpdatePropertyCache() except ValueError: raise MutateFail(mol) # 2. RW = type(mol) == Chem.RWMol # 3. ResetProps(mol) # 4. if canonicalTautomer and not tautomerize is False: mol = Tautomerize(mol, aromatic) # 5. try: Sanitize(mol, aromatic) except Exception as e: print "Error in Finalize with", Chem.MolToSmiles(mol, False), if verbose: print e, if debug: for item in traceback.extract_stack(): print item # 6. if aromatic: Chem.SetAromaticity(mol) # 7. if RW and not type(mol) == Chem.RWMol: return Chem.RWMol(mol) else: return mol
def Tautomerize(mol): try: if mol.GetBoolProp('tautomerized'): return except KeyError: pass smi1 = Chem.MolToSmiles(mol) from molvs import Standardizer s = Standardizer() try: s.standardize(mol) except ValueError as e: MutateFail(mol) return False #from molvs.tautomer import TautomerCanonicalizer #t = TautomerCanonicalizer() #t.canonicalize(mol) mol.SetBoolProp('tautomerized', True) smi2 = Chem.MolToSmiles(mol) if not smi1 == smi2: print "tautomerized:", smi1, 'to:', smi2 return True
def SingleMutate(candidateraw): if candidateraw is None: raise ValueError('candidate is none') else: candidate = Chem.RWMol(candidateraw) global stats parent = candidate.GetProp('isosmi') ResetProps(candidate) change = False candidate.SetProp('parent', parent) for mutationtype, mutator in mutators.items(): if random.random() < mutator.p: #print "in mutate:", mutationtype, Chem.MolToSmiles(candidate), change = True stats[mutator.nname] += 1 try: candidate = mutator(candidate) # I don't understand why these errors are not similar?! but this works except MutateFail as e: stats[mutator.nnameFail] += 1 except Exception as e: if 'MutateFail' in repr(e): #print "exotic MutateFail" stats[mutator.nnameFail] += 1 else: print "Exception is not MutateFail!" print e, repr(e), type(e) raise # should we allow multiple mutation at the same time: # if not: # break if not change: stats['nNoMutation'] += 1 raise MutateFail() return candidate
def Tautomerize(mol, aromatic=aromaticity): try: if mol.GetBoolProp('tautomerized'): return mol except KeyError: pass Chem.SanitizeMol(mol) if not (aromatic or aromaticity): Chem.Kekulize(mol, True) smi1 = Chem.MolToSmiles(mol) from molvs import Standardizer s = Standardizer() try: molnew = s.standardize(mol) except ValueError as e: raise MutateFail(mol) if not aromatic: Chem.Kekulize(molnew, True) smi2 = Chem.MolToSmiles(molnew) if smi1 == smi2: # we return mol because it contains some properties # tautomerized mols need to get the props again mol.SetBoolProp('tautomerized', True) return mol else: if mol.HasProp('failedfilter'): ff = mol.GetProp('failedfilter') molnew.SetProp('failedfilter', ff) #print "tautomerized:", smi1, 'to:', smi2 with open('tautomerized.smi', 'a') as f: f.write("{} {}\n".format(smi1, smi2)) molnew.SetBoolProp('tautomerized', True) return molnew
def SwitchAtom(mol, atom): if atom.HasProp('group') and not atom.HasProp('grouprep'): raise MutateFail(mol, 'protected or grouped atom passed to switchatom') changed = False neighbors=list(atom.GetNeighbors()) # # # # # # # # # # # # # # from Filters import OptSulfone # Special rules for sulfurs that can optionally be sulfones #if atom.GetAtomicNum()==16 and len(OptSulfone)>0: # SulfCand=set() # for group in OptSulfone: # for match in mol.GetSubstructMatches(group, True): # for mat in GetIAtoms(match, mol): # if mat.target.IsSulfur(): SulfCand.add(mat.target) # if atom in SulfCand and random.random()>0.4: # if atom.HasProp('grouprep'): # RemoveGroup(mol,atom.GetProp('group')) # return # elif not atom.HasProp('group'): # MakeSulfone(mol,atom) # return #If it's a group representative, switching it will delete the #rest of the group; since they're hard to create, they're also #hard to destroy #if atom.HasProp('grouprep': # if random.random()>0.3: # RemoveGroup(mol,atom.GetProp('group')) # changed=True # else: # raise MutateFail() # # # # # # # # # # # # # # #Special cases for Nitro groups and halogens, #which can only be on aromatic rings #if ( atom.GetExplicitValence()==1 # and neighbors[0].IsAromatic() ): # Add a halogen, if possible # we only add halogens when neighbor and neighbor-neighbor only C if atom.GetExplicitValence()==1: if (len(halogens)>0 and random.random()>0.6 and neighbors[0].GetAtomicNum()==6): CanAddHalogen=True nextneighbors=neighbors[0].GetNeighbors() for nb in nextneighbors: if ( nb.GetAtomicNum() != 6 and nb.GetIdx() != atom.GetIdx()): CanAddHalogen=False break if CanAddHalogen: atom.SetAtomicNum( random.choice(halogens)) return # If not a halogen, maybe make a nitro group # if random.random() < 0.15: # MakeNitro(mol,atom) # return # # # # # # # # # # Regular atom switching atnum = atom.GetAtomicNum() elems = [el for el in elements if el != atnum] for itry in range(50): cand = random.choice(elems) if MaxValence[cand] >= atom.GetExplicitValence(): atom.SetAtomicNum(cand) return if not changed: raise MutateFail() # if here, mutation failed ... return mol
def RemoveAtom(mol, atom): # # Deal with special groups # if atom.HasProp('protected') or atom.HasProp('fixed'): raise MutateFatal(mol, 'Protected or fixed atom passed to' + " RemoveAtom.") Degree = atom.GetDegree() if debug: print "D{:d}".format(Degree) # If atom is the representative of a larger group (e.g. N in nitro group) # delete the entire group if atom.HasProp('grouprep'): groupnum = atom.GetProp('group') RemoveGroup(mol, groupnum) #If there's only one atom, refuse to remove it if len(atom.GetNeighbors()) == 0: raise MutateFail(mol) #Remove a terminal atom: elif Degree == 1: for bond in atom.GetBonds(): mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) mol.RemoveAtom(atom.GetIdx()) # until here it seems fine #Remove an in-chain atom: # #NOT WORKING WELL elif Degree == 2: #print Chem.MolToSmiles(mol, True) nbr = [] for neighb in atom.GetNeighbors(): nbr.append(neighb) for bond in atom.GetBonds(): mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) mol.RemoveAtom(atom.GetIdx()) # this line give indexerrors: mol.AddBond(nbr[0].GetIdx(), nbr[1].GetIdx(), bondorder[1]) elif True: pass #Remove a 3-bond atom: elif Degree == 3: nbr = [] nbrCarbon = [] nChoice = 3 for neighb in atom.GetNeighbors(): nbr.append(neighb) if neighb.GetAtomicNum()==6 and \ len(neighb.GetNeighbors())==3: nChoice = nChoice + 1 nbrCarbon.append(neighb) choose = random.randrange(0, nChoice, 1) for bond in atom.GetBonds(): mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetBeginAtomIdx()) mol.RemoveAtom(atom.GetIdx()) if choose == 0: mol.AddBond(nbr[0].GetIdx(), nbr[1].GetIdx(), bondorder[1]) mol.AddBond(nbr[1].GetIdx(), nbr[2].GetIdx(), bondorder[1]) elif choose == 1: mol.AddBond(nbr[0].GetIdx(), nbr[2].GetIdx(), bondorder[1]) mol.AddBond(nbr[2].GetIdx(), nbr[1].GetIdx(), bondorder[1]) elif choose == 2: mol.AddBond(nbr[0].GetIdx(), nbr[1].GetIdx(), bondorder[1]) mol.AddBond(nbr[2].GetIdx(), nbr[0].GetIdx(), bondorder[1]) else: for neighb in nbr: if not neighb.GetIdx() == nbrCarbon[choose - 3].GetIdx(): mol.AddBond(neighb.GetIdx(), nbrCarbon[choose - 3].GetIdx(), bondorder[1]) #Remove a 4-bond atom elif Degree == 4: nbr = [] for neighb in atom.GetNeighbors(): nbr.append(neighb) for bond in atom.GetBonds(): mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) mol.RemoveAtom(atom.GetIdx()) n1 = nbr.pop(random.randrange(0, 4, 1)) if random.random() < 0.75: #XA4 -> A-A-A-A n2 = nbr.pop(random.randrange(0, 3, 1)) mol.AddBond(n1.GetIdx(), n2.GetIdx(), bondorder[1]) n3 = nbr.pop(random.randrange(0, 2, 1)) mol.AddBond(n2.GetIdx(), n3.GetIdx(), bondorder[1]) mol.AddBond(n3.GetIdx(), nbr[0].GetIdx(), bondorder[1]) else: #XA4 -> A(A3) for neighb in nbr: mol.AddBond(n1.GetIdx(), neighb.GetIdx(), bondorder[1]) #Make sure nothing is bonded twice oldpairs = [] todelete = [] for bond in mol.GetBonds(): pair = [bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()] pair.sort() if pair in oldpairs: todelete.append(bond) else: oldpairs.append(pair) for bond in todelete: mol.RemoveBond(bond.GetIdx()) # Make sure bonding makes sense for atom in mol.GetAtoms(): try: if EmptyValence(atom) < 0: for bond in atom.GetBonds(): if not bond.HasProp('group'): bond.SetBondType(bondorder[1]) if EmptyValence(atom) < 0: raise MutateFail(mol) except KeyError: raise MutateFail(mol) try: Finalize(mol, aromatic=False) except Exception as e: print "in Remove Atom with:", Chem.MolToSmiles(mol, True), e return mol
def Crossover(m1, m2): #Kekulize mols: for m in (m1, m2): Chem.Kekulize(m, True) #Fragment molecules m1fs = GetFragment(m1) m2fs = GetFragment(m2) if debug: print "CX1", #print "in Crossover:", m1fs, m2fs #pick fragments for crossover checking: # 1. Molecular weight Choices = [] #maxWeight = mprms.maxWeight if maxWeight > 0: w1 = [Descriptors.MolWt(f) for f in m1fs] w2 = [Descriptors.MolWt(f) for f in m2fs] for i, j in ((i, j) for i in xrange(2) for j in xrange(2)): if w1[i] + w2[j] < maxWeight + 50.0: Choices.append((i, j)) # 2. Number of Atoms #MxAtm=mprms.MxAtm if MxAtm > 0: a1 = [f.GetNumAtoms() for f in m1fs] a2 = [f.GetNumAtoms() for f in m2fs] for i, j in ((i, j) for i in xrange(2) for j in xrange(2)): if a1[i] + a2[j] < MxAtm + 4: Choices.append((i, j)) if len(Choices) == 0: raise MutateFail() else: choice = random.choice(Choices) mol1 = m1fs[choice[0]] mol2 = m2fs[choice[1]] # now mol2 has to be connected to mol1 # but what happens here? mol1.SetProp('parent1', m1.GetProp('isosmi')) mol2.SetProp('parent2', m2.GetProp('isosmi')) if debug: print "CX2", ''' if mol1.HasProp('groups'): m1groups=[ groupid for groupid in mol1.GetProp('groups') if len(MolAtIsInGroup(mol1, groupid))>0 ] mol1.ClearProp('groups') else: m1groups=[] igrp=1 if mol2.HasProp('groups'): for groupnum in mol2.GetProp('groups'): m2groups=mol2.GetProp('groups') if len(MolAtIsInGroup(mol2, groupnum))==0: continue if groupnum in m1groups: #need to reassign group id while igrp in m1groups+m2groups: igrp+=1 m1groups.append(igrp) for atom in MolAtIsInGroup(mol2, groupnum): atom.SetProp('group',igrp) for bond in MolBondIsInGroup(mol2, groupnum): bond.SetProp('group',igrp) m2groups.append(igrp) m2groups.remove(groupnum) else: m1groups.append(groupnum) if len(m2groups)>0: mol2.SetProp('groups',m2groups) if len(m1groups)>0: mol1.SetProp('groups',m1groups) ''' ################################### #Append molecule 2 to molecule 1. newmol = Chem.CombineMols(mol1, mol2) newids = Chem.GetMolFrags(newmol) # Now, bond the two fragments to create the child molecule. # We choose a random pair of possible atoms to do the attachment; possibleA1 = [ atom for atom in GetIAtoms(newids[0], newmol) if EmptyValence(atom) > 0 ] possibleA2 = [ atom for atom in GetIAtoms(newids[1], newmol) if EmptyValence(atom) > 0 ] if len(possibleA1) == 0 or len(possibleA2) == 0: #print "no possible atoms!" raise MutateFail() if debug: print "CX3", newmolRW = Chem.RWMol(newmol) atom1 = random.choice(possibleA1) atom2 = random.choice(possibleA2) newmolRW.AddBond(atom1.GetIdx(), atom2.GetIdx(), Chem.BondType.SINGLE) if debug: print "CX4", #print "new mol!", Chem.MolToSmiles(newmolRW) mol = newmolRW.GetMol() try: Finalize(mol, aromatic=False) except: raise MutateFail() if debug: print "CX5", return mol
def DelAtom(mol): inismi = Chem.MolToSmiles(mol) atoms = filter(CanRemoveAtom, mol.GetAtoms()) if len(atoms) == 1: raise MutateFail() atom = random.choice(atoms) # or GetTotalDegree? #Degree = atom.GetDegree() + atom.GetNumRadicalElectrons() Degree = atom.GetDegree() #If there's only one atom, refuse to remove it if len(atom.GetNeighbors()) == 0: raise MutateFail(mol) #Remove a terminal atom: elif Degree == 1: for bond in atom.GetBonds(): mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) mol.RemoveAtom(atom.GetIdx()) # until here it seems fine #Remove an in-chain atom: # #NOT WORKING WELL elif Degree == 2: #print Chem.MolToSmiles(mol, True) nbr = [] for neighb in atom.GetNeighbors(): nbr.append(neighb) for bond in atom.GetBonds(): mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) mol.RemoveAtom(atom.GetIdx()) # this line give indexerrors: mol.AddBond(nbr[0].GetIdx(), nbr[1].GetIdx(), bondorder[1]) elif True: pass #Remove a 3-bond atom: elif Degree == 3: nbr = [] nbrCarbon = [] nChoice = 3 for neighb in atom.GetNeighbors(): nbr.append(neighb) if neighb.GetAtomicNum()==6 and \ len(neighb.GetNeighbors())==3: nChoice = nChoice + 1 nbrCarbon.append(neighb) choose = random.randrange(0, nChoice, 1) for bond in atom.GetBonds(): mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetBeginAtomIdx()) mol.RemoveAtom(atom.GetIdx()) if choose == 0: mol.AddBond(nbr[0].GetIdx(), nbr[1].GetIdx(), bondorder[1]) mol.AddBond(nbr[1].GetIdx(), nbr[2].GetIdx(), bondorder[1]) elif choose == 1: mol.AddBond(nbr[0].GetIdx(), nbr[2].GetIdx(), bondorder[1]) mol.AddBond(nbr[2].GetIdx(), nbr[1].GetIdx(), bondorder[1]) elif choose == 2: mol.AddBond(nbr[0].GetIdx(), nbr[1].GetIdx(), bondorder[1]) mol.AddBond(nbr[2].GetIdx(), nbr[0].GetIdx(), bondorder[1]) else: for neighb in nbr: if not neighb.GetIdx() == nbrCarbon[choose - 3].GetIdx(): mol.AddBond(neighb.GetIdx(), nbrCarbon[choose - 3].GetIdx(), bondorder[1]) #Remove a 4-bond atom elif Degree == 4: nbr = [] for neighb in atom.GetNeighbors(): nbr.append(neighb) for bond in atom.GetBonds(): mol.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()) mol.RemoveAtom(atom.GetIdx()) n1 = nbr.pop(random.randrange(0, 4, 1)) if random.random() < 0.75: #XA4 -> A-A-A-A n2 = nbr.pop(random.randrange(0, 3, 1)) mol.AddBond(n1.GetIdx(), n2.GetIdx(), bondorder[1]) n3 = nbr.pop(random.randrange(0, 2, 1)) mol.AddBond(n2.GetIdx(), n3.GetIdx(), bondorder[1]) mol.AddBond(n3.GetIdx(), nbr[0].GetIdx(), bondorder[1]) else: #XA4 -> A(A3) for neighb in nbr: mol.AddBond(n1.GetIdx(), neighb.GetIdx(), bondorder[1]) #Make sure nothing is bonded twice oldpairs = [] todelete = [] for bond in mol.GetBonds(): pair = [bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()] pair.sort() if pair in oldpairs: todelete.append(bond) else: oldpairs.append(pair) for bond in todelete: mol.RemoveBond(bond.GetIdx()) # Make sure bonding makes sense for atom in mol.GetAtoms(): try: if EmptyValence(atom) < 0: for bond in atom.GetBonds(): if not bond.HasProp('group'): bond.SetBondType(bondorder[1]) if EmptyValence(atom) < 0: raise MutateFail(mol) except KeyError: raise MutateFail(mol) if debug: # print a file with the results from these mutations: with open('delatoms.smi', 'a') as f: f.write(Chem.MolToSmiles(mol) + '\n') return mol