// 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++ } } } }
// 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 } }
// 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] } } }
// 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] } } }
// Solve solves optimisation problem func (o *Optimiser) Solve() { // benchmark if o.Verbose { t0 := gotime.Now() defer func() { io.Pf("\nnfeval = %d\n", o.Nfeval) io.Pfblue2("cpu time = %v\n", gotime.Now().Sub(t0)) }() } // output if o.Output != nil { o.Output(0, o.Solutions) } // perform evolution done := make(chan int, o.Ncpu) time := 0 texc := time + o.DtExc for time < o.Tf { // run groups in parallel. up to exchange time for icpu := 0; icpu < o.Ncpu; icpu++ { go func(cpu int) { nfeval := 0 for t := time; t < texc; t++ { if cpu == 0 && o.Verbose { io.Pf("time = %10d\r", t+1) } nfeval += o.EvolveOneGroup(cpu) } done <- nfeval }(icpu) } for cpu := 0; cpu < o.Ncpu; cpu++ { o.Nfeval += <-done } // compute metrics with all solutions included o.Metrics.Compute(o.Solutions) // exchange via tournament if o.Ncpu > 1 { if o.ExcTour { for i := 0; i < o.Ncpu; i++ { j := (i + 1) % o.Ncpu I := rnd.IntGetUnique(o.Groups[i].Indices, 2) J := rnd.IntGetUnique(o.Groups[j].Indices, 2) A, B := o.Groups[i].All[I[0]], o.Groups[i].All[I[1]] a, b := o.Groups[j].All[J[0]], o.Groups[j].All[J[1]] o.Tournament(A, B, a, b, o.Metrics) } } // exchange one randomly if o.ExcOne { rnd.IntGetGroups(o.cpupairs, utl.IntRange(o.Ncpu)) for _, pair := range o.cpupairs { i, j := pair[0], pair[1] n := utl.Imin(o.Groups[i].Ncur, o.Groups[j].Ncur) k := rnd.Int(0, n) A := o.Groups[i].All[k] B := o.Groups[j].All[k] B.CopyInto(o.tmp) A.CopyInto(B) o.tmp.CopyInto(A) } } } // update time variables time += o.DtExc texc += o.DtExc time = utl.Imin(time, o.Tf) texc = utl.Imin(texc, o.Tf) // output if o.Output != nil { o.Output(time, o.Solutions) } } }