Modernized GAlib  3.0.0 current
GAListGenome.hpp
1 /* ----------------------------------------------------------------------------
2  mbwall 25feb95
3  Copyright (c) 1995 Massachusetts Institute of Technology
4  all rights reserved
5 ---------------------------------------------------------------------------- */
6 
7 #pragma once
8 
9 #include <GAGenome.h>
10 #include <GAList.hpp>
11 #include <GAMask.h>
12 #include <garandom.h>
13 
14 
18 template <class T> class GAListGenome : public GAList<T>, public GAGenome
19 {
20  public:
21  GADefineIdentity("GAListGenome", GAID::ListGenome);
22 
23  // Mutate a list by nuking nodes. Any node has a pmut chance of getting
24  // nuked.
25  // This is actually kind of bogus for the second part of the if clause (if
26  // nMut is greater than or equal to 1). Nodes end up having more than pmut
27  // probability of getting nuked.
28  // After the mutation the iterator is left at the head of the list.
29  static int DestructiveMutator(GAGenome &c, float pmut)
30  {
31  GAListGenome<T> &child = DYN_CAST(GAListGenome<T> &, c);
32 
33  if (pmut <= 0.0)
34  return 0;
35 
36  int n = child.size();
37  float nMut = pmut * STA_CAST(float, n);
38  if (nMut < 1.0)
39  { // we have to do a flip test for each node
40  nMut = 0;
41  for (int i = 0; i < n; i++)
42  {
43  if (GAFlipCoin(pmut) && child.warp(i))
44  {
45  child.destroy();
46  nMut++;
47  }
48  }
49  }
50  else
51  { // only nuke the number of nodes we need to
52  for (int i = 0; i < nMut; i++)
53  {
54  if (child.warp(GARandomInt(0, n - 1)))
55  child.destroy();
56  }
57  }
58  child.head(); // set iterator to root node
59 
60  return (STA_CAST(int, nMut));
61  }
62 
63  // Mutate a list by swapping two nodes. Any node has a pmut chance of
64  // getting
65  // swapped, and the swap could happen to any other node. And in the case of
66  // nMut < 1, the swap may generate a swap partner that is the same node, in
67  // which case no swap occurs (we don't check).
68  // After the mutation the iterator is left at the head of the list.
69  static int SwapMutator(GAGenome &c, float pmut)
70  {
71  GAListGenome<T> &child = DYN_CAST(GAListGenome<T> &, c);
72  int n, i;
73  if (pmut <= 0.0)
74  return 0;
75 
76  n = child.size();
77  float nMut = pmut * STA_CAST(float, n);
78  nMut *= 0.5; // swapping one node swaps another as well
79  if (nMut < 1.0)
80  { // we have to do a flip test for each node
81  nMut = 0;
82  for (i = 0; i < n; i++)
83  {
84  if (GAFlipCoin(pmut))
85  {
86  child.swap(i, GARandomInt(0, n - 1));
87  nMut++;
88  }
89  }
90  }
91  else
92  { // only nuke the number of nodes we need to
93  for (i = 0; i < nMut; i++)
94  child.swap(GARandomInt(0, n - 1), GARandomInt(0, n - 1));
95  }
96  child.head(); // set iterator to root node
97 
98  return (STA_CAST(int, nMut * 2));
99  }
100 
101  // This comparator returns the number of elements that the two lists have
102  // in common (both in position and in value). If they are different lengths
103  // then we just say they are completely different. This is probably barely
104  // adequate for most applications - we really should give more credit for
105  // nodes that are the same but in different positions. But that would be
106  // pretty nasty to compute.
107  // This is somewhat problematic since there is no absolute measure against
108  // which to compare this diversity measure. Its kind of hard to define this
109  // one in a general way...
110  static float NodeComparator(const GAGenome &a, const GAGenome &b)
111  {
112  if (&a == &b)
113  return 0;
114  const GAListGenome<T> &sis = DYN_CAST(const GAListGenome<T> &, a);
115  const GAListGenome<T> &bro = DYN_CAST(const GAListGenome<T> &, b);
116 
117  if (sis.size() > bro.size())
118  return (float)(sis.size() - bro.size());
119  if (sis.size() < bro.size())
120  return (float)(bro.size() - sis.size());
121  if (sis.size() == 0)
122  return 0;
123 
124  float count = 0;
125  GAListIter<T> biter(bro), siter(sis);
126 
127  for (int i = siter.size() - 1; i >= 0; i--)
128  {
129  auto sptr = siter.next();
130  auto bptr = biter.next();
131  if (sptr != nullptr && bptr != nullptr)
132  count += ((*sptr == *bptr) ? 0 : 1);
133  }
134  return count;
135  }
136 
137  // This crossover picks a site between nodes in each parent. It is the same
138  // as single point crossover on a resizeable binary string genome. The site
139  // in the mother is not necessarily the same as the site in the father!
140  // When we pick a crossover site, it is between nodes of the list
141  // (otherwise
142  // we won't be able to do NULL lists or get an empty list from one parent or
143  // the other). Beware that a list is numbered from 0 to size-1, inclusive,
144  // whereas the cross site possibilities are numbered from 0 to size,
145  // inclusive. This means we have to map the site to the list to determine
146  // whether an insertion should occur before or after a node.
147  // We first copy the mother into the child (this deletes whatever contents
148  // were in the child originally). Then we clone the father from the cross
149  // site to the end of the list. Then we delete the tail of the child from
150  // the mother's cross site to the end of the list. Finally, we insert the
151  // clone at the end of the child.
152  // The last thing we do is delete the clone (the contents of the clone are
153  // now owned by the child, but the clone itself uses memory that we must
154  // free).
155  // This implementation isn't particularly efficient. For example, we copy
156  // the mother then proceed to destroy much of the copy we just made. We
157  // could do better by copying only what we need of the mother.
158  static int OnePointCrossover(const GAGenome &p1, const GAGenome &p2,
159  GAGenome *c1, GAGenome *c2)
160  {
161  const GAListGenome<T> &mom = DYN_CAST(const GAListGenome<T> &, p1);
162  const GAListGenome<T> &dad = DYN_CAST(const GAListGenome<T> &, p2);
163 
164  int nc = 0;
165  int a = GARandomInt(0, mom.size());
166  int b = GARandomInt(0, dad.size());
167  GAList<T> *list;
168 
169  if (c1)
170  {
171  GAListGenome<T> &sis = DYN_CAST(GAListGenome<T> &, *c1);
172  sis.GAList<T>::copy(mom);
173  list = dad.GAList<T>::clone(b);
174  if (a < mom.size())
175  {
176  T *site = sis.warp(a);
177  while (sis.tail() != site)
178  sis.destroy(); // delete the tail node
179  sis.destroy(); // trash the trailing node (list[a])
180  }
181  else
182  {
183  sis.tail(); // move to the end of the list
184  }
185  sis.insert(list); // stick the clone onto the end
186  delete list;
187  sis.head(); // set iterator to head of list
188  nc += 1;
189  }
190 
191  if (c2)
192  {
193  GAListGenome<T> &bro = DYN_CAST(GAListGenome<T> &, *c2);
194  bro.GAList<T>::copy(dad);
195  list = mom.GAList<T>::clone(a);
196  if (b < dad.size())
197  {
198  T *site = bro.warp(b);
199  while (bro.tail() != site)
200  bro.destroy(); // delete the tail node
201  bro.destroy(); // trash the trailing node (list[a])
202  }
203  else
204  {
205  bro.tail(); // move to the end of the list
206  }
207  bro.insert(list); // stick the clone onto the end
208  delete list;
209  bro.head(); // set iterator to head of list
210  nc += 1;
211  }
212 
213  return nc;
214  }
215 
216  // Partial match crossover for list genomes. We need two versions of this
217  // routine: one for lists whose nodes are pointers to objects (each genome
218  // points to the same objects as all of the other genomes) and one for
219  // lists whose nodes contain independent objects (each node has its own copy
220  // of the object).
221  // This version of the partial match crossover uses objects that are
222  // multiply
223  // instantiated - each list genome contains its own objects in its nodes.
224  // The operator== method must be defined on the object for this
225  // implementation to work! In this case, the 'object' is an int, so we're
226  // OK. If you are putting your own objects in the nodes, be sure you have
227  // operator== defined for your object. You must also have operator!=
228  // defined for your object. We do not do any assignments, so operator=
229  // and/or copy is not required.
230  // We assume that none of the nodes will return a NULL pointer. Also
231  // assume
232  // that the cross site has been selected properly.
233  // First we make a copy of the mother. Then we loop through the match
234  // section and try to swap each element in the child's match section with
235  // its partner (as defined by the current node in the father's match
236  // section).
237  // Mirroring will work the same way - just swap mom & dad and you're all
238  // set. The parents should be the same size as the child (and they should
239  // contain
240  // the same nodes, but in any order). We do not check for this!!
241 
242  static int PartialMatchCrossover(const GAGenome &p1, const GAGenome &p2,
243  GAGenome *c1, GAGenome *c2)
244  {
245  const GAListGenome<T> &mom = DYN_CAST(const GAListGenome<T> &, p1);
246  const GAListGenome<T> &dad = DYN_CAST(const GAListGenome<T> &, p2);
247 
248  if (mom.size() != dad.size())
249  {
250  GAErr(GA_LOC, mom.className(), "cross", GAError::BadParentLength);
251  return 0;
252  }
253 
254  int a = GARandomInt(0, mom.size());
255  int b = GARandomInt(0, dad.size());
256  if (b < a)
257  SWAP(a, b);
258  T *index;
259  int i, j, nc = 0;
260 
261  if (c1)
262  {
263  GAListGenome<T> &sis = DYN_CAST(GAListGenome<T> &, *c1);
264  sis.GAList<T>::copy(mom);
265  GAListIter<T> diter(dad);
266  index = diter.warp(a);
267  for (i = a; i < b; i++, index = diter.next())
268  {
269  if (*sis.head() == *index)
270  {
271  sis.swap(i, 0);
272  }
273  else
274  {
275  for (j = 1; (j < sis.size()) && (*sis.next() != *index);
276  j++)
277  ;
278  sis.swap(i, j); // no op if j>size
279  }
280  }
281  sis.head(); // set iterator to head of list
282  nc += 1;
283  }
284 
285  if (c2)
286  {
287  GAListGenome<T> &bro = DYN_CAST(GAListGenome<T> &, *c2);
288  bro.GAList<T>::copy(dad);
289  GAListIter<T> miter(mom);
290  index = miter.warp(a);
291  for (i = a; i < b; i++, index = miter.next())
292  {
293  if (*bro.head() == *index)
294  {
295  bro.swap(i, 0);
296  }
297  else
298  {
299  for (j = 1; (j < bro.size()) && (*bro.next() != *index);
300  j++)
301  ;
302  bro.swap(i, j); // no op if j>size
303  }
304  }
305  bro.head(); // set iterator to head of list
306  nc += 2;
307  }
308 
309  return nc;
310  }
311 
312  static int OrderCrossover(const GAGenome &p1, const GAGenome &p2,
313  GAGenome *c1, GAGenome *c2)
314  {
315  const GAListGenome<T> &mom = DYN_CAST(const GAListGenome<T> &, p1);
316  const GAListGenome<T> &dad = DYN_CAST(const GAListGenome<T> &, p2);
317 
318  if (mom.size() != dad.size())
319  {
320  GAErr(GA_LOC, mom.className(), "cross", GAError::BadParentLength);
321  return 0;
322  }
323 
324  int a = GARandomInt(0, mom.size());
325  int b = GARandomInt(0, dad.size());
326  if (b < a)
327  SWAP(a, b);
328  int i, j, index, nc = 0;
329 
330  if (c1)
331  {
332  GAListGenome<T> &sis = DYN_CAST(GAListGenome<T> &, *c1);
333  sis.GAList<T>::copy(mom);
334  GAListIter<T> siter(sis);
335  GAListIter<T> diter(dad);
336 
337  // Move all the 'holes' into the crossover section and maintain the
338  // ordering of the non-hole elements.
339  for (i = 0, index = b; i < sis.size(); i++, index++)
340  {
341  if (index >= sis.size())
342  index = 0;
343  if (GAListIsHole(sis, dad, index, a, b))
344  break;
345  }
346  for (; i < sis.size() - b + a; i++, index++)
347  {
348  if (index >= sis.size())
349  index = 0;
350  j = index;
351  do
352  {
353  j++;
354  if (j >= sis.size())
355  j = 0;
356  } while (GAListIsHole(sis, dad, j, a, b));
357  sis.swap(index, j);
358  }
359 
360  // Now put the 'holes' in the proper order within the crossover
361  // section.
362  for (i = a, sis.warp(a), diter.warp(a); i < b;
363  i++, sis.next(), diter.next())
364  {
365  if (*sis.current() != *diter.current())
366  {
367  siter.warp(i);
368  for (j = i + 1; j < b; j++)
369  if (*siter.next() == *diter.current())
370  {
371  sis.swap(i, j);
372  sis.warp(siter); // move iterator back to previous
373  // location
374  break;
375  }
376  }
377  }
378 
379  sis.head(); // set iterator to head of list
380  nc += 1;
381  }
382 
383  if (c2)
384  {
385  GAListGenome<T> &bro = DYN_CAST(GAListGenome<T> &, *c2);
386  bro.GAList<T>::copy(dad);
387  GAListIter<T> biter(bro);
388  GAListIter<T> miter(mom);
389 
390  // Move all the 'holes' into the crossover section and maintain the
391  // ordering of the non-hole elements.
392  for (i = 0, index = b; i < bro.size(); i++, index++)
393  {
394  if (index >= bro.size())
395  index = 0;
396  if (GAListIsHole(bro, mom, index, a, b))
397  break;
398  }
399 
400  for (; i < bro.size() - b + a; i++, index++)
401  {
402  if (index >= bro.size())
403  index = 0;
404  j = index;
405  do
406  {
407  j++;
408  if (j >= bro.size())
409  j = 0;
410  } while (GAListIsHole(bro, mom, j, a, b));
411  bro.swap(index, j);
412  }
413 
414  // Now put the 'holes' in the proper order within the crossover
415  // section.
416  for (i = a, bro.warp(a), miter.warp(a); i < b;
417  i++, bro.next(), miter.next())
418  {
419  if (*bro.current() != *miter.current())
420  {
421  biter.warp(i);
422  for (j = i + 1; j < b; j++)
423  if (*biter.next() == *miter.current())
424  {
425  bro.swap(i, j);
426  bro.warp(biter); // move iterator back to previous
427  // location
428  break;
429  }
430  }
431  }
432  bro.head(); // set iterator to head of list
433  nc += 1;
434  }
435 
436  return nc;
437  }
438 
439  static int CycleCrossover(const GAGenome &p1, const GAGenome &p2,
440  GAGenome *c1, GAGenome *c2)
441  {
442  const GAListGenome<T> &mom = DYN_CAST(const GAListGenome<T> &, p1);
443  const GAListGenome<T> &dad = DYN_CAST(const GAListGenome<T> &, p2);
444 
445  if (mom.size() != dad.size())
446  {
447  GAErr(GA_LOC, mom.className(), "cross", GAError::BadParentLength);
448  return 0;
449  }
450 
451  GAMask mask;
452  mask.size(mom.size());
453  mask.clear();
454  int i, nc = 0;
455 
456  if (c1)
457  {
458  GAListGenome<T> &sis = DYN_CAST(GAListGenome<T> &, *c1);
459  sis.GAList<T>::copy(mom);
460  GAListIter<T> diter(dad);
461 
462  // Cycle through mom & dad to get the cyclic part of the crossover.
463  mask[0] = 1;
464  diter.head();
465  while (*diter.current() != *sis.head())
466  {
467  for (i = 0; i < sis.size(); i++, sis.next())
468  {
469  if (*sis.current() == *diter.current())
470  {
471  mask[i] = 1;
472  diter.warp(i);
473  break;
474  }
475  }
476  }
477 
478  // Now fill in the rest of the sis with dad's contents that we
479  // didn't use in the cycle.
480  sis.head();
481  diter.head();
482  for (i = 0; i < sis.size(); i++)
483  {
484  if (mask[i] == 0)
485  *sis.current() = *diter.current();
486  sis.next();
487  diter.next();
488  }
489  sis.head(); // set iterator to head of list
490  nc += 1;
491  }
492 
493  if (c2)
494  {
495  GAListGenome<T> &bro = DYN_CAST(GAListGenome<T> &, *c2);
496  bro.GAList<T>::copy(dad);
497  GAListIter<T> miter(mom);
498 
499  // Cycle through mom & dad to get the cyclic part of the crossover.
500  mask[0] = 1;
501  miter.head();
502  while (*miter.current() != *bro.head())
503  {
504  for (i = 0; i < bro.size(); i++, bro.next())
505  {
506  if (*bro.current() == *miter.current())
507  {
508  mask[i] = 1;
509  miter.warp(i);
510  break;
511  }
512  }
513  }
514 
515  // Now fill in the rest of the bro with dad's contents that we
516  // didn't use in the cycle.
517  bro.head();
518  miter.head();
519  for (i = 0; i < bro.size(); i++)
520  {
521  if (mask[i] == 0)
522  *bro.current() = *miter.current();
523  bro.next();
524  miter.next();
525  }
526  bro.head(); // set iterator to head of list
527  nc += 1;
528  }
529 
530  return nc;
531  }
532 
533  public:
534  GAListGenome(GAGenome::Evaluator f = nullptr, void *u = nullptr)
535  : GAList<T>(), GAGenome(DEFAULT_LIST_INITIALIZER, DEFAULT_LIST_MUTATOR,
536  DEFAULT_LIST_COMPARATOR)
537  {
538  evaluator(f);
539  userData(u);
540  crossover(DEFAULT_LIST_CROSSOVER);
541  }
542 
543  GAListGenome(const GAListGenome<T> &orig) : GAList<T>(), GAGenome()
544  {
545  GAListGenome<T>::copy(orig);
546  }
547 
548  GAListGenome<T> &operator=(const GAGenome &orig)
549  {
550  copy(orig);
551  return *this;
552  }
553 
554  ~GAListGenome() override = default;
555 
556  GAGenome *
557  clone(GAGenome::CloneMethod flag = CloneMethod::CONTENTS) const override
558  {
559  auto *cpy = new GAListGenome<T>();
560  if (flag == CloneMethod::CONTENTS)
561  {
562  cpy->copy(*this);
563  } // the cast is for metrowerks...
564  else
565  {
566  cpy->GAGenome::copy(*this);
567  }
568  return cpy;
569  }
570 
571  void copy(const GAGenome &orig) override
572  {
573  if (&orig == this)
574  return;
575  const GAListGenome<T> *c = DYN_CAST(const GAListGenome<T> *, &orig);
576  if (c)
577  {
578  GAGenome::copy(*c);
579  GAList<T>::copy(*c);
580  }
581  }
582 
583  // Traverse the list (breadth-first) and dump the contents as best we can to
584  // the stream. We don't try to write the contents of the nodes - we simply
585  // write a . for each node in the list.
586  int write(std::ostream &os) const override
587  {
588  os << "node next prev contents\n";
589  if (!this->hd)
590  return 0;
591  ;
592  os.width(10);
593  os << this->hd << " ";
594  os.width(10);
595  os << this->hd->next << " ";
596  os.width(10);
597  os << this->hd->prev << " ";
598  os.width(10);
599  os << &(DYN_CAST(GANode<T> *, this->hd)->contents) << "\n";
600 
601  for (GANodeBASE *tmp = this->hd->next; tmp && tmp != this->hd;
602  tmp = tmp->next)
603  {
604  os.width(10);
605  os << tmp << " ";
606  os.width(10);
607  os << tmp->next << " ";
608  os.width(10);
609  os << tmp->prev << " ";
610  os.width(10);
611  os << &(DYN_CAST(GANode<T> *, tmp)->contents) << "\n";
612  }
613  return 0;
614  }
615 
616  // Both the == and != operators assume that both operator== and operator!=
617  // are
618  // defined for the object that is store in the node of your list. If it is
619  // not, you'll get an error message. If you're storing pointers in the
620  // nodes, then you have nothing to worry about.
621  // Neither of these operators affects the internal iterator of either
622  // list genome in any way.
623  bool equal(const GAGenome &c) const override
624  {
625  if (this == &c)
626  return true;
627  const GAListGenome<T> &b = DYN_CAST(const GAListGenome<T> &, c);
628  if (this->size() != b.size())
629  return false;
630 
631  GAListIter<T> iterA(*this), iterB(b);
632  T *tmpA = iterA.head(), *tmpB = iterB.head();
633  T *head = tmpA;
634  do
635  {
636  if (tmpA && tmpB && *tmpA != *tmpB)
637  return false;
638  tmpB = iterB.next();
639  tmpA = iterA.next();
640  } while (tmpA && tmpA != head);
641  return true;
642  }
643 
644  // Here we do inlined versions of the access members of the super class. We
645  // do our own here so that we can set/unset the _evaluated flag
646  // appropriately.
647 
648  int destroy()
649  {
650  _evaluated = false;
651  return GAList<T>::destroy();
652  }
653  int swap(unsigned int i, unsigned int j)
654  {
655  _evaluated = false;
656  return GAList<T>::swap(i, j);
657  }
658  T *remove()
659  {
660  _evaluated = false;
661  return GAList<T>::remove();
662  }
663  int insert(GAList<T> *t, GAListBASE::Location where = GAListBASE::AFTER)
664  {
665  _evaluated = false;
666  return GAList<T>::insert(t, where);
667  }
668  int insert(const T &t, GAListBASE::Location where = GAListBASE::AFTER)
669  {
670  _evaluated = false;
671  return GAList<T>::insert(t, where);
672  }
673 
674  private:
675  // Order crossover for lists. As described in Goldberg's book.
676  // We assume that we'll never get a NULL pointer while iterating through
677  // the
678  // list. Also we assume that the lists are the same size and non-NULL.
679  static int GAListIsHole(const GAListGenome<T> &child,
680  const GAListGenome<T> &parent, int index, int a,
681  int b)
682  {
683  GAListIter<T> citer(child), piter(parent);
684  citer.warp(index);
685  piter.warp(a);
686  for (int i = a; i < b; i++)
687  {
688  if (*citer.current() == *piter.current())
689  return 1;
690  piter.next();
691  }
692  return 0;
693  }
694 };
The base genome class just defines the genome interface - how to mutate, crossover,...
Definition: GAGenome.h:200
GAListGenome.
Definition: GAListGenome.hpp:19
Definition: GAList.hpp:345
Container for nodes that have a list structure.
Definition: GAList.hpp:88
int destroy()
Remove the current node from the list and free the memory it was using.
Definition: GAList.hpp:174
int insert(GAList< T > *t, GAListBASE::Location where=GAListBASE::AFTER)
Inserts the contents of list in to the current list and removes it from the original list.
Definition: GAList.hpp:259
T * remove()
Remove the current node from the list.
Definition: GAList.hpp:232
int swap(unsigned int a, unsigned int b)
Swap two nodes in the list.
Definition: GAList.hpp:199
void copy(const GAList< T > &orig)
Make a complete copy of the list and return a pointer to the new list.
Definition: GAList.hpp:153
GAMask.
Definition: GAMask.h:15
This is the basic node object.
Definition: GANode.h:18
Definition: GANode.h:79