1 /* Copyright (C) 2009-2010  Syed Asad Rahman <asad@ebi.ac.uk>
2  *
3  * Contact: cdk-devel@lists.sourceforge.net
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1
8  * of the License, or (at your option) any later version.
9  * All we ask is that proper credit is given for our work, which includes
10  * - but is not limited to - adding the above copyright notice to the beginning
11  * of your source code files, and to any copyright notice that you may distribute
12  * with programs based on this work.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 package org.openscience.cdk.smsd.algorithm.vflib;
24 
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.TreeMap;
32 import java.util.logging.Level;
33 import org.openscience.cdk.exception.CDKException;
34 import org.openscience.cdk.interfaces.IAtom;
35 import org.openscience.cdk.interfaces.IAtomContainer;
36 import org.openscience.cdk.isomorphism.matchers.IQueryAtomContainer;
37 import org.openscience.cdk.smsd.algorithm.mcgregor.McGregor;
38 import org.openscience.cdk.smsd.algorithm.vflib.interfaces.IMapper;
39 import org.openscience.cdk.smsd.algorithm.vflib.interfaces.INode;
40 import org.openscience.cdk.smsd.algorithm.vflib.interfaces.IQuery;
41 import org.openscience.cdk.smsd.algorithm.vflib.map.VFMapper;
42 import org.openscience.cdk.smsd.algorithm.vflib.query.QueryCompiler;
43 import org.openscience.cdk.smsd.interfaces.AbstractSubGraph;
44 import org.openscience.cdk.smsd.interfaces.IMCSBase;
45 import org.openscience.cdk.smsd.tools.MolHandler;
46 import org.openscience.cdk.tools.ILoggingTool;
47 import org.openscience.cdk.tools.LoggingToolFactory;
48 
49 /**
50  * This is an ultra fast method to report if query
51  * is a substructure for target molecule. If this case is true
52  * then it returns only one mapping.
53  *
54  * This is much faster than {@link
55  * org.openscience.cdk.smsd.algorithm.vflib.VFlibMCSHandler} class
56  * as it only reports first match and backtracks.
57  *
58  * This class should only be used to report if a query
59  * graph is a substructure of the target graph.
60  *
61  * @cdk.module smsd
62  * @cdk.githash
63  * @author Syed Asad Rahman &lt;asad@ebi.ac.uk&gt;
64  * @deprecated SMSD has been deprecated from the CDK with a newer, more recent
65  *             version of SMSD is available at <a href="http://github.com/asad/smsd">http://github.com/asad/smsd</a>.
66  */
67 @Deprecated
68 public class VFlibTurboHandler extends AbstractSubGraph implements IMCSBase {
69 
70     private              List<Map<IAtom, IAtom>>     allAtomMCS     = null;
71     private              Map<IAtom, IAtom>           atomsMCS       = null;
72     private              List<Map<IAtom, IAtom>>     allAtomMCSCopy = null;
73     private              Map<Integer, Integer>       firstMCS       = null;
74     private              List<Map<Integer, Integer>> allMCS         = null;
75     private              List<Map<Integer, Integer>> allMCSCopy     = null;
76     private              IQueryAtomContainer         queryMol       = null;
77     private              IAtomContainer              mol1           = null;
78     private              IAtomContainer              mol2           = null;
79     private              Map<INode, IAtom>           vfLibSolutions = null;
80     private              int                         vfMCSSize      = -1;
81     private              boolean                     bondMatchFlag  = false;
82     private final static ILoggingTool                LOGGER         = LoggingToolFactory
83             .createLoggingTool(VFlibTurboHandler.class);
84 
85     /**
86      * Constructor for an extended VF Algorithm for the MCS search
87      */
VFlibTurboHandler()88     public VFlibTurboHandler() {
89         allAtomMCS = new ArrayList<Map<IAtom, IAtom>>();
90         allAtomMCSCopy = new ArrayList<Map<IAtom, IAtom>>();
91         atomsMCS = new HashMap<IAtom, IAtom>();
92         firstMCS = new TreeMap<Integer, Integer>();
93         allMCS = new ArrayList<Map<Integer, Integer>>();
94         allMCSCopy = new ArrayList<Map<Integer, Integer>>();
95     }
96 
setFirstMappings()97     private void setFirstMappings() {
98         if (!allAtomMCS.isEmpty()) {
99             atomsMCS.putAll(allAtomMCS.get(0));
100             firstMCS.putAll(allMCS.get(0));
101         }
102     }
103 
mcgregorFlag()104     private boolean mcgregorFlag() {
105         int commonAtomCount = checkCommonAtomCount(getReactantMol(), getProductMol());
106         if (commonAtomCount > vfMCSSize && commonAtomCount > vfMCSSize) {
107             return true;
108         }
109         return false;
110     }
111 
112     /** {@inheritDoc}
113      *
114      * Set the VFLib MCS software
115      *
116      * @param reactant
117      * @param product
118      */
119     @Override
set(MolHandler reactant, MolHandler product)120     public void set(MolHandler reactant, MolHandler product) {
121         mol1 = reactant.getMolecule();
122         mol2 = product.getMolecule();
123     }
124 
125     /** {@inheritDoc}
126      *
127      * @param source
128      * @param target
129      */
130     @Override
set(IQueryAtomContainer source, IAtomContainer target)131     public void set(IQueryAtomContainer source, IAtomContainer target) {
132         queryMol = source;
133         mol2 = target;
134     }
135 
hasMap(Map<Integer, Integer> map, List<Map<Integer, Integer>> mapGlobal)136     private boolean hasMap(Map<Integer, Integer> map, List<Map<Integer, Integer>> mapGlobal) {
137         for (Map<Integer, Integer> test : mapGlobal) {
138             if (test.equals(map)) {
139                 return true;
140             }
141         }
142         return false;
143     }
144 
145     /** {@inheritDoc}
146      *
147      */
148     @Override
getAllAtomMapping()149     public List<Map<IAtom, IAtom>> getAllAtomMapping() {
150         return Collections.unmodifiableList(allAtomMCS);
151     }
152 
153     /** {@inheritDoc}
154      */
155     @Override
getAllMapping()156     public List<Map<Integer, Integer>> getAllMapping() {
157         return Collections.unmodifiableList(allMCS);
158     }
159 
160     /** {@inheritDoc}
161      */
162     @Override
getFirstAtomMapping()163     public Map<IAtom, IAtom> getFirstAtomMapping() {
164         return Collections.unmodifiableMap(atomsMCS);
165     }
166 
167     /** {@inheritDoc}
168      */
169     @Override
getFirstMapping()170     public Map<Integer, Integer> getFirstMapping() {
171         return Collections.unmodifiableMap(firstMCS);
172     }
173 
checkCommonAtomCount(IAtomContainer reactantMolecule, IAtomContainer productMolecule)174     private int checkCommonAtomCount(IAtomContainer reactantMolecule, IAtomContainer productMolecule) {
175         ArrayList<String> atoms = new ArrayList<String>();
176         for (int i = 0; i < reactantMolecule.getAtomCount(); i++) {
177             atoms.add(reactantMolecule.getAtom(i).getSymbol());
178         }
179         int common = 0;
180         for (int i = 0; i < productMolecule.getAtomCount(); i++) {
181             String symbol = productMolecule.getAtom(i).getSymbol();
182             if (atoms.contains(symbol)) {
183                 atoms.remove(symbol);
184                 common++;
185             }
186         }
187         return common;
188     }
189 
searchVFMappings()190     private void searchVFMappings() {
191         //        System.out.println("searchVFMappings ");
192         IQuery query = null;
193         IMapper mapper = null;
194         vfLibSolutions = new HashMap<INode, IAtom>();
195         if (queryMol != null) {
196             query = new QueryCompiler(queryMol).compile();
197             mapper = new VFMapper(query);
198             if (mapper.hasMap(getProductMol())) {
199                 Map<INode, IAtom> map = mapper.getFirstMap(getProductMol());
200                 if (map != null) {
201                     vfLibSolutions.putAll(map);
202                 }
203             }
204             setVFMappings(true, query);
205         } else if (getReactantMol().getAtomCount() <= getProductMol().getAtomCount()) {
206             query = new QueryCompiler(mol1, isBondMatchFlag()).compile();
207             mapper = new VFMapper(query);
208             if (mapper.hasMap(getProductMol())) {
209                 Map<INode, IAtom> map = mapper.getFirstMap(getProductMol());
210                 if (map != null) {
211                     vfLibSolutions.putAll(map);
212                 }
213             }
214             setVFMappings(true, query);
215         } else {
216             query = new QueryCompiler(getProductMol(), isBondMatchFlag()).compile();
217             mapper = new VFMapper(query);
218             if (mapper.hasMap(getReactantMol())) {
219                 Map<INode, IAtom> map = mapper.getFirstMap(getReactantMol());
220                 if (map != null) {
221                     vfLibSolutions.putAll(map);
222                 }
223             }
224             setVFMappings(false, query);
225         }
226     }
227 
searchMcGregorMapping()228     private void searchMcGregorMapping() throws CDKException, IOException {
229         List<List<Integer>> mappings = new ArrayList<List<Integer>>();
230         for (Map<Integer, Integer> firstPassMappings : allMCSCopy) {
231             McGregor mgit = new McGregor(getReactantMol(), getProductMol(), mappings, isBondMatchFlag());
232             mgit.startMcGregorIteration(mgit.getMCSSize(), firstPassMappings); //Start McGregor search
233             mappings = mgit.getMappings();
234             mgit = null;
235         }
236         //        System.out.println("\nSol count after MG" + mappings.size());
237         setMcGregorMappings(mappings);
238         vfMCSSize = vfMCSSize / 2;
239         //        System.out.println("After set Sol count MG" + allMCS.size());
240         //        System.out.println("MCSSize " + vfMCSSize + "\n");
241     }
242 
setVFMappings(boolean ronp, IQuery query)243     private void setVFMappings(boolean ronp, IQuery query) {
244         int counter = 0;
245 
246         Map<IAtom, IAtom> atomatomMapping = new HashMap<IAtom, IAtom>();
247         Map<Integer, Integer> indexindexMapping = new TreeMap<Integer, Integer>();
248         if (vfLibSolutions.size() > vfMCSSize) {
249             this.vfMCSSize = vfLibSolutions.size();
250             allAtomMCSCopy.clear();
251             allMCSCopy.clear();
252             counter = 0;
253         }
254         for (Map.Entry<INode, IAtom> mapping : vfLibSolutions.entrySet()) {
255             IAtom qAtom = null;
256             IAtom tAtom = null;
257             if (ronp) {
258                 qAtom = query.getAtom(mapping.getKey());
259                 tAtom = mapping.getValue();
260             } else {
261                 tAtom = query.getAtom(mapping.getKey());
262                 qAtom = mapping.getValue();
263             }
264             Integer qIndex = Integer.valueOf(getReactantMol().indexOf(qAtom));
265             Integer tIndex = Integer.valueOf(getProductMol().indexOf(tAtom));
266             if (qIndex != null && tIndex != null) {
267                 atomatomMapping.put(qAtom, tAtom);
268                 indexindexMapping.put(qIndex, tIndex);
269             } else {
270                 try {
271                     throw new CDKException("Atom index pointing to NULL");
272                 } catch (CDKException ex) {
273                     LOGGER.error(Level.SEVERE, null, ex);
274                 }
275             }
276         }
277         //            System.out.println("indexindexMapping " + indexindexMapping.size());
278         //            System.out.println("MCS Size " + vfMCSSize);
279         if (!atomatomMapping.isEmpty() && !hasMap(indexindexMapping, allMCSCopy)
280                 && indexindexMapping.size() == vfMCSSize) {
281             allAtomMCSCopy.add(counter, atomatomMapping);
282             allMCSCopy.add(counter, indexindexMapping);
283             counter++;
284         }
285         //        System.out.println("allMCSCopy " + allMCSCopy.size());
286     }
287 
setMcGregorMappings(List<List<Integer>> mappings)288     private void setMcGregorMappings(List<List<Integer>> mappings) throws CDKException {
289         int counter = 0;
290         this.vfMCSSize = 0;
291         for (List<Integer> mapping : mappings) {
292             if (mapping.size() > vfMCSSize) {
293                 vfMCSSize = (mapping.size() / 2);
294                 allAtomMCS.clear();
295                 allMCS.clear();
296                 counter = 0;
297             }
298             Map<IAtom, IAtom> atomatomMapping = new HashMap<IAtom, IAtom>();
299             Map<Integer, Integer> indexindexMapping = new TreeMap<Integer, Integer>();
300             for (int index = 0; index < mapping.size(); index += 2) {
301                 IAtom qAtom = null;
302                 IAtom tAtom = null;
303 
304                 qAtom = getReactantMol().getAtom(mapping.get(index));
305                 tAtom = getProductMol().getAtom(mapping.get(index + 1));
306 
307                 Integer qIndex = mapping.get(index);
308                 Integer tIndex = mapping.get(index + 1);
309 
310                 if (qIndex != -1 && tIndex != -1) {
311                     atomatomMapping.put(qAtom, tAtom);
312                     indexindexMapping.put(qIndex, tIndex);
313                 } else {
314                     throw new CDKException("Atom index pointing to NULL");
315                 }
316             }
317             if (!atomatomMapping.isEmpty() && !hasMap(indexindexMapping, allMCS)
318                     && (indexindexMapping.size()) == vfMCSSize) {
319                 allAtomMCS.add(counter, atomatomMapping);
320                 allMCS.add(counter, indexindexMapping);
321                 counter++;
322             }
323         }
324     }
325 
326     @Override
isSubgraph(boolean shouldMatchBonds)327     public boolean isSubgraph(boolean shouldMatchBonds) {
328         setBondMatchFlag(shouldMatchBonds);
329         searchVFMappings();
330         //        boolean flag = mcgregorFlag();
331         //        if (flag && !vfLibSolutions.isEmpty()) {
332         //            try {
333         //                searchMcGregorMapping();
334         //            } catch (CDKException ex) {
335         //                LOGGER.error(Level.SEVERE, null, ex);
336         //            } catch (IOException ex) {
337         //                LOGGER.error(Level.SEVERE, null, ex);
338         //            }
339         //
340         //        } else
341 
342         if (!allAtomMCSCopy.isEmpty()) {
343             allAtomMCS.addAll(allAtomMCSCopy);
344             allMCS.addAll(allMCSCopy);
345         }
346         setFirstMappings();
347         return (!allMCS.isEmpty() && allMCS.iterator().next().size() == getReactantMol().getAtomCount()) ? true : false;
348     }
349 
350     /**
351      * @return the shouldMatchBonds
352      */
isBondMatchFlag()353     public boolean isBondMatchFlag() {
354         return bondMatchFlag;
355     }
356 
357     /**
358      * @param shouldMatchBonds the shouldMatchBonds to set
359      */
setBondMatchFlag(boolean shouldMatchBonds)360     public void setBondMatchFlag(boolean shouldMatchBonds) {
361         this.bondMatchFlag = shouldMatchBonds;
362     }
363 
getReactantMol()364     private IAtomContainer getReactantMol() {
365         return queryMol == null ? mol1 : queryMol;
366     }
367 
getProductMol()368     private IAtomContainer getProductMol() {
369         return mol2;
370     }
371 }
372