github.com/gonum/matrix@v0.0.0-20181209220409-c518dec07be9/mat64/hogsvd.go (about)

     1  // Copyright ©2017 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 mat64
     6  
     7  import (
     8  	"errors"
     9  
    10  	"github.com/gonum/blas/blas64"
    11  	"github.com/gonum/matrix"
    12  )
    13  
    14  // HOGSVD is a type for creating and using the Higher Order Generalized Singular Value
    15  // Decomposition (HOGSVD) of a set of matrices.
    16  //
    17  // The factorization is a linear transformation of the data sets from the given
    18  // variable×sample spaces to reduced and diagonalized "eigenvariable"×"eigensample"
    19  // spaces.
    20  type HOGSVD struct {
    21  	n int
    22  	v *Dense
    23  	b []Dense
    24  
    25  	err error
    26  }
    27  
    28  // Factorize computes the higher order generalized singular value decomposition (HOGSVD)
    29  // of the n input r_i×c column tall matrices in m. HOGSV extends the GSVD case from 2 to n
    30  // input matrices.
    31  //
    32  //  M_0 = U_0 * Σ_0 * V^T
    33  //  M_1 = U_1 * Σ_1 * V^T
    34  //  .
    35  //  .
    36  //  .
    37  //  M_{n-1} = U_{n-1} * Σ_{n-1} * V^T
    38  //
    39  // where U_i are r_i×c matrices of singular vectors, Σ are c×c matrices singular values, and V
    40  // is a c×c matrix of singular vectors.
    41  //
    42  // Factorize returns whether the decomposition succeeded. If the decomposition
    43  // failed, routines that require a successful factorization will panic.
    44  func (gsvd *HOGSVD) Factorize(m ...Matrix) (ok bool) {
    45  	// Factorize performs the HOGSVD factorisation
    46  	// essentially as described by Ponnapalli et al.
    47  	// https://doi.org/10.1371/journal.pone.0028072
    48  
    49  	if len(m) < 2 {
    50  		panic("hogsvd: too few matrices")
    51  	}
    52  	gsvd.n = 0
    53  
    54  	r, c := m[0].Dims()
    55  	a := make([]Cholesky, len(m))
    56  	var ts SymDense
    57  	for i, d := range m {
    58  		rd, cd := d.Dims()
    59  		if rd < cd {
    60  			gsvd.err = matrix.ErrShape
    61  			return false
    62  		}
    63  		if rd > r {
    64  			r = rd
    65  		}
    66  		if cd != c {
    67  			panic(matrix.ErrShape)
    68  		}
    69  		ts.Reset()
    70  		ts.SymOuterK(1, d.T())
    71  		ok = a[i].Factorize(&ts)
    72  		if !ok {
    73  			gsvd.err = errors.New("hogsvd: cholesky decomposition failed")
    74  			return false
    75  		}
    76  	}
    77  
    78  	s := getWorkspace(c, c, true)
    79  	defer putWorkspace(s)
    80  	sij := getWorkspace(c, c, false)
    81  	defer putWorkspace(sij)
    82  	for i, ai := range a {
    83  		for _, aj := range a[i+1:] {
    84  			gsvd.err = sij.solveTwoChol(&ai, &aj)
    85  			if gsvd.err != nil {
    86  				return false
    87  			}
    88  			s.Add(s, sij)
    89  
    90  			gsvd.err = sij.solveTwoChol(&aj, &ai)
    91  			if gsvd.err != nil {
    92  				return false
    93  			}
    94  			s.Add(s, sij)
    95  		}
    96  	}
    97  	s.Scale(1/float64(len(m)*(len(m)-1)), s)
    98  
    99  	var eig Eigen
   100  	ok = eig.Factorize(s.T(), false, true)
   101  	if !ok {
   102  		gsvd.err = errors.New("hogsvd: eigen decomposition failed")
   103  		return false
   104  	}
   105  	v := eig.Vectors()
   106  	for j := 0; j < c; j++ {
   107  		cv := v.ColView(j)
   108  		cv.ScaleVec(1/blas64.Nrm2(c, cv.mat), cv)
   109  	}
   110  
   111  	b := make([]Dense, len(m))
   112  	biT := getWorkspace(c, r, false)
   113  	defer putWorkspace(biT)
   114  	for i, d := range m {
   115  		// All calls to reset will leave a zeroed
   116  		// matrix with capacity to store the result
   117  		// without additional allocation.
   118  		biT.Reset()
   119  		gsvd.err = biT.Solve(v, d.T())
   120  		if gsvd.err != nil {
   121  			return false
   122  		}
   123  		b[i].Clone(biT.T())
   124  	}
   125  
   126  	gsvd.n = len(m)
   127  	gsvd.v = v
   128  	gsvd.b = b
   129  	return true
   130  }
   131  
   132  // Err returns the reason for a factorization failure.
   133  func (gsvd *HOGSVD) Err() error {
   134  	return gsvd.err
   135  }
   136  
   137  // Len returns the number of matrices that have been factorized. If Len returns
   138  // zero, the factorization was not successful.
   139  func (gsvd *HOGSVD) Len() int {
   140  	return gsvd.n
   141  }
   142  
   143  // UFromHOGSVD extracts the matrix U_n from the singular value decomposition, storing
   144  // the result in-place into the receiver. U_n is size r×c.
   145  //
   146  // UFromHOGSVD will panic if the receiver does not contain a successful factorization.
   147  func (m *Dense) UFromHOGSVD(gsvd *HOGSVD, n int) {
   148  	if gsvd.n == 0 {
   149  		panic("hogsvd: unsuccessful factorization")
   150  	}
   151  	if n < 0 || gsvd.n <= n {
   152  		panic("hogsvd: invalid index")
   153  	}
   154  
   155  	m.reuseAs(gsvd.b[n].Dims())
   156  	m.Copy(&gsvd.b[n])
   157  	for j, f := range gsvd.Values(nil, n) {
   158  		v := m.ColView(j)
   159  		v.ScaleVec(1/f, v)
   160  	}
   161  }
   162  
   163  // Values returns the nth set of singular values of the factorized system.
   164  // If the input slice is non-nil, the values will be stored in-place into the slice.
   165  // In this case, the slice must have length c, and Values will panic with
   166  // matrix.ErrSliceLengthMismatch otherwise. If the input slice is nil,
   167  // a new slice of the appropriate length will be allocated and returned.
   168  //
   169  // Values will panic if the receiver does not contain a successful factorization.
   170  func (gsvd *HOGSVD) Values(s []float64, n int) []float64 {
   171  	if gsvd.n == 0 {
   172  		panic("hogsvd: unsuccessful factorization")
   173  	}
   174  	if n < 0 || gsvd.n <= n {
   175  		panic("hogsvd: invalid index")
   176  	}
   177  
   178  	r, c := gsvd.b[n].Dims()
   179  	if s == nil {
   180  		s = make([]float64, c)
   181  	} else if len(s) != c {
   182  		panic(matrix.ErrSliceLengthMismatch)
   183  	}
   184  	for j := 0; j < c; j++ {
   185  		s[j] = blas64.Nrm2(r, gsvd.b[n].ColView(j).mat)
   186  	}
   187  	return s
   188  }
   189  
   190  // VFromHOGSVD extracts the matrix V from the singular value decomposition, storing
   191  // the result in-place into the receiver. V is size c×c.
   192  //
   193  // VFromHOGSVD will panic if the receiver does not contain a successful factorization.
   194  func (m *Dense) VFromHOGSVD(gsvd *HOGSVD) {
   195  	if gsvd.n == 0 {
   196  		panic("hogsvd: unsuccessful factorization")
   197  	}
   198  	*m = *DenseCopyOf(gsvd.v)
   199  }