Coverage for strongcoca / calculators / utilities.py: 100%

10 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-04-15 18:15 +0000

1import numpy as np 

2 

3 

4def get_dipole_dipole_tensor(distance_vector: np.ndarray) -> np.ndarray: 

5 r"""Returns the dipole-dipole tensor. The dipole-dipole tensor is defined as 

6 

7 .. math: 

8 

9 T_{i\mu j\nu} = \nabla_\mu \nabla_\nu \frac{1}{|\vec{R_{ij}}|} 

10 = \frac{\delta_{\mu\nu}}{|\vec{R_{ij}}|^3} 

11 - 3 \frac{R_{ij,\mu} R_{ij,\nu}}{|\vec{R_{ij}}|^5} 

12 

13 Parameters 

14 ---------- 

15 distance_vector 

16 Distance vector between the two subsystems. 

17 

18 Example 

19 ------- 

20 

21 .. doctest: 

22 

23 >>> get_dipole_dipole_tensor(np.array([1, 2, 3])) 

24 array([[ 0.01499936, -0.00818147, -0.0122722 ], 

25 [-0.00818147, 0.00272716, -0.0245444 ], 

26 [-0.0122722 , -0.0245444 , -0.01772651]]) 

27 

28 """ 

29 assert distance_vector.shape == (3,), \ 

30 f'distance_vector has the wrong shape ({distance_vector})' 

31 r = np.linalg.norm(distance_vector) 

32 T = np.eye(3) / r ** 3 - 3 * np.outer(distance_vector, distance_vector) / r ** 5 

33 return T # type: ignore 

34 

35 

36def logm_batched(A_wnn: np.ndarray) -> np.ndarray: 

37 """Batched matrix logarithm via eigendecomposition. 

38 

39 Computes ``log(A)`` for each matrix in a batch using the decomposition 

40 ``A = V diag(λ) V⁻¹ → log(A) = V diag(log λ) V⁻¹``. 

41 

42 Parameters 

43 ---------- 

44 A_wnn 

45 Batch of square matrices, shape (W, N, N). Complex inputs supported. 

46 

47 Returns 

48 ------- 

49 :class:`np.ndarray` 

50 Array of the same shape as *A_wnn*. 

51 

52 Notes 

53 ----- 

54 Uses the principal value of the logarithm, identical to 

55 ``scipy.linalg.logm`` for matrices without eigenvalues on the negative 

56 real axis. 

57 

58 Examples 

59 -------- 

60 

61 >>> import numpy as np 

62 >>> from strongcoca.calculators.utilities import logm_batched 

63 >>> A = np.array([[[2., 0.], [0., 4.]]]) 

64 >>> np.allclose(logm_batched(A), [[[np.log(2), 0.], [0., np.log(4)]]]) 

65 True 

66 """ 

67 eigvals_wn, eigvecs_wnn = np.linalg.eig(A_wnn) 

68 log_eigvals_wn = np.log(eigvals_wn) 

69 # logm(A) = V @ diag(log λ) @ V⁻¹; scale each column of V by its log-eigenvalue 

70 return (eigvecs_wnn * log_eigvals_wn[:, None, :]) @ np.linalg.inv(eigvecs_wnn)