The Conley-Morse Graphs Software
coord.h
Go to the documentation of this file.
1/////////////////////////////////////////////////////////////////////////////
2///
3/// @file coord.h
4///
5/// The coordinator class for the Conley-Morse graphs computation program.
6/// This file contains the definition of the coordinator class which prepares
7/// data for workers (chunks of the parameter range) and collects results
8/// of their computations.
9///
10/// @author Pawel Pilarczyk
11///
12/////////////////////////////////////////////////////////////////////////////
13
14// Copyright (C) 1997-2022 by Pawel Pilarczyk.
15//
16// This file is part of my research software package. This is free software:
17// you can redistribute it and/or modify it under the terms of the GNU
18// General Public License as published by the Free Software Foundation,
19// either version 3 of the License, or (at your option) any later version.
20//
21// This software is distributed in the hope that it will be useful,
22// but WITHOUT ANY WARRANTY; without even the implied warranty of
23// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24// GNU General Public License for more details.
25//
26// You should have received a copy of the GNU General Public License
27// along with this software; see the file "license.txt". If not,
28// please, see <https://www.gnu.org/licenses/>.
29
30// Started on February 11, 2008. Last revision: March 29, 2022.
31
32
33#ifndef _CMGRAPHS_COORD_H_
34#define _CMGRAPHS_COORD_H_
35
36
37// include some standard C++ header files
38#include <string>
39#include <iostream>
40#include <fstream>
41#include <sstream>
42#include <cctype>
43#include <vector>
44
45// include selected header files from the CHomP library
46#include "chomp/system/config.h"
47#include "chomp/system/textfile.h"
48#include "chomp/system/timeused.h"
49#include "chomp/cubes/cube.h"
50#include "chomp/multiwork/mw.h"
51
52// include local header files
53#include "config.h"
54#include "confinfo.h"
55#include "typedefs.h"
56#include "typedyns.h"
57#include "utils.h"
58#include "dataconv.h"
59#include "datavector.h"
60#include "matcharr.h"
61#include "dotgraph.h"
62#include "mapopt.h"
63
64
65// --------------------------------------------------
66// ------------- the coordinator class --------------
67// --------------------------------------------------
68
69/// The coordinator class which prepares chunks of parameter space
70/// to be processed by workers, and then incorporates the results
71/// of the computations into the final picture, and also saves the results
72/// to a log file.
73class Coordinator: public chomp::multiwork::mwCoordinator
74{
75public:
76 /// The complete constructor of a coordinator.
77 Coordinator (const char *_interFileName,
78 const char *_mapOptFileName, const char *_mapOptPrefix,
79 const char *_graphsFileName, const char *_graphsPrefix,
80 const char *_finalPrefix, const char *_sharePrefix,
81 const char *_phaseSpacePrefix, const char *_cubesPrefix,
82 const char *_morseDecPrefix,
83 const char *_procPrefix, bool _fullPhaseSpace,
84 int _skipIndices, int _nPatches);
85
86 /// The destructor.
87 ~Coordinator ();
88
89private:
90 /// A function for preparing data by the coordinator.
91 int Prepare (chomp::multiwork::mwData &data);
92
93 /// A function for accepting results by the coordinator.
94 int Accept (chomp::multiwork::mwData &data);
95
96 /// A function for taking rejected data by the coordinator.
97 int Reject (chomp::multiwork::mwData &data);
98
99 // The copy constructor is not allowed.
100// Coordinator (const Coordinator &) {return;}
101
102 // The assignment operator is not allowed.
103// Coordinator &operator = (const Coordinator &) {return *this;}
104
105private:
106 /// This is an auxiliary class whose objects store the information
107 /// on the data chunks sent to workers.
108 struct datapack
109 {
110 /// The default constructor of an uninitialized data pack.
112
113 /// A nice constructor which initializes all the data.
114 datapack (int _num, const parCube &_box):
115 num (_num), box (_box) {}
116
117 // the default copy constructor and operator = are good
118
119 // no destructor is necessary
120
121 /// The number of the data pack.
122 int num;
123
124 /// The cube (with respect to the iterator) sent to a worker.
126
127 }; /* struct datapack */
128
129
130private:
131 /// A procedure for reading the previously computed results.
132 void readPreviousResults (const char *filename);
133
134 /// A file to append the results to.
135 std::ofstream interFile;
136
137 /// A file to append the optimization results to.
138 std::string mapOptFileName;
139
140 /// A file name prefix for optimization data for each parameter.
141 std::string mapOptPrefix;
142
143 /// Is the graphs file in use?
145
146 /// A file to append the computed Conley-Morse graphs to.
147 std::ofstream graphsFile;
148
149 /// The current number of data pack sent for pre-caching indices.
151
152 /// The number of indices to pre-cache.
154
155 /// The number of pre-cached indices waiting to complete.
157
158 /// The coordinates of a box to send for pre-caching indices.
160
161 /// The current number of data pack to prepare by the coordinator.
163
164 /// The number of parameter regions which are to be processed
165 /// in this run of the program.
167
168 /// The data packs sent for processing to workers.
169 chomp::homology::multitable<datapack> sent;
170
171 /// The number of data packs currently being processed by workers.
173
174 /// The minimal size of a Morse set for which the indices should not
175 /// be computed. Zero to compute all the Conley indices.
177
178 /// The file name prefix for saving the text Conley-Morse graphs.
179 std::string graphsPrefix;
180
181 /// The file name prefix for saving the final result.
182 std::string finalPrefix;
183
184 /// The file name prefix for saving Morse sets by workers.
185 std::string sharePrefix;
186
187 /// The file name prefix for saving the phase space pictures
188 /// of Morse decompositions.
189 std::string phaseSpacePrefix;
190
191 /// The file name prefix for saving the actual cubes of Morse sets
192 /// and possibly also additional sets (while post-processing).
193 std::string cubesPrefix;
194
195 /// The file name prefix for cached compressed Morse decompositions.
196 std::string morseDecPrefix;
197
198 /// The file name prefix for saving post-processing information.
199 std::string procPrefix;
200
201 /// Should the full phase space be taken for plotting the pictures
202 /// of Morse sets?
204
205 /// The coordinates for lower (and upper) ends of rectangular regions
206 /// in each direction.
207 std::vector<parCoord> subCoords [paramDim];
208
209 /// The minimal corner of the rectangle to iterate (zero coords).
211
212 /// The numbers of rectangular regions in each direction to iterate.
214
215 /// A rectangle which iterates the rectangular sets to send.
217
218 /// Parity bits of which rectangles are to be sent to workers.
220
221 /// Have all the rectangular regions already been sent?
223
224 /// The array of matching between parameter boxes.
226
227 /// The total number of computed Morse decopositions.
229
230 /// The total number of re-used Morse decompositions.
232
233 /// The parameter boxes for which wrong Conley indices
234 /// were computed by workers.
236
237 /// The parameter boxes for which the Conley-Morse graph
238 /// has already been written to the corresponding file.
240
241 /// The boxes representing parameter ranges which have already been
242 /// processed in the previous run of the coordinator.
244
245 /// The lists of Morse set numbers for which the wrong Conley indices
246 /// appeared.
247 chomp::homology::multitable<std::vector<int> > wrongIndices;
248
249 /// The left coordinates of a box that contains all the Morse sets.
251
252 /// The right coordinates of a box that contains all the Morse sets.
254
255 /// The maximal image diameter encountered by the workers.
257
258 /// The maximal image volume encountered by the workers.
260
261 /// Map optimization data for all the parameters.
263
264}; /* class Coordinator */
265
266// --------------------------------------------------
267
268inline Coordinator::Coordinator (const char *_interFileName,
269 const char *_mapOptFileName, const char *_mapOptPrefix,
270 const char *_graphsFileName, const char *_graphsPrefix,
271 const char *_finalPrefix, const char *_sharePrefix,
272 const char *_phaseSpacePrefix, const char *_cubesPrefix,
273 const char *_morseDecPrefix,
274 const char *_procPrefix, bool _fullPhaseSpace,
275 int _skipIndices, int _nPatches):
276 usingGraphsFile (false),
277 currentCache (0), maxCache (0), waitingCache (0),
278 current (0), maxCurrent (0), sentlen (0),
279 skipIndices (_skipIndices), fullPhaseSpace (_fullPhaseSpace),
280 rectIterator (0), allSent (false),
281 matchArray (paramDim, paramSubdiv),
282 morseDecComputed (0), morseDecReused (0),
283 maxImgDiam (0), maxImgVol (0)
284{
285 using chomp::homology::sout;
286
287 // remember a file name prefix for optimization data for each parameter
288 if (_mapOptPrefix && *_mapOptPrefix)
289 mapOptPrefix = _mapOptPrefix;
290
291 // remember the prefix for saving Morse sets
292 if (_sharePrefix && *_sharePrefix)
293 sharePrefix = _sharePrefix;
294
295 // remember the prefix for saving pictures of Morse decompositions
296 if (_phaseSpacePrefix && *_phaseSpacePrefix)
297 phaseSpacePrefix = _phaseSpacePrefix;
298
299 // remember the prefix for saving Morse sets as list of cubes
300 if (_cubesPrefix && *_cubesPrefix)
301 cubesPrefix = _cubesPrefix;
302
303 // remember the prefix for saving Morse sets as list of cubes
304 if (_morseDecPrefix && *_morseDecPrefix)
305 morseDecPrefix = _morseDecPrefix;
306
307 // remember the prefix for saving post-processing information
308 if (_procPrefix && *_procPrefix)
309 procPrefix = _procPrefix;
310
311 // display a warning if no intermediate file name is provided
312 if (!_interFileName || !*_interFileName)
313 {
314 sout << "WARNING: No file name for intermediate results "
315 "has been provided.\n"
316 " It will not be possible to restart "
317 "the computations if they are interrupted.\n";
318 }
319
320 // display a warning if no file name for saving graphs was given
321 if ((!_graphsFileName || !*_graphsFileName) &&
322 (!_graphsPrefix || !*_graphsPrefix))
323 {
324 sout << "WARNING: No file name for saving the Conley-Morse "
325 "graphs has been provided.\n"
326 " The computed Conley-Morse graphs "
327 "Will be discarded.\n";
328 }
329
330 // remember the prefix for file names to which the final result
331 // will be saved
332 if (_finalPrefix && *_finalPrefix)
333 finalPrefix = _finalPrefix;
334 else
335 throw "No file name prefix provided for the results.";
336
337 // read the previously computed results of the computations
338 if (_interFileName && *_interFileName)
339 this -> readPreviousResults (_interFileName);
340
341 // read the previously computed results of the computations
342 if (_mapOptFileName && *_mapOptFileName)
343 {
344 mapOptFileName = std::string (_mapOptFileName);
345 std::ifstream in (_mapOptFileName);
346 size_t prevSize (mapOptimization. size ());
347 if (in)
348 {
349 sout << "Reading map optimization data... ";
350 mapOptimization. loadData (in);
351 sout << (mapOptimization. size () - prevSize) <<
352 " items added.\n";
353 }
354 }
355
356 // prepare a rectangle for iterating the rectangular regions
358 subCoords, _nPatches);
359
360 // prepare the parity bits
361 for (int i = 0; i < paramDim; ++ i)
362 parityBits [i] = 0;
363
364 // compute the number of regions which will be sent for processing
365 maxCurrent = 1;
366 for (int i = 0; i < paramDim; ++ i)
367 maxCurrent *= maxIter [i];
368
369 // open the graphs file for appending
370 if (_graphsFileName && *_graphsFileName)
371 {
372 graphsFile. open (_graphsFileName,
373 std::ios::out | std::ios::app);
374 graphsFile << "; --- Conley-Morse graphs ---\n";
375 graphsFile << std::flush;
376 usingGraphsFile = true;
377 }
378
379 // remember the prefix for graph file names
380 if (_graphsPrefix && *_graphsPrefix)
381 graphsPrefix = _graphsPrefix;
382
383 // open the results file for appending
384 if (_interFileName && *_interFileName)
385 {
386 interFile. open (_interFileName,
387 std::ios::out | std::ios::app);
388 interFile << "; Started on " <<
389 chomp::homology::currenttime () << ";\n";
391 interFile << std::flush;
392 }
393
394 // make sure that the range of spce coordinates will not be exhausted
395 if (finalDepth + refineDepth + 2 > 8 * sizeof(spcCoord))
396 {
397 sout << "Serious problem: finalDepth (" <<
398 finalDepth << ") + refineDepth (" << refineDepth <<
399 ") = " << (finalDepth + refineDepth) << ".\n";
400 sout << "However, the space coordinates are " <<
401 (8 * sizeof(spcCoord)) << "-bit integers.\n";
402 sout << "Please, modify 'typespace.h': the 'spcCoord' type "
403 "must have at least " <<
404 (finalDepth + refineDepth + 2) << " bits.\n";
405 throw "Not enough bits available in spcCoord.";
406 }
407
408 // initialize the coordinate ranges
409 for (int i = 0; i < spaceDim; ++ i)
410 {
411 coordLeftMin [i] = 1 << finalDepth;
412 coordRightMax [i] = 0;
413 }
414
415 // initialize the data for pre-computation of cached Conley indices
416#ifndef CONFIG_ONLYPREPROCESSING
417 if (!sharePrefix. empty ())
418#endif
419 {
420 for (int i = 0; i < paramDim; ++ i)
421 cacheCoord [i] = 0;
422#ifndef CONFIG_NOPREPROCESSING
423 maxCache = 1;
424 for (int i = 0; i < paramDim; ++ i)
425 maxCache *= paramSubdiv [i];
427#endif
428 }
429
430 return;
431} /* Coordinator::Coordinator */
432
433inline void Coordinator::readPreviousResults (const char *filename)
434{
435 using chomp::homology::sout;
436 using chomp::homology::ignoreline;
437 using chomp::homology::ignorecomments;
438
439 std::ifstream in (filename);
440 if (!in)
441 {
442 sout << "Note: Could not open the results file. "
443 "Any previous results will be ignored.\n";
444 return;
445 }
446
447 int countClasses = 0;
448 int countBoxes = 0;
449 ignorecomments (in);
450 while (!in. eof ())
451 {
452 // add an equivalence class to the array of connections
453 if (in. peek () == 'E')
454 {
455 // read the letter indicating an equivalence class
456 in. get ();
457 ignorecomments (in);
458
459 // read and ignore the number of cubes in the class
460 if (std::isdigit (in. peek ()))
461 {
462 int nCubes = 0;
463 in >> nCubes;
464 ignorecomments (in);
465 }
466
467 // read the first cube in the equivalence class
468 parCube firstCube;
469 in >> firstCube;
470 parCoord firstCoord [paramDim];
471 firstCube. coord (firstCoord);
472 ignorecomments (in);
473
474 // read all the remaining cubes
475 while (in. peek () == '(')
476 {
477 parCube otherCube;
478 in >> otherCube;
479 parCoord otherCoord [paramDim];
480 otherCube. coord (otherCoord);
481 matchArray. join (firstCoord, otherCoord);
482 ignorecomments (in);
483 }
484
485 // update the class counter
486 ++ countClasses;
487 }
488
489 // add a box to the list of previously processed boxes
490 else if (in. peek () == '*')
491 {
492 // read the asterisk
493 in. get ();
494 ignorecomments (in);
495
496 // read and ignore the datapack number
497 if (std::isdigit (in. peek ()))
498 {
499 int num = 0;
500 in >> num;
501 ignorecomments (in);
502 }
503
504 // read the cube which has already been processed
505 parCube paramBox;
506 in >> paramBox;
507 previousBoxes. add (paramBox);
508 ignorecomments (in);
509
510 // update the box counter
511 ++ countBoxes;
512 }
513
514 // ignore the line if it is not understood
515 else
516 {
517 ignoreline (in);
518 ignorecomments (in);
519 }
520 }
521
522 chomp::homology::sout << countBoxes << " previously computed "
523 "parameter ranges read from a file.\n";
524 maxCurrent -= countBoxes;
525 chomp::homology::sout << countClasses << " previously computed "
526 "equivalence classes processed.\n";
527 return;
528} /* Coordinator::readPreviousResults */
529
531{
532 // remove the rectangle iterator
533 if (rectIterator)
534 delete rectIterator;
535
536 // write remaining map optimization data if any
537 if (!mapOptFileName. empty ())
538 {
539 std::ofstream mapOptFile;
540 mapOptFile. open (mapOptFileName. c_str (),
541 std::ios::out | std::ios::app);
542 mapOptimization. saveData (mapOptFile, true);
543 }
544
545 // close the intermediate results file
546 interFile << "; Finished on " << chomp::homology::currenttime ();
547 interFile. close ();
548
549 return;
550} /* Coordinator::~Coordinator */
551
552inline int Coordinator::Prepare (chomp::multiwork::mwData &data)
553{
554 using chomp::homology::sout;
555 using chomp::homology::sbug;
556 using chomp::homology::timeused;
557
558 // measure working time
559 timeused workingTime;
560 workingTime = 0;
561
562 // read a list of bad boxes which must be skipped, if available
563 parCubes badBoxes;
564 std::ifstream in ("_badboxes.cub");
565 in >> badBoxes;
566 if (!badBoxes. empty ())
567 sbug << badBoxes. size () << " bad boxes will be skipped.\n";
568 in. close ();
569
570 // show a message about the first phase of computations
571 if (!currentCache && maxCache)
572 sout << "=== PHASE 1: Computation of Conley indices ===\n";
573
574 // skip all single boxes whose "ind", "png" and "bz2" files
575 // already exist
576 while (currentCache < maxCache)
577 {
578 // skip the box if it is bad
579 bool skip = false;
580 if (!badBoxes. empty ())
581 {
583 if (badBoxes. check (p))
584 {
585 skip = true;
586 sbug << "Skipping bad parameter box " << p << ".\n";
587 -- waitingCache;
588 }
589 }
590
591 // if the cached indices file does not exist then send it
592 if (!skip && !sharePrefix. empty ())
593 {
594 std::string cacheFileName (sharePrefix +
595 coord2str (cacheCoord, paramDim) + ".ind");
596 if (!fileExists (cacheFileName. c_str ()))
597 break;
598 }
599
600 // if the phase space picture does not exist then send it
601 if (!skip && (!phaseSpacePrefix. empty () && (spaceDim == 2)))
602 {
603 std::string pictureFileName = phaseSpacePrefix +
604 coord2str (cacheCoord, paramDim) + ".png";
605 if (!fileExists (pictureFileName. c_str ()))
606 break;
607 }
608
609 // if the cached Morse dec file does not exist then send it
610 if (!skip && !morseDecPrefix. empty ())
611 {
612 std::string morseDecFileName (morseDecPrefix +
613 coord2str (cacheCoord, paramDim) + ".bz2");
614 if (!fileExists (morseDecFileName. c_str ()))
615 break;
616 }
617
618 // if preprocessing only was requested then send the point
619 // even if all the prefixes are empty
620#ifdef CONFIG_ONLYPREPROCESSING
621 if (!skip)
622 break;
623#endif
624
625 // take the next point
626 for (int i = 0; i < paramDim; ++ i)
627 {
628 if (++ (cacheCoord [i]) < paramSubdiv [i])
629 break;
630 cacheCoord [i] = 0;
631 }
632 ++ currentCache;
633
634 // show a message about the second phase of computations
635 if (currentCache == maxCache)
636 {
637 sout << "=== PHASE 2: Continuation of Morse "
638 "decompositions ===\n";
639 }
640 }
641
642#ifdef CONFIG_SEPARATEPHASES
643 // if Phase 1 must be fully completed before Phase 2 can begin
645 {
646 sbug << "[Note: waiting for " << waitingCache << " indices to be cached.]\n";
647 return chomp::multiwork::mwNoData;
648 }
649#endif
650
651 // decide if a single box for pre-caching of indices should be sent
652 bool cacheToSend = (currentCache < maxCache);
653
654 // determine the data pack to send if any
655 const parCoord *coord = 0;
656#ifndef CONFIG_ONLYPREPROCESSING
657 while (!cacheToSend && !allSent)
658 {
659 // take the next available set of coordinates
660 coord = rectIterator -> get ();
661
662 // if no new coordinates are available...
663 if (!coord)
664 {
665 // increase the parity bits
666 int nBit = 0;
667 while ((nBit < paramDim) && (parityBits [nBit] == 1))
668 parityBits [nBit ++] = 0;
669
670 // quit if all the parity bits have been used
671 if (nBit == paramDim)
672 {
673 allSent = true;
674 break;
675 }
676
677 // increase the bit that was found
678 parityBits [nBit] = 1;
679
680 // rewind the iterator of box coordinates
681 coord = rectIterator -> get ();
682 }
683
684 // check if the coordinates satisfy the parity condition
685 bool parityOk = true;
686 for (int i = 0; i < paramDim; ++ i)
687 {
688 if (parityBits [i] && (coord [i] & 1))
689 continue;
690 if (!parityBits [i] && !(coord [i] & 1))
691 continue;
692 parityOk = false;
693 break;
694 }
695
696 // if this box has already been processed before then skip it
697 if (parityOk && !previousBoxes. empty () &&
698 previousBoxes. check (parCube (coord, paramDim)))
699 {
700 continue;
701 }
702
703 // if the parity condition is satisfied then break the loop
704 if (parityOk)
705 break;
706 }
707#endif
708
709 // save the final results if this is the end of computations.
710#ifndef CONFIG_ONLYPREPROCESSING
711 if (allSent && !sentlen && !finalPrefix. empty ())
712#else
713 if (!sentlen)
714#endif
715 {
716 sout << "================================\n";
717
718 // show a warning about wrong Conley indices
719 if (!wrongIndexBoxes. empty ())
720 {
721 sout << "\n*** WARNING: Some Conley indices at " <<
722 wrongIndexBoxes. size () <<
723 " parameters were not computed\n"
724 "*** because of lack of isolation.\n\n";
725 if (!finalPrefix. empty ())
726 {
727 std::ofstream bad ((finalPrefix + ".bad").
728 c_str ());
729 bad << "; Parameter boxes for which "
730 "at least one Conley index "
731 "could not be computed:\n";
732 bad << wrongIndexBoxes;
733 }
734 }
735
736 // show a summary on the number of computed Morse decomps
737 int volume = 1;
738 for (int i = 0; i < paramDim; ++ i)
739 volume *= paramSubdiv [i];
740 int percent = static_cast<int>
741 (morseDecComputed * 100.0 / volume);
742 sout << morseDecComputed << " out of " <<
743 volume << " Morse decomps computed (" <<
744 percent << "%) and " << morseDecReused <<
745 " reused.\n";
746
747 // show the range of cubes in the Morse sets
749 sout << "The computed Morse sets are in " <<
750 coordMinMax << ".\n";
751 sout << "Real coordinates: ";
752 showRealCoords (sout, coordMinMax) << ".\n";
753
754 // show the max image diameter and volume encountered
755 sout << "Max img diam = " << maxImgDiam <<
756 ", max img vol = " << maxImgVol << ".\n";
757
758 // save the continuation classes to files
759 sout << "Saving the continuation classes...\n";
760 matchArray. saveSets (finalPrefix. c_str ());
761 }
762
763 // if all data has already been sent then return no data
764 if (allSent)
765 {
766 // show working time if the work took a lot of time
767 if (static_cast<double> (workingTime) > 1.0)
768 {
769 sbug << "It took " << workingTime << " to "
770 "see that there is no data to send.\n";
771 }
772
773 return chomp::multiwork::mwNoData;
774 }
775
776#ifdef CONFIG_ONLYPREPROCESSING
777 // if preprocessing only was requested, no more work is necessary
778 if (!cacheToSend)
779 return chomp::multiwork::mwNoData;
780#endif
781
782 // create a data pack with the current box
783 parCube dataBox (cacheToSend ? cacheCoord : coord, paramDim);
784 int dataNumber = cacheToSend ? -(currentCache + 1) : current;
785 sent [sentlen ++] = datapack (dataNumber, dataBox);
786
787 // prepare the parameter coordinate ranges to send
788 int leftCoords [paramDim];
789 int rightCoords [paramDim];
790 for (int i = 0; i < paramDim; ++ i)
791 {
792 if (cacheToSend)
793 {
794 leftCoords [i] = cacheCoord [i];
795 rightCoords [i] = cacheCoord [i] + 1;
796 }
797 else
798 {
799 // prepare the range of coordinates to process
800 // and add an overlap margin if necessary
801 int left = subCoords [i] [coord [i]];
802 int right = subCoords [i] [coord [i] + 1];
803 if (right < paramSubdiv [i])
804 ++ right;
805 leftCoords [i] = left;
806 rightCoords [i] = right;
807 }
808 }
809
810 // prepare map optimization data
811 parCoord parLeft [paramDim];
812 parCoord parRight [paramDim];
813 for (int i = 0; i < paramDim; ++ i)
814 {
815 parLeft [i] = static_cast<parCoord> (leftCoords [i]);
816 parRight [i] = static_cast<parCoord> (rightCoords [i]);
817 }
818 std::vector<std::vector<std::string> > mapOptLines;
819 parRect rect (parLeft, parRight, paramDim);
820 const parCoord *curCoord;
821 int curNumber = 0;
822 while ((curCoord = rect. get ()) != 0)
823 {
824 // prepare the actual parameters for this parameter box
825 double leftMapParam [paramCount];
826 double rightMapParam [paramCount];
827 computeParam (curCoord, leftMapParam, rightMapParam);
828
829 // prepare map optimization data for these parameters
830 std::vector<std::string> data;
831 mapOptLines. push_back (data);
832 mapOptimization. prepareData (mapOptLines [curNumber],
833 leftMapParam, rightMapParam, paramCount);
834
835 // increase the counter of the current optimization data set
836 ++ curNumber;
837 }
838
839 // encode the data to send
840 data. Reset ();
841 data << dataNumber;
842 data << paramDim;
843 for (int i = 0; i < paramDim; ++ i)
844 data << leftCoords [i] << rightCoords [i];
845 data << skipIndices;
846 data << mapOptPrefix;
847 data << sharePrefix;
848 data << phaseSpacePrefix;
849 data << cubesPrefix;
850 data << morseDecPrefix;
851 data << graphsPrefix;
852 data << procPrefix;
853 data << fullPhaseSpace;
854 data << mapOptLines;
855 data << controlNumber;
856
857 // save the information on this data to the results file
858 if (!cacheToSend)
859 {
860 interFile << "+ " << dataNumber << " " << dataBox << "\n" <<
861 std::flush;
862 }
863
864 // show a message on what is being sent
865 // and indicate the percentage of work done
866 sout << "Sending " << dataNumber << " " << dataBox;
867 if (!cacheToSend)
868 {
869 for (int i = 0; i < paramDim; ++ i)
870 {
871 sout << (i ? " x [" : ": [") << leftCoords [i] <<
872 "," << rightCoords [i] << "]";
873 }
874 }
875 int countSent = cacheToSend ? currentCache : current;
876 int maxToSend = cacheToSend ? maxCache : maxCurrent;
877 sout << ", " << (100 * (countSent + 1) /
878 (maxToSend ? maxToSend : 1)) << "% sent.\n";
879
880 if (cacheToSend)
881 {
882 // increase the counter of the current box
883 for (int i = 0; i < paramDim; ++ i)
884 {
885 if (++ (cacheCoord [i]) < paramSubdiv [i])
886 break;
887 cacheCoord [i] = 0;
888 }
889 ++ currentCache;
890
891 // show a message about the second phase of computations
892 if (currentCache == maxCache)
893 {
894 sout << "=== PHASE 2: Continuation of Morse "
895 "decompositions ===\n";
896 }
897 }
898 else
899 {
900 ++ current;
901 }
902
903 // show working time if the work took considerable amount of time
904 if (static_cast<double> (workingTime) > 1.0)
905 {
906 sbug << "It took " << workingTime <<
907 " to prepare new data.\n";
908 }
909 return chomp::multiwork::mwOk;
910} /* Coordinator::Prepare */
911
912inline int Coordinator::Accept (chomp::multiwork::mwData &data)
913{
914 using chomp::homology::sout;
915 using chomp::homology::sbug;
916 using chomp::homology::timeused;
917
918 // measure working time
919 timeused workingTime;
920 workingTime = 0;
921
922 // decode the number
923 int dataNumber = 0;
924 data >> dataNumber;
925
926 // show a message informing about the reception of the data
927 // and indicate the percentage of work done
928 int countSent = sentlen;
929 int countPrepared = (dataNumber >= 0) ? current : currentCache;
930 int maxPrepared = (dataNumber >= 0) ? maxCurrent : maxCache;
931 for (int i = 0; i < sentlen; ++ i)
932 {
933 if ((dataNumber >= 0) && (sent [i]. num < 0))
934 -- countSent;
935 else if ((dataNumber < 0) && (sent [i]. num >= 0))
936 -- countSent;
937 }
938 sout << "Receiving " << dataNumber << ", " <<
939 (100 * (countPrepared - countSent + 1) /
940 (maxPrepared ? maxPrepared : 1)) << "% completed.\n";
941
942 // if this was a result of pre-computation then count it
943 if (dataNumber < 0)
944 -- waitingCache;
945
946 // find the data chunk that corresponds to this number
947 int pos = 0;
948 while ((pos < sentlen) && (sent [pos]. num != dataNumber))
949 ++ pos;
950 if (pos >= sentlen)
951 throw "Wrong data chunk number returned by a worker.";
952
953 // make a note in the log file that this result has been received
954 if (dataNumber >= 0)
955 {
956 interFile << "* " << dataNumber << " " <<
957 sent [pos]. box << "\n";
958 }
959
960 // retrieve the time of processing this entire chunk of boxes
961 double processingTime = 0;
962 data >> processingTime;
963
964 // acquire the numbers of Morse sets with wrong Conley indices
965 std::vector<std::vector<int> > localWrongIndices;
966 data >> localWrongIndices;
967
968 // acquire the numbers of Morse sets for which the Conley index
969 // computation was skipped
970 std::vector<std::vector<int> > skippedIndices;
971 data >> skippedIndices;
972
973 // acquire the numbers of Morse sets which are attractors
974 std::vector<std::vector<int> > attractors;
975 data >> attractors;
976
977 // acquire the ranges of coordinates
978 spcCube cubeLeftMin;
979 spcCube cubeRightMax;
980 data >> cubeLeftMin;
981 data >> cubeRightMax;
982 spcCoord localLeftMin [spaceDim];
983 spcCoord localRightMax [spaceDim];
984 cubeLeftMin. coord (localLeftMin);
985 cubeRightMax. coord (localRightMax);
986 for (int i = 0; i < spaceDim; ++ i)
987 {
988 if (coordLeftMin [i] > localLeftMin [i])
989 coordLeftMin [i] = localLeftMin [i];
990 if (coordRightMax [i] < localRightMax [i])
991 coordRightMax [i] = localRightMax [i];
992 }
993
994 // determine the number of processed boxes
995 int countBoxes = 0;
996 data >> countBoxes;
997
998 // acquire data for each box
999 bool graphsOutput = false;
1000 parCubes paramBoxes;
1001 for (int curNumber = 0; curNumber < countBoxes; ++ curNumber)
1002 {
1003 // determine the actual box coordinates
1004 parCube paramBox;
1005 data >> paramBox;
1006 paramBoxes. add (paramBox);
1007 if (paramBoxes. size () <= curNumber)
1008 throw "Repeated box found in the results.";
1009
1010 // retrieve the time used to process this box
1011 double timeUsed = 0;
1012 data >> timeUsed;
1013
1014 // was this Morse decomposition computed by this worker?
1015 bool wasComputed = false;
1016 data >> wasComputed;
1017 if (wasComputed)
1019 else
1020 ++ morseDecReused;
1021
1022 // decode the Morse graph
1023 chomp::homology::diGraph<> morseGraph;
1024 data >> morseGraph;
1025
1026 // decode the sizes of each Morse set
1027 int nSets = morseGraph. countVertices ();
1028 std::vector<int_t> sizes (nSets);
1029 for (int n = 0; n < nSets; ++ n)
1030 data >> sizes [n];
1031
1032 // make a note of wrong Conley indices if any
1033 if (!localWrongIndices [curNumber]. empty ())
1034 {
1035 wrongIndices [wrongIndexBoxes. size ()] =
1036 localWrongIndices [curNumber];
1037 wrongIndexBoxes. add (paramBox);
1038 }
1039
1040 // decode the Conley indices and the eigenvalues
1041 std::vector<theConleyIndexType> indices (nSets);
1042 std::vector<IndexEigenValues> eigenValues (nSets);
1043 for (int n = 0; n < nSets; ++ n)
1044 {
1045 // decode the Morse index of the n-th Morse set
1046 data >> indices [n];
1047
1048 // decode the sets of nonzero eigenvalues
1049 data >> eigenValues [n];
1050 }
1051
1052 // write the Conley-Morse graph for the given box
1053 if (((usingGraphsFile || !graphsPrefix. empty ())) &&
1054 !graphsWritten. check (paramBox))
1055 {
1056 // prepare a string containing the text graph
1057 std::ostringstream dotGraphStream;
1058 writeDotGraph (dotGraphStream, morseGraph, sizes,
1059 indices, eigenValues,
1060 localWrongIndices [curNumber],
1061 skippedIndices [curNumber],
1062 attractors [curNumber]);
1063 std::string dotGraph (dotGraphStream. str ());
1064
1065 // write the text graph to the main graph file
1066 if (usingGraphsFile)
1067 {
1068 graphsFile << paramBox << " " << dotGraph <<
1069 "\n";
1070 graphsOutput = true;
1071 }
1072
1073 // write the text graph to a single graph file
1074 // NOTE: This is already contained in the
1075 // "computeMorseDecomposition" procedure.
1076 if (false && !graphsPrefix. empty ())
1077 {
1078 std::string graphFileName = graphsPrefix +
1079 coord2str (paramBox) + ".txt";
1080 if (!fileExists (graphFileName. c_str ()))
1081 {
1082 std::ofstream graphFile
1083 (graphFileName. c_str ());
1084 graphFile << dotGraph << "\n";
1085 graphFile. close ();
1086 }
1087 }
1088
1089 // remember that this graph has been written
1090 graphsWritten. add (paramBox);
1091 }
1092 }
1093 if (paramBoxes. empty ())
1094 {
1095 sbug << "Results for no boxes accepted.\n";
1096 }
1097 else if (paramBoxes. size () == 1)
1098 {
1099 sbug << "Results for one box (processed in " <<
1100 processingTime << " sec) accepted.\n";
1101 }
1102 else
1103 {
1104 sbug << "Results for " << paramBoxes. size () << " boxes "
1105 "(processed in " << processingTime <<
1106 " sec) accepted.\n";
1107 }
1108
1109 // acquire data regarding the continuation relation between the boxes
1110 std::vector<std::vector<parCube> > matchClasses;
1111 data >> matchClasses;
1112 int nClasses = matchClasses. size ();
1113 for (int n = 0; n < nClasses; ++ n)
1114 {
1115 // determine the number of boxes in the equivalence class
1116 const std::vector<parCube> &theClass = matchClasses [n];
1117 int nBoxes = theClass. size ();
1118 if (nBoxes < 2)
1119 continue;
1120 interFile << "E " << nBoxes;
1121
1122 // prepare the coordinates of the first box in the class
1123 parCoord coordZero [paramDim];
1124 theClass [0]. coord (coordZero);
1125 interFile << " " << theClass [0];
1126
1127 // make a note of all the other boxes being equivalent
1128 for (int i = 1; i < nBoxes; ++ i)
1129 {
1130 interFile << " " << theClass [i];
1131 parCoord coordBox [paramDim];
1132 theClass [i]. coord (coordBox);
1133 matchArray. join (coordZero, coordBox);
1134 }
1135 interFile << "\n";
1136 }
1137
1138 // retrieve computed max image diameter and volume
1139 int localMaxImgDiam = 0;
1140 int localMaxImgVol = 0;
1141 data >> localMaxImgDiam;
1142 data >> localMaxImgVol;
1143 if (maxImgDiam < localMaxImgDiam)
1144 maxImgDiam = localMaxImgDiam;
1145 if (maxImgVol < localMaxImgVol)
1146 maxImgVol = localMaxImgVol;
1147
1148 // retrieve map optimization data
1149 std::vector<std::string> mapOptLines;
1150 data >> mapOptLines;
1151 if (!mapOptLines. empty ())
1152 {
1153 // add the map optimization data to the aggregate structure
1154 for (size_t n = 0; n < mapOptLines. size (); ++ n)
1155 {
1156 if (!mapOptLines [n]. empty ())
1157 {
1158 mapOptimization. addData (mapOptLines [n],
1159 true);
1160 }
1161 }
1162
1163 // save new map optimization data if any
1164 if (!mapOptFileName. empty ())
1165 {
1166 std::ofstream mapOptFile;
1167 mapOptFile. open (mapOptFileName. c_str (),
1168 std::ios::out | std::ios::app);
1169 mapOptimization. saveData (mapOptFile, true);
1170 }
1171 }
1172
1173 // remove the data from the table containing the data that was sent
1174 -- sentlen;
1175 if (pos != sentlen)
1176 sent [pos] = sent [sentlen];
1177
1178 // indicate the number of equivalence classes acquired.
1179// sbug << nClasses << " equivalence class" <<
1180// ((nClasses == 1) ? "" : "es") << " acquired.\n";
1181
1182 // flush the output files
1183 if (dataNumber >= 0)
1184 interFile << std::flush;
1185 if (graphsOutput)
1186 graphsFile << std::flush;
1187
1188 // show working time if the work took considerable amount of time
1189 if (static_cast<double> (workingTime) > 1.0)
1190 {
1191 sbug << "It took " << workingTime <<
1192 " to accept the data.\n";
1193 }
1194
1195 return chomp::multiwork::mwOk;
1196} /* Coordinator::Accept */
1197
1198inline int Coordinator::Reject (chomp::multiwork::mwData &/*data*/)
1199{
1200 using chomp::homology::sout;
1201 sout << "WARNING: Data rejected by a worker.\n";
1202 return chomp::multiwork::mwError;
1203} /* Coordinator::Reject */
1204
1205
1206#endif // _CMGRAPHS_COORD_H_
1207
const int controlNumber
The control number that is used to confirm the compatibility between the coordinator and workers.
Definition: c_differ.h:79
A class whose objects store, update and show coordinate ranges.
Definition: utils.h:444
The coordinator class which prepares chunks of parameter space to be processed by workers,...
Definition: coord.h:74
int current
The current number of data pack to prepare by the coordinator.
Definition: coord.h:162
int maxCache
The number of indices to pre-cache.
Definition: coord.h:153
std::string morseDecPrefix
The file name prefix for cached compressed Morse decompositions.
Definition: coord.h:196
std::string sharePrefix
The file name prefix for saving Morse sets by workers.
Definition: coord.h:185
std::string finalPrefix
The file name prefix for saving the final result.
Definition: coord.h:182
int sentlen
The number of data packs currently being processed by workers.
Definition: coord.h:172
std::ofstream interFile
A file to append the results to.
Definition: coord.h:135
MapOptimization mapOptimization
Map optimization data for all the parameters.
Definition: coord.h:262
parCoord zeroIter[paramDim]
The minimal corner of the rectangle to iterate (zero coords).
Definition: coord.h:210
parCubes previousBoxes
The boxes representing parameter ranges which have already been processed in the previous run of the ...
Definition: coord.h:243
chomp::homology::multitable< datapack > sent
The data packs sent for processing to workers.
Definition: coord.h:169
int waitingCache
The number of pre-cached indices waiting to complete.
Definition: coord.h:156
int Accept(chomp::multiwork::mwData &data)
A function for accepting results by the coordinator.
Definition: coord.h:912
int maxImgDiam
The maximal image diameter encountered by the workers.
Definition: coord.h:256
int maxCurrent
The number of parameter regions which are to be processed in this run of the program.
Definition: coord.h:166
int maxImgVol
The maximal image volume encountered by the workers.
Definition: coord.h:259
int currentCache
The current number of data pack sent for pre-caching indices.
Definition: coord.h:150
void readPreviousResults(const char *filename)
A procedure for reading the previously computed results.
Definition: coord.h:433
std::string phaseSpacePrefix
The file name prefix for saving the phase space pictures of Morse decompositions.
Definition: coord.h:189
chomp::homology::multitable< std::vector< int > > wrongIndices
The lists of Morse set numbers for which the wrong Conley indices appeared.
Definition: coord.h:247
int morseDecComputed
The total number of computed Morse decopositions.
Definition: coord.h:228
bool allSent
Have all the rectangular regions already been sent?
Definition: coord.h:222
parCubes wrongIndexBoxes
The parameter boxes for which wrong Conley indices were computed by workers.
Definition: coord.h:235
std::string cubesPrefix
The file name prefix for saving the actual cubes of Morse sets and possibly also additional sets (whi...
Definition: coord.h:193
parCoord cacheCoord[paramDim]
The coordinates of a box to send for pre-caching indices.
Definition: coord.h:159
MatchArray< parCoord, int > matchArray
The array of matching between parameter boxes.
Definition: coord.h:225
parCoord parityBits[paramDim]
Parity bits of which rectangles are to be sent to workers.
Definition: coord.h:219
std::string graphsPrefix
The file name prefix for saving the text Conley-Morse graphs.
Definition: coord.h:179
std::ofstream graphsFile
A file to append the computed Conley-Morse graphs to.
Definition: coord.h:147
std::string mapOptPrefix
A file name prefix for optimization data for each parameter.
Definition: coord.h:141
std::string procPrefix
The file name prefix for saving post-processing information.
Definition: coord.h:199
int Prepare(chomp::multiwork::mwData &data)
A function for preparing data by the coordinator.
Definition: coord.h:552
std::string mapOptFileName
A file to append the optimization results to.
Definition: coord.h:138
bool fullPhaseSpace
Should the full phase space be taken for plotting the pictures of Morse sets?
Definition: coord.h:203
bool usingGraphsFile
Is the graphs file in use?
Definition: coord.h:144
Coordinator(const char *_interFileName, const char *_mapOptFileName, const char *_mapOptPrefix, const char *_graphsFileName, const char *_graphsPrefix, const char *_finalPrefix, const char *_sharePrefix, const char *_phaseSpacePrefix, const char *_cubesPrefix, const char *_morseDecPrefix, const char *_procPrefix, bool _fullPhaseSpace, int _skipIndices, int _nPatches)
The complete constructor of a coordinator.
Definition: coord.h:268
parCubes graphsWritten
The parameter boxes for which the Conley-Morse graph has already been written to the corresponding fi...
Definition: coord.h:239
~Coordinator()
The destructor.
Definition: coord.h:530
spcCoord coordRightMax[spaceDim]
The right coordinates of a box that contains all the Morse sets.
Definition: coord.h:253
int morseDecReused
The total number of re-used Morse decompositions.
Definition: coord.h:231
parCoord maxIter[paramDim]
The numbers of rectangular regions in each direction to iterate.
Definition: coord.h:213
std::vector< parCoord > subCoords[paramDim]
The coordinates for lower (and upper) ends of rectangular regions in each direction.
Definition: coord.h:207
int Reject(chomp::multiwork::mwData &data)
A function for taking rejected data by the coordinator.
Definition: coord.h:1198
spcCoord coordLeftMin[spaceDim]
The left coordinates of a box that contains all the Morse sets.
Definition: coord.h:250
int skipIndices
The minimal size of a Morse set for which the indices should not be computed.
Definition: coord.h:176
parRect * rectIterator
A rectangle which iterates the rectangular sets to send.
Definition: coord.h:216
A class whose objects are responsible for adjusting the integration parameters such as step size and ...
Definition: mapopt.h:59
Choice of configuration settings.
Writing configuration settings to an output stream.
void showConfigInfo(std::ostream &f, const char *prefix, const parCoord *maxIter)
Writes the configuration information to the given output stream.
Definition: confinfo.h:53
Data conversion for sending/receiving.
Data conversion for sending/receiving: std::vectors and hashed sets of any objects.
Writing a graph in the "dot" format.
std::ostream & writeDotGraph(std::ostream &out, const chomp::homology::diGraph<> &g, const std::vector< int_t > &sizes, const std::vector< theConleyIndexType > &indices, const std::vector< IndexEigenValues > &eigenValues, const std::vector< int > &wrongIndices, const std::vector< int > &skippedIndices, const std::vector< int > &attractors)
Writes the given Conley-Morse graph to the output stream in the format for the 'dot' program.
Definition: dotgraph.h:61
Map optimization data saving, retrieving, and sending.
Array for matching Morse decompositions.
const int refineDepth
The number of refinements that should be done if a Morse set with the trivial index is encountered or...
Definition: p_differ.h:139
const int paramDim
The dimension of the parameter space to iterate.
Definition: p_differ.h:67
const int spaceDim
The dimension of the phase space.
Definition: p_differ.h:48
const int paramCount
The number of all the parameters, both varying and fixed.
Definition: p_differ.h:82
const int finalDepth
The final depth of subdivisions in the phase space.
Definition: p_differ.h:58
const short int paramSubdiv[paramDim]
The numbers of subintervals in each direction of the parameter space.
Definition: p_differ.h:71
This is an auxiliary class whose objects store the information on the data chunks sent to workers.
Definition: coord.h:109
int num
The number of the data pack.
Definition: coord.h:122
parCube box
The cube (with respect to the iterator) sent to a worker.
Definition: coord.h:125
datapack(int _num, const parCube &_box)
A nice constructor which initializes all the data.
Definition: coord.h:114
datapack()
The default constructor of an uninitialized data pack.
Definition: coord.h:111
Customizable data types for the Conley-Morse graphs computation program.
Data types for the dynamical systems data structures.
chomp::homology::tCubeFix< paramDim, parCoord > parCube
The type of a cube in the set of parameters.
Definition: typeparam.h:58
short int parCoord
The type of coordinates of cubes in the set of parameters.
Definition: typeparam.h:53
chomp::homology::tRectangle< parCoord > parRect
The type of a rectangle used to iterate sets of cubes of parameters.
Definition: typeparam.h:64
chomp::homology::hashedset< parCube > parCubes
The type of a set of cubes in the set of parameters.
Definition: typeparam.h:61
int spcCoord
The type of coordinates of cubes in the phase space.
Definition: typespace.h:50
chomp::homology::tCubeBase< spcCoord > spcCube
The type of a cube in the phase space.
Definition: typespace.h:55
Utilites and helper functions.
OutStream & showRealCoords(OutStream &out, const CoordMinMax &range)
Shows the real coordinates of the coordinate range.
Definition: utils.h:543
bool fileExists(const char *filename)
Returns 'true' iff the given file exists and it is allowed to read it.
Definition: utils.h:69
parRect * subRectangles(parCoord *zeroIter, parCoord *maxIter, std::vector< parCoord > *subCoords, int minCount)
Determines heuristically an optimal subdivision of a large parameter region into the given minimal nu...
Definition: utils.h:213
std::string coord2str(const intType1 *coords, const intType2 *maxCoords, int dim)
Generates an underscore-separated list of non-negative coordinates padded with zeros to the same widt...
Definition: utils.h:386
void computeParam(const intType *curCoord, double *leftMapParam, double *rightMapParam)
Computes the real coordinates of the parameter cube which corresponds to the given box.
Definition: utils.h:160