示例#1
0
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
}
示例#2
0
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
}