com.actelion.research.chem.chemicalspaces.ptree.search.SynthonPharmTreeGenerator 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
package com.actelion.research.chem.chemicalspaces.ptree.search;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import com.actelion.research.chem.Canonizer;
import com.actelion.research.chem.IDCodeParser;
import com.actelion.research.chem.Molecule;
import com.actelion.research.chem.SSSearcher;
import com.actelion.research.chem.StereoMolecule;
import com.actelion.research.chem.chemicalspaces.ptree.PharmTreeSynthonReactionHelper;
import com.actelion.research.chem.chemicalspaces.ptree.synthon.PharmTreeSynthon;
import com.actelion.research.chem.chemicalspaces.ptree.synthon.PharmTreeSynthonLibrary;
import com.actelion.research.chem.chemicalspaces.synthon.SynthonReactor;
import com.actelion.research.chem.descriptor.pharmacophoretree.FeatureCalculator;
import com.actelion.research.chem.descriptor.pharmacophoretree.PharmacophoreNode;
import com.actelion.research.chem.descriptor.pharmacophoretree.PharmacophoreTree;
import com.actelion.research.chem.descriptor.pharmacophoretree.PharmacophoreTreeGenerator;
import com.actelion.research.chem.phesa.pharmacophore.PharmacophoreCalculator;
/**
* Generates PharmacophoreTrees (PharmTrees) for building blocks (or fragments) that are part of virtual chemical space.
* Building blocks are represented as synthon structures, containing reactive linkers (R1,R2,R3,R4).
* In order to make the searches in the virtual chemical space using PharmTrees more efficient, atoms that will be part
* of the same ring (formed by the reaction), are merged into one node. Thereby, also the number of linkers required
* to make the ring formation is reduced
* @author Joel Wahl
*
*/
public class SynthonPharmTreeGenerator {
private PharmTreeSynthonLibrary synthonLib;
private List genericSynthons;
private PharmTreeSynthonReactionHelper rxnHelper;
public SynthonPharmTreeGenerator(PharmTreeSynthonLibrary synthonLib) {
this.synthonLib = synthonLib;
this.genericSynthons = synthonLib.getGenericReactants();
this.rxnHelper = synthonLib.getReactionHelper();
furtherProcessGenericSynthons();
}
public void processBuildingBlocks() {
Map> ringReactantIndeces = rxnHelper.getRingReactantIndeces();
Map> ringWithLinks = rxnHelper.getRingWithLinks();
StereoMolecule genericProduct = rxnHelper.getGenericProduct();
Map> reactantsWithLinkers = rxnHelper.getReactantsWithLinkers().entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey(), e -> new ArrayList(e.getValue()))); //deep copy of map, since it will be modified later
Set linkersToDelete = new HashSet();
for(int ring : ringReactantIndeces.keySet()) {
int nLinkers = ringReactantIndeces.get(ring).size()-1;
//number of linkers actually needed to describe ring closure reaction is equal to number of reactants that are involved in forming the ring minus 1
int toDelete = ringWithLinks.get(ring).size()-nLinkers; // number of links that can be removed
Iterator iterator = ringWithLinks.get(ring).iterator();
for(int i=0;i v.remove(Integer.valueOf(del)));
}
}
for(int i=0;i synthonList = synthonLib.getSynthons().get(i);
for(int bb=0;bb nodesToBeDeleted = new ArrayList();
for(PharmacophoreNode node : bbTree.getNodes()) {
if(node.isLinkNode()) {
if(linkersToDelete.contains(node.getFunctionalities()[0]))
nodesToBeDeleted.add(node);
}
}
nodesToBeDeleted.forEach(e -> {
bbTree.removeNode(e);
});
synthonList.get(bb).setPharmacophoreTree(bbTree);
}
}
}
public PharmacophoreTree calculatePTree(StereoMolecule bb, StereoMolecule genericProduct, int synthonID,
Map> ringReactantIndeces, Map> reactantsWithLinkers) {
FeatureCalculator calculator = new FeatureCalculator(genericProduct);
calculator.calculate();
int[][] productAtomFunctionalities = calculator.getAtomFunctionalities();
double[] productAtomVolumes = PharmacophoreTreeGenerator.getAtomVolumes(genericProduct);
List ringFunctionalities = new ArrayList();
List> ringVolumes = new ArrayList>();
for(Set ring : rxnHelper.getFormedRings()) {
int[] func = new int[PharmacophoreCalculator.LIPO_ID+1];
List volumes = new ArrayList();
for(int ringAtom : ring) {
int[] atomFunc = productAtomFunctionalities[ringAtom];
IntStream.range(0,func.length).forEach(e -> func[e]+=atomFunc[e]);
volumes.add(productAtomVolumes[ringAtom]);
}
ringFunctionalities.add(func);
ringVolumes.add(volumes);
}
//from this info, the node properties of the ring nodes representing rings formed
//in the reaction are fully defined. Now we need to map them to the correct ring indeces
// and process the building blocks accordingly
//one building block involved in the ring formation should bear the full ring node,
//the others are dummy nodes
int[] reactantToBBMap = new int[0];
Map> rings = new HashMap>(); //formed rings are indiced
if(rxnHelper.getFormedRings().size()>0) {
SSSearcher searcher = new SSSearcher();
searcher.setFragment(genericSynthons.get(synthonID));
searcher.setMolecule(bb);
searcher.findFragmentInMolecule();
ArrayList matches = searcher.getMatchList();
if(matches.size()!=1) {
System.err.println("could not match generic reactant to building block");
System.err.println(genericSynthons.get(synthonID).getIDCode());
System.err.println(bb.getIDCode());
return null;
}
else {
reactantToBBMap = matches.get(0);
}
for(int a=0;a=SynthonReactor.CONNECTOR_OFFSET)
continue;
for(int i=0;i());
rings.get(ringIndex).add(a);
}
}
}
}
}
}
Map> atomToNodes = new HashMap>();
PharmacophoreTree bbTree = PharmacophoreTreeGenerator.generate(bb, atomToNodes,rings.values().stream().collect(Collectors.toList()));
Map ringMainReactants = new HashMap();
//for every formed ring in the reaction, one reactant has all the features assigned to it, whereas the remaining ones have zero nodes
// the main reactant should be the one with the highest number of linkers
for(int ring : ringReactantIndeces.keySet()) {
Set reactants = ringReactantIndeces.get(ring);
Iterator iterator = reactants.iterator();
int maxLinkers = 0;
int mainReactant = -1;
while(iterator.hasNext()) {
int reactant = iterator.next();
if(reactantsWithLinkers.get(reactant).size()>maxLinkers) {
maxLinkers = reactantsWithLinkers.get(reactant).size();
mainReactant = reactant;
}
}
ringMainReactants.put(ring,mainReactant);
}
// for the nodes that represent groups of atoms that will be part of a newly formed ring, do the following:
// if the building block is considered the main reactant for this ring formation, all ring functionalities/volumes
// are assigned to it
for(PharmacophoreNode node : bbTree.getNodes()) {
if(node.isLinkNode())
continue;
else {
boolean nodeProcessed = false;
for(int index=0;index formedRing = rxnHelper.getFormedRings().get(l);
if(formedRing.contains(productIndex)) {
nodeProcessed = true;
int ringIndex = l;
int mainReactantSynthonID = ringMainReactants.get(ringIndex);
if(synthonID==mainReactantSynthonID) {
int dummyAtomsToAdd = formedRing.size()-node.getAtoms().size();
for(int da=0;da weights = new ArrayList();
IntStream.range(0, node.getAtoms().size()).forEach(e -> weights.add(1.0));
node.setFunctionalities(ringFunctionalities.get(l));
node.setVolumes(ringVolumes.get(l));
}
else {
node.setFunctionalities(new int[PharmacophoreCalculator.LIPO_ID+1]);
node.setVolumes(new ArrayList());
node.setWeights(new ArrayList());
node.setAtoms(new ArrayList());
node.setRole(PharmacophoreNode.ZERO_NODE);
}
node.calculate();
}
}
}
}
}
}
}
return bbTree;
}
/**
* only keep parts of synthons responsible for ring formation
* if one synthon is involved in the formation of more than one ring, it is split into different, disconnected
* fragments so that atoms that are not involved in ring formation are omitted
* @param genericSynthons
* @param rxnHelper
*/
private void furtherProcessGenericSynthons() {
List newGenericSynthons = new ArrayList<>();
Map> ringWithLinks = rxnHelper.getRingWithLinks();
Map> reactantsWithRings = new HashMap>();
Map> ringsWithReactants = rxnHelper.getRingReactantIndeces();
for(int s=0;s());
for(Map.Entry> entry : ringsWithReactants.entrySet()) {
if (entry.getValue().contains(s)) {
reactantsWithRings.get(s).add(entry.getKey());
}
}
StereoMolecule ringFormationSynthon = new StereoMolecule();
StereoMolecule mol = genericSynthons.get(s);
if(reactantsWithRings.get(s).size()==0)
ringFormationSynthon.addMolecule(mol);
else {
PharmTreeSynthon synthon = new PharmTreeSynthon(mol);
List allConnectorAtoms = new ArrayList<>(synthon.getConnectorLabels().values());
List usedConnectorAtoms = new ArrayList<>();
for(int ring : reactantsWithRings.get(s)) {
synthon = new PharmTreeSynthon(mol);
List connectorAtomsToKeep = new ArrayList<>();
for(Map.Entry entry : synthon.getConnectorLabels().entrySet()) {
int linkerNo = synthon.getLinkerNoFromConnectorLabel(entry.getKey());
Set ringLinks = ringWithLinks.get(ring);
if(ringLinks.contains(linkerNo)) {
connectorAtomsToKeep.add(entry.getValue());
}
}
usedConnectorAtoms.addAll(connectorAtomsToKeep);
ringFormationSynthon.addMolecule(synthon.cutConnectorPaths(synthon.getStructure(), connectorAtomsToKeep));
}
for(int connAtom: allConnectorAtoms) {
if(!usedConnectorAtoms.contains(connAtom)) {
List atomPath = Arrays.asList(connAtom, mol.getConnAtom(connAtom, 0));
ringFormationSynthon.addMolecule(synthon.cutConnectorPaths(synthon.getStructure(), atomPath));
}
}
}
ringFormationSynthon.ensureHelperArrays(Molecule.cHelperParities);
newGenericSynthons.add(new Canonizer(ringFormationSynthon).getCanMolecule());
}
genericSynthons.clear();
for(StereoMolecule gs : newGenericSynthons) {
gs.ensureHelperArrays(Molecule.cHelperParities);
genericSynthons.add(gs);
}
rxnHelper = new PharmTreeSynthonReactionHelper(genericSynthons);
}
}