| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004 |
- // Copyright ©2013 The Gonum Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package mat
- import (
- "math"
- "gonum.org/v1/gonum/blas"
- "gonum.org/v1/gonum/blas/blas64"
- "gonum.org/v1/gonum/floats"
- "gonum.org/v1/gonum/lapack"
- "gonum.org/v1/gonum/lapack/lapack64"
- )
- // Matrix is the basic matrix interface type.
- type Matrix interface {
- // Dims returns the dimensions of a Matrix.
- Dims() (r, c int)
- // At returns the value of a matrix element at row i, column j.
- // It will panic if i or j are out of bounds for the matrix.
- At(i, j int) float64
- // T returns the transpose of the Matrix. Whether T returns a copy of the
- // underlying data is implementation dependent.
- // This method may be implemented using the Transpose type, which
- // provides an implicit matrix transpose.
- T() Matrix
- }
- // allMatrix represents the extra set of methods that all mat Matrix types
- // should satisfy. This is used to enforce compile-time consistency between the
- // Dense types, especially helpful when adding new features.
- type allMatrix interface {
- Reseter
- IsEmpty() bool
- Zero()
- }
- // denseMatrix represents the extra set of methods that all Dense Matrix types
- // should satisfy. This is used to enforce compile-time consistency between the
- // Dense types, especially helpful when adding new features.
- type denseMatrix interface {
- DiagView() Diagonal
- Tracer
- }
- var (
- _ Matrix = Transpose{}
- _ Untransposer = Transpose{}
- )
- // Transpose is a type for performing an implicit matrix transpose. It implements
- // the Matrix interface, returning values from the transpose of the matrix within.
- type Transpose struct {
- Matrix Matrix
- }
- // At returns the value of the element at row i and column j of the transposed
- // matrix, that is, row j and column i of the Matrix field.
- func (t Transpose) At(i, j int) float64 {
- return t.Matrix.At(j, i)
- }
- // Dims returns the dimensions of the transposed matrix. The number of rows returned
- // is the number of columns in the Matrix field, and the number of columns is
- // the number of rows in the Matrix field.
- func (t Transpose) Dims() (r, c int) {
- c, r = t.Matrix.Dims()
- return r, c
- }
- // T performs an implicit transpose by returning the Matrix field.
- func (t Transpose) T() Matrix {
- return t.Matrix
- }
- // Untranspose returns the Matrix field.
- func (t Transpose) Untranspose() Matrix {
- return t.Matrix
- }
- // Untransposer is a type that can undo an implicit transpose.
- type Untransposer interface {
- // Note: This interface is needed to unify all of the Transpose types. In
- // the mat methods, we need to test if the Matrix has been implicitly
- // transposed. If this is checked by testing for the specific Transpose type
- // then the behavior will be different if the user uses T() or TTri() for a
- // triangular matrix.
- // Untranspose returns the underlying Matrix stored for the implicit transpose.
- Untranspose() Matrix
- }
- // UntransposeBander is a type that can undo an implicit band transpose.
- type UntransposeBander interface {
- // Untranspose returns the underlying Banded stored for the implicit transpose.
- UntransposeBand() Banded
- }
- // UntransposeTrier is a type that can undo an implicit triangular transpose.
- type UntransposeTrier interface {
- // Untranspose returns the underlying Triangular stored for the implicit transpose.
- UntransposeTri() Triangular
- }
- // UntransposeTriBander is a type that can undo an implicit triangular banded
- // transpose.
- type UntransposeTriBander interface {
- // Untranspose returns the underlying Triangular stored for the implicit transpose.
- UntransposeTriBand() TriBanded
- }
- // Mutable is a matrix interface type that allows elements to be altered.
- type Mutable interface {
- // Set alters the matrix element at row i, column j to v.
- // It will panic if i or j are out of bounds for the matrix.
- Set(i, j int, v float64)
- Matrix
- }
- // A RowViewer can return a Vector reflecting a row that is backed by the matrix
- // data. The Vector returned will have length equal to the number of columns.
- type RowViewer interface {
- RowView(i int) Vector
- }
- // A RawRowViewer can return a slice of float64 reflecting a row that is backed by the matrix
- // data.
- type RawRowViewer interface {
- RawRowView(i int) []float64
- }
- // A ColViewer can return a Vector reflecting a column that is backed by the matrix
- // data. The Vector returned will have length equal to the number of rows.
- type ColViewer interface {
- ColView(j int) Vector
- }
- // A RawColViewer can return a slice of float64 reflecting a column that is backed by the matrix
- // data.
- type RawColViewer interface {
- RawColView(j int) []float64
- }
- // A ClonerFrom can make a copy of a into the receiver, overwriting the previous value of the
- // receiver. The clone operation does not make any restriction on shape and will not cause
- // shadowing.
- type ClonerFrom interface {
- CloneFrom(a Matrix)
- }
- // A Reseter can reset the matrix so that it can be reused as the receiver of a dimensionally
- // restricted operation. This is commonly used when the matrix is being used as a workspace
- // or temporary matrix.
- //
- // If the matrix is a view, using Reset may result in data corruption in elements outside
- // the view. Similarly, if the matrix shares backing data with another variable, using
- // Reset may lead to unexpected changes in data values.
- type Reseter interface {
- Reset()
- }
- // A Copier can make a copy of elements of a into the receiver. The submatrix copied
- // starts at row and column 0 and has dimensions equal to the minimum dimensions of
- // the two matrices. The number of row and columns copied is returned.
- // Copy will copy from a source that aliases the receiver unless the source is transposed;
- // an aliasing transpose copy will panic with the exception for a special case when
- // the source data has a unitary increment or stride.
- type Copier interface {
- Copy(a Matrix) (r, c int)
- }
- // A Grower can grow the size of the represented matrix by the given number of rows and columns.
- // Growing beyond the size given by the Caps method will result in the allocation of a new
- // matrix and copying of the elements. If Grow is called with negative increments it will
- // panic with ErrIndexOutOfRange.
- type Grower interface {
- Caps() (r, c int)
- Grow(r, c int) Matrix
- }
- // A BandWidther represents a banded matrix and can return the left and right half-bandwidths, k1 and
- // k2.
- type BandWidther interface {
- BandWidth() (k1, k2 int)
- }
- // A RawMatrixSetter can set the underlying blas64.General used by the receiver. There is no restriction
- // on the shape of the receiver. Changes to the receiver's elements will be reflected in the blas64.General.Data.
- type RawMatrixSetter interface {
- SetRawMatrix(a blas64.General)
- }
- // A RawMatrixer can return a blas64.General representation of the receiver. Changes to the blas64.General.Data
- // slice will be reflected in the original matrix, changes to the Rows, Cols and Stride fields will not.
- type RawMatrixer interface {
- RawMatrix() blas64.General
- }
- // A RawVectorer can return a blas64.Vector representation of the receiver. Changes to the blas64.Vector.Data
- // slice will be reflected in the original matrix, changes to the Inc field will not.
- type RawVectorer interface {
- RawVector() blas64.Vector
- }
- // A NonZeroDoer can call a function for each non-zero element of the receiver.
- // The parameters of the function are the element indices and its value.
- type NonZeroDoer interface {
- DoNonZero(func(i, j int, v float64))
- }
- // A RowNonZeroDoer can call a function for each non-zero element of a row of the receiver.
- // The parameters of the function are the element indices and its value.
- type RowNonZeroDoer interface {
- DoRowNonZero(i int, fn func(i, j int, v float64))
- }
- // A ColNonZeroDoer can call a function for each non-zero element of a column of the receiver.
- // The parameters of the function are the element indices and its value.
- type ColNonZeroDoer interface {
- DoColNonZero(j int, fn func(i, j int, v float64))
- }
- // untranspose untransposes a matrix if applicable. If a is an Untransposer, then
- // untranspose returns the underlying matrix and true. If it is not, then it returns
- // the input matrix and false.
- func untranspose(a Matrix) (Matrix, bool) {
- if ut, ok := a.(Untransposer); ok {
- return ut.Untranspose(), true
- }
- return a, false
- }
- // untransposeExtract returns an untransposed matrix in a built-in matrix type.
- //
- // The untransposed matrix is returned unaltered if it is a built-in matrix type.
- // Otherwise, if it implements a Raw method, an appropriate built-in type value
- // is returned holding the raw matrix value of the input. If neither of these
- // is possible, the untransposed matrix is returned.
- func untransposeExtract(a Matrix) (Matrix, bool) {
- ut, trans := untranspose(a)
- switch m := ut.(type) {
- case *DiagDense, *SymBandDense, *TriBandDense, *BandDense, *TriDense, *SymDense, *Dense:
- return m, trans
- // TODO(btracey): Add here if we ever have an equivalent of RawDiagDense.
- case RawSymBander:
- rsb := m.RawSymBand()
- if rsb.Uplo != blas.Upper {
- return ut, trans
- }
- var sb SymBandDense
- sb.SetRawSymBand(rsb)
- return &sb, trans
- case RawTriBander:
- rtb := m.RawTriBand()
- if rtb.Diag == blas.Unit {
- return ut, trans
- }
- var tb TriBandDense
- tb.SetRawTriBand(rtb)
- return &tb, trans
- case RawBander:
- var b BandDense
- b.SetRawBand(m.RawBand())
- return &b, trans
- case RawTriangular:
- rt := m.RawTriangular()
- if rt.Diag == blas.Unit {
- return ut, trans
- }
- var t TriDense
- t.SetRawTriangular(rt)
- return &t, trans
- case RawSymmetricer:
- rs := m.RawSymmetric()
- if rs.Uplo != blas.Upper {
- return ut, trans
- }
- var s SymDense
- s.SetRawSymmetric(rs)
- return &s, trans
- case RawMatrixer:
- var d Dense
- d.SetRawMatrix(m.RawMatrix())
- return &d, trans
- default:
- return ut, trans
- }
- }
- // TODO(btracey): Consider adding CopyCol/CopyRow if the behavior seems useful.
- // TODO(btracey): Add in fast paths to Row/Col for the other concrete types
- // (TriDense, etc.) as well as relevant interfaces (RowColer, RawRowViewer, etc.)
- // Col copies the elements in the jth column of the matrix into the slice dst.
- // The length of the provided slice must equal the number of rows, unless the
- // slice is nil in which case a new slice is first allocated.
- func Col(dst []float64, j int, a Matrix) []float64 {
- r, c := a.Dims()
- if j < 0 || j >= c {
- panic(ErrColAccess)
- }
- if dst == nil {
- dst = make([]float64, r)
- } else {
- if len(dst) != r {
- panic(ErrColLength)
- }
- }
- aU, aTrans := untranspose(a)
- if rm, ok := aU.(RawMatrixer); ok {
- m := rm.RawMatrix()
- if aTrans {
- copy(dst, m.Data[j*m.Stride:j*m.Stride+m.Cols])
- return dst
- }
- blas64.Copy(blas64.Vector{N: r, Inc: m.Stride, Data: m.Data[j:]},
- blas64.Vector{N: r, Inc: 1, Data: dst},
- )
- return dst
- }
- for i := 0; i < r; i++ {
- dst[i] = a.At(i, j)
- }
- return dst
- }
- // Row copies the elements in the ith row of the matrix into the slice dst.
- // The length of the provided slice must equal the number of columns, unless the
- // slice is nil in which case a new slice is first allocated.
- func Row(dst []float64, i int, a Matrix) []float64 {
- r, c := a.Dims()
- if i < 0 || i >= r {
- panic(ErrColAccess)
- }
- if dst == nil {
- dst = make([]float64, c)
- } else {
- if len(dst) != c {
- panic(ErrRowLength)
- }
- }
- aU, aTrans := untranspose(a)
- if rm, ok := aU.(RawMatrixer); ok {
- m := rm.RawMatrix()
- if aTrans {
- blas64.Copy(blas64.Vector{N: c, Inc: m.Stride, Data: m.Data[i:]},
- blas64.Vector{N: c, Inc: 1, Data: dst},
- )
- return dst
- }
- copy(dst, m.Data[i*m.Stride:i*m.Stride+m.Cols])
- return dst
- }
- for j := 0; j < c; j++ {
- dst[j] = a.At(i, j)
- }
- return dst
- }
- // Cond returns the condition number of the given matrix under the given norm.
- // The condition number must be based on the 1-norm, 2-norm or ∞-norm.
- // Cond will panic with matrix.ErrShape if the matrix has zero size.
- //
- // BUG(btracey): The computation of the 1-norm and ∞-norm for non-square matrices
- // is innacurate, although is typically the right order of magnitude. See
- // https://github.com/xianyi/OpenBLAS/issues/636. While the value returned will
- // change with the resolution of this bug, the result from Cond will match the
- // condition number used internally.
- func Cond(a Matrix, norm float64) float64 {
- m, n := a.Dims()
- if m == 0 || n == 0 {
- panic(ErrShape)
- }
- var lnorm lapack.MatrixNorm
- switch norm {
- default:
- panic("mat: bad norm value")
- case 1:
- lnorm = lapack.MaxColumnSum
- case 2:
- var svd SVD
- ok := svd.Factorize(a, SVDNone)
- if !ok {
- return math.Inf(1)
- }
- return svd.Cond()
- case math.Inf(1):
- lnorm = lapack.MaxRowSum
- }
- if m == n {
- // Use the LU decomposition to compute the condition number.
- var lu LU
- lu.factorize(a, lnorm)
- return lu.Cond()
- }
- if m > n {
- // Use the QR factorization to compute the condition number.
- var qr QR
- qr.factorize(a, lnorm)
- return qr.Cond()
- }
- // Use the LQ factorization to compute the condition number.
- var lq LQ
- lq.factorize(a, lnorm)
- return lq.Cond()
- }
- // Det returns the determinant of the matrix a. In many expressions using LogDet
- // will be more numerically stable.
- func Det(a Matrix) float64 {
- det, sign := LogDet(a)
- return math.Exp(det) * sign
- }
- // Dot returns the sum of the element-wise product of a and b.
- // Dot panics if the matrix sizes are unequal.
- func Dot(a, b Vector) float64 {
- la := a.Len()
- lb := b.Len()
- if la != lb {
- panic(ErrShape)
- }
- if arv, ok := a.(RawVectorer); ok {
- if brv, ok := b.(RawVectorer); ok {
- return blas64.Dot(arv.RawVector(), brv.RawVector())
- }
- }
- var sum float64
- for i := 0; i < la; i++ {
- sum += a.At(i, 0) * b.At(i, 0)
- }
- return sum
- }
- // Equal returns whether the matrices a and b have the same size
- // and are element-wise equal.
- func Equal(a, b Matrix) bool {
- ar, ac := a.Dims()
- br, bc := b.Dims()
- if ar != br || ac != bc {
- return false
- }
- aU, aTrans := untranspose(a)
- bU, bTrans := untranspose(b)
- if rma, ok := aU.(RawMatrixer); ok {
- if rmb, ok := bU.(RawMatrixer); ok {
- ra := rma.RawMatrix()
- rb := rmb.RawMatrix()
- if aTrans == bTrans {
- for i := 0; i < ra.Rows; i++ {
- for j := 0; j < ra.Cols; j++ {
- if ra.Data[i*ra.Stride+j] != rb.Data[i*rb.Stride+j] {
- return false
- }
- }
- }
- return true
- }
- for i := 0; i < ra.Rows; i++ {
- for j := 0; j < ra.Cols; j++ {
- if ra.Data[i*ra.Stride+j] != rb.Data[j*rb.Stride+i] {
- return false
- }
- }
- }
- return true
- }
- }
- if rma, ok := aU.(RawSymmetricer); ok {
- if rmb, ok := bU.(RawSymmetricer); ok {
- ra := rma.RawSymmetric()
- rb := rmb.RawSymmetric()
- // Symmetric matrices are always upper and equal to their transpose.
- for i := 0; i < ra.N; i++ {
- for j := i; j < ra.N; j++ {
- if ra.Data[i*ra.Stride+j] != rb.Data[i*rb.Stride+j] {
- return false
- }
- }
- }
- return true
- }
- }
- if ra, ok := aU.(*VecDense); ok {
- if rb, ok := bU.(*VecDense); ok {
- // If the raw vectors are the same length they must either both be
- // transposed or both not transposed (or have length 1).
- for i := 0; i < ra.mat.N; i++ {
- if ra.mat.Data[i*ra.mat.Inc] != rb.mat.Data[i*rb.mat.Inc] {
- return false
- }
- }
- return true
- }
- }
- for i := 0; i < ar; i++ {
- for j := 0; j < ac; j++ {
- if a.At(i, j) != b.At(i, j) {
- return false
- }
- }
- }
- return true
- }
- // EqualApprox returns whether the matrices a and b have the same size and contain all equal
- // elements with tolerance for element-wise equality specified by epsilon. Matrices
- // with non-equal shapes are not equal.
- func EqualApprox(a, b Matrix, epsilon float64) bool {
- ar, ac := a.Dims()
- br, bc := b.Dims()
- if ar != br || ac != bc {
- return false
- }
- aU, aTrans := untranspose(a)
- bU, bTrans := untranspose(b)
- if rma, ok := aU.(RawMatrixer); ok {
- if rmb, ok := bU.(RawMatrixer); ok {
- ra := rma.RawMatrix()
- rb := rmb.RawMatrix()
- if aTrans == bTrans {
- for i := 0; i < ra.Rows; i++ {
- for j := 0; j < ra.Cols; j++ {
- if !floats.EqualWithinAbsOrRel(ra.Data[i*ra.Stride+j], rb.Data[i*rb.Stride+j], epsilon, epsilon) {
- return false
- }
- }
- }
- return true
- }
- for i := 0; i < ra.Rows; i++ {
- for j := 0; j < ra.Cols; j++ {
- if !floats.EqualWithinAbsOrRel(ra.Data[i*ra.Stride+j], rb.Data[j*rb.Stride+i], epsilon, epsilon) {
- return false
- }
- }
- }
- return true
- }
- }
- if rma, ok := aU.(RawSymmetricer); ok {
- if rmb, ok := bU.(RawSymmetricer); ok {
- ra := rma.RawSymmetric()
- rb := rmb.RawSymmetric()
- // Symmetric matrices are always upper and equal to their transpose.
- for i := 0; i < ra.N; i++ {
- for j := i; j < ra.N; j++ {
- if !floats.EqualWithinAbsOrRel(ra.Data[i*ra.Stride+j], rb.Data[i*rb.Stride+j], epsilon, epsilon) {
- return false
- }
- }
- }
- return true
- }
- }
- if ra, ok := aU.(*VecDense); ok {
- if rb, ok := bU.(*VecDense); ok {
- // If the raw vectors are the same length they must either both be
- // transposed or both not transposed (or have length 1).
- for i := 0; i < ra.mat.N; i++ {
- if !floats.EqualWithinAbsOrRel(ra.mat.Data[i*ra.mat.Inc], rb.mat.Data[i*rb.mat.Inc], epsilon, epsilon) {
- return false
- }
- }
- return true
- }
- }
- for i := 0; i < ar; i++ {
- for j := 0; j < ac; j++ {
- if !floats.EqualWithinAbsOrRel(a.At(i, j), b.At(i, j), epsilon, epsilon) {
- return false
- }
- }
- }
- return true
- }
- // LogDet returns the log of the determinant and the sign of the determinant
- // for the matrix that has been factorized. Numerical stability in product and
- // division expressions is generally improved by working in log space.
- func LogDet(a Matrix) (det float64, sign float64) {
- // TODO(btracey): Add specialized routines for TriDense, etc.
- var lu LU
- lu.Factorize(a)
- return lu.LogDet()
- }
- // Max returns the largest element value of the matrix A.
- // Max will panic with matrix.ErrShape if the matrix has zero size.
- func Max(a Matrix) float64 {
- r, c := a.Dims()
- if r == 0 || c == 0 {
- panic(ErrShape)
- }
- // Max(A) = Max(Aᵀ)
- aU, _ := untranspose(a)
- switch m := aU.(type) {
- case RawMatrixer:
- rm := m.RawMatrix()
- max := math.Inf(-1)
- for i := 0; i < rm.Rows; i++ {
- for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+rm.Cols] {
- if v > max {
- max = v
- }
- }
- }
- return max
- case RawTriangular:
- rm := m.RawTriangular()
- // The max of a triangular is at least 0 unless the size is 1.
- if rm.N == 1 {
- return rm.Data[0]
- }
- max := 0.0
- if rm.Uplo == blas.Upper {
- for i := 0; i < rm.N; i++ {
- for _, v := range rm.Data[i*rm.Stride+i : i*rm.Stride+rm.N] {
- if v > max {
- max = v
- }
- }
- }
- return max
- }
- for i := 0; i < rm.N; i++ {
- for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+i+1] {
- if v > max {
- max = v
- }
- }
- }
- return max
- case RawSymmetricer:
- rm := m.RawSymmetric()
- if rm.Uplo != blas.Upper {
- panic(badSymTriangle)
- }
- max := math.Inf(-1)
- for i := 0; i < rm.N; i++ {
- for _, v := range rm.Data[i*rm.Stride+i : i*rm.Stride+rm.N] {
- if v > max {
- max = v
- }
- }
- }
- return max
- default:
- r, c := aU.Dims()
- max := math.Inf(-1)
- for i := 0; i < r; i++ {
- for j := 0; j < c; j++ {
- v := aU.At(i, j)
- if v > max {
- max = v
- }
- }
- }
- return max
- }
- }
- // Min returns the smallest element value of the matrix A.
- // Min will panic with matrix.ErrShape if the matrix has zero size.
- func Min(a Matrix) float64 {
- r, c := a.Dims()
- if r == 0 || c == 0 {
- panic(ErrShape)
- }
- // Min(A) = Min(Aᵀ)
- aU, _ := untranspose(a)
- switch m := aU.(type) {
- case RawMatrixer:
- rm := m.RawMatrix()
- min := math.Inf(1)
- for i := 0; i < rm.Rows; i++ {
- for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+rm.Cols] {
- if v < min {
- min = v
- }
- }
- }
- return min
- case RawTriangular:
- rm := m.RawTriangular()
- // The min of a triangular is at most 0 unless the size is 1.
- if rm.N == 1 {
- return rm.Data[0]
- }
- min := 0.0
- if rm.Uplo == blas.Upper {
- for i := 0; i < rm.N; i++ {
- for _, v := range rm.Data[i*rm.Stride+i : i*rm.Stride+rm.N] {
- if v < min {
- min = v
- }
- }
- }
- return min
- }
- for i := 0; i < rm.N; i++ {
- for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+i+1] {
- if v < min {
- min = v
- }
- }
- }
- return min
- case RawSymmetricer:
- rm := m.RawSymmetric()
- if rm.Uplo != blas.Upper {
- panic(badSymTriangle)
- }
- min := math.Inf(1)
- for i := 0; i < rm.N; i++ {
- for _, v := range rm.Data[i*rm.Stride+i : i*rm.Stride+rm.N] {
- if v < min {
- min = v
- }
- }
- }
- return min
- default:
- r, c := aU.Dims()
- min := math.Inf(1)
- for i := 0; i < r; i++ {
- for j := 0; j < c; j++ {
- v := aU.At(i, j)
- if v < min {
- min = v
- }
- }
- }
- return min
- }
- }
- // Norm returns the specified (induced) norm of the matrix a. See
- // https://en.wikipedia.org/wiki/Matrix_norm for the definition of an induced norm.
- //
- // Valid norms are:
- // 1 - The maximum absolute column sum
- // 2 - Frobenius norm, the square root of the sum of the squares of the elements.
- // Inf - The maximum absolute row sum.
- // Norm will panic with ErrNormOrder if an illegal norm order is specified and
- // with matrix.ErrShape if the matrix has zero size.
- func Norm(a Matrix, norm float64) float64 {
- r, c := a.Dims()
- if r == 0 || c == 0 {
- panic(ErrShape)
- }
- aU, aTrans := untranspose(a)
- var work []float64
- switch rma := aU.(type) {
- case RawMatrixer:
- rm := rma.RawMatrix()
- n := normLapack(norm, aTrans)
- if n == lapack.MaxColumnSum {
- work = getFloats(rm.Cols, false)
- defer putFloats(work)
- }
- return lapack64.Lange(n, rm, work)
- case RawTriangular:
- rm := rma.RawTriangular()
- n := normLapack(norm, aTrans)
- if n == lapack.MaxRowSum || n == lapack.MaxColumnSum {
- work = getFloats(rm.N, false)
- defer putFloats(work)
- }
- return lapack64.Lantr(n, rm, work)
- case RawSymmetricer:
- rm := rma.RawSymmetric()
- n := normLapack(norm, aTrans)
- if n == lapack.MaxRowSum || n == lapack.MaxColumnSum {
- work = getFloats(rm.N, false)
- defer putFloats(work)
- }
- return lapack64.Lansy(n, rm, work)
- case *VecDense:
- rv := rma.RawVector()
- switch norm {
- default:
- panic(ErrNormOrder)
- case 1:
- if aTrans {
- imax := blas64.Iamax(rv)
- return math.Abs(rma.At(imax, 0))
- }
- return blas64.Asum(rv)
- case 2:
- return blas64.Nrm2(rv)
- case math.Inf(1):
- if aTrans {
- return blas64.Asum(rv)
- }
- imax := blas64.Iamax(rv)
- return math.Abs(rma.At(imax, 0))
- }
- }
- switch norm {
- default:
- panic(ErrNormOrder)
- case 1:
- var max float64
- for j := 0; j < c; j++ {
- var sum float64
- for i := 0; i < r; i++ {
- sum += math.Abs(a.At(i, j))
- }
- if sum > max {
- max = sum
- }
- }
- return max
- case 2:
- var sum float64
- for i := 0; i < r; i++ {
- for j := 0; j < c; j++ {
- v := a.At(i, j)
- sum += v * v
- }
- }
- return math.Sqrt(sum)
- case math.Inf(1):
- var max float64
- for i := 0; i < r; i++ {
- var sum float64
- for j := 0; j < c; j++ {
- sum += math.Abs(a.At(i, j))
- }
- if sum > max {
- max = sum
- }
- }
- return max
- }
- }
- // normLapack converts the float64 norm input in Norm to a lapack.MatrixNorm.
- func normLapack(norm float64, aTrans bool) lapack.MatrixNorm {
- switch norm {
- case 1:
- n := lapack.MaxColumnSum
- if aTrans {
- n = lapack.MaxRowSum
- }
- return n
- case 2:
- return lapack.Frobenius
- case math.Inf(1):
- n := lapack.MaxRowSum
- if aTrans {
- n = lapack.MaxColumnSum
- }
- return n
- default:
- panic(ErrNormOrder)
- }
- }
- // Sum returns the sum of the elements of the matrix.
- func Sum(a Matrix) float64 {
- var sum float64
- aU, _ := untranspose(a)
- switch rma := aU.(type) {
- case RawSymmetricer:
- rm := rma.RawSymmetric()
- for i := 0; i < rm.N; i++ {
- // Diagonals count once while off-diagonals count twice.
- sum += rm.Data[i*rm.Stride+i]
- var s float64
- for _, v := range rm.Data[i*rm.Stride+i+1 : i*rm.Stride+rm.N] {
- s += v
- }
- sum += 2 * s
- }
- return sum
- case RawTriangular:
- rm := rma.RawTriangular()
- var startIdx, endIdx int
- for i := 0; i < rm.N; i++ {
- // Start and end index for this triangle-row.
- switch rm.Uplo {
- case blas.Upper:
- startIdx = i
- endIdx = rm.N
- case blas.Lower:
- startIdx = 0
- endIdx = i + 1
- default:
- panic(badTriangle)
- }
- for _, v := range rm.Data[i*rm.Stride+startIdx : i*rm.Stride+endIdx] {
- sum += v
- }
- }
- return sum
- case RawMatrixer:
- rm := rma.RawMatrix()
- for i := 0; i < rm.Rows; i++ {
- for _, v := range rm.Data[i*rm.Stride : i*rm.Stride+rm.Cols] {
- sum += v
- }
- }
- return sum
- case *VecDense:
- rm := rma.RawVector()
- for i := 0; i < rm.N; i++ {
- sum += rm.Data[i*rm.Inc]
- }
- return sum
- default:
- r, c := a.Dims()
- for i := 0; i < r; i++ {
- for j := 0; j < c; j++ {
- sum += a.At(i, j)
- }
- }
- return sum
- }
- }
- // A Tracer can compute the trace of the matrix. Trace must panic if the
- // matrix is not square.
- type Tracer interface {
- Trace() float64
- }
- // Trace returns the trace of the matrix. Trace will panic if the
- // matrix is not square.
- func Trace(a Matrix) float64 {
- m, _ := untransposeExtract(a)
- if t, ok := m.(Tracer); ok {
- return t.Trace()
- }
- r, c := a.Dims()
- if r != c {
- panic(ErrSquare)
- }
- var v float64
- for i := 0; i < r; i++ {
- v += a.At(i, i)
- }
- return v
- }
- func min(a, b int) int {
- if a < b {
- return a
- }
- return b
- }
- func max(a, b int) int {
- if a > b {
- return a
- }
- return b
- }
- // use returns a float64 slice with l elements, using f if it
- // has the necessary capacity, otherwise creating a new slice.
- func use(f []float64, l int) []float64 {
- if l <= cap(f) {
- return f[:l]
- }
- return make([]float64, l)
- }
- // useZeroed returns a float64 slice with l elements, using f if it
- // has the necessary capacity, otherwise creating a new slice. The
- // elements of the returned slice are guaranteed to be zero.
- func useZeroed(f []float64, l int) []float64 {
- if l <= cap(f) {
- f = f[:l]
- zero(f)
- return f
- }
- return make([]float64, l)
- }
- // zero zeros the given slice's elements.
- func zero(f []float64) {
- for i := range f {
- f[i] = 0
- }
- }
- // useInt returns an int slice with l elements, using i if it
- // has the necessary capacity, otherwise creating a new slice.
- func useInt(i []int, l int) []int {
- if l <= cap(i) {
- return i[:l]
- }
- return make([]int, l)
- }
|