ECF 1.5
Tree.cpp
1#include "../ECF_base.h"
2#include "Tree.h"
3#include <iostream>
4#include <sstream>
5#include <cmath>
6#include <sstream>
7
8// operators
9#include "TreeMutPermutation.h"
10#include "TreeMutGauss.h"
11#include "TreeMutNodeReplace.h"
12#include "TreeMutNodeComplement.h"
13#include "TreeMutHoist.h"
14#include "TreeMutShrink.h"
15#include "TreeMutSubtree.h"
16#include "TreeCrxSimple.h"
17#include "TreeCrxUniform.h"
18#include "TreeCrxContextPreserved.h"
19#include "TreeCrxSizeFair.h"
20#include "TreeCrxOnePoint.h"
21
22
23namespace Tree
24{
25
26Tree::Tree(void)
27{
28 name_ = "Tree";
29 startDepth_ = 0;
30}
31
32
33Tree::~Tree(void)
34{ }
35
36
37Tree* Tree::copy()
38{
39 Tree *newObject = new Tree(*this);
40
41 // create new nodes, copy primitives if necessary
42 for(int i = 0; i < (int) this->size(); i++) {
43 (*newObject)[i] = (NodeP) (new Node((*this)[i]));
44 }
45 return newObject;
46}
47
48
49std::vector<CrossoverOpP> Tree::getCrossoverOp()
50{
51 std::vector<CrossoverOpP> crx;
52 crx.push_back((CrossoverOpP) (new TreeCrxSimple));
53 crx.push_back((CrossoverOpP) (new TreeCrxContextPreserved));
54 crx.push_back((CrossoverOpP) (new TreeCrxUniform));
55 crx.push_back((CrossoverOpP) (new TreeCrxSizeFair));
56 crx.push_back((CrossoverOpP) (new TreeCrxOnePoint));
57 return crx;
58}
59
60
61std::vector<MutationOpP> Tree::getMutationOp()
62{
63 std::vector<MutationOpP> mut;
64 mut.push_back((MutationOpP) (new TreeMutPermutation));
65 mut.push_back((MutationOpP) (new TreeMutGauss));
66 mut.push_back((MutationOpP) (new TreeMutNodeReplace));
67 mut.push_back((MutationOpP) (new TreeMutNodeComplement));
68 mut.push_back((MutationOpP) (new TreeMutHoist));
69 mut.push_back((MutationOpP) (new TreeMutShrink));
70 mut.push_back((MutationOpP) (new TreeMutSubtree));
71
72 return mut;
73}
74
75
80bool Tree::addFunction(PrimitiveP func)
81{
82 userFunctions_.push_back(func);
83 return true;
84}
85
86
91bool Tree::addTerminal(PrimitiveP term)
92{
93 userTerminals_.push_back(term);
94 return true;
95}
96
97
98void Tree::registerParameters(StateP state)
99{
100 registerParameter(state, "maxdepth", (voidP) (new uint(5)), ECF::UINT,
101 "maximum tree depth (default: 5)");
102 registerParameter(state, "mindepth", (voidP) (new uint(1)), ECF::UINT,
103 "minimum tree depth (default: 1)");
104 registerParameter(state, "initmaxdepth", (voidP) (new uint(5)), ECF::UINT,
105 "maximum initial tree depth (default: 5)");
106 registerParameter(state, "initmindepth", (voidP) (new uint(1)), ECF::UINT,
107 "minimum initial tree depth (default: 1)");
108 registerParameter(state, "functionset", (voidP) (new std::string), ECF::STRING,
109 "set of functional tree elements (mandatory)");
110 registerParameter(state, "terminalset", (voidP) (new std::string), ECF::STRING,
111 "set of terminal tree elements (mandatory)");
112}
113
114
115bool Tree::initialize(StateP state)
116{
117 // 'hometree' is a Tree instance kept in the State;
118 // we use it to link the PrimitiveSet to it and store the parameters
119 Tree* hometree = (Tree*) state->getGenotypes()[genotypeId_].get();
120 state_ = state;
121
122 // if we are the first one to initialize
123 if(!hometree->primitiveSet_)
124 initializeFirst(hometree);
125
126 // in any case, read parameters from hometree
127 this->primitiveSet_ = hometree->primitiveSet_;
128 initMaxDepth_ = hometree->initMaxDepth_;
129 initMinDepth_ = hometree->initMinDepth_;
130 maxDepth_ = hometree->maxDepth_;
131 minDepth_ = hometree->minDepth_;
132
133 // build tree (TODO: reimplement for ramped-half-and-half)
134 if(state->getRandomizer()->getRandomInteger(0, 1) % 2 == 0)
135 fullBuild(primitiveSet_);
136 else
137 growBuild(primitiveSet_);
138
139 return true;
140}
141
142
149void Tree::initializeFirst(Tree* hometree)
150{
151 // create and link PrimitiveSet to the hometree
152 hometree->primitiveSet_ = (PrimitiveSetP) (new PrimitiveSet);
153 hometree->primitiveSet_->initialize(state_);
154 this->primitiveSet_ = hometree->primitiveSet_;
155
156 // add user defined functions to primitiveSet
157 for(int i = 0; i < (int) userFunctions_.size(); i++) {
158 primitiveSet_->mAllPrimitives_[userFunctions_[i]->getName()] = userFunctions_[i];
159 primitiveSet_->mAllPrimitives_[userFunctions_[i]->getName()]->initialize(state_);
160 }
161
162 // read function set from the configuration
163 voidP sptr = getParameterValue(state_, "functionset");
164 std::stringstream names;
165 std::string name;
166 names << *((std::string*) sptr.get());
167 while(names >> name) {
168 if(!primitiveSet_->addFunction(name)) {
169 ECF_LOG_ERROR(state_, "Error: unknown function in function set (\'" + name + "\')!");
170 throw("");
171 }
172 }
173 if(primitiveSet_->getFunctionSetSize() == 0) {
174 ECF_LOG_ERROR(state_, "Tree genotype: empty function set!");
175 throw("");
176 }
177
178 // set default terminal type
179 Primitives::terminal_type currentType = Primitives::Double;
180 type_iter typeIter;
181
182 // read terminal set from the configuration
183 std::stringstream tNames;
184 sptr = getParameterValue(state_, "terminalset");
185 tNames << *((std::string*) sptr.get());
186
187 while(tNames >> name)
188 {
189 // read current terminal type (if set)
190 typeIter = primitiveSet_->mTypeNames_.find(name);
191 if(typeIter != primitiveSet_->mTypeNames_.end()) {
192 currentType = typeIter->second;
193 continue;
194 }
195
196 // see if it's a user-defined terminal
197 uint iTerminal = 0;
198 for(; iTerminal < userTerminals_.size(); iTerminal++)
199 if(userTerminals_[iTerminal]->getName() == name)
200 break;
201 if(iTerminal < userTerminals_.size()) {
202 userTerminals_[iTerminal]->initialize(state_);
203 primitiveSet_->addTerminal(userTerminals_[iTerminal]);
204 continue;
205 }
206
207
208 // read ERC definition (if set)
209 // ERC's are defined as interval [x y] or set {a b c}
210 if(name[0] == '[' || name[0] == '{') {
211
212 std::string ercValues = "";
213
214 // name this ERC (ERC's name is its value!)
215 PrimitiveP erc;
216 switch(currentType) {
217 case Primitives::Double:
218 erc = (PrimitiveP) (new Primitives::ERCD);
219 ercValues = DBL_PREFIX;
220 break;
221 case Primitives::Int:
222 erc = (PrimitiveP) (new Primitives::ERC<int>);
223 ercValues = INT_PREFIX;
224 break;
225 case Primitives::Bool:
226 erc = (PrimitiveP) (new Primitives::ERC<bool>);
227 ercValues = BOOL_PREFIX;
228 break;
229 case Primitives::Char:
230 erc = (PrimitiveP) (new Primitives::ERC<char>);
231 ercValues = CHR_PREFIX;
232 break;
233 case Primitives::String:
234 erc = (PrimitiveP) (new Primitives::ERC<std::string>);
235 ercValues = STR_PREFIX;
236 break;
237 }
238
239 while(name[name.size() - 1] != ']' && name[name.size() - 1] != '}') {
240 ercValues += " " + name;
241 tNames >> name;
242 }
243 ercValues += " " + name;
244
245 // ERC ocekuje "D_ [<value> <value> ...]" kao ime prije initialize()
246 erc->setName(ercValues);
247 erc->initialize(state_);
248 primitiveSet_->addTerminal(erc);
249
250 continue;
251 }
252
253
254 // otherwise, read terminal of current type
255 PrimitiveP terminal;
256 switch(currentType)
257 {
258 case Primitives::Double:
259 terminal = (PrimitiveP) (new Primitives::Terminal); break;
260 case Primitives::Int:
261 terminal = (PrimitiveP) (new Primitives::TerminalT<int>); break;
262 case Primitives::Bool:
263 terminal = (PrimitiveP) (new Primitives::TerminalT<bool>); break;
264 case Primitives::Char:
265 terminal = (PrimitiveP) (new Primitives::TerminalT<char>); break;
266 case Primitives::String:
267 terminal = (PrimitiveP) (new Primitives::TerminalT<std::string>); break;
268 }
269
270 // if the 'name' can be identified as a value of the 'currentType', then it's a _constant terminal_ (of that value)
271 // otherwise, it's a regular terminal with 'name'
272 std::istringstream ss(name);
273 switch(currentType)
274 {
275 case Primitives::Double:
276 double dblValue;
277 ss >> dblValue;
278 if(ss.fail() == false)
279 terminal->setValue(&dblValue);
280 break;
281 case Primitives::Int:
282 int intValue;
283 ss >> intValue;
284 if(ss.fail() == false)
285 terminal->setValue(&intValue);
286 break;
287 case Primitives::Bool:
288 bool boolValue;
289 ss >> boolValue;
290 if(name == "true")
291 boolValue = true;
292 else if(name == "false")
293 boolValue = false;
294 if(ss.fail() == false || name == "true" || name == "false") {
295 if(boolValue)
296 name = "true";
297 else
298 name = "false";
299 terminal->setValue(&boolValue);
300 }
301 break;
302 case Primitives::Char:
303 char charValue;
304 ss >> charValue;
305 if(ss.fail() == false)
306 terminal->setValue(&charValue);
307 break;
308 case Primitives::String:
309 std::string stringValue;
310 ss >> stringValue;
311 if(ss.fail() == false)
312 terminal->setValue(&stringValue);
313 break;
314 }
315 terminal->setName(name);
316 primitiveSet_->addTerminal(terminal);
317
318 }
319
320 if(primitiveSet_->getTerminalSetSize() == 0) {
321 ECF_LOG_ERROR(state_, "Tree: Empty terminal set!");
322 throw("");
323 }
324
325 // read tree depth constraints, store in hometree
326 sptr = getParameterValue(state_, "maxdepth");
327 hometree->maxDepth_ = *((uint*) sptr.get());
328 sptr = getParameterValue(state_, "mindepth");
329 hometree->minDepth_ = *((uint*) sptr.get());
330 if(hometree->maxDepth_ < hometree->minDepth_ || hometree->maxDepth_ < 1) {
331 ECF_LOG_ERROR(state_, "Tree genotype: invalid values for max and min tree depth!");
332 throw("");
333 }
334
335 hometree->initMaxDepth_ = hometree->maxDepth_;
336 if(isParameterDefined(state_, "initmaxdepth")) {
337 sptr = getParameterValue(state_, "initmaxdepth");
338 hometree->initMaxDepth_ = *((uint*) sptr.get());
339 }
340 hometree->initMinDepth_ = hometree->minDepth_;
341 if(isParameterDefined(state_, "initmindepth")) {
342 sptr = getParameterValue(state_, "initmindepth");
343 hometree->initMinDepth_ = *((uint*) sptr.get());
344 }
345 if(hometree->initMaxDepth_ < hometree->initMinDepth_ || hometree->initMaxDepth_ < 1) {
346 ECF_LOG_ERROR(state_, "Tree genotype: invalid values for initial max and min tree depth!");
347 throw("");
348 }
349 if(hometree->initMaxDepth_ > hometree->maxDepth_) {
350 ECF_LOG_ERROR(state_, "Tree genotype: initial max depth cannot be greater than evolution max depth!");
351 throw("");
352 }
353}
354
355
362void Tree::execute(void *result)
363{
364 iNode_ = 0;
365 this->at(iNode_)->primitive_->execute(result, *this);
366}
367
368
369void Tree::addNode(Node *node)
370{
371 this->push_back(static_cast<NodeP> (node));
372}
373
374
375void Tree::addNode(NodeP node)
376{
377 this->push_back(node);
378}
379
380
382uint Tree::fullBuild(PrimitiveSetP primitiveSet, uint myDepth)
383{
384 Node* node = new Node();
385 node->depth_ = myDepth;
386
387 if(node->depth_ < this->initMaxDepth_) {
388 node->setPrimitive(primitiveSet->getRandomFunction());
389 this->addNode(node);
390 } else {
391 node->setPrimitive(primitiveSet->getRandomTerminal());
392 this->addNode(node);
393 }
394
395 for(int i = 0; i < node->primitive_->getNumberOfArguments(); i++ ) {
396 node->size_ += fullBuild(primitiveSet, myDepth + 1);
397 }
398
399 return node->size_;
400}
401
402
404uint Tree::growBuild(PrimitiveSetP primitiveSet, uint myDepth)
405{
406 Node* node = new Node();
407 node->depth_ = myDepth;
408
409 if(node->depth_ < this->initMinDepth_) {
410 node->setPrimitive(primitiveSet->getRandomFunction());
411 this->addNode(node);
412 }
413 else if(node->depth_ < this->initMaxDepth_) {
414 node->setPrimitive(primitiveSet->getRandomPrimitive());
415 this->addNode(node);
416 }
417 else {
418 node->setPrimitive(primitiveSet->getRandomTerminal());
419 this->addNode(node);
420 }
421
422 for(int i = 0; i < node->primitive_->getNumberOfArguments(); i++) {
423 node->size_ += growBuild(primitiveSet, myDepth + 1);
424 }
425
426 return node->size_;
427}
428
429
435void Tree::update()
436{
437 this->at(0)->size_ = setSize(0);
438
439 iNode_ = 0;
440 for(int i = 0; i < this->at(0)->primitive_->getNumberOfArguments(); i++) {
441 iNode_++;
442 setDepth(startDepth_ + 1);
443 }
444 this->at(0)->depth_ = startDepth_;
445}
446
447
451uint Tree::setSize(uint iNode)
452{
453 uint myNode = iNode;
454 uint mySize = 1;
455 for(int i = 0; i < this->at(myNode)->primitive_->getNumberOfArguments(); i++) {
456 uint childSize = setSize(iNode + 1);
457 mySize += childSize;
458 iNode += childSize;
459 }
460 this->at(myNode)->size_ = mySize;
461 return mySize;
462}
463
464
468void Tree::setDepth(uint myDepth)
469{
470 uint index = iNode_;
471 int nArgs = this->at( iNode_ )->primitive_->getNumberOfArguments();
472 for(int i = 0; i < nArgs; i++) {
473 iNode_++;
474 setDepth(myDepth + 1);
475 }
476 this->at(index)->depth_ = myDepth;
477}
478
479
483void Tree::growBuild(PrimitiveSetP primitiveSet)
484{
485 growBuild(primitiveSet, startDepth_);
486}
487
488
492void Tree::fullBuild(PrimitiveSetP primitiveSet)
493{
494 fullBuild(primitiveSet, startDepth_);
495}
496
497
504void Tree::setTerminalValue(std::string name, void* value)
505{
506 PrimitiveP term = primitiveSet_->getTerminalByName(name);
507 if(term == PrimitiveP()) {
508 ECF_LOG_ERROR(state_, "Tree genotype: invalid terminal name referenced in setTerminalValue()!");
509 throw("");
510 }
511
512 term->setValue(value);
513}
514
515
522void Tree::getTerminalValue(std::string name, void* value)
523{
524 PrimitiveP term = primitiveSet_->getTerminalByName(name);
525 if(term == PrimitiveP()) {
526 ECF_LOG_ERROR(state_, "Tree genotype: invalid terminal name referenced in getTerminalValue()!");
527 throw("");
528 }
529
530 term->getValue(value);
531}
532
533
534
535void Tree::write(XMLNode& xTree)
536{
537 xTree = XMLNode::createXMLTopNode("Tree");
538 std::stringstream sValue;
539 sValue << this->size();
540 xTree.addAttribute("size", sValue.str().c_str());
541
542 sValue.str("");
543 for(uint i = 0; i < this->size(); i++) {
544 sValue << this->at(i)->primitive_->getName() << " ";
545 }
546 xTree.addText(sValue.str().c_str());
547}
548
549
550void Tree::read(XMLNode& xTree)
551{
552 this->clear();
553
554 XMLCSTR sizeStr = xTree.getAttribute("size");
555 uint size = str2uint(sizeStr);
556
557 XMLCSTR tree = xTree.getText();
558 std::stringstream stream;
559 stream << tree;
560
561 std::vector<PrimitiveP>& primitives = primitiveSet_->primitives_;
562 std::string primitiveStr;
563 uint position = 0;
564
565 for(uint iNode = 0; iNode < size; iNode++) {
566 stream >> primitiveStr;
567 Node* node = new Node();
568
569 // 'regular' primitives
570 PrimitiveP prim = primitiveSet_->getPrimitiveByName(primitiveStr);
571 if(prim != PrimitiveP()) {
572 node->setPrimitive(prim);
573 this->addNode(node);
574 continue;
575 }
576
577 // ERCs
578 // (TODO: include user defined ERC types)
579 PrimitiveP erc;
580 std::string prefix = primitiveStr.substr(0, 2);
581 std::string value = primitiveStr.substr(2);
582 std::stringstream ss;
583 ss << value;
584 if(prefix == DBL_PREFIX) {
585 erc = (PrimitiveP) (new Primitives::ERCD);
586 double v;
587 ss >> v;
588 erc->setValue(&v);
589 }
590 else if(prefix == INT_PREFIX) {
591 erc = (PrimitiveP) (new Primitives::ERC<int>);
592 int v;
593 ss >> v;
594 erc->setValue(&v);
595 }
596 else if(prefix == BOOL_PREFIX) {
597 erc = (PrimitiveP) (new Primitives::ERC<bool>);
598 bool v;
599 ss >> v;
600 erc->setValue(&v);
601 }
602 else if(prefix == CHR_PREFIX) {
603 erc = (PrimitiveP) (new Primitives::ERC<char>);
604 char v;
605 ss >> v;
606 erc->setValue(&v);
607 }
608 else if(prefix == STR_PREFIX) {
609 erc = (PrimitiveP) (new Primitives::ERC<std::string>);
610 std::string v;
611 ss >> v;
612 erc->setValue(&v);
613 }
614 else {
615 ECF_LOG_ERROR(state_, "Tree genotype: undefined primitive (" + primitiveStr + ")!");
616 throw("");
617 }
618 erc->setName(primitiveStr);
619 node->primitive_ = erc;
620 this->addNode(node);
621 }
622
623 // set node depths and sizes
624 this->update();
625}
626
627}
Node base class (Tree genotype)
Definition: Node.h:20
Primitive set class: collects all Tree Primitives.
Definition: PrimitiveSet.h:18
Ephemereal random constant (ERC) node of type double (Tree genotype).
Definition: Terminal.h:189
Ephemereal random constant (ERC) node class (Tree genotype).
Definition: Terminal.h:86
Terminal tree node class (Tree genotype).
Definition: Terminal.h:16
Tree genotype: context presevation crx operator. Tries to make crossover at the 'same' point in both ...
Tree genotype: one point crx operator. Tries to select a crossing point in parent tree's common regio...
Tree genotype: simple tree crossover operator (with default 90% bias toward functional node) Referenc...
Definition: TreeCrxSimple.h:14
Tree genotype: size fair crx operator. Reference: http://dces.essex.ac.uk/staff/rpoli/gp-field-guide/...
Tree genotype: uniform crx operator. Reference: http://dces.essex.ac.uk/staff/rpoli/gp-field-guide/53...
Tree class - implements genotype as a tree.
Definition: Tree_c.h:29
Tree genotype: standard normal distribution noise mutation operator. Applicable only on ephemereal ra...
Definition: TreeMutGauss.h:16
Tree genotype: mutation operator that replaces original tree with a randomly chosen subtree from the ...
Definition: TreeMutHoist.h:13
Tree genotype: complement node mutation operator. For the operator to succeed, the chosen primitive m...
Tree genotype: node replacement mutation operator. Tries to replace the selected primitive with a dif...
Tree genotype: permutation mutation operator.
Tree genotype: mutation operator that shrinks randomly chosen subtree.
Definition: TreeMutShrink.h:13
Tree genotype: subtree size-fair mutation operator. This is a 'standard' GP subtree mutation.
Definition: nodes.h:92