Beispiel #1
0
func TestLinprog(t *testing.T) {
	m := 500
	n := 1000
	tol := 1e-8

	A := mat.RandN(m, n)
	c := mat.RandVec(n)
	b := mat.NewVec(m)
	xt := mat.RandVec(n)
	b.Apply(A, xt)

	At := A.TrView()

	rd := mat.NewVec(n)
	rp := mat.NewVec(m)
	rs := mat.NewVec(n)

	prob := NewStandard(c, A, b)

	//Example for printing duality gap and infeasibilities
	result := Solve(prob, nil, NewDisplay(2))

	rd.Sub(c, result.S)
	rd.AddMul(At, result.Y, -1)
	rp.Apply(A, result.X)
	rp.Sub(b, rp)
	rs.Mul(result.X, result.S)
	rs.Neg(rs)

	dev := (rd.Asum() + rp.Asum() + rs.Asum()) / float64(n)
	if dev > tol {
		t.Log(dev)
		t.Fail()
	}
}
Beispiel #2
0
func (sol ProjGrad) Solve(o Grad, proj Projection, in *Solution, p *Params, u ...Updater) *Result {
	r := NewResult(in)
	obj := ObjGradWrapper{r: r, o: o}
	r.initGrad(obj)
	h := newHelper(r.Solution, u)

	n := len(r.X)
	s := 1.0 //initial step size

	d := mat.NewVec(n)
	d.Copy(r.GradX)
	d.Scal(-1)

	xTemp := mat.NewVec(n)

	xTemp.Copy(r.X)
	xTemp.Axpy(s/2, d)
	proj.Project(xTemp)
	xTemp.Sub(xTemp, r.X)
	xTemp.Scal(2 / s)

	gLin := -xTemp.Nrm2Sq()

	lineFunc := NewLineFuncProj(obj, proj, r.X, d)
	lsInit := uni.NewSolution()
	lsParams := uni.NewParams()

	for ; r.Status == NotTerminated; h.update(r, p) {
		lsInit.SetX(s)
		lsInit.SetLB(0, r.ObjX, gLin)
		lsRes := sol.LineSearch.Solve(lineFunc, lsInit, lsParams)
		if lsRes.Status < 0 {
			r.Status = Status(lsRes.Status)
			break
		}
		s, r.ObjX = lsRes.X, lsRes.ObjX

		r.X.Axpy(s, d)
		proj.Project(r.X)

		obj.ValGrad(r.X, r.GradX)
		d.Copy(r.GradX)
		d.Scal(-1)

		xTemp.Copy(r.X)
		xTemp.Axpy(s/2, d)
		proj.Project(xTemp)
		xTemp.Sub(xTemp, r.X)
		xTemp.Scal(2 / s)

		gLin = -xTemp.Nrm2Sq()
	}
	return r
}
Beispiel #3
0
func NewLineFuncDeriv(f Grad, x, d mat.Vec) *LineFuncDeriv {
	n := len(x)
	if len(d) != n {
		panic("dimension mismatch")
	}
	return &LineFuncDeriv{
		f:     f,
		x:     x,
		d:     d,
		g:     mat.NewVec(n),
		xTemp: mat.NewVec(n),
	}
}
Beispiel #4
0
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)
}
Beispiel #5
0
func newHelper(in *Solution, u []Updater) *helper {
	h := &helper{}
	h.initialTime = time.Now()
	h.updates = u

	if in.GradX != nil {
		h.initialGradNorm = in.GradX.Nrm2()
	} else {
		h.initialGradNorm = nan
	}

	h.oldX = mat.NewVec(len(in.X)).Scal(nan)
	h.temp = mat.NewVec(len(in.X)).Scal(nan)
	h.oldObjX = nan
	h.gradNorm = nan
	return h
}
Beispiel #6
0
func NewLineFuncProj(f Function, p Projection, x, d mat.Vec) *LineFuncProj {
	n := len(x)
	if len(d) != n {
		panic("dimension mismatch")
	}
	return &LineFuncProj{
		f:     f,
		p:     p,
		x:     x,
		d:     d,
		xTemp: mat.NewVec(n),
	}
}
Beispiel #7
0
func NewLineFunc(f Function, x, d mat.Vec) *LineFunc {
	n := len(x)
	if len(d) != n {
		panic("dimension mismatch")
	}
	return &LineFunc{
		f:     f,
		x:     x,
		d:     d,
		Dir:   1,
		xTemp: mat.NewVec(n),
	}
}
Beispiel #8
0
func NewResult(prob *Problem) *Result {
	r := &Result{}
	r.Solution = &Solution{}
	m, n := prob.A.Dims()
	r.X = mat.NewVec(n)
	r.S = mat.NewVec(n)
	r.Y = mat.NewVec(m)

	return r

	/*
		if in.X != nil {
			r.X.Copy(in.X)
		}
		if in.S != nil {
			r.S.Copy(in.S)
		}
		if in.Y != nil {
			r.Y.Copy(in.Y)
		}
	*/
}
Beispiel #9
0
func BenchmarkLinprog(bench *testing.B) {
	bench.StopTimer()
	m := 50
	n := 100
	tol := 1e-3
	rd := mat.NewVec(n)
	rp := mat.NewVec(m)
	rs := mat.NewVec(n)

	for i := 0; i < bench.N; i++ {
		A := mat.RandN(m, n)
		c := mat.RandVec(n)
		b := mat.NewVec(m)
		xt := mat.RandVec(n)
		b.Apply(A, xt)

		At := A.TrView()

		prob := NewStandard(c, A, b)
		bench.StartTimer()
		result := Solve(prob, nil)
		bench.StopTimer()

		rd.Sub(c, result.S)
		rd.AddMul(At, result.Y, -1)
		rp.Apply(A, result.X)
		rp.Sub(b, rp)
		rs.Mul(result.X, result.S)
		rs.Neg(rs)

		dev := (rd.Asum() + rp.Asum() + rs.Asum()) / float64(n)
		if dev > tol {
			bench.Log(dev)
		}
	}
}
Beispiel #10
0
func NewQuadratic(A *mat.Dense, b mat.Vec, c float64) *Quadratic {
	m, n := A.Dims()
	if m != n {
		panic("matrix has to be quadratic")
	}
	if n != len(b) {
		panic("dimension mismatch between A and b")
	}
	return &Quadratic{
		A:    A,
		B:    b,
		C:    c,
		temp: mat.NewVec(n),
	}
}
Beispiel #11
0
func TestLinprog2(t *testing.T) {
	m := 1
	n := 5
	tol := 1e-8

	a := mat.NewVec(n).AddSc(1)
	xStar := mat.NewVec(n)
	xStar[0] = 1

	A := mat.NewFromArray(a, true, m, n)
	At := A.TrView()
	b := mat.NewVec(m).AddSc(1)
	c := mat.NewVec(n)
	c[0] = -1

	prob := NewStandard(c, A, b)

	result := Solve(prob, nil)

	rd := mat.NewVec(n)
	rp := mat.NewVec(m)
	rs := mat.NewVec(n)

	rd.Sub(c, result.S)
	rd.AddMul(At, result.Y, -1)
	rp.Apply(A, result.X)
	rp.Sub(b, rp)
	rs.Mul(result.X, result.S)
	rs.Neg(rs)
	dev := (rd.Asum() + rp.Asum() + rs.Asum()) / float64(n)
	if dev > tol {
		t.Fail()
	}

	temp := mat.NewVec(n)
	temp.Sub(result.X, xStar)

	if temp.Nrm2() > tol {
		t.Log(result.X)
		t.Fail()
	}
}
Beispiel #12
0
func (sol SteepestDescent) 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)

	n := len(r.X)
	s := 1.0 //initial step size
	gLin := -r.GradX.Nrm2Sq()

	d := mat.NewVec(n)
	d.Copy(r.GradX)
	d.Scal(-1)

	lineFunc := NewLineFuncDeriv(obj, r.X, d)
	lsInit := uni.NewSolution()
	lsParams := uni.NewParams()

	for ; r.Status == NotTerminated; h.update(r, p) {
		lsInit.SetX(s)
		lsInit.SetLB(0, r.ObjX, gLin)
		lsRes := sol.LineSearch.Solve(lineFunc, lsInit, lsParams)
		if lsRes.Status < 0 {
			r.Status = Status(lsRes.Status)
			break
		}
		s, r.ObjX = lsRes.X, lsRes.ObjX

		r.X.Axpy(s, d)

		obj.ValGrad(r.X, r.GradX)
		d.Copy(r.GradX)
		d.Scal(-1)

		gLin = -d.Nrm2Sq()
	}
	return r
}
Beispiel #13
0
func TestQuadratic(t *testing.T) {
	mat.Register(cops)
	n := 10
	xStar := mat.NewVec(n)
	xStar.AddSc(1)
	A := mat.RandN(n)
	At := A.TrView()
	AtA := mat.New(n)
	AtA.Mul(At, A)

	bTmp := mat.NewVec(n)
	bTmp.Apply(A, xStar)
	b := mat.NewVec(n)
	b.Apply(At, bTmp)
	b.Scal(-2)

	c := bTmp.Nrm2Sq()

	//Define input arguments
	obj := opt.NewQuadratic(AtA, b, c)
	p := NewParams()
	sol := NewSolution(mat.NewVec(n))

	//Steepest descent with armijo
	stDesc := NewSteepestDescent()
	res1 := stDesc.Solve(obj, sol, p, NewDisplay(100))

	t.Log(res1.ObjX, res1.FunEvals, res1.GradEvals, res1.Status)

	//Steepest descent with Quadratic
	stDesc.LineSearch = uni.DerivWrapper{uni.NewQuadratic()}
	res2 := stDesc.Solve(obj, sol, p, NewDisplay(100))

	t.Log(res2.ObjX, res2.FunEvals, res2.GradEvals, res2.Status)

	//LBFGS with armijo
	lbfgs := NewLBFGS()
	res3 := lbfgs.Solve(obj, sol, p, NewDisplay(10))

	t.Log(res3.ObjX, res3.FunEvals, res3.GradEvals, res3.Status)

	//constrained problems (constraints described as projection)
	projGrad := NewProjGrad()

	res4 := projGrad.Solve(obj, opt.RealPlus{}, sol, p, NewDisplay(100))

	t.Log(res4.ObjX, res4.FunEvals, res4.GradEvals, res4.Status)

	if math.Abs(res1.ObjX) > 0.01 {
		t.Fail()
	}
	if math.Abs(res2.ObjX) > 0.01 {
		t.Fail()
	}
	if math.Abs(res3.ObjX) > 0.01 {
		t.Fail()
	}
	if math.Abs(res4.ObjX) > 0.01 {
		t.Fail()
	}
}
Beispiel #14
0
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
}
Beispiel #15
0
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
}
Beispiel #16
0
//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
}