func direcbetween(from, to *optim.Point, m optim.Mesh) []int { d := make([]int, from.Len()) step := m.Step() for i, x0 := range from.Pos { d[i] = int((to.Pos[i] - x0) / step) } return d }
func pointFromDirec(from *optim.Point, direc []int, m optim.Mesh) *optim.Point { pos := make([]float64, from.Len()) step := m.Step() for i, x0 := range from.Pos { pos[i] = x0 + float64(direc[i])*step } return &optim.Point{m.Nearest(pos), math.Inf(1)} }
func (m *Method) Iterate(obj optim.Objectiver, mesh optim.Mesh) (best *optim.Point, neval int, err error) { defer func() { m.iter++ }() // project positions onto mesh pmap := make(map[*optim.Point]*Particle, len(m.Pop)) points := make([]*optim.Point, len(m.Pop)) for i, particle := range m.Pop { p := particle.Point.Clone() p.Val = math.Inf(1) points[i] = p pmap[p] = particle } if mesh != nil { for _, p := range points { p.Pos = mesh.Nearest(p.Pos) } } // evaluate current positions results, n, err := m.Evaler.Eval(obj, points...) for _, p := range results { pmap[p].Update(p) } // TODO: write test to make sure this checks pbest.Best.Val instead of p.Val. pbest := m.Pop.Best() if pbest != nil && pbest.Best.Val < m.best.Val { m.best = pbest.Best } m.updateDb(mesh) // move particles and update current best for _, p := range m.Pop { p.Move(m.best, m.Vmax, m.InertiaFn(m.iter), m.Social, m.Cognition) } // Kill slow particles near global optimum. // This MUST go after the updating of the iterator's best position. for i, p := range m.Pop { if p.Kill(m.best, m.Xtol, m.Vtol) { m.Pop = append(m.Pop[:i], m.Pop[i+1:]...) } } return m.best, n, err }
func (m *Method) updateDb(mesh optim.Mesh) { if m.Db == nil { return } tx, err := m.Db.Begin() if err != nil { panic(err.Error()) } defer tx.Commit() s0, err := tx.Prepare("INSERT INTO " + TblParticles + " (particle,iter,val,posid,velid,vel) VALUES (?,?,?,?,?,?);") if checkdberr(err) { return } s0b, err := tx.Prepare("INSERT INTO " + TblParticlesMeshed + " (particle,iter,val,posid) VALUES (?,?,?,?);") if checkdberr(err) { return } s1, err := tx.Prepare("INSERT INTO " + TblParticlesBest + " (particle,iter,best,posid) VALUES (?,?,?,?);") if checkdberr(err) { return } pts := []*optim.Point{} for _, p := range m.Pop { vel := &optim.Point{Pos: p.Vel} pts = append(pts, p.Point) pts = append(pts, p.Best) // best might be a projected location and not present in normal eval points pts = append(pts, vel) _, err := s0.Exec(p.Id, m.iter, p.Val, p.HashSlice(), vel.HashSlice(), p.L2Vel()) if checkdberr(err) { return } _, err = s1.Exec(p.Id, m.iter, p.Best.Val, p.Best.HashSlice()) if checkdberr(err) { return } pp := &optim.Point{mesh.Nearest(p.Pos), p.Val} _, err = s0b.Exec(p.Id, m.iter, p.Val, pp.HashSlice()) if checkdberr(err) { return } } s2, err := tx.Prepare("INSERT INTO " + TblBest + " (iter,val,posid) VALUES (?,?,?);") glob := m.best _, err = s2.Exec(m.iter, glob.Val, glob.HashSlice()) if checkdberr(err) { return } pts = append(pts, glob) err = optim.RecordPointPos(tx, pts...) if checkdberr(err) { return } }
// Poll polls on mesh m centered on point from. It is responsible for // selecting points and evaluating them with ev using obj. If a better // point was found, it returns success == true, the point, and number of // evaluations. If a better point was not found, it returns false, the // from point, and the number of evaluations. If err is non-nil, success // must be false and best must be from - neval may be non-zero. func (cp *Poller) Poll(obj optim.Objectiver, ev optim.Evaler, m optim.Mesh, from *optim.Point) (success bool, best *optim.Point, neval int, err error) { best = from if cp.Spanner == nil { ndim := len(from.Pos) if ndim > 10 { cp.Spanner = &RandomN{N: len(from.Pos)} } else { cp.Spanner = Compass2N{} } } pollpoints := []*optim.Point{} // Only poll compass directions if we haven't polled from this point // before. DONT DELETE - this can fire sometimes if the mesh isn't // allowed to contract below a certain step (i.e. integer meshes). h := from.Hash() if cp.FlipCompass > 0 && cp.nConsecFail >= cp.FlipCompass { // Use compass directions instead cp.Spanner = CompassNp1{} } pollpoints = genPollPoints(from, cp.Spanner, m) cp.prevhash = h cp.prevstep = m.Step() // Add successful directions from last poll. We want to add these points // in front of the other points so we can potentially stop earlier if // polling opportunistically. perms := optim.Rand.Perm(len(pollpoints)) // this is an extra safety check to make sure we don't index out of bounds // on the perms slice max := len(cp.keepdirecs) if max > len(perms) { max = len(perms) } c2n := Compass2N{} if cp.Spanner != c2n { for i, dir := range cp.keepdirecs[:max] { swapindex := perms[i] pollpoints[swapindex] = pointFromDirec(from, dir.dir, m) } } // project points onto feasible region and mesh grid if m != nil { for _, p := range pollpoints { p.Pos = m.Nearest(p.Pos) } } cp.points = make([]*optim.Point, 0, len(pollpoints)) if cp.SkipEps == 0 { cp.points = pollpoints } else { for _, p := range pollpoints { // It is possible that due to the mesh gridding, the poll point is // outside of constraints or bounds and will be rounded back to the // current point. Check for this and skip the poll point if this is // the case. dist := optim.L2Dist(from, p) if dist > cp.SkipEps { cp.points = append(cp.points, p) } } } objstop := &objStopper{Objectiver: obj, Best: from.Val} results, n, err := ev.Eval(objstop, cp.points...) if err == FoundBetterErr { err = nil } // this is separate from best to allow all points better than from to be // added to keepdirecs before we update the best point. nextbest := from // Sort results and keep the best Nkeep as poll directions. for _, p := range results { if p.Val < best.Val { cp.keepdirecs = append(cp.keepdirecs, direc{direcbetween(from, p, m), p.Val}) } if p.Val < nextbest.Val { nextbest = p } } best = nextbest nkeep := cp.Nkeep if max := len(pollpoints) / 4; max < nkeep { nkeep = max } sort.Sort(byval(cp.keepdirecs)) if len(cp.keepdirecs) > nkeep { cp.keepdirecs = cp.keepdirecs[:nkeep] } if best.Val < from.Val { cp.nConsecFail = 0 } else { cp.nConsecFail++ } return best.Val < from.Val, best, n, err }
// Iterate mutates m and so for each iteration, the same, mutated m should be // passed in. func (m *Method) Iterate(o optim.Objectiver, mesh optim.Mesh) (best *optim.Point, n int, err error) { if m.count == 0 { m.origstep = mesh.Step() } else if mesh.Step() < m.ResetStep { mesh.SetStep(m.ResetStepSize) } var nevalsearch, nevalpoll int var success bool defer m.updateDb(&nevalsearch, &nevalpoll, mesh.Step()) m.count++ prevstep := mesh.Step() if !m.DiscreteSearch { mesh.SetStep(0) } success, best, nevalsearch, err = m.Searcher.Search(o, mesh, m.Curr) mesh.SetStep(prevstep) n += nevalsearch if success { m.Curr = best return best, n, err } // It is important to recenter mesh on new best point before polling. // This is necessary because the search may not be operating on the // current mesh grid. This doesn't need to happen if search succeeds // because search either always operates on the same grid, or always // operates in continuous space. mesh.SetOrigin(m.Curr.Pos) // TODO: test that this doesn't get set to Zero pos [0 0 0...] on first iteration. var err2 error success, best, nevalpoll, err2 = m.Poller.Poll(o, m.ev, mesh, m.Curr) m.Poller.Spanner.Update(mesh.Step(), success) n += nevalpoll if success { m.Curr = best m.nsuccess++ if m.nsuccess == m.NsuccessGrow { // == allows -1 to mean never grow mesh.SetStep(mesh.Step() / m.StepMult) m.nsuccess = 0 // reset after resize } // Important to recenter mesh on new best point. More particularly, // the mesh may have been resized and the new best may not lie on the // previous mesh grid. mesh.SetOrigin(best.Pos) return best, n, collect(err, err2) } else { m.nsuccess = 0 if nextstep := mesh.Step() * m.StepMult; nextstep > 0 { mesh.SetStep(mesh.Step() * m.StepMult) } return m.Curr, n, collect(err, err2) } }