1 /*
2 ================================================================================
3 PROJECT:
4
5 John Eddy's Genetic Algorithms (JEGA)
6
7 CONTENTS:
8
9 Implementation of class MaxDesignsNichePressureApplicator.
10
11 NOTES:
12
13 See notes of MaxDesignsNichePressureApplicator.hpp.
14
15 PROGRAMMERS:
16
17 John Eddy (jpeddy@sandia.gov) (JE)
18
19 ORGANIZATION:
20
21 Sandia National Laboratories
22
23 COPYRIGHT:
24
25 See the LICENSE file in the top level JEGA directory.
26
27 VERSION:
28
29 2.7.0
30
31 CHANGES:
32
33 Wed Dec 21 16:25:44 2011 - Original Version (JE)
34
35 ================================================================================
36 */
37
38
39
40
41 /*
42 ================================================================================
43 Document This File
44 ================================================================================
45 */
46 /** \file
47 * \brief Contains the implementation of the MaxDesignsNichePressureApplicator
48 * class.
49 */
50
51
52
53
54 /*
55 ================================================================================
56 Includes
57 ================================================================================
58 */
59 // JEGAConfig.hpp should be the first include in all JEGA files.
60 #include <../Utilities/include/JEGAConfig.hpp>
61
62 #include <FitnessRecord.hpp>
63 #include <GeneticAlgorithmSelector.hpp>
64 #include <../Utilities/include/Logging.hpp>
65 #include <../Utilities/include/DesignGroup.hpp>
66 #include <utilities/include/EDDY_DebugScope.hpp>
67 #include <../Utilities/include/ParameterExtractor.hpp>
68 #include <utilities/include/RandomNumberGenerator.hpp>
69 #include <../Utilities/include/MultiObjectiveStatistician.hpp>
70 #include <../MOGA/include/NichePressureApplicators/MaxDesignsNichePressureApplicator.hpp>
71
72
73
74
75
76
77
78 /*
79 ================================================================================
80 Namespace Using Directives
81 ================================================================================
82 */
83 using namespace std;
84 using namespace JEGA::Logging;
85 using namespace JEGA::Utilities;
86 using namespace eddy::utilities;
87
88
89
90
91
92
93 /*
94 ================================================================================
95 Begin Namespace
96 ================================================================================
97 */
98 namespace JEGA {
99 namespace Algorithms {
100
101
102
103
104
105 /*
106 ================================================================================
107 Static Member Data Definitions
108 ================================================================================
109 */
110 const double MaxDesignsNichePressureApplicator::DEFAULT_DIST_PCT(0.01);
111 const size_t MaxDesignsNichePressureApplicator::DEFAULT_MAX_DESIGNS(100);
112
113
114
115
116
117
118
119 /*
120 ================================================================================
121 Mutators
122 ================================================================================
123 */
124
125 void
SetDistancePercentages(const JEGA::DoubleVector & pcts)126 MaxDesignsNichePressureApplicator::SetDistancePercentages(
127 const JEGA::DoubleVector& pcts
128 )
129 {
130 EDDY_FUNC_DEBUGSCOPE
131
132 const std::size_t nof = this->GetDesignTarget().GetNOF();
133
134 JEGAIFLOG_CF_II(nof < pcts.size(), this->GetLogger(), lquiet(), this,
135 text_entry(lquiet(),
136 this->GetName() + ": Received more percentages than there are "
137 "objective functions. Extras will be ignored.")
138 )
139
140 JEGAIFLOG_CF_II(nof > pcts.size() && pcts.size() > 1, this->GetLogger(),
141 lquiet(), this, ostream_entry(lquiet(),
142 this->GetName() + ": Received fewer percentages (") << pcts.size()
143 << ") than there are objective functions (" << nof << "). "
144 "Using default value of " << DEFAULT_DIST_PCT << " to fill in."
145 )
146
147 JEGAIFLOG_CF_II(nof > pcts.size() && pcts.size() == 1, this->GetLogger(),
148 lquiet(), this, ostream_entry(lquiet(),
149 this->GetName() + ": Received a single distance percentage for a ")
150 << nof << " objective function problem. Using the supplied value "
151 "of " << pcts[0] << " for all objectives."
152 )
153
154 this->_distPcts = pcts;
155
156 const double fill_val =
157 (this->_distPcts.size() == 1) ? this->_distPcts[0] : DEFAULT_DIST_PCT;
158
159 if(nof > this->_distPcts.size())
160 this->_distPcts.resize(
161 static_cast<JEGA::DoubleVector::size_type>(nof), fill_val
162 );
163
164 // now go through and set each one individually so that they can be checked
165 // for legitimacy.
166 for(JEGA::DoubleVector::size_type i=0; i<nof; ++i)
167 this->SetDistancePercentage(i, this->_distPcts[i]);
168 }
169
170 void
SetDistancePercentages(double pct)171 MaxDesignsNichePressureApplicator::SetDistancePercentages(
172 double pct
173 )
174 {
175 EDDY_FUNC_DEBUGSCOPE
176
177 JEGA_LOGGING_IF_ON(
178 static const double minPct = std::numeric_limits<double>::min();
179 )
180
181 const std::size_t nof = this->GetDesignTarget().GetNOF();
182
183 JEGAIFLOG_CF_II(pct < 0.0, this->GetLogger(), lquiet(), this,
184 ostream_entry(lquiet(),
185 this->GetName() + ": Distance percentages must be at least ")
186 << minPct << " Supplied value of " << pct
187 << " will be replaced by the minimum."
188 )
189
190 JEGAIFLOG_CF_II(pct > 1.0, this->GetLogger(), lquiet(), this,
191 ostream_entry(lquiet(),
192 this->GetName() + ": Distance percentages cannot exceed 100%. "
193 "Supplied value of ") << pct << " will be replaced by 100%."
194 )
195
196 pct = Math::Max(0.0, Math::Min(pct, 1.0));
197
198 this->_distPcts.assign(
199 static_cast<JEGA::DoubleVector::size_type>(nof), pct
200 );
201
202 JEGALOG_II(this->GetLogger(), lverbose(), this,
203 ostream_entry(lverbose(),
204 this->GetName() + ": All distance percentages now = ") << pct
205 )
206 }
207
208 void
SetDistancePercentage(std::size_t of,double pct)209 MaxDesignsNichePressureApplicator::SetDistancePercentage(
210 std::size_t of,
211 double pct
212 )
213 {
214 EDDY_FUNC_DEBUGSCOPE
215
216 JEGA_LOGGING_IF_ON(
217 static const double minPct = std::numeric_limits<double>::min();
218 )
219
220 const DesignTarget& target = GetDesignTarget();
221 const std::size_t nof = target.GetNOF();
222 const JEGA::DoubleVector::size_type dvsof =
223 static_cast<JEGA::DoubleVector::size_type>(of);
224
225 // make sure we have enough locations in the percentages vector.
226 this->_distPcts.resize(
227 static_cast<JEGA::DoubleVector::size_type>(nof), DEFAULT_DIST_PCT
228 );
229
230 // now verify the supplied objective function index.
231 JEGAIFLOG_CF_II_F(of >= nof, this->GetLogger(), this,
232 ostream_entry(lfatal(),
233 this->GetName() + ": Request to change objective with index #")
234 << of << ". Valid indices are 0 through " << (nof-1) << "."
235 )
236
237 // now verify the supplied value.
238 JEGAIFLOG_CF_II(pct < 0.0, this->GetLogger(), lquiet(), this,
239 ostream_entry(lquiet(),
240 this->GetName() + ": Distance percentages must be at least ")
241 << minPct << " Supplied value of " << pct << " for objective \""
242 << target.GetObjectiveFunctionInfos()[dvsof]->GetLabel()
243 << "\" will be replaced by the minimum."
244 )
245
246 JEGAIFLOG_CF_II(pct < 0.0, this->GetLogger(), lquiet(), this,
247 ostream_entry(lquiet(),
248 this->GetName() + ": Distance percentages cannot exceed 100%. "
249 "Supplied value of ") << pct << " for objective \""
250 << target.GetObjectiveFunctionInfos()[dvsof]->GetLabel()
251 << "\" will be replaced by 100%."
252 )
253
254 pct = Math::Max(0.0, Math::Min(pct, 1.0));
255
256 this->_distPcts[dvsof] = pct;
257
258 JEGALOG_II(this->GetLogger(), lverbose(), this,
259 ostream_entry(lverbose(),
260 this->GetName() + ": Distance for objective \"")
261 << target.GetObjectiveFunctionInfos()[dvsof]->GetLabel()
262 << "\" now = " << pct << "."
263 )
264 }
265
266 void
SetMaximumDesigns(std::size_t maxDesigns)267 MaxDesignsNichePressureApplicator::SetMaximumDesigns(
268 std::size_t maxDesigns
269 )
270 {
271 EDDY_FUNC_DEBUGSCOPE
272
273 this->_maxDesigns = maxDesigns;
274
275 JEGALOG_II(this->GetLogger(), lverbose(), this,
276 ostream_entry(lverbose(),
277 this->GetName() + ": Maximum designs now = "
278 ) << this->_maxDesigns
279 )
280 }
281
282
283
284
285
286
287
288
289 /*
290 ================================================================================
291 Accessors
292 ================================================================================
293 */
294
295
296
297 /*
298 ================================================================================
299 Public Methods
300 ================================================================================
301 */
302
303 const string&
Name()304 MaxDesignsNichePressureApplicator::Name(
305 )
306 {
307 EDDY_FUNC_DEBUGSCOPE
308 static const string ret("max_designs");
309 return ret;
310 }
311
312 const string&
Description()313 MaxDesignsNichePressureApplicator::Description(
314 )
315 {
316 EDDY_FUNC_DEBUGSCOPE
317
318 static const string ret(
319 "This niche pressure applicator is designed to choose a limited "
320 "number of solutions to remain in the population. It does so in "
321 "order to balance the tendency for populations to grow very large "
322 "and thus consuming too many computer resources. It operates by "
323 "ranking designs according to their fitness standing and a "
324 "computed count of how many other designs are too close to them. Too "
325 "close is a function of the supplied niche_vector. Once the designs "
326 "are all ranked, the top max_designs designs are kept in the "
327 "population and the remaining ones are buffered or discarded "
328 "depending on the value of the cache_niched_designs flag. Note that "
329 "like other niching operators, this one will not discard an extreme "
330 "design."
331 );
332 return ret;
333 }
334
335 GeneticAlgorithmOperator*
Create(GeneticAlgorithm & algorithm)336 MaxDesignsNichePressureApplicator::Create(
337 GeneticAlgorithm& algorithm
338 )
339 {
340 EDDY_FUNC_DEBUGSCOPE
341 return new MaxDesignsNichePressureApplicator(algorithm);
342 }
343
344
345
346
347
348
349
350 /*
351 ================================================================================
352 Subclass Visible Methods
353 ================================================================================
354 */
355
356
357 JEGA::DoubleVector
ComputeCutoffDistances(const eddy::utilities::extremes<obj_val_t> & exts) const358 MaxDesignsNichePressureApplicator::ComputeCutoffDistances(
359 const eddy::utilities::extremes<obj_val_t>& exts
360 ) const
361 {
362 EDDY_FUNC_DEBUGSCOPE
363
364 typedef eddy::utilities::extremes<obj_val_t> extremes_t;
365
366 // the cutoff distance is a percentage of the range of the objective
367 // considering only the non-dominated designs.
368 const std::size_t nof = this->GetDesignTarget().GetNOF();
369
370 JEGAIFLOG_CF_II_F(nof != exts.size(), GetLogger(), this,
371 ostream_entry(lfatal(), this->GetName() + ": Extremes contain "
372 "record of ") << exts.size() << " objectives for an "
373 << nof << " objective problem."
374 )
375
376 // Prepare a vector for return.
377 JEGA::DoubleVector ret(nof);
378
379 for(extremes_t::size_type i=0; i<nof; ++i)
380 ret[i] = Math::Abs(
381 this->GetDistancePercentage(i) * exts.get_range(i)
382 );
383
384 // return the square route of the sum of squares.
385 return ret;
386 }
387
388 double
ComputeObjectiveDistance(const Design & des1,const Design & des2,size_t of)389 MaxDesignsNichePressureApplicator::ComputeObjectiveDistance(
390 const Design& des1,
391 const Design& des2,
392 size_t of
393 )
394 {
395 EDDY_FUNC_DEBUGSCOPE
396 return Math::Abs(des1.GetObjective(of) - des2.GetObjective(of));
397 }
398
399
400
401
402
403
404 /*
405 ================================================================================
406 Subclass Overridable Methods
407 ================================================================================
408 */
409
410
411 string
GetName() const412 MaxDesignsNichePressureApplicator::GetName(
413 ) const
414 {
415 EDDY_FUNC_DEBUGSCOPE
416 return MaxDesignsNichePressureApplicator::Name();
417 }
418
419 string
GetDescription() const420 MaxDesignsNichePressureApplicator::GetDescription(
421 ) const
422 {
423 EDDY_FUNC_DEBUGSCOPE
424 return MaxDesignsNichePressureApplicator::Description();
425 }
426
427 GeneticAlgorithmOperator*
Clone(GeneticAlgorithm & algorithm) const428 MaxDesignsNichePressureApplicator::Clone(
429 GeneticAlgorithm& algorithm
430 ) const
431 {
432 EDDY_FUNC_DEBUGSCOPE
433 return new MaxDesignsNichePressureApplicator(*this, algorithm);
434 }
435
436 bool
PollForParameters(const ParameterDatabase & db)437 MaxDesignsNichePressureApplicator::PollForParameters(
438 const ParameterDatabase& db
439 )
440 {
441 EDDY_FUNC_DEBUGSCOPE
442
443 bool success = ParameterExtractor::GetDoubleVectorFromDB(
444 db, "method.jega.niche_vector", this->_distPcts
445 );
446
447 // If we did not find the distance percentages, warn about it and use the
448 // default values. Note that if !success, then _distPcts has not been
449 // altered.
450 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
451 text_entry(lverbose(), this->GetName() + ": The distance percentages "
452 "were not found in the parameter database. Using the current "
453 "values.")
454 )
455
456 this->SetDistancePercentages(this->_distPcts);
457
458 success = ParameterExtractor::GetSizeTypeFromDB(
459 db, "method.jega.max_designs", this->_maxDesigns
460 );
461
462 // If we did not find the maximum number of designs, warn about it and move
463 // on to trying the population size. Note that if !success, then
464 // _maxDesigns has not been altered.
465 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
466 text_entry(lverbose(), this->GetName() + ": The maximum post niching "
467 "design count was not found in the parameter database. Attempting "
468 "to find the population size to use it.")
469 )
470
471 if(!success) success = ParameterExtractor::GetSizeTypeFromDB(
472 db, "method.population_size", this->_maxDesigns
473 );
474
475 // If we did not find the population size either, warn about it and move
476 // on to using the default size. Note that if !success, then
477 // _maxDesigns has not been altered.
478 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
479 text_entry(lverbose(), this->GetName() + ": The population size "
480 "was not found in the parameter database either. Using the "
481 "default value for the maximum post niching design count.")
482 )
483
484 this->SetMaximumDesigns(this->_maxDesigns);
485
486 return this->GeneticAlgorithmNichePressureApplicator::PollForParameters(db);
487 }
488
489 void
PreSelection(DesignGroup & population)490 MaxDesignsNichePressureApplicator::PreSelection(
491 DesignGroup& population
492 )
493 {
494 EDDY_FUNC_DEBUGSCOPE
495
496 // if we are not caching designs, we needn't do anything here.
497 if(!this->GetCacheDesigns()) return;
498
499 // Synchronize the lists just in case.
500 population.SynchronizeOFAndDVContainers();
501
502 JEGA_LOGGING_IF_ON(
503 const DesignOFSortSet::size_type initPSize = population.SizeOF();
504 )
505
506 // Re-assimilate the buffered designs into the population so that they
507 // can be considered when making the initial selection. We will cull
508 // them out again when ApplyNichePressure is called later if appropriate.
509 this->ReAssimilateBufferedDesigns(population);
510
511 JEGALOG_II(this->GetLogger(), lverbose(), this,
512 ostream_entry(lverbose(), this->GetName() + ": Returned ")
513 << (population.SizeOF() - initPSize) << " designs during "
514 "pre-selection phase of niche pressure application."
515 )
516 }
517
518
519 void
ApplyNichePressure(DesignGroup & population,const FitnessRecord & fitnesses)520 MaxDesignsNichePressureApplicator::ApplyNichePressure(
521 DesignGroup& population,
522 const FitnessRecord& fitnesses
523 )
524 {
525 EDDY_FUNC_DEBUGSCOPE
526
527 JEGALOG_II(GetLogger(), ldebug(), this, text_entry(ldebug(),
528 "max designs nicher: in use."))
529
530 // If the population is empty, we needn't go any further.
531 if(population.IsEmpty()) return;
532
533 // Make sure that the Taboo mark is clear on all designs.
534 for(DesignDVSortSet::const_iterator it(population.BeginDV());
535 it!=population.EndDV(); ++it) (*it)->ModifyAttribute(TABOO_MARK, false);
536
537 // in case we are not caching, we will need the target below.
538 DesignTarget& target = this->GetDesignTarget();
539
540 // we will need the number of objectives for a few things here.
541 const size_t nof = target.GetNOF();
542
543 // Synchronize the lists just in case.
544 population.SynchronizeOFAndDVContainers();
545
546 // The number of designs to keep for repeated use below.
547 const size_t n2Keep = this->GetMaximumDesigns();
548
549 // See if there are fewer solutions in the population than we are to
550 // niche to. If so, we keep them all.
551 if(population.SizeOF() < n2Keep) return;
552
553 JEGALOG_II(this->GetLogger(), lverbose(), this,
554 ostream_entry(lverbose(), this->GetName() + ": Population size "
555 "before niching is ") << population.GetSize() << "."
556 )
557
558 const DesignOFSortSet& popByOf = population.GetOFSortContainer();
559
560 JEGA_LOGGING_IF_ON(std::size_t prevPopSize = popByOf.size();)
561
562 // Now continue by extracting the populations objective function extremes
563 eddy::utilities::extremes<obj_val_t> popExtremes(
564 DesignStatistician::GetObjectiveFunctionExtremes(popByOf)
565 );
566
567 NicheCountMap ncts(this->ComputeNicheCounts(popByOf, popExtremes));
568
569 // We want to make sure not to discard any "Pareto" extremes. So find them
570 // all and keep them. Then go into the loop below.
571 DesignOFSortSet pareto(GetBest(popByOf, fitnesses));
572
573 // Now continue by extracting the Pareto extremes
574 this->TagTabooNicheDesigns(pareto);
575
576 double minFit = DBL_MAX;
577 double maxFit = DBL_MIN;
578
579 for(DesignOFSortSet::const_iterator it(popByOf.begin());
580 it!=popByOf.end(); ++it)
581 {
582 const double fit = fitnesses.GetFitness(**it);
583 if(fit < minFit) minFit = fit;
584 if(fit > maxFit) maxFit = fit;
585 }
586
587 const double fitRng = maxFit - minFit;
588
589 const size_t maxNCT = ncts.GetMaxValue();
590 const size_t nctRng = maxNCT - ncts.GetMinValue();
591
592 FitnessRecord nicheFits(popByOf.size());
593 vector<const Design*> allDesVec;
594 allDesVec.reserve(popByOf.size());
595
596 for(DesignOFSortSet::const_iterator curr(popByOf.begin());
597 curr!=popByOf.end(); ++curr)
598 {
599 const Design& des = **curr;
600 double normFit = (fitnesses.GetFitness(des) - minFit) / fitRng;
601 double normNCV = double(maxNCT - ncts.GetValue(des)) / nctRng;
602 nicheFits.AddFitness(&des, normFit + normNCV);
603 allDesVec.push_back(&des);
604 }
605
606 sort(
607 allDesVec.begin(), allDesVec.end(),
608 GeneticAlgorithmSelector::FitnessPred(nicheFits)
609 );
610
611 // Start at the end of the list and remove the required number all the while
612 // skipping any taboo.
613 size_t n2Remove = allDesVec.size() - n2Keep;
614 size_t index = allDesVec.size()-1;
615
616 while(n2Remove > 0)
617 {
618 const Design* des = allDesVec[index--];
619 if(des->HasAttribute(TABOO_MARK)) continue;
620
621 const bool buffered = this->BufferDesign(des);
622 population.Erase(des);
623 if(!buffered) target.TakeDesign(const_cast<Design*>(des));
624 --n2Remove;
625 }
626
627 JEGALOG_II(this->GetLogger(), lverbose(), this,
628 ostream_entry(lverbose(), this->GetName() + ": Final population size "
629 "after niching is ") << population.GetSize() << "."
630 )
631
632 }
633
634 /*
635 ================================================================================
636 Private Methods
637 ================================================================================
638 */
639 MaxDesignsNichePressureApplicator::NicheCountMap
ComputeNicheCounts(const DesignOFSortSet & designs,const eddy::utilities::extremes<obj_val_t> & exts) const640 MaxDesignsNichePressureApplicator::ComputeNicheCounts(
641 const DesignOFSortSet& designs,
642 const eddy::utilities::extremes<obj_val_t>& exts
643 ) const
644 {
645 NicheCountMap ncm(designs.size());
646 ncm.SuspendStatistics();
647
648 JEGA::DoubleVector dists(this->ComputeCutoffDistances(exts));
649
650 const size_t nof = this->GetDesignTarget().GetNOF();
651
652 for(
653 DesignOFSortSet::const_iterator iit(designs.begin());
654 iit!=designs.end(); ++iit
655 )
656 {
657 size_t ncAfter = 1; // 1 to count self
658 const Design& id = **iit;
659
660 DesignOFSortSet::const_iterator jit(iit);
661 for(++jit; jit!=designs.end(); ++jit)
662 {
663 const Design& jd = **jit;
664
665 // If jit is too close to iit, then we increment ncAfter and
666 // increment the count for jit stored in ncm. That way, ncm will
667 // always have record of those that are too close prior to iit at
668 // the beginning of an iit loop.
669 const double obj0Dist = this->ComputeObjectiveDistance(id, jd, 0);
670
671 // If the distance at obj0 is large enough, we can get out of this
672 // inner loop and move onto the next "iit". This is b/c of the
673 // hierarchical sorting by obj0.
674 if(obj0Dist > dists[0]) break;
675
676 // prepare to store whether or not jit is too close.
677 bool tooClose = true;
678
679 // We need to see if the distances are all too small. If any are
680 // larger than the cutoff, then jit is far enough away.
681 for(size_t of=1; of<nof; ++of)
682 if(this->ComputeObjectiveDistance(id, jd, of) > dists[of])
683 { tooClose = false; break; }
684
685 // If it is too close, we mark it as such. Otherwise we move on.
686 if(tooClose)
687 {
688 ++ncAfter;
689 ncm.AddToValue(jd, 1);
690 }
691 }
692
693 ncm.AddToValue(id, ncAfter);
694 }
695
696 ncm.ResumeStatistics(true);
697 return ncm;
698 }
699
700
701
702
703
704
705 /*
706 ================================================================================
707 Structors
708 ================================================================================
709 */
710
711
712
MaxDesignsNichePressureApplicator(GeneticAlgorithm & algorithm)713 MaxDesignsNichePressureApplicator::MaxDesignsNichePressureApplicator(
714 GeneticAlgorithm& algorithm
715 ) :
716 GeneticAlgorithmNichePressureApplicator(algorithm),
717 _maxDesigns(DEFAULT_MAX_DESIGNS)
718 {
719 EDDY_FUNC_DEBUGSCOPE
720 }
721
MaxDesignsNichePressureApplicator(const MaxDesignsNichePressureApplicator & copy)722 MaxDesignsNichePressureApplicator::MaxDesignsNichePressureApplicator(
723 const MaxDesignsNichePressureApplicator& copy
724 ) :
725 GeneticAlgorithmNichePressureApplicator(copy),
726 _maxDesigns(copy._maxDesigns)
727 {
728 EDDY_FUNC_DEBUGSCOPE
729 }
730
MaxDesignsNichePressureApplicator(const MaxDesignsNichePressureApplicator & copy,GeneticAlgorithm & algorithm)731 MaxDesignsNichePressureApplicator::MaxDesignsNichePressureApplicator(
732 const MaxDesignsNichePressureApplicator& copy,
733 GeneticAlgorithm& algorithm
734 ) :
735 GeneticAlgorithmNichePressureApplicator(copy, algorithm),
736 _maxDesigns(copy._maxDesigns)
737 {
738 EDDY_FUNC_DEBUGSCOPE
739 }
740
741
742
743
744
745
746 /*
747 ================================================================================
748 End Namespace
749 ================================================================================
750 */
751 } // namespace Algorithms
752 } // namespace JEGA
753
754