// IntMutation performs the mutation of genetic data from A // Output: modified individual 'A' func IntMutation(A []int, time int, ops *OpsData) { size := len(A) if !rnd.FlipCoin(ops.Pm) || size < 1 { return } pos := rnd.IntGetUniqueN(0, size, ops.Nchanges) for _, i := range pos { m := rnd.Int(1, int(ops.Mmax)) if rnd.FlipCoin(0.5) { A[i] += m * A[i] } else { A[i] -= m * A[i] } } }
// Fight implements the competition between A and B func (A *Solution) Fight(B *Solution) (A_wins bool) { // compare solutions A_dom, B_dom := A.Compare(B) if A_dom { return true } if B_dom { return false } // tie: single-objective problems if A.prms.Nova < 2 { if A.DistNeigh > B.DistNeigh { return true } if B.DistNeigh > A.DistNeigh { return false } return rnd.FlipCoin(0.5) } // tie: multi-objective problems: same Pareto front if A.FrontId == B.FrontId { if A.DistCrowd > B.DistCrowd { return true } if B.DistCrowd > A.DistCrowd { return false } return rnd.FlipCoin(0.5) } // tie: multi-objective problems: different Pareto fronts if A.FrontId < B.FrontId { return true } if B.FrontId < A.FrontId { return false } if A.DistNeigh > B.DistNeigh { return true } if B.DistNeigh > A.DistNeigh { return false } return rnd.FlipCoin(0.5) }
// StrCrossover performs the crossover of genetic data from A and B // Output: // a and b -- offspring // Example: // 0 1 2 3 4 5 6 7 // A = a b c d e f g h size = 8 // B = * . . . . * * * cuts = [1, 5] // ↑ ↑ ↑ ends = [1, 5, 8] // 1 5 8 // a = a . . . . f g h // b = * b c d e * * * func StrCrossover(a, b, A, B []string, time int, ops *OpsData) (ends []int) { size := len(A) if !rnd.FlipCoin(ops.Pc) || size < 2 { for i := 0; i < len(A); i++ { a[i], b[i] = A[i], B[i] } return } ends = GenerateCxEnds(size, ops.Ncuts, ops.Cuts) swap := false start := 0 for _, end := range ends { if swap { for j := start; j < end; j++ { b[j], a[j] = A[j], B[j] } } else { for j := start; j < end; j++ { a[j], b[j] = A[j], B[j] } } start = end swap = !swap } return }
// MtInt performs the mutation of genetic data from A // Output: modified individual 'A' func MtInt(A []int, prms *Parameters) { size := len(A) if !rnd.FlipCoin(prms.IntPm) || size < 1 { return } mmax := 2 pos := rnd.IntGetUniqueN(0, size, prms.IntNchanges) for _, i := range pos { m := rnd.Int(1, mmax) if rnd.FlipCoin(0.5) { A[i] += m * A[i] } else { A[i] -= m * A[i] } } }
// BytCrossover performs the crossover of genetic data from A and B // Output: // a and b -- offspring // Example: // 0 1 2 3 4 5 6 7 // A = a b c d e f g h size = 8 // B = * . . . . * * * cuts = [1, 5] // ↑ ↑ ↑ ends = [1, 5, 8] // 1 5 8 // a = a . . . . f g h // b = * b c d e * * * func BytCrossover(a, b, A, B [][]byte, time int, ops *OpsData) (ends []int) { size := len(A) if !rnd.FlipCoin(ops.Pc) || size < 2 { for i := 0; i < len(A); i++ { copy(a[i], A[i]) copy(b[i], B[i]) } return } ends = GenerateCxEnds(size, ops.Ncuts, ops.Cuts) swap := false start := 0 for _, end := range ends { if swap { for j := start; j < end; j++ { copy(b[j], A[j]) copy(a[j], B[j]) } } else { for j := start; j < end; j++ { copy(a[j], A[j]) copy(b[j], B[j]) } } start = end swap = !swap } return }
// CxInt performs the crossover of genetic data from A and B // Output: // a and b -- offspring // Example: // 0 1 2 3 4 5 6 7 // A = a b c d e f g h size = 8 // B = * . . . . * * * cuts = [1, 5] // ↑ ↑ ↑ ends = [1, 5, 8] // 1 5 8 // a = a . . . . f g h // b = * b c d e * * * func CxInt(a, b, A, B []int, prms *Parameters) { size := len(A) if !rnd.FlipCoin(prms.IntPc) || size < 2 { for i := 0; i < len(A); i++ { a[i], b[i] = A[i], B[i] } return } ends := GenerateCxEnds(size, prms.IntNcuts, nil) swap := false start := 0 for _, end := range ends { if swap { for j := start; j < end; j++ { b[j], a[j] = A[j], B[j] } } else { for j := start; j < end; j++ { a[j], b[j] = A[j], B[j] } } start = end swap = !swap } return }
// StrMutation performs the mutation of genetic data from A // Output: modified individual 'A' func StrMutation(A []string, time int, ops *OpsData) { size := len(A) if !rnd.FlipCoin(ops.Pm) || size < 1 { return } pos := rnd.IntGetUniqueN(0, size, ops.Nchanges) for _, i := range pos { A[i] = "TODO" // TODO: improve this } }
// KeyMutation performs the mutation of genetic data from A // Output: modified individual 'A' func KeyMutation(A []byte, time int, ops *OpsData) { size := len(A) if !rnd.FlipCoin(ops.Pm) || size < 1 { return } pos := rnd.IntGetUniqueN(0, size, ops.Nchanges) for _, i := range pos { v := rnd.Int(0, 100) A[i] = byte(v) // TODO: improve this } }
// FunMutation performs the mutation of genetic data from A // Output: modified individual 'A' func FunMutation(A []Func_t, time int, ops *OpsData) { size := len(A) if !rnd.FlipCoin(ops.Pm) || size < 1 { return } pos := rnd.IntGetUniqueN(0, size, ops.Nchanges) for _, i := range pos { // TODO: improve this A[i] = func(ind *Individual) string { return "mutated" } } }
// MtIntBin performs the mutation of a binary chromosome // Output: modified individual 'A' func MtIntBin(A []int, prms *Parameters) { size := len(A) if !rnd.FlipCoin(prms.IntPm) || size < 1 { return } pos := rnd.IntGetUniqueN(0, size, prms.IntNchanges) for _, i := range pos { if A[i] == 0 { A[i] = 1 } else { A[i] = 0 } } }
// IntBinMutation performs the mutation of a binary chromosome // Output: modified individual 'A' func IntBinMutation(A []int, time int, ops *OpsData) { size := len(A) if !rnd.FlipCoin(ops.Pm) || size < 1 { return } pos := rnd.IntGetUniqueN(0, size, ops.Nchanges) for _, i := range pos { if A[i] == 0 { A[i] = 1 } else { A[i] = 0 } } }
// MtIntOrd performs the mutation of genetic data from a ordered list of integers A // Output: modified individual 'A' // Note: using DM method as explained in [1] (citing [2]) // References: // [1] Larrañaga P, Kuijpers CMH, Murga RH, Inza I and Dizdarevic S. Genetic Algorithms for the // Travelling Salesman Problem: A Review of Representations and Operators. Artificial // Intelligence Review, 13:129-170; 1999. doi:10.1023/A:1006529012972 // [2] Michalewicz Z. Genetic Algorithms + Data Structures = Evolution Programs. Berlin // Heidelberg: Springer Verlag; 1992 // Joint Conference on Artificial Intelligence, 162-164; 1985. // // DM displacement mutation method: // Ex: // 0 1 2 3 4 5 6 7 // A = a b c d e f g h s = 2 // ↑ ↑ t = 5 // 2 5 // // core = c d e (subtour) ncore = t - s = 5 - 2 = 3 // // 0 1 2 3 4 // remain = a b f g h (remaining) nrem = size - ncore = 8 - 3 = 5 // ↑ // 4 = ins func MtIntOrd(A []int, prms *Parameters) { size := len(A) if !rnd.FlipCoin(prms.IntPm) || size < 3 { if size == 2 { A[0], A[1] = A[1], A[0] } return } var indices []int var s, t, ncore, nrem, ins int if indices != nil { s, t, ins = indices[0], indices[1], indices[2] ncore = t - s nrem = size - ncore } else { s = rnd.Int(1, size-2) t = rnd.Int(s+1, size-1) ncore = t - s nrem = size - ncore ins = rnd.Int(1, nrem) } core := make([]int, ncore) remain := make([]int, nrem) var jc, jr int for i := 0; i < size; i++ { if i >= s && i < t { core[jc] = A[i] jc++ } else { remain[jr] = A[i] jr++ } } jc, jr = 0, 0 for i := 0; i < size; i++ { if i < ins { A[i] = remain[jr] jr++ } else { if jc < ncore { A[i] = core[jc] jc++ } else { A[i] = remain[jr] jr++ } } } }
// CxIntOrd performs the crossover in a pair of individuals with integer numbers // that correspond to a ordered sequence, e.g. for traveling salesman problem // Output: // a and b -- offspring chromosomes // Note: using OX1 method explained in [1] (proposed in [2]) // References: // [1] Larrañaga P, Kuijpers CMH, Murga RH, Inza I and Dizdarevic S. Genetic Algorithms for the // Travelling Salesman Problem: A Review of Representations and Operators. Artificial // Intelligence Review, 13:129-170; 1999. doi:10.1023/A:1006529012972 // [2] Davis L. Applying Adaptive Algorithms to Epistatic Domains. Proceedings of International // Joint Conference on Artificial Intelligence, 162-164; 1985. // Example: // data: // 0 1 2 3 4 5 6 7 // A = a b | c d e | f g h size = 8 // B = b d | f h g | e c a cuts = [2, 5] // ↑ ↑ ↑ ends = [2, 5, 8] // 2 5 8 // first step: copy subtours // a = . . | f h g | . . . // b = . . | c d e | . . . // second step: copy unique from subtour's end, position 5 // start adding here // ↓ 5 6 7 0 1 2 3 4 // a = d e | f h g | a b c get from A: | f̶ g̶ h̶ | a b | c d e // b = h g | c d e | a b f get from B: | e̶ c̶ a | b d̶ | f h g func CxIntOrd(a, b, A, B []int, prms *Parameters) { size := len(A) if !rnd.FlipCoin(prms.IntPc) || size < 3 { for i := 0; i < len(A); i++ { a[i], b[i] = A[i], B[i] } return } var s, t int var cuts []int if len(cuts) == 2 { s, t = cuts[0], cuts[1] } else { s = rnd.Int(1, size-2) t = rnd.Int(s+1, size-1) } chk.IntAssertLessThan(s, t) acore := B[s:t] bcore := A[s:t] ncore := t - s acorehas := make(map[int]bool) // TODO: check if map can be replaced => improve efficiency bcorehas := make(map[int]bool) for i := 0; i < ncore; i++ { a[s+i] = acore[i] b[s+i] = bcore[i] acorehas[acore[i]] = true bcorehas[bcore[i]] = true } ja, jb := t, t for i := 0; i < size; i++ { k := (i + t) % size if !acorehas[A[k]] { a[ja] = A[k] ja++ if ja == size { ja = 0 } } if !bcorehas[B[k]] { b[jb] = B[k] jb++ if jb == size { jb = 0 } } } return }
// IntOrdMutation performs the mutation of genetic data from a ordered list of integers A // Output: modified individual 'A' // Note: using DM method as explained in [1] (citing [2]) // References: // [1] Larrañaga P, Kuijpers CMH, Murga RH, Inza I and Dizdarevic S. Genetic Algorithms for the // Travelling Salesman Problem: A Review of Representations and Operators. Artificial // Intelligence Review, 13:129-170; 1999. doi:10.1023/A:1006529012972 // [2] Michalewicz Z. Genetic Algorithms + Data Structures = Evolution Programs. Berlin // Heidelberg: Springer Verlag; 1992 // Joint Conference on Artificial Intelligence, 162-164; 1985. // // DM displacement mutation method: // Ex: // 0 1 2 3 4 5 6 7 // A = a b c d e f g h s = 2 // ↑ ↑ t = 5 // 2 5 // // core = c d e (subtour) ncore = t - s = 5 - 2 = 3 // // 0 1 2 3 4 // remain = a b f g h (remaining) nrem = size - ncore = 8 - 3 = 5 // ↑ // 4 = ins func IntOrdMutation(A []int, time int, ops *OpsData) { size := len(A) if !rnd.FlipCoin(ops.Pm) || size < 3 { if size == 2 { A[0], A[1] = A[1], A[0] } return } var s, t, ncore, nrem, ins int if ops.OrdSti != nil { s, t, ins = ops.OrdSti[0], ops.OrdSti[1], ops.OrdSti[2] ncore = t - s nrem = size - ncore } else { s = rnd.Int(1, size-2) t = rnd.Int(s+1, size-1) ncore = t - s nrem = size - ncore ins = rnd.Int(1, nrem) } core := make([]int, ncore) remain := make([]int, nrem) var jc, jr int for i := 0; i < size; i++ { if i >= s && i < t { core[jc] = A[i] jc++ } else { remain[jr] = A[i] jr++ } } jc, jr = 0, 0 for i := 0; i < size; i++ { if i < ins { A[i] = remain[jr] jr++ } else { if jc < ncore { A[i] = core[jc] jc++ } else { A[i] = remain[jr] jr++ } } } }
// GenTrialSolutions generates (initial) trial solutions func GenTrialSolutions(sols []*Solution, prms *Parameters) { // floats n := len(sols) // cannot use Nsol here because subsets of Solutions may be provided; e.g. parallel code if prms.Nflt > 0 { // interior points switch prms.GenType { case "latin": K := rnd.LatinIHS(prms.Nflt, n, prms.LatinDup) for i := 0; i < n; i++ { for j := 0; j < prms.Nflt; j++ { sols[i].Flt[j] = prms.FltMin[j] + float64(K[j][i]-1)*prms.DelFlt[j]/float64(n-1) } } case "halton": H := rnd.HaltonPoints(prms.Nflt, n) for i := 0; i < n; i++ { for j := 0; j < prms.Nflt; j++ { sols[i].Flt[j] = prms.FltMin[j] + H[j][i]*prms.DelFlt[j] } } default: for i := 0; i < n; i++ { for j := 0; j < prms.Nflt; j++ { sols[i].Flt[j] = rnd.Float64(prms.FltMin[j], prms.FltMax[j]) } } } // extra points if prms.UseMesh { initX := func(isol int) { for k := 0; k < prms.Nflt; k++ { sols[isol].Flt[k] = (prms.FltMin[k] + prms.FltMax[k]) / 2.0 } } isol := prms.Nsol - prms.NumExtraSols for i := 0; i < prms.Nflt-1; i++ { for j := i + 1; j < prms.Nflt; j++ { // (min,min) corner initX(isol) sols[isol].Flt[i] = prms.FltMin[i] sols[isol].Flt[j] = prms.FltMin[j] sols[isol].Fixed = true isol++ // (min,max) corner initX(isol) sols[isol].Flt[i] = prms.FltMin[i] sols[isol].Flt[j] = prms.FltMax[j] sols[isol].Fixed = true isol++ // (max,max) corner initX(isol) sols[isol].Flt[i] = prms.FltMax[i] sols[isol].Flt[j] = prms.FltMax[j] sols[isol].Fixed = true isol++ // (max,min) corner initX(isol) sols[isol].Flt[i] = prms.FltMax[i] sols[isol].Flt[j] = prms.FltMin[j] sols[isol].Fixed = true isol++ // Xi-min middle points ndelta := float64(prms.Nbry - 1) for m := 0; m < prms.Nbry-2; m++ { initX(isol) sols[isol].Flt[i] = prms.FltMin[i] sols[isol].Flt[j] = prms.FltMin[j] + float64(m+1)*prms.DelFlt[j]/ndelta sols[isol].Fixed = true isol++ } // Xi-max middle points for m := 0; m < prms.Nbry-2; m++ { initX(isol) sols[isol].Flt[i] = prms.FltMax[i] sols[isol].Flt[j] = prms.FltMin[j] + float64(m+1)*prms.DelFlt[j]/ndelta sols[isol].Fixed = true isol++ } // Xj-min middle points for m := 0; m < prms.Nbry-2; m++ { initX(isol) sols[isol].Flt[i] = prms.FltMin[i] + float64(m+1)*prms.DelFlt[i]/ndelta sols[isol].Flt[j] = prms.FltMin[j] sols[isol].Fixed = true isol++ } // Xj-max middle points for m := 0; m < prms.Nbry-2; m++ { initX(isol) sols[isol].Flt[i] = prms.FltMin[i] + float64(m+1)*prms.DelFlt[i]/ndelta sols[isol].Flt[j] = prms.FltMax[j] sols[isol].Fixed = true isol++ } } } chk.IntAssert(isol, prms.Nsol) } } // skip if there are no ints if prms.Nint < 2 { return } // binary numbers if prms.BinInt > 0 { for i := 0; i < n; i++ { for j := 0; j < prms.Nint; j++ { if rnd.FlipCoin(0.5) { sols[i].Int[j] = 1 } else { sols[i].Int[j] = 0 } } } return } // general integers L := rnd.LatinIHS(prms.Nint, n, prms.LatinDup) for i := 0; i < n; i++ { for j := 0; j < prms.Nint; j++ { sols[i].Int[j] = prms.IntMin[j] + (L[j][i]-1)*prms.DelInt[j]/(n-1) } } }
func Test_int01(tst *testing.T) { //verbose() chk.PrintTitle("int01. organise sequence of ints") io.Pf("\n") // initialise random numbers generator rnd.Init(0) // 0 => use current time as seed // parameters C := NewConfParams() C.Nova = 1 C.Noor = 0 C.Nisl = 1 C.Ninds = 20 C.RegTol = 0 C.NumInts = 20 //C.GAtype = "crowd" C.CrowdSize = 2 C.Tf = 50 C.Verbose = chk.Verbose C.CalcDerived() // mutation function C.Ops.MtInt = func(A []int, time int, ops *OpsData) { size := len(A) if !rnd.FlipCoin(ops.Pm) || size < 1 { return } pos := rnd.IntGetUniqueN(0, size, ops.Nchanges) for _, i := range pos { if A[i] == 1 { A[i] = 0 } if A[i] == 0 { A[i] = 1 } } } // generation function C.PopIntGen = func(id int, cc *ConfParams) Population { o := make([]*Individual, cc.Ninds) genes := make([]int, cc.NumInts) for i := 0; i < cc.Ninds; i++ { for j := 0; j < cc.NumInts; j++ { genes[j] = rand.Intn(2) } o[i] = NewIndividual(cc.Nova, cc.Noor, cc.Nbases, genes) } return o } // objective function C.OvaOor = func(ind *Individual, idIsland, time int, report *bytes.Buffer) { score := 0.0 count := 0 for _, val := range ind.Ints { if val == 0 && count%2 == 0 { score += 1.0 } if val == 1 && count%2 != 0 { score += 1.0 } count++ } ind.Ovas[0] = 1.0 / (1.0 + score) return } // run optimisation evo := NewEvolver(C) evo.Run() // results ideal := 1.0 / (1.0 + float64(C.NumInts)) io.PfGreen("\nBest = %v\nBestOV = %v (ideal=%v)\n", evo.Best.Ints, evo.Best.Ovas[0], ideal) }
// PopFltGen generates a population of individuals with float point numbers // Notes: (1) ngenes = len(C.RangeFlt) func PopFltGen(id int, C *ConfParams) Population { o := make([]*Individual, C.Ninds) ngenes := len(C.RangeFlt) for i := 0; i < C.Ninds; i++ { o[i] = NewIndividual(C.Nova, C.Noor, C.Nbases, make([]float64, ngenes)) } if C.Latin { K := rnd.LatinIHS(ngenes, C.Ninds, C.LatinDf) dx := make([]float64, ngenes) for i := 0; i < ngenes; i++ { dx[i] = (C.RangeFlt[i][1] - C.RangeFlt[i][0]) / float64(C.Ninds-1) } for i := 0; i < ngenes; i++ { for j := 0; j < C.Ninds; j++ { o[j].SetFloat(i, C.RangeFlt[i][0]+float64(K[i][j]-1)*dx[i]) } } return o } npts := int(math.Pow(float64(C.Ninds), 1.0/float64(ngenes))) // num points in 'square' grid ntot := int(math.Pow(float64(npts), float64(ngenes))) // total num of individuals in grid den := 1.0 // denominator to calculate dx if npts > 1 { den = float64(npts - 1) } var lfto int // leftover, e.g. n % (nx*ny) var rdim int // reduced dimension, e.g. (nx*ny) var idx int // index of gene in grid var dx, x, mul, xmin, xmax float64 for i := 0; i < C.Ninds; i++ { if i < ntot { // on grid lfto = i for j := 0; j < ngenes; j++ { rdim = int(math.Pow(float64(npts), float64(ngenes-1-j))) idx = lfto / rdim lfto = lfto % rdim xmin = C.RangeFlt[j][0] xmax = C.RangeFlt[j][1] dx = xmax - xmin x = xmin + float64(idx+id)*dx/den if C.Noise > 0 { mul = rnd.Float64(0, C.Noise) if rnd.FlipCoin(0.5) { x += mul * x } else { x -= mul * x } } if x < xmin { x = xmin + (xmin - x) } if x > xmax { x = xmax - (x - xmax) } o[i].SetFloat(j, x) } } else { // additional individuals for j := 0; j < ngenes; j++ { xmin = C.RangeFlt[j][0] xmax = C.RangeFlt[j][1] x = rnd.Float64(xmin, xmax) o[i].SetFloat(j, x) } } } return o }