func (lbfgs *Lbfgs) Iterate(loc *multi.Location, obj *uni.Objective, grad *multi.Gradient, fun optimize.MultiObjGrad) (status.Status, error) { counter := lbfgs.counter q := lbfgs.q a := lbfgs.a b := lbfgs.b rhoHist := lbfgs.rhoHist sHist := lbfgs.sHist yHist := lbfgs.yHist gamma_k := lbfgs.gamma_k tmp := lbfgs.tmp p_k := lbfgs.p_k s_k := lbfgs.s_k y_k := lbfgs.y_k z := lbfgs.z // Calculate search direction for i, val := range grad.Curr() { q[i] = val } for i := counter - 1; i >= 0; i-- { a[i] = rhoHist[i] * floats.Dot(sHist[i], q) copy(tmp, yHist[i]) floats.Scale(a[i], tmp) floats.Sub(q, tmp) } for i := lbfgs.NumStore - 1; i >= counter; i-- { a[i] = rhoHist[i] * floats.Dot(sHist[i], q) copy(tmp, yHist[i]) floats.Scale(a[i], tmp) //fmt.Println(q) //fmt.Println(tmp) floats.Sub(q, tmp) } // Assume H_0 is the identity times gamma_k copy(z, q) floats.Scale(gamma_k, z) // Second loop for update, going oldest to newest for i := counter; i < lbfgs.NumStore; i++ { b[i] = rhoHist[i] * floats.Dot(yHist[i], z) copy(tmp, sHist[i]) floats.Scale(a[i]-b[i], tmp) floats.Add(z, tmp) } for i := 0; i < counter; i++ { b[i] = rhoHist[i] * floats.Dot(yHist[i], z) copy(tmp, sHist[i]) floats.Scale(a[i]-b[i], tmp) floats.Add(z, tmp) } lbfgs.a = a lbfgs.b = b copy(p_k, z) floats.Scale(-1, p_k) normP_k := floats.Norm(p_k, 2) // Perform line search -- need to find some way to implement this, especially bookkeeping function values linesearchResult, err := linesearch.Linesearch(fun, lbfgs.LinesearchMethod, lbfgs.LinesearchSettings, lbfgs.Wolfe, p_k, loc.Curr(), obj.Curr(), grad.Curr()) // In the future add a check to switch to a different linesearcher? if err != nil { return status.LinesearchFailure, err } x_kp1 := linesearchResult.Loc f_kp1 := linesearchResult.Obj g_kp1 := linesearchResult.Grad alpha_k := linesearchResult.Step // Update hessian estimate copy(s_k, p_k) floats.Scale(alpha_k, s_k) copy(y_k, g_kp1) floats.Sub(y_k, grad.Curr()) skDotYk := floats.Dot(s_k, y_k) // Bookkeep the results stepSize := alpha_k * normP_k lbfgs.step.AddToHist(stepSize) lbfgs.step.SetCurr(stepSize) loc.SetCurr(x_kp1) //lbfgs.loc.AddToHist(x_kp1) //fmt.Println(lbfgs.loc.GetHist()) obj.SetCurr(f_kp1) grad.SetCurr(g_kp1) copy(sHist[counter], s_k) copy(yHist[counter], y_k) rhoHist[counter] = 1 / skDotYk lbfgs.gamma_k = skDotYk / floats.Dot(y_k, y_k) lbfgs.counter += 1 if lbfgs.counter == lbfgs.NumStore { lbfgs.counter = 0 } return status.Continue, nil }
func (cubic *Cubic) Iterate(loc *uni.Location, obj *uni.Objective, grad *uni.Gradient, fun optimize.UniObjGrad) (status.Status, error) { // Initialize var stepMultiplier float64 updateCurrPoint := false reverseDirection := false currG := grad.Curr() currF := obj.Curr() // Evaluate trial point // Step Size is from the original point var trialX float64 if cubic.initialGradNegative { trialX = cubic.step.Curr() + loc.Init() } else { trialX = -cubic.step.Curr() + loc.Init() } /* fmt.Println(trialX, "trialX") fmt.Println("cubic current step", cubic.step.Curr()) */ trialF, trialG, err := fun.ObjGrad(trialX) if err != nil { return status.UserFunctionError, errors.New("gofunopter: cubic: user defined function error: " + err.Error()) } var newStepSize float64 /* fmt.Println() fmt.Println("curr step size", cubic.step.Curr()) fmt.Println("LB", cubic.step.Lb()) fmt.Println("UB", cubic.step.Ub()) fmt.Println("initX", loc.Init()) fmt.Println("currX", loc.Curr()) fmt.Println("trialX ", trialX) fmt.Println("InitF \t", obj.Init()) fmt.Println("currF \t", currF) fmt.Println("trialF \t", trialF) fmt.Println("InitG", grad.Init()) fmt.Println("currG", currG) fmt.Println("trialG", trialG) */ absTrialG := math.Abs(trialG) // Find guess for next point deltaF := trialF - currF decreaseInValue := (deltaF <= 0) // See if we can trust the deltaF measurement var canTrustDeltaF bool divisor := math.Max(math.Abs(trialF), math.Abs(currF)) /* fmt.Println("Divisor is ", divisor) fmt.Println("Delta F", deltaF) */ if divisor == 0 { canTrustDeltaF = true // Both are zero, so is >= 0 } else { if math.Abs(deltaF) > divisor*cubic.FloatingpointRelTol { // Change large enough to trust canTrustDeltaF = true } // otherwise can't trust } changeInDerivSign := (currG > 0 && trialG < 0) || (currG < 0 && trialG > 0) decreaseInDerivMagnitude := (absTrialG < math.Abs(currG)) /* fmt.Println("Decrease in value ", decreaseInValue) fmt.Println("Change in deriv sign ", changeInDerivSign) fmt.Println("Decrease in deriv mag ", decreaseInDerivMagnitude) */ // Find coefficients of the cubic polynomial fit between the current point and the new point // Derived from fitting a cubic between (0, CurrF) and (1,TrialF). // Apply transformations later to reshift the coordinate axis // Need to play games with derivatives trialFitG := trialG currFitG := currG if cubic.initialGradNegative { trialFitG *= -1 currFitG *= -1 } if cubic.currStepDirectionPositive { trialFitG *= -1 currFitG *= -1 } var a, b, c float64 a = trialG + currG - 2*deltaF b = 3*deltaF - 2*currG - trialG c = currG det := (math.Pow(b, 2) - 3*a*c) //fmt.Println("det", det) if a == 0 { //Perfect quadratic fit stepMultiplier = -c / (2 * b) } else if det < 0 { if decreaseInValue && !changeInDerivSign { // The trial point has lower function value // and steeper gradient. Set this location as a // lower bound for the minimum, and set the // next point farther in that direction. cubic.setBound("Lower") // We know we need to increase in step, but unsure how much, so make a guess stepMultiplier = cubic.unclearStepIncrease(loc.Curr()) if decreaseInDerivMagnitude { updateCurrPoint = true } } else { // All other conditions we want to decrease the step size, but the // cubic doesn't give an estimate of how much. Just do a binary search cubic.setBound("Upper") //for i := 0; i < 10; i++ { // fmt.Println("Blah") //} //fmt.Println(cubic.step.Curr) stepMultiplier = 0.5 } } else { // Use the cubic projection to guess the minimum location for the line search minCubic := (-b + math.Sqrt(det)) / (3 * a) /* fmt.Println("sht") fmt.Println("minCubic", minCubic) fmt.Println("SizeLB",cubic.SizeLB) fmt.Println("SizeUB",cubic.SizeUB) */ switch { case changeInDerivSign: // There is a change in derivative sign between the current // point and the trial point. Make the trial point an upper // bound for the minimum, and set it as the current point // if the derivative is smaller in magnitude. cubic.setBound("Upper") stepMultiplier = cubic.sizeDecrease(minCubic) if decreaseInDerivMagnitude && decreaseInValue { updateCurrPoint = true reverseDirection = true } case (!changeInDerivSign && decreaseInValue) || !canTrustDeltaF: // No change in derivative sign, but a decrease in // function value. Want to move more in this direction // and the trial point is a new lower bound and a new // base for the cubic approximation cubic.setBound("Lower") updateCurrPoint = true if decreaseInDerivMagnitude { if minCubic < cubic.StepIncreaseMin { // Cubic gave a bad approximation (minimum is more // in this direction). Assume linear decrease in // derivative // Check this line stepMultiplier = math.Abs(currG) / (math.Abs(trialG) - math.Abs(currG)) } stepMultiplier = cubic.sizeIncrease(stepMultiplier) } else { // Found a better point, but the derivative increased // Use cubic approximation if it gives a reasonable guess // otherwise just project forward if minCubic < cubic.StepIncreaseMin { stepMultiplier = cubic.unclearStepIncrease(loc.Curr()) } else { stepMultiplier = cubic.sizeIncrease(minCubic) } } case (!changeInDerivSign && !decreaseInValue) || canTrustDeltaF: // Neither a decrease in value nor a change in derivative sign. // This means there must be a local minimum between the starting location and // this one. Don't update the cubic point cubic.setBound("Upper") stepMultiplier = math.Min(minCubic, 0.75) stepMultiplier = math.Max(stepMultiplier, 0.25) default: panic("Bad logic in cases") } } var deltaXTrialCurrent float64 deltaXTrialCurrent = cubic.step.Curr() - cubic.deltaCurrent newDeltaXFromCurrent := deltaXTrialCurrent * stepMultiplier newStepSize = newDeltaXFromCurrent + cubic.deltaCurrent // Want to make sure that the new search location isn't pushing beyond // previously established bounds. If it is, just do a binary search between // the bounds if !cubic.step.WithinBounds(newStepSize) { newStepSize = cubic.step.Midpoint() } /* } else { fmt.Println("Can't trust deltaF") // Can't trust delta F, so just do a binary search updateCurrPoint = true switch { case changeInDerivSign && decreaseInDerivMagnitude: cubic.setBound("Upper") // this trial is the new upper bound updateCurrPoint = true case changeInDerivSign && !decreaseInDerivMagnitude: cubic.setBound("Upper") // this trial is the new upper bound updateCurrPoint = false case !changeInDerivSign && decreaseInDerivMagnitude: cubic.setBound("Lower") updateCurrPoint = true case !changeInDerivSign && !decreaseInDerivMagnitude: return nFunEvals, errors.New("Gradient tolerance too high for the precision of the gradient") } newStepSize = cubic.step.Midpoint() } */ if updateCurrPoint { loc.SetCurr(trialX) cubic.prevF = obj.Curr() obj.SetCurr(trialF) grad.SetCurr(trialG) cubic.deltaCurrent = trialX - loc.Init() if cubic.initialGradNegative { cubic.deltaCurrent *= -1 } if reverseDirection { cubic.currStepDirectionPositive = !cubic.currStepDirectionPositive } } cubic.step.SetCurr(newStepSize) return status.Continue, nil }