forte2.helpers.matrix_functions =============================== .. py:module:: forte2.helpers.matrix_functions Module Contents --------------- .. py:data:: MACHEPS :value: 1e-14 .. py:function:: print_metric_info(info, description=None) .. py:function:: invsqrt_matrix(M, rtol=1e-07, precomp=None) Compute the inverse square root of a symmetric (Hermitian) matrix A. :Parameters: **M** : NDArray A symmetric matrix (must be positive semi-definite). **rtol** : float, optional, default=1e-7 Relative threshold for treating eigenvalues as zero. Eigenvalues smaller than rtol * max_eigenvalue will be discarded in the computation of the inverse square root. **precomp** : tuple(NDArray, NDArray, dict), optional If provided, should be the output of _eigh_metric_kernel(M, rtol=rtol), i.e., (eigenvalues, eigenvectors, info). This allows reusing the eigen-decomposition if it has already been computed for the same matrix M with the same rtol, which can be more efficient if multiple functions need to use the same decomposition. :Returns: **invsqrt_M** : NDArray The inverse square root of A. **sqrt_M** : NDArray The square root of A. **info** : dict A dictionary containing additional information from the eigen-decomposition, including: - "max_eigenvalue": The largest eigenvalue of M. - "min_eigenvalue": The smallest eigenvalue of M. - "condition_number": The condition number of M (max_eigenvalue / min_eigenvalue). - "n_discarded": The number of eigenvalues discarded due to being below the threshold. - "n_kept": The number of eigenvalues kept. - "largest_discarded_eigenvalue": The largest eigenvalue that was discarded. - "smallest_kept_eigenvalue": The smallest eigenvalue that was kept. .. !! processed by numpydoc !! .. py:function:: canonical_orth(S, rtol=1e-07, precomp=None) Compute the canonical orthogonalization given the metric matrix S. :Parameters: **S** : NDArray Metric matrix (must be positive semi-definite). **rtol** : float, optional, default=1e-7 Relative threshold t for which values below t * max_eigenvalue are treated as zero. **precomp** : tuple(NDArray, NDArray, dict), optional If provided, should be the output of _eigh_metric_kernel(S, rtol=rtol), i.e., (eigenvalues, eigenvectors, info). This allows reusing the eigen-decomposition if it has already been computed for the same matrix S with the same rtol, which can be more efficient if multiple functions need to use the same decomposition. :Returns: **X** : NDArray The (possibly rectangular) canonical orthogonalization matrix X, such that ``X.T @ S @ X = I``. **Xm1** : NDArray The inverse of the orthogonalization matrix, such that ``Xm1 @ X = I``. **info** : dict A dictionary containing additional information, including: - "max_eigenvalue": The largest eigenvalue of S. - "min_eigenvalue": The smallest eigenvalue of S. - "condition_number": The condition number of S (max_eigenvalue / min_eigenvalue). - "n_discarded": The number of eigenvalues discarded due to being below the threshold. - "n_kept": The number of eigenvalues kept. - "largest_discarded_eigenvalue": The largest eigenvalue that was discarded. - "smallest_kept_eigenvalue": The smallest eigenvalue that was kept. :Raises: ValueError If the matrix S is not positive semi-definite. .. rubric:: Notes The canonical orthogonalization is defined as follows: .. math:: \mathbf{X}_{\eta} = \mathbf{U}_{\eta} \mathbf{s}^{-1/2}_{\eta}, where :math:`\mathbf{U}_{\eta}` are the eigenvectors of :math:`\mathbf{S}` corresponding to eigenvalues larger than :math:`\eta`, :math:`\mathbf{s}^{-1/2}_{\eta}` is a diagonal matrix containing the inverse square roots of those eigenvalues, and :math:`\eta=\mathrm{rtol} \times \max(\mathbf{s})` is the threshold for discarding small eigenvalues. The resulting matrix :math:`\mathbf{X}_{\eta}` satisfies :math:`\mathbf{X}_{\eta}^{\dagger} \mathbf{S} \mathbf{X}_{\eta} = \mathbf{I}_{\eta}`. If any eigenvalues are discarded, the resulting :math:`\mathbf{X}_{\eta}` will be rectangular with fewer columns than rows, and the inverse :math:`\mathbf{X}_{\eta}^{-1}` will be a left-inverse satisfying :math:`\mathbf{X}_{\eta}^{-1} \mathbf{X}_{\eta} = \mathbf{I}_{\eta}` but not necessarily :math:`\mathbf{X}_{\eta} \mathbf{X}_{\eta}^{-1} = \mathbf{I}`. .. !! processed by numpydoc !! .. py:function:: eigh_gen(A, B, rtol=1e-07, mode='canonical') Solve the generalized eigenvalue problem ``A @ x = lambda * B @ x``. :Parameters: **A** : NDArray The matrix A. **B** : NDArray The metric matrix B (must be positive semi-definite). If identity, the problem reduces to a standard eigenvalue problem. **rtol** : float, optional, default=1e-7 Relative threshold for removing linear dependencies, passed to the orthogonalization step. **mode** : str, optional, default="canonical" - "auto": Automatically choose the orthogonalization method based on the condition number of B. If the inverse condition number of B is larger than rtol, use symmetric orthogonalization; otherwise, use canonical orthogonalization. - "canonical": Always use canonical orthogonalization. - "symmetric": Always use symmetric orthogonalization. :Returns: tuple(NDArray, NDArray, dict) A tuple containing the eigenvalues, eigenvectors, and additional information. .. !! processed by numpydoc !! .. py:function:: givens_rotation(A, c, s, i, j, column=True) Apply a Givens rotation to the matrix A. :Parameters: **A** : NDArray The matrix to apply the rotation to. **c** : float The cosine of the rotation angle. **s** : float The sine of the rotation angle. **i** : int The index of the first row/column to rotate. **j** : int The index of the second row/column to rotate. **column** : bool, optional, default=True If True, apply the rotation to columns; if False, to rows. :Returns: NDArray The rotated matrix. .. !! processed by numpydoc !! .. py:function:: cholesky_wrapper(M, tol) Perform a Cholesky decomposition with complete pivoting, works with any symmetric positive semi-definite matrix. :Parameters: **M** : NDArray The matrix to decompose. **tol** : float The tolerance for the decomposition. :Returns: **B** : NDArray The Cholesky factor such that ``B.T @ B = M``. .. !! processed by numpydoc !! .. py:function:: block_diag_2x2(M, complex=True) Return a block-diagonal matrix with two copies of `M` on the diagonal. Note this is **not** a function to block-diagonalize a matrix. :Parameters: **M** : NDArray The matrix to convert, shape (n, n). **complex** : bool, optional, default=True If True, the output will be explicitly converted to complex type. :Returns: NDArray The block-diagonal matrix, shape (2n, 2n). .. !! processed by numpydoc !! .. py:function:: random_unitary(size, cmplx=True, rng=None, rotation=True) Generate a random orthogonal/unitary matrix of given size. :Parameters: **size** : int The size of the matrix. **cmplx** : bool, optional, default=True If True, generate a complex unitary matrix; otherwise, generate a real orthogonal matrix. **rng** : np.random.Generator, optional A random number generator for reproducibility. **rotation** : bool, optional, default=True If True, return a proper rotation (determinant = 1) by adjusting the sign of the last column if necessary. If False, the determinant may be -1. :Returns: NDArray A random unitary (or orthogonal) matrix of shape (size, size). It is guaranteed to be uniformly distributed over the appropriate group ((S)O(n) or (S)U(n)). .. rubric:: Notes The QR of a random (not necessarily Hermitian) matrix with normally distributed entries can give an orthogonal/unitary matrix. However, due to the way QR works, the distribution of the resulting matrices is not uniform over O(n) or U(n). To ensure a uniform distribution (Haar measure), we need to adjust the signs/phases of the columns based on the diagonal of R. This method is commonly used to generate random unitary/orthogonal matrices that are uniformly distributed over the appropriate group (O(n) or U(n)). These matrices will have determinant ±1 for O(n) and determinant with magnitude 1 and arbitrary phase for U(n). For special groups (determinant = 1), we can further adjust the sign/phase of a single column (here chosen as the first column) to ensure the determinant is exactly 1, which gives us a uniform distribution over SO(n) or SU(n)). See more at https://case.edu/artsci/math/mwmeckes/elizabeth/Meckes_SAMSI_Lecture2.pdf .. !! processed by numpydoc !! .. py:function:: i_sigma_dot(scalar, x, y, z) Construct the matrix i * (I2, sigma_x, sigma_y, sigma_z) dot (scalar, x, y, z). :Parameters: **scalar** : ndarray The scalar component. **x** : ndarray The x component. **y** : ndarray The y component. **z** : ndarray The z component. :Returns: NDArray The resulting matrix, with double the dimensions of the input arrays. .. !! processed by numpydoc !!