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