/* * Compute * B = B*diag(D).-1 flags & RIGHT == true * B = diag(D).-1*B flags & LEFT == true * * If flags is LEFT (RIGHT) then element-wise divides columns (rows) of B with vector D. * * Arguments: * B M-by-N matrix if flags&RIGHT == true or N-by-M matrix if flags&LEFT == true * * D N element column or row vector or N-by-N matrix * * flags Indicator bits, LEFT or RIGHT */ func SolveDiag(B, D *cmat.FloatMatrix, flags int, confs ...*gomas.Config) *gomas.Error { var c, d0 cmat.FloatMatrix var d *cmat.FloatMatrix conf := gomas.CurrentConf(confs...) d = D if !D.IsVector() { d0.Diag(D) d = &d0 } dn := d0.Len() br, bc := B.Size() switch flags & (gomas.LEFT | gomas.RIGHT) { case gomas.LEFT: if br != dn { return gomas.NewError(gomas.ESIZE, "SolveDiag") } // scale rows; for k := 0; k < dn; k++ { c.Row(B, k) blasd.InvScale(&c, d.GetAt(k), conf) } case gomas.RIGHT: if bc != dn { return gomas.NewError(gomas.ESIZE, "SolveDiag") } // scale columns for k := 0; k < dn; k++ { c.Column(B, k) blasd.InvScale(&c, d.GetAt(k), conf) } } return nil }
// unblocked LU decomposition w/o pivots, FLAME LU nopivots variant 5 func unblockedLUnoPiv(A *cmat.FloatMatrix, conf *gomas.Config) *gomas.Error { var ATL, ATR, ABL, ABR cmat.FloatMatrix var A00, a01, A02, a10, a11, a12, A20, a21, A22 cmat.FloatMatrix var err *gomas.Error = nil util.Partition2x2( &ATL, &ATR, &ABL, &ABR, A, 0, 0, util.PTOPLEFT) for m(&ATL) < m(A) { util.Repartition2x2to3x3(&ATL, &A00, &a01, &A02, &a10, &a11, &a12, &A20, &a21, &A22, A, 1, util.PBOTTOMRIGHT) // a21 = a21/a11 blasd.InvScale(&a21, a11.Get(0, 0)) // A22 = A22 - a21*a12 blasd.MVUpdate(&A22, &a21, &a12, -1.0) util.Continue3x3to2x2( &ATL, &ATR, &ABL, &ABR, &A00, &a11, &A22, A, util.PBOTTOMRIGHT) } return err }
// Compute eigenvector corresponding precomputed deltas func trdsecEigenVecDelta(qi, delta, z *cmat.FloatMatrix) { var dk, zk float64 for k := 0; k < delta.Len(); k++ { zk = z.GetAt(k) dk = delta.GetAt(k) qi.SetAt(k, zk/dk) } s := blasd.Nrm2(qi) blasd.InvScale(qi, s) }
func TestDVecScal(t *testing.T) { const N = 911 X := cmat.NewMatrix(N, 1) Y := cmat.NewMatrix(N, 1) zeromean := cmat.NewFloatUniformSource(2.0, 0.5) X.SetFrom(zeromean) Y.Copy(X) // B = A*B blasd.Scale(X, 2.0) blasd.InvScale(X, 2.0) ok := X.AllClose(Y) t.Logf("X = InvScale(Scale(X, 2.0), 2.0) : %v\n", ok) }
func trdsecEigenBuildInplace(Q, z *cmat.FloatMatrix) { var QTL, QBR, Q00, q11, q12, q21, Q22, qi cmat.FloatMatrix var zk0, zk1, dk0, dk1 float64 util.Partition2x2( &QTL, nil, nil, &QBR /**/, Q, 0, 0, util.PTOPLEFT) for m(&QBR) > 0 { util.Repartition2x2to3x3(&QTL, &Q00, nil, nil, nil, &q11, &q12, nil, &q21, &Q22 /**/, Q, 1, util.PBOTTOMRIGHT) //--------------------------------------------------------------- k := m(&Q00) zk0 = z.GetAt(k) dk0 = q11.Get(0, 0) q11.Set(0, 0, zk0/dk0) for i := 0; i < q12.Len(); i++ { zk1 = z.GetAt(k + i + 1) dk0 = q12.GetAt(i) dk1 = q21.GetAt(i) q12.SetAt(i, zk0/dk1) q21.SetAt(i, zk1/dk0) } //--------------------------------------------------------------- util.Continue3x3to2x2( &QTL, nil, nil, &QBR /**/, &Q00, &q11, &Q22 /**/, Q, util.PBOTTOMRIGHT) } // scale column eigenvectors for k := 0; k < z.Len(); k++ { qi.Column(Q, k) t := blasd.Nrm2(&qi) blasd.InvScale(&qi, t) } }
func test1(N int, beta float64, t *testing.T) { var sI cmat.FloatMatrix if N&0x1 != 0 { N = N + 1 } D := cmat.NewMatrix(N, 1) Z := cmat.NewMatrix(N, 1) Y := cmat.NewMatrix(N, 1) V := cmat.NewMatrix(N, 1) Q := cmat.NewMatrix(N, N) I := cmat.NewMatrix(N, N) D.SetAt(0, 1.0) Z.SetAt(0, 2.0) for i := 1; i < N-1; i++ { if i < N/2 { D.SetAt(i, 2.0-float64(N/2-i)*beta) } else { D.SetAt(i, 2.0+float64(i+1-N/2)*beta) } Z.SetAt(i, beta) } D.SetAt(N-1, 10.0/3.0) Z.SetAt(N-1, 2.0) w := blasd.Nrm2(Z) blasd.InvScale(Z, w) rho := 1.0 / (w * w) lapackd.TRDSecularSolveAll(Y, V, Q, D, Z, rho) lapackd.TRDSecularEigen(Q, V, nil) blasd.Mult(I, Q, Q, 1.0, 0.0, gomas.TRANSA) sI.Diag(I) sI.Add(-1.0) nrm := lapackd.NormP(I, lapackd.NORM_ONE) t.Logf("N=%d, beta=%e ||I - Q.T*Q||_1: %e\n", N, beta, nrm) }
func unblockedLowerCHOL(A *cmat.FloatMatrix, flags int, nr int) (err *gomas.Error) { var ATL, ATR, ABL, ABR cmat.FloatMatrix var A00, a10, a11, A20, a21, A22 cmat.FloatMatrix err = nil util.Partition2x2( &ATL, &ATR, &ABL, &ABR, A, 0, 0, util.PTOPLEFT) for m(&ATL) < m(A) { util.Repartition2x2to3x3(&ATL, &A00, nil, nil, &a10, &a11, nil, &A20, &a21, &A22, A, 1, util.PBOTTOMRIGHT) // a11 = sqrt(a11) aval := a11.Get(0, 0) if aval < 0.0 { if err == nil { err = gomas.NewError(gomas.ENEGATIVE, "DecomposeCHOL", m(&ATL)+nr) } } else { a11.Set(0, 0, math.Sqrt(aval)) } // a21 = a21/a11 blasd.InvScale(&a21, a11.Get(0, 0)) // A22 = A22 - a21*a21' (SYR) blasd.MVUpdateSym(&A22, &a21, -1.0, gomas.LOWER) util.Continue3x3to2x2( &ATL, &ATR, &ABL, &ABR, &A00, &a11, &A22, A, util.PBOTTOMRIGHT) } return }
/* From LAPACK/dlarfg.f * * Generates a real elementary reflector H of order n, such * that * * H * ( alpha ) = ( beta ), H**T * H = I. * ( x ) ( 0 ) * * where alpha and beta are scalars, and x is an (n-1)-element real * vector. H is represented in the form * * H = I - tau * ( 1 ) * ( 1 v**T ) , * ( v ) * * where tau is a real scalar and v is a real (n-1)-element * vector. * * If the elements of x are all zero, then tau = 0 and H is taken to be * the unit cmat. * * Otherwise 1 <= tau <= 2. */ func computeHouseholder(a11, x, tau *cmat.FloatMatrix) { // norm_x2 = ||x||_2 norm_x2 := blasd.Nrm2(x) if norm_x2 == 0.0 { tau.Set(0, 0, 0.0) return } alpha := a11.Get(0, 0) sign := 1.0 if math.Signbit(alpha) { sign = -1.0 } // beta = -(alpha / |alpha|) * ||alpha x|| // = -sign(alpha) * sqrt(alpha**2, norm_x2**2) beta := -sign * sqrtX2Y2(alpha, norm_x2) // x = x /(a11 - beta) blasd.InvScale(x, alpha-beta) tau.Set(0, 0, (beta-alpha)/beta) a11.Set(0, 0, beta) }
/* * Unblocked solve A*X = B for Bunch-Kauffman factorized symmetric real matrix. */ func unblkSolveBKUpper(B, A *cmat.FloatMatrix, p Pivots, phase int, conf *gomas.Config) *gomas.Error { var err *gomas.Error = nil var ATL, ATR, ABL, ABR cmat.FloatMatrix var A00, a01, A02, a11, a12t, A22 cmat.FloatMatrix var Aref *cmat.FloatMatrix var BT, BB, B0, b1, B2, Bx cmat.FloatMatrix var pT, pB, p0, p1, p2 Pivots var aStart, aDir, bStart, bDir util.Direction var nc int np := 0 if phase == 2 { aStart = util.PTOPLEFT aDir = util.PBOTTOMRIGHT bStart = util.PTOP bDir = util.PBOTTOM nc = 1 Aref = &ABR } else { aStart = util.PBOTTOMRIGHT aDir = util.PTOPLEFT bStart = util.PBOTTOM bDir = util.PTOP nc = m(A) Aref = &ATL } util.Partition2x2( &ATL, &ATR, &ABL, &ABR, A, 0, 0, aStart) util.Partition2x1( &BT, &BB, B, 0, bStart) partitionPivot2x1( &pT, &pB, p, 0, bStart) // phase 1: // - solve U*D*X = B, overwriting B with X // - looping from BOTTOM to TOP // phase 1: // - solve U*X = B, overwriting B with X // - looping from TOP to BOTTOM for n(Aref) > 0 { // see if next diagonal block is 1x1 or 2x2 np = 1 if p[nc-1] < 0 { np = 2 } // repartition according the pivot size util.Repartition2x2to3x3(&ATL, &A00, &a01, &A02, nil, &a11, &a12t, nil, nil, &A22 /**/, A, np, aDir) util.Repartition2x1to3x1(&BT, &B0, &b1, &B2 /**/, B, np, bDir) repartPivot2x1to3x1(&pT, &p0, &p1, &p2 /**/, p, np, bDir) // ------------------------------------------------------------ switch phase { case 1: // computes D.-1*(U.-1*B); // b1 is current row, last row of BT if np == 1 { if p1[0] != nc { // swap rows on top part of B swapRows(&BT, m(&BT)-1, p1[0]-1) } // B0 = B0 - a01*b1 blasd.MVUpdate(&B0, &a01, &b1, -1.0) // b1 = b1/d1 blasd.InvScale(&b1, a11.Get(0, 0)) nc -= 1 } else if np == 2 { if p1[0] != -nc { // swap rows on top part of B swapRows(&BT, m(&BT)-2, -p1[0]-1) } b := a11.Get(0, 1) apb := a11.Get(0, 0) / b dpb := a11.Get(1, 1) / b // (a/b)*(d/b)-1.0 == (a*d - b^2)/b^2 scale := apb*dpb - 1.0 scale *= b // B0 = B0 - a01*b1 blasd.Mult(&B0, &a01, &b1, -1.0, 1.0, gomas.NONE, conf) // b1 = a11.-1*b1.T //(2x2 block, no subroutine for doing this in-place) for k := 0; k < n(&b1); k++ { s0 := b1.Get(0, k) s1 := b1.Get(1, k) b1.Set(0, k, (dpb*s0-s1)/scale) b1.Set(1, k, (apb*s1-s0)/scale) } nc -= 2 } case 2: // compute X = U.-T*B if np == 1 { blasd.MVMult(&b1, &B0, &a01, -1.0, 1.0, gomas.TRANS) if p1[0] != nc { // swap rows on bottom part of B util.Merge2x1(&Bx, &B0, &b1) swapRows(&Bx, m(&Bx)-1, p1[0]-1) } nc += 1 } else if np == 2 { blasd.Mult(&b1, &a01, &B0, -1.0, 1.0, gomas.TRANSA, conf) if p1[0] != -nc { // swap rows on bottom part of B util.Merge2x1(&Bx, &B0, &b1) swapRows(&Bx, m(&Bx)-2, -p1[0]-1) } nc += 2 } } // ------------------------------------------------------------ util.Continue3x3to2x2( &ATL, &ATR, &ABL, &ABR, &A00, &a11, &A22, A, aDir) util.Continue3x1to2x1( &BT, &BB, &B0, &b1, B, bDir) contPivot3x1to2x1( &pT, &pB, p0, p1, p, bDir) } return err }
func unblkBoundedBKUpper(A, wrk *cmat.FloatMatrix, p *Pivots, ncol int, conf *gomas.Config) (*gomas.Error, int) { var err *gomas.Error = nil var ATL, ATR, ABL, ABR cmat.FloatMatrix var A00, a01, A02, a11, a12, A22, a11inv cmat.FloatMatrix var w00, w01, w11 cmat.FloatMatrix var cwrk cmat.FloatMatrix var pT, pB, p0, p1, p2 Pivots err = nil nc := 0 if ncol > n(A) { ncol = n(A) } // permanent working space for symmetric inverse of a11 a11inv.SubMatrix(wrk, m(wrk)-2, 0, 2, 2) a11inv.Set(0, 1, -1.0) a11inv.Set(1, 0, -1.0) util.Partition2x2( &ATL, &ATR, &ABL, &ABR, A, 0, 0, util.PBOTTOMRIGHT) partitionPivot2x1( &pT, &pB, *p, 0, util.PBOTTOM) for n(&ATL) > 0 && nc < ncol { util.Partition2x2( &w00, &w01, nil, &w11, wrk, nc, nc, util.PBOTTOMRIGHT) r, np := findAndBuildBKPivotUpper(&ATL, &ATR, &w00, &w01, nc) if np > ncol-nc { // next pivot does not fit into ncol columns, // return with number of factorized columns return err, nc } cwrk.SubMatrix(&w00, 0, n(&w00)-np, m(&ATL), np) if r != -1 { // pivoting needed; do swaping here k := m(&ATL) - np applyBKPivotSymUpper(&ATL, k, r) // swap right hand rows to get correct updates swapRows(&ATR, k, r) swapRows(&w01, k, r) if np == 2 && r != k { /* for 2x2 blocks we need diagonal pivots. * [r, r] | [ r,-1] * a11 == ---------------- 2-by-2 pivot, swapping [1,0] and [r,0] * [-1,r] | [-1,-1] */ t0 := w00.Get(k, -1) tr := w00.Get(r, -1) w00.Set(k, -1, tr) w00.Set(r, -1, t0) t0 = w00.Get(k, -2) tr = w00.Get(r, -2) w00.Set(k, -2, tr) w00.Set(r, -2, t0) } } // repartition according the pivot size util.Repartition2x2to3x3(&ATL, &A00, &a01, &A02, nil, &a11, &a12, nil, nil, &A22 /**/, A, np, util.PTOPLEFT) repartPivot2x1to3x1(&pT, &p0, &p1, &p2 /**/, *p, np, util.PTOP) // ------------------------------------------------------------ wlc := n(&w00) - np cwrk.SubMatrix(&w00, 0, wlc, m(&a01), n(&a01)) if np == 1 { // a11.Set(0, 0, w00.Get(m(&a01), wlc)) // a21 = a21/a11 blasd.Copy(&a01, &cwrk) blasd.InvScale(&a01, a11.Get(0, 0)) // store pivot point relative to original matrix if r == -1 { p1[0] = m(&ATL) } else { p1[0] = r + 1 } } else if np == 2 { /* a | b d/b | -1 * w00 == ------ == a11 --> a11.-1 == -------- * scale * . | d -1 | a/b */ a := w00.Get(m(&ATL)-2, -2) b := w00.Get(m(&ATL)-2, -1) d := w00.Get(m(&ATL)-1, -1) a11inv.Set(0, 0, d/b) a11inv.Set(1, 1, a/b) // denominator: (a/b)*(d/b)-1.0 == (a*d - b^2)/b^2 scale := 1.0 / ((a/b)*(d/b) - 1.0) scale /= b // a01 = a01*a11.-1 blasd.Mult(&a01, &cwrk, &a11inv, scale, 0.0, gomas.NONE, conf) a11.Set(0, 0, a) a11.Set(0, 1, b) a11.Set(1, 1, d) // store pivot point relative to original matrix p1[0] = -(r + 1) p1[1] = p1[0] } // ------------------------------------------------------------ nc += np util.Continue3x3to2x2( &ATL, &ATR, &ABL, &ABR, &A00, &a11, &A22, A, util.PTOPLEFT) contPivot3x1to2x1( &pT, &pB, p0, p1, *p, util.PTOP) } return err, nc }
/* * Unblocked Bunch-Kauffman LDL factorization. * * Corresponds lapack.DSYTF2 */ func unblkDecompBKUpper(A, wrk *cmat.FloatMatrix, p Pivots, conf *gomas.Config) (*gomas.Error, int) { var err *gomas.Error = nil var ATL, ATR, ABL, ABR cmat.FloatMatrix var A00, a01, A02, a11, a12, A22, a11inv cmat.FloatMatrix var cwrk cmat.FloatMatrix var pT, pB, p0, p1, p2 Pivots err = nil nc := 0 util.Partition2x2( &ATL, &ATR, &ABL, &ABR, A, 0, 0, util.PBOTTOMRIGHT) partitionPivot2x1( &pT, &pB, p, 0, util.PBOTTOM) // permanent working space for symmetric inverse of a11 a11inv.SubMatrix(wrk, 0, n(wrk)-2, 2, 2) a11inv.Set(1, 0, -1.0) a11inv.Set(0, 1, -1.0) for n(&ATL) > 0 { nr := m(&ATL) - 1 r, np := findBKPivotUpper(&ATL) if r != -1 { cwrk.SubMatrix(&ATL, 0, n(&ATL)-np, m(&ATL), np) // pivoting needed; do swaping here applyBKPivotSymUpper(&ATL, m(&ATL)-np, r) if np == 2 { /* [r, r] | [r, nr] * a11 == ---------------- 2-by-2 pivot, swapping [nr-1,nr] and [r,nr] * [nr,r] | [nr,nr] (nr is the current diagonal entry) */ t := ATL.Get(nr-1, nr) ATL.Set(nr-1, nr, ATL.Get(r, nr)) ATL.Set(r, nr, t) } } // repartition according the pivot size util.Repartition2x2to3x3(&ATL, &A00, &a01, &A02, nil, &a11, &a12, nil, nil, &A22 /**/, A, np, util.PTOPLEFT) repartPivot2x1to3x1(&pT, &p0, &p1, &p2 /**/, p, np, util.PTOP) // ------------------------------------------------------------ if np == 1 { // A00 = A00 - a01*a01.T/a11 blasd.MVUpdateTrm(&A00, &a01, &a01, -1.0/a11.Get(0, 0), gomas.UPPER) // a01 = a01/a11 blasd.InvScale(&a01, a11.Get(0, 0)) // store pivot point relative to original matrix if r == -1 { p1[0] = m(&ATL) } else { p1[0] = r + 1 } } else if np == 2 { /* see comments on unblkDecompBKLower() */ a := a11.Get(0, 0) b := a11.Get(0, 1) d := a11.Get(1, 1) a11inv.Set(0, 0, d/b) a11inv.Set(1, 1, a/b) // denominator: (a/b)*(d/b)-1.0 == (a*d - b^2)/b^2 scale := 1.0 / ((a/b)*(d/b) - 1.0) scale /= b // cwrk = a21 cwrk.SubMatrix(wrk, 2, 0, m(&a01), n(&a01)) blasd.Copy(&cwrk, &a01) // a01 = a01*a11.-1 blasd.Mult(&a01, &cwrk, &a11inv, scale, 0.0, gomas.NONE, conf) // A00 = A00 - a01*a11.-1*a01.T = A00 - a01*cwrk.T blasd.UpdateTrm(&A00, &a01, &cwrk, -1.0, 1.0, gomas.UPPER|gomas.TRANSB, conf) // store pivot point relative to original matrix p1[0] = -(r + 1) p1[1] = p1[0] } // ------------------------------------------------------------ nc += np util.Continue3x3to2x2( &ATL, &ATR, &ABL, &ABR, &A00, &a11, &A22, A, util.PTOPLEFT) contPivot3x1to2x1( &pT, &pB, p0, p1, p, util.PTOP) } return err, nc }
/* * Unblocked, bounded Bunch-Kauffman LDL factorization for at most ncol columns. * At most ncol columns are factorized and trailing matrix updates are restricted * to ncol columns. Also original columns are accumulated to working matrix, which * is used by calling blocked algorithm to update the trailing matrix with BLAS3 * update. * * Corresponds lapack.DLASYF */ func unblkBoundedBKLower(A, wrk *cmat.FloatMatrix, p *Pivots, ncol int, conf *gomas.Config) (*gomas.Error, int) { var err *gomas.Error = nil var ATL, ATR, ABL, ABR cmat.FloatMatrix var A00, a10t, a11, A20, a21, A22, a11inv cmat.FloatMatrix var w00, w10, w11 cmat.FloatMatrix var cwrk cmat.FloatMatrix var pT, pB, p0, p1, p2 Pivots err = nil nc := 0 if ncol > n(A) { ncol = n(A) } // permanent working space for symmetric inverse of a11 a11inv.SubMatrix(wrk, 0, n(wrk)-2, 2, 2) a11inv.Set(1, 0, -1.0) a11inv.Set(0, 1, -1.0) util.Partition2x2( &ATL, &ATR, &ABL, &ABR, A, 0, 0, util.PTOPLEFT) partitionPivot2x1( &pT, &pB, *p, 0, util.PTOP) for n(&ABR) > 0 && nc < ncol { util.Partition2x2( &w00, nil, &w10, &w11, wrk, nc, nc, util.PTOPLEFT) r, np := findAndBuildBKPivotLower(&ABL, &ABR, &w10, &w11, nc) if np > ncol-nc { // next pivot does not fit into ncol columns, restore last column, // return with number of factorized columns return err, nc } if r != 0 && r != np-1 { // pivoting needed; do swaping here applyBKPivotSymLower(&ABR, np-1, r) // swap left hand rows to get correct updates swapRows(&ABL, np-1, r) swapRows(&w10, np-1, r) if np == 2 { /* * [0,0] | [r,0] * a11 == ------------- 2-by-2 pivot, swapping [1,0] and [r,0] * [r,0] | [r,r] */ t0 := w11.Get(1, 0) tr := w11.Get(r, 0) w11.Set(1, 0, tr) w11.Set(r, 0, t0) // interchange diagonal entries on w11[:,1] t0 = w11.Get(1, 1) tr = w11.Get(r, 1) w11.Set(1, 1, tr) w11.Set(r, 1, t0) } } // repartition according the pivot size util.Repartition2x2to3x3(&ATL, &A00, nil, nil, &a10t, &a11, nil, &A20, &a21, &A22 /**/, A, np, util.PBOTTOMRIGHT) repartPivot2x1to3x1(&pT, &p0, &p1, &p2 /**/, *p, np, util.PBOTTOM) // ------------------------------------------------------------ if np == 1 { // cwrk.SubMatrix(&w11, np, 0, m(&a21), np) a11.Set(0, 0, w11.Get(0, 0)) // a21 = a21/a11 blasd.Copy(&a21, &cwrk) blasd.InvScale(&a21, a11.Get(0, 0)) // store pivot point relative to original matrix p1[0] = r + m(&ATL) + 1 } else if np == 2 { /* * See comments for this block in unblkDecompBKLower(). */ a := w11.Get(0, 0) b := w11.Get(1, 0) d := w11.Get(1, 1) a11inv.Set(0, 0, d/b) a11inv.Set(1, 1, a/b) // denominator: (a/b)*(d/b)-1.0 == (a*d - b^2)/b^2 scale := 1.0 / ((a/b)*(d/b) - 1.0) scale /= b cwrk.SubMatrix(&w11, np, 0, m(&a21), np) // a21 = a21*a11.-1 blasd.Mult(&a21, &cwrk, &a11inv, scale, 0.0, gomas.NONE, conf) a11.Set(0, 0, a) a11.Set(1, 0, b) a11.Set(1, 1, d) // store pivot point relative to original matrix p1[0] = -(r + m(&ATL) + 1) p1[1] = p1[0] } // ------------------------------------------------------------ nc += np util.Continue3x3to2x2( &ATL, &ATR, &ABL, &ABR, &A00, &a11, &A22, A, util.PBOTTOMRIGHT) contPivot3x1to2x1( &pT, &pB, p0, p1, *p, util.PBOTTOM) } return err, nc }
/* * Unblocked Bunch-Kauffman LDL factorization. * * Corresponds lapack.DSYTF2 */ func unblkDecompBKLower(A, wrk *cmat.FloatMatrix, p Pivots, conf *gomas.Config) (*gomas.Error, int) { var err *gomas.Error = nil var ATL, ATR, ABL, ABR cmat.FloatMatrix var A00, a10t, a11, A20, a21, A22, a11inv cmat.FloatMatrix var cwrk cmat.FloatMatrix var pT, pB, p0, p1, p2 Pivots err = nil nc := 0 util.Partition2x2( &ATL, &ATR, &ABL, &ABR, A, 0, 0, util.PTOPLEFT) partitionPivot2x1( &pT, &pB, p, 0, util.PTOP) // permanent working space for symmetric inverse of a11 a11inv.SubMatrix(wrk, 0, n(wrk)-2, 2, 2) a11inv.Set(1, 0, -1.0) a11inv.Set(0, 1, -1.0) for n(&ABR) > 0 { r, np := findBKPivotLower(&ABR) if r != 0 && r != np-1 { // pivoting needed; do swaping here applyBKPivotSymLower(&ABR, np-1, r) if np == 2 { /* [0,0] | [r,0] * a11 == ------------- 2-by-2 pivot, swapping [1,0] and [r,0] * [r,0] | [r,r] */ t := ABR.Get(1, 0) ABR.Set(1, 0, ABR.Get(r, 0)) ABR.Set(r, 0, t) } } // repartition according the pivot size util.Repartition2x2to3x3(&ATL, &A00, nil, nil, &a10t, &a11, nil, &A20, &a21, &A22 /**/, A, np, util.PBOTTOMRIGHT) repartPivot2x1to3x1(&pT, &p0, &p1, &p2 /**/, p, np, util.PBOTTOM) // ------------------------------------------------------------ if np == 1 { // A22 = A22 - a21*a21.T/a11 blasd.MVUpdateTrm(&A22, &a21, &a21, -1.0/a11.Get(0, 0), gomas.LOWER) // a21 = a21/a11 blasd.InvScale(&a21, a11.Get(0, 0)) // store pivot point relative to original matrix p1[0] = r + m(&ATL) + 1 } else if np == 2 { /* from Bunch-Kaufmann 1977: * (E2 C.T) = ( I2 0 )( E 0 )( I[n-2] E.-1*C.T ) * (C B ) ( C*E.-1 I[n-2] )( 0 A[n-2] )( 0 I2 ) * * A[n-2] = B - C*E.-1*C.T * * E.-1 is inverse of a symmetric matrix, cannot use * triangular solve. We calculate inverse of 2x2 matrix. * Following is inspired by lapack.SYTF2 * * a | b 1 d | -b b d/b | -1 * inv ----- = ------ * ------ = ----------- * -------- * b | d (ad-b^2) -b | a (a*d - b^2) -1 | a/b * */ a := a11.Get(0, 0) b := a11.Get(1, 0) d := a11.Get(1, 1) a11inv.Set(0, 0, d/b) a11inv.Set(1, 1, a/b) // denominator: (a/b)*(d/b)-1.0 == (a*d - b^2)/b^2 scale := 1.0 / ((a/b)*(d/b) - 1.0) scale /= b // cwrk = a21 cwrk.SubMatrix(wrk, 2, 0, m(&a21), n(&a21)) blasd.Copy(&cwrk, &a21) // a21 = a21*a11.-1 blasd.Mult(&a21, &cwrk, &a11inv, scale, 0.0, gomas.NONE, conf) // A22 = A22 - a21*a11.-1*a21.T = A22 - a21*cwrk.T blasd.UpdateTrm(&A22, &a21, &cwrk, -1.0, 1.0, gomas.LOWER|gomas.TRANSB, conf) // store pivot point relative to original matrix p1[0] = -(r + m(&ATL) + 1) p1[1] = p1[0] } // ------------------------------------------------------------ nc += np util.Continue3x3to2x2( &ATL, &ATR, &ABL, &ABR, &A00, &a11, &A22, A, util.PBOTTOMRIGHT) contPivot3x1to2x1( &pT, &pB, p0, p1, p, util.PBOTTOM) } return err, nc }
// unblocked LU decomposition with pivots: FLAME LU variant 3; Left-looking func unblockedLUpiv(A *cmat.FloatMatrix, p *Pivots, offset int, conf *gomas.Config) *gomas.Error { var err *gomas.Error = nil var ATL, ATR, ABL, ABR cmat.FloatMatrix var A00, a01, A02, a10, a11, a12, A20, a21, A22 cmat.FloatMatrix var AL, AR, A0, a1, A2, aB1, AB0 cmat.FloatMatrix var pT, pB, p0, p1, p2 Pivots err = nil util.Partition2x2( &ATL, &ATR, &ABL, &ABR, A, 0, 0, util.PTOPLEFT) util.Partition1x2( &AL, &AR, A, 0, util.PLEFT) partitionPivot2x1( &pT, &pB, *p, 0, util.PTOP) for m(&ATL) < m(A) && n(&ATL) < n(A) { util.Repartition2x2to3x3(&ATL, &A00, &a01, &A02, &a10, &a11, &a12, &A20, &a21, &A22 /**/, A, 1, util.PBOTTOMRIGHT) util.Repartition1x2to1x3(&AL, &A0, &a1, &A2 /**/, A, 1, util.PRIGHT) repartPivot2x1to3x1(&pT, &p0, &p1, &p2 /**/, *p, 1, util.PBOTTOM) // apply previously computed pivots on current column applyPivots(&a1, p0) // a01 = trilu(A00) \ a01 (TRSV) blasd.MVSolveTrm(&a01, &A00, 1.0, gomas.LOWER|gomas.UNIT) // a11 = a11 - a10 *a01 aval := a11.Get(0, 0) - blasd.Dot(&a10, &a01) a11.Set(0, 0, aval) // a21 = a21 -A20*a01 blasd.MVMult(&a21, &A20, &a01, -1.0, 1.0, gomas.NONE) // pivot index on current column [a11, a21].T aB1.Column(&ABR, 0) p1[0] = pivotIndex(&aB1) // pivots to current column applyPivots(&aB1, p1) // a21 = a21 / a11 if aval == 0.0 { if err == nil { ij := m(&ATL) + p1[0] - 1 err = gomas.NewError(gomas.ESINGULAR, "DecomposeLU", ij) } } else { blasd.InvScale(&a21, a11.Get(0, 0)) } // apply pivots to previous columns AB0.SubMatrix(&ABL, 0, 0) applyPivots(&AB0, p1) // scale last pivots to origin matrix row numbers p1[0] += m(&ATL) util.Continue3x3to2x2( &ATL, &ATR, &ABL, &ABR, &A00, &a11, &A22, A, util.PBOTTOMRIGHT) util.Continue1x3to1x2( &AL, &AR, &A0, &a1, A, util.PRIGHT) contPivot3x1to2x1( &pT, &pB, p0, p1, *p, util.PBOTTOM) } if n(&ATL) < n(A) { applyPivots(&ATR, *p) blasd.SolveTrm(&ATR, &ATL, 1.0, gomas.LEFT|gomas.UNIT|gomas.LOWER, conf) } return err }