triangular.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. // Copyright ©2015 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. "math"
  7. "gonum.org/v1/gonum/blas"
  8. "gonum.org/v1/gonum/blas/blas64"
  9. "gonum.org/v1/gonum/lapack/lapack64"
  10. )
  11. var (
  12. triDense *TriDense
  13. _ Matrix = triDense
  14. _ Triangular = triDense
  15. _ RawTriangular = triDense
  16. _ MutableTriangular = triDense
  17. _ NonZeroDoer = triDense
  18. _ RowNonZeroDoer = triDense
  19. _ ColNonZeroDoer = triDense
  20. )
  21. const badTriCap = "mat: bad capacity for TriDense"
  22. // TriDense represents an upper or lower triangular matrix in dense storage
  23. // format.
  24. type TriDense struct {
  25. mat blas64.Triangular
  26. cap int
  27. }
  28. // Triangular represents a triangular matrix. Triangular matrices are always square.
  29. type Triangular interface {
  30. Matrix
  31. // Triangle returns the number of rows/columns in the matrix and its
  32. // orientation.
  33. Triangle() (n int, kind TriKind)
  34. // TTri is the equivalent of the T() method in the Matrix interface but
  35. // guarantees the transpose is of triangular type.
  36. TTri() Triangular
  37. }
  38. // A RawTriangular can return a blas64.Triangular representation of the receiver.
  39. // Changes to the blas64.Triangular.Data slice will be reflected in the original
  40. // matrix, changes to the N, Stride, Uplo and Diag fields will not.
  41. type RawTriangular interface {
  42. RawTriangular() blas64.Triangular
  43. }
  44. // A MutableTriangular can set elements of a triangular matrix.
  45. type MutableTriangular interface {
  46. Triangular
  47. SetTri(i, j int, v float64)
  48. }
  49. var (
  50. _ Matrix = TransposeTri{}
  51. _ Triangular = TransposeTri{}
  52. _ UntransposeTrier = TransposeTri{}
  53. )
  54. // TransposeTri is a type for performing an implicit transpose of a Triangular
  55. // matrix. It implements the Triangular interface, returning values from the
  56. // transpose of the matrix within.
  57. type TransposeTri struct {
  58. Triangular Triangular
  59. }
  60. // At returns the value of the element at row i and column j of the transposed
  61. // matrix, that is, row j and column i of the Triangular field.
  62. func (t TransposeTri) At(i, j int) float64 {
  63. return t.Triangular.At(j, i)
  64. }
  65. // Dims returns the dimensions of the transposed matrix. Triangular matrices are
  66. // square and thus this is the same size as the original Triangular.
  67. func (t TransposeTri) Dims() (r, c int) {
  68. c, r = t.Triangular.Dims()
  69. return r, c
  70. }
  71. // T performs an implicit transpose by returning the Triangular field.
  72. func (t TransposeTri) T() Matrix {
  73. return t.Triangular
  74. }
  75. // Triangle returns the number of rows/columns in the matrix and its orientation.
  76. func (t TransposeTri) Triangle() (int, TriKind) {
  77. n, upper := t.Triangular.Triangle()
  78. return n, !upper
  79. }
  80. // TTri performs an implicit transpose by returning the Triangular field.
  81. func (t TransposeTri) TTri() Triangular {
  82. return t.Triangular
  83. }
  84. // Untranspose returns the Triangular field.
  85. func (t TransposeTri) Untranspose() Matrix {
  86. return t.Triangular
  87. }
  88. func (t TransposeTri) UntransposeTri() Triangular {
  89. return t.Triangular
  90. }
  91. // NewTriDense creates a new Triangular matrix with n rows and columns. If data == nil,
  92. // a new slice is allocated for the backing slice. If len(data) == n*n, data is
  93. // used as the backing slice, and changes to the elements of the returned TriDense
  94. // will be reflected in data. If neither of these is true, NewTriDense will panic.
  95. // NewTriDense will panic if n is zero.
  96. //
  97. // The data must be arranged in row-major order, i.e. the (i*c + j)-th
  98. // element in the data slice is the {i, j}-th element in the matrix.
  99. // Only the values in the triangular portion corresponding to kind are used.
  100. func NewTriDense(n int, kind TriKind, data []float64) *TriDense {
  101. if n <= 0 {
  102. if n == 0 {
  103. panic(ErrZeroLength)
  104. }
  105. panic("mat: negative dimension")
  106. }
  107. if data != nil && len(data) != n*n {
  108. panic(ErrShape)
  109. }
  110. if data == nil {
  111. data = make([]float64, n*n)
  112. }
  113. uplo := blas.Lower
  114. if kind == Upper {
  115. uplo = blas.Upper
  116. }
  117. return &TriDense{
  118. mat: blas64.Triangular{
  119. N: n,
  120. Stride: n,
  121. Data: data,
  122. Uplo: uplo,
  123. Diag: blas.NonUnit,
  124. },
  125. cap: n,
  126. }
  127. }
  128. func (t *TriDense) Dims() (r, c int) {
  129. return t.mat.N, t.mat.N
  130. }
  131. // Triangle returns the dimension of t and its orientation. The returned
  132. // orientation is only valid when n is not zero.
  133. func (t *TriDense) Triangle() (n int, kind TriKind) {
  134. return t.mat.N, t.triKind()
  135. }
  136. func (t *TriDense) isUpper() bool {
  137. return isUpperUplo(t.mat.Uplo)
  138. }
  139. func (t *TriDense) triKind() TriKind {
  140. return TriKind(isUpperUplo(t.mat.Uplo))
  141. }
  142. func isUpperUplo(u blas.Uplo) bool {
  143. switch u {
  144. case blas.Upper:
  145. return true
  146. case blas.Lower:
  147. return false
  148. default:
  149. panic(badTriangle)
  150. }
  151. }
  152. func uploToTriKind(u blas.Uplo) TriKind {
  153. switch u {
  154. case blas.Upper:
  155. return Upper
  156. case blas.Lower:
  157. return Lower
  158. default:
  159. panic(badTriangle)
  160. }
  161. }
  162. // asSymBlas returns the receiver restructured as a blas64.Symmetric with the
  163. // same backing memory. Panics if the receiver is unit.
  164. // This returns a blas64.Symmetric and not a *SymDense because SymDense can only
  165. // be upper triangular.
  166. func (t *TriDense) asSymBlas() blas64.Symmetric {
  167. if t.mat.Diag == blas.Unit {
  168. panic("mat: cannot convert unit TriDense into blas64.Symmetric")
  169. }
  170. return blas64.Symmetric{
  171. N: t.mat.N,
  172. Stride: t.mat.Stride,
  173. Data: t.mat.Data,
  174. Uplo: t.mat.Uplo,
  175. }
  176. }
  177. // T performs an implicit transpose by returning the receiver inside a Transpose.
  178. func (t *TriDense) T() Matrix {
  179. return Transpose{t}
  180. }
  181. // TTri performs an implicit transpose by returning the receiver inside a TransposeTri.
  182. func (t *TriDense) TTri() Triangular {
  183. return TransposeTri{t}
  184. }
  185. func (t *TriDense) RawTriangular() blas64.Triangular {
  186. return t.mat
  187. }
  188. // SetRawTriangular sets the underlying blas64.Triangular used by the receiver.
  189. // Changes to elements in the receiver following the call will be reflected
  190. // in the input.
  191. //
  192. // The supplied Triangular must not use blas.Unit storage format.
  193. func (t *TriDense) SetRawTriangular(mat blas64.Triangular) {
  194. if mat.Diag == blas.Unit {
  195. panic("mat: cannot set TriDense with Unit storage format")
  196. }
  197. t.mat = mat
  198. }
  199. // Reset zeros the dimensions of the matrix so that it can be reused as the
  200. // receiver of a dimensionally restricted operation.
  201. //
  202. // See the Reseter interface for more information.
  203. func (t *TriDense) Reset() {
  204. // N and Stride must be zeroed in unison.
  205. t.mat.N, t.mat.Stride = 0, 0
  206. // Defensively zero Uplo to ensure
  207. // it is set correctly later.
  208. t.mat.Uplo = 0
  209. t.mat.Data = t.mat.Data[:0]
  210. }
  211. // Zero sets all of the matrix elements to zero.
  212. func (t *TriDense) Zero() {
  213. if t.isUpper() {
  214. for i := 0; i < t.mat.N; i++ {
  215. zero(t.mat.Data[i*t.mat.Stride+i : i*t.mat.Stride+t.mat.N])
  216. }
  217. return
  218. }
  219. for i := 0; i < t.mat.N; i++ {
  220. zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+i+1])
  221. }
  222. }
  223. // IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the
  224. // receiver for size-restricted operations. TriDense matrices can be zeroed using Reset.
  225. func (t *TriDense) IsZero() bool {
  226. // It must be the case that t.Dims() returns
  227. // zeros in this case. See comment in Reset().
  228. return t.mat.Stride == 0
  229. }
  230. // untranspose untransposes a matrix if applicable. If a is an Untransposer, then
  231. // untranspose returns the underlying matrix and true. If it is not, then it returns
  232. // the input matrix and false.
  233. func untransposeTri(a Triangular) (Triangular, bool) {
  234. if ut, ok := a.(UntransposeTrier); ok {
  235. return ut.UntransposeTri(), true
  236. }
  237. return a, false
  238. }
  239. // reuseAs resizes a zero receiver to an n×n triangular matrix with the given
  240. // orientation. If the receiver is non-zero, reuseAs checks that the receiver
  241. // is the correct size and orientation.
  242. func (t *TriDense) reuseAs(n int, kind TriKind) {
  243. if n == 0 {
  244. panic(ErrZeroLength)
  245. }
  246. ul := blas.Lower
  247. if kind == Upper {
  248. ul = blas.Upper
  249. }
  250. if t.mat.N > t.cap {
  251. panic(badTriCap)
  252. }
  253. if t.IsZero() {
  254. t.mat = blas64.Triangular{
  255. N: n,
  256. Stride: n,
  257. Diag: blas.NonUnit,
  258. Data: use(t.mat.Data, n*n),
  259. Uplo: ul,
  260. }
  261. t.cap = n
  262. return
  263. }
  264. if t.mat.N != n {
  265. panic(ErrShape)
  266. }
  267. if t.mat.Uplo != ul {
  268. panic(ErrTriangle)
  269. }
  270. }
  271. // isolatedWorkspace returns a new TriDense matrix w with the size of a and
  272. // returns a callback to defer which performs cleanup at the return of the call.
  273. // This should be used when a method receiver is the same pointer as an input argument.
  274. func (t *TriDense) isolatedWorkspace(a Triangular) (w *TriDense, restore func()) {
  275. n, kind := a.Triangle()
  276. if n == 0 {
  277. panic(ErrZeroLength)
  278. }
  279. w = getWorkspaceTri(n, kind, false)
  280. return w, func() {
  281. t.Copy(w)
  282. putWorkspaceTri(w)
  283. }
  284. }
  285. // DiagView returns the diagonal as a matrix backed by the original data.
  286. func (t *TriDense) DiagView() Diagonal {
  287. if t.mat.Diag == blas.Unit {
  288. panic("mat: cannot take view of Unit diagonal")
  289. }
  290. n := t.mat.N
  291. return &DiagDense{
  292. mat: blas64.Vector{
  293. N: n,
  294. Inc: t.mat.Stride + 1,
  295. Data: t.mat.Data[:(n-1)*t.mat.Stride+n],
  296. },
  297. }
  298. }
  299. // Copy makes a copy of elements of a into the receiver. It is similar to the
  300. // built-in copy; it copies as much as the overlap between the two matrices and
  301. // returns the number of rows and columns it copied. Only elements within the
  302. // receiver's non-zero triangle are set.
  303. //
  304. // See the Copier interface for more information.
  305. func (t *TriDense) Copy(a Matrix) (r, c int) {
  306. r, c = a.Dims()
  307. r = min(r, t.mat.N)
  308. c = min(c, t.mat.N)
  309. if r == 0 || c == 0 {
  310. return 0, 0
  311. }
  312. switch a := a.(type) {
  313. case RawMatrixer:
  314. amat := a.RawMatrix()
  315. if t.isUpper() {
  316. for i := 0; i < r; i++ {
  317. copy(t.mat.Data[i*t.mat.Stride+i:i*t.mat.Stride+c], amat.Data[i*amat.Stride+i:i*amat.Stride+c])
  318. }
  319. } else {
  320. for i := 0; i < r; i++ {
  321. copy(t.mat.Data[i*t.mat.Stride:i*t.mat.Stride+i+1], amat.Data[i*amat.Stride:i*amat.Stride+i+1])
  322. }
  323. }
  324. case RawTriangular:
  325. amat := a.RawTriangular()
  326. aIsUpper := isUpperUplo(amat.Uplo)
  327. tIsUpper := t.isUpper()
  328. switch {
  329. case tIsUpper && aIsUpper:
  330. for i := 0; i < r; i++ {
  331. copy(t.mat.Data[i*t.mat.Stride+i:i*t.mat.Stride+c], amat.Data[i*amat.Stride+i:i*amat.Stride+c])
  332. }
  333. case !tIsUpper && !aIsUpper:
  334. for i := 0; i < r; i++ {
  335. copy(t.mat.Data[i*t.mat.Stride:i*t.mat.Stride+i+1], amat.Data[i*amat.Stride:i*amat.Stride+i+1])
  336. }
  337. default:
  338. for i := 0; i < r; i++ {
  339. t.set(i, i, amat.Data[i*amat.Stride+i])
  340. }
  341. }
  342. default:
  343. isUpper := t.isUpper()
  344. for i := 0; i < r; i++ {
  345. if isUpper {
  346. for j := i; j < c; j++ {
  347. t.set(i, j, a.At(i, j))
  348. }
  349. } else {
  350. for j := 0; j <= i; j++ {
  351. t.set(i, j, a.At(i, j))
  352. }
  353. }
  354. }
  355. }
  356. return r, c
  357. }
  358. // InverseTri computes the inverse of the triangular matrix a, storing the result
  359. // into the receiver. If a is ill-conditioned, a Condition error will be returned.
  360. // Note that matrix inversion is numerically unstable, and should generally be
  361. // avoided where possible, for example by using the Solve routines.
  362. func (t *TriDense) InverseTri(a Triangular) error {
  363. t.checkOverlapMatrix(a)
  364. n, _ := a.Triangle()
  365. t.reuseAs(a.Triangle())
  366. t.Copy(a)
  367. work := getFloats(3*n, false)
  368. iwork := getInts(n, false)
  369. cond := lapack64.Trcon(CondNorm, t.mat, work, iwork)
  370. putFloats(work)
  371. putInts(iwork)
  372. if math.IsInf(cond, 1) {
  373. return Condition(cond)
  374. }
  375. ok := lapack64.Trtri(t.mat)
  376. if !ok {
  377. return Condition(math.Inf(1))
  378. }
  379. if cond > ConditionTolerance {
  380. return Condition(cond)
  381. }
  382. return nil
  383. }
  384. // MulTri takes the product of triangular matrices a and b and places the result
  385. // in the receiver. The size of a and b must match, and they both must have the
  386. // same TriKind, or Mul will panic.
  387. func (t *TriDense) MulTri(a, b Triangular) {
  388. n, kind := a.Triangle()
  389. nb, kindb := b.Triangle()
  390. if n != nb {
  391. panic(ErrShape)
  392. }
  393. if kind != kindb {
  394. panic(ErrTriangle)
  395. }
  396. aU, _ := untransposeTri(a)
  397. bU, _ := untransposeTri(b)
  398. t.checkOverlapMatrix(bU)
  399. t.checkOverlapMatrix(aU)
  400. t.reuseAs(n, kind)
  401. var restore func()
  402. if t == aU {
  403. t, restore = t.isolatedWorkspace(aU)
  404. defer restore()
  405. } else if t == bU {
  406. t, restore = t.isolatedWorkspace(bU)
  407. defer restore()
  408. }
  409. // TODO(btracey): Improve the set of fast-paths.
  410. if kind == Upper {
  411. for i := 0; i < n; i++ {
  412. for j := i; j < n; j++ {
  413. var v float64
  414. for k := i; k <= j; k++ {
  415. v += a.At(i, k) * b.At(k, j)
  416. }
  417. t.SetTri(i, j, v)
  418. }
  419. }
  420. return
  421. }
  422. for i := 0; i < n; i++ {
  423. for j := 0; j <= i; j++ {
  424. var v float64
  425. for k := j; k <= i; k++ {
  426. v += a.At(i, k) * b.At(k, j)
  427. }
  428. t.SetTri(i, j, v)
  429. }
  430. }
  431. }
  432. // ScaleTri multiplies the elements of a by f, placing the result in the receiver.
  433. // If the receiver is non-zero, the size and kind of the receiver must match
  434. // the input, or ScaleTri will panic.
  435. func (t *TriDense) ScaleTri(f float64, a Triangular) {
  436. n, kind := a.Triangle()
  437. t.reuseAs(n, kind)
  438. // TODO(btracey): Improve the set of fast-paths.
  439. switch a := a.(type) {
  440. case RawTriangular:
  441. amat := a.RawTriangular()
  442. if t != a {
  443. t.checkOverlap(generalFromTriangular(amat))
  444. }
  445. if kind == Upper {
  446. for i := 0; i < n; i++ {
  447. ts := t.mat.Data[i*t.mat.Stride+i : i*t.mat.Stride+n]
  448. as := amat.Data[i*amat.Stride+i : i*amat.Stride+n]
  449. for i, v := range as {
  450. ts[i] = v * f
  451. }
  452. }
  453. return
  454. }
  455. for i := 0; i < n; i++ {
  456. ts := t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+i+1]
  457. as := amat.Data[i*amat.Stride : i*amat.Stride+i+1]
  458. for i, v := range as {
  459. ts[i] = v * f
  460. }
  461. }
  462. return
  463. default:
  464. t.checkOverlapMatrix(a)
  465. isUpper := kind == Upper
  466. for i := 0; i < n; i++ {
  467. if isUpper {
  468. for j := i; j < n; j++ {
  469. t.set(i, j, f*a.At(i, j))
  470. }
  471. } else {
  472. for j := 0; j <= i; j++ {
  473. t.set(i, j, f*a.At(i, j))
  474. }
  475. }
  476. }
  477. }
  478. }
  479. // Trace returns the trace of the matrix.
  480. func (t *TriDense) Trace() float64 {
  481. // TODO(btracey): could use internal asm sum routine.
  482. var v float64
  483. for i := 0; i < t.mat.N; i++ {
  484. v += t.mat.Data[i*t.mat.Stride+i]
  485. }
  486. return v
  487. }
  488. // copySymIntoTriangle copies a symmetric matrix into a TriDense
  489. func copySymIntoTriangle(t *TriDense, s Symmetric) {
  490. n, upper := t.Triangle()
  491. ns := s.Symmetric()
  492. if n != ns {
  493. panic("mat: triangle size mismatch")
  494. }
  495. ts := t.mat.Stride
  496. if rs, ok := s.(RawSymmetricer); ok {
  497. sd := rs.RawSymmetric()
  498. ss := sd.Stride
  499. if upper {
  500. if sd.Uplo == blas.Upper {
  501. for i := 0; i < n; i++ {
  502. copy(t.mat.Data[i*ts+i:i*ts+n], sd.Data[i*ss+i:i*ss+n])
  503. }
  504. return
  505. }
  506. for i := 0; i < n; i++ {
  507. for j := i; j < n; j++ {
  508. t.mat.Data[i*ts+j] = sd.Data[j*ss+i]
  509. }
  510. }
  511. return
  512. }
  513. if sd.Uplo == blas.Upper {
  514. for i := 0; i < n; i++ {
  515. for j := 0; j <= i; j++ {
  516. t.mat.Data[i*ts+j] = sd.Data[j*ss+i]
  517. }
  518. }
  519. return
  520. }
  521. for i := 0; i < n; i++ {
  522. copy(t.mat.Data[i*ts:i*ts+i+1], sd.Data[i*ss:i*ss+i+1])
  523. }
  524. return
  525. }
  526. if upper {
  527. for i := 0; i < n; i++ {
  528. for j := i; j < n; j++ {
  529. t.mat.Data[i*ts+j] = s.At(i, j)
  530. }
  531. }
  532. return
  533. }
  534. for i := 0; i < n; i++ {
  535. for j := 0; j <= i; j++ {
  536. t.mat.Data[i*ts+j] = s.At(i, j)
  537. }
  538. }
  539. }
  540. // DoNonZero calls the function fn for each of the non-zero elements of t. The function fn
  541. // takes a row/column index and the element value of t at (i, j).
  542. func (t *TriDense) DoNonZero(fn func(i, j int, v float64)) {
  543. if t.isUpper() {
  544. for i := 0; i < t.mat.N; i++ {
  545. for j := i; j < t.mat.N; j++ {
  546. v := t.at(i, j)
  547. if v != 0 {
  548. fn(i, j, v)
  549. }
  550. }
  551. }
  552. return
  553. }
  554. for i := 0; i < t.mat.N; i++ {
  555. for j := 0; j <= i; j++ {
  556. v := t.at(i, j)
  557. if v != 0 {
  558. fn(i, j, v)
  559. }
  560. }
  561. }
  562. }
  563. // DoRowNonZero calls the function fn for each of the non-zero elements of row i of t. The function fn
  564. // takes a row/column index and the element value of t at (i, j).
  565. func (t *TriDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
  566. if i < 0 || t.mat.N <= i {
  567. panic(ErrRowAccess)
  568. }
  569. if t.isUpper() {
  570. for j := i; j < t.mat.N; j++ {
  571. v := t.at(i, j)
  572. if v != 0 {
  573. fn(i, j, v)
  574. }
  575. }
  576. return
  577. }
  578. for j := 0; j <= i; j++ {
  579. v := t.at(i, j)
  580. if v != 0 {
  581. fn(i, j, v)
  582. }
  583. }
  584. }
  585. // DoColNonZero calls the function fn for each of the non-zero elements of column j of t. The function fn
  586. // takes a row/column index and the element value of t at (i, j).
  587. func (t *TriDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
  588. if j < 0 || t.mat.N <= j {
  589. panic(ErrColAccess)
  590. }
  591. if t.isUpper() {
  592. for i := 0; i <= j; i++ {
  593. v := t.at(i, j)
  594. if v != 0 {
  595. fn(i, j, v)
  596. }
  597. }
  598. return
  599. }
  600. for i := j; i < t.mat.N; i++ {
  601. v := t.at(i, j)
  602. if v != 0 {
  603. fn(i, j, v)
  604. }
  605. }
  606. }