band.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. package mat
  5. import (
  6. "gonum.org/v1/gonum/blas"
  7. "gonum.org/v1/gonum/blas/blas64"
  8. )
  9. var (
  10. bandDense *BandDense
  11. _ Matrix = bandDense
  12. _ allMatrix = bandDense
  13. _ denseMatrix = bandDense
  14. _ Banded = bandDense
  15. _ RawBander = bandDense
  16. _ NonZeroDoer = bandDense
  17. _ RowNonZeroDoer = bandDense
  18. _ ColNonZeroDoer = bandDense
  19. )
  20. // BandDense represents a band matrix in dense storage format.
  21. type BandDense struct {
  22. mat blas64.Band
  23. }
  24. // Banded is a band matrix representation.
  25. type Banded interface {
  26. Matrix
  27. // Bandwidth returns the lower and upper bandwidth values for
  28. // the matrix. The total bandwidth of the matrix is kl+ku+1.
  29. Bandwidth() (kl, ku int)
  30. // TBand is the equivalent of the T() method in the Matrix
  31. // interface but guarantees the transpose is of banded type.
  32. TBand() Banded
  33. }
  34. // A RawBander can return a blas64.Band representation of the receiver.
  35. // Changes to the blas64.Band.Data slice will be reflected in the original
  36. // matrix, changes to the Rows, Cols, KL, KU and Stride fields will not.
  37. type RawBander interface {
  38. RawBand() blas64.Band
  39. }
  40. // A MutableBanded can set elements of a band matrix.
  41. type MutableBanded interface {
  42. Banded
  43. SetBand(i, j int, v float64)
  44. }
  45. var (
  46. _ Matrix = TransposeBand{}
  47. _ Banded = TransposeBand{}
  48. _ UntransposeBander = TransposeBand{}
  49. )
  50. // TransposeBand is a type for performing an implicit transpose of a band
  51. // matrix. It implements the Banded interface, returning values from the
  52. // transpose of the matrix within.
  53. type TransposeBand struct {
  54. Banded Banded
  55. }
  56. // At returns the value of the element at row i and column j of the transposed
  57. // matrix, that is, row j and column i of the Banded field.
  58. func (t TransposeBand) At(i, j int) float64 {
  59. return t.Banded.At(j, i)
  60. }
  61. // Dims returns the dimensions of the transposed matrix.
  62. func (t TransposeBand) Dims() (r, c int) {
  63. c, r = t.Banded.Dims()
  64. return r, c
  65. }
  66. // T performs an implicit transpose by returning the Banded field.
  67. func (t TransposeBand) T() Matrix {
  68. return t.Banded
  69. }
  70. // Bandwidth returns the lower and upper bandwidth values for
  71. // the transposed matrix.
  72. func (t TransposeBand) Bandwidth() (kl, ku int) {
  73. kl, ku = t.Banded.Bandwidth()
  74. return ku, kl
  75. }
  76. // TBand performs an implicit transpose by returning the Banded field.
  77. func (t TransposeBand) TBand() Banded {
  78. return t.Banded
  79. }
  80. // Untranspose returns the Banded field.
  81. func (t TransposeBand) Untranspose() Matrix {
  82. return t.Banded
  83. }
  84. // UntransposeBand returns the Banded field.
  85. func (t TransposeBand) UntransposeBand() Banded {
  86. return t.Banded
  87. }
  88. // NewBandDense creates a new Band matrix with r rows and c columns. If data == nil,
  89. // a new slice is allocated for the backing slice. If len(data) == min(r, c+kl)*(kl+ku+1),
  90. // data is used as the backing slice, and changes to the elements of the returned
  91. // BandDense will be reflected in data. If neither of these is true, NewBandDense
  92. // will panic. kl must be at least zero and less r, and ku must be at least zero and
  93. // less than c, otherwise NewBandDense will panic.
  94. // NewBandDense will panic if either r or c is zero.
  95. //
  96. // The data must be arranged in row-major order constructed by removing the zeros
  97. // from the rows outside the band and aligning the diagonals. For example, the matrix
  98. // 1 2 3 0 0 0
  99. // 4 5 6 7 0 0
  100. // 0 8 9 10 11 0
  101. // 0 0 12 13 14 15
  102. // 0 0 0 16 17 18
  103. // 0 0 0 0 19 20
  104. // becomes (* entries are never accessed)
  105. // * 1 2 3
  106. // 4 5 6 7
  107. // 8 9 10 11
  108. // 12 13 14 15
  109. // 16 17 18 *
  110. // 19 20 * *
  111. // which is passed to NewBandDense as []float64{*, 1, 2, 3, 4, ...} with kl=1 and ku=2.
  112. // Only the values in the band portion of the matrix are used.
  113. func NewBandDense(r, c, kl, ku int, data []float64) *BandDense {
  114. if r <= 0 || c <= 0 || kl < 0 || ku < 0 {
  115. if r == 0 || c == 0 {
  116. panic(ErrZeroLength)
  117. }
  118. panic("mat: negative dimension")
  119. }
  120. if kl+1 > r || ku+1 > c {
  121. panic("mat: band out of range")
  122. }
  123. bc := kl + ku + 1
  124. if data != nil && len(data) != min(r, c+kl)*bc {
  125. panic(ErrShape)
  126. }
  127. if data == nil {
  128. data = make([]float64, min(r, c+kl)*bc)
  129. }
  130. return &BandDense{
  131. mat: blas64.Band{
  132. Rows: r,
  133. Cols: c,
  134. KL: kl,
  135. KU: ku,
  136. Stride: bc,
  137. Data: data,
  138. },
  139. }
  140. }
  141. // NewDiagonalRect is a convenience function that returns a diagonal matrix represented by a
  142. // BandDense. The length of data must be min(r, c) otherwise NewDiagonalRect will panic.
  143. func NewDiagonalRect(r, c int, data []float64) *BandDense {
  144. return NewBandDense(r, c, 0, 0, data)
  145. }
  146. // Dims returns the number of rows and columns in the matrix.
  147. func (b *BandDense) Dims() (r, c int) {
  148. return b.mat.Rows, b.mat.Cols
  149. }
  150. // Bandwidth returns the upper and lower bandwidths of the matrix.
  151. func (b *BandDense) Bandwidth() (kl, ku int) {
  152. return b.mat.KL, b.mat.KU
  153. }
  154. // T performs an implicit transpose by returning the receiver inside a Transpose.
  155. func (b *BandDense) T() Matrix {
  156. return Transpose{b}
  157. }
  158. // TBand performs an implicit transpose by returning the receiver inside a TransposeBand.
  159. func (b *BandDense) TBand() Banded {
  160. return TransposeBand{b}
  161. }
  162. // RawBand returns the underlying blas64.Band used by the receiver.
  163. // Changes to elements in the receiver following the call will be reflected
  164. // in returned blas64.Band.
  165. func (b *BandDense) RawBand() blas64.Band {
  166. return b.mat
  167. }
  168. // SetRawBand sets the underlying blas64.Band used by the receiver.
  169. // Changes to elements in the receiver following the call will be reflected
  170. // in the input.
  171. func (b *BandDense) SetRawBand(mat blas64.Band) {
  172. b.mat = mat
  173. }
  174. // IsEmpty returns whether the receiver is empty. Empty matrices can be the
  175. // receiver for size-restricted operations. The receiver can be zeroed using Reset.
  176. func (b *BandDense) IsEmpty() bool {
  177. return b.mat.Stride == 0
  178. }
  179. // Reset empties the matrix so that it can be reused as the
  180. // receiver of a dimensionally restricted operation.
  181. //
  182. // Reset should not be used when the matrix shares backing data.
  183. // See the Reseter interface for more information.
  184. func (b *BandDense) Reset() {
  185. b.mat.Rows = 0
  186. b.mat.Cols = 0
  187. b.mat.KL = 0
  188. b.mat.KU = 0
  189. b.mat.Stride = 0
  190. b.mat.Data = b.mat.Data[:0:0]
  191. }
  192. // DiagView returns the diagonal as a matrix backed by the original data.
  193. func (b *BandDense) DiagView() Diagonal {
  194. n := min(b.mat.Rows, b.mat.Cols)
  195. return &DiagDense{
  196. mat: blas64.Vector{
  197. N: n,
  198. Inc: b.mat.Stride,
  199. Data: b.mat.Data[b.mat.KL : (n-1)*b.mat.Stride+b.mat.KL+1],
  200. },
  201. }
  202. }
  203. // DoNonZero calls the function fn for each of the non-zero elements of b. The function fn
  204. // takes a row/column index and the element value of b at (i, j).
  205. func (b *BandDense) DoNonZero(fn func(i, j int, v float64)) {
  206. for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ {
  207. for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ {
  208. v := b.at(i, j)
  209. if v != 0 {
  210. fn(i, j, v)
  211. }
  212. }
  213. }
  214. }
  215. // DoRowNonZero calls the function fn for each of the non-zero elements of row i of b. The function fn
  216. // takes a row/column index and the element value of b at (i, j).
  217. func (b *BandDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
  218. if i < 0 || b.mat.Rows <= i {
  219. panic(ErrRowAccess)
  220. }
  221. for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ {
  222. v := b.at(i, j)
  223. if v != 0 {
  224. fn(i, j, v)
  225. }
  226. }
  227. }
  228. // DoColNonZero calls the function fn for each of the non-zero elements of column j of b. The function fn
  229. // takes a row/column index and the element value of b at (i, j).
  230. func (b *BandDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
  231. if j < 0 || b.mat.Cols <= j {
  232. panic(ErrColAccess)
  233. }
  234. for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ {
  235. if i-b.mat.KL <= j && j < i+b.mat.KU+1 {
  236. v := b.at(i, j)
  237. if v != 0 {
  238. fn(i, j, v)
  239. }
  240. }
  241. }
  242. }
  243. // Zero sets all of the matrix elements to zero.
  244. func (b *BandDense) Zero() {
  245. m := b.mat.Rows
  246. kL := b.mat.KL
  247. nCol := b.mat.KU + 1 + kL
  248. for i := 0; i < m; i++ {
  249. l := max(0, kL-i)
  250. u := min(nCol, m+kL-i)
  251. zero(b.mat.Data[i*b.mat.Stride+l : i*b.mat.Stride+u])
  252. }
  253. }
  254. // Trace computes the trace of the matrix.
  255. func (b *BandDense) Trace() float64 {
  256. r, c := b.Dims()
  257. if r != c {
  258. panic(ErrShape)
  259. }
  260. rb := b.RawBand()
  261. var tr float64
  262. for i := 0; i < r; i++ {
  263. tr += rb.Data[rb.KL+i*rb.Stride]
  264. }
  265. return tr
  266. }
  267. // MulVecTo computes B⋅x or Bᵀ⋅x storing the result into dst.
  268. func (b *BandDense) MulVecTo(dst *VecDense, trans bool, x Vector) {
  269. m, n := b.Dims()
  270. if trans {
  271. m, n = n, m
  272. }
  273. if x.Len() != n {
  274. panic(ErrShape)
  275. }
  276. dst.reuseAsNonZeroed(m)
  277. t := blas.NoTrans
  278. if trans {
  279. t = blas.Trans
  280. }
  281. xMat, _ := untransposeExtract(x)
  282. if xVec, ok := xMat.(*VecDense); ok {
  283. if dst != xVec {
  284. dst.checkOverlap(xVec.mat)
  285. blas64.Gbmv(t, 1, b.mat, xVec.mat, 0, dst.mat)
  286. } else {
  287. xCopy := getWorkspaceVec(n, false)
  288. xCopy.CloneFromVec(xVec)
  289. blas64.Gbmv(t, 1, b.mat, xCopy.mat, 0, dst.mat)
  290. putWorkspaceVec(xCopy)
  291. }
  292. } else {
  293. xCopy := getWorkspaceVec(n, false)
  294. xCopy.CloneFromVec(x)
  295. blas64.Gbmv(t, 1, b.mat, xCopy.mat, 0, dst.mat)
  296. putWorkspaceVec(xCopy)
  297. }
  298. }