// 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 }