Esempio n. 1
0
func TestFlattenTriangular(t *testing.T) {
	for i, test := range []struct {
		a   [][]float64
		ans []float64
		ul  blas.Uplo
	}{
		{
			a: [][]float64{
				{1, 2, 3},
				{0, 4, 5},
				{0, 0, 6},
			},
			ul:  blas.Upper,
			ans: []float64{1, 2, 3, 4, 5, 6},
		},
		{
			a: [][]float64{
				{1, 0, 0},
				{2, 3, 0},
				{4, 5, 6},
			},
			ul:  blas.Lower,
			ans: []float64{1, 2, 3, 4, 5, 6},
		},
	} {
		a := flattenTriangular(test.a, test.ul)
		if !floats.Equal(a, test.ans) {
			t.Errorf("Case %v. Want %v, got %v.", i, test.ans, a)
		}
	}
}
Esempio n. 2
0
func (b *BatchGradient) Func(params []float64) float64 {
	if floats.Equal(params, b.lastParams) {
		return b.lastFunc
	}
	b.lastFunc = b.funcGrad(params, b.lastGrad)
	return b.lastFunc
}
Esempio n. 3
0
func (ls *LinesearchMethod) initNextLinesearch(loc *Location, xNext []float64) (EvaluationType, IterationType, error) {
	copy(ls.x, loc.X)

	var stepSize float64
	if ls.first {
		stepSize = ls.NextDirectioner.InitDirection(loc, ls.dir)
		ls.first = false
	} else {
		stepSize = ls.NextDirectioner.NextDirection(loc, ls.dir)
	}

	projGrad := floats.Dot(loc.Gradient, ls.dir)
	if projGrad >= 0 {
		ls.evalType = NoEvaluation
		ls.iterType = NoIteration
		return ls.evalType, ls.iterType, ErrNonNegativeStepDirection
	}

	ls.evalType = ls.Linesearcher.Init(loc.F, projGrad, stepSize)

	floats.AddScaledTo(xNext, ls.x, stepSize, ls.dir)
	// Compare the starting point for the current iteration with the next
	// evaluation point to make sure that rounding errors do not prevent progress.
	if floats.Equal(ls.x, xNext) {
		ls.evalType = NoEvaluation
		ls.iterType = NoIteration
		return ls.evalType, ls.iterType, ErrNoProgress
	}

	ls.iterType = MinorIteration
	return ls.evalType, ls.iterType, nil
}
Esempio n. 4
0
// initNextLinesearch initializes the next linesearch using the previous
// complete location stored in loc. It fills loc.X and returns an evaluation
// to be performed at loc.X.
func (ls *LinesearchMethod) initNextLinesearch(loc *Location) (Operation, error) {
	copy(ls.x, loc.X)

	var step float64
	if ls.first {
		ls.first = false
		step = ls.NextDirectioner.InitDirection(loc, ls.dir)
	} else {
		step = ls.NextDirectioner.NextDirection(loc, ls.dir)
	}

	projGrad := floats.Dot(loc.Gradient, ls.dir)
	if projGrad >= 0 {
		return ls.error(ErrNonNegativeStepDirection)
	}

	op := ls.Linesearcher.Init(loc.F, projGrad, step)
	if !op.isEvaluation() {
		panic("linesearch: Linesearcher returned invalid operation")
	}

	floats.AddScaledTo(loc.X, ls.x, step, ls.dir)
	if floats.Equal(ls.x, loc.X) {
		// Step size is so small that the next evaluation point is
		// indistinguishable from the starting point for the current iteration
		// due to rounding errors.
		return ls.error(ErrNoProgress)
	}

	ls.lastStep = step
	ls.eval = NoOperation // Invalidate all fields of loc.

	ls.lastOp = op
	return ls.lastOp, nil
}
Esempio n. 5
0
// evaluate evaluates the problem given by p at xNext, stores the answer into
// loc and updates stats. If loc.X is not equal to xNext, then unused fields of
// loc are set to NaN.
// evaluate panics if the function does not support the requested evalType.
func evaluate(p *Problem, evalType EvaluationType, xNext []float64, loc *Location, stats *Stats) {
	if !floats.Equal(loc.X, xNext) {
		if evalType == NoEvaluation {
			// Optimizers should not request NoEvaluation at a new location.
			// The intent and therefore an appropriate action are both unclear.
			panic("optimize: no evaluation requested at new location")
		}
		invalidate(loc)
		copy(loc.X, xNext)
	}

	toEval := evalType
	if evalType&FuncEvaluation != 0 {
		loc.F = p.Func(loc.X)
		stats.FuncEvaluations++
		toEval &= ^FuncEvaluation
	}
	if evalType&GradEvaluation != 0 {
		p.Grad(loc.X, loc.Gradient)
		stats.GradEvaluations++
		toEval &= ^GradEvaluation
	}
	if evalType&HessEvaluation != 0 {
		p.Hess(loc.X, loc.Hessian)
		stats.HessEvaluations++
		toEval &= ^HessEvaluation
	}

	if toEval != NoEvaluation {
		panic(fmt.Sprintf("optimize: unknown evaluation type %v", evalType))
	}
}
Esempio n. 6
0
func DrsclTest(t *testing.T, impl Drscler) {
	for _, test := range []struct {
		x []float64
		a float64
	}{
		{
			x: []float64{1, 2, 3, 4, 5},
			a: 4,
		},
		{
			x: []float64{1, 2, 3, 4, 5},
			a: math.MaxFloat64,
		},
		{
			x: []float64{1, 2, 3, 4, 5},
			a: 1e-307,
		},
	} {
		xcopy := make([]float64, len(test.x))
		copy(xcopy, test.x)

		// Cannot test the scaling directly because of floating point scaling issues
		// (the purpose of Drscl). Instead, check that scaling and scaling back
		// yeilds approximately x. If overflow or underflow occurs then the scaling
		// won't match.
		impl.Drscl(len(test.x), test.a, xcopy, 1)
		if floats.Equal(xcopy, test.x) {
			t.Errorf("x unchanged during call to drscl. a = %v, x = %v.", test.a, test.x)
		}
		impl.Drscl(len(test.x), 1/test.a, xcopy, 1)
		if !floats.EqualApprox(xcopy, test.x, 1e-14) {
			t.Errorf("x not equal after scaling and unscaling. a = %v, x = %v.", test.a, test.x)
		}
	}
}
Esempio n. 7
0
func (b *BatchGradient) Grad(params, deriv []float64) {
	if floats.Equal(params, b.lastParams) {
		copy(deriv, b.lastGrad)
		return
	}
	b.lastFunc = b.funcGrad(params, b.lastGrad)
	copy(deriv, b.lastGrad)
}
Esempio n. 8
0
func TestQuantile(t *testing.T) {
	cumulantKinds := []CumulantKind{Empirical}
	for i, test := range []struct {
		p   []float64
		x   []float64
		w   []float64
		ans [][]float64
	}{
		{
			p:   []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
			x:   []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			w:   nil,
			ans: [][]float64{{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10}},
		},
		{
			p:   []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
			x:   []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
			w:   []float64{3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
			ans: [][]float64{{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10}},
		},
	} {
		copyX := make([]float64, len(test.x))
		copy(copyX, test.x)
		var copyW []float64
		if test.w != nil {
			copyW = make([]float64, len(test.w))
			copy(copyW, test.w)
		}
		for j, p := range test.p {
			for k, kind := range cumulantKinds {
				v := Quantile(p, kind, test.x, test.w)
				if !floats.Equal(copyX, test.x) {
					t.Errorf("x changed for case %d kind %d percentile %v", i, k, p)
				}
				if !floats.Equal(copyW, test.w) {
					t.Errorf("x changed for case %d kind %d percentile %v", i, k, p)
				}
				if v != test.ans[k][j] {
					t.Errorf("mismatch case %d kind %d percentile %v. Expected: %v, found: %v", i, k, p, test.ans[k][j], v)
				}
			}
		}
	}
}
Esempio n. 9
0
func TestCDF(t *testing.T) {
	cumulantKinds := []CumulantKind{Empirical}
	for i, test := range []struct {
		q       []float64
		x       []float64
		weights []float64
		ans     [][]float64
	}{
		{},
		{
			q:   []float64{0, 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1},
			x:   []float64{1, 2, 3, 4, 5},
			ans: [][]float64{{0, 0, 0.2, 0.2, 0.4, 0.6, 0.6, 0.8, 1, 1}},
		},
		{
			q:       []float64{0, 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1},
			x:       []float64{1, 2, 3, 4, 5},
			weights: []float64{1, 1, 1, 1, 1},
			ans:     [][]float64{{0, 0, 0.2, 0.2, 0.4, 0.6, 0.6, 0.8, 1, 1}},
		},
	} {
		copyX := make([]float64, len(test.x))
		copy(copyX, test.x)
		var copyW []float64
		if test.weights != nil {
			copyW = make([]float64, len(test.weights))
			copy(copyW, test.weights)
		}
		for j, q := range test.q {
			for k, kind := range cumulantKinds {
				v := CDF(q, kind, test.x, test.weights)
				if !floats.Equal(copyX, test.x) {
					t.Errorf("x changed for case %d kind %d percentile %v", i, k, q)
				}
				if !floats.Equal(copyW, test.weights) {
					t.Errorf("x changed for case %d kind %d percentile %v", i, k, q)
				}
				if v != test.ans[k][j] {
					t.Errorf("mismatch case %d kind %d percentile %v. Expected: %v, found: %v", i, k, q, test.ans[k][j], v)
				}
			}
		}
	}
}
Esempio n. 10
0
func TestFlatten2D(t *testing.T) {

	s2d := [][]float64{{11, 22}, {33, 44}, {55, 66}}
	expected := []float64{11, 22, 33, 44, 55, 66}

	flatten := Flatten2D(s2d)
	if !floats.Equal(flatten, expected) {
		t.Fatalf("Flatten failed. expected %+v, got %+v", expected, flatten)
	}
}
Esempio n. 11
0
func (ls *LinesearchMethod) Iterate(loc *Location, xNext []float64) (EvaluationType, IterationType, error) {
	if ls.iterType == SubIteration {
		// We needed to evaluate invalid fields of Location. Now we have them
		// and can announce MajorIteration.
		copy(xNext, loc.X)
		ls.evalType = NoEvaluation
		ls.iterType = MajorIteration
		return ls.evalType, ls.iterType, nil
	}

	if ls.iterType == MajorIteration {
		// The linesearch previously signaled MajorIteration. Since we're here,
		// it means that the previous location is not good enough to converge,
		// so start the next linesearch.
		return ls.initNextLinesearch(loc, xNext)
	}

	projGrad := floats.Dot(loc.Gradient, ls.dir)
	if ls.Linesearcher.Finished(loc.F, projGrad) {
		copy(xNext, loc.X)
		// Check if the last evaluation evaluated all fields of Location.
		ls.evalType = complementEval(loc, ls.evalType)
		if ls.evalType == NoEvaluation {
			// Location is complete and MajorIteration can be announced directly.
			ls.iterType = MajorIteration
		} else {
			// Location is not complete, evaluate its invalid fields in SubIteration.
			ls.iterType = SubIteration
		}
		return ls.evalType, ls.iterType, nil
	}

	// Line search not done, just iterate.
	stepSize, evalType, err := ls.Linesearcher.Iterate(loc.F, projGrad)
	if err != nil {
		ls.evalType = NoEvaluation
		ls.iterType = NoIteration
		return ls.evalType, ls.iterType, err
	}

	floats.AddScaledTo(xNext, ls.x, stepSize, ls.dir)
	// Compare the starting point for the current iteration with the next
	// evaluation point to make sure that rounding errors do not prevent progress.
	if floats.Equal(ls.x, xNext) {
		ls.evalType = NoEvaluation
		ls.iterType = NoIteration
		return ls.evalType, ls.iterType, ErrNoProgress
	}

	ls.evalType = evalType
	ls.iterType = MinorIteration
	return ls.evalType, ls.iterType, nil
}
Esempio n. 12
0
func TestLegendreSingle(t *testing.T) {
	for c, test := range []struct {
		n        int
		min, max float64
	}{
		{
			n:   100,
			min: -1,
			max: 1,
		},
		{
			n:   50,
			min: -3,
			max: -1,
		},
		{
			n:   1000,
			min: 2,
			max: 7,
		},
	} {
		l := Legendre{}
		n := test.n
		xs := make([]float64, n)
		weights := make([]float64, n)
		l.FixedLocations(xs, weights, test.min, test.max)

		xsSingle := make([]float64, n)
		weightsSingle := make([]float64, n)
		for i := range xsSingle {
			xsSingle[i], weightsSingle[i] = l.FixedLocationSingle(n, i, test.min, test.max)
		}
		if !floats.Equal(xs, xsSingle) {
			t.Errorf("Case %d: xs mismatch batch and single", c)
		}
		if !floats.Equal(weights, weightsSingle) {
			t.Errorf("Case %d: weights mismatch batch and single", c)
		}
	}
}
Esempio n. 13
0
func denseEqual(a *Dense, acomp matComp) bool {
	ar2, ac2 := a.Dims()
	if ar2 != acomp.r {
		return false
	}
	if ac2 != acomp.c {
		return false
	}
	if !floats.Equal(a.mat.Data, acomp.data) {
		return false
	}
	return true
}
Esempio n. 14
0
func TestCopy2D(t *testing.T) {

	s1 := [][]float64{{11, 22}, {33, 44}, {55, 66}}

	s2 := CopyFloat2D(s1)

	for k, _ := range s1 {
		if &s1[k] == &s2[k] {
			t.Fatalf("Slices have the same address, not a copy.")
		}
		if !floats.Equal(s1[k], s2[k]) {
			t.Fatalf("Copy failed. want: %+v, have: %+v", s1, s2)
		}
	}
}
Esempio n. 15
0
func TestPredictFeaturized(t *testing.T) {
	for _, test := range []struct {
		z              []float64
		featureWeights [][]float64
		output         []float64
		Name           string
	}{
		{
			Name: "General",
			z:    []float64{1, 2, 3},
			featureWeights: [][]float64{
				{3, 4},
				{1, 2},
				{0.5, 0.4},
			},
			output: []float64{6.5, 9.2},
		},
	} {
		zCopy := make([]float64, len(test.z))
		copy(zCopy, test.z)
		fwMat := flatten(test.featureWeights)
		fwMatCopy := &mat64.Dense{}
		fwMatCopy.Clone(fwMat)

		output := make([]float64, len(test.output))

		predictFeaturized(zCopy, fwMat, output)

		// Test that z wasn't changed
		if !floats.Equal(test.z, zCopy) {
			t.Errorf("z changed during call")
		}

		if !floats.EqualApprox(output, test.output, 1e-14) {
			t.Errorf("output doesn't match for test %v. Expected %v, found %v", test.Name, test.output, output)
		}
	}
}
Esempio n. 16
0
func TestHistogram(t *testing.T) {
	for i, test := range []struct {
		x        []float64
		weights  []float64
		dividers []float64
		ans      []float64
	}{
		{
			x:        []float64{1, 3, 5, 6, 7, 8},
			dividers: []float64{2, 4, 6, 7},
			ans:      []float64{1, 1, 1, 1, 2},
		},
		{
			x:        []float64{1, 3, 5, 6, 7, 8},
			dividers: []float64{2, 4, 6, 7},
			weights:  []float64{1, 2, 1, 1, 1, 2},
			ans:      []float64{1, 2, 1, 1, 3},
		},
		{
			x:        []float64{1, 8},
			dividers: []float64{2, 4, 6, 7},
			weights:  []float64{1, 2},
			ans:      []float64{1, 0, 0, 0, 2},
		},
		{
			x:        []float64{1, 8},
			dividers: []float64{2, 4, 6, 7},
			ans:      []float64{1, 0, 0, 0, 1},
		},
	} {
		hist := Histogram(nil, test.dividers, test.x, test.weights)
		if !floats.Equal(hist, test.ans) {
			t.Errorf("Hist mismatch case %d. Expected %v, Found %v", i, test.ans, hist)
		}
	}
}
Esempio n. 17
0
func Dorml2Test(t *testing.T, impl Dorml2er) {
	rnd := rand.New(rand.NewSource(1))
	// TODO(btracey): This test is not complete, because it
	// doesn't test individual values of m, n, and k, instead only testing
	// a specific subset of possible k values.
	for _, side := range []blas.Side{blas.Left, blas.Right} {
		for _, trans := range []blas.Transpose{blas.NoTrans, blas.Trans} {
			for _, test := range []struct {
				common, adim, cdim, lda, ldc int
			}{
				{3, 4, 5, 0, 0},
				{3, 5, 4, 0, 0},
				{4, 3, 5, 0, 0},
				{4, 5, 3, 0, 0},
				{5, 3, 4, 0, 0},
				{5, 4, 3, 0, 0},

				{3, 4, 5, 6, 20},
				{3, 5, 4, 6, 20},
				{4, 3, 5, 6, 20},
				{4, 5, 3, 6, 20},
				{5, 3, 4, 6, 20},
				{5, 4, 3, 6, 20},
				{3, 4, 5, 20, 6},
				{3, 5, 4, 20, 6},
				{4, 3, 5, 20, 6},
				{4, 5, 3, 20, 6},
				{5, 3, 4, 20, 6},
				{5, 4, 3, 20, 6},
			} {
				var ma, na, mc, nc int
				if side == blas.Left {
					ma = test.adim
					na = test.common
					mc = test.common
					nc = test.cdim
				} else {
					ma = test.adim
					na = test.common
					mc = test.cdim
					nc = test.common
				}
				// Generate a random matrix
				lda := test.lda
				if lda == 0 {
					lda = na
				}
				a := make([]float64, ma*lda)
				for i := range a {
					a[i] = rnd.Float64()
				}
				ldc := test.ldc
				if ldc == 0 {
					ldc = nc
				}
				// Compute random C matrix
				c := make([]float64, mc*ldc)
				for i := range c {
					c[i] = rnd.Float64()
				}

				// Compute LQ
				k := min(ma, na)
				tau := make([]float64, k)
				work := make([]float64, 1)
				impl.Dgelqf(ma, na, a, lda, tau, work, -1)
				work = make([]float64, int(work[0]))
				impl.Dgelqf(ma, na, a, lda, tau, work, len(work))

				// Build Q from result
				q := constructQ("LQ", ma, na, a, lda, tau)

				cMat := blas64.General{
					Rows:   mc,
					Cols:   nc,
					Stride: ldc,
					Data:   make([]float64, len(c)),
				}
				copy(cMat.Data, c)
				cMatCopy := blas64.General{
					Rows:   cMat.Rows,
					Cols:   cMat.Cols,
					Stride: cMat.Stride,
					Data:   make([]float64, len(cMat.Data)),
				}
				copy(cMatCopy.Data, cMat.Data)
				switch {
				default:
					panic("bad test")
				case side == blas.Left && trans == blas.NoTrans:
					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, q, cMatCopy, 0, cMat)
				case side == blas.Left && trans == blas.Trans:
					blas64.Gemm(blas.Trans, blas.NoTrans, 1, q, cMatCopy, 0, cMat)
				case side == blas.Right && trans == blas.NoTrans:
					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, cMatCopy, q, 0, cMat)
				case side == blas.Right && trans == blas.Trans:
					blas64.Gemm(blas.NoTrans, blas.Trans, 1, cMatCopy, q, 0, cMat)
				}
				// Do Dorm2r ard compare
				if side == blas.Left {
					work = make([]float64, nc)
				} else {
					work = make([]float64, mc)
				}
				aCopy := make([]float64, len(a))
				copy(aCopy, a)
				tauCopy := make([]float64, len(tau))
				copy(tauCopy, tau)
				impl.Dorml2(side, trans, mc, nc, k, a, lda, tau, c, ldc, work)
				if !floats.Equal(a, aCopy) {
					t.Errorf("a changed in call")
				}
				if !floats.Equal(tau, tauCopy) {
					t.Errorf("tau changed in call")
				}
				if !floats.EqualApprox(cMat.Data, c, 1e-14) {
					isLeft := side == blas.Left
					isTrans := trans == blas.Trans
					t.Errorf("Multiplication mismatch. IsLeft = %v. IsTrans = %v", isLeft, isTrans)
				}
			}
		}
	}
}
Esempio n. 18
0
func TestGradient(t *testing.T) {
	for i, test := range []struct {
		nDim   int
		tol    float64
		method Method
	}{
		{
			nDim:   2,
			tol:    2e-4,
			method: Forward,
		},
		{
			nDim:   2,
			tol:    1e-6,
			method: Central,
		},
		{
			nDim:   40,
			tol:    2e-4,
			method: Forward,
		},
		{
			nDim:   40,
			tol:    1e-6,
			method: Central,
		},
	} {
		x := make([]float64, test.nDim)
		for i := range x {
			x[i] = rand.Float64()
		}
		xcopy := make([]float64, len(x))
		copy(xcopy, x)

		r := Rosenbrock{len(x)}
		trueGradient := make([]float64, len(x))
		r.FDf(x, trueGradient)

		settings := DefaultSettings()
		settings.Method = test.method

		// try with gradient nil
		gradient := Gradient(nil, r.F, x, settings)
		if !floats.EqualApprox(gradient, trueGradient, test.tol) {
			t.Errorf("Case %v: gradient mismatch in serial with nil. Want: %v, Got: %v.", i, trueGradient, gradient)
		}
		if !floats.Equal(x, xcopy) {
			t.Errorf("Case %v: x modified during call to gradient in serial with nil.", i)
		}

		for i := range gradient {
			gradient[i] = rand.Float64()
		}

		Gradient(gradient, r.F, x, settings)
		if !floats.EqualApprox(gradient, trueGradient, test.tol) {
			t.Errorf("Case %v: gradient mismatch in serial. Want: %v, Got: %v.", i, trueGradient, gradient)
		}
		if !floats.Equal(x, xcopy) {
			t.Errorf("Case %v: x modified during call to gradient in serial with non-nil.", i)
		}

		// Try with known value
		for i := range gradient {
			gradient[i] = rand.Float64()
		}
		settings.OriginKnown = true
		settings.OriginValue = r.F(x)
		Gradient(gradient, r.F, x, settings)
		if !floats.EqualApprox(gradient, trueGradient, test.tol) {
			t.Errorf("Case %v: gradient mismatch with known origin in serial. Want: %v, Got: %v.", i, trueGradient, gradient)
		}

		// Concurrently
		for i := range gradient {
			gradient[i] = rand.Float64()
		}
		settings.Concurrent = true
		settings.OriginKnown = false
		settings.Workers = 1000
		Gradient(gradient, r.F, x, settings)
		if !floats.EqualApprox(gradient, trueGradient, test.tol) {
			t.Errorf("Case %v: gradient mismatch with unknown origin in parallel. Want: %v, Got: %v.", i, trueGradient, gradient)
		}
		if !floats.Equal(x, xcopy) {
			t.Errorf("Case %v: x modified during call to gradient in parallel", i)
		}

		// Concurrently with origin known
		for i := range gradient {
			gradient[i] = rand.Float64()
		}
		settings.OriginKnown = true
		Gradient(gradient, r.F, x, settings)
		if !floats.EqualApprox(gradient, trueGradient, test.tol) {
			t.Errorf("Case %v: gradient mismatch with known origin in parallel. Want: %v, Got: %v.", i, trueGradient, gradient)
		}

		// With default settings
		for i := range gradient {
			gradient[i] = rand.Float64()
		}
		settings = nil
		Gradient(gradient, r.F, x, settings)
		if !floats.EqualApprox(gradient, trueGradient, test.tol) {
			t.Errorf("Case %v: gradient mismatch with default settings. Want: %v, Got: %v.", i, trueGradient, gradient)
		}

	}
}
Esempio n. 19
0
func (ls *LinesearchMethod) Iterate(loc *Location) (Operation, error) {
	switch ls.lastOp {
	case NoOperation:
		// TODO(vladimir-ch): Either Init has not been called, or the caller is
		// trying to resume the optimization run after Iterate previously
		// returned with an error. Decide what is the proper thing to do. See also #125.

	case MajorIteration:
		// The previous updated location did not converge the full
		// optimization. Initialize a new Linesearch.
		return ls.initNextLinesearch(loc)

	default:
		// Update the indicator of valid fields of loc.
		ls.eval |= ls.lastOp

		if ls.nextMajor {
			ls.nextMajor = false

			// Linesearcher previously finished, and the invalid fields of loc
			// have now been validated. Announce MajorIteration.
			ls.lastOp = MajorIteration
			return ls.lastOp, nil
		}
	}

	// Continue the linesearch.

	f := math.NaN()
	if ls.eval&FuncEvaluation != 0 {
		f = loc.F
	}
	projGrad := math.NaN()
	if ls.eval&GradEvaluation != 0 {
		projGrad = floats.Dot(loc.Gradient, ls.dir)
	}
	op, step, err := ls.Linesearcher.Iterate(f, projGrad)
	if err != nil {
		return ls.error(err)
	}

	switch op {
	case MajorIteration:
		// Linesearch has been finished.

		ls.lastOp = complementEval(loc, ls.eval)
		if ls.lastOp == NoOperation {
			// loc is complete, MajorIteration can be declared directly.
			ls.lastOp = MajorIteration
		} else {
			// Declare MajorIteration on the next call to Iterate.
			ls.nextMajor = true
		}

	case FuncEvaluation, GradEvaluation, FuncEvaluation | GradEvaluation:
		if step != ls.lastStep {
			// We are moving to a new location, and not, say, evaluating extra
			// information at the current location.

			// Compute the next evaluation point and store it in loc.X.
			floats.AddScaledTo(loc.X, ls.x, step, ls.dir)
			if floats.Equal(ls.x, loc.X) {
				// Step size has become so small that the next evaluation point is
				// indistinguishable from the starting point for the current
				// iteration due to rounding errors.
				return ls.error(ErrNoProgress)
			}
			ls.lastStep = step
			ls.eval = NoOperation // Indicate all invalid fields of loc.
		}
		ls.lastOp = op

	default:
		panic("linesearch: Linesearcher returned invalid operation")
	}

	return ls.lastOp, nil
}
Esempio n. 20
0
func (g *GP) marginalLikelihoodDerivative(x, grad []float64, trainNoise bool, mem *margLikeMemory) {
	// d/dTheta_j log[(p|X,theta)] =
	//		1/2 * y^T * K^-1 dK/dTheta_j * K^-1 * y - 1/2 * tr(K^-1 * dK/dTheta_j)
	//		1/2 * α^T * dK/dTheta_j * α - 1/2 * tr(K^-1 dK/dTheta_j)
	// Multiply by the same -2
	//		-α^T * K^-1 * α + tr(K^-1 dK/dTheta_j)
	// This first computation is an inner product.
	n := len(g.outputs)
	nHyper := g.kernel.NumHyper()
	k := mem.k
	chol := mem.chol
	alpha := mem.alpha
	dKdTheta := mem.dKdTheta
	kInvDK := mem.kInvDK

	y := mat64.NewVector(n, g.outputs)

	var noise float64
	if trainNoise {
		noise = math.Exp(x[len(x)-1])
	} else {
		noise = g.noise
	}

	// If x is the same, then reuse what has been computed in the function.
	if !floats.Equal(mem.lastX, x) {
		copy(mem.lastX, x)
		g.kernel.SetHyper(x[:nHyper])
		g.setKernelMat(k, noise)
		//chol.Cholesky(k, false)
		chol.Factorize(k)
		alpha.SolveCholeskyVec(chol, y)
	}
	g.setKernelMatDeriv(dKdTheta, trainNoise, noise)
	for i := range dKdTheta {
		kInvDK.SolveCholesky(chol, dKdTheta[i])
		inner := mat64.Inner(alpha, dKdTheta[i], alpha)
		grad[i] = -inner + mat64.Trace(kInvDK)
	}
	floats.Scale(1/float64(n), grad)

	bounds := g.kernel.Bounds()
	if trainNoise {
		bounds = append(bounds, Bound{minLogNoise, maxLogNoise})
	}
	barrierGrad := make([]float64, len(grad))
	for i, v := range x {
		// Quadratic barrier penalty.
		if v < bounds[i].Min {
			diff := bounds[i].Min - v
			barrierGrad[i] = -(barrierPow) * math.Pow(diff, barrierPow-1)
		}
		if v > bounds[i].Max {
			diff := v - bounds[i].Max
			barrierGrad[i] = (barrierPow) * math.Pow(diff, barrierPow-1)
		}
	}
	fmt.Println("noise, minNoise", x[len(x)-1], bounds[len(x)-1].Min)
	fmt.Println("barrier Grad", barrierGrad)
	floats.Add(grad, barrierGrad)
	//copy(grad, barrierGrad)
}
Esempio n. 21
0
func TestCovarianceMatrix(t *testing.T) {
	// An alternate way to test this is to call the Variance
	// and Covariance functions and ensure that the results are identical.
	for i, test := range []struct {
		data    *mat64.Dense
		weights []float64
		ans     *mat64.Dense
	}{
		{
			data: mat64.NewDense(5, 2, []float64{
				-2, -4,
				-1, 2,
				0, 0,
				1, -2,
				2, 4,
			}),
			weights: nil,
			ans: mat64.NewDense(2, 2, []float64{
				2.5, 3,
				3, 10,
			}),
		}, {
			data: mat64.NewDense(3, 2, []float64{
				1, 1,
				2, 4,
				3, 9,
			}),
			weights: []float64{
				1,
				1.5,
				1,
			},
			ans: mat64.NewDense(2, 2, []float64{
				.8, 3.2,
				3.2, 13.142857142857146,
			}),
		},
	} {
		// Make a copy of the data to check that it isn't changing.
		r := test.data.RawMatrix()
		d := make([]float64, len(r.Data))
		copy(d, r.Data)

		w := make([]float64, len(test.weights))
		if test.weights != nil {
			copy(w, test.weights)
		}
		c := CovarianceMatrix(nil, test.data, test.weights)
		if !c.Equals(test.ans) {
			t.Errorf("%d: expected cov %v, found %v", i, test.ans, c)
		}
		if !floats.Equal(d, r.Data) {
			t.Errorf("%d: data was modified during execution", i)
		}
		if !floats.Equal(w, test.weights) {
			t.Errorf("%d: weights was modified during execution", i)
		}

		// compare with call to Covariance
		_, cols := c.Dims()
		for ci := 0; ci < cols; ci++ {
			for cj := 0; cj < cols; cj++ {
				x := test.data.Col(nil, ci)
				y := test.data.Col(nil, cj)
				cov := Covariance(x, y, test.weights)
				if math.Abs(cov-c.At(ci, cj)) > 1e-14 {
					t.Errorf("CovMat does not match at (%v, %v). Want %v, got %v.", ci, cj, cov, c.At(ci, cj))
				}
			}
		}

	}
	if !Panics(func() { CovarianceMatrix(nil, mat64.NewDense(5, 2, nil), []float64{}) }) {
		t.Errorf("CovarianceMatrix did not panic with weight size mismatch")
	}
	if !Panics(func() { CovarianceMatrix(mat64.NewDense(1, 1, nil), mat64.NewDense(5, 2, nil), nil) }) {
		t.Errorf("CovarianceMatrix did not panic with preallocation size mismatch")
	}
	if !Panics(func() { CovarianceMatrix(nil, mat64.NewDense(2, 2, []float64{1, 2, 3, 4}), []float64{1, -1}) }) {
		t.Errorf("CovarianceMatrix did not panic with negative weights")
	}
}
Esempio n. 22
0
func TestMetropolisHastingser(t *testing.T) {
	for seed, test := range []struct {
		dim, burnin, rate, samples int
	}{
		{3, 10, 1, 1},
		{3, 10, 2, 1},
		{3, 10, 1, 2},
		{3, 10, 3, 2},
		{3, 10, 7, 4},
		{3, 10, 7, 4},

		{3, 11, 51, 103},
		{3, 11, 103, 51},
		{3, 51, 11, 103},
		{3, 51, 103, 11},
		{3, 103, 11, 51},
		{3, 103, 51, 11},
	} {
		dim := test.dim

		initial := make([]float64, dim)
		target, ok := randomNormal(dim)
		if !ok {
			t.Fatal("bad test, sigma not pos def")
		}

		sigmaImp := mat64.NewSymDense(dim, nil)
		for i := 0; i < dim; i++ {
			sigmaImp.SetSym(i, i, 0.25)
		}
		proposal, ok := NewProposalNormal(sigmaImp, nil)
		if !ok {
			t.Fatal("bad test, sigma not pos def")
		}

		// Test the Metropolis Hastingser by generating all the samples, then generating
		// the same samples with a burnin and rate.
		rand.Seed(int64(seed))
		mh := MetropolisHastingser{
			Initial:  initial,
			Target:   target,
			Proposal: proposal,
			Src:      nil,
			BurnIn:   0,
			Rate:     0,
		}
		samples := test.samples
		burnin := test.burnin
		rate := test.rate
		fullBatch := mat64.NewDense(1+burnin+rate*(samples-1), dim, nil)
		mh.Sample(fullBatch)
		mh = MetropolisHastingser{
			Initial:  initial,
			Target:   target,
			Proposal: proposal,
			Src:      nil,
			BurnIn:   burnin,
			Rate:     rate,
		}
		rand.Seed(int64(seed))
		batch := mat64.NewDense(samples, dim, nil)
		mh.Sample(batch)

		same := true
		count := burnin
		for i := 0; i < samples; i++ {
			if !floats.Equal(batch.RawRowView(i), fullBatch.RawRowView(count)) {
				fmt.Println("sample ", i, "is different")
				same = false
				break
			}
			count += rate
		}

		if !same {
			fmt.Printf("%v\n", mat64.Formatted(batch))
			fmt.Printf("%v\n", mat64.Formatted(fullBatch))

			t.Errorf("sampling mismatch: dim = %v, burnin = %v, rate = %v, samples = %v", dim, burnin, rate, samples)
		}
	}
}
Esempio n. 23
0
// testTwoInput tests a method that has two input arguments.
func testTwoInput(c *check.C,
	// name is the name of the method being tested.
	name string,

	// receiver is a value of the receiver type.
	receiver Matrix,

	// method is the generalized receiver.Method(a, b).
	method func(receiver, a, b Matrix),

	// denseComparison performs the same operation as method, but with dense
	// matrices for comparison with the result.
	denseComparison func(receiver, a, b *Dense),

	// legalTypes returns whether the concrete types in Matrix are valid for
	// the method.
	legalTypes func(a, b Matrix) bool,

	// dimsOK returns whether the matrix sizes are valid for the method.
	legalSize func(ar, ac, br, bc int) bool,

	// tol is the tolerance for equality when comparing method results.
	tol float64,
) {
	for _, aMat := range testMatrices {
		for _, bMat := range testMatrices {
			// Loop over all of the size combinations (bigger, smaller, etc.).
			for _, test := range []struct {
				ar, ac, br, bc int
			}{
				{1, 1, 1, 1},
				{6, 6, 6, 6},
				{7, 7, 7, 7},

				{1, 1, 1, 5},
				{1, 1, 5, 1},
				{1, 5, 1, 1},
				{5, 1, 1, 1},

				{6, 6, 6, 11},
				{6, 6, 11, 6},
				{6, 11, 6, 6},
				{11, 6, 6, 6},
				{11, 11, 11, 6},
				{11, 11, 6, 11},
				{11, 6, 11, 11},
				{6, 11, 11, 11},

				{1, 1, 5, 5},
				{1, 5, 1, 5},
				{1, 5, 5, 1},
				{5, 1, 1, 5},
				{5, 1, 5, 1},
				{5, 5, 1, 1},
				{6, 6, 11, 11},
				{6, 11, 6, 11},
				{6, 11, 11, 6},
				{11, 6, 6, 11},
				{11, 6, 11, 6},
				{11, 11, 6, 6},

				{1, 1, 17, 11},
				{1, 1, 11, 17},
				{1, 11, 1, 17},
				{1, 17, 1, 11},
				{1, 11, 17, 1},
				{1, 17, 11, 1},
				{11, 1, 1, 17},
				{17, 1, 1, 11},
				{11, 1, 17, 1},
				{17, 1, 11, 1},
				{11, 17, 1, 1},
				{17, 11, 1, 1},

				{6, 6, 1, 11},
				{6, 6, 11, 1},
				{6, 11, 6, 1},
				{6, 1, 6, 11},
				{6, 11, 1, 6},
				{6, 1, 11, 6},
				{11, 6, 6, 1},
				{1, 6, 6, 11},
				{11, 6, 1, 6},
				{1, 6, 11, 6},
				{11, 1, 6, 6},
				{1, 11, 6, 6},

				{6, 6, 17, 1},
				{6, 6, 1, 17},
				{6, 1, 6, 17},
				{6, 17, 6, 1},
				{6, 1, 17, 6},
				{6, 17, 1, 6},
				{1, 6, 6, 17},
				{17, 6, 6, 1},
				{1, 6, 17, 6},
				{17, 6, 1, 6},
				{1, 17, 6, 6},
				{17, 1, 6, 6},

				{6, 6, 17, 11},
				{6, 6, 11, 17},
				{6, 11, 6, 17},
				{6, 17, 6, 11},
				{6, 11, 17, 6},
				{6, 17, 11, 6},
				{11, 6, 6, 17},
				{17, 6, 6, 11},
				{11, 6, 17, 6},
				{17, 6, 11, 6},
				{11, 17, 6, 6},
				{17, 11, 6, 6},
			} {
				// Skip the test if any argument would not be assignable to the
				// method's corresponding input parameter or it is not possible
				// to construct an argument of the requested size.
				if !legalTypes(aMat, bMat) {
					continue
				}
				if !legalDims(aMat, test.ar, test.ac) {
					continue
				}
				if !legalDims(bMat, test.br, test.bc) {
					continue
				}
				a := makeRandOf(aMat, test.ar, test.ac)
				b := makeRandOf(bMat, test.br, test.bc)

				// Compute the true answer if the sizes are legal.
				dimsOK := legalSize(test.ar, test.ac, test.br, test.bc)
				var want Dense
				if dimsOK {
					var aDense, bDense Dense
					aDense.Clone(a)
					bDense.Clone(b)
					denseComparison(&want, &aDense, &bDense)
				}
				aCopy := makeCopyOf(a)
				bCopy := makeCopyOf(b)

				// Test the method for a zero-value of the receiver.
				aType, aTrans := untranspose(a)
				bType, bTrans := untranspose(b)
				errStr := fmt.Sprintf("%T.%s(%T, %T), sizes: %#v, atrans %v, btrans %v", receiver, name, aType, bType, test, aTrans, bTrans)
				zero := makeRandOf(receiver, 0, 0)
				panicked, err := panics(func() { method(zero, a, b) })
				if !dimsOK && !panicked {
					c.Errorf("Did not panic with illegal size: %s", errStr)
					continue
				}
				if dimsOK && panicked {
					c.Errorf("Panicked with legal size: %s %s", errStr, err)
					continue
				}
				if !equal(a, aCopy) {
					c.Errorf("First input argument changed in call: %s", errStr)
				}
				if !equal(b, bCopy) {
					c.Errorf("Second input argument changed in call: %s", errStr)
				}
				if !dimsOK {
					continue
				}
				if !equalApprox(zero, &want, tol) {
					c.Errorf("Answer mismatch with zero receiver: %s", errStr)
					continue
				}

				// Test the method with a non-zero-value of the receiver.
				// The receiver has been overwritten in place so use its size
				// to construct a new random matrix.
				rr, rc := zero.Dims()
				nonZero := makeRandOf(receiver, rr, rc)
				panicked, _ = panics(func() { method(nonZero, a, b) })
				if panicked {
					c.Errorf("Panicked with non-zero receiver: %s", errStr)
				}
				if !equalApprox(nonZero, &want, tol) {
					c.Errorf("Answer mismatch non-zero receiver: %s", errStr)
				}

				// Test with an incorrectly sized matrix.
				switch receiver.(type) {
				default:
					panic("matrix type not coded for incorrect receiver size")
				case *Dense:
					wrongSize := makeRandOf(receiver, rr+1, rc)
					panicked, _ = panics(func() { method(wrongSize, a, b) })
					if !panicked {
						c.Errorf("Did not panic with wrong number of rows: %s", errStr)
					}
					wrongSize = makeRandOf(receiver, rr, rc+1)
					panicked, _ = panics(func() { method(wrongSize, a, b) })
					if !panicked {
						c.Errorf("Did not panic with wrong number of columns: %s", errStr)
					}
				case *TriDense, *SymDense:
					// Add to the square size.
					wrongSize := makeRandOf(receiver, rr+1, rc+1)
					panicked, _ = panics(func() { method(wrongSize, a, b) })
					if !panicked {
						c.Errorf("Did not panic with wrong size: %s", errStr)
					}
				case *Vector:
					// Add to the column length.
					wrongSize := makeRandOf(receiver, rr+1, rc)
					panicked, _ = panics(func() { method(wrongSize, a, b) })
					if !panicked {
						c.Errorf("Did not panic with wrong number of rows: %s", errStr)
					}
				}

				// The receiver and an input may share a matrix pointer
				// if the type and size of the receiver and one of the
				// arguments match. Test the method works properly
				// when this is the case.
				aMaybeSame := maybeSame(nonZero, a)
				bMaybeSame := maybeSame(nonZero, b)
				if aMaybeSame {
					aSame := makeCopyOf(a)
					receiver = aSame
					u, ok := aSame.(Untransposer)
					if ok {
						receiver = u.Untranspose()
					}
					preData := underlyingData(receiver)
					panicked, err = panics(func() { method(receiver, aSame, b) })
					if panicked {
						c.Errorf("Panics when a maybeSame: %s: %s", errStr, err)
					} else {
						if !equalApprox(receiver, &want, tol) {
							c.Errorf("Wrong answer when a maybeSame: %s", errStr)
						}
						postData := underlyingData(receiver)
						if !floats.Equal(preData, postData) {
							c.Errorf("Original data slice not modified when a maybeSame: %s", errStr)
						}
					}
				}
				if bMaybeSame {
					bSame := makeCopyOf(b)
					receiver = bSame
					u, ok := bSame.(Untransposer)
					if ok {
						receiver = u.Untranspose()
					}
					preData := underlyingData(receiver)
					panicked, err = panics(func() { method(receiver, a, bSame) })
					if panicked {
						c.Errorf("Panics when b maybeSame: %s", errStr)
					} else {
						if !equalApprox(receiver, &want, tol) {
							c.Errorf("Wrong answer when b maybeSame: %s: %s", errStr, err)
						}
						postData := underlyingData(receiver)
						if !floats.Equal(preData, postData) {
							c.Errorf("Original data slice not modified when b maybeSame: %s", errStr)
						}
					}
				}
				if aMaybeSame && bMaybeSame {
					aSame := makeCopyOf(a)
					receiver = aSame
					u, ok := aSame.(Untransposer)
					if ok {
						receiver = u.Untranspose()
					}
					// Ensure that b is the correct transpose type if applicable.
					// The receiver is always a concrete type so use it.
					bSame := receiver
					u, ok = bSame.(Untransposer)
					if ok {
						bSame = retranspose(bSame, receiver)
					}
					// Compute the real answer for this case. It is different
					// from the inital answer since now a and b have the
					// same data.
					zero = makeRandOf(zero, 0, 0)
					method(zero, aSame, bSame)
					preData := underlyingData(receiver)
					panicked, err = panics(func() { method(receiver, aSame, bSame) })
					if panicked {
						c.Errorf("Panics when both maybeSame: %s: %s", errStr, err)
					} else {
						if !equalApprox(receiver, zero, tol) {
							c.Errorf("Wrong answer when both maybeSame: %s", errStr)
						}
						postData := underlyingData(receiver)
						if !floats.Equal(preData, postData) {
							c.Errorf("Original data slice not modified when both maybeSame: %s", errStr)
						}
					}
				}
			}
		}
	}
}
Esempio n. 24
0
func TestPrivatePredict(t *testing.T) {
	for _, test := range []struct {
		input          []float64
		features       [][]float64
		b              []float64
		featureWeights [][]float64
		Name           string
	}{
		{
			input: []float64{8, 9, 10},
			featureWeights: [][]float64{
				{8, 9},
				{0.4, 0.2},
				{9.8, 1.6},
				{-4, -8},
			},
			features: [][]float64{
				{0.9, 0.8, 0.7},
				{-0.7, 0.2, 15},
				{1.5, 7.8, -2.4},
				{9.7, 9.2, 1.2},
			},
			b:    []float64{0.7, 1.2, 0.2, 0.01234},
			Name: "General",
		},
	} {
		inputCopy := make([]float64, len(test.input))
		copy(inputCopy, test.input)

		fwMat := flatten(test.featureWeights)
		fwMatCopy := &mat64.Dense{}
		fwMatCopy.Clone(fwMat)

		featureMat := flatten(test.features)
		featureMatCopy := &mat64.Dense{}
		featureMatCopy.Clone(featureMat)

		bCopy := make([]float64, len(test.b))
		copy(bCopy, test.b)

		// This test assumes ComputeZ and PredictWithZ work
		nOutput := len(test.featureWeights[0])
		nFeatures := len(test.featureWeights)
		zOutput := make([]float64, nOutput)
		predOutput := make([]float64, nOutput)

		for i := range predOutput {
			predOutput[i] = rand.NormFloat64()
			zOutput[i] = rand.NormFloat64()
		}

		sqrt2OverD := math.Sqrt(2.0 / float64(nFeatures))

		z := make([]float64, nFeatures)
		for i := range z {
			z[i] = computeZ(test.input, featureMat.RowView(i), test.b[i], sqrt2OverD)
		}
		predictFeaturized(z, fwMat, zOutput)

		predict(inputCopy, featureMatCopy, bCopy, fwMatCopy, predOutput)

		// Check to make sure nothing changed
		if !floats.Equal(inputCopy, test.input) {
			t.Errorf("input has been modified")
		}
		if !floats.Equal(bCopy, test.b) {
			t.Errorf("b has been modified")
		}
		if !fwMat.Equals(fwMatCopy) {
			t.Errorf("feature weights changed")
		}
		if !featureMat.Equals(featureMatCopy) {
			t.Errorf("features changed")
		}
		if !floats.EqualApprox(zOutput, predOutput, 1e-14) {
			t.Errorf("Prediction doesn't match for case %v. Expected %v, found %v", test.Name, zOutput, predOutput)
		}
	}
}
Esempio n. 25
0
func DtrconTest(t *testing.T, impl Dtrconer) {
	// Hand crafted tests.
	for _, test := range []struct {
		a       []float64
		n       int
		uplo    blas.Uplo
		diag    blas.Diag
		condOne float64
		condInf float64
	}{
		{
			a: []float64{
				8, 5, 6,
				0, 7, 8,
				0, 0, 6,
			},
			n:       3,
			uplo:    blas.Upper,
			diag:    blas.Unit,
			condOne: 1.0 / 645,
			condInf: 1.0 / 480,
		},
		{
			a: []float64{
				8, 5, 6,
				0, 7, 8,
				0, 0, 6,
			},
			n:       3,
			uplo:    blas.Upper,
			diag:    blas.NonUnit,
			condOne: 0.137704918032787,
			condInf: 0.157894736842105,
		},
		{
			a: []float64{
				8, 0, 0,
				5, 7, 0,
				6, 8, 6,
			},
			n:       3,
			uplo:    blas.Lower,
			diag:    blas.Unit,
			condOne: 1.0 / 480,
			condInf: 1.0 / 645,
		},
		{
			a: []float64{
				8, 0, 0,
				5, 7, 0,
				6, 8, 6,
			},
			n:       3,
			uplo:    blas.Lower,
			diag:    blas.NonUnit,
			condOne: 0.157894736842105,
			condInf: 0.137704918032787,
		},
	} {
		lda := test.n
		work := make([]float64, 3*test.n)
		for i := range work {
			work[i] = rand.Float64()
		}
		iwork := make([]int, test.n)
		for i := range iwork {
			iwork[i] = rand.Int()
		}
		aCopy := make([]float64, len(test.a))
		copy(aCopy, test.a)
		condOne := impl.Dtrcon(lapack.MaxColumnSum, test.uplo, test.diag, test.n, test.a, lda, work, iwork)
		if math.Abs(condOne-test.condOne) > 1e-14 {
			t.Errorf("One norm mismatch. Want %v, got %v.", test.condOne, condOne)
		}
		if !floats.Equal(aCopy, test.a) {
			t.Errorf("a modified during call")
		}
		condInf := impl.Dtrcon(lapack.MaxRowSum, test.uplo, test.diag, test.n, test.a, lda, work, iwork)
		if math.Abs(condInf-test.condInf) > 1e-14 {
			t.Errorf("Inf norm mismatch. Want %v, got %v.", test.condInf, condInf)
		}
		if !floats.Equal(aCopy, test.a) {
			t.Errorf("a modified during call")
		}
	}

	// Dtrcon does not match the Dgecon output in many cases. See
	// https://github.com/xianyi/OpenBLAS/issues/636
	// TODO(btracey): Uncomment this when the mismatch between Dgecon and Dtrcon
	// is understood.
	/*
		// Randomized tests against Dgecon.
		for _, uplo := range []blas.Uplo{blas.Lower, blas.Upper} {
			for _, diag := range []blas.Diag{blas.NonUnit, blas.Unit} {
				for _, test := range []struct {
					n, lda int
				}{
					{3, 0},
					{4, 9},
				} {
					for trial := 0; trial < 1; trial++ {
						n := test.n
						lda := test.lda
						if lda == 0 {
							lda = n
						}
						a := make([]float64, n*lda)
						if trial == 0 {
							for i := range a {
								a[i] = float64(i + 2)
							}
						} else {
							for i := range a {
								a[i] = rand.NormFloat64()
							}
						}

						aDense := make([]float64, len(a))
						if uplo == blas.Upper {
							for i := 0; i < n; i++ {
								for j := i; j < n; j++ {
									aDense[i*lda+j] = a[i*lda+j]
								}
							}
						} else {
							for i := 0; i < n; i++ {
								for j := 0; j <= i; j++ {
									aDense[i*lda+j] = a[i*lda+j]
								}
							}
						}
						if diag == blas.Unit {
							for i := 0; i < n; i++ {
								aDense[i*lda+i] = 1
							}
						}

						ipiv := make([]int, n)
						work := make([]float64, 4*n)
						denseOne := impl.Dlange(lapack.MaxColumnSum, n, n, aDense, lda, work)
						denseInf := impl.Dlange(lapack.MaxRowSum, n, n, aDense, lda, work)

						aDenseLU := make([]float64, len(aDense))
						copy(aDenseLU, aDense)
						impl.Dgetrf(n, n, aDenseLU, lda, ipiv)
						iwork := make([]int, n)
						want := impl.Dgecon(lapack.MaxColumnSum, n, aDenseLU, lda, denseOne, work, iwork)
						got := impl.Dtrcon(lapack.MaxColumnSum, uplo, diag, n, a, lda, work, iwork)
						if math.Abs(want-got) > 1e-14 {
							t.Errorf("One norm mismatch. Upper = %v, unit = %v, want %v, got %v", uplo == blas.Upper, diag == blas.Unit, want, got)
						}
						want = impl.Dgecon(lapack.MaxRowSum, n, aDenseLU, lda, denseInf, work, iwork)
						got = impl.Dtrcon(lapack.MaxRowSum, uplo, diag, n, a, lda, work, iwork)
						if math.Abs(want-got) > 1e-14 {
							t.Errorf("Inf norm mismatch. Upper = %v, unit = %v, want %v, got %v", uplo == blas.Upper, diag == blas.Unit, want, got)
						}
					}
				}
			}
		}
	*/
}
Esempio n. 26
0
func TestCorrelationMatrix(t *testing.T) {
	for i, test := range []struct {
		data    *mat64.Dense
		weights []float64
		ans     *mat64.Dense
	}{
		{
			data: mat64.NewDense(3, 3, []float64{
				1, 2, 3,
				3, 4, 5,
				5, 6, 7,
			}),
			weights: nil,
			ans: mat64.NewDense(3, 3, []float64{
				1, 1, 1,
				1, 1, 1,
				1, 1, 1,
			}),
		},
		{
			data: mat64.NewDense(5, 2, []float64{
				-2, -4,
				-1, 2,
				0, 0,
				1, -2,
				2, 4,
			}),
			weights: nil,
			ans: mat64.NewDense(2, 2, []float64{
				1, 0.6,
				0.6, 1,
			}),
		}, {
			data: mat64.NewDense(3, 2, []float64{
				1, 1,
				2, 4,
				3, 9,
			}),
			weights: []float64{
				1,
				1.5,
				1,
			},
			ans: mat64.NewDense(2, 2, []float64{
				1, 0.9868703275903379,
				0.9868703275903379, 1,
			}),
		},
	} {
		// Make a copy of the data to check that it isn't changing.
		r := test.data.RawMatrix()
		d := make([]float64, len(r.Data))
		copy(d, r.Data)

		w := make([]float64, len(test.weights))
		if test.weights != nil {
			copy(w, test.weights)
		}
		c := CorrelationMatrix(nil, test.data, test.weights)
		if !c.Equals(test.ans) {
			t.Errorf("%d: expected corr %v, found %v", i, test.ans, c)
		}
		if !floats.Equal(d, r.Data) {
			t.Errorf("%d: data was modified during execution", i)
		}
		if !floats.Equal(w, test.weights) {
			t.Errorf("%d: weights was modified during execution", i)
		}

		// compare with call to Covariance
		_, cols := c.Dims()
		for ci := 0; ci < cols; ci++ {
			for cj := 0; cj < cols; cj++ {
				x := test.data.Col(nil, ci)
				y := test.data.Col(nil, cj)
				corr := Correlation(x, y, test.weights)
				if math.Abs(corr-c.At(ci, cj)) > 1e-14 {
					t.Errorf("CorrMat does not match at (%v, %v). Want %v, got %v.", ci, cj, corr, c.At(ci, cj))
				}
			}
		}

	}
	if !Panics(func() { CorrelationMatrix(nil, mat64.NewDense(5, 2, nil), []float64{}) }) {
		t.Errorf("CorrelationMatrix did not panic with weight size mismatch")
	}
	if !Panics(func() { CorrelationMatrix(mat64.NewDense(1, 1, nil), mat64.NewDense(5, 2, nil), nil) }) {
		t.Errorf("CorrelationMatrix did not panic with preallocation size mismatch")
	}
	if !Panics(func() { CorrelationMatrix(nil, mat64.NewDense(2, 2, []float64{1, 2, 3, 4}), []float64{1, -1}) }) {
		t.Errorf("CorrelationMatrix did not panic with negative weights")
	}
}
Esempio n. 27
0
func Dorm2rTest(t *testing.T, impl Dorm2rer) {
	rnd := rand.New(rand.NewSource(1))
	for _, side := range []blas.Side{blas.Left, blas.Right} {
		for _, trans := range []blas.Transpose{blas.NoTrans, blas.Trans} {
			for _, test := range []struct {
				common, adim, cdim, lda, ldc int
			}{
				{3, 4, 5, 0, 0},
				{3, 5, 4, 0, 0},
				{4, 3, 5, 0, 0},
				{4, 5, 3, 0, 0},
				{5, 3, 4, 0, 0},
				{5, 4, 3, 0, 0},
				{3, 4, 5, 6, 20},
				{3, 5, 4, 6, 20},
				{4, 3, 5, 6, 20},
				{4, 5, 3, 6, 20},
				{5, 3, 4, 6, 20},
				{5, 4, 3, 6, 20},
				{3, 4, 5, 20, 6},
				{3, 5, 4, 20, 6},
				{4, 3, 5, 20, 6},
				{4, 5, 3, 20, 6},
				{5, 3, 4, 20, 6},
				{5, 4, 3, 20, 6},
			} {
				var ma, na, mc, nc int
				if side == blas.Left {
					ma = test.common
					na = test.adim
					mc = test.common
					nc = test.cdim
				} else {
					ma = test.common
					na = test.adim
					mc = test.cdim
					nc = test.common
				}

				// Generate a random matrix
				lda := test.lda
				if lda == 0 {
					lda = na
				}
				a := make([]float64, ma*lda)
				for i := range a {
					a[i] = rnd.Float64()
				}
				ldc := test.ldc
				if ldc == 0 {
					ldc = nc
				}
				// Compute random C matrix
				c := make([]float64, mc*ldc)
				for i := range c {
					c[i] = rnd.Float64()
				}

				// Compute QR
				k := min(ma, na)
				tau := make([]float64, k)
				work := make([]float64, 1)
				impl.Dgeqrf(ma, na, a, lda, tau, work, -1)
				work = make([]float64, int(work[0]))
				impl.Dgeqrf(ma, na, a, lda, tau, work, len(work))

				// Build Q from result
				q := constructQ("QR", ma, na, a, lda, tau)

				cMat := blas64.General{
					Rows:   mc,
					Cols:   nc,
					Stride: ldc,
					Data:   make([]float64, len(c)),
				}
				copy(cMat.Data, c)
				cMatCopy := blas64.General{
					Rows:   cMat.Rows,
					Cols:   cMat.Cols,
					Stride: cMat.Stride,
					Data:   make([]float64, len(cMat.Data)),
				}
				copy(cMatCopy.Data, cMat.Data)
				switch {
				default:
					panic("bad test")
				case side == blas.Left && trans == blas.NoTrans:
					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, q, cMatCopy, 0, cMat)
				case side == blas.Left && trans == blas.Trans:
					blas64.Gemm(blas.Trans, blas.NoTrans, 1, q, cMatCopy, 0, cMat)
				case side == blas.Right && trans == blas.NoTrans:
					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, cMatCopy, q, 0, cMat)
				case side == blas.Right && trans == blas.Trans:
					blas64.Gemm(blas.NoTrans, blas.Trans, 1, cMatCopy, q, 0, cMat)
				}
				// Do Dorm2r ard compare
				if side == blas.Left {
					work = make([]float64, nc)
				} else {
					work = make([]float64, mc)
				}
				aCopy := make([]float64, len(a))
				copy(aCopy, a)
				tauCopy := make([]float64, len(tau))
				copy(tauCopy, tau)
				impl.Dorm2r(side, trans, mc, nc, k, a, lda, tau, c, ldc, work)
				if !floats.Equal(a, aCopy) {
					t.Errorf("a changed in call")
				}
				if !floats.Equal(tau, tauCopy) {
					t.Errorf("tau changed in call")
				}
				if !floats.EqualApprox(cMat.Data, c, 1e-14) {
					t.Errorf("Multiplication mismatch.\n Want %v \n got %v.", cMat.Data, c)
				}
			}
		}
	}
}
Esempio n. 28
0
// TestPredict tests that predict returns the expected value, and that calling predict in parallel
// also works
func TestPredictAndBatch(t *testing.T, p Predictor, inputs, trueOutputs common.RowMatrix, name string) {
	nSamples, inputDim := inputs.Dims()
	if inputDim != p.InputDim() {
		panic("input Dim doesn't match predictor input dim")
	}
	nOutSamples, outputDim := trueOutputs.Dims()
	if outputDim != p.OutputDim() {
		panic("outpuDim doesn't match predictor outputDim")
	}
	if nOutSamples != nSamples {
		panic("inputs and outputs have different number of rows")
	}

	// First, test sequentially
	for i := 0; i < nSamples; i++ {
		trueOut := make([]float64, outputDim)
		for j := 0; j < outputDim; j++ {
			trueOut[j] = trueOutputs.At(i, j)
		}
		// Predict with nil
		input := make([]float64, inputDim)
		inputCpy := make([]float64, inputDim)
		for j := 0; j < inputDim; j++ {
			input[j] = inputs.At(i, j)
			inputCpy[j] = inputs.At(i, j)
		}

		out1, err := p.Predict(input, nil)
		if err != nil {
			t.Errorf(name + ": Error predicting with nil output")
			return
		}
		if !floats.Equal(input, inputCpy) {
			t.Errorf("%v: input changed with nil input for row %v", name, i)
			break
		}
		out2 := make([]float64, outputDim)
		for j := 0; j < outputDim; j++ {
			out2[j] = rand.NormFloat64()
		}

		_, err = p.Predict(input, out2)
		if err != nil {
			t.Errorf("%v: error predicting with non-nil input for row %v", name, i)
			break
		}
		if !floats.Equal(input, inputCpy) {
			t.Errorf("%v: input changed with non-nil input for row %v", name, i)
			break
		}

		if !floats.Equal(out1, out2) {
			t.Errorf(name + ": different answers with nil and non-nil predict ")
			break
		}
		if !floats.EqualApprox(out1, trueOut, 1e-14) {
			t.Errorf("%v: predicted output doesn't match for row %v. Expected %v, found %v", name, i, trueOut, out1)
			break
		}
	}

	// Check that predict errors with bad sized arguments
	badOuput := make([]float64, outputDim+1)
	input := make([]float64, inputDim)
	for i := 0; i < inputDim; i++ {
		input[i] = inputs.At(0, i)
	}
	output := make([]float64, outputDim)
	for i := 0; i < outputDim; i++ {
		output[i] = trueOutputs.At(0, i)
	}

	_, err := p.Predict(input, badOuput)
	if err == nil {
		t.Errorf("Predict did not throw an error with an output too large")
	}
	if outputDim > 1 {
		badOuput := make([]float64, outputDim-1)
		_, err := p.Predict(input, badOuput)
		if err == nil {
			t.Errorf("Predict did not throw an error with an output too small")
		}
	}

	badInput := make([]float64, inputDim+1)
	_, err = p.Predict(badInput, output)
	if err == nil {
		t.Errorf("Predict did not err when input is too large")
	}
	if inputDim > 1 {
		badInput := make([]float64, inputDim-1)
		_, err = p.Predict(badInput, output)
		if err == nil {
			t.Errorf("Predict did not err when input is too small")
		}
	}

	// Now, test batch
	// With non-nil
	inputCpy := &mat64.Dense{}
	inputCpy.Clone(inputs)
	predOutput, err := p.PredictBatch(inputs, nil)
	if err != nil {
		t.Errorf("Error batch predicting: %v", err)
	}
	if !inputCpy.Equals(inputs) {
		t.Errorf("Inputs changed during call to PredictBatch")
	}
	predOutputRows, predOutputCols := predOutput.Dims()
	if predOutputRows != nSamples || predOutputCols != outputDim {
		t.Errorf("Dimension mismatch after predictbatch with nil input")
	}

	outputs := mat64.NewDense(nSamples, outputDim, nil)
	_, err = p.PredictBatch(inputs, outputs)

	pd := predOutput.(*mat64.Dense)
	if !pd.Equals(outputs) {
		t.Errorf("Different outputs from predict batch with nil and non-nil")
	}

	badInputs := mat64.NewDense(nSamples, inputDim+1, nil)
	_, err = p.PredictBatch(badInputs, outputs)
	if err == nil {
		t.Error("PredictBatch did not err when input dim too large")
	}
	badInputs = mat64.NewDense(nSamples+1, inputDim, nil)
	_, err = p.PredictBatch(badInputs, outputs)
	if err == nil {
		t.Errorf("PredictBatch did not err with row mismatch")
	}
	badOuputs := mat64.NewDense(nSamples, outputDim+1, nil)
	_, err = p.PredictBatch(inputs, badOuputs)
	if err == nil {
		t.Errorf("PredictBatch did not err with output dim too large")
	}
}
Esempio n. 29
0
func TestGetAndSetParameters(t *testing.T, p ParameterGetterSetter, name string) {

	// Test that we can get parameters from nil
	// TODO: Add panic guard
	var nilParam []float64
	f := func() {
		nilParam = p.Parameters(nil)
	}

	if maybe(f) {
		t.Errorf("%v: Parameters panicked with nil input", name)
		return
	}

	if len(nilParam) != p.NumParameters() {
		t.Errorf("%v: On nil input, incorrect length returned from Parameters()", name)
	}
	nilParamCopy := make([]float64, p.NumParameters())
	copy(nilParamCopy, nilParam)
	nonNilParam := make([]float64, p.NumParameters())
	p.Parameters(nonNilParam)
	if !floats.Equal(nilParam, nonNilParam) {
		t.Errorf("%v: Return from Parameters() with nil argument and non nil argument are different", name)
	}
	for i := range nonNilParam {
		nonNilParam[i] = rand.NormFloat64()
	}
	if !floats.Equal(nilParam, nilParamCopy) {
		t.Errorf("%v: Modifying the return from Parameters modified the underlying parameters", name)
	}
	setParam := make([]float64, p.NumParameters())
	copy(setParam, nonNilParam)
	p.SetParameters(setParam)
	if !floats.Equal(setParam, nonNilParam) {
		t.Errorf("%v: Input slice modified during call to SetParameters", name)
	}

	afterParam := p.Parameters(nil)
	if !floats.Equal(afterParam, setParam) {
		t.Errorf("%v: Set parameters followed by Parameters don't return the same argument", name)
	}

	// Test that there are panics on bad length arguments
	badLength := make([]float64, p.NumParameters()+3)

	f = func() {
		p.Parameters(badLength)
	}
	if !panics(f) {
		t.Errorf("%v: Parameters did not panic given a slice too long", name)
	}
	f = func() {
		p.SetParameters(badLength)
	}
	if !panics(f) {
		t.Errorf("%v: SetParameters did not panic given a slice too long", name)
	}
	if p.NumParameters() == 0 {
		return
	}
	badLength = badLength[:p.NumParameters()-1]
	f = func() {
		p.Parameters(badLength)
	}
	if !panics(f) {
		t.Errorf("%v: Parameters did not panic given a slice too short", name)
	}
	f = func() {
		p.SetParameters(badLength)
	}
	if !panics(f) {
		t.Errorf("%v: SetParameters did not panic given a slice too short", name)
	}
}
Esempio n. 30
0
func DgesvdTest(t *testing.T, impl Dgesvder) {
	rnd := rand.New(rand.NewSource(1))
	// TODO(btracey): Add tests for all of the cases when the SVD implementation
	// is finished.
	// TODO(btracey): Add tests for m > mnthr and n > mnthr when other SVD
	// conditions are implemented. Right now mnthr is 5,000,000 which is too
	// large to create a square matrix of that size.
	for _, test := range []struct {
		m, n, lda, ldu, ldvt int
	}{
		{5, 5, 0, 0, 0},
		{5, 6, 0, 0, 0},
		{6, 5, 0, 0, 0},
		{5, 9, 0, 0, 0},
		{9, 5, 0, 0, 0},

		{5, 5, 10, 11, 12},
		{5, 6, 10, 11, 12},
		{6, 5, 10, 11, 12},
		{5, 5, 10, 11, 12},
		{5, 9, 10, 11, 12},
		{9, 5, 10, 11, 12},

		{300, 300, 0, 0, 0},
		{300, 400, 0, 0, 0},
		{400, 300, 0, 0, 0},
		{300, 600, 0, 0, 0},
		{600, 300, 0, 0, 0},

		{300, 300, 400, 450, 460},
		{300, 400, 500, 550, 560},
		{400, 300, 550, 550, 560},
		{300, 600, 700, 750, 760},
		{600, 300, 700, 750, 760},
	} {
		jobU := lapack.SVDAll
		jobVT := lapack.SVDAll

		m := test.m
		n := test.n
		lda := test.lda
		if lda == 0 {
			lda = n
		}
		ldu := test.ldu
		if ldu == 0 {
			ldu = m
		}
		ldvt := test.ldvt
		if ldvt == 0 {
			ldvt = n
		}

		a := make([]float64, m*lda)
		for i := range a {
			a[i] = rnd.NormFloat64()
		}

		u := make([]float64, m*ldu)
		for i := range u {
			u[i] = rnd.NormFloat64()
		}

		vt := make([]float64, n*ldvt)
		for i := range vt {
			vt[i] = rnd.NormFloat64()
		}

		uAllOrig := make([]float64, len(u))
		copy(uAllOrig, u)
		vtAllOrig := make([]float64, len(vt))
		copy(vtAllOrig, vt)
		aCopy := make([]float64, len(a))
		copy(aCopy, a)

		s := make([]float64, min(m, n))

		work := make([]float64, 1)
		impl.Dgesvd(jobU, jobVT, m, n, a, lda, s, u, ldu, vt, ldvt, work, -1)

		if !floats.Equal(a, aCopy) {
			t.Errorf("a changed during call to get work length")
		}

		work = make([]float64, int(work[0]))
		impl.Dgesvd(jobU, jobVT, m, n, a, lda, s, u, ldu, vt, ldvt, work, len(work))

		errStr := fmt.Sprintf("m = %v, n = %v, lda = %v, ldu = %v, ldv = %v", m, n, lda, ldu, ldvt)
		svdCheck(t, false, errStr, m, n, s, a, u, ldu, vt, ldvt, aCopy, lda)
		svdCheckPartial(t, impl, lapack.SVDAll, errStr, uAllOrig, vtAllOrig, aCopy, m, n, a, lda, s, u, ldu, vt, ldvt, work, false)

		// Test InPlace
		jobU = lapack.SVDInPlace
		jobVT = lapack.SVDInPlace
		copy(a, aCopy)
		copy(u, uAllOrig)
		copy(vt, vtAllOrig)

		impl.Dgesvd(jobU, jobVT, m, n, a, lda, s, u, ldu, vt, ldvt, work, len(work))
		svdCheck(t, true, errStr, m, n, s, a, u, ldu, vt, ldvt, aCopy, lda)
		svdCheckPartial(t, impl, lapack.SVDInPlace, errStr, uAllOrig, vtAllOrig, aCopy, m, n, a, lda, s, u, ldu, vt, ldvt, work, false)
	}
}