1 /**************************************************************************/
2 /* Copyright 2012 Tim Day */
3 /* */
4 /* This file is part of Evolvotron */
5 /* */
6 /* Evolvotron is free software: you can redistribute it and/or modify */
7 /* it under the terms of the GNU General Public License as published by */
8 /* the Free Software Foundation, either version 3 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* Evolvotron is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU General Public License */
17 /* along with Evolvotron. If not, see <http://www.gnu.org/licenses/>. */
18 /**************************************************************************/
19
20 /*! \file
21 \brief Implementation of class FunctionNode and derived classes.
22 */
23
24
25 #include "function_node.h"
26
27 #include "function_compose_pair.h"
28 #include "function_constant.h"
29 #include "function_node_info.h"
30 #include "function_registry.h"
31 #include "margin.h"
32 #include "mutation_parameters.h"
33
cloneargs() const34 std::unique_ptr<boost::ptr_vector<FunctionNode> > FunctionNode::cloneargs() const
35 {
36 std::unique_ptr<boost::ptr_vector<FunctionNode> > ret(new boost::ptr_vector<FunctionNode>());
37 for (boost::ptr_vector<FunctionNode>::const_iterator it=args().begin();it!=args().end();it++)
38 {
39 ret->push_back(it->deepclone().release());
40 }
41 return ret;
42 }
43
cloneparams() const44 const std::vector<real> FunctionNode::cloneparams() const
45 {
46 return params();
47 }
48
49 //! Obtain some statistics about the image function
get_stats(uint & total_nodes,uint & total_parameters,uint & depth,uint & width,real & proportion_constant) const50 void FunctionNode::get_stats(uint& total_nodes,uint& total_parameters,uint& depth,uint& width,real& proportion_constant) const
51 {
52 uint total_sub_nodes=0;
53 uint total_sub_parameters=0;
54 uint max_sub_depth=0;
55 uint total_sub_width=0;
56 real sub_constants=0.0;
57
58 // Traverse child nodes. Need to reconstruct the actual numbers from the proportions
59 for (boost::ptr_vector<FunctionNode>::const_iterator it=args().begin();it!=args().end();it++)
60 {
61 uint sub_nodes;
62 uint sub_parameters;
63 uint sub_depth;
64 uint sub_width;
65 real sub_proportion_constant;
66
67 it->get_stats(sub_nodes,sub_parameters,sub_depth,sub_width,sub_proportion_constant);
68
69 total_sub_nodes+=sub_nodes;
70 total_sub_parameters+=sub_parameters;
71 if (sub_depth>max_sub_depth) max_sub_depth=sub_depth;
72 total_sub_width+=sub_width;
73 sub_constants+=sub_nodes*sub_proportion_constant;
74 }
75
76 // And add our own details
77 total_nodes=1+total_sub_nodes;
78
79 total_parameters=params().size()+total_sub_parameters;
80
81 depth=1+max_sub_depth;
82
83 if (total_sub_width==0)
84 {
85 width=1;
86 }
87 else
88 {
89 width=total_sub_width;
90 }
91
92 if (is_constant())
93 {
94 proportion_constant=1.0;
95 }
96 else
97 {
98 proportion_constant=sub_constants/(1+total_sub_nodes);
99 }
100 }
101
verify_info(const FunctionNodeInfo & info,unsigned int np,unsigned int na,bool it,std::string & report)102 bool FunctionNode::verify_info(const FunctionNodeInfo& info,unsigned int np,unsigned int na,bool it,std::string& report)
103 {
104 if (info.params().size()!=np)
105 {
106 std::stringstream msg;
107 msg << "Error: For function " << info.type() << ": expected " << np << " parameters, but found " << info.params().size() << "\n";
108 report+=msg.str();
109 return false;
110 }
111 if (info.args().size()!=na)
112 {
113 std::stringstream msg;
114 msg << "Error: For function " << info.type() << ": expected " << na << " arguments, but found " << info.args().size() << "\n";
115 report+=msg.str();
116 return false;
117 }
118 if (info.iterations()!=0 && !it)
119 {
120 std::stringstream msg;
121 msg << "Error: For function " << info.type() << ": unexpected iteration count\n";
122 report+=msg.str();
123 return false;
124 }
125 if (info.iterations()==0 && it)
126 {
127 std::stringstream msg;
128 msg << "Error: For function " << info.type() << ": expected iteration count but none found\n";
129 report+=msg.str();
130 return false;
131 }
132 return true;
133 }
134
is_constant() const135 bool FunctionNode::is_constant() const
136 {
137 if (args().empty()) return false;
138 for (unsigned int i=0;i<args().size();i++)
139 {
140 if (!arg(i).is_constant()) return false;
141 }
142 return true;
143 }
144
ok() const145 bool FunctionNode::ok() const
146 {
147 bool good=true;
148 for (boost::ptr_vector<FunctionNode>::const_iterator it=args().begin();good && it!=args().end();it++)
149 {
150 good=(*it).ok();
151 }
152
153 return good;
154 }
155
create_args(const FunctionRegistry & function_registry,const FunctionNodeInfo & info,boost::ptr_vector<FunctionNode> & args,std::string & report)156 bool FunctionNode::create_args(const FunctionRegistry& function_registry,const FunctionNodeInfo& info,boost::ptr_vector<FunctionNode>& args,std::string& report)
157 {
158 for (boost::ptr_vector<FunctionNodeInfo>::const_iterator it=info.args().begin();it!=info.args().end();it++)
159 {
160 std::unique_ptr<FunctionNode> fn(FunctionNode::create(function_registry,*it,report));
161 // Check whether something has gone wrong. If it has, delete everything allocated so far and return false.
162 if (!fn.get())
163 {
164 args.clear();
165 return false;
166 }
167 args.push_back(fn.release());
168 }
169 return true;
170 }
171
stub(const MutationParameters & parameters,bool exciting)172 std::unique_ptr<FunctionNode> FunctionNode::stub(const MutationParameters& parameters,bool exciting)
173 {
174 return parameters.random_function_stub(exciting);
175 }
176
177 /*! This setus up a vector of random bits of stub, used for initialiing nodes with children.
178 */
stubargs(boost::ptr_vector<FunctionNode> & v,const MutationParameters & parameters,uint n,bool exciting)179 void FunctionNode::stubargs(boost::ptr_vector<FunctionNode>& v,const MutationParameters& parameters,uint n,bool exciting)
180 {
181 assert(v.empty());
182 for (uint i=0;i<n;i++)
183 v.push_back(stub(parameters,exciting).release());
184 }
185
stubparams(std::vector<real> & v,const MutationParameters & parameters,uint n)186 void FunctionNode::stubparams(std::vector<real>& v,const MutationParameters& parameters,uint n)
187 {
188 assert(v.empty());
189 for (uint i=0;i<n;i++)
190 v.push_back((parameters.r01() < 0.5f ? -1.0f : 1.0f)*parameters.rnegexp());
191 }
192
stubiterations(const MutationParameters & parameters)193 uint FunctionNode::stubiterations(const MutationParameters& parameters)
194 {
195 return 1+static_cast<uint>(floor(parameters.r01()*parameters.max_initial_iterations()));
196 }
197
FunctionNode(const std::vector<real> & p,boost::ptr_vector<FunctionNode> & a,uint iter)198 FunctionNode::FunctionNode(const std::vector<real>& p,boost::ptr_vector<FunctionNode>& a,uint iter)
199 :_args(a.release())
200 ,_params(p)
201 ,_iterations(iter)
202 {}
203
204 /*! Returns null ptr if there's a problem, in which case there will be an explanation in report.
205 */
create(const FunctionRegistry & function_registry,const FunctionNodeInfo & info,std::string & report)206 std::unique_ptr<FunctionNode> FunctionNode::create(const FunctionRegistry& function_registry,const FunctionNodeInfo& info,std::string& report)
207 {
208 const FunctionRegistration*const reg=function_registry.lookup(info.type());
209 if (reg)
210 {
211 return std::unique_ptr<FunctionNode>((*(reg->create_fn()))(function_registry,info,report));
212 }
213 else
214 {
215 report+="Error: Unrecognised function name: "+info.type()+"\n";
216 return std::unique_ptr<FunctionNode>();
217 }
218 }
219
220 /*! Deletes all arguments. No one else should be referencing nodes except the root node of an image.
221 */
~FunctionNode()222 FunctionNode::~FunctionNode()
223 {}
224
225 /*! There are 2 kinds of mutation:
226 - random adjustments to constants
227 - structural mutations (messing with the function tree)
228 For structural mutations the obvious things to do are:
229 - reordering argsuments
230 - dropping argsuments and replacing them with new "stubs".
231 - duplicating arguments
232 - substituting nodes with other types (can't do this for ourself very easily, but we can do it for children)
233 - inserting new nodes between children and ourself
234
235 And of course all children have to be mutated too.
236 */
mutate(const MutationParameters & parameters,bool mutate_own_parameters)237 void FunctionNode::mutate(const MutationParameters& parameters,bool mutate_own_parameters)
238 {
239 // First mutate all child nodes.
240 for (boost::ptr_vector<FunctionNode>::iterator it=args().begin();it!=args().end();it++)
241 it->mutate(parameters);
242
243 // Perturb any parameters we have
244 if (mutate_own_parameters)
245 {
246 if (parameters.r01()<parameters.effective_probability_parameter_reset())
247 {
248 std::vector<real> p;
249 stubparams(p,parameters,params().size());
250 params(p);
251 }
252 else
253 {
254 for (std::vector<real>::iterator it=params().begin();it!=params().end();it++)
255 {
256 (*it)+=parameters.effective_magnitude_parameter_variation()*(parameters.r01()<0.5 ? -parameters.rnegexp() : parameters.rnegexp());
257 }
258 }
259 }
260
261 // Perturb iteration count if any
262 if (_iterations)
263 {
264 if (parameters.r01()<parameters.effective_probability_iterations_change_step())
265 {
266 if (parameters.r01()<0.5)
267 {
268 if (_iterations>=2) _iterations--;
269 }
270 else
271 {
272 _iterations++;
273 }
274 if (parameters.r01()<parameters.effective_probability_iterations_change_jump())
275 {
276 if (parameters.r01()<0.5)
277 {
278 if (_iterations>1) _iterations=(_iterations+1)/2;
279 }
280 else
281 {
282 _iterations*=2;
283 }
284 }
285
286 // For safety but shouldn't happen
287 if (_iterations==0) _iterations=1;
288 }
289 }
290
291
292 // Then go to work on the argument structure...
293
294 // Think about glitching some nodes.
295 for (uint i=0;i<args().size();i++)
296 {
297 if (parameters.r01()<parameters.effective_probability_glitch())
298 {
299 args().replace(i,stub(parameters,false).release());
300 }
301 }
302
303 // Think about substituting some nodes.
304 //! \todo Substitution might make more sense if it was for a node with the same/similar number of arguments.
305 for (uint i=0;i<args().size();i++)
306 {
307 if (parameters.r01()<parameters.effective_probability_substitute())
308 {
309 // Take a copy of the nodes parameters and arguments
310 std::unique_ptr<boost::ptr_vector<FunctionNode> > a(args()[i].deepclone_args());
311 std::vector<real> p(args()[i].params());
312
313 // Replace the node with something interesting (maybe this should depend on how complex the original node was)
314 args().replace(i,stub(parameters,false).release());
315
316 FunctionNode& it=args()[i];
317 // Do we need some extra arguments ?
318 if (a->size()<it.args().size())
319 {
320 boost::ptr_vector<FunctionNode> xa;
321 stubargs(xa,parameters,it.args().size()-a->size());
322 a->transfer(a->end(),xa.begin(),xa.end(),xa);
323 }
324 // Shuffle them
325 random_shuffle(*a,parameters.rng01());
326 // Have we got too many arguments ?
327 while (a->size()>it.args().size())
328 {
329 a->pop_back();
330 }
331
332 // Do we need some extra parameters ?
333 if (p.size()<it.params().size())
334 {
335 std::vector<real> xp;
336 stubparams(xp,parameters,it.params().size()-p.size());
337 p.insert(p.end(),xp.begin(),xp.end());
338 }
339 // Shuffle them
340 random_shuffle(p,parameters.rng01());
341 // Have we go too many arguments ?
342 while (p.size()>it.params().size())
343 {
344 p.pop_back();
345 }
346
347 // Impose the new parameters and arguments on the new node (iterations not touched)
348 it.args(*a);
349 it.params()=p;
350 }
351 }
352
353 // Think about randomising child order
354 if (parameters.r01()<parameters.effective_probability_shuffle())
355 {
356 random_shuffle(args(),parameters.rng01());
357 }
358
359 // Think about inserting a random stub between us and some subnodes
360 for (uint i=0;i<args().size();i++)
361 {
362 if (parameters.r01()<parameters.effective_probability_insert())
363 {
364 boost::ptr_vector<FunctionNode> a;
365 a.transfer(a.begin(),args().begin()+i,args());
366 a.push_back(stub(parameters,false).release());
367
368 std::vector<real> p;
369 args().insert(args().begin()+i,new FunctionComposePair(p,a,0));
370 }
371 }
372 }
373
simplify_constants()374 void FunctionNode::simplify_constants()
375 {
376 for (uint i=0;i<args().size();i++)
377 {
378 if (args()[i].is_constant())
379 {
380 const XYZ v(args()[i](XYZ(0.0,0.0,0.0)));
381 std::vector<real> vp;
382 vp.push_back(v.x());
383 vp.push_back(v.y());
384 vp.push_back(v.z());
385 boost::ptr_vector<FunctionNode> va;
386 std::unique_ptr<FunctionConstant> replacement(new FunctionConstant(vp,va,0));
387 args().replace(i,replacement.release());
388 }
389 else
390 {
391 args()[i].simplify_constants();
392 }
393 }
394 }
395
deepclone_args() const396 std::unique_ptr<boost::ptr_vector<FunctionNode> > FunctionNode::deepclone_args() const
397 {
398 std::unique_ptr<boost::ptr_vector<FunctionNode> > ret(new boost::ptr_vector<FunctionNode>());
399 for (boost::ptr_vector<FunctionNode>::const_iterator it=args().begin();it!=args().end();it++)
400 ret->push_back(it->deepclone().release());
401 return ret;
402 }
403
is_a_FunctionTop() const404 const FunctionTop* FunctionNode::is_a_FunctionTop() const
405 {
406 return 0;
407 }
408
is_a_FunctionTop()409 FunctionTop* FunctionNode::is_a_FunctionTop()
410 {
411 return 0;
412 }
413
414 /*! This function only saves the parameters, iteration count if any and child nodes.
415 It is intended to be called from save_function of subclasses which will write a function node wrapper.
416 The indent number is just the level of recursion, incrementing by 1 each time.
417 Outputting multiple spaces per level is handled by the Margin class.
418 */
save_function(std::ostream & out,uint indent,const std::string & function_name) const419 std::ostream& FunctionNode::save_function(std::ostream& out,uint indent,const std::string& function_name) const
420 {
421 out << Margin(indent) << "<f>\n";
422 out << Margin(indent+1) << "<type>" << function_name << "</type>\n";
423
424 if (iterations()!=0) out << Margin(indent+1) << "<i>" << iterations() << "</i>\n";
425
426 for (std::vector<real>::const_iterator it=params().begin();it!=params().end();it++)
427 {
428 out << Margin(indent+1) << "<p>" << (*it) << "</p>\n";
429 }
430
431 for (boost::ptr_vector<FunctionNode>::const_iterator it=args().begin();it!=args().end();it++)
432 {
433 (*it).save_function(out,indent+1);
434 }
435
436 out << Margin(indent) << "</f>\n";
437 return out;
438 }
439