func (c *Cubic) Initialize(loc *uni.Location, obj *uni.Objective, grad *uni.Gradient) (err error) { //if c.step.Init() == math.NaN() { // c.step.SetInit(1) //} // Now initialize the three to set the initial location to the current location err = c.step.Initialize() if err != nil { return errors.New("cubic: error initializing: " + err.Error()) } // Initialize the rest of the memory c.prevF = obj.Init() c.initialGradNegative = (grad.Curr() < 0) c.currStepDirectionPositive = true c.deltaCurrent = 0.0 // How far is the current point from the initial point // Add in some checking on the Step Increase and decrease sizes return nil }
func (cubic *Cubic) Iterate(loc *uni.Location, obj *uni.Objective, grad *uni.Gradient, fun common.UniObjGrad) (common.Status, error) { // Initialize var stepMultiplier float64 updateCurrPoint := false reverseDirection := false currG := grad.Current() currF := obj.Current() // Evaluate trial point // Step Size is from the original point var trialX float64 if cubic.initialGradNegative { trialX = cubic.step.Current + loc.Initial } else { trialX = -cubic.step.Current + loc.Initial } /* fmt.Println(trialX, "trialX") fmt.Println("cubic current step", cubic.step.Current) */ trialF, trialG, err := fun.ObjGrad(trialX) if err != nil { return common.UserFunctionError, errors.New("gofunopter: cubic: user defined function error: " + err.Error()) } var newStepSize float64 /* fmt.Println() fmt.Println("curr step size", cubic.step.Current) fmt.Println("LB", cubic.step.Lb()) fmt.Println("UB", cubic.step.Ub()) fmt.Println("initX", loc.Initial) fmt.Println("currX", loc.Current()) 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.Current()) 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.Current()) } 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.Current - 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.SetCurrent(trialX) cubic.prevF = obj.Current() obj.SetCurrent(trialF) grad.SetCurrent(trialG) cubic.deltaCurrent = trialX - loc.Initial if cubic.initialGradNegative { cubic.deltaCurrent *= -1 } if reverseDirection { cubic.currStepDirectionPositive = !cubic.currStepDirectionPositive } } cubic.step.Current = newStepSize return common.Continue, nil }