00001 #ifndef MST_CLUSTER_MAKER_CPP
00002 #define MST_CLUSTER_MAKER_CPP
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include "MSTClusterMaker.h"
00038 #include "ESTAnalyzer.h"
00039 #include "MSTMultiListCache.h"
00040 #include "MSTHeapCache.h"
00041 #include "EST.h"
00042 #include "MPIStats.h"
00043 #include "HeuristicChain.h"
00044 #include "Heuristic.h"
00045 #include <fstream>
00046 #include <sstream>
00047
00048
00049 #define MANAGER_RANK 0
00050
00051
00052 #define NO_ERROR 0
00053
00054
00055 char DefCacheType[10] = "heap";
00056
00057
00058 int MSTClusterMaker::cacheSize = 128;
00059 bool MSTClusterMaker::noCacheRepop = true;
00060 bool MSTClusterMaker::strictOrder = false;
00061 char* MSTClusterMaker::inputMSTFile = NULL;
00062 char* MSTClusterMaker::outputMSTFile = NULL;
00063 bool MSTClusterMaker::dontCluster = false;
00064 bool MSTClusterMaker::prettyPrint = false;
00065 bool MSTClusterMaker::guiPrint = false;
00066 int MSTClusterMaker::maxUse = -1;
00067 float MSTClusterMaker::clsThreshold = 1.0;
00068 char* MSTClusterMaker::cacheType = DefCacheType;
00069 char* MSTClusterMaker::progFileName = NULL;
00070
00071
00072 arg_parser::arg_record MSTClusterMaker::argsList[] = {
00073 {"--cache", "#similarity metrics to cache per EST",
00074 &MSTClusterMaker::cacheSize, arg_parser::INTEGER},
00075 {"--no-cache-repop", "Suppress EST cache repopulation",
00076 &MSTClusterMaker::noCacheRepop, arg_parser::BOOLEAN},
00077 {"--no-order", "Disable strict order of processing messages",
00078 &MSTClusterMaker::strictOrder, arg_parser::BOOLEAN},
00079 {"--input-mst-file", "Read MST data from file (skip parallel MST building)",
00080 &MSTClusterMaker::inputMSTFile, arg_parser::STRING},
00081 {"--output-mst-file", "Output MST data to file",
00082 &MSTClusterMaker::outputMSTFile, arg_parser::STRING},
00083 {"--dont-cluster", "Just generate MST data. Don't do clustering",
00084 &MSTClusterMaker::dontCluster, arg_parser::BOOLEAN},
00085 {"--pretty-print", "Print a pretty cluster tree.",
00086 &MSTClusterMaker::prettyPrint, arg_parser::BOOLEAN},
00087 {"--gui-print", "Print the cluster tree for GUI processing.",
00088 &MSTClusterMaker::guiPrint, arg_parser::BOOLEAN},
00089 {"--maxUse", "Set a threshold to aggressively use metrics (default=0)",
00090 &MSTClusterMaker::maxUse, arg_parser::INTEGER},
00091 {"--clsThreshold", "Set a threshold for clustering (default=1.0)",
00092 &MSTClusterMaker::clsThreshold, arg_parser::FLOAT},
00093 {"--cacheType", "Set type of cache (heap or mlist) to use (default=heap)",
00094 &MSTClusterMaker::cacheType, arg_parser::STRING},
00095 {"--progress", "Log MST construction progress in a file (used by GUI)",
00096 &MSTClusterMaker::progFileName, arg_parser::STRING},
00097 {NULL, NULL, NULL, arg_parser::BOOLEAN}
00098 };
00099
00100 MSTClusterMaker::MSTClusterMaker(ESTAnalyzer *analyzer,
00101 const int refESTidx,
00102 const std::string& outputFile)
00103 : ClusterMaker("mst", analyzer, refESTidx, outputFile), mst(NULL) {
00104
00105 }
00106
00107 MSTClusterMaker::~MSTClusterMaker() {
00108 if (mst != NULL) {
00109 delete mst;
00110 }
00111 }
00112
00113 void
00114 MSTClusterMaker::showArguments(std::ostream& os) {
00115 ClusterMaker::showArguments(os);
00116
00117 os << "Options for " << name << " are:\n";
00118 arg_parser ap(MSTClusterMaker::argsList);
00119 os << ap;
00120 }
00121
00122 bool
00123 MSTClusterMaker::parseArguments(int& argc, char **argv) {
00124 arg_parser ap(MSTClusterMaker::argsList);
00125 ap.check_args(argc, argv, false);
00126
00127 if (cacheSize < 1) {
00128 std::cerr << "Invalid cache size (must be greater than zero)\n";
00129 return false;
00130 }
00131
00132 strictOrder = !strictOrder;
00133
00134 return ClusterMaker::parseArguments(argc, argv);
00135 }
00136
00137
00138 void
00139 MSTClusterMaker::estAdded(const int estIdx, std::vector<int>& repopulateList) {
00140
00141 sendToWorkers(estIdx, ADD_EST);
00142
00143
00144 cache->pruneCaches(estIdx, repopulateList, false);
00145
00146
00147 const int WorkerCount = MPI_GET_SIZE();
00148 for(int workerID = 1; (workerID < WorkerCount); workerID++) {
00149
00150
00151 MPI_STATUS msgInfo;
00152 MPI_CODE({
00153 const int sourceRank=(strictOrder ? workerID : MPI_ANY_SOURCE);
00154 MPI_PROBE(sourceRank, REPOPULATE_REQUEST, msgInfo);
00155 });
00156
00157
00158 const int dataSize = msgInfo.Get_count(MPI_TYPE_INT);
00159 int *requestData = new int[dataSize];
00160 MPI_RECV(requestData, dataSize, MPI_TYPE_INT,
00161 msgInfo.Get_source(), REPOPULATE_REQUEST);
00162
00163 if (requestData[0] > 0) {
00164 std::copy(requestData + 1, requestData + dataSize - 1,
00165 std::back_inserter(repopulateList));
00166
00167 }
00168
00169 delete [] requestData;
00170 }
00171 }
00172
00173 int
00174 MSTClusterMaker::managerUpdateCaches(int estIdx, const bool refreshEST) {
00175
00176
00177 std::vector<int> repopulateList;
00178 if (estIdx != -1) {
00179 estAdded(estIdx, repopulateList);
00180 }
00181
00182
00183
00184 if (refreshEST) {
00185 repopulateList.push_back(estIdx);
00186 }
00187
00188 for(std::vector<int>::iterator curr = repopulateList.begin();
00189 (curr != repopulateList.end()); curr++) {
00190
00191 sendToWorkers(*curr, COMPUTE_SIMILARITY_REQUEST);
00192
00193 populateCache(*curr);
00194
00195
00196 const int ownerRank = getOwnerProcess(*curr);
00197 if (ownerRank != MANAGER_RANK) {
00198 MPI_CODE({
00199 int dummy;
00200 TRACK_IDLE_TIME(MPI_RECV(&dummy, 1, MPI_TYPE_INT, ownerRank,
00201 SIMILARITY_COMPUTATION_DONE));
00202 });
00203 }
00204 }
00205
00206 return 0;
00207 }
00208
00209 void
00210 MSTClusterMaker::computeNextESTidx(int& parentESTidx, int& estToAdd,
00211 float& similarity, int& alignmentData,
00212 int& directionData) const {
00213
00214
00215 sendToWorkers(-1, COMPUTE_MAX_SIMILARITY_REQUEST);
00216
00217 cache->getBestEntry(parentESTidx, estToAdd, similarity, alignmentData,
00218 directionData);
00219
00220 const int ProcessCount = MPI_GET_SIZE();
00221 for(int rank = 1; (rank < ProcessCount); rank++) {
00222
00223 int remoteData[5] = {0, 0, 0, 0, 0};
00224 MPI_CODE({
00225
00226 const int workerRank = (strictOrder ? rank : MPI_ANY_SOURCE);
00227 TRACK_IDLE_TIME(MPI_RECV(remoteData, 5, MPI_TYPE_INT,
00228 workerRank, MAX_SIMILARITY_RESPONSE));
00229 });
00230
00231 const float remoteSim = *(reinterpret_cast<float*>(remoteData + 2));
00232
00233 if (analyzer->compareMetrics(remoteSim, similarity) ||
00234 (estToAdd == -1)) {
00235
00236
00237 similarity = remoteSim;
00238 parentESTidx = remoteData[0];
00239 estToAdd = remoteData[1];
00240 alignmentData = remoteData[3];
00241 directionData = remoteData[4];
00242 }
00243 }
00244 }
00245
00246 void
00247 MSTClusterMaker::addMoreChildESTs(const int parentESTidx, int& estToAdd,
00248 float &metric, int& alignmentData,
00249 int& directionData, int& pendingESTs) {
00250
00251 int newParent = -1;
00252
00253 do {
00254
00255 ASSERT ( estToAdd != -1 );
00256 mst->addNode(parentESTidx, estToAdd, metric, alignmentData,
00257 directionData);
00258 if (--pendingESTs == 0) {
00259
00260
00261 break;
00262 }
00263
00264
00265 managerUpdateCaches(estToAdd, false);
00266
00267
00268 int newChild = -1;
00269 float childMetric = 0;
00270 int childAlignment = 0;
00271 int childDirection = 0;
00272 computeNextESTidx(newParent, newChild, childMetric, childAlignment,
00273 childDirection);
00274
00275 if ((newParent == parentESTidx) &&
00276 (analyzer->compareMetrics(childMetric, (float) maxUse))) {
00277
00278
00279 estToAdd = newChild;
00280 metric = childMetric;
00281 alignmentData = childAlignment;
00282 directionData = childDirection;
00283 } else {
00284
00285 newParent = -1;
00286 }
00287 } while (newParent == parentESTidx);
00288 }
00289
00290 void
00291 MSTClusterMaker::updateProgress(const int estsAnalyzed,
00292 const int totalESTcount) {
00293 if (progFileName == NULL) {
00294
00295 return;
00296 }
00297
00298 if (!progressFile.is_open()) {
00299 progressFile.open(progFileName);
00300 }
00301
00302 if (progressFile.good()) {
00303 progressFile << estsAnalyzed << "," << totalESTcount
00304 << "\n" << std::flush;
00305 progressFile.seekp(0);
00306 }
00307 }
00308
00309 int
00310 MSTClusterMaker::manager() {
00311
00312 const int TotalESTcount = EST::getESTList().size() -
00313 EST::getProcessedESTCount();
00314 int pendingESTs = TotalESTcount;
00315
00316 int dummy;
00317 mst = new MST(pendingESTs, analyzer->getAlignmentData(dummy));
00318
00319
00320 int parentESTidx = -1;
00321 int estToAdd = refESTidx;
00322
00323
00324 if (EST::getEST(estToAdd)->hasBeenProcessed()) {
00325 std::cerr << "Warning: The reference EST has been filtered out.\n"
00326 << "Trying to select the first non-filtered EST instead.\n";
00327 estToAdd = 0;
00328 while ((estToAdd < EST::getESTCount()) &&
00329 (EST::getEST(estToAdd)->hasBeenProcessed())) {
00330 estToAdd++;
00331 }
00332 if (estToAdd >= EST::getESTCount()) {
00333 std::cerr << "Error: All the ESTs have been filtered out.\n"
00334 << "Nothing left to be clustered.\n";
00335 return 0;
00336 } else {
00337 std::cerr << "Using EST at index " << estToAdd
00338 << " as root.\n";
00339 }
00340 }
00341
00342 float metric = analyzer->getValidMetric();
00343 int alignmentInfo = 0;
00344 int directionInfo = 0;
00345 do {
00346
00347 updateProgress(TotalESTcount - pendingESTs, TotalESTcount);
00348
00349 if ((maxUse == -1) || (parentESTidx == -1)) {
00350 ASSERT ( estToAdd != -1 );
00351 mst->addNode(parentESTidx, estToAdd, metric, alignmentInfo,
00352 directionInfo);
00353 if (--pendingESTs == 0) {
00354
00355
00356 break;
00357 }
00358 }
00359
00360
00361 managerUpdateCaches(estToAdd);
00362
00363
00364
00365 computeNextESTidx(parentESTidx, estToAdd, metric, alignmentInfo,
00366 directionInfo);
00367 ASSERT( parentESTidx != -1 );
00368 ASSERT( estToAdd != -1 );
00369 if (maxUse != -1) {
00370
00371
00372 addMoreChildESTs(parentESTidx, estToAdd, metric,
00373 alignmentInfo, directionInfo, pendingESTs);
00374 }
00375 } while (pendingESTs > 0);
00376
00377
00378 updateProgress(TotalESTcount - pendingESTs, TotalESTcount);
00379
00380
00381 sendToWorkers(-1, ADD_EST);
00382
00383 return NO_ERROR;
00384 }
00385
00386 int
00387 MSTClusterMaker::worker() {
00388
00389
00390
00391 int estAdded = -1;
00392
00393
00394 MPI_STATUS msgInfo;
00395 do {
00396
00397
00398 MPI_PROBE(MANAGER_RANK, MPI_ANY_TAG, msgInfo);
00399 if (msgInfo.Get_tag() == COMPUTE_SIMILARITY_REQUEST) {
00400
00401
00402
00403 int estIdx = -1;
00404 MPI_RECV(&estIdx, 1, MPI_TYPE_INT, MANAGER_RANK,
00405 COMPUTE_SIMILARITY_REQUEST);
00406
00407 populateCache(estIdx);
00408 } else if (msgInfo.Get_tag() == COMPUTE_MAX_SIMILARITY_REQUEST) {
00409
00410
00411
00412 MPI_CODE({
00413 int dummy = 0;
00414 MPI_RECV(&dummy, 1, MPI_TYPE_INT, MANAGER_RANK,
00415 COMPUTE_MAX_SIMILARITY_REQUEST);
00416 });
00417 int bestEntry[5];
00418 float similarity = 0;
00419
00420 cache->getBestEntry(bestEntry[0], bestEntry[1],
00421 similarity, bestEntry[3], bestEntry[4]);
00422
00423
00424
00425 int *temp = reinterpret_cast<int*>(&similarity);
00426 bestEntry[2] = *temp;
00427 MPI_SEND(bestEntry, 5, MPI_TYPE_INT, MANAGER_RANK,
00428 MAX_SIMILARITY_RESPONSE);
00429 } else if (msgInfo.Get_tag() == ADD_EST) {
00430
00431 MPI_RECV(&estAdded, 1, MPI_TYPE_INT, MANAGER_RANK, ADD_EST);
00432 if (estAdded == -1) {
00433
00434
00435 break;
00436 }
00437
00438
00439 std::vector<int> repopulateList;
00440 cache->pruneCaches(estAdded, repopulateList);
00441
00442 MPI_SEND(&repopulateList[0], repopulateList.size(),
00443 MPI_TYPE_INT, MANAGER_RANK, REPOPULATE_REQUEST);
00444 }
00445 } while (estAdded != -1);
00446
00447 return NO_ERROR;
00448 }
00449
00450 int
00451 MSTClusterMaker::getOwnerProcess(const int estIdx) const {
00452 const int ESTsPerProcess = EST::getESTList().size() / MPI_GET_SIZE();
00453 const int ExtraESTs = EST::getESTList().size() % MPI_GET_SIZE();
00454 const int ExtraESTsCutOff= (ExtraESTs * ESTsPerProcess) + ExtraESTs;
00455
00456
00457
00458
00459
00460 if (estIdx < ExtraESTsCutOff) {
00461 return estIdx / (ESTsPerProcess + 1);
00462 }
00463
00464 return (estIdx - ExtraESTs) / ESTsPerProcess;
00465 }
00466
00467 float
00468 MSTClusterMaker::analyze(const int otherEST) {
00469 return analyzer->analyze(otherEST);
00470 }
00471
00472 int
00473 MSTClusterMaker::initialize() {
00474
00475
00476 int result;
00477 if ((result = analyzer->initialize()) != NO_ERROR) {
00478
00479 return result;
00480 }
00481
00482 return NO_ERROR;
00483 }
00484
00485 void
00486 MSTClusterMaker::populateCache(const int estIdx, SMList* metricList) {
00487
00488
00489 int startESTidx, endESTidx;
00490 getOwnedESTidx(startESTidx, endESTidx);
00491
00492
00493 analyzer->setReferenceEST(estIdx);
00494
00495
00496 SMList smList;
00497 const float InvalidMetric = analyzer->getInvalidMetric();
00498
00499 bool needInvalidMetric = true;
00500 for(int otherIdx = startESTidx; (otherIdx < endESTidx); otherIdx++) {
00501 if (cache->isESTinMST(otherIdx) || (otherIdx == estIdx) ||
00502 (EST::getEST(otherIdx)->hasBeenProcessed())) {
00503
00504
00505 continue;
00506 }
00507
00508 const float metric = analyze(otherIdx);
00509 ASSERT ( metric >= 0 );
00510
00511 int alignmentData = 0;
00512 int directionData = 1;
00513 analyzer->getAlignmentData(alignmentData);
00514 HeuristicChain* chain = HeuristicChain::getHeuristicChain();
00515 if (chain != NULL) {
00516 chain->getHint("MST_RC", directionData);
00517 }
00518
00519
00520
00521 const bool isMetricOK= analyzer->compareMetrics(metric, InvalidMetric);
00522 if ((isMetricOK) || (needInvalidMetric)) {
00523
00524 smList.push_back(CachedESTInfo(estIdx, otherIdx,
00525 metric, alignmentData,
00526 directionData));
00527
00528
00529 needInvalidMetric= (needInvalidMetric && isMetricOK);
00530 }
00531 }
00532
00533
00534 cache->preprocess(smList);
00535
00536 const int ownerRank = getOwnerProcess(estIdx);
00537 if (ownerRank != MPI_GET_RANK()) {
00538
00539
00540
00541
00542
00543
00544
00545 if (smList.empty()) {
00546 smList.push_back(CachedESTInfo(-1, -1, -1.0f, -1, -1));
00547 }
00548 MPI_SEND(&smList[0], smList.size() * sizeof(CachedESTInfo),
00549 MPI_TYPE_CHAR, ownerRank, SIMILARITY_LIST);
00550
00551
00552 return;
00553 }
00554
00555
00556
00557
00558 cache->mergeList(estIdx, smList);
00559
00560 if (metricList != NULL) {
00561 metricList->insert(metricList->end(), smList.begin(), smList.end());
00562 }
00563
00564
00565
00566 const int MyRank = MPI_GET_RANK();
00567 for(int pid = 0; (pid < MPI_GET_SIZE()); pid++) {
00568 if (pid == MyRank) {
00569
00570 continue;
00571 }
00572
00573 MPI_STATUS msgInfo;
00574 MPI_CODE({
00575
00576 const int rank = (strictOrder ? pid : MPI_ANY_SOURCE);
00577
00578
00579 MPI_PROBE(rank, SIMILARITY_LIST, msgInfo);
00580 });
00581
00582
00583 const int dataSize = msgInfo.Get_count(MPI_TYPE_CHAR);
00584 SMList remoteList(dataSize / sizeof(CachedESTInfo));
00585
00586
00587
00588 MPI_RECV(&remoteList[0], dataSize, MPI_TYPE_CHAR,
00589 msgInfo.Get_source(), SIMILARITY_LIST);
00590
00591
00592 if (hasValidSMEntry(remoteList)) {
00593 cache->mergeList(estIdx, remoteList);
00594
00595 if (metricList != NULL) {
00596 metricList->insert(metricList->end(), remoteList.begin(),
00597 remoteList.end());
00598 }
00599 }
00600 }
00601
00602
00603
00604 if (MPI_GET_RANK() != MANAGER_RANK) {
00605 MPI_CODE({
00606 const int dummy = -1;
00607 MPI_SEND(&dummy, 1, MPI_TYPE_INT, MANAGER_RANK,
00608 SIMILARITY_COMPUTATION_DONE);
00609 });
00610 }
00611 }
00612
00613 void
00614 MSTClusterMaker::getOwnedESTidx(int& startIndex, int& endIndex) {
00615 const int ESTsPerProcess = EST::getESTList().size() / MPI_GET_SIZE();
00616 const int ExtraESTs = EST::getESTList().size() % MPI_GET_SIZE();
00617 const int MyRank = MPI_GET_RANK();
00618
00619
00620
00621 startIndex = MyRank * ESTsPerProcess;
00622
00623 if (MyRank <= ExtraESTs) {
00624
00625
00626
00627 startIndex = ((ESTsPerProcess + 1) * MyRank);
00628 } else {
00629 startIndex += ExtraESTs;
00630 }
00631
00632
00633 endIndex = startIndex + ESTsPerProcess;
00634 if (MyRank < ExtraESTs) {
00635
00636
00637 endIndex++;
00638 }
00639 }
00640
00641 void
00642 MSTClusterMaker::displayStats(std::ostream& os) {
00643
00644 cache->displayStats(os, MPI_GET_RANK());
00645
00646 MPIStats::displayStats(os);
00647 }
00648
00649 int
00650 MSTClusterMaker::populateMST() {
00651 if (inputMSTFile != NULL) {
00652
00653
00654 if ((mst = MST::deSerialize(inputMSTFile)) == NULL) {
00655
00656 return 2;
00657 }
00658 }
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668 int result = NO_ERROR;
00669 int startESTidx, endESTidx;
00670 getOwnedESTidx(startESTidx, endESTidx);
00671
00672 if (!strcmp(cacheType, "mlist")) {
00673 cache = new MSTMultiListCache(EST::getESTList().size(), startESTidx,
00674 endESTidx - startESTidx, analyzer,
00675 !noCacheRepop, cacheSize);
00676 } else {
00677 cache = new MSTHeapCache(EST::getESTList().size(), startESTidx,
00678 endESTidx - startESTidx, analyzer,
00679 !noCacheRepop, cacheSize);
00680 }
00681 ASSERT ( cache != NULL );
00682
00683 if (MPI_GET_RANK() == MANAGER_RANK) {
00684
00685 result = manager();
00686 } else {
00687
00688
00689 result = worker();
00690 }
00691
00692 return result;
00693 }
00694
00695 int
00696 MSTClusterMaker::addDummyCluster(const std::string name) {
00697 MSTCluster *dummy = new MSTCluster(&root, name);
00698 root.add(dummy);
00699
00700 return dummy->getClusterID();
00701 }
00702
00703 void
00704 MSTClusterMaker::addEST(const int clusterID, const int estIdx) {
00705
00706 MSTNode node(-1, estIdx, analyzer->getInvalidMetric());
00707 root.add(clusterID, node);
00708
00709 EST::getEST(estIdx)->setProcessed(true);
00710 }
00711
00712 int
00713 MSTClusterMaker::buildAndShowClusters() {
00714
00715 root.makeClusters(mst->getNodes(), analyzer, clsThreshold);
00716
00717
00718 std::ofstream outFile;
00719 if (!outputFileName.empty()) {
00720 outFile.open(outputFileName.c_str());
00721 if (!outFile.good()) {
00722 std::cerr << "Error opening output file " << outputFileName
00723 << " for writing cluster data.\n"
00724 << "Cluster data will be dumped on stdout.\n";
00725 }
00726 }
00727
00728 std::ostream& dest = (outFile.is_open() && outFile.good()) ?
00729 outFile : std::cout;
00730 if (prettyPrint) {
00731 root.printClusterTree(dest);
00732 } else if (guiPrint) {
00733 root.guiPrintClusterTree(dest, analyzer->getInputFileName());
00734 } else {
00735
00736 dest << root << std::endl;
00737 }
00738
00739 return NO_ERROR;
00740 }
00741
00742
00743 int
00744 MSTClusterMaker::makeClusters() {
00745 int result = NO_ERROR;
00746
00747
00748 result = populateMST();
00749
00750
00751
00752
00753
00754 std::ostringstream buffer;
00755 displayStats(buffer);
00756 std::cout << buffer.str() << std::endl;
00757
00758 delete cache;
00759
00760 if ((result != NO_ERROR) || (mst == NULL)) {
00761
00762 return result;
00763 }
00764
00765 ASSERT ( mst != NULL );
00766
00767 if ((outputMSTFile != NULL) && (mst != NULL)) {
00768 mst->serialize(outputMSTFile, (inputMSTFile != NULL) ? inputMSTFile :
00769 analyzer->getInputFileName(), clsThreshold);
00770 }
00771
00772
00773 if (!dontCluster) {
00774 result = buildAndShowClusters();
00775 }
00776
00777 return result;
00778 }
00779
00780 void
00781 MSTClusterMaker::sendToWorkers(int data, const int tag) const {
00782 const int ProcessCount = MPI_GET_SIZE();
00783 for(int rank = 1; (rank < ProcessCount); rank++) {
00784 MPI_SEND(&data, 1, MPI_TYPE_INT, rank, tag);
00785 }
00786 }
00787
00788 bool
00789 MSTClusterMaker::hasValidSMEntry(const SMList& list) const {
00790 if (list.empty()) {
00791
00792
00793 return false;
00794 }
00795 if (list.size() > 1) {
00796
00797
00798 return true;
00799 }
00800 if (list[0].estIdx == -1) {
00801
00802
00803 return false;
00804 }
00805
00806 return true;
00807 }
00808
00809 #endif