func ExampleCholesky() { // Construct a symmetric positive definite matrix. tmp := mat64.NewDense(4, 4, []float64{ 2, 6, 8, -4, 1, 8, 7, -2, 2, 2, 1, 7, 8, -2, -2, 1, }) var a mat64.SymDense a.SymOuterK(1, tmp) fmt.Printf("a = %0.4v\n", mat64.Formatted(&a, mat64.Prefix(" "))) // Compute the cholesky factorization. var chol mat64.Cholesky if ok := chol.Factorize(&a); !ok { fmt.Println("a matrix is not positive semi-definite.") } // Find the determinant. fmt.Printf("\nThe determinant of a is %0.4g\n\n", chol.Det()) // Use the factorization to solve the system of equations a * x = b. b := mat64.NewVector(4, []float64{1, 2, 3, 4}) var x mat64.Vector if err := x.SolveCholeskyVec(&chol, b); err != nil { fmt.Println("Matrix is near singular: ", err) } fmt.Println("Solve a * x = b") fmt.Printf("x = %0.4v\n", mat64.Formatted(&x, mat64.Prefix(" "))) // Extract the factorization and check that it equals the original matrix. var t mat64.TriDense t.LFromCholesky(&chol) var test mat64.Dense test.Mul(&t, t.T()) fmt.Println() fmt.Printf("L * L^T = %0.4v\n", mat64.Formatted(&a, mat64.Prefix(" "))) // Output: // a = ⎡120 114 -4 -16⎤ // ⎢114 118 11 -24⎥ // ⎢ -4 11 58 17⎥ // ⎣-16 -24 17 73⎦ // // The determinant of a is 1.543e+06 // // Solve a * x = b // x = ⎡ -0.239⎤ // ⎢ 0.2732⎥ // ⎢-0.04681⎥ // ⎣ 0.1031⎦ // // L * L^T = ⎡120 114 -4 -16⎤ // ⎢114 118 11 -24⎥ // ⎢ -4 11 58 17⎥ // ⎣-16 -24 17 73⎦ }
// randomNormal constructs a random Normal distribution. func randomNormal(dim int) (*distmv.Normal, bool) { data := make([]float64, dim*dim) for i := range data { data[i] = rand.Float64() } a := mat64.NewDense(dim, dim, data) var sigma mat64.SymDense sigma.SymOuterK(1, a) mu := make([]float64, dim) for i := range mu { mu[i] = rand.NormFloat64() } return distmv.NewNormal(mu, &sigma, nil) }
// CovarianceMatrix calculates a covariance matrix (also known as a // variance-covariance matrix) from a matrix of data, using a two-pass // algorithm. // // The weights must have length equal to the number of rows in // input data matrix x. If cov is nil, then a new matrix with appropriate size will // be constructed. If cov is not nil, it should have the same number of columns as the // input data matrix x, and it will be used as the destination for the covariance // data. Weights must not be negative. func CovarianceMatrix(cov *mat64.SymDense, x mat64.Matrix, weights []float64) *mat64.SymDense { // This is the matrix version of the two-pass algorithm. It doesn't use the // additional floating point error correction that the Covariance function uses // to reduce the impact of rounding during centering. r, c := x.Dims() if cov == nil { cov = mat64.NewSymDense(c, nil) } else if n := cov.Symmetric(); n != c { panic(matrix.ErrShape) } var xt mat64.Dense xt.Clone(x.T()) // Subtract the mean of each of the columns. for i := 0; i < c; i++ { v := xt.RawRowView(i) // This will panic with ErrShape if len(weights) != len(v), so // we don't have to check the size later. mean := Mean(v, weights) floats.AddConst(-mean, v) } if weights == nil { // Calculate the normalization factor // scaled by the sample size. cov.SymOuterK(1/(float64(r)-1), &xt) return cov } // Multiply by the sqrt of the weights, so that multiplication is symmetric. sqrtwts := make([]float64, r) for i, w := range weights { if w < 0 { panic("stat: negative covariance matrix weights") } sqrtwts[i] = math.Sqrt(w) } // Weight the rows. for i := 0; i < c; i++ { v := xt.RawRowView(i) floats.Mul(v, sqrtwts) } // Calculate the normalization factor // scaled by the weighted sample size. cov.SymOuterK(1/(floats.Sum(weights)-1), &xt) return cov }
func randomNormal(sz int, rnd *rand.Rand) *Normal { mu := make([]float64, sz) for i := range mu { mu[i] = rnd.Float64() } data := make([]float64, sz*sz) for i := range data { data[i] = rnd.Float64() } dM := mat64.NewDense(sz, sz, data) var sigma mat64.SymDense sigma.SymOuterK(1, dM) normal, ok := NewNormal(mu, &sigma, nil) if !ok { log.Fatal("bad test, not pos def") } return normal }
func TestMarginalSingle(t *testing.T) { for _, test := range []struct { mu []float64 sigma *mat64.SymDense }{ { mu: []float64{2, 3, 4}, sigma: mat64.NewSymDense(3, []float64{2, 0.5, 3, 0.5, 1, 0.6, 3, 0.6, 10}), }, { mu: []float64{2, 3, 4, 5}, sigma: mat64.NewSymDense(4, []float64{2, 0.5, 3, 0.1, 0.5, 1, 0.6, 0.2, 3, 0.6, 10, 0.3, 0.1, 0.2, 0.3, 3}), }, } { normal, ok := NewNormal(test.mu, test.sigma, nil) if !ok { t.Fatalf("Bad test, covariance matrix not positive definite") } // Verify with nil Sigma. normal.sigma = nil for i, mean := range test.mu { norm := normal.MarginalNormalSingle(i, nil) if norm.Mean() != mean { t.Errorf("Mean mismatch nil Sigma, idx %v: want %v, got %v.", i, mean, norm.Mean()) } std := math.Sqrt(test.sigma.At(i, i)) if math.Abs(norm.StdDev()-std) > 1e-14 { t.Errorf("StdDev mismatch nil Sigma, idx %v: want %v, got %v.", i, std, norm.StdDev()) } } // Verify with non-nil Sigma. normal.setSigma() for i, mean := range test.mu { norm := normal.MarginalNormalSingle(i, nil) if norm.Mean() != mean { t.Errorf("Mean mismatch non-nil Sigma, idx %v: want %v, got %v.", i, mean, norm.Mean()) } std := math.Sqrt(test.sigma.At(i, i)) if math.Abs(norm.StdDev()-std) > 1e-14 { t.Errorf("StdDev mismatch non-nil Sigma, idx %v: want %v, got %v.", i, std, norm.StdDev()) } } } // Test matching with TestMarginal. rnd := rand.New(rand.NewSource(1)) for cas := 0; cas < 10; cas++ { dim := rnd.Intn(10) + 1 mu := make([]float64, dim) for i := range mu { mu[i] = rnd.Float64() } x := make([]float64, dim*dim) for i := range x { x[i] = rnd.Float64() } mat := mat64.NewDense(dim, dim, x) var sigma mat64.SymDense sigma.SymOuterK(1, mat) normal, ok := NewNormal(mu, &sigma, nil) if !ok { t.Fatal("bad test") } for i := 0; i < dim; i++ { single := normal.MarginalNormalSingle(i, nil) mult, ok := normal.MarginalNormal([]int{i}, nil) if !ok { t.Fatal("bad test") } if math.Abs(single.Mean()-mult.Mean(nil)[0]) > 1e-14 { t.Errorf("Mean mismatch") } if math.Abs(single.Variance()-mult.CovarianceMatrix(nil).At(0, 0)) > 1e-14 { t.Errorf("Variance mismatch") } } } }