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 (p *Particle) Update(newp *optim.Point) { // DO NOT update p's position with newp's position - it may have been // projected onto a mesh and be different. p.Val = newp.Val if p.Val < p.Best.Val { p.Best = newp.Clone() } }
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 genPollPoints(from *optim.Point, span Spanner, m optim.Mesh) []*optim.Point { ndim := from.Len() dirs := span.Span(ndim) polls := make([]*optim.Point, 0, len(dirs)) for _, d := range dirs { polls = append(polls, pointFromDirec(from, d, m)) } return polls }
func New(start *optim.Point, opts ...Option) *Method { m := &Method{ Curr: start, ev: optim.SerialEvaler{}, Poller: &Poller{Nkeep: start.Len() / 4, SkipEps: 1e-10}, Searcher: NullSearcher{}, NsuccessGrow: -1, } for _, opt := range opts { opt(m) } m.initdb() return m }
// 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 { 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 h != cp.prevhash || cp.prevstep != m.Step() { // TODO: write test that checks we poll compass dirs again if only mesh // step changed (and not from point) pollpoints = genPollPoints(from, cp.Spanner, m) cp.prevhash = h } else { // Use random directions instead. n := len(genPollPoints(from, cp.Spanner, m)) pollpoints = genPollPoints(from, &RandomN{N: n}, m) } 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) } 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 != nil && err != FoundBetterErr { return false, best, n, err } // 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 { return true, best, n, nil } else { return false, from, n, nil } }