// 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 mat64 import ( "math" "github.com/gonum/blas" ) var blasEngine blas.Float64 func Register(b blas.Float64) { blasEngine = b } func Registered() blas.Float64 { return blasEngine } const BlasOrder = blas.RowMajor var ( matrix *Dense _ Matrix = matrix _ Mutable = matrix _ Vectorer = matrix _ VectorSetter = matrix _ Cloner = matrix _ Viewer = matrix _ Submatrixer = matrix _ RowViewer = matrix _ Adder = matrix _ Suber = matrix _ Muler = matrix _ Dotter = matrix _ ElemMuler = matrix _ Scaler = matrix _ Applyer = matrix _ TransposeCopier = matrix // _ TransposeViewer = matrix _ Tracer = matrix _ Normer = matrix _ Sumer = matrix _ Uer = matrix _ Ler = matrix _ Stacker = matrix _ Augmenter = matrix _ Equaler = matrix _ ApproxEqualer = matrix _ RawMatrixLoader = matrix _ RawMatrixer = matrix ) type Dense struct { mat RawMatrix } func NewDense(r, c int, mat []float64) *Dense { if mat != nil && r*c != len(mat) { panic(ErrShape) } if mat == nil { mat = make([]float64, r*c) } return &Dense{RawMatrix{ Order: BlasOrder, Rows: r, Cols: c, Stride: c, Data: mat, }} } // DenseCopyOf returns a newly allocated copy of the elements of a. func DenseCopyOf(a Matrix) *Dense { d := &Dense{} d.Clone(a) return d } func (m *Dense) LoadRawMatrix(b RawMatrix) { if b.Order != BlasOrder { panic(ErrIllegalOrder) } m.mat = b } func (m *Dense) RawMatrix() RawMatrix { return m.mat } func (m *Dense) isZero() bool { return m.mat.Cols == 0 || m.mat.Rows == 0 } func (m *Dense) At(r, c int) float64 { return m.mat.Data[r*m.mat.Stride+c] } func (m *Dense) Set(r, c int, v float64) { m.mat.Data[r*m.mat.Stride+c] = v } func (m *Dense) Dims() (r, c int) { return m.mat.Rows, m.mat.Cols } func (m *Dense) Col(col []float64, c int) []float64 { if c >= m.mat.Cols || c < 0 { panic(ErrIndexOutOfRange) } if col == nil { col = make([]float64, m.mat.Rows) } col = col[:min(len(col), m.mat.Rows)] if blasEngine == nil { panic(ErrNoEngine) } blasEngine.Dcopy(len(col), m.mat.Data[c:], m.mat.Stride, col, 1) return col } func (m *Dense) SetCol(c int, v []float64) int { if c >= m.mat.Cols || c < 0 { panic(ErrIndexOutOfRange) } if blasEngine == nil { panic(ErrNoEngine) } blasEngine.Dcopy(min(len(v), m.mat.Rows), v, 1, m.mat.Data[c:], m.mat.Stride) return min(len(v), m.mat.Rows) } func (m *Dense) Row(row []float64, r int) []float64 { if r >= m.mat.Rows || r < 0 { panic(ErrIndexOutOfRange) } if row == nil { row = make([]float64, m.mat.Cols) } copy(row, m.rowView(r)) return row } func (m *Dense) SetRow(r int, v []float64) int { if r >= m.mat.Rows || r < 0 { panic(ErrIndexOutOfRange) } copy(m.rowView(r), v) return min(len(v), m.mat.Cols) } func (m *Dense) RowView(r int) []float64 { if r >= m.mat.Rows || r < 0 { panic(ErrIndexOutOfRange) } return m.rowView(r) } func (m *Dense) rowView(r int) []float64 { return m.mat.Data[r*m.mat.Stride : r*m.mat.Stride+m.mat.Cols] } // View returns a view on the receiver. func (m *Dense) View(a Matrix, i, j, r, c int) { *m = *a.(*Dense) m.mat.Data = m.mat.Data[i*m.mat.Stride+j : (i+r-1)*m.mat.Stride+(j+c)] m.mat.Rows = r m.mat.Cols = c } func (m *Dense) Submatrix(a Matrix, i, j, r, c int) { // This is probably a bad idea, but for the moment, we do it. m.View(a, i, j, r, c) m.Clone(m) } func (m *Dense) Reset() { m.mat.Rows, m.mat.Cols = 0, 0 m.mat.Data = m.mat.Data[:0] } func (m *Dense) Clone(a Matrix) { r, c := a.Dims() m.mat = RawMatrix{ Order: BlasOrder, Rows: r, Cols: c, } data := make([]float64, r*c) switch a := a.(type) { case RawMatrixer: amat := a.RawMatrix() for i := 0; i < r; i++ { copy(data[i*c:(i+1)*c], amat.Data[i*amat.Stride:i*amat.Stride+c]) } m.mat.Stride = c m.mat.Data = data case Vectorer: for i := 0; i < r; i++ { a.Row(data[i*c:(i+1)*c], i) } m.mat.Stride = c m.mat.Data = data default: m.mat.Data = data for i := 0; i < r; i++ { for j := 0; j < c; j++ { m.Set(i, j, a.At(i, j)) } } } } func (m *Dense) Copy(a Matrix) (r, c int) { r, c = a.Dims() r = min(r, m.mat.Rows) c = min(c, m.mat.Cols) switch a := a.(type) { case RawMatrixer: amat := a.RawMatrix() for i := 0; i < r; i++ { copy(m.mat.Data[i*m.mat.Stride:i*m.mat.Stride+c], amat.Data[i*amat.Stride:i*amat.Stride+c]) } case Vectorer: for i := 0; i < r; i++ { a.Row(m.mat.Data[i*m.mat.Stride:i*m.mat.Stride+c], i) } default: for i := 0; i < r; i++ { for j := 0; j < c; j++ { m.Set(r, c, a.At(r, c)) } } } return r, c } func (m *Dense) Min() float64 { min := m.mat.Data[0] for k := 0; k < m.mat.Rows; k++ { for _, v := range m.rowView(k) { min = math.Min(min, v) } } return min } func (m *Dense) Max() float64 { max := m.mat.Data[0] for k := 0; k < m.mat.Rows; k++ { for _, v := range m.rowView(k) { max = math.Max(max, v) } } return max } func (m *Dense) Trace() float64 { if m.mat.Rows != m.mat.Cols { panic(ErrSquare) } var t float64 for i := 0; i < len(m.mat.Data); i += m.mat.Stride + 1 { t += m.mat.Data[i] } return t } var inf = math.Inf(1) const ( epsilon = 2.2204e-16 small = math.SmallestNonzeroFloat64 ) func (m *Dense) Norm(ord float64) float64 { var n float64 switch { case ord == 1: col := make([]float64, m.mat.Rows) for i := 0; i < m.mat.Cols; i++ { var s float64 for _, e := range m.Col(col, i) { s += e } n = math.Max(math.Abs(s), n) } case math.IsInf(ord, +1): row := make([]float64, m.mat.Cols) for i := 0; i < m.mat.Rows; i++ { var s float64 for _, e := range m.Row(row, i) { s += e } n = math.Max(math.Abs(s), n) } case ord == -1: n = math.MaxFloat64 col := make([]float64, m.mat.Rows) for i := 0; i < m.mat.Cols; i++ { var s float64 for _, e := range m.Col(col, i) { s += e } n = math.Min(math.Abs(s), n) } case math.IsInf(ord, -1): n = math.MaxFloat64 row := make([]float64, m.mat.Cols) for i := 0; i < m.mat.Rows; i++ { var s float64 for _, e := range m.Row(row, i) { s += e } n = math.Min(math.Abs(s), n) } case ord == 0: for i := 0; i < len(m.mat.Data); i += m.mat.Stride { for _, v := range m.mat.Data[i : i+m.mat.Cols] { n += v * v } } return math.Sqrt(n) case ord == 2, ord == -2: s := SVD(m, epsilon, small, false, false).Sigma if ord == 2 { return s[0] } return s[len(s)-1] default: panic(ErrNormOrder) } return n } func (m *Dense) Add(a, b Matrix) { ar, ac := a.Dims() br, bc := b.Dims() if ar != br || ac != bc { panic(ErrShape) } if m.isZero() { m.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: ac, Stride: ac, Data: use(m.mat.Data, ar*ac), } } else if ar != m.mat.Rows || ac != m.mat.Cols { panic(ErrShape) } if a, ok := a.(RawMatrixer); ok { if b, ok := b.(RawMatrixer); ok { amat, bmat := a.RawMatrix(), b.RawMatrix() for ja, jb, jm := 0, 0, 0; ja < ar*amat.Stride; ja, jb, jm = ja+amat.Stride, jb+bmat.Stride, jm+m.mat.Stride { for i, v := range amat.Data[ja : ja+ac] { m.mat.Data[i+jm] = v + bmat.Data[i+jb] } } return } } if a, ok := a.(Vectorer); ok { if b, ok := b.(Vectorer); ok { rowa := make([]float64, ac) rowb := make([]float64, bc) for r := 0; r < ar; r++ { a.Row(rowa, r) for i, v := range b.Row(rowb, r) { rowa[i] += v } copy(m.rowView(r), rowa) } return } } for r := 0; r < ar; r++ { for c := 0; c < ac; c++ { m.Set(r, c, a.At(r, c)+b.At(r, c)) } } } func (m *Dense) Sub(a, b Matrix) { ar, ac := a.Dims() br, bc := b.Dims() if ar != br || ac != bc { panic(ErrShape) } if m.isZero() { m.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: ac, Stride: ac, Data: use(m.mat.Data, ar*ac), } } else if ar != m.mat.Rows || ac != m.mat.Cols { panic(ErrShape) } if a, ok := a.(RawMatrixer); ok { if b, ok := b.(RawMatrixer); ok { amat, bmat := a.RawMatrix(), b.RawMatrix() for ja, jb, jm := 0, 0, 0; ja < ar*amat.Stride; ja, jb, jm = ja+amat.Stride, jb+bmat.Stride, jm+m.mat.Stride { for i, v := range amat.Data[ja : ja+ac] { m.mat.Data[i+jm] = v - bmat.Data[i+jb] } } return } } if a, ok := a.(Vectorer); ok { if b, ok := b.(Vectorer); ok { rowa := make([]float64, ac) rowb := make([]float64, bc) for r := 0; r < ar; r++ { a.Row(rowa, r) for i, v := range b.Row(rowb, r) { rowa[i] -= v } copy(m.rowView(r), rowa) } return } } for r := 0; r < ar; r++ { for c := 0; c < ac; c++ { m.Set(r, c, a.At(r, c)-b.At(r, c)) } } } func (m *Dense) MulElem(a, b Matrix) { ar, ac := a.Dims() br, bc := b.Dims() if ar != br || ac != bc { panic(ErrShape) } if m.isZero() { m.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: ac, Stride: ac, Data: use(m.mat.Data, ar*ac), } } else if ar != m.mat.Rows || ac != m.mat.Cols { panic(ErrShape) } if a, ok := a.(RawMatrixer); ok { if b, ok := b.(RawMatrixer); ok { amat, bmat := a.RawMatrix(), b.RawMatrix() for ja, jb, jm := 0, 0, 0; ja < ar*amat.Stride; ja, jb, jm = ja+amat.Stride, jb+bmat.Stride, jm+m.mat.Stride { for i, v := range amat.Data[ja : ja+ac] { m.mat.Data[i+jm] = v * bmat.Data[i+jb] } } return } } if a, ok := a.(Vectorer); ok { if b, ok := b.(Vectorer); ok { rowa := make([]float64, ac) rowb := make([]float64, bc) for r := 0; r < ar; r++ { a.Row(rowa, r) for i, v := range b.Row(rowb, r) { rowa[i] *= v } copy(m.rowView(r), rowa) } return } } for r := 0; r < ar; r++ { for c := 0; c < ac; c++ { m.Set(r, c, a.At(r, c)*b.At(r, c)) } } } func (m *Dense) Dot(b Matrix) float64 { mr, mc := m.Dims() br, bc := b.Dims() if mr != br || mc != bc { panic(ErrShape) } var d float64 if b, ok := b.(RawMatrixer); ok { bmat := b.RawMatrix() if m.mat.Order != BlasOrder || bmat.Order != BlasOrder { panic(ErrIllegalOrder) } for jm, jb := 0, 0; jm < mr*m.mat.Stride; jm, jb = jm+m.mat.Stride, jb+bmat.Stride { for i, v := range m.mat.Data[jm : jm+mc] { d += v * bmat.Data[i+jb] } } return d } if b, ok := b.(Vectorer); ok { row := make([]float64, bc) for r := 0; r < br; r++ { for i, v := range b.Row(row, r) { d += m.mat.Data[r*m.mat.Stride+i] * v } } return d } for r := 0; r < mr; r++ { for c := 0; c < mc; c++ { d += m.At(r, c) * b.At(r, c) } } return d } func (m *Dense) Mul(a, b Matrix) { ar, ac := a.Dims() br, bc := b.Dims() if ac != br { panic(ErrShape) } var w Dense if m != a && m != b { w = *m } if w.isZero() { w.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: bc, Stride: bc, Data: use(w.mat.Data, ar*bc), } } else if ar != w.mat.Rows || bc != w.mat.Cols { panic(ErrShape) } if a, ok := a.(RawMatrixer); ok { if b, ok := b.(RawMatrixer); ok { amat, bmat := a.RawMatrix(), b.RawMatrix() if blasEngine == nil { panic(ErrNoEngine) } blasEngine.Dgemm( BlasOrder, blas.NoTrans, blas.NoTrans, ar, bc, ac, 1., amat.Data, amat.Stride, bmat.Data, bmat.Stride, 0., w.mat.Data, w.mat.Stride) *m = w return } } if a, ok := a.(Vectorer); ok { if b, ok := b.(Vectorer); ok { row := make([]float64, ac) col := make([]float64, br) if blasEngine == nil { panic(ErrNoEngine) } for r := 0; r < ar; r++ { for c := 0; c < bc; c++ { w.mat.Data[r*w.mat.Stride+w.mat.Cols] = blasEngine.Ddot(ac, a.Row(row, r), 1, b.Col(col, c), 1) } } *m = w return } } row := make([]float64, ac) for r := 0; r < ar; r++ { for i := range row { row[i] = a.At(r, i) } for c := 0; c < bc; c++ { var v float64 for i, e := range row { v += e * b.At(i, c) } w.mat.Data[r*w.mat.Stride+w.mat.Cols] = v } } *m = w } func (m *Dense) Scale(f float64, a Matrix) { ar, ac := a.Dims() if m.isZero() { m.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: ac, Stride: ac, Data: use(m.mat.Data, ar*ac), } } else if ar != m.mat.Rows || ac != m.mat.Cols { panic(ErrShape) } if a, ok := a.(RawMatrixer); ok { amat := a.RawMatrix() for ja, jm := 0, 0; ja < ar*amat.Stride; ja, jm = ja+amat.Stride, jm+m.mat.Stride { for i, v := range amat.Data[ja : ja+ac] { m.mat.Data[i+jm] = v * f } } return } if a, ok := a.(Vectorer); ok { row := make([]float64, ac) for r := 0; r < ar; r++ { for i, v := range a.Row(row, r) { row[i] = f * v } copy(m.rowView(r), row) } return } for r := 0; r < ar; r++ { for c := 0; c < ac; c++ { m.Set(r, c, f*a.At(r, c)) } } } func (m *Dense) Apply(f ApplyFunc, a Matrix) { ar, ac := a.Dims() if m.isZero() { m.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: ac, Stride: ac, Data: use(m.mat.Data, ar*ac), } } else if ar != m.mat.Rows || ac != m.mat.Cols { panic(ErrShape) } if a, ok := a.(RawMatrixer); ok { amat := a.RawMatrix() for j, ja, jm := 0, 0, 0; ja < ar*amat.Stride; j, ja, jm = j+1, ja+amat.Stride, jm+m.mat.Stride { for i, v := range amat.Data[ja : ja+ac] { m.mat.Data[i+jm] = f(j, i, v) } } return } if a, ok := a.(Vectorer); ok { row := make([]float64, ac) for r := 0; r < ar; r++ { for i, v := range a.Row(row, r) { row[i] = f(r, i, v) } copy(m.rowView(r), row) } return } for r := 0; r < ar; r++ { for c := 0; c < ac; c++ { m.Set(r, c, f(r, c, a.At(r, c))) } } } func zero(f []float64) { f[0] = 0 for i := 1; i < len(f); { i += copy(f[i:], f[:i]) } } func (m *Dense) U(a Matrix) { ar, ac := a.Dims() if ar != ac { panic(ErrSquare) } switch { case m == a: m.zeroLower() return case m.isZero(): m.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: ac, Stride: ac, Data: use(m.mat.Data, ar*ac), } case ar != m.mat.Rows || ac != m.mat.Cols: panic(ErrShape) } if a, ok := a.(RawMatrixer); ok { amat := a.RawMatrix() copy(m.mat.Data[:ac], amat.Data[:ac]) for j, ja, jm := 1, amat.Stride, m.mat.Stride; ja < ar*amat.Stride; j, ja, jm = j+1, ja+amat.Stride, jm+m.mat.Stride { zero(m.mat.Data[jm : jm+j]) copy(m.mat.Data[jm+j:jm+ac], amat.Data[ja+j:ja+ac]) } return } if a, ok := a.(Vectorer); ok { row := make([]float64, ac) copy(m.mat.Data[:m.mat.Cols], a.Row(row, 0)) for r := 1; r < ar; r++ { zero(m.mat.Data[r*m.mat.Stride : r*(m.mat.Stride+1)]) copy(m.mat.Data[r*(m.mat.Stride+1):r*m.mat.Stride+m.mat.Cols], a.Row(row, r)) } return } m.zeroLower() for r := 0; r < ar; r++ { for c := r; c < ac; c++ { m.Set(r, c, a.At(r, c)) } } } func (m *Dense) zeroLower() { for i := 1; i < m.mat.Rows; i++ { zero(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+i]) } } func (m *Dense) L(a Matrix) { ar, ac := a.Dims() if ar != ac { panic(ErrSquare) } switch { case m == a: m.zeroUpper() return case m.isZero(): m.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: ac, Stride: ac, Data: use(m.mat.Data, ar*ac), } case ar != m.mat.Rows || ac != m.mat.Cols: panic(ErrShape) } if a, ok := a.(RawMatrixer); ok { amat := a.RawMatrix() copy(m.mat.Data[:ar], amat.Data[:ar]) for j, ja, jm := 1, amat.Stride, m.mat.Stride; ja < ac*amat.Stride; j, ja, jm = j+1, ja+amat.Stride, jm+m.mat.Stride { zero(m.mat.Data[jm : jm+j]) copy(m.mat.Data[jm+j:jm+ar], amat.Data[ja+j:ja+ar]) } return } if a, ok := a.(Vectorer); ok { row := make([]float64, ac) for r := 0; r < ar; r++ { a.Row(row[:r+1], r) m.SetRow(r, row) } return } m.zeroUpper() for c := 0; c < ac; c++ { for r := c; r < ar; r++ { m.Set(r, c, a.At(r, c)) } } } func (m *Dense) zeroUpper() { for i := 0; i < m.mat.Rows-1; i++ { zero(m.mat.Data[i*m.mat.Stride+i+1 : (i+1)*m.mat.Stride]) } } func (m *Dense) TCopy(a Matrix) { ar, ac := a.Dims() var w Dense if m != a { w = *m } if w.isZero() { w.mat = RawMatrix{ Order: BlasOrder, Rows: ac, Cols: ar, Data: use(w.mat.Data, ar*ac), } w.mat.Stride = ar } else if ar != m.mat.Cols || ac != m.mat.Rows { panic(ErrShape) } switch a := a.(type) { case *Dense: for i := 0; i < ac; i++ { for j := 0; j < ar; j++ { w.Set(i, j, a.At(j, i)) } } default: for i := 0; i < ac; i++ { for j := 0; j < ar; j++ { w.Set(i, j, a.At(j, i)) } } } *m = w } func (m *Dense) Stack(a, b Matrix) { ar, ac := a.Dims() br, bc := b.Dims() if ac != bc || m == a || m == b { panic(ErrShape) } if m.isZero() { m.mat = RawMatrix{ Order: BlasOrder, Rows: ar + br, Cols: ac, Stride: ac, Data: use(m.mat.Data, (ar+br)*ac), } } else if ar+br != m.mat.Rows || ac != m.mat.Cols { panic(ErrShape) } m.Copy(a) var w Dense w.View(m, ar, 0, br, bc) w.Copy(b) } func (m *Dense) Augment(a, b Matrix) { ar, ac := a.Dims() br, bc := b.Dims() if ar != br || m == a || m == b { panic(ErrShape) } if m.isZero() { m.mat = RawMatrix{ Order: BlasOrder, Rows: ar, Cols: ac + bc, Stride: ac + bc, Data: use(m.mat.Data, ar*(ac+bc)), } } else if ar != m.mat.Rows || ac+bc != m.mat.Cols { panic(ErrShape) } m.Copy(a) var w Dense w.View(m, 0, ac, br, bc) w.Copy(b) } func (m *Dense) Sum() float64 { l := m.mat.Cols var s float64 for i := 0; i < len(m.mat.Data); i += m.mat.Stride { for _, v := range m.mat.Data[i : i+l] { s += v } } return s } func (m *Dense) Equals(b Matrix) bool { br, bc := b.Dims() if br != m.mat.Rows || bc != m.mat.Cols { return false } if b, ok := b.(RawMatrixer); ok { bmat := b.RawMatrix() for jb, jm := 0, 0; jm < br*m.mat.Stride; jb, jm = jb+bmat.Stride, jm+m.mat.Stride { for i, v := range m.mat.Data[jm : jm+bc] { if v != bmat.Data[i+jb] { return false } } } return true } if b, ok := b.(Vectorer); ok { rowb := make([]float64, bc) for r := 0; r < br; r++ { rowm := m.mat.Data[r*m.mat.Stride : r*m.mat.Stride+m.mat.Cols] for i, v := range b.Row(rowb, r) { if rowm[i] != v { return false } } } return true } for r := 0; r < br; r++ { for c := 0; c < bc; c++ { if m.At(r, c) != b.At(r, c) { return false } } } return true } func (m *Dense) EqualsApprox(b Matrix, epsilon float64) bool { br, bc := b.Dims() if br != m.mat.Rows || bc != m.mat.Cols { return false } if b, ok := b.(RawMatrixer); ok { bmat := b.RawMatrix() for jb, jm := 0, 0; jm < br*m.mat.Stride; jb, jm = jb+bmat.Stride, jm+m.mat.Stride { for i, v := range m.mat.Data[jm : jm+bc] { if math.Abs(v-bmat.Data[i+jb]) > epsilon { return false } } } return true } if b, ok := b.(Vectorer); ok { rowb := make([]float64, bc) for r := 0; r < br; r++ { rowm := m.mat.Data[r*m.mat.Stride : r*m.mat.Stride+m.mat.Cols] for i, v := range b.Row(rowb, r) { if math.Abs(rowm[i]-v) > epsilon { return false } } } return true } for r := 0; r < br; r++ { for c := 0; c < bc; c++ { if math.Abs(m.At(r, c)-b.At(r, c)) > epsilon { return false } } } return true }