gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/mat/lu_test.go (about)

     1  // Copyright ©2013 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mat
     6  
     7  import (
     8  	"testing"
     9  
    10  	"golang.org/x/exp/rand"
    11  )
    12  
    13  func TestLU(t *testing.T) {
    14  	t.Parallel()
    15  	const tol = 1e-16
    16  	rnd := rand.New(rand.NewSource(1))
    17  	for _, n := range []int{1, 2, 3, 4, 5, 10, 11, 50} {
    18  		// Construct a random matrix A.
    19  		a := NewDense(n, n, nil)
    20  		a.Apply(func(_, _ int, _ float64) float64 { return rnd.NormFloat64() }, a)
    21  
    22  		// Compute the LU factorization of A.
    23  		var lu LU
    24  		lu.Factorize(a)
    25  
    26  		// Compare A and LU using At.
    27  		if !EqualApprox(a, &lu, tol*float64(n)) {
    28  			var diff Dense
    29  			diff.Sub(a, &lu)
    30  			t.Errorf("n=%d: A and LU not equal\ndiff=%v", n, Formatted(&diff, Prefix("     ")))
    31  		}
    32  
    33  		// Recover A using RowPivots, LTo and UTo.
    34  		var l, u TriDense
    35  		lu.LTo(&l)
    36  		lu.UTo(&u)
    37  		var got Dense
    38  		got.Mul(&l, &u)
    39  		got.PermuteRows(lu.RowPivots(nil), false)
    40  		if !EqualApprox(&got, a, tol*float64(n)) {
    41  			var diff Dense
    42  			diff.Sub(&got, a)
    43  			t.Errorf("n=%d: A and P*L*U not equal\ndiff=%v", n, Formatted(&diff, Prefix("     ")))
    44  		}
    45  	}
    46  }
    47  
    48  func TestLURankOne(t *testing.T) {
    49  	t.Parallel()
    50  	const tol = 1e-14
    51  	rnd := rand.New(rand.NewSource(1))
    52  	for _, n := range []int{1, 2, 3, 4, 5, 10, 50} {
    53  		// Construct a random matrix A.
    54  		a := NewDense(n, n, nil)
    55  		a.Apply(func(_, _ int, _ float64) float64 { return rnd.NormFloat64() }, a)
    56  
    57  		// Compute the LU factorization of A.
    58  		var lu LU
    59  		lu.Factorize(a)
    60  
    61  		// Apply a rank one update to A. Ensure the update magnitude is larger than
    62  		// the equal tolerance.
    63  		alpha := rnd.Float64() + 1
    64  		x := NewVecDense(n, nil)
    65  		y := NewVecDense(n, nil)
    66  		for i := 0; i < n; i++ {
    67  			x.setVec(i, rnd.Float64()+1)
    68  			y.setVec(i, rnd.Float64()+1)
    69  		}
    70  		a.RankOne(a, alpha, x, y)
    71  
    72  		// Apply the same rank one update to the LU factorization of A.
    73  		var luNew LU
    74  		luNew.RankOne(&lu, alpha, x, y)
    75  		lu.RankOne(&lu, alpha, x, y)
    76  
    77  		if !EqualApprox(&lu, a, tol*float64(n)) {
    78  			var diff Dense
    79  			diff.Sub(&lu, a)
    80  			t.Errorf("n=%d: rank one mismatch\ndiff=%v", n, Formatted(&diff, Prefix("     ")))
    81  		}
    82  
    83  		if !Equal(&lu, &luNew) {
    84  			var diff Dense
    85  			diff.Sub(&lu, &luNew)
    86  			t.Errorf("n=%d: rank one mismatch with new receiver\ndiff=%v", n, Formatted(&diff, Prefix("     ")))
    87  		}
    88  	}
    89  }
    90  
    91  func TestLUSolveTo(t *testing.T) {
    92  	t.Parallel()
    93  	rnd := rand.New(rand.NewSource(1))
    94  	for _, test := range []struct {
    95  		n, bc int
    96  	}{
    97  		{5, 5},
    98  		{5, 10},
    99  		{10, 5},
   100  	} {
   101  		n := test.n
   102  		bc := test.bc
   103  		a := NewDense(n, n, nil)
   104  		for i := 0; i < n; i++ {
   105  			for j := 0; j < n; j++ {
   106  				a.Set(i, j, rnd.NormFloat64())
   107  			}
   108  		}
   109  		b := NewDense(n, bc, nil)
   110  		for i := 0; i < n; i++ {
   111  			for j := 0; j < bc; j++ {
   112  				b.Set(i, j, rnd.NormFloat64())
   113  			}
   114  		}
   115  		var lu LU
   116  		lu.Factorize(a)
   117  		var x Dense
   118  		if err := lu.SolveTo(&x, false, b); err != nil {
   119  			continue
   120  		}
   121  		var got Dense
   122  		got.Mul(a, &x)
   123  		if !EqualApprox(&got, b, 1e-12) {
   124  			t.Errorf("SolveTo mismatch for non-singular matrix. n = %v, bc = %v.\nWant: %v\nGot: %v", n, bc, b, got)
   125  		}
   126  	}
   127  	// TODO(btracey): Add testOneInput test when such a function exists.
   128  }
   129  
   130  func TestLUSolveToCond(t *testing.T) {
   131  	t.Parallel()
   132  	for _, test := range []*Dense{
   133  		NewDense(2, 2, []float64{1, 0, 0, 1e-20}),
   134  	} {
   135  		m, _ := test.Dims()
   136  		var lu LU
   137  		lu.Factorize(test)
   138  		b := NewDense(m, 2, nil)
   139  		var x Dense
   140  		if err := lu.SolveTo(&x, false, b); err == nil {
   141  			t.Error("No error for near-singular matrix in matrix solve.")
   142  		}
   143  
   144  		bvec := NewVecDense(m, nil)
   145  		var xvec VecDense
   146  		if err := lu.SolveVecTo(&xvec, false, bvec); err == nil {
   147  			t.Error("No error for near-singular matrix in matrix solve.")
   148  		}
   149  	}
   150  }
   151  
   152  func TestLUSolveVecTo(t *testing.T) {
   153  	t.Parallel()
   154  	rnd := rand.New(rand.NewSource(1))
   155  	for _, n := range []int{5, 10} {
   156  		a := NewDense(n, n, nil)
   157  		for i := 0; i < n; i++ {
   158  			for j := 0; j < n; j++ {
   159  				a.Set(i, j, rnd.NormFloat64())
   160  			}
   161  		}
   162  		b := NewVecDense(n, nil)
   163  		for i := 0; i < n; i++ {
   164  			b.SetVec(i, rnd.NormFloat64())
   165  		}
   166  		var lu LU
   167  		lu.Factorize(a)
   168  		var x VecDense
   169  		if err := lu.SolveVecTo(&x, false, b); err != nil {
   170  			continue
   171  		}
   172  		var got VecDense
   173  		got.MulVec(a, &x)
   174  		if !EqualApprox(&got, b, 1e-12) {
   175  			t.Errorf("SolveTo mismatch n = %v.\nWant: %v\nGot: %v", n, b, got)
   176  		}
   177  	}
   178  	// TODO(btracey): Add testOneInput test when such a function exists.
   179  }