// checkPLU checks that the PLU factorization contained in factorize matches // the original matrix contained in original. func checkPLU(t *testing.T, ok bool, m, n, lda int, ipiv []int, factorized, original []float64, tol float64, print bool) { var hasZeroDiagonal bool for i := 0; i < min(m, n); i++ { if factorized[i*lda+i] == 0 { hasZeroDiagonal = true break } } if hasZeroDiagonal && ok { t.Error("Has a zero diagonal but returned ok") } if !hasZeroDiagonal && !ok { t.Error("Non-zero diagonal but returned !ok") } // Check that the LU decomposition is correct. mn := min(m, n) l := make([]float64, m*mn) ldl := mn u := make([]float64, mn*n) ldu := n for i := 0; i < m; i++ { for j := 0; j < n; j++ { v := factorized[i*lda+j] switch { case i == j: l[i*ldl+i] = 1 u[i*ldu+i] = v case i > j: l[i*ldl+j] = v case i < j: u[i*ldu+j] = v } } } LU := blas64.General{ Rows: m, Cols: n, Stride: n, Data: make([]float64, m*n), } U := blas64.General{ Rows: mn, Cols: n, Stride: ldu, Data: u, } L := blas64.General{ Rows: m, Cols: mn, Stride: ldl, Data: l, } blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, L, U, 0, LU) p := make([]float64, m*m) ldp := m for i := 0; i < m; i++ { p[i*ldp+i] = 1 } for i := len(ipiv) - 1; i >= 0; i-- { v := ipiv[i] blas64.Swap(m, blas64.Vector{1, p[i*ldp:]}, blas64.Vector{1, p[v*ldp:]}) } P := blas64.General{ Rows: m, Cols: m, Stride: m, Data: p, } aComp := blas64.General{ Rows: m, Cols: n, Stride: lda, Data: make([]float64, m*lda), } copy(aComp.Data, factorized) blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, P, LU, 0, aComp) if !floats.EqualApprox(aComp.Data, original, tol) { if print { t.Errorf("PLU multiplication does not match original matrix.\nWant: %v\nGot: %v", original, aComp.Data) return } t.Error("PLU multiplication does not match original matrix.") } }
func testDgebak(t *testing.T, impl Dgebaker, job lapack.Job, side blas.Side, ilo, ihi int, v blas64.General, rnd *rand.Rand) { const tol = 1e-15 n := v.Rows m := v.Cols extra := v.Stride - v.Cols // Create D and D^{-1} by generating random scales between ilo and ihi. d := eye(n, n) dinv := eye(n, n) scale := nanSlice(n) if job == lapack.Scale || job == lapack.PermuteScale { if ilo == ihi { scale[ilo] = 1 } else { for i := ilo; i <= ihi; i++ { scale[i] = 2 * rnd.Float64() d.Data[i*d.Stride+i] = scale[i] dinv.Data[i*dinv.Stride+i] = 1 / scale[i] } } } // Create P by generating random column swaps. p := eye(n, n) if job == lapack.Permute || job == lapack.PermuteScale { // Make up some random permutations. for i := n - 1; i > ihi; i-- { scale[i] = float64(rnd.Intn(i + 1)) blas64.Swap(n, blas64.Vector{p.Stride, p.Data[i:]}, blas64.Vector{p.Stride, p.Data[int(scale[i]):]}) } for i := 0; i < ilo; i++ { scale[i] = float64(i + rnd.Intn(ihi-i+1)) blas64.Swap(n, blas64.Vector{p.Stride, p.Data[i:]}, blas64.Vector{p.Stride, p.Data[int(scale[i]):]}) } } got := cloneGeneral(v) impl.Dgebak(job, side, n, ilo, ihi, scale, m, got.Data, got.Stride) prefix := fmt.Sprintf("Case job=%v, side=%v, n=%v, ilo=%v, ihi=%v, m=%v, extra=%v", job, side, n, ilo, ihi, m, extra) if !generalOutsideAllNaN(got) { t.Errorf("%v: out-of-range write to V\n%v", prefix, got.Data) } // Compute D*V or D^{-1}*V and store into dv. dv := zeros(n, m, m) if side == blas.Right { blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, d, v, 0, dv) } else { blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, dinv, v, 0, dv) } // Compute P*D*V or P*D^{-1}*V and store into want. want := zeros(n, m, m) blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, p, dv, 0, want) if !equalApproxGeneral(want, got, tol) { t.Errorf("%v: unexpected value of V", prefix) } }
func testDgebal(t *testing.T, impl Dgebaler, job lapack.Job, a blas64.General) { const tol = 1e-14 n := a.Rows extra := a.Stride - n var scale []float64 if n > 0 { scale = nanSlice(n) } want := cloneGeneral(a) ilo, ihi := impl.Dgebal(job, n, a.Data, a.Stride, scale) prefix := fmt.Sprintf("Case job=%v, n=%v, extra=%v", job, n, extra) if !generalOutsideAllNaN(a) { t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data) } if n == 0 { if ilo != 0 { t.Errorf("%v: unexpected ilo when n=0. Want 0, got %v", prefix, n, ilo) } if ihi != -1 { t.Errorf("%v: unexpected ihi when n=0. Want -1, got %v", prefix, n, ihi) } return } if job == lapack.None { if ilo != 0 { t.Errorf("%v: unexpected ilo when job=None. Want 0, got %v", prefix, ilo) } if ihi != n-1 { t.Errorf("%v: unexpected ihi when job=None. Want %v, got %v", prefix, n-1, ihi) } k := -1 for i := range scale { if scale[i] != 1 { k = i break } } if k != -1 { t.Errorf("%v: unexpected scale[%v] when job=None. Want 1, got %v", prefix, k, scale[k]) } if !equalApproxGeneral(a, want, 0) { t.Errorf("%v: unexpected modification of A when job=None", prefix) } return } if ilo < 0 || ihi < ilo || n <= ihi { t.Errorf("%v: invalid ordering of ilo=%v and ihi=%v", prefix, ilo, ihi) } if ilo >= 2 && !isUpperTriangular(blas64.General{ilo - 1, ilo - 1, a.Stride, a.Data}) { t.Errorf("%v: T1 is not upper triangular", prefix) } m := n - ihi - 1 // Order of T2. k := ihi + 1 if m >= 2 && !isUpperTriangular(blas64.General{m, m, a.Stride, a.Data[k*a.Stride+k:]}) { t.Errorf("%v: T2 is not upper triangular", prefix) } if job == lapack.Permute || job == lapack.PermuteScale { // Check that all rows in [ilo:ihi+1] have at least one nonzero // off-diagonal element. zeroRow := -1 for i := ilo; i <= ihi; i++ { onlyZeros := true for j := ilo; j <= ihi; j++ { if i != j && a.Data[i*a.Stride+j] != 0 { onlyZeros = false break } } if onlyZeros { zeroRow = i break } } if zeroRow != -1 && ilo != ihi { t.Errorf("%v: row %v has only zero off-diagonal elements, ilo=%v, ihi=%v", prefix, zeroRow, ilo, ihi) } // Check that all columns in [ilo:ihi+1] have at least one nonzero // off-diagonal element. zeroCol := -1 for j := ilo; j <= ihi; j++ { onlyZeros := true for i := ilo; i <= ihi; i++ { if i != j && a.Data[i*a.Stride+j] != 0 { onlyZeros = false break } } if onlyZeros { zeroCol = j break } } if zeroCol != -1 && ilo != ihi { t.Errorf("%v: column %v has only zero off-diagonal elements, ilo=%v, ihi=%v", prefix, zeroCol, ilo, ihi) } // Create the permutation matrix P. p := eye(n, n) for j := n - 1; j > ihi; j-- { blas64.Swap(n, blas64.Vector{p.Stride, p.Data[j:]}, blas64.Vector{p.Stride, p.Data[int(scale[j]):]}) } for j := 0; j < ilo; j++ { blas64.Swap(n, blas64.Vector{p.Stride, p.Data[j:]}, blas64.Vector{p.Stride, p.Data[int(scale[j]):]}) } // Compute P^T*A*P and store into want. ap := zeros(n, n, n) blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, want, p, 0, ap) blas64.Gemm(blas.Trans, blas.NoTrans, 1, p, ap, 0, want) } if job == lapack.Scale || job == lapack.PermuteScale { // Modify want by D and D^{-1}. d := eye(n, n) dinv := eye(n, n) for i := ilo; i <= ihi; i++ { d.Data[i*d.Stride+i] = scale[i] dinv.Data[i*dinv.Stride+i] = 1 / scale[i] } ad := zeros(n, n, n) blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, want, d, 0, ad) blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, dinv, ad, 0, want) } if !equalApproxGeneral(want, a, tol) { t.Errorf("%v: unexpected value of A, ilo=%v, ihi=%v", prefix, ilo, ihi) } }