func (Q *Quadratic) Val(x mat.Vec) float64 { val := 0.0 Q.temp.Apply(Q.A, x) val += mat.Dot(x, Q.temp) val += mat.Dot(x, Q.B) val += Q.C return val }
func (Q *Quadratic) ValGrad(x, g mat.Vec) float64 { At := Q.A.TrView() Q.temp.Apply(Q.A, x) g.Apply(At, x) g.Add(g, Q.temp) g.Add(g, Q.B) val := 0.0 val += mat.Dot(x, Q.temp) val += mat.Dot(x, Q.B) val += Q.C return val }
func TestExampleModel(t *testing.T) { //badly conditioned Hessian leads to zig-zagging of the steepest descent //algorithm condNo := 100.0 optSol := mat.Vec{1, 2} A := mat.NewFromArray([]float64{condNo, 0, 0, 1}, true, 2, 2) b := mat.Vec{-2 * optSol[0] * condNo, -2 * optSol[1]} c := -0.5 * mat.Dot(b, optSol) //define objective function fun := opt.NewQuadratic(A, b, c) //set inital solution estimate sol := NewSolution(mat.NewVec(2)) //set termination parameters p := NewParams() p.IterMax = 5 //Use steepest descent solver to solve the model result := NewSteepestDescent().Solve(fun, sol, p, NewDisplay(1)) fmt.Println("x =", result.X) //should be [1,2], but because of the bad conditioning we made little //progress in the second dimension //Use a BFGS solver to refine the result: result = NewLBFGS().Solve(fun, result.Solution, p, NewDisplay(1)) fmt.Println("x =", result.X) }
func (lf *LineFuncDeriv) ValDeriv(x float64) (float64, float64) { lf.xTemp.Copy(lf.x) lf.xTemp.Axpy(x, lf.d) val := lf.f.ValGrad(lf.xTemp, lf.g) return val, mat.Dot(lf.d, lf.g) }
func (sol LBFGS) Solve(o Grad, in *Solution, p *Params, u ...Updater) *Result { r := NewResult(in) obj := ObjGradWrapper{r: r, o: o} r.initGrad(obj) h := newHelper(r.Solution, u) stepSize := 1.0 gLin := 0.0 n := len(r.X) S := make([]mat.Vec, sol.Mem) Y := make([]mat.Vec, sol.Mem) for i := 0; i < sol.Mem; i++ { S[i] = mat.NewVec(n) Y[i] = mat.NewVec(n) } d := mat.NewVec(n) xOld := mat.NewVec(n) gOld := mat.NewVec(n) sNew := mat.NewVec(n) yNew := mat.NewVec(n) alphas := mat.NewVec(sol.Mem) betas := mat.NewVec(sol.Mem) rhos := mat.NewVec(sol.Mem) lineFunc := NewLineFuncDeriv(obj, r.X, d) lsInit := uni.NewSolution() lsParams := uni.NewParams() for ; r.Status == NotTerminated; h.update(r, p) { d.Copy(r.GradX) if r.Iter > 0 { yNew.Sub(r.GradX, gOld) sNew.Sub(r.X, xOld) temp := S[len(S)-1] copy(S[1:], S) S[0] = temp S[0].Copy(sNew) temp = Y[len(S)-1] copy(Y[1:], Y) Y[0] = temp Y[0].Copy(yNew) copy(rhos[1:], rhos) rhos[0] = 1 / mat.Dot(sNew, yNew) for i := 0; i < sol.Mem; i++ { alphas[i] = rhos[i] * mat.Dot(S[i], d) d.Axpy(-alphas[i], Y[i]) } for i := sol.Mem - 1; i >= 0; i-- { betas[i] = rhos[i] * mat.Dot(Y[i], d) d.Axpy(alphas[i]-betas[i], S[i]) } } d.Scal(-1) gLin = mat.Dot(d, r.GradX) lsInit.SetX(stepSize) lsInit.SetLB(0, r.ObjX, gLin) lsRes := sol.LineSearch.Solve(lineFunc, lsInit, lsParams) if lsRes.Status < 0 { fmt.Println("Linesearch:", lsRes.Status) d.Copy(r.GradX) d.Scal(-1) lsInit.SetLB(0, r.ObjX, -r.GradX.Nrm2Sq()) lsRes = sol.LineSearch.Solve(lineFunc, lsInit, lsParams) if lsRes.Status < 0 { fmt.Println("Linesearch:", lsRes.Status) r.Status = Status(lsRes.Status) break } } stepSize, r.ObjX = lsRes.X, lsRes.ObjX xOld.Copy(r.X) gOld.Copy(r.GradX) r.X.Axpy(stepSize, d) obj.ValGrad(r.X, r.GradX) } return r }
func (sol *Rosenbrock) Solve(o Function, in *Solution, p *Params, u ...Updater) *Result { r := NewResult(in) obj := ObjWrapper{r: r, o: o} r.init(obj) h := newHelper(r.Solution, u) eps := 1.0 n := len(r.X) d := make([]mat.Vec, n) for i := range d { d[i] = mat.NewVec(n) d[i][i] = 1 } lambda := make([]float64, n) lf := make([]*LineFunc, n) for i := range lf { lf[i] = NewLineFunc(obj, r.X, d[i]) } lsInit := uni.NewSolution() lsParams := uni.NewParams() lsParams.XTolAbs = p.XTolAbs lsParams.XTolRel = p.XTolRel lsParams.FunTolAbs = 0 lsParams.FunTolRel = 0 for ; r.Status == NotTerminated; h.update(r, p) { //Search in all directions for i := range d { lf[i].Dir = 1 valNeg := 0.0 valPos := lf[i].Val(eps) if valPos >= r.ObjX { lf[i].Dir = -1 valNeg = lf[i].Val(eps) if valNeg >= r.ObjX { eps *= 0.5 lf[i].Dir = 1 lsInit.SetLB(-eps) lsInit.SetUB(eps) lsInit.SetX(0) } else { lsInit.SetUB() lsInit.SetLB() lsInit.SetX(eps) } } else { lsInit.SetUB() lsInit.SetLB() lsInit.SetX(eps) } lsRes := sol.LineSearch.Solve(lf[i], lsInit, lsParams) lambda[i] = lf[i].Dir * lsRes.X r.X.Axpy(lambda[i], d[i]) r.ObjX = lsRes.ObjX } //Find new directions for i := range d { if math.Abs(lambda[i]) > p.XTolAbs { d[i].Scal(lambda[i]) for j := i + 1; j < n; j++ { d[i].Axpy(lambda[j], d[j]) } } } //Gram-Schmidt, TODO:use QR factorization for i := range d { d[i].Scal(1 / d[i].Nrm2()) for j := i + 1; j < n; j++ { d[j].Axpy(-mat.Dot(d[i], d[j]), d[i]) } } } return r }
//Predictor-Corrector Interior Point implementation func (sol *PredCorr) Solve(prob *Problem, p *Params, u ...Updater) *Result { res := NewResult(prob) h := newHelper(u) A := prob.A m, n := A.Dims() At := A.TrView() var mu, sigma float64 res.X.AddSc(1) res.S.AddSc(1) res.Rd = mat.NewVec(n) res.Rp = mat.NewVec(m) res.Rs = mat.NewVec(n) x := res.X s := res.S y := res.Y dx := mat.NewVec(n) ds := mat.NewVec(n) dy := mat.NewVec(m) dxAff := mat.NewVec(n) dsAff := mat.NewVec(n) dyAff := mat.NewVec(m) dxCC := mat.NewVec(n) dsCC := mat.NewVec(n) dyCC := mat.NewVec(m) xdivs := mat.NewVec(n) temp := mat.New(m, n) lhs := mat.New(m, m) rhs := mat.NewVec(m) soli := mat.NewVec(m) triU := mat.New(m, m) triUt := triU.TrView() nTemp1 := mat.NewVec(n) nTemp2 := mat.NewVec(n) alpha := 0.0 for { res.Rd.Sub(prob.C, res.S) res.Rd.AddMul(At, res.Y, -1) res.Rp.Apply(A, res.X) res.Rp.Sub(prob.B, res.Rp) res.Rs.Mul(res.X, res.S) res.Rs.Neg(res.Rs) if h.update(res, p); res.Status != 0 { break } if checkKKT(res, p); res.Status != 0 { break } mu = res.Rs.Asum() / float64(n) //determining left hand side temp.ScalCols(A, xdivs.Div(x, s)) lhs.Mul(temp, At) //factorization lhs.Chol(triU) //right hand side nTemp1.Add(res.Rd, s) nTemp1.Mul(nTemp1, xdivs) rhs.Apply(A, nTemp1) rhs.Add(rhs, res.Rp) //solving for dyAff soli.Trsv(triUt, rhs) dyAff.Trsv(triU, soli) //calculating other steps (dxAff, dsAff) nTemp1.Apply(At, dyAff) dxAff.Sub(nTemp1, res.Rd) dxAff.Sub(dxAff, s) dxAff.Mul(dxAff, xdivs) dsAff.Div(dxAff, xdivs) dsAff.Add(dsAff, s) dsAff.Neg(dsAff) //determining step size alpha = 1.0 for i := range dxAff { if dxAff[i] < 0 { alph := -x[i] / dxAff[i] if alph < alpha { alpha = alph } } } for i := range dsAff { if dsAff[i] < 0 { alph := -s[i] / dsAff[i] if alph < alpha { alpha = alph } } } alpha *= 0.99995 //calculating duality gap measure for affine case nTemp1.Copy(x) nTemp1.Axpy(alpha, dxAff) nTemp2.Copy(s) nTemp2.Axpy(alpha, dsAff) mu_aff := mat.Dot(nTemp1, nTemp2) / float64(n) //centering parameter sigma = math.Pow(mu_aff/mu, 3) //right hand side for predictor corrector step res.Rs.Mul(dxAff, dsAff) res.Rs.Neg(res.Rs) res.Rs.AddSc(sigma * mu_aff) nTemp1.Div(res.Rs, s) nTemp1.Neg(nTemp1) rhs.Apply(A, nTemp1) //solving for dyCC soli.Trsv(triUt, rhs) dyCC.Trsv(triU, soli) //calculating other steps (dxAff, dsAff) nTemp1.Apply(At, dyCC) dxCC.Mul(nTemp1, x) dxCC.Add(res.Rs, dxCC) dxCC.Div(dxCC, s) dsCC.Mul(dxCC, s) dsCC.Sub(res.Rs, dsCC) dsCC.Div(dsCC, x) dx.Add(dxAff, dxCC) dy.Add(dyAff, dyCC) ds.Add(dsAff, dsCC) alpha = 1 for i := range dx { if dx[i] < 0 { alph := -x[i] / dx[i] if alph < alpha { alpha = alph } } } for i := range ds { if ds[i] < 0 { alph := -s[i] / ds[i] if alph < alpha { alpha = alph } } } alpha *= 0.99995 x.Axpy(alpha, dx) y.Axpy(alpha, dy) s.Axpy(alpha, ds) } return res }