speciation.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package gago
  2. import (
  3. "errors"
  4. "fmt"
  5. "math"
  6. "math/rand"
  7. )
  8. // A Speciator partitions a population into n smaller subpopulations. Each
  9. // subpopulation shares the same random number generator inherited from the
  10. // initial population.
  11. type Speciator interface {
  12. Apply(indis Individuals, rng *rand.Rand) ([]Individuals, error)
  13. Validate() error
  14. }
  15. // SpecKMedoids (k-medoid clustering).
  16. type SpecKMedoids struct {
  17. K int // Number of medoids
  18. MinPerCluster int
  19. Metric Metric // Dissimimilarity measure
  20. MaxIterations int
  21. }
  22. // Apply SpecKMedoids.
  23. func (spec SpecKMedoids) Apply(indis Individuals, rng *rand.Rand) ([]Individuals, error) {
  24. // Check there are at least K Individuals
  25. if len(indis) < spec.K {
  26. return nil, fmt.Errorf("SpecKMedoids: have %d individuals and need at least %d",
  27. len(indis), spec.K)
  28. }
  29. var (
  30. species = make([]Individuals, spec.K)
  31. medoids = make(Individuals, spec.K)
  32. dm = newDistanceMemoizer(spec.Metric)
  33. )
  34. // Initialize the clusters with the individuals having the lowest average
  35. // distances with the other individuals
  36. indis.SortByDistanceToMedoid(dm)
  37. copy(medoids, indis[:spec.K])
  38. // Add each medoid to a cluster
  39. for i, medoid := range medoids {
  40. species[i] = append(species[i], medoid)
  41. }
  42. // Keep track of the total distance from the medoid to each of the cluster's
  43. // members, this total will then be used to compare with different cluster
  44. // dispositions
  45. var total float64
  46. // Assign each individual that is not a medoid to the closest initial medoid
  47. for _, indi := range indis[spec.K:] {
  48. var i = indi.IdxOfClosest(medoids, dm)
  49. species[i] = append(species[i], indi)
  50. total += dm.GetDistance(medoids[i], indi)
  51. }
  52. var nIterations int
  53. for nIterations < spec.MaxIterations {
  54. nIterations++
  55. var (
  56. newSpecies = make([]Individuals, len(species))
  57. newTotal float64
  58. )
  59. // Recompute the new medoid inside each specie
  60. for i, specie := range species {
  61. specie.SortByDistanceToMedoid(dm)
  62. medoids[i] = specie[0]
  63. newSpecies[i] = append(newSpecies[i], specie[0])
  64. }
  65. // Reassign each individual to the closest new medoid
  66. for _, specie := range species {
  67. for _, indi := range specie[1:] {
  68. var i = indi.IdxOfClosest(medoids, dm)
  69. newSpecies[i] = append(newSpecies[i], indi)
  70. newTotal += dm.GetDistance(medoids[i], indi)
  71. }
  72. }
  73. // No more iterations are needed if the new total is worse
  74. if newTotal >= total {
  75. break
  76. }
  77. copy(species, newSpecies)
  78. total = newTotal
  79. }
  80. // Rebalance the species so that their are at least
  81. rebalanceClusters(species, dm, spec.MinPerCluster)
  82. return species, nil
  83. }
  84. // Validate SpecKMedoids fields.
  85. func (spec SpecKMedoids) Validate() error {
  86. if spec.K < 2 {
  87. return errors.New("K should be higher than 1")
  88. }
  89. if spec.Metric == nil {
  90. return errors.New("Metric field has to be provided")
  91. }
  92. if spec.MaxIterations < 1 {
  93. return errors.New("K should be higher than 0")
  94. }
  95. return nil
  96. }
  97. // SpecFitnessInterval speciates a population based on the fitness of each
  98. // individual where each species contains m = n/k (rounded to the closest upper
  99. // integer) individuals with similar fitnesses. For example, with 4 species, 30
  100. // individuals would be split into 3 groups of 8 individuals and 1 group of 6
  101. // individuals (3*8 + 1*6 = 30). More generally each group is of size
  102. // min(n-i, m) where i is a multiple of m.
  103. type SpecFitnessInterval struct {
  104. K int // Number of intervals
  105. }
  106. // Apply SpecFitnessInterval.
  107. func (spec SpecFitnessInterval) Apply(indis Individuals, rng *rand.Rand) ([]Individuals, error) {
  108. // Check there are at least K Individuals
  109. if len(indis) < spec.K {
  110. return nil, fmt.Errorf("SpecFitnessInterval: have %d individuals and need at least %d",
  111. len(indis), spec.K)
  112. }
  113. var (
  114. species = make([]Individuals, spec.K)
  115. n = len(indis)
  116. m = min(int(math.Ceil(float64(n/spec.K))), n)
  117. )
  118. for i := range species {
  119. var a, b = i * m, min((i+1)*m, n)
  120. species[i] = indis[a:b]
  121. }
  122. return species, nil
  123. }
  124. // Validate SpecFitnessInterval fields.
  125. func (spec SpecFitnessInterval) Validate() error {
  126. if spec.K < 2 {
  127. return errors.New("K should be higher than 1")
  128. }
  129. return nil
  130. }