gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/mat/lq_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 TestLQ(t *testing.T) {
    14  	t.Parallel()
    15  	const tol = 1e-14
    16  	rnd := rand.New(rand.NewSource(1))
    17  	for cas, test := range []struct {
    18  		m, n int
    19  	}{
    20  		{5, 5},
    21  		{5, 10},
    22  	} {
    23  		m := test.m
    24  		n := test.n
    25  		a := NewDense(m, n, nil)
    26  		for i := 0; i < m; i++ {
    27  			for j := 0; j < n; j++ {
    28  				a.Set(i, j, rnd.NormFloat64())
    29  			}
    30  		}
    31  		var want Dense
    32  		want.CloneFrom(a)
    33  
    34  		var lq LQ
    35  		lq.Factorize(a)
    36  
    37  		if !EqualApprox(a, &lq, tol) {
    38  			t.Errorf("case %d: A and LQ are not equal", cas)
    39  		}
    40  
    41  		var l, q Dense
    42  		lq.QTo(&q)
    43  
    44  		if !isOrthonormal(&q, tol) {
    45  			t.Errorf("Q is not orthonormal: m = %v, n = %v", m, n)
    46  		}
    47  
    48  		lq.LTo(&l)
    49  
    50  		var got Dense
    51  		got.Mul(&l, &q)
    52  		if !EqualApprox(&got, &want, tol) {
    53  			t.Errorf("LQ does not equal original matrix. \nWant: %v\nGot: %v", want, got)
    54  		}
    55  	}
    56  }
    57  
    58  func TestLQSolveTo(t *testing.T) {
    59  	t.Parallel()
    60  	rnd := rand.New(rand.NewSource(1))
    61  	for _, trans := range []bool{false, true} {
    62  		for _, test := range []struct {
    63  			m, n, bc int
    64  		}{
    65  			{5, 5, 1},
    66  			{5, 10, 1},
    67  			{5, 5, 3},
    68  			{5, 10, 3},
    69  		} {
    70  			m := test.m
    71  			n := test.n
    72  			bc := test.bc
    73  			a := NewDense(m, n, nil)
    74  			for i := 0; i < m; i++ {
    75  				for j := 0; j < n; j++ {
    76  					a.Set(i, j, rnd.Float64())
    77  				}
    78  			}
    79  			br := m
    80  			if trans {
    81  				br = n
    82  			}
    83  			b := NewDense(br, bc, nil)
    84  			for i := 0; i < br; i++ {
    85  				for j := 0; j < bc; j++ {
    86  					b.Set(i, j, rnd.Float64())
    87  				}
    88  			}
    89  			var x Dense
    90  			lq := &LQ{}
    91  			lq.Factorize(a)
    92  			err := lq.SolveTo(&x, trans, b)
    93  			if err != nil {
    94  				t.Errorf("unexpected error from LQ solve: %v", err)
    95  			}
    96  
    97  			// Test that the normal equations hold.
    98  			// Aᵀ * A * x = Aᵀ * b if !trans
    99  			// A * Aᵀ * x = A * b if trans
   100  			var lhs Dense
   101  			var rhs Dense
   102  			if trans {
   103  				var tmp Dense
   104  				tmp.Mul(a, a.T())
   105  				lhs.Mul(&tmp, &x)
   106  				rhs.Mul(a, b)
   107  			} else {
   108  				var tmp Dense
   109  				tmp.Mul(a.T(), a)
   110  				lhs.Mul(&tmp, &x)
   111  				rhs.Mul(a.T(), b)
   112  			}
   113  			if !EqualApprox(&lhs, &rhs, 1e-10) {
   114  				t.Errorf("Normal equations do not hold.\nLHS: %v\n, RHS: %v\n", lhs, rhs)
   115  			}
   116  		}
   117  	}
   118  	// TODO(btracey): Add in testOneInput when it exists.
   119  }
   120  
   121  func TestLQSolveToVec(t *testing.T) {
   122  	t.Parallel()
   123  	rnd := rand.New(rand.NewSource(1))
   124  	for _, trans := range []bool{false, true} {
   125  		for _, test := range []struct {
   126  			m, n int
   127  		}{
   128  			{5, 5},
   129  			{5, 10},
   130  		} {
   131  			m := test.m
   132  			n := test.n
   133  			a := NewDense(m, n, nil)
   134  			for i := 0; i < m; i++ {
   135  				for j := 0; j < n; j++ {
   136  					a.Set(i, j, rnd.Float64())
   137  				}
   138  			}
   139  			br := m
   140  			if trans {
   141  				br = n
   142  			}
   143  			b := NewVecDense(br, nil)
   144  			for i := 0; i < br; i++ {
   145  				b.SetVec(i, rnd.Float64())
   146  			}
   147  			var x VecDense
   148  			lq := &LQ{}
   149  			lq.Factorize(a)
   150  			err := lq.SolveVecTo(&x, trans, b)
   151  			if err != nil {
   152  				t.Errorf("unexpected error from LQ solve: %v", err)
   153  			}
   154  
   155  			// Test that the normal equations hold.
   156  			// Aᵀ * A * x = Aᵀ * b if !trans
   157  			// A * Aᵀ * x = A * b if trans
   158  			var lhs Dense
   159  			var rhs Dense
   160  			if trans {
   161  				var tmp Dense
   162  				tmp.Mul(a, a.T())
   163  				lhs.Mul(&tmp, &x)
   164  				rhs.Mul(a, b)
   165  			} else {
   166  				var tmp Dense
   167  				tmp.Mul(a.T(), a)
   168  				lhs.Mul(&tmp, &x)
   169  				rhs.Mul(a.T(), b)
   170  			}
   171  			if !EqualApprox(&lhs, &rhs, 1e-10) {
   172  				t.Errorf("Normal equations do not hold.\nLHS: %v\n, RHS: %v\n", lhs, rhs)
   173  			}
   174  		}
   175  	}
   176  	// TODO(btracey): Add in testOneInput when it exists.
   177  }
   178  
   179  func TestLQSolveToCond(t *testing.T) {
   180  	t.Parallel()
   181  	for _, test := range []*Dense{
   182  		NewDense(2, 2, []float64{1, 0, 0, 1e-20}),
   183  		NewDense(2, 3, []float64{1, 0, 0, 0, 1e-20, 0}),
   184  	} {
   185  		m, _ := test.Dims()
   186  		var lq LQ
   187  		lq.Factorize(test)
   188  		b := NewDense(m, 2, nil)
   189  		var x Dense
   190  		if err := lq.SolveTo(&x, false, b); err == nil {
   191  			t.Error("No error for near-singular matrix in matrix solve.")
   192  		}
   193  
   194  		bvec := NewVecDense(m, nil)
   195  		var xvec VecDense
   196  		if err := lq.SolveVecTo(&xvec, false, bvec); err == nil {
   197  			t.Error("No error for near-singular matrix in matrix solve.")
   198  		}
   199  	}
   200  }