1 /*
2 ================================================================================
3 PROJECT:
4
5 John Eddy's Genetic Algorithms (JEGA)
6
7 CONTENTS:
8
9 Implementation of class GeneticAlgorithm.
10
11 NOTES:
12
13 See notes of GeneticAlgorithm.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 1.0.0
30
31 CHANGES:
32
33 Thu May 15 08:25:23 2003 - 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 GeneticAlgorithm class.
48 */
49
50
51 /*
52 ================================================================================
53 Includes
54 ================================================================================
55 */
56 // JEGAConfig.hpp should be the first include in all JEGA files.
57 #include <../Utilities/include/JEGAConfig.hpp>
58
59 #include <ctime>
60 #include <cfloat>
61 #include <fstream>
62 #include <FitnessRecord.hpp>
63 #include <GeneticAlgorithm.hpp>
64 #include <GeneticAlgorithmMutator.hpp>
65 #include <GeneticAlgorithmCrosser.hpp>
66 #include <GeneticAlgorithmMainLoop.hpp>
67 #include <GeneticAlgorithmSelector.hpp>
68 #include <GeneticAlgorithmConverger.hpp>
69 #include <GeneticAlgorithmEvaluator.hpp>
70 #include <GeneticAlgorithmInitializer.hpp>
71 #include <../Utilities/include/Logging.hpp>
72 #include <GeneticAlgorithmFitnessAssessor.hpp>
73 #include <PostProcessors/NullPostProcessor.hpp>
74 #include <utilities/include/EDDY_DebugScope.hpp>
75 #include <../Utilities/include/LRUDesignCache.hpp>
76 #include <../Utilities/include/DesignGroupVector.hpp>
77 #include <../Utilities/include/ParameterExtractor.hpp>
78 #include <../Utilities/include/DesignVariableInfo.hpp>
79 #include <GeneticAlgorithmNichePressureApplicator.hpp>
80
81
82 /*
83 ================================================================================
84 Namespace Using Directives
85 ================================================================================
86 */
87 using namespace std;
88 using namespace JEGA::Logging;
89 using namespace JEGA::Utilities;
90
91
92
93
94
95
96
97 /*
98 ================================================================================
99 Begin Namespace
100 ================================================================================
101 */
102 namespace JEGA {
103 namespace Algorithms {
104
105
106
107 /*
108 ================================================================================
109 File Scope Helper Functions
110 ================================================================================
111 */
112 /// Creates a string from the argument \a val using an ostringstream.
113 /**
114 * \param val The value of type T to convert to a string.
115 * \return The string representation of \a val created using an ostringstream.
116 */
117 template <typename T>
118 string
asstring(const T & val)119 asstring(
120 const T& val
121 )
122 {
123 EDDY_FUNC_DEBUGSCOPE
124 ostringstream ostr;
125 ostr << val;
126 return ostr.str();
127 }
128
129 /**
130 * \brief Replaces all occurrences of some search string within a source string
131 * with a supplied replacement string.
132 *
133 * \param of The string to search for.
134 * \param in The string in which to search.
135 * \param with The string with which to replace all occurrences of \a of in
136 * \a in.
137 */
138 string
ReplaceAllOccurances(const string & of,string in,const string & with)139 ReplaceAllOccurances(
140 const string& of,
141 string in,
142 const string& with
143 )
144 {
145 EDDY_FUNC_DEBUGSCOPE
146
147 string::size_type cpos = in.find(of);
148
149 while(cpos != string::npos)
150 {
151 in.replace(cpos, of.size(), with);
152 cpos = in.find(of, cpos + with.size());
153 }
154
155 return in;
156 }
157
158
159
160 /*
161 ================================================================================
162 Private Template Method Implementations
163 ================================================================================
164 */
165
166 template<typename Op_T>
167 bool
SetOperator(Op_T * op,Op_T & (GeneticAlgorithmOperatorSet::* getFunc)(),void (GeneticAlgorithmOperatorSet::* setFunc)(Op_T * op),bool groupHasOp,const std::string & JEGA_LOGGING_IF_ON (opType))168 GeneticAlgorithm::SetOperator(
169 Op_T* op,
170 Op_T& (GeneticAlgorithmOperatorSet::*getFunc)(), // Get
171 void (GeneticAlgorithmOperatorSet::*setFunc)(Op_T* op),
172 bool groupHasOp,
173 const std::string& JEGA_LOGGING_IF_ON(opType)
174 )
175 {
176 EDDY_FUNC_DEBUGSCOPE
177
178 // See if the current group has the operator. If it does, we can go ahead
179 // and use the supplied operator without any further considerations.
180 if(groupHasOp)
181 {
182 (this->GetOperatorSet().*setFunc)(op);
183 return true;
184 }
185
186 // if it doesn't, we have to see if we can find a group that does.
187 // Begin by storing the current converger.
188 Op_T& current = (this->GetOperatorSet().*getFunc)();
189
190 // now put the new converger in the set so we can match the it.
191 (this->GetOperatorSet().*setFunc)(op);
192
193 const GeneticAlgorithmOperatorGroup* match =
194 this->MatchGroup(this->GetOperatorSet());
195
196 // now, warn if we had no success matching a group.
197 if(match == 0x0)
198 {
199 JEGALOG_II(this->GetLogger(), lquiet(), this, text_entry(lquiet(),
200 "Cannot set " + opType + " to " + op->GetName() +
201 ". It is incompatible with the other existing operators "
202 "according to the known groups. Retaining current " + opType +
203 " of " + current.GetName())
204 )
205 (this->GetOperatorSet().*setFunc)(¤t);
206 return false;
207 }
208
209 // we matched a group. Adopt the new group and warn about it.
210 JEGALOG_II(this->GetLogger(), lquiet(), this, text_entry(lquiet(),
211 this->GetName() + ": Matched the modified set to group " +
212 match->GetName() + " in response to a set " + opType + " call."))
213
214 this->SetOperatorGroup(*match);
215
216 return true;
217 }
218
219
220
221 /*
222 ================================================================================
223 Static Member Data Definitions
224 ================================================================================
225 */
226 eddy::utilities::uint64_t GeneticAlgorithm::_instanceCt = 0;
227
228
229
230
231
232
233
234 /*
235 ================================================================================
236 Mutators
237 ================================================================================
238 */
239
240 void
SetCurrentFitnesses(const FitnessRecord * ftns)241 GeneticAlgorithm::SetCurrentFitnesses(
242 const FitnessRecord* ftns
243 )
244 {
245 EDDY_FUNC_DEBUGSCOPE
246 this->_lastFtns.reset(ftns);
247 }
248
249 void
SetName(const std::string & name)250 GeneticAlgorithm::SetName(
251 const std::string& name
252 )
253 {
254 EDDY_FUNC_DEBUGSCOPE
255
256 if(name.empty())
257 {
258 if(this->_name.empty())
259 {
260 this->_name = GetDefaultName();
261 JEGALOG_II(this->GetLogger(), lquiet(), this,
262 text_entry(lquiet(), this->_name + ": Empty name supplied to "
263 "unnamed GA. Using default name of " + this->_name + "."
264 )
265 )
266 }
267 else
268 {
269 JEGALOG_II(this->GetLogger(), lquiet(), this,
270 text_entry(lquiet(), this->_name + ": Attempt to rename GA "
271 "from " + this->_name + " to an empty string failed. "
272 "Keeping the existing name"
273 )
274 )
275 }
276 }
277 else this->_name = name;
278
279 JEGALOG_II(this->GetLogger(), lverbose(), this,
280 text_entry(
281 lverbose(), this->_name + ": The name of this GA is now " +
282 this->_name
283 )
284 )
285 }
286
287 void
SetFinalDataFilename(const std::string & name)288 GeneticAlgorithm::SetFinalDataFilename(
289 const std::string& name
290 )
291 {
292 EDDY_FUNC_DEBUGSCOPE
293
294 if(name.empty())
295 {
296 if(this->_finalDataFile.empty())
297 {
298 this->_finalDataFile = "finaldata#.dat";
299 JEGALOG_II(this->GetLogger(), lquiet(), this,
300 text_entry(lquiet(), this->_name + ": Empty name supplied for "
301 "final data filename. Using default name of " +
302 this->_finalDataFile + "."
303 )
304 )
305 }
306 }
307 else this->_finalDataFile = name;
308
309 this->_finalDataFile.assign(
310 ReplaceAllOccurances(
311 "#", this->_finalDataFile, asstring(this->_instanceNum)
312 )
313 );
314
315 JEGALOG_II(this->GetLogger(), lverbose(), this,
316 text_entry(lverbose(), this->GetName() + ": The name of the file to "
317 "which final data will be written is now " + this->_finalDataFile)
318 )
319 }
320
321 void
SetPrintFinalData(bool print)322 GeneticAlgorithm::SetPrintFinalData(
323 bool print
324 )
325 {
326 EDDY_FUNC_DEBUGSCOPE
327
328 this->_printFinalData = print;
329
330 JEGALOG_II(this->GetLogger(), lverbose(), this,
331 ostream_entry(lverbose(), this->GetName() +
332 ": The print final data flag is now set to ")
333 << this->_printFinalData
334 )
335 }
336
337 void
SetDataDirectory(const std::string & dir)338 GeneticAlgorithm::SetDataDirectory(
339 const std::string& dir
340 )
341 {
342 EDDY_FUNC_DEBUGSCOPE
343
344 this->_dataDir = dir;
345
346 JEGALOG_II(this->GetLogger(), lverbose(), this,
347 ostream_entry(lverbose(), this->GetName() +
348 ": The data directory is now set to ")
349 << this->_dataDir
350 )
351 }
352
353 void
SetPrintDiscards(bool print)354 GeneticAlgorithm::SetPrintDiscards(
355 bool print
356 )
357 {
358 EDDY_FUNC_DEBUGSCOPE
359
360 this->_printDiscards = print;
361
362 JEGALOG_II(this->GetLogger(), lverbose(), this,
363 ostream_entry(lverbose(), this->GetName() +
364 ": The print discards flag is now set to ")
365 << this->_printDiscards
366 )
367 }
368
369
370 void
SetPrintEachPopulation(bool print)371 GeneticAlgorithm::SetPrintEachPopulation(
372 bool print
373 )
374 {
375 EDDY_FUNC_DEBUGSCOPE
376
377 this->_printPopEachGen = print;
378
379 JEGALOG_II(this->GetLogger(), lverbose(), this,
380 ostream_entry(lverbose(), this->GetName() +
381 ": The print each population flag is now set to ")
382 << this->_printPopEachGen
383 )
384 }
385
386
387
388
389
390
391 /*
392 ================================================================================
393 Accessors
394 ================================================================================
395 */
396 const FitnessRecord&
GetCurrentFitnesses()397 GeneticAlgorithm::GetCurrentFitnesses(
398 )
399 {
400 EDDY_FUNC_DEBUGSCOPE
401 if(this->_lastFtns.get() == 0x0)
402 {
403 DesignGroupVector dgv(2, &this->_pop);
404 dgv[1] = &this->_cldrn;
405 this->SetCurrentFitnesses(
406 this->GetFitnessAssessor().AssessFitness(dgv)
407 );
408 }
409 return *this->_lastFtns;
410 }
411
412
413
414
415
416
417
418 /*
419 ================================================================================
420 Public Methods
421 ================================================================================
422 */
423
424 bool
ExtractParameters(const ParameterDatabase & pdb)425 GeneticAlgorithm::ExtractParameters(
426 const ParameterDatabase& pdb
427 )
428 {
429 EDDY_FUNC_DEBUGSCOPE
430 bool ret = this->PollForParameters(pdb);
431 this->ExtractOperatorParameters(this->GetConverger(), pdb);
432 this->ExtractOperatorParameters(this->GetCrosser(), pdb);
433 this->ExtractOperatorParameters(this->GetEvaluator(), pdb);
434 this->ExtractOperatorParameters(this->GetFitnessAssessor(), pdb);
435 this->ExtractOperatorParameters(this->GetInitializer(), pdb);
436 this->ExtractOperatorParameters(this->GetNichePressureApplicator(), pdb);
437 this->ExtractOperatorParameters(this->GetMainLoop(), pdb);
438 this->ExtractOperatorParameters(this->GetMutator(), pdb);
439 this->ExtractOperatorParameters(this->GetSelector(), pdb);
440 this->ExtractOperatorParameters(this->GetPostProcessor(), pdb);
441 return ret;
442 }
443
444
445 eddy::utilities::uint64_t
GetNumberEvaluations() const446 GeneticAlgorithm::GetNumberEvaluations(
447 ) const
448 {
449 EDDY_FUNC_DEBUGSCOPE
450 return this->GetEvaluator().GetNumberEvaluations();
451 }
452
453 eddy::utilities::uint64_t
GetGenerationNumber() const454 GeneticAlgorithm::GetGenerationNumber(
455 ) const
456 {
457 EDDY_FUNC_DEBUGSCOPE
458 return this->GetMainLoop().GetCurrentGeneration();
459 }
460
461 bool
SetOperatorGroup(const GeneticAlgorithmOperatorGroup & to)462 GeneticAlgorithm::SetOperatorGroup(
463 const GeneticAlgorithmOperatorGroup& to
464 )
465 {
466 EDDY_FUNC_DEBUGSCOPE
467
468 GeneticAlgorithmOperatorGroupRegistry& ogReg =
469 this->GetOperatorGroupRegistry();
470
471 bool ret = ogReg.is_registered(to.GetName());
472 if(ret)
473 {
474 if(!to.ContainsSet(this->GetOperatorSet()))
475 {
476 JEGALOG_II(this->GetLogger(), ldebug(), this,
477 text_entry(ldebug(), this->GetName() + ": Current operator set "
478 "is not compatible with new operator group \"" +
479 to.GetName() + "\". Clearing the set and "
480 "adopting the group.")
481 )
482 this->GetOperatorSet().Clear();
483 }
484 this->_opGroup = &to;
485 }
486 return ret;
487 }
488
489 #define SET_OP_METHOD(optype) \
490 bool \
491 GeneticAlgorithm::Set##optype( \
492 GeneticAlgorithm##optype* to \
493 ) \
494 { \
495 EDDY_FUNC_DEBUGSCOPE \
496 return this->SetOperator<GeneticAlgorithm##optype>( \
497 to, \
498 &GeneticAlgorithmOperatorSet::Get##optype, \
499 &GeneticAlgorithmOperatorSet::Set##optype, \
500 this->GetOperatorGroup().Has##optype(*to), \
501 #optype \
502 ); \
503 }
504
505 SET_OP_METHOD(Converger)
SET_OP_METHOD(Crosser)506 SET_OP_METHOD(Crosser)
507 SET_OP_METHOD(Evaluator)
508 SET_OP_METHOD(FitnessAssessor)
509 SET_OP_METHOD(Initializer)
510 SET_OP_METHOD(MainLoop)
511 SET_OP_METHOD(Mutator)
512 SET_OP_METHOD(Selector)
513 SET_OP_METHOD(PostProcessor)
514 SET_OP_METHOD(NichePressureApplicator)
515
516 bool
517 GeneticAlgorithm::SetOperatorSet(
518 const GeneticAlgorithmOperatorSet& to
519 )
520 {
521 EDDY_FUNC_DEBUGSCOPE
522
523 const GeneticAlgorithmOperatorGroup* gp = this->MatchGroup(to);
524
525 // first, warn if we had no success matching a group.
526 if(gp == 0x0)
527 {
528 JEGALOG_II(this->GetLogger(), lquiet(), this, text_entry(lquiet(),
529 this->GetName() + ": Attempt to use an operator set which cannot "
530 "be matched to a group failed. Retaining current operator set."))
531 return false;
532 }
533
534 // start by accepting our new set b/c we have success.
535 this->_opSet->operator =(to);
536
537 // we matched a group. now we must see if it is our current group.
538 // if it is, fine. If not, adopt the new group and warn about it.
539 if(gp != this->_opGroup)
540 {
541 JEGALOG_II(this->GetLogger(), lquiet(), this, text_entry(lquiet(),
542 this->GetName() + ": Matched new set to a group that is not the "
543 "current group. Adopting the new group."))
544 this->SetOperatorGroup(*gp);
545 }
546
547 // return our success
548 return true;
549 }
550
551 Design*
GetNewDesign() const552 GeneticAlgorithm::GetNewDesign(
553 ) const
554 {
555 EDDY_FUNC_DEBUGSCOPE
556
557 Design* ret = this->_target.GetNewDesign();
558 EDDY_ASSERT(ret != 0x0);
559 return ret;
560 }
561
562 Design*
GetNewDesign(const Design & copy) const563 GeneticAlgorithm::GetNewDesign(
564 const Design& copy
565 ) const
566 {
567 EDDY_FUNC_DEBUGSCOPE
568
569 Design* ret = this->_target.GetNewDesign(copy);
570 EDDY_ASSERT(ret != 0x0);
571 return ret;
572 }
573
574 size_t
ValidateVariableValues(const DesignGroupVector & groups) const575 GeneticAlgorithm::ValidateVariableValues(
576 const DesignGroupVector& groups
577 ) const
578 {
579 EDDY_FUNC_DEBUGSCOPE
580
581 size_t numIll = 0;
582
583 // iterate over the "groups" and call the overload
584 for(size_t i=0; i<groups.size(); ++i)
585 numIll += this->ValidateVariableValues(*groups[i]);
586
587 return numIll;
588 }
589
590 size_t
LogIllconditionedDesigns(const JEGA::Utilities::DesignGroup & from) const591 GeneticAlgorithm::LogIllconditionedDesigns(
592 const JEGA::Utilities::DesignGroup& from
593 ) const
594 {
595 size_t nLogged = 0;
596
597 #if defined(JEGA_LOGGING_ON)
598 if(this->GetLogger().Gate().will_log(
599 this->GetLogger().Gate().get_default_level(), lquiet()
600 ))
601 {
602 ostream_entry ent(lquiet(), this->GetName());
603
604 ent << ": Design Variable Values for Ill-conditioned "
605 "Designs:\n";
606
607 for(
608 DesignDVSortSet::const_iterator dvit(from.BeginDV());
609 dvit!=from.EndDV(); ++dvit
610 )
611 {
612 const Design* des = *dvit;
613 if(des->IsIllconditioned())
614 {
615 ent << des->GetVariableValue(0);
616 for(size_t i=1; i<des->GetNDV(); ++i)
617 ent << ' ' << des->GetVariableValue(i);
618 ent << '\n';
619 ++nLogged;
620 }
621 }
622 this->GetLogger().Gate().simple_log(lquiet(), ent);
623 }
624 #endif
625
626 return nLogged;
627 }
628
629 size_t
ValidateVariableValues(DesignGroup & group) const630 GeneticAlgorithm::ValidateVariableValues(
631 DesignGroup& group
632 ) const
633 {
634 EDDY_FUNC_DEBUGSCOPE
635
636 // get the design variable info objects.
637 const DesignVariableInfoVector& dvInfos =
638 this->_target.GetDesignVariableInfos();
639
640 size_t ndv = dvInfos.size();
641
642 // keep a temporary list of the altered designs.
643 vector<Design*> changedDesigns;
644 changedDesigns.reserve(group.SizeDV());
645
646 size_t numIll = 0;
647
648 // go through the Designs and then the infos
649 const DesignDVSortSet::const_iterator de(group.EndDV());
650
651 for(DesignDVSortSet::iterator dit(group.BeginDV()); dit!=de;)
652 {
653 // keep track of whether or not the design gets changed.
654 bool changed = false;
655
656 Design* des = *dit;
657
658 for(size_t dv = 0; dv<ndv; ++dv)
659 {
660 var_rep_t rep = des->GetVariableRep(dv);
661
662 const DesignVariableInfo& dvi = *dvInfos[dv];
663
664 if(dvi.IsValidRep(rep)) continue;
665
666 // get a corrected variable value.
667 var_rep_t corrected = dvi.IsRepInBounds(rep) ?
668 dvi.GetNearestValidRep(rep) : dvi.GetRandomRep();
669
670 // a return of -limits::max means that the rep could
671 // not be corrected.
672 if(corrected == -std::numeric_limits<var_rep_t>::max())
673 {
674 JEGALOG_II(this->GetLogger(), lquiet(), this,
675 ostream_entry(lquiet(), this->GetName() + ": "
676 "Non-correctable invalid variable rep of ")
677 << rep << " found for variable " << dvi.GetLabel()
678 )
679 des->SetIllconditioned(true);
680 ++numIll;
681 break;
682 }
683 // otherwise, install the corrected value and
684 // set the flag so that we know to remove and
685 // re-insert the Design.
686 else if(corrected != rep)
687 {
688 JEGALOG_II(this->GetLogger(), lquiet(), this,
689 ostream_entry(
690 lquiet(), this->GetName() + ": Invalid variable rep of "
691 )
692 << rep << " found for variable " << dvi.GetLabel()
693 << ". Corrected to " << corrected << " (value="
694 << dvi.GetValueOf(corrected) << ")."
695 )
696 des->SetVariableRep(dv, corrected);
697 des->RemoveAsClone();
698 changed = true;
699 }
700 }
701
702 // if the design was changed, we have to remove it temporarily.
703 if(changed)
704 {
705 changedDesigns.push_back(des);
706 dit = group.EraseRetDV(dit);
707 }
708 else ++dit;
709 }
710
711 // now put back all the changed Designs.
712 for(size_t i=0; i<changedDesigns.size(); ++i)
713 {
714 changedDesigns[i]->SetEvaluated(false);
715 changedDesigns[i]->SetIllconditioned(false);
716 group.Insert(changedDesigns[i]);
717 }
718
719 // this will verify that all variables are now valid.
720 #if defined(JEGA_LOGGING_ON) && defined(JEGA_OPTION_DEBUG)
721
722 for(DesignDVSortSet::iterator dit(group.BeginDV()); dit!=de; ++dit)
723 {
724 for(DesignVariableInfoVector::const_iterator dvit(dvInfos.begin());
725 dvit != dvInfos.end(); ++dvit)
726 {
727 JEGAIFLOG_CF_F(!(*dit)->IsIllconditioned() &&
728 !(*dvit)->IsValidRep(
729 (*dvit)->WhichRep(**dit)
730 ),
731 this->GetLogger(),
732 ostream_entry(lfatal(),
733 "Invalid variable representation found in "
734 "non-ill-conditioned design after variable "
735 "correction operation. Variable ")
736 << (*dvit)->GetLabel() << ", Representation "
737 << (*dvit)->WhichRep(**dit)
738 )
739 }
740 }
741 #endif
742
743 return numIll;
744 }
745
746 double
GetElapsedTime() const747 GeneticAlgorithm::GetElapsedTime(
748 ) const
749 {
750 EDDY_FUNC_DEBUGSCOPE
751 return double(clock() - this->_startTime) / CLOCKS_PER_SEC;
752 }
753
754 /*
755 ================================================================================
756 Subclass Visible Methods
757 ================================================================================
758 */
759
760 string
GetDefaultName() const761 GeneticAlgorithm::GetDefaultName(
762 ) const
763 {
764 EDDY_FUNC_DEBUGSCOPE
765
766 ostringstream ostr;
767 ostr << this->GetAlgorithmTypeName() << " #" << this->GetInstanceNumber();
768 return ostr.str();
769 }
770
771 void
ExtractOperatorParameters(GeneticAlgorithmOperator & op,const ParameterDatabase & pdb)772 GeneticAlgorithm::ExtractOperatorParameters(
773 GeneticAlgorithmOperator& op,
774 const ParameterDatabase& pdb
775 )
776 {
777 EDDY_FUNC_DEBUGSCOPE
778 JEGAIFLOG_CF_II_F(!op.ExtractParameters(pdb), this->GetLogger(), this,
779 text_entry(lfatal(),
780 this->GetName() + ": Failed to retrieve the parameters for \""
781 ) << op.GetName() << "\"."
782 );
783 }
784
785 const GeneticAlgorithmOperatorGroupRegistry&
GetOperatorGroupRegistry() const786 GeneticAlgorithm::GetOperatorGroupRegistry(
787 ) const
788 {
789 EDDY_FUNC_DEBUGSCOPE
790 return const_cast<GeneticAlgorithm*>(this)->GetOperatorGroupRegistry_FWD();
791 }
792
793 bool
WritePopulationToFile() const794 GeneticAlgorithm::WritePopulationToFile(
795 ) const
796 {
797 EDDY_FUNC_DEBUGSCOPE
798
799 ostringstream fname;
800 fname << "population_" << this->GetGenerationNumber() << ".dat";
801 return this->WritePopulationToFile(
802 this->GetDataDirectory() + "/" + fname.str()
803 );
804 }
805
806 bool
WritePopulationToFile(const std::string & fname) const807 GeneticAlgorithm::WritePopulationToFile(
808 const std::string& fname
809 ) const
810 {
811 EDDY_FUNC_DEBUGSCOPE
812 return this->WriteGroupToFile(this->_pop.GetOFSortContainer(), fname);
813 }
814
815 bool
WriteGroupToFile(const JEGA::Utilities::DesignDVSortSet & group,const std::string & fname) const816 GeneticAlgorithm::WriteGroupToFile(
817 const JEGA::Utilities::DesignDVSortSet& group,
818 const std::string& fname
819 ) const
820 {
821 EDDY_FUNC_DEBUGSCOPE
822
823 ofstream ofile(fname.c_str());
824
825 if(!ofile.is_open())
826 {
827 JEGALOG_II(this->GetLogger(), lquiet(), this, text_entry(lquiet(),
828 this->GetName() + ": Unable to open file " + fname +
829 " for writing design group. No data written.")
830 )
831 return false;
832 }
833
834 group.stream_out(ofile);
835 ofile.close();
836
837 JEGALOG_II(this->GetLogger(), lverbose(), this,
838 text_entry(lverbose(),
839 this->GetName() + ": Wrote designs file \"" + fname + "\".")
840 )
841
842 return true;
843 }
844
845 bool
WriteGroupToFile(const JEGA::Utilities::DesignOFSortSet & group,const std::string & fname) const846 GeneticAlgorithm::WriteGroupToFile(
847 const JEGA::Utilities::DesignOFSortSet& group,
848 const std::string& fname
849 ) const
850 {
851 EDDY_FUNC_DEBUGSCOPE
852
853 ofstream ofile(fname.c_str());
854
855 if(!ofile.is_open())
856 {
857 JEGALOG_II(this->GetLogger(), lquiet(), this, text_entry(lquiet(),
858 this->GetName() + ": Unable to open file " + fname +
859 " for writing design group. No data written.")
860 )
861 return false;
862 }
863
864 group.stream_out(ofile);
865 ofile.close();
866
867 JEGALOG_II(this->GetLogger(), lverbose(), this,
868 text_entry(lverbose(),
869 this->GetName() + ": Wrote designs file \"" + fname + "\".")
870 )
871
872 return true;
873 }
874
875
876
877
878
879 /*
880 ================================================================================
881 Subclass Overridable Methods
882 ================================================================================
883 */
884 bool
PollForParameters(const JEGA::Utilities::ParameterDatabase & db)885 GeneticAlgorithm::PollForParameters(
886 const JEGA::Utilities::ParameterDatabase& db
887 )
888 {
889 EDDY_FUNC_DEBUGSCOPE
890
891 string tname;
892 bool success = ParameterExtractor::GetStringFromDB(
893 db, "method.jega.algorithm_name", tname
894 );
895
896 // If we did not find the name, warn about it and use the default
897 // value. Note that if !success, then tname is unaltered
898 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
899 text_entry(lverbose(), this->GetName() + ": The algorithm name string "
900 "was not found in the parameter database. A default name will "
901 "be created.")
902 )
903
904 SetName(tname);
905
906 bool tmp = false;
907
908 // Begin by extracting the population printing flag.
909 success = ParameterExtractor::GetBooleanFromDB(
910 db, "method.print_each_pop", tmp
911 );
912
913 if(success) this->SetPrintEachPopulation(tmp);
914
915 // If we did not find the flag, warn about it and use the default
916 // value. Note that if !success, then _printPopEachGen is unaltered
917 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
918 ostream_entry(lverbose(), this->GetName() + ": The population printing "
919 "flag was not found in the parameter database. Using the current "
920 "value of ") << (this->_printPopEachGen ? "true." : "false.")
921 )
922
923 success = ParameterExtractor::GetBooleanFromDB(
924 db, "method.print_final_data", tmp
925 );
926
927 if(success) this->SetPrintFinalData(tmp);
928
929 // If we did not find the flag, warn about it and use the default
930 // value. Note that if !success, then _printFinalData is unaltered
931 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
932 ostream_entry(lverbose(), this->GetName() + ": The final data printing "
933 "flag was not found in the parameter database. Using the current "
934 "value of ") << (this->_printFinalData ? "true." : "false.")
935 )
936
937 success = ParameterExtractor::GetBooleanFromDB(
938 db, "method.print_discards", tmp
939 );
940
941 if(success) this->SetPrintDiscards(tmp);
942
943 // If we did not find the flag, warn about it and use the default
944 // value. Note that if !success, then _printDiscards is unaltered
945 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
946 ostream_entry(lverbose(), this->GetName() + ": The discards printing "
947 "flag was not found in the parameter database. Using the current "
948 "value of ") << (this->_printDiscards ? "true." : "false.")
949 )
950
951 success = ParameterExtractor::GetStringFromDB(
952 db, "method.jega.final_data_filename", tname
953 );
954
955 if(success) this->SetFinalDataFilename(tname);
956
957 // If we did not find the name, warn about it and use the default
958 // value. Note that if !success, then tname is unaltered
959 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
960 text_entry(lverbose(), this->GetName() + ": The final data "
961 "filename/pattern was not found in the parameter database. Using "
962 "the current pattern of " + this->_finalDataFile + ".")
963 )
964
965 success = ParameterExtractor::GetStringFromDB(
966 db, "method.jega.data_directory", tname
967 );
968
969 if(success) this->SetDataDirectory(tname);
970
971 // If we did not find the data directory, warn about it and use the default
972 // value. Note that if !success, then tname is unaltered
973 JEGAIFLOG_CF_II(!success, this->GetLogger(), lverbose(), this,
974 text_entry(lverbose(), this->GetName() + ": The data directory "
975 "was not found in the parameter database. Using the current "
976 "value of " + this->_dataDir + ".")
977 )
978
979 return success;
980 }
981
982
983 void
DoCrossover()984 GeneticAlgorithm::DoCrossover(
985 )
986 {
987 EDDY_FUNC_DEBUGSCOPE
988
989 // it should be unnecessary to do this but we will anyway.
990 this->_cldrn.FlushAll();
991
992 // all is set to use the crossover operator.
993 // cross members of _pop and put the results in _cldrn.
994 this->GetCrosser().Crossover(this->_pop, this->_cldrn);
995 }
996
997 const FitnessRecord*
DoFitnessAssessment()998 GeneticAlgorithm::DoFitnessAssessment(
999 )
1000 {
1001 EDDY_FUNC_DEBUGSCOPE
1002
1003 // create a vector of groups to be simultaneously considered
1004 // for fitness assessment.
1005 DesignGroupVector gpvec(2, &this->_pop);
1006 gpvec[1] = &this->_cldrn;
1007
1008 // now use the fitness assessor operator on the collected groups.
1009 return this->GetFitnessAssessor().AssessFitness(gpvec);
1010 }
1011
1012 void
DoMutation()1013 GeneticAlgorithm::DoMutation(
1014 )
1015 {
1016 EDDY_FUNC_DEBUGSCOPE
1017
1018 // Everything should be all set to go right into the mutation operator.
1019 this->GetMutator().Mutate(this->_pop, this->_cldrn);
1020 }
1021
1022 void
DoPreSelection()1023 GeneticAlgorithm::DoPreSelection(
1024 )
1025 {
1026 EDDY_FUNC_DEBUGSCOPE
1027 this->GetNichePressureApplicator().PreSelection(this->_pop);
1028 }
1029
1030 void
AbsorbEvaluatorInjections(bool allowDuplicates)1031 GeneticAlgorithm::AbsorbEvaluatorInjections(
1032 bool allowDuplicates
1033 )
1034 {
1035 EDDY_FUNC_DEBUGSCOPE
1036
1037 // Merge in any designs injected via the evaluator. They get merged
1038 // into the children and must already be evaluated.
1039 GeneticAlgorithmEvaluator& evaler = this->GetEvaluator();
1040 if(evaler.GetInjections().empty()) return;
1041
1042 evaler.MergeInjectedDesigns(this->_cldrn, false, false, allowDuplicates);
1043
1044 // Now verify that there are no children that duplicate population members
1045 // if disallowing duplicates.
1046 if(!allowDuplicates)
1047 {
1048 std::size_t nrem =
1049 this->GetPopulation().GetDVSortContainer().test_for_clones(
1050 this->_cldrn.GetDVSortContainer()
1051 );
1052
1053 if(nrem > 0)
1054 {
1055 nrem = this->_cldrn.FlushCloneDesigns();
1056
1057 JEGAIFLOG_CF_II(nrem > 0, this->GetLogger(),
1058 lverbose(), this,
1059 ostream_entry(lverbose(), this->GetName() + ": flushed ")
1060 << nrem << " designs from the children set (possibly "
1061 "injections) that duplicated existing population members."
1062 )
1063 }
1064 }
1065
1066 // now that we've absorbed them all, dispose of them in the evaluator.
1067 evaler.ClearInjectedDesigns();
1068 }
1069
1070 void
DoSelection(const FitnessRecord & fitnesses)1071 GeneticAlgorithm::DoSelection(
1072 const FitnessRecord& fitnesses
1073 )
1074 {
1075 EDDY_FUNC_DEBUGSCOPE
1076
1077 // create a vector of all groups to be selected from.
1078 DesignGroupVector gpvec(2, &this->_pop);
1079 gpvec[1] = &this->_cldrn;
1080
1081 JEGA_LOGGING_IF_ON(const std::size_t iPopSize = this->_pop.GetSize());
1082 JEGA_LOGGING_IF_ON(const std::size_t iCldrnSize = this->_cldrn.GetSize());
1083
1084 // prepare a group to put the results into.
1085 DesignGroup into(this->_target);
1086
1087 // actually perform the selection
1088 this->GetSelector().Select(gpvec, into, this->_pop.GetSize(), fitnesses);
1089
1090 // expel whatever is left in the Population and Children after indicating
1091 // what is gone and what remains.
1092 JEGALOG_II(this->GetLogger(), lverbose(), this,
1093 ostream_entry(lverbose(), this->GetName() + ": ")
1094 << this->_pop.GetSize() << " of " << iPopSize
1095 << " population members were not selected to continue. "
1096 << this->_cldrn.GetSize() << " of " << iCldrnSize
1097 << " offspring were immediately rejected."
1098 )
1099
1100 this->_pop.FlushAll();
1101 this->_cldrn.FlushAll();
1102
1103 // set the new population to be the new group
1104 this->_pop = into;
1105 }
1106
1107 void
ApplyNichePressure(JEGA::Utilities::DesignGroup & group,const FitnessRecord & fitnesses)1108 GeneticAlgorithm::ApplyNichePressure(
1109 JEGA::Utilities::DesignGroup& group,
1110 const FitnessRecord& fitnesses
1111 )
1112 {
1113 EDDY_FUNC_DEBUGSCOPE
1114 this->GetNichePressureApplicator().ApplyNichePressure(group, fitnesses);
1115 }
1116
1117 void
InitializePopulation()1118 GeneticAlgorithm::InitializePopulation(
1119 )
1120 {
1121 EDDY_FUNC_DEBUGSCOPE
1122
1123 // Everything should be all set to go right into the initialization
1124 // operator.
1125 this->GetInitializer().Initialize(this->_pop);
1126 }
1127
1128 bool
DoEvaluation(DesignGroup & group)1129 GeneticAlgorithm::DoEvaluation(
1130 DesignGroup& group
1131 )
1132 {
1133 EDDY_FUNC_DEBUGSCOPE
1134
1135 // Everything should be all set to go right into the evaluator operator.
1136 bool success = this->GetEvaluator().Evaluate(group);
1137
1138 // now refresh the DesignOFSortSet of group so that it reflects
1139 // the fact that it's Designs should now be evaluated.
1140 group.SynchronizeOFAndDVContainers();
1141 return success;
1142 }
1143
1144 bool
TestForConvergence(const FitnessRecord & fitnesses)1145 GeneticAlgorithm::TestForConvergence(
1146 const FitnessRecord& fitnesses
1147 )
1148 {
1149 EDDY_FUNC_DEBUGSCOPE
1150
1151 // all should be set to use the convergence operator.
1152 // recall that the CheckConvergence operator returns
1153 // whether or not convergence has occurred.
1154 return this->GetConverger().CheckConvergence(this->_pop, fitnesses);
1155 }
1156
1157 void
DoPostProcessing()1158 GeneticAlgorithm::DoPostProcessing(
1159 )
1160 {
1161 EDDY_FUNC_DEBUGSCOPE
1162 this->GetPostProcessor().PostProcess(this->_pop);
1163 }
1164
1165 bool
AlgorithmInitialize()1166 GeneticAlgorithm::AlgorithmInitialize(
1167 )
1168 {
1169 EDDY_FUNC_DEBUGSCOPE
1170
1171 this->_startTime = clock();
1172
1173 // initialize the populations design variable values.
1174 this->InitializePopulation();
1175
1176 if(this->GetInitializer().CanProduceInvalidVariableValues())
1177 {
1178 // verify the validity of each design variable of each Design.
1179 size_t numIll = this->ValidateVariableValues(this->_pop);
1180
1181 // any designs that could not be evaluated must be removed.
1182 if(numIll > 0)
1183 {
1184 this->LogIllconditionedDesigns(this->_pop);
1185
1186 JEGA_LOGGING_IF_ON(DesignDVSortSet::size_type nrem =)
1187 this->_pop.FlushIllconditionedDesigns();
1188
1189 JEGAIFLOG_CF_II(nrem > 0, this->GetLogger(), lquiet(), this,
1190 ostream_entry(lquiet(), this->GetName() + ": flushed ") << nrem
1191 << " designs from the initial population whose variables "
1192 "could not be corrected."
1193 )
1194 }
1195 }
1196
1197 // check for clones in the initial population to avoid re-evaluation
1198 this->_pop.GetDVSortContainer().test_within_list_for_clones();
1199
1200 // check for duplicates in the discarded designs to avoid re-evaluations.
1201 const LRUDesignCache& discards =
1202 this->GetDesignTarget().CheckoutDiscards();
1203 discards.test_for_clones(this->_pop.GetDVSortContainer());
1204 this->GetDesignTarget().CheckinDiscards();
1205
1206 // perform evaluation.
1207 bool evald = this->DoEvaluation(this->_pop);
1208
1209 JEGAIFLOG_CF_II(!evald, this->GetLogger(), lquiet(), this,
1210 text_entry(
1211 lquiet(), this->GetName() + ": Errors were encountered while "
1212 "evaluating the offspring designs"
1213 )
1214 )
1215
1216 // any designs that could not be evaluated must be removed.
1217 if(!evald)
1218 {
1219 this->LogIllconditionedDesigns(this->_pop);
1220
1221 JEGA_LOGGING_IF_ON(DesignDVSortSet::size_type nrem =)
1222 this->_pop.FlushIllconditionedDesigns();
1223
1224 JEGAIFLOG_CF_II(nrem > 0, this->GetLogger(), lquiet(), this,
1225 ostream_entry(
1226 lquiet(), this->GetName() + ": encountered and flushed "
1227 )
1228 << nrem
1229 << " ill-conditioned designs after evaluation of the initial "
1230 "population."
1231 )
1232 }
1233
1234 // load the designs into the objective function sorted list.
1235 this->_pop.SynchronizeOFAndDVContainers();
1236
1237 // initialize the generation number to 0.
1238 this->GetMainLoop().SetCurrentGeneration(0);
1239
1240 if(this->_printPopEachGen) this->WritePopulationToFile();
1241
1242 this->_isInitialized = true;
1243
1244 return true;
1245 }
1246
1247 bool
AlgorithmProcess()1248 GeneticAlgorithm::AlgorithmProcess(
1249 )
1250 {
1251 EDDY_FUNC_DEBUGSCOPE
1252
1253 // If this algorithm has already reported itself converged, don't do
1254 // anything.
1255 // Or, similarly, if we test it for convergence using the parameter-less
1256 // convergence method and it says it is converged, then do nothing.
1257 if(this->GetConverger().GetConverged() ||
1258 this->GetConverger().CheckConvergence())
1259 return false;
1260
1261 const bool ret = this->GetMainLoop().RunGeneration();
1262
1263 if(this->_printPopEachGen) this->WritePopulationToFile();
1264
1265 JEGA_LOGGING_IF_ON(
1266 this->GetLogger().FlushStreams();
1267 if(&this->GetLogger() != &Logger::Global())
1268 Logger::Global().FlushStreams();
1269 )
1270
1271 return ret;
1272 }
1273
1274 bool
AlgorithmFinalize()1275 GeneticAlgorithm::AlgorithmFinalize(
1276 )
1277 {
1278 EDDY_FUNC_DEBUGSCOPE
1279
1280 JEGA_LOGGING_IF_ON(std::size_t prevSize = this->_pop.GetSize();)
1281
1282 // make sure that the children structure is empty.
1283 this->_cldrn.FlushAll();
1284 // prepare a return value.
1285 bool ret = true;
1286
1287 // Finalize all the operators with the exception of the post processor
1288 // and the fitness assessor both of which will be needed below.
1289 ret &= this->GetConverger().Finalize();
1290 ret &= this->GetCrosser().Finalize();
1291 ret &= this->GetEvaluator().Finalize();
1292 ret &= this->GetInitializer().Finalize();
1293 ret &= this->GetMainLoop().Finalize();
1294 ret &= this->GetMutator().Finalize();
1295 ret &= this->GetNichePressureApplicator().Finalize();
1296 ret &= this->GetSelector().Finalize();
1297
1298 // reclaim any optimal from the discards.
1299 this->ReclaimOptimal();
1300
1301 JEGALOG_II(this->GetLogger(), lverbose(), this,
1302 ostream_entry(lquiet(), this->GetName() + ": Reclaimed ")
1303 << (this->_pop.GetSize() - prevSize) << " optimal designs that "
1304 "had been selected out."
1305 )
1306
1307 // now flush out all non-optimal designs.
1308 this->FlushNonOptimal();
1309
1310 // Now run the post-processor.
1311 this->DoPostProcessing();
1312
1313 // now we can finalize our fitness assessor and post processor.
1314 ret &= this->GetFitnessAssessor().Finalize();
1315 ret &= this->GetPostProcessor().Finalize();
1316
1317 JEGALOG_II(this->GetLogger(), lquiet(), this,
1318 ostream_entry(lquiet(), this->GetName() + ": Ran ")
1319 << this->GetGenerationNumber() << " total generations."
1320 )
1321
1322 // let the user know how many final Designs are left
1323 JEGALOG_II(this->GetLogger(), lquiet(), this,
1324 ostream_entry(lquiet(), this->GetName() + ": Final population size = ")
1325 << this->_pop.GetSize()
1326 )
1327
1328 // write out the final set of optimal designs to a tab delimited file.
1329 if(this->_printFinalData &&
1330 !this->WritePopulationToFile(
1331 this->GetDataDirectory() + "/" + this->_finalDataFile
1332 )) ret = false;
1333
1334 // now write all other Designs considered to another tab delimited file.
1335 if(this->_printDiscards)
1336 {
1337 const LRUDesignCache& discards =
1338 this->GetDesignTarget().CheckoutDiscards();
1339 if(!this->WriteGroupToFile(
1340 discards.DVSortSet(),
1341 this->GetDataDirectory() + "/" + "discards.dat"
1342 )) ret = false;
1343 this->GetDesignTarget().CheckinDiscards();
1344 }
1345
1346 this->_isFinalized = true;
1347
1348 return ret;
1349 }
1350
1351 const Design*
GetBestDesign()1352 GeneticAlgorithm::GetBestDesign(
1353 )
1354 {
1355 EDDY_FUNC_DEBUGSCOPE
1356
1357 DesignGroupVector gps(1, &this->GetPopulation());
1358
1359 DesignOFSortSet bests(this->GetSelector().SelectNBest(
1360 gps, 1, this->GetCurrentFitnesses()
1361 ));
1362
1363 return bests.empty() ? 0x0 : *bests.begin();
1364 }
1365
1366 /*
1367 ================================================================================
1368 Private Methods
1369 ================================================================================
1370 */
1371 const GeneticAlgorithmOperatorGroup*
MatchGroup(const GeneticAlgorithmOperatorSet & set) const1372 GeneticAlgorithm::MatchGroup(
1373 const GeneticAlgorithmOperatorSet& set
1374 ) const
1375 {
1376 EDDY_FUNC_DEBUGSCOPE
1377
1378 // go through all the possible groups until one is found that matches.
1379 // when one is found, make it the new group and return true.
1380 // if none are found, return false.
1381 const GeneticAlgorithmOperatorGroupRegistry& gReg =
1382 GetOperatorGroupRegistry();
1383
1384 GeneticAlgorithmOperatorGroupRegistry::const_iterator it(gReg.begin());
1385
1386 for(; it!=gReg.end(); ++it)
1387 {
1388 const GeneticAlgorithmOperatorGroup& cgp = (*it).second();
1389
1390 JEGALOG_II(this->GetLogger(), lverbose(), this,
1391 text_entry(
1392 ldebug(), this->GetName() + " Matching Group: Trying \"" +
1393 cgp.GetName() + "\"."
1394 )
1395 )
1396
1397 if(cgp.ContainsSet(set))
1398 {
1399 JEGALOG_II(this->GetLogger(), lverbose(), this,
1400 text_entry(ldebug(),
1401 this->GetName() + " Matching Group: \"" + cgp.GetName() +
1402 "\" match succeeded.")
1403 )
1404 return &cgp;
1405 }
1406 }
1407
1408 return 0x0;
1409 }
1410
1411 const GeneticAlgorithmOperatorGroupRegistry&
GetOperatorGroupRegistry_FWD()1412 GeneticAlgorithm::GetOperatorGroupRegistry_FWD(
1413 )
1414 {
1415 EDDY_FUNC_DEBUGSCOPE
1416 return this->GetOperatorGroupRegistry();
1417 }
1418
1419
1420
1421
1422
1423
1424 /*
1425 ================================================================================
1426 Structors
1427 ================================================================================
1428 */
GeneticAlgorithm(DesignTarget & target,Logger & logger)1429 GeneticAlgorithm::GeneticAlgorithm(
1430 DesignTarget& target,
1431 Logger& logger
1432 ) :
1433 _opGroup(0x0),
1434 _opSet(0x0),
1435 _pop(target),
1436 _cldrn(target),
1437 _target(target),
1438 _log(logger),
1439 _name(),
1440 _finalDataFile("finaldata#.dat"),
1441 _instanceNum(++_instanceCt),
1442 _printPopEachGen(false),
1443 _printFinalData(true),
1444 _printDiscards(true),
1445 _myDesignSpace(target.GetDesignSpace()),
1446 _isFinalized(false),
1447 _isInitialized(false),
1448 _lastFtns(nullptr),
1449 _dataDir("./"),
1450 _startTime(std::numeric_limits<clock_t>::max())
1451 {
1452 EDDY_FUNC_DEBUGSCOPE
1453 this->_opSet = new GeneticAlgorithmOperatorSet(*this);
1454 }
1455
~GeneticAlgorithm()1456 GeneticAlgorithm::~GeneticAlgorithm(
1457 )
1458 {
1459 EDDY_FUNC_DEBUGSCOPE
1460
1461 this->_pop.FlushAll();
1462 this->_cldrn.FlushAll();
1463 this->_opSet->DestroyOperators();
1464 delete this->_opSet;
1465
1466 JEGALOG_II(this->GetLogger(), lverbose(), this,
1467 text_entry(lquiet(), this->GetName() + ": goodbye!\n\n")
1468 )
1469
1470 this->GetLogger().FlushStreams();
1471 }
1472
1473
1474
1475
1476
1477 /*
1478 ================================================================================
1479 End Namespace
1480 ================================================================================
1481 */
1482 } // namespace Algorithms
1483 } // namespace JEGA
1484