Ejemplo n.º 1
0
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
}
Ejemplo n.º 2
0
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
}