$darkmode
DENOPTIM
AtomOrganizer.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2019 Marco Foscato <marco.foscato@uib.no>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package denoptim.utils;
20
21import java.util.ArrayList;
22import java.util.HashMap;
23import java.util.HashSet;
24import java.util.List;
25import java.util.Map;
26import java.util.Set;
27
28import org.openscience.cdk.Bond;
29import org.openscience.cdk.interfaces.IAtom;
30import org.openscience.cdk.interfaces.IAtomContainer;
31import org.openscience.cdk.interfaces.IBond;
32import org.openscience.cdk.interfaces.IChemObjectBuilder;
33import org.openscience.cdk.silent.SilentChemObjectBuilder;
34
35import denoptim.constants.DENOPTIMConstants;
36import denoptim.exception.DENOPTIMException;
37import denoptim.graph.APClass;
38import denoptim.graph.Edge.BondType;
39import denoptim.graph.Ring;
40
41
47public class AtomOrganizer
48{
49//------------------------------------------------------------------------------
50
51//indexes of atoms bearing AP
52
53 //Flags per atoms
54 private ArrayList<ArrayList<Boolean>> flags;
55 //Alternative atom order
56 private Map<Integer, ArrayList<Integer>> atomOrders;
57 private Map<Integer, ArrayList<Integer>> oldToNewOrder;
58 //layers
59 private Map<Integer, Map<Integer, Set<Integer>>> layers;
60 //layer index
61 private int lidx;
62 //Type of reordering scheme:
63 // 1 => branch-oriented approach
64 // 2 => layer-oriented approach
65 private int scheme = 1;
66
67//------------------------------------------------------------------------------
68
70 {
71 flags = new ArrayList<>();
72 atomOrders = new HashMap<>();
73 oldToNewOrder = new HashMap<>();
74 lidx = 0;
75 layers = new HashMap<>();
76 }
77
78//------------------------------------------------------------------------------
79
80 public void setScheme(int scheme)
81 {
82 this.scheme = scheme;
83 }
84
85//------------------------------------------------------------------------------
86
97 public ArrayList<Integer> getNewToOldOrder(int seed)
98 {
99 ArrayList<Integer> lst = new ArrayList<Integer>();
100 for (Integer i : atomOrders.get(seed))
101 {
102 lst.add(i.intValue());
103 }
104 return lst;
105 }
106
107//------------------------------------------------------------------------------
108
119 public ArrayList<Integer> getOldToNewOrder(int seed)
120 {
121 ArrayList<Integer> lst = new ArrayList<Integer>();
122 for (Integer i : oldToNewOrder.get(seed))
123 {
124 lst.add(i.intValue());
125 }
126 return lst;
127 }
128
129//------------------------------------------------------------------------------
140 public IAtomContainer reorderStartingFrom(int seed, IAtomContainer mol)
141 throws DENOPTIMException {
142 int flag = getFreeAtomsFlag(mol);
143
144 //find new order of atoms using starting from 'seed'
145 if (!atomOrders.containsKey(seed)) {
146 //create all empty vectors of proper size
147 ArrayList<Integer> reorderedAtms = new ArrayList<>(mol.getAtomCount());
148 atomOrders.put(seed, reorderedAtms);
149 ArrayList<Integer> pointer = new ArrayList<>(mol.getAtomCount());
150 oldToNewOrder.put(seed, pointer);
151 for (int i = 0; i < mol.getAtomCount(); i++) {
152 oldToNewOrder.get(seed).add(i, -1);
153 }
154
155 //add atoms to new other
156 switch (scheme) {
157 case 1:
158 branchOrienterReordering(seed, flag, mol);
159 break;
160 case 2:
161 layerOrientedReordering(seed, flag, mol);
162 break;
163 default:
164 String str = "ERROR! Atom ordering: scheme not recognized";
165 throw new DENOPTIMException(str);
166 }
167
168 //delete temp vector of flags
169 deleteAtomsFlag(flag);
170 }
171
172 return makeReorderedCopy(mol,
173 atomOrders.get(seed),
174 oldToNewOrder.get(seed));
175 }
176
177//------------------------------------------------------------------------------
178
208 public static IAtomContainer makeReorderedCopy(IAtomContainer original,
209 List<Integer> newToOldOrder, List<Integer> oldToNewOrder)
210 throws DENOPTIMException
211 {
212 IChemObjectBuilder builder = SilentChemObjectBuilder.getInstance();
213 IAtomContainer iac = builder.newAtomContainer();
214
215 StringBuilder sbMolProp = new StringBuilder();
216 for (int i = 0; i < original.getAtomCount(); i++)
217 {
218 IAtom originalAtom = original.getAtom(newToOldOrder.get(i));
219 IAtom atm = MoleculeUtils.makeSameAtomAs(originalAtom, true, true);
220 String atmPropVID = " none";
221 Object p = originalAtom.getProperty(DENOPTIMConstants.ATMPROPVERTEXID);
222 if (p != null)
223 {
224 atmPropVID = " " + ((Long) p).longValue();
225 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXID,
226 ((Long) p).longValue());
227 }
228 sbMolProp.append(atmPropVID);
229
230 Object pVPath = originalAtom.getProperty(
232 if (pVPath != null)
233 {
234 atm.setProperty(DENOPTIMConstants.ATMPROPVERTEXPATH,
235 pVPath.toString());
236 }
237
238 Object pAPCls = originalAtom.getProperty(
240 if (pAPCls != null)
241 {
242 atm.setProperty(DENOPTIMConstants.RCAPROPAPCTORCA,
243 (APClass) pAPCls);
244 }
245
246 Object pBndTyp = originalAtom.getProperty(
248 if (pBndTyp != null)
249 {
250 atm.setProperty(DENOPTIMConstants.RCAPROPCHORDBNDTYP,
251 (BondType) pBndTyp);
252 }
253
254 Object pRing = originalAtom.getProperty(
256 if (pRing != null)
257 {
258 atm.setProperty(DENOPTIMConstants.RCAPROPRINGUSER,
259 (Ring) pRing);
260 }
261
262 iac.addAtom(atm);
263 }
264 //NB: ATMPROPVERTEXID is integer when it's atom property, but it's string
265 // when collected in a molecular property.
266 iac.setProperty(DENOPTIMConstants.ATMPROPVERTEXID,
267 sbMolProp.toString().trim());
268
269 Object e = original.getProperty(DENOPTIMConstants.MOLERRORTAG);
270 if (e != null)
271 {
272 iac.setProperty(DENOPTIMConstants.MOLERRORTAG,
273 e.toString().trim());
274 }
275
276 //Regenerate Bonds
277 for (int i = 0; i < original.getBondCount(); i++) {
278 try {
279 IBond oldBnd = original.getBond(i);
280 if (oldBnd.getAtomCount() != 2) {
281 String msg = "Multicenter bond not supported.";
282 throw new DENOPTIMException(msg);
283 }
284 int atm1 = original.indexOf(oldBnd.getAtom(0));
285 int atm2 = original.indexOf(oldBnd.getAtom(1));
286 int newatm1 = oldToNewOrder.get(atm1);
287 int newatm2 = oldToNewOrder.get(atm2);
288 IBond.Order order = oldBnd.getOrder();
289 IBond.Stereo stereo = oldBnd.getStereo();
290 IBond bnd = new Bond(iac.getAtom(newatm1),
291 iac.getAtom(newatm2), order, stereo);
292 iac.addBond(bnd);
293 } catch (Throwable t) {
294 String msg = "ERROR in making new bond";
295 throw new DENOPTIMException(msg);
296 }
297 }
298
299 return iac;
300 }
301
302//------------------------------------------------------------------------------
303
314 private void branchOrienterReordering(int seed, int flag, IAtomContainer mol)
315 {
316 int ap = seed;
317
318 //add the seed to the new list
319 processAtom(seed, ap, flag);
320
321 //add all other atoms
322 exploreMolecule(seed, flag, mol, ap);
323 }
324
325//------------------------------------------------------------------------------
329 private void exploreMolecule(int seed, int flag, IAtomContainer mol, int ap)
330 {
331 // Get the list of neighbours (connected atoms)
332 List<IAtom> neighbourAtoms = mol.getConnectedAtomsList(mol.getAtom(seed));
333
334 // Throw away atoms already done
335 List<IAtom> purgedList = new ArrayList<>();
336 for (IAtom connectedAtom : neighbourAtoms)
337 {
338 int atmidx = mol.indexOf(connectedAtom);
339 if (!flags.get(flag).get(atmidx))
340 {
341 purgedList.add(connectedAtom);
342 }
343 }
344
345 // In case nothing else to do
346 if (purgedList.isEmpty())
347 {
348 return;
349 }
350
351 // Reorder according to priority
352 if (purgedList.size() > 1)
353 {
354 purgedList = prioritizeAtomList(purgedList, mol);
355 }
356
357 // Add all the atoms connected
358 for (IAtom connectedAtom : purgedList) {
359 // Identify atom
360 int atmidx = mol.indexOf(connectedAtom);
361
362 // Use flag to avoid giving a bite on your own tail!
363 if (flags.get(flag).get(atmidx)) {
364 continue;
365 }
366
367 // Add to lists
368 processAtom(atmidx, ap, flag);
369 }
370
371 // Move to the next level
372 for (IAtom connectedAtom : purgedList) {
373 //get atom
374 //identify atom
375 int atmidx = mol.indexOf(connectedAtom);
376
377 // move to the next shell of atoms
378 if (mol.getConnectedBondsCount(connectedAtom) > 1) {
379 exploreMolecule(atmidx, flag, mol, ap);
380 }
381 }
382 }
383
384//------------------------------------------------------------------------------
398 private void layerOrientedReordering(int seed, int flag, IAtomContainer mol)
399 {
400 // Set flag for detecting layers
401 int lyflg = getFreeAtomsFlag(mol);
402
403 // Set seed as center of all layers
404 lidx = 0;
405 int ap = seed;
406 addAtomToLayer(seed, ap, lidx, lyflg);
407
408 // Loop over layers to find new layers
409 boolean goon = true;
410 Map<Integer, Set<Integer>> layersOfAP = layers.get(ap);
411 while (goon)
412 {
413 // Get atoms in this layer
414 Set<Integer> atmInLyr = layersOfAP.get(lidx);
415
416 // get connected atoms for all atoms in this layer checking flag
417 for (int atmIdx : atmInLyr)
418 {
419 List<IAtom> neighbourAtoms = mol.getConnectedAtomsList(mol.getAtom(atmIdx));
420 // Set layer lidx+1 to all connected atoms
421 for (IAtom ngbAtm : neighbourAtoms)
422 {
423 int ngbAtmIdx = mol.indexOf(ngbAtm);
424 if (!flags.get(lyflg).get(ngbAtmIdx))
425 {
426 int layerOfNgbAtm = lidx + 1;
427 addAtomToLayer(ngbAtmIdx, ap, layerOfNgbAtm, lyflg);
428 }
429 }
430 }
431
432 // Update goon-condition
433 goon = layersOfAP.containsKey(lidx + 1);
434
435 // Update layer identifier
436 lidx++;
437 }
438
439 // Explore layers and reorder atoms
440 int numberOfLayers = layersOfAP.keySet().size();
441 for (int i = 0; i < numberOfLayers; i++)
442 {
443 // Get atoms in this layer
444 Set<Integer> idxInLyr = layersOfAP.get(i);
445 List<IAtom> atmInLyr = new ArrayList<>();
446 for (Integer idx : idxInLyr)
447 {
448 atmInLyr.add(mol.getAtom(idx));
449 }
450
451 // Get prioritized list of layer's members
452 atmInLyr = prioritizeAtomList(atmInLyr, mol);
453
454 // Report atoms to ordered list
455 for (IAtom orderedAtm : atmInLyr) {
456 // Identify atom
457 int atmidx = mol.indexOf(orderedAtm);
458
459 // Add to ordered lists
460 processAtom(atmidx, ap, flag);
461 }
462 }
463
464 //delete temp vector of flags
465 deleteAtomsFlag(lyflg);
466
467 }
468
469//------------------------------------------------------------------------------
479 private void addAtomToLayer(int atmidx, int ap, int layer, int doneFlag)
480 {
481 // Check existence of AP-related layers
482 if (!layers.containsKey(ap))
483 {
484 Map<Integer, Set<Integer>> layersForAP = new HashMap<>();
485 layers.put(ap, layersForAP);
486 }
487
488 // check existence of this layer in the list of layers
489 if (!layers.get(ap).containsKey(layer))
490 {
491 Set<Integer> atmInLayer = new HashSet<>();
492 layers.get(ap).put(layer, atmInLayer);
493 }
494
495 // Add atom to layer
496 layers.get(ap).get(layer).add(atmidx);
497
498 //Set the doneFlag to true ( = 'DONE')
499 flags.get(doneFlag).set(atmidx, true);
500 }
501
502//------------------------------------------------------------------------------
511 private void processAtom(int atmidx, int ap, int doneFlag)
512 {
513 //Set the doneFlag to true ( = 'DONE')
514 flags.get(doneFlag).set(atmidx, true);
515
516 //Add OLD atom index to the NEW ORDER pointer
517 atomOrders.get(ap).add(atmidx);
518
519 //get new index of this atom
520 int newIdx = atomOrders.get(ap).size();
521
522 //Add NEW atom index to the OLD ORDER pointer
523 oldToNewOrder.get(ap).set(atmidx, newIdx-1);
524 }
525
526//------------------------------------------------------------------------------
536 private List<IAtom> prioritizeAtomList(List<IAtom> inList, IAtomContainer mol)
537 {
538 List<IAtom> outList = new ArrayList<>();
539
540 //make a list of connected ligands
541 List<ConnectedLigand> ligList = new ArrayList<>();
542 for (IAtom seed : inList)
543 {
544 ConnectedLigand lig = new ConnectedLigand(seed,
545 mol.getConnectedBondsCount(seed));
546 ligList.add(lig);
547 }
548
549 ligList.sort(new ConnectedLigandComparator());
550
551 for (ConnectedLigand connectedLigand : ligList) {
552 IAtom a = connectedLigand.getAtom();
553 outList.add(a);
554 }
555 return outList;
556 }
557
558//------------------------------------------------------------------------------
568 private int getFreeAtomsFlag(IAtomContainer mol)
569 {
570 int freeFlag = -1;
571 //create a vector with false entries
572 int atoms = mol.getAtomCount();
573 ArrayList<Boolean> flg = new ArrayList<>();
574 for (int i = 0; i < atoms; i++)
575 {
576 flg.add(false);
577 }
578
579 //add the vector to the list of flags
580 flags.add(flg);
581 freeFlag = flags.indexOf(flg);
582
583 return freeFlag;
584 }
585
586//------------------------------------------------------------------------------
592 private void deleteAtomsFlag(int flagID)
593 {
594 flags.remove(flagID);
595 }
596
597//------------------------------------------------------------------------------
598}
General set of constants used in DENOPTIM.
static final String ATMPROPVERTEXID
String tag of Atom property used to store the unique ID of the Vertex corresponding to the molecular ...
static final Object RCAPROPRINGUSER
Property of a IAtom representing a RingClosingAttractor.
static final Object RCAPROPCHORDBNDTYP
Property of a IAtom representing a RingClosingAttractor.
static final String ATMPROPVERTEXPATH
Name of Atom property used to store the unique ID of the Vertex that owns the atom and the IDs of any...
static final String MOLERRORTAG
SDF tag containing errors during execution of molecule specific tasks.
static final Object RCAPROPAPCTORCA
Property of a IAtom representing a RingClosingAttractor.
This class represents the closure of a ring in a spanning tree.
Definition: Ring.java:40
Tool for re-organizing the list of atoms of an IAtomContainer
void deleteAtomsFlag(int flagID)
Destroy a vector of flags.
void processAtom(int atmidx, int ap, int doneFlag)
Perform all the operation to report an atom in the new order.
void branchOrienterReordering(int seed, int flag, IAtomContainer mol)
Reorders atoms according to the branch-oriented scheme.
List< IAtom > prioritizeAtomList(List< IAtom > inList, IAtomContainer mol)
Change the order of atoms in a list according to the priority rules defined in the comparator Connect...
Map< Integer, ArrayList< Integer > > oldToNewOrder
void addAtomToLayer(int atmidx, int ap, int layer, int doneFlag)
Set the layer membership of an antom.
ArrayList< Integer > getNewToOldOrder(int seed)
Returns the list of indexes that allow to map the new atom position with the old one.
Map< Integer, Map< Integer, Set< Integer > > > layers
Map< Integer, ArrayList< Integer > > atomOrders
int getFreeAtomsFlag(IAtomContainer mol)
Generates a vector of boolean flags.
static IAtomContainer makeReorderedCopy(IAtomContainer original, List< Integer > newToOldOrder, List< Integer > oldToNewOrder)
Produces a new container that looks very similar to the original one, but has a different atom order.
IAtomContainer reorderStartingFrom(int seed, IAtomContainer mol)
Reorder the atoms in mol starting with atom seed
ArrayList< Integer > getOldToNewOrder(int seed)
Returns the list of indexes that allow to map the old atom position with the new one.
void layerOrientedReordering(int seed, int flag, IAtomContainer mol)
Reorders atoms according to the layer-oriented scheme.
ArrayList< ArrayList< Boolean > > flags
void exploreMolecule(int seed, int flag, IAtomContainer mol, int ap)
Method meant for recursion on all atoms connected to a starting point.
Compare two ConnectedLigand according to the number of connected atoms and the mass number.
A ConnectedLigand is just an atom with an explicit field reporting the number of connected atoms.
Utilities for molecule conversion.
static IAtom makeSameAtomAs(IAtom oAtm)
Method that constructs an atom that reflect the same atom given as parameter in terms of element symb...
Possible chemical bond types an edge can represent.
Definition: Edge.java:303