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) } } }
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 }
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 }
// 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 }
// 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)) } }
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) } } }
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) }
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) } } } } }
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) } } } } }
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) } }
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 }
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) } } }
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 }
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) } } }
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) } } }
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) } } }
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) } } } } }
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) } } }
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 }
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) }
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") } }
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) } } }
// 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) } } } } } } }
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) } } }
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) } } } } } */ }
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") } }
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) } } } } }
// 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") } }
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) } }
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) } }