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