1 /*
2  *  Open BEAGLE
3  *  Copyright (C) 2001-2007 by Christian Gagne and Marc Parizeau
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2.1 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *  Contact:
20  *  Laboratoire de Vision et Systemes Numeriques
21  *  Departement de genie electrique et de genie informatique
22  *  Universite Laval, Quebec, Canada, G1K 7P4
23  *  http://vision.gel.ulaval.ca
24  *
25  */
26 
27 /*!
28  *  \file   beagle/GP/src/CrossoverOp.cpp
29  *  \brief  Source code of class GP::CrossoverOp.
30  *  \author Christian Gagne
31  *  \author Marc Parizeau
32  *  $Revision: 1.12.2.1 $
33  *  $Date: 2007/05/09 01:51:06 $
34  */
35 
36 #include "beagle/GP.hpp"
37 
38 #include <algorithm>
39 #include <string>
40 
41 using namespace Beagle;
42 
43 
44 /*!
45  *  \brief Construct a GP crossover operator.
46  *  \param inMatingPbName Individual mating probability parameter name used in register.
47  *  \param inDistribPbName Distribution probability parameter name used in register.
48  *  \param inName Name of the operator.
49  */
CrossoverOp(Beagle::string inMatingPbName,Beagle::string inDistribPbName,Beagle::string inName)50 GP::CrossoverOp::CrossoverOp(Beagle::string inMatingPbName,
51                              Beagle::string inDistribPbName,
52                              Beagle::string inName) :
53   Beagle::CrossoverOp(inMatingPbName, inName),
54   mDistribPbName(inDistribPbName)
55 { }
56 
57 
58 /*!
59  *  \brief Initialize the GP crossover operator.
60  *  \param ioSystem System of the evolution.
61  */
initialize(Beagle::System & ioSystem)62 void GP::CrossoverOp::initialize(Beagle::System& ioSystem)
63 {
64   Beagle_StackTraceBeginM();
65   Beagle::CrossoverOp::initialize(ioSystem);
66   if(ioSystem.getRegister().isRegistered(mMatingProbaName)) {
67     ioSystem.getRegister().deleteEntry(mMatingProbaName);
68   }
69 
70   if(ioSystem.getRegister().isRegistered(mMatingProbaName)) {
71     mMatingProba = castHandleT<Float>(ioSystem.getRegister()[mMatingProbaName]);
72   } else {
73     mMatingProba = new Float(float(0.9));
74     Register::Description lDescription(
75       "Individual crossover probability",
76       "Float",
77       "0.9",
78       "Individual crossover probability at each generation."
79     );
80     ioSystem.getRegister().addEntry(mMatingProbaName, mMatingProba, lDescription);
81   }
82 
83   if(ioSystem.getRegister().isRegistered(mDistribPbName)) {
84     mDistributionProba = castHandleT<Float>(ioSystem.getRegister()[mDistribPbName]);
85   } else {
86     mDistributionProba = new Float(float(0.9));
87     string lLongDescrip = "Probability that a crossover point is a branch ";
88     lLongDescrip += "(node with sub-trees). Value of 1.0 means that all crossover points are ";
89     lLongDescrip += "branches, and value of 0.0 means that all crossover points are leaves.";
90     Register::Description lDescription(
91       "Crossover distribution prob.",
92       "Float",
93       "0.9",
94       lLongDescrip
95     );
96     ioSystem.getRegister().addEntry(mDistribPbName, mDistributionProba, lDescription);
97   }
98 
99   if(ioSystem.getRegister().isRegistered("gp.tree.maxdepth")) {
100     mMaxTreeDepth = castHandleT<UInt>(ioSystem.getRegister()["gp.tree.maxdepth"]);
101   } else {
102     mMaxTreeDepth = new UInt(17);
103     Register::Description lDescription(
104       "Maximum tree depth",
105       "UInt",
106       "17",
107       "Maximum allowed depth for the trees."
108     );
109     ioSystem.getRegister().addEntry("gp.tree.maxdepth", mMaxTreeDepth, lDescription);
110   }
111 
112   if(ioSystem.getRegister().isRegistered("gp.try")) {
113     mNumberAttempts = castHandleT<UInt>(ioSystem.getRegister()["gp.try"]);
114   } else {
115     mNumberAttempts = new UInt(2);
116     string lLongDescrip = "Maximum number of attempts to modify a GP tree in a genetic ";
117     lLongDescrip += "operation. As there is topological constraints on GP trees (i.e. tree ";
118     lLongDescrip += "depth limit), it is often necessary to try a genetic operation several times.";
119     Register::Description lDescription(
120       "Max number of attempts",
121       "UInt",
122       "2",
123       lLongDescrip
124     );
125     ioSystem.getRegister().addEntry("gp.try", mNumberAttempts, lDescription);
126   }
127   Beagle_StackTraceEndM("void GP::CrossoverOp::initialize(Beagle::System& ioSystem)");
128 }
129 
130 
131 /*!
132  *  \brief Mate two GP individuals for a crossover.
133  *  \param ioIndiv1   First individual to mate.
134  *  \param ioContext1 Evolutionary context of the first individual.
135  *  \param ioIndiv2   Second individual to mate.
136  *  \param ioContext2 Evolutionary context of the second individual.
137  *  \return True if the individuals are effectively mated, false if not.
138  */
mate(Beagle::Individual & ioIndiv1,Beagle::Context & ioContext1,Beagle::Individual & ioIndiv2,Beagle::Context & ioContext2)139 bool GP::CrossoverOp::mate(Beagle::Individual& ioIndiv1, Beagle::Context& ioContext1,
140                            Beagle::Individual& ioIndiv2, Beagle::Context& ioContext2)
141 {
142   Beagle_StackTraceBeginM();
143   // Initial parameters checks
144   Beagle_AssertM(ioIndiv1.size() > 0);
145   Beagle_AssertM(ioIndiv1.size() == ioIndiv2.size());
146   Beagle_ValidateParameterM(mNumberAttempts->getWrappedValue()>0,"gp.try",">0");
147 
148   // Cast method arguments.
149   GP::Individual& lIndiv1   = castObjectT<GP::Individual&>(ioIndiv1);
150   GP::Individual& lIndiv2   = castObjectT<GP::Individual&>(ioIndiv2);
151   GP::Context&    lContext1 = castObjectT<GP::Context&>(ioContext1);
152   GP::Context&    lContext2 = castObjectT<GP::Context&>(ioContext2);
153 
154   // Get parameters in local values, with the total number of nodes of an individual.
155   bool             lMatingDone     = false;
156   float            lDistrProba     = mDistributionProba->getWrappedValue();
157   unsigned int     lMaxTreeDepth   = mMaxTreeDepth->getWrappedValue();
158   GP::Tree::Handle lOldTreeHandle1 = lContext1.getGenotypeHandle();
159   unsigned int     lOldTreeIndex1  = lContext1.getGenotypeIndex();
160   GP::Tree::Handle lOldTreeHandle2 = lContext2.getGenotypeHandle();
161   unsigned int     lOldTreeIndex2  = lContext2.getGenotypeIndex();
162   unsigned int     lSizeIndiv1     = 0;
163   for(unsigned int i=0; i<lIndiv1.size(); i++) lSizeIndiv1 += lIndiv1[i]->size();
164 
165   Beagle_LogDebugM(
166     ioContext1.getSystem().getLogger(),
167     "crossover", "Beagle::GP::CrossoverOp",
168     string("First individual to mate (before GP crossover): ")+
169     lIndiv1.serialize()
170   );
171   Beagle_LogDebugM(
172     ioContext1.getSystem().getLogger(),
173     "crossover", "Beagle::GP::CrossoverOp",
174     string("Second individual to mate (before GP crossover): ")+
175     lIndiv2.serialize()
176   );
177 
178   // Crossover loop. Try the given number of attempts to mate two individuals.
179   for(unsigned int lAttempt=0; lAttempt<mNumberAttempts->getWrappedValue(); lAttempt++) {
180 
181      // Choose a node in all the individual node.
182     unsigned int lChoosenNode1 =
183       lContext1.getSystem().getRandomizer().rollInteger(0, lSizeIndiv1-1);
184 
185     // Get the tree in which the choosen node is. Change the global node index to the tree's index.
186     unsigned int lChoosenTree1 = 0;
187     for(; lChoosenTree1<lIndiv1.size(); lChoosenTree1++) {
188       if(lChoosenNode1 < lIndiv1[lChoosenTree1]->size()) break;
189       Beagle_AssertM(lChoosenNode1 >= lIndiv1[lChoosenTree1]->size());
190       lChoosenNode1 -= lIndiv1[lChoosenTree1]->size();
191     }
192     Beagle_AssertM(lChoosenTree1 < lIndiv1.size());
193 
194     // Choose a type of node (branch or leaf) following the distribution probability and change the
195     // node for another node of the same tree if the types mismatch.
196     GP::Tree& lTree1 = *lIndiv1[lChoosenTree1];
197     const unsigned int lPrimitiveSetIndex1 = lTree1.getPrimitiveSetIndex();
198     if(lTree1.size() > 1) {
199       bool lTypeNode1 =
200         (lContext1.getSystem().getRandomizer().rollUniform(0.0, 1.0) < lDistrProba);
201       while((lTree1[lChoosenNode1].mPrimitive->getNumberArguments() != 0) != lTypeNode1) {
202         lChoosenNode1 = lContext1.getSystem().getRandomizer().rollInteger(0, lTree1.size()-1);
203       }
204     }
205 
206     // Choose a node in the second individual from a tree with the same primitive set index.
207     unsigned int lSizeIndiv2 = 0;
208     for(unsigned int i=0; i<lIndiv2.size(); i++) {
209       if(lIndiv2[i]->getPrimitiveSetIndex() == lPrimitiveSetIndex1) {
210         lSizeIndiv2 += lIndiv2[i]->size();
211       }
212     }
213 
214     // Check to see that there is at least one node that can be selected
215     if(lSizeIndiv2==0) {
216       Beagle_LogVerboseM(
217         ioContext1.getSystem().getLogger(),
218         "crossover", "Beagle::GP::CrossoverConstrainedOp",
219         string("Crossover attempt failed:  The tree chosen from the first individual has a primitive set index of ")+
220         uint2str(lPrimitiveSetIndex1)+
221         string(" and there are no trees in the second individual with that primitive set index")
222       );
223       continue;
224     }
225 
226     // Choose a node in the second individual
227     unsigned int lChoosenNode2 = lContext2.getSystem().getRandomizer().rollInteger(0, lSizeIndiv2-1);
228 
229     // Find which tree the choosen node is in.
230     unsigned int lChoosenTree2 = 0;
231     for(; lChoosenTree2<lIndiv2.size(); lChoosenTree2++) {
232       if(lIndiv2[lChoosenTree2]->getPrimitiveSetIndex() == lPrimitiveSetIndex1) {
233         if(lChoosenNode2 < lIndiv2[lChoosenTree2]->size()) break;
234         Beagle_AssertM(lChoosenNode2 >= lIndiv2[lChoosenTree2]->size());
235         lChoosenNode2 -= lIndiv2[lChoosenTree2]->size();
236       }
237     }
238     Beagle_AssertM(lChoosenTree2 < lIndiv2.size());
239     GP::Tree& lTree2 = *lIndiv2[lChoosenTree2];
240 
241     // Choose a type of node (branch or leaf) following the distribution probability and change the
242     // node for another node of the same tree if the types mismatch.
243     if(lTree2.size() > 1) {
244       bool lTypeNode2 =
245         (lContext2.getSystem().getRandomizer().rollUniform(0.0, 1.0) < lDistrProba);
246       while((lTree2[lChoosenNode2].mPrimitive->getNumberArguments() != 0) != lTypeNode2) {
247         lChoosenNode2 = lContext2.getSystem().getRandomizer().rollInteger(0, lTree2.size()-1);
248       }
249     }
250 
251     // Set the first context to the node of the first tree.
252     // Check if depth is ok. Do a new crossover attempt if not.
253     lTree1.setContextToNode(lChoosenNode1, lContext1);
254     unsigned int lNewDepthTree1 =
255       lContext1.getCallStackSize() + lTree2.getTreeDepth(lChoosenNode2) - 1;
256     if(lNewDepthTree1 > lMaxTreeDepth) {
257       Beagle_LogVerboseM(
258         ioContext1.getSystem().getLogger(),
259         "crossover", "Beagle::GP::CrossoverConstrainedOp",
260         string("Crossover attempt failed because the depth of the resulting tree in the ")+
261         string("first individual would exceed the depth constraint")
262       );
263       continue;
264     }
265 
266     // Set the first context to the node of the second tree.
267     // Check if depth is ok. Do a new crossover attempt if not.
268     lTree2.setContextToNode(lChoosenNode2, lContext2);
269     unsigned int lNewDepthTree2 =
270       lContext2.getCallStackSize() + lTree1.getTreeDepth(lChoosenNode1) - 1;
271     if(lNewDepthTree2 > lMaxTreeDepth) {
272       Beagle_LogVerboseM(
273         ioContext1.getSystem().getLogger(),
274         "crossover", "Beagle::GP::CrossoverConstrainedOp",
275         string("Crossover attempt failed because the depth of the resulting tree in the ")+
276         string("second individual would exceed the depth constraint")
277       );
278       continue;
279     }
280 
281     // Mate the trees.
282     Beagle_LogVerboseM(
283       ioContext1.getSystem().getLogger(),
284       "crossover", "Beagle::GP::CrossoverOp",
285       string("Trying to mate the ")+uint2ordinal(lChoosenTree1+1)+
286       string(" tree of the first individual with the ")+uint2ordinal(lChoosenTree2+1)+
287       string(" tree of the second individual")
288     );
289     Beagle_LogVerboseM(
290       ioContext1.getSystem().getLogger(),
291       "crossover", "Beagle::GP::CrossoverOp",
292       string("Trying to exchange the ")+uint2ordinal(lChoosenNode1+1)+
293       string(" node of the first tree with the ")+uint2ordinal(lChoosenNode2+1)+
294       string(" node of the second tree")
295     );
296 
297     mateTrees(lTree1, lChoosenNode1, lContext1, lTree2, lChoosenNode2, lContext2);
298 
299     lMatingDone = true;
300     Beagle_LogVerboseM(
301       ioContext1.getSystem().getLogger(),
302       "crossover", "Beagle::GP::CrossoverOp",
303       "GP crossover valid"
304     );
305     break;   // The crossover is valid.
306   }
307 
308   // Replace the contexts.
309   lContext1.setGenotypeHandle(lOldTreeHandle1);
310   lContext1.setGenotypeIndex(lOldTreeIndex1);
311   lContext2.setGenotypeHandle(lOldTreeHandle2);
312   lContext2.setGenotypeIndex(lOldTreeIndex2);
313 
314   if(lMatingDone) {
315     Beagle_LogDebugM(
316       ioContext1.getSystem().getLogger(),
317       "crossover", "Beagle::GP::CrossoverOp",
318       string("First individual mated (after GP crossover): ")+
319       lIndiv1.serialize()
320     );
321     Beagle_LogDebugM(
322       ioContext1.getSystem().getLogger(),
323       "crossover", "Beagle::GP::CrossoverOp",
324       string("Second individual mated (after GP crossover): ")+
325       lIndiv2.serialize()
326     );
327   }
328   else {
329     Beagle_LogVerboseM(
330       ioContext1.getSystem().getLogger(),
331       "crossover", "Beagle::GP::CrossoverOp",
332       "No GP crossover done"
333     );
334   }
335 
336   return lMatingDone;
337   Beagle_StackTraceEndM("bool GP::CrossoverOp::mate(Beagle::Individual& ioIndiv1, Beagle::Context& ioContext1, Beagle::Individual& ioIndiv2, Beagle::Context& ioContext2)");
338 }
339 
340 
341 /*!
342  *  \brief  Mate two GP trees on given points.
343  *  \param  ioTree1 First tree to mate.
344  *  \param  inNode1 Node index of the croosover point in the first tree to mate.
345  *  \param  ioContext1 Evolutionary context relatively to the first tree.
346  *  \param  ioTree2 Second tree to mate.
347  *  \param  inNode2 Node index of the croosover point in the second tree to mate.
348  *  \param  ioContext2 Evolutionary context relatively to the second tree.
349  */
mateTrees(GP::Tree & ioTree1,unsigned int inNode1,GP::Context & ioContext1,GP::Tree & ioTree2,unsigned int inNode2,GP::Context & ioContext2)350 void GP::CrossoverOp::mateTrees(GP::Tree& ioTree1, unsigned int inNode1, GP::Context& ioContext1,
351                                 GP::Tree& ioTree2, unsigned int inNode2, GP::Context& ioContext2)
352 {
353   Beagle_StackTraceBeginM();
354   Beagle_AssertM(&ioTree1 != &ioTree2);
355   unsigned int lSwapSize1 = ioTree1[inNode1].mSubTreeSize;
356   unsigned int lSwapSize2 = ioTree2[inNode2].mSubTreeSize;
357   if(lSwapSize1 <= lSwapSize2) {
358     std::swap_ranges(ioTree1.begin()+inNode1, ioTree1.begin()+inNode1+lSwapSize1, ioTree2.begin()+inNode2);
359     ioTree1.insert(ioTree1.begin()+inNode1+lSwapSize1,
360                    ioTree2.begin()+inNode2+lSwapSize1,
361                    ioTree2.begin()+inNode2+lSwapSize2);
362     ioTree2.erase(ioTree2.begin()+inNode2+lSwapSize1, ioTree2.begin()+inNode2+lSwapSize2);
363   }
364   else {
365     std::swap_ranges(ioTree1.begin()+inNode1, ioTree1.begin()+inNode1+lSwapSize2, ioTree2.begin()+inNode2);
366     ioTree2.insert(ioTree2.begin()+inNode2+lSwapSize2,
367                    ioTree1.begin()+inNode1+lSwapSize2,
368                    ioTree1.begin()+inNode1+lSwapSize1);
369     ioTree1.erase(ioTree1.begin()+inNode1+lSwapSize2, ioTree1.begin()+inNode1+lSwapSize1);
370   }
371   int lDiffSize = lSwapSize1 - lSwapSize2;
372   for(unsigned int i=0; i<(ioContext1.getCallStackSize()-1); i++)
373     ioTree1[ioContext1.getCallStackElement(i)].mSubTreeSize -= lDiffSize;
374   for(unsigned int j=0; j<(ioContext2.getCallStackSize()-1); j++)
375     ioTree2[ioContext2.getCallStackElement(j)].mSubTreeSize += lDiffSize;
376   Beagle_StackTraceEndM("void GP::CrossoverOp::mateTrees(GP::Tree& ioTree1, unsigned int inNode1, GP::Context& ioContext1, GP::Tree& ioTree2, unsigned int inNode2, GP::Context& ioContext2)");
377 }
378 
379 
380 /*!
381  *  \brief Read a crossover operator for XML subtree.
382  *  \param inIter XML iterator to use to read crossover operator.
383  *  \param inOpMap Operator map to use to read crossover operator.
384  */
readWithMap(PACC::XML::ConstIterator inIter,OperatorMap & inOpMap)385 void GP::CrossoverOp::readWithMap(PACC::XML::ConstIterator inIter, OperatorMap& inOpMap)
386 {
387   Beagle_StackTraceBeginM();
388   if((inIter->getType()!=PACC::XML::eData) || (inIter->getValue()!=getName().c_str())) {
389     std::ostringstream lOSS;
390     lOSS << "tag <" << getName() << "> expected!" << std::flush;
391     throw Beagle_IOExceptionNodeM(*inIter, lOSS.str().c_str());
392   }
393   string mMatingProbaReadName = inIter->getAttribute("matingpb").c_str();
394   if(mMatingProbaReadName.empty() == false) mMatingProbaName = mMatingProbaReadName;
395   string mDistribPbReadName = inIter->getAttribute("distrpb").c_str();
396   if(mDistribPbReadName.empty() == false) mDistribPbName = mDistribPbReadName;
397   Beagle_StackTraceEndM("void GP::CrossoverOp::readWithMap(PACC::XML::ConstIterator inIter, OperatorMap& inOpMap)");
398 }
399 
400 
401 /*!
402  *  \brief Write crossover operator into XML streamer.
403  *  \param ioStreamer XML streamer to write crossover operator into.
404  *  \param inIndent Whether XML output should be indented.
405  */
writeContent(PACC::XML::Streamer & ioStreamer,bool inIndent) const406 void GP::CrossoverOp::writeContent(PACC::XML::Streamer& ioStreamer, bool inIndent) const
407 {
408   Beagle_StackTraceBeginM();
409   Beagle::CrossoverOp::writeContent(ioStreamer, inIndent);
410   ioStreamer.insertAttribute("distrpb", mDistribPbName);
411   Beagle_StackTraceEndM("void GP::CrossoverOp::writeContent(PACC::XML::Streamer& ioStreamer, bool inIndent) const");
412 }
413 
414