Skip to content

Commit 0600cdb

Browse files
committed
Add TypeTensor check for Hermitian positive-definiteness
In the real-valued case, the check reduces gracefully to symmetric positive-definiteness. We allow the user to specify a tolerance so that matrices which fail to satisfy the checks by a "small" amount (due to e.g. floating point error) can still be considered Hermitian positive definite.
1 parent b975b67 commit 0600cdb

2 files changed

Lines changed: 60 additions & 0 deletions

File tree

include/numerics/type_tensor.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,13 @@ class TypeTensor
436436
void write_unformatted (std::ostream & out_stream,
437437
const bool newline = true) const;
438438

439+
/**
440+
* Returns true if the TypeTensor is Hermitian (or symmetric, when
441+
* T==Real) and positive-definite to within the provided relative
442+
* tolerance, false otherwise.
443+
*/
444+
bool is_hpd(Real rel_tol = TOLERANCE*TOLERANCE) const;
445+
439446
protected:
440447

441448
/**

src/numerics/type_tensor.C

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,59 @@ void TypeTensor<T>::write_unformatted (std::ostream & out_stream,
6363
out_stream << '\n';
6464
}
6565

66+
template <typename T>
67+
bool TypeTensor<T>::is_hpd(Real rel_tol) const
68+
{
69+
// Convenient reference to this object, to be used for matrix
70+
// operations.
71+
const auto & A = *this;
72+
73+
// The norm of this matrix, needed for relative tolerance checks.
74+
auto A_norm = A.norm();
75+
76+
// The zero matrix is only positive semi-definite
77+
if (A_norm == 0)
78+
return false;
79+
80+
// An absolute tolerance (based on the user's provided relative
81+
// tolerance) to be used in the floating point comparisons below.
82+
auto abs_tol = rel_tol * A_norm;
83+
84+
// Form the complex conjugate transpose of A
85+
TypeTensor<T> A_conjugate_transpose;
86+
for (unsigned int i=0; i<LIBMESH_DIM; i++)
87+
for (unsigned int j=0; j<LIBMESH_DIM; j++)
88+
A_conjugate_transpose(i,j) = libmesh_conj(A(j,i));
89+
90+
// Check if Hermitian
91+
if ((A - A_conjugate_transpose).norm() > abs_tol)
92+
return false;
93+
94+
// If we made it here, then we are Hermitian, so now we just need to
95+
// check if we are positive-definite by checking that all principal
96+
// minors are positive. Note: the determinant of a Hermitian matrix
97+
// and all principal minors are real-valued. Since we already
98+
// checked that the matrix is Hermitian above, we don't bother to
99+
// check the complex parts are also zero now.
100+
if (std::real(A.det()) < -abs_tol)
101+
return false;
102+
103+
// For 3x3, also check the upper 2x2 determinant
104+
#if LIBMESH_DIM > 2
105+
if (std::real(A(0,0)*A(1,1) - A(0,1)*A(1,0)) < -abs_tol)
106+
return false;
107+
#endif
108+
109+
// For 3x3 and 2x2, also check the 1x1 determinant
110+
#if LIBMESH_DIM > 1
111+
if (std::real(A(0,0)) < -abs_tol)
112+
return false;
113+
#endif
114+
115+
// If we made it here, then the matrix is Hermitian
116+
// positive-definite.
117+
return true;
118+
}
66119

67120

68121
template <>

0 commit comments

Comments
 (0)