source: src/Graph/CyclicStructureAnalysis.cpp@ 8dbcaf

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 Candidate_v1.7.0 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 8dbcaf was 8dbcaf, checked in by Frederik Heber <heber@…>, 12 years ago

FIX: CyclicStructureAnalysis did not work correctly.

  • many errors with local variables that probably should have been in the class.
  • we use OtherAtom in subsequent RetrieveCycleMembers() but we have not given CyclicBFSFromRootToRoot() its ref.
  • the idea now is to use first get all cycles (CyclicBFSFromRootToRoot(). RetrieveCycleMembers()) and then to assign all remaining atoms via AssignRingSizetoNonCycleMembers() with BFSToNextCycle() where we make use of locality property of BFS.
  • Property mode set to 100644
File size: 17.7 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2012 University of Bonn. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder 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 General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * CyclicStructureAnalysis.cpp
25 *
26 * Created on: Feb 16, 2011
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35#include "CodePatterns/MemDebug.hpp"
36
37#include "CyclicStructureAnalysis.hpp"
38
39#include "Atom/atom.hpp"
40#include "Bond/bond.hpp"
41#include "CodePatterns/Assert.hpp"
42#include "CodePatterns/Info.hpp"
43#include "CodePatterns/Log.hpp"
44#include "CodePatterns/Verbose.hpp"
45#include "Element/element.hpp"
46#include "molecule.hpp"
47
48CyclicStructureAnalysis::CyclicStructureAnalysis(const enum HydrogenTreatment _treatment) :
49 treatment(_treatment)
50{}
51
52CyclicStructureAnalysis::~CyclicStructureAnalysis()
53{}
54
55/** Initialise vertex as white with no predecessor, no shortest path(-1), color white.
56 * \param atom_id id of atom whose node we address
57 */
58void CyclicStructureAnalysis::InitNode(atomId_t atom_id)
59{
60 ShortestPathList[atom_id] = -1;
61 PredecessorList[atom_id] = 0;
62 ColorList[atom_id] = GraphEdge::white;
63}
64
65void CyclicStructureAnalysis::Reset()
66{
67 // clear what's present
68 ShortestPathList.clear();
69 PredecessorList.clear();
70 ColorList.clear();
71 BFSStack.clear();
72 TouchedStack.clear();
73}
74
75/** Clean the accounting structure for all nodes touched so far.
76 */
77void CyclicStructureAnalysis::CleanAllTouched()
78{
79 atom *Walker = NULL;
80 while (!TouchedStack.empty()) {
81 Walker = TouchedStack.front();
82 TouchedStack.pop_front();
83 PredecessorList[Walker->getNr()] = NULL;
84 ShortestPathList[Walker->getNr()] = -1;
85 ColorList[Walker->getNr()] = GraphEdge::white;
86 }
87}
88
89/** Resets shortest path list and BFSStack.
90 * \param *&Walker current node, pushed onto BFSStack and TouchedStack
91 */
92void CyclicStructureAnalysis::InitializeToRoot(atom *&Root)
93{
94 ShortestPathList[Root->getNr()] = 0;
95 BFSStack.clear(); // start with empty BFS stack
96 BFSStack.push_front(Root);
97 TouchedStack.push_front(Root);
98}
99
100/** Performs a BFS from \a *Root, trying to find the same node and hence a cycle.
101 * \param OtherAtom pointing to Root on return indicating found cycle
102 * \param *&BackEdge the edge from root that we don't want to move along
103 */
104void CyclicStructureAnalysis::CyclicBFSFromRootToRoot(atom *&OtherAtom, bond::ptr &BackEdge)
105{
106 atom *Walker = NULL;
107 do { // look for Root
108 ASSERT(!BFSStack.empty(), "CyclicStructureAnalysis_CyclicBFSFromRootToRoot() - BFSStack is empty!");
109 Walker = BFSStack.front();
110 BFSStack.pop_front();
111 LOG(2, "INFO: Current Walker is " << *Walker << ", we look for SP to Root " << *Root << ".");
112 const BondList& ListOfBonds = Walker->getListOfBonds();
113 for (BondList::const_iterator Runner = ListOfBonds.begin();
114 Runner != ListOfBonds.end();
115 ++Runner) {
116 if ((*Runner) != BackEdge) { // only walk along DFS spanning tree (otherwise we always find SP of one being backedge Binder)
117 OtherAtom = (*Runner)->GetOtherAtom(Walker);
118 if ((treatment == IncludeHydrogen) || (OtherAtom->getType()->getAtomicNumber() != 1)) {
119 LOG(2, "INFO: Current OtherAtom is: " << OtherAtom->getName() << " for bond " << *(*Runner) << ".");
120 if (ColorList[OtherAtom->getNr()] == GraphEdge::white) {
121 TouchedStack.push_front(OtherAtom);
122 ColorList[OtherAtom->getNr()] = GraphEdge::lightgray;
123 PredecessorList[OtherAtom->getNr()] = Walker; // Walker is the predecessor
124 ShortestPathList[OtherAtom->getNr()] = ShortestPathList[Walker->getNr()] + 1;
125 LOG(2, "INFO: Coloring OtherAtom " << OtherAtom->getName() << " lightgray, its predecessor is " << Walker->getName() << " and its Shortest Path is " << ShortestPathList[OtherAtom->getNr()] << " egde(s) long.");
126 //if (ShortestPathList[OtherAtom->getNr()] < MinimumRingSize[Walker->GetTrueFather()->getNr()]) { // Check for maximum distance
127 LOG(3, "ACCEPT: Putting OtherAtom " << OtherAtom->getName() << " into queue.");
128 BFSStack.push_front(OtherAtom);
129 //}
130 } else {
131 LOG(3, "REJECT: Not Adding, has already been visited.");
132 }
133 if (OtherAtom == Root)
134 break;
135 } else {
136 LOG(2, "INFO: Skipping hydrogen atom " << *OtherAtom << ".");
137 ColorList[OtherAtom->getNr()] = GraphEdge::black;
138 }
139 } else {
140 LOG(2, "REJECT: Bond " << *(*Runner) << " not Visiting, is the back edge.");
141 }
142 }
143 ColorList[Walker->getNr()] = GraphEdge::black;
144 LOG(1, "INFO: Coloring Walker " << Walker->getName() << " " << GraphEdge::getColorName(ColorList[Walker->getNr()]) << ".");
145 if (OtherAtom == Root) { // if we have found the root, check whether this cycle wasn't already found beforehand
146 // step through predecessor list
147 LOG(4, "DEBUG: Checking whether all predecessors are already marked cyclic ...");
148 while (OtherAtom != BackEdge->rightatom) { // Note that leftatom is Root itself
149 if (!OtherAtom->GetTrueFather()->IsCyclic) { // if one bond in the loop is not marked as cyclic, we haven't found this cycle yet
150 LOG(4, "\tDEBUG: OtherAtom " << *OtherAtom << " is not cyclic, breaking.");
151 break;
152 } else
153 OtherAtom = PredecessorList[OtherAtom->getNr()];
154 }
155 LOG(4, "DEBUG: Checking done.");
156 // if each atom in found cycle is cyclic, loop's been found before already
157 if (OtherAtom == BackEdge->rightatom) { // loop got round completely
158 LOG(3, "INFO: This cycle was already found before, skipping and removing seeker from search.");
159 do {
160 ASSERT(!TouchedStack.empty(), "CyclicStructureAnalysis_CyclicBFSFromRootToRoot() - TouchedStack is empty!");
161 OtherAtom = TouchedStack.front();
162 TouchedStack.pop_front();
163 if (PredecessorList[OtherAtom->getNr()] == Walker) {
164 LOG(4, "INFO: Removing " << *OtherAtom << " from lists and stacks.");
165 PredecessorList[OtherAtom->getNr()] = NULL;
166 ShortestPathList[OtherAtom->getNr()] = -1;
167 ColorList[OtherAtom->getNr()] = GraphEdge::white;
168 // rats ... deque has no find()
169 std::deque<atom *>::iterator iter = find(
170 BFSStack.begin(),
171 BFSStack.end(),
172 OtherAtom);
173 ASSERT(iter != BFSStack.end(),
174 "CyclicStructureAnalysis_CyclicBFSFromRootToRoot() - can't find "+toString(*OtherAtom)+" on stack!");
175 BFSStack.erase(iter);
176 }
177 } while ((!TouchedStack.empty()) && (PredecessorList[OtherAtom->getNr()] == NULL));
178 TouchedStack.push_front(OtherAtom); // last was wrongly popped
179 OtherAtom = BackEdge->rightatom; // set to not Root
180 } else {
181 OtherAtom = Root;
182 LOG(2, "INFO: We have reached Root " << *OtherAtom << " and may extract the cycle.");
183 }
184 }
185 } while ((!BFSStack.empty()) && (OtherAtom != Root) && (OtherAtom != NULL)); // || (ShortestPathList[OtherAtom->getNr()] < MinimumRingSize[Walker->GetTrueFather()->getNr()])));
186}
187
188/** Climb back the BFSAccounting::PredecessorList and find cycle members.
189 * \param *&OtherAtom
190 * \param *&BackEdge denotes the edge we did not want to travel along when doing CyclicBFSFromRootToRoot()
191 * \param &BFS accounting structure
192 * \param &MinRingSize global minimum distance from one node without encountering oneself, set on return
193 * \param &NumCyles number of cycles in graph
194 */
195void CyclicStructureAnalysis::RetrieveCycleMembers(
196 atom *&OtherAtom,
197 bond::ptr &BackEdge,
198 int &MinRingSize,
199 int &NumCycles)
200{
201 atom *Walker = NULL;
202 int RingSize = -1;
203
204 if (OtherAtom == Root) {
205 // now climb back the predecessor list and thus find the cycle members
206 NumCycles++;
207 RingSize = 1;
208 Root->GetTrueFather()->IsCyclic = true;
209
210 std::stringstream output;
211 output << "Found ring contains: ";
212 Walker = Root;
213 while (Walker != BackEdge->rightatom) {
214 output << Walker->getName() << " <-> ";
215 Walker = PredecessorList[Walker->getNr()];
216 Walker->GetTrueFather()->IsCyclic = true;
217 RingSize++;
218 }
219 output << Walker->getName() << " with a length of " << RingSize << ".";
220 LOG(0, "INFO: " << output.str());
221
222 // walk through all and set MinimumRingSize
223 Walker = Root;
224 if ((MinimumRingSize.count(Walker->GetTrueFather()->getNr()) == 0)
225 || (RingSize < MinimumRingSize[Walker->GetTrueFather()->getNr()])) {
226 MinimumRingSize[Walker->GetTrueFather()->getNr()] = RingSize;
227 } else {
228 LOG(3, "INFO: Not setting MinimumRingSize of "<< *(Walker->GetTrueFather())
229 << " to " << RingSize << " which is already set to "
230 << MinimumRingSize[Walker->GetTrueFather()->getNr()] << ".");
231 }
232 while (Walker != BackEdge->rightatom) { // note that Root is leftatom
233 Walker = PredecessorList[Walker->getNr()];
234 if ((MinimumRingSize.count(Walker->GetTrueFather()->getNr()) == 0)
235 || (RingSize < MinimumRingSize[Walker->GetTrueFather()->getNr()]))
236 MinimumRingSize[Walker->GetTrueFather()->getNr()] = RingSize;
237 }
238 if ((RingSize < MinRingSize) || (MinRingSize == -1))
239 MinRingSize = RingSize;
240 } else {
241 LOG(1, "INFO: No ring containing " << *Root << " with length equal to or smaller than " << MinimumRingSize[Root->GetTrueFather()->getNr()] << " found.");
242 }
243}
244
245/** From a given node performs a BFS to touch the next cycle, for whose nodes \a MinimumRingSize is set and set it accordingly.
246 * \param *&Walker node to look for closest cycle from, i.e. \a MinimumRingSize is set for this node
247 * \param AtomCount number of nodes in graph
248 */
249void CyclicStructureAnalysis::BFSToNextCycle(atom *Walker)
250{
251 atom *Root = Walker;
252 atom *OtherAtom = Walker;
253
254 Reset();
255
256 InitializeToRoot(Walker);
257 while (OtherAtom != NULL) { // look for Root
258 ASSERT(!BFSStack.empty(), "CyclicStructureAnalysis_BFSToNextCycle() - BFSStack is empty!");
259 Walker = BFSStack.front();
260 BFSStack.pop_front();
261 LOG(2, "INFO: Current Walker is " << *Walker << ", BFS-stepping away from Root " << *Root << ".");
262 const BondList& ListOfBonds = Walker->getListOfBonds();
263 for (BondList::const_iterator Runner = ListOfBonds.begin();
264 Runner != ListOfBonds.end();
265 ++Runner) {
266 // "removed (*Runner) != BackEdge) || " from next if, is u
267
268 // only walk along DFS spanning tree (otherwise we always find SP of 1
269 // being backedge Binder), but terminal hydrogens may be connected via
270 // backedge, hence extra check
271// if ((ListOfBonds.size() != 1)) {
272 OtherAtom = (*Runner)->GetOtherAtom(Walker);
273 if ((treatment == IncludeHydrogen) || (OtherAtom->getType()->getAtomicNumber() != 1)) {
274 LOG(2, "INFO: Current OtherAtom is: " << OtherAtom->getName() << " for bond " << *(*Runner) << ".");
275 if (ColorList[OtherAtom->getNr()] == GraphEdge::white) {
276 TouchedStack.push_front(OtherAtom);
277 ColorList[OtherAtom->getNr()] = GraphEdge::lightgray;
278 PredecessorList[OtherAtom->getNr()] = Walker; // Walker is the predecessor
279 ShortestPathList[OtherAtom->getNr()] = ShortestPathList[Walker->getNr()] + 1;
280 LOG(2, "ACCEPT: Coloring OtherAtom "
281 << OtherAtom->getName() << " lightgray, its predecessor is "
282 << Walker->getName() << " and its Shortest Path is "
283 << ShortestPathList[OtherAtom->getNr()] << " egde(s) long.");
284 // distance is a locally optimal criterion (we have eliminated all
285 // cycles already). Hence, we may assume that all set MinimumRingSize
286 // correspond to shortest distances to cycles. I.e., as soon as we reach
287 // as set MinimumRingSize we may use it and the current shortest path
288 // distance to it
289 if (MinimumRingSize.count(OtherAtom->GetTrueFather()->getNr())) {
290 LOG(2, "SUCCESS: Found set MinimumRingSize at " << *OtherAtom
291 << ", walking back to Root " << *Root << ".");
292 // set all predecessors
293 const unsigned int shorttestpath = ShortestPathList[OtherAtom->getNr()];
294 atom *Backwalker = OtherAtom;
295 while (Backwalker != Root) {
296 Backwalker = PredecessorList[Backwalker->getNr()];
297 MinimumRingSize[Backwalker->GetTrueFather()->getNr()] =
298 (shorttestpath - ShortestPathList[Backwalker->getNr()])
299 + MinimumRingSize[OtherAtom->GetTrueFather()->getNr()];
300 LOG(2, "Setting MinimumRingSize of " << *Backwalker << " to "
301 << MinimumRingSize[Backwalker->GetTrueFather()->getNr()] << ".");
302 }
303 OtherAtom = NULL; //break;
304 break;
305 } else
306 BFSStack.push_front(OtherAtom);
307 } else {
308 LOG(3, "REJECT: Not Adding, has already been visited.");
309 }
310 } else {
311 LOG(3, "REJECT: Not Visiting, is a back edge to hydrogen.");
312 }
313// }
314 }
315 ColorList[Walker->getNr()] = GraphEdge::black;
316 LOG(1, "INFO: Coloring Walker " << Walker->getName() << " " << GraphEdge::getColorName(ColorList[Walker->getNr()]) << ".");
317 }
318}
319
320/** All nodes that are not in cycles get assigned a \a *&MinimumRingSize by BFS to next cycle.
321 * \param *&MinimumRingSize array with minimum distance without encountering oneself for each atom
322 * \param MinRingSize global minium distance
323 * \param NumCyles number of cycles in graph
324 */
325void CyclicStructureAnalysis::AssignRingSizetoNonCycleMembers(const int MinRingSize, const int NumCycles)
326{
327 atom *Walker = NULL;
328 if (MinRingSize != -1) { // if rings are present
329 // go over all atoms
330 World::AtomComposite allatoms = World::getInstance().getAllAtoms();
331 for (World::AtomComposite::const_iterator iter = allatoms.begin();
332 iter != allatoms.end();
333 ++iter) {
334 Walker = *iter;
335
336 if (MinimumRingSize.find(Walker->GetTrueFather()->getNr()) == MinimumRingSize.end()) { // check whether MinimumRingSize is set, if not BFS to next where it is
337 LOG(1, "---------------------------------------------------------------------------------------------------------");
338 BFSToNextCycle(Walker);
339 }
340 ASSERT(MinimumRingSize.find(Walker->GetTrueFather()->getNr()) != MinimumRingSize.end(),
341 "CyclicStructureAnalysis::AssignRingSizetoNonCycleMembers() - BFSToNextCycle did not set MinimumRingSize of "
342 +toString(*(Walker->GetTrueFather()))+".");
343 LOG(1, "INFO: Minimum ring size of " << *Walker << " is " << MinimumRingSize[Walker->GetTrueFather()->getNr()] << ".");
344 }
345 LOG(1, "INFO: Minimum ring size is " << MinRingSize << ", over " << NumCycles << " cycle(s) total.");
346 } else
347 LOG(1, "INFO: No rings were detected in the molecular structure.");
348}
349
350/** Analyses the cycles found and returns minimum of all cycle lengths.
351 * We begin with a list of Back edges found during DepthFirstSearchAnalysis(). We go through this list - one end is the Root,
352 * the other our initial Walker - and do a Breadth First Search for the Root. We mark down each Predecessor and as soon as
353 * we have found the Root via BFS, we may climb back the closed cycle via the Predecessors. Thereby we mark atoms and bonds
354 * as cyclic and print out the cycles.
355 * \param *BackEdgeStack stack with all back edges found during DFS scan. Beware: This stack contains the bonds from the total molecule, not from the subgraph!
356 * \todo BFS from the not-same-LP to find back to starting point of tributary cycle over more than one bond
357 */
358void CyclicStructureAnalysis::operator()(std::deque<bond::ptr > * BackEdgeStack)
359{
360 Info FunctionInfo("CyclicStructureAnalysis");
361 atom *Walker = NULL;
362 atom *OtherAtom = NULL;
363 bond::ptr BackEdge;
364 int NumCycles = 0;
365 int MinRingSize = -1;
366
367 {
368 std::stringstream output;
369 output << "Back edge list - ";
370 for (std::deque<bond::ptr >::const_iterator iter = BackEdgeStack->begin();
371 iter != BackEdgeStack->end(); ++iter)
372 output << **iter << " ";
373 LOG(0, output.str());
374 }
375
376 LOG(1, "STATUS: Analysing cycles ... ");
377 NumCycles = 0;
378 while (!BackEdgeStack->empty()) {
379 BackEdge = BackEdgeStack->front();
380 BackEdgeStack->pop_front();
381 // this is the target
382 Root = BackEdge->leftatom;
383 // this is the source point
384 Walker = BackEdge->rightatom;
385
386 InitializeToRoot(Walker);
387
388 LOG(1, "---------------------------------------------------------------------------------------------------------");
389 OtherAtom = NULL;
390 // go to next cycle via BFS
391 CyclicBFSFromRootToRoot(OtherAtom, BackEdge);
392 // get all member nodes of this cycle
393 RetrieveCycleMembers(OtherAtom, BackEdge, MinRingSize, NumCycles);
394
395 CleanAllTouched();
396 }
397 AssignRingSizetoNonCycleMembers(MinRingSize, NumCycles);
398}
399
400/** Output a list of flags, stating whether the bond was visited or not.
401 * \param *list list to print
402 */
403void CyclicStructureAnalysis::OutputAlreadyVisited(int *list)
404{
405 std::stringstream output;
406 output << "Already Visited Bonds:\t";
407 for (int i = 1; i <= list[0]; i++)
408 output << list[i] << " ";
409 LOG(0, output.str());
410}
411
412const std::map<atomId_t, int >& CyclicStructureAnalysis::getMinimumRingSize() const
413{
414 return MinimumRingSize;
415}
Note: See TracBrowser for help on using the repository browser.