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.
10 "gonum.org/v1/gonum/blas"
11 "gonum.org/v1/gonum/blas/blas64"
12 "gonum.org/v1/gonum/lapack/lapack64"
18 _ Triangular = triDense
19 _ RawTriangular = triDense
20 _ MutableTriangular = triDense
22 _ NonZeroDoer = triDense
23 _ RowNonZeroDoer = triDense
24 _ ColNonZeroDoer = triDense
27 const badTriCap = "mat: bad capacity for TriDense"
29 // TriDense represents an upper or lower triangular matrix in dense storage
31 type TriDense struct {
36 // Triangular represents a triangular matrix. Triangular matrices are always square.
37 type Triangular interface {
39 // Triangular returns the number of rows/columns in the matrix and its
41 Triangle() (n int, kind TriKind)
43 // TTri is the equivalent of the T() method in the Matrix interface but
44 // guarantees the transpose is of triangular type.
48 // A RawTriangular can return a view of itself as a BLAS Triangular matrix.
49 type RawTriangular interface {
50 RawTriangular() blas64.Triangular
53 // A MutableTriangular can set elements of a triangular matrix.
54 type MutableTriangular interface {
56 SetTri(i, j int, v float64)
60 _ Matrix = TransposeTri{}
61 _ Triangular = TransposeTri{}
62 _ UntransposeTrier = TransposeTri{}
65 // TransposeTri is a type for performing an implicit transpose of a Triangular
66 // matrix. It implements the Triangular interface, returning values from the
67 // transpose of the matrix within.
68 type TransposeTri struct {
72 // At returns the value of the element at row i and column j of the transposed
73 // matrix, that is, row j and column i of the Triangular field.
74 func (t TransposeTri) At(i, j int) float64 {
75 return t.Triangular.At(j, i)
78 // Dims returns the dimensions of the transposed matrix. Triangular matrices are
79 // square and thus this is the same size as the original Triangular.
80 func (t TransposeTri) Dims() (r, c int) {
81 c, r = t.Triangular.Dims()
85 // T performs an implicit transpose by returning the Triangular field.
86 func (t TransposeTri) T() Matrix {
90 // Triangle returns the number of rows/columns in the matrix and its orientation.
91 func (t TransposeTri) Triangle() (int, TriKind) {
92 n, upper := t.Triangular.Triangle()
96 // TTri performs an implicit transpose by returning the Triangular field.
97 func (t TransposeTri) TTri() Triangular {
101 // Untranspose returns the Triangular field.
102 func (t TransposeTri) Untranspose() Matrix {
106 func (t TransposeTri) UntransposeTri() Triangular {
110 // NewTriDense creates a new Triangular matrix with n rows and columns. If data == nil,
111 // a new slice is allocated for the backing slice. If len(data) == n*n, data is
112 // used as the backing slice, and changes to the elements of the returned TriDense
113 // will be reflected in data. If neither of these is true, NewTriDense will panic.
115 // The data must be arranged in row-major order, i.e. the (i*c + j)-th
116 // element in the data slice is the {i, j}-th element in the matrix.
117 // Only the values in the triangular portion corresponding to kind are used.
118 func NewTriDense(n int, kind TriKind, data []float64) *TriDense {
120 panic("mat: negative dimension")
122 if data != nil && len(data) != n*n {
126 data = make([]float64, n*n)
133 mat: blas64.Triangular{
144 func (t *TriDense) Dims() (r, c int) {
145 return t.mat.N, t.mat.N
148 // Triangle returns the dimension of t and its orientation. The returned
149 // orientation is only valid when n is not zero.
150 func (t *TriDense) Triangle() (n int, kind TriKind) {
151 return t.mat.N, TriKind(!t.IsZero()) && t.triKind()
154 func (t *TriDense) isUpper() bool {
155 return isUpperUplo(t.mat.Uplo)
158 func (t *TriDense) triKind() TriKind {
159 return TriKind(isUpperUplo(t.mat.Uplo))
162 func isUpperUplo(u blas.Uplo) bool {
173 // asSymBlas returns the receiver restructured as a blas64.Symmetric with the
174 // same backing memory. Panics if the receiver is unit.
175 // This returns a blas64.Symmetric and not a *SymDense because SymDense can only
176 // be upper triangular.
177 func (t *TriDense) asSymBlas() blas64.Symmetric {
178 if t.mat.Diag == blas.Unit {
179 panic("mat: cannot convert unit TriDense into blas64.Symmetric")
181 return blas64.Symmetric{
183 Stride: t.mat.Stride,
189 // T performs an implicit transpose by returning the receiver inside a Transpose.
190 func (t *TriDense) T() Matrix {
194 // TTri performs an implicit transpose by returning the receiver inside a TransposeTri.
195 func (t *TriDense) TTri() Triangular {
196 return TransposeTri{t}
199 func (t *TriDense) RawTriangular() blas64.Triangular {
203 // Reset zeros the dimensions of the matrix so that it can be reused as the
204 // receiver of a dimensionally restricted operation.
206 // See the Reseter interface for more information.
207 func (t *TriDense) Reset() {
208 // N and Stride must be zeroed in unison.
209 t.mat.N, t.mat.Stride = 0, 0
210 // Defensively zero Uplo to ensure
211 // it is set correctly later.
213 t.mat.Data = t.mat.Data[:0]
216 // IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the
217 // receiver for size-restricted operations. TriDense matrices can be zeroed using Reset.
218 func (t *TriDense) IsZero() bool {
219 // It must be the case that t.Dims() returns
220 // zeros in this case. See comment in Reset().
221 return t.mat.Stride == 0
224 // untranspose untransposes a matrix if applicable. If a is an Untransposer, then
225 // untranspose returns the underlying matrix and true. If it is not, then it returns
226 // the input matrix and false.
227 func untransposeTri(a Triangular) (Triangular, bool) {
228 if ut, ok := a.(UntransposeTrier); ok {
229 return ut.UntransposeTri(), true
234 // reuseAs resizes a zero receiver to an n×n triangular matrix with the given
235 // orientation. If the receiver is non-zero, reuseAs checks that the receiver
236 // is the correct size and orientation.
237 func (t *TriDense) reuseAs(n int, kind TriKind) {
246 t.mat = blas64.Triangular{
250 Data: use(t.mat.Data, n*n),
259 if t.mat.Uplo != ul {
264 // isolatedWorkspace returns a new TriDense matrix w with the size of a and
265 // returns a callback to defer which performs cleanup at the return of the call.
266 // This should be used when a method receiver is the same pointer as an input argument.
267 func (t *TriDense) isolatedWorkspace(a Triangular) (w *TriDense, restore func()) {
268 n, kind := a.Triangle()
272 w = getWorkspaceTri(n, kind, false)
279 // Copy makes a copy of elements of a into the receiver. It is similar to the
280 // built-in copy; it copies as much as the overlap between the two matrices and
281 // returns the number of rows and columns it copied. Only elements within the
282 // receiver's non-zero triangle are set.
284 // See the Copier interface for more information.
285 func (t *TriDense) Copy(a Matrix) (r, c int) {
289 if r == 0 || c == 0 {
293 switch a := a.(type) {
295 amat := a.RawMatrix()
297 for i := 0; i < r; i++ {
298 copy(t.mat.Data[i*t.mat.Stride+i:i*t.mat.Stride+c], amat.Data[i*amat.Stride+i:i*amat.Stride+c])
301 for i := 0; i < r; i++ {
302 copy(t.mat.Data[i*t.mat.Stride:i*t.mat.Stride+i+1], amat.Data[i*amat.Stride:i*amat.Stride+i+1])
306 amat := a.RawTriangular()
307 aIsUpper := isUpperUplo(amat.Uplo)
308 tIsUpper := t.isUpper()
310 case tIsUpper && aIsUpper:
311 for i := 0; i < r; i++ {
312 copy(t.mat.Data[i*t.mat.Stride+i:i*t.mat.Stride+c], amat.Data[i*amat.Stride+i:i*amat.Stride+c])
314 case !tIsUpper && !aIsUpper:
315 for i := 0; i < r; i++ {
316 copy(t.mat.Data[i*t.mat.Stride:i*t.mat.Stride+i+1], amat.Data[i*amat.Stride:i*amat.Stride+i+1])
319 for i := 0; i < r; i++ {
320 t.set(i, i, amat.Data[i*amat.Stride+i])
324 isUpper := t.isUpper()
325 for i := 0; i < r; i++ {
327 for j := i; j < c; j++ {
328 t.set(i, j, a.At(i, j))
331 for j := 0; j <= i; j++ {
332 t.set(i, j, a.At(i, j))
341 // InverseTri computes the inverse of the triangular matrix a, storing the result
342 // into the receiver. If a is ill-conditioned, a Condition error will be returned.
343 // Note that matrix inversion is numerically unstable, and should generally be
344 // avoided where possible, for example by using the Solve routines.
345 func (t *TriDense) InverseTri(a Triangular) error {
346 if rt, ok := a.(RawTriangular); ok {
347 t.checkOverlap(generalFromTriangular(rt.RawTriangular()))
350 t.reuseAs(a.Triangle())
352 work := getFloats(3*n, false)
353 iwork := getInts(n, false)
354 cond := lapack64.Trcon(CondNorm, t.mat, work, iwork)
357 if math.IsInf(cond, 1) {
358 return Condition(cond)
360 ok := lapack64.Trtri(t.mat)
362 return Condition(math.Inf(1))
364 if cond > ConditionTolerance {
365 return Condition(cond)
370 // MulTri takes the product of triangular matrices a and b and places the result
371 // in the receiver. The size of a and b must match, and they both must have the
372 // same TriKind, or Mul will panic.
373 func (t *TriDense) MulTri(a, b Triangular) {
374 n, kind := a.Triangle()
375 nb, kindb := b.Triangle()
383 aU, _ := untransposeTri(a)
384 bU, _ := untransposeTri(b)
388 t, restore = t.isolatedWorkspace(aU)
391 t, restore = t.isolatedWorkspace(bU)
395 // TODO(btracey): Improve the set of fast-paths.
397 for i := 0; i < n; i++ {
398 for j := i; j < n; j++ {
400 for k := i; k <= j; k++ {
401 v += a.At(i, k) * b.At(k, j)
408 for i := 0; i < n; i++ {
409 for j := 0; j <= i; j++ {
411 for k := j; k <= i; k++ {
412 v += a.At(i, k) * b.At(k, j)
419 // ScaleTri multiplies the elements of a by f, placing the result in the receiver.
420 // If the receiver is non-zero, the size and kind of the receiver must match
421 // the input, or ScaleTri will panic.
422 func (t *TriDense) ScaleTri(f float64, a Triangular) {
423 n, kind := a.Triangle()
426 // TODO(btracey): Improve the set of fast-paths.
427 switch a := a.(type) {
429 amat := a.RawTriangular()
431 for i := 0; i < n; i++ {
432 ts := t.mat.Data[i*t.mat.Stride+i : i*t.mat.Stride+n]
433 as := amat.Data[i*amat.Stride+i : i*amat.Stride+n]
434 for i, v := range as {
440 for i := 0; i < n; i++ {
441 ts := t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+i+1]
442 as := amat.Data[i*amat.Stride : i*amat.Stride+i+1]
443 for i, v := range as {
449 isUpper := kind == Upper
450 for i := 0; i < n; i++ {
452 for j := i; j < n; j++ {
453 t.set(i, j, f*a.At(i, j))
456 for j := 0; j <= i; j++ {
457 t.set(i, j, f*a.At(i, j))
464 // copySymIntoTriangle copies a symmetric matrix into a TriDense
465 func copySymIntoTriangle(t *TriDense, s Symmetric) {
466 n, upper := t.Triangle()
469 panic("mat: triangle size mismatch")
472 if rs, ok := s.(RawSymmetricer); ok {
473 sd := rs.RawSymmetric()
476 if sd.Uplo == blas.Upper {
477 for i := 0; i < n; i++ {
478 copy(t.mat.Data[i*ts+i:i*ts+n], sd.Data[i*ss+i:i*ss+n])
482 for i := 0; i < n; i++ {
483 for j := i; j < n; j++ {
484 t.mat.Data[i*ts+j] = sd.Data[j*ss+i]
489 if sd.Uplo == blas.Upper {
490 for i := 0; i < n; i++ {
491 for j := 0; j <= i; j++ {
492 t.mat.Data[i*ts+j] = sd.Data[j*ss+i]
497 for i := 0; i < n; i++ {
498 copy(t.mat.Data[i*ts:i*ts+i+1], sd.Data[i*ss:i*ss+i+1])
503 for i := 0; i < n; i++ {
504 for j := i; j < n; j++ {
505 t.mat.Data[i*ts+j] = s.At(i, j)
510 for i := 0; i < n; i++ {
511 for j := 0; j <= i; j++ {
512 t.mat.Data[i*ts+j] = s.At(i, j)
517 // DoNonZero calls the function fn for each of the non-zero elements of t. The function fn
518 // takes a row/column index and the element value of t at (i, j).
519 func (t *TriDense) DoNonZero(fn func(i, j int, v float64)) {
521 for i := 0; i < t.mat.N; i++ {
522 for j := i; j < t.mat.N; j++ {
531 for i := 0; i < t.mat.N; i++ {
532 for j := 0; j <= i; j++ {
541 // DoRowNonZero calls the function fn for each of the non-zero elements of row i of t. The function fn
542 // takes a row/column index and the element value of t at (i, j).
543 func (t *TriDense) DoRowNonZero(i int, fn func(i, j int, v float64)) {
544 if i < 0 || t.mat.N <= i {
548 for j := i; j < t.mat.N; j++ {
556 for j := 0; j <= i; j++ {
564 // DoColNonZero calls the function fn for each of the non-zero elements of column j of t. The function fn
565 // takes a row/column index and the element value of t at (i, j).
566 func (t *TriDense) DoColNonZero(j int, fn func(i, j int, v float64)) {
567 if j < 0 || t.mat.N <= j {
571 for i := 0; i <= j; i++ {
579 for i := j; i < t.mat.N; i++ {