Coverage for src/cvx/linalg/covariance/rand_cov.py: 100%

7 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-03 18:56 +0000

1"""Random covariance matrix generation utilities. 

2 

3This module provides functions for generating random positive semi-definite 

4covariance matrices. These are useful for testing and simulation purposes. 

5 

6Example: 

7 Generate a random covariance matrix: 

8 

9 >>> import numpy as np 

10 >>> from cvx.linalg import rand_cov 

11 >>> # Generate a 5x5 random covariance matrix 

12 >>> cov = rand_cov(5, seed=42) 

13 >>> cov.shape 

14 (5, 5) 

15 >>> # Verify it's symmetric 

16 >>> bool(np.allclose(cov, cov.T)) 

17 True 

18 >>> # Verify it's positive semi-definite 

19 >>> bool(np.all(np.linalg.eigvals(cov) >= -1e-10)) 

20 True 

21 

22""" 

23 

24from __future__ import annotations 

25 

26import numpy as np 

27 

28from ..core.types import Matrix 

29 

30 

31def rand_cov(n: int, seed: int | None = None) -> Matrix: 

32 """Construct a random positive semi-definite covariance matrix of size n x n. 

33 

34 The matrix is constructed as A^T @ A where A is a random n x n matrix with 

35 elements drawn from a standard normal distribution. This ensures the result 

36 is symmetric and positive semi-definite. 

37 

38 Args: 

39 n: Size of the covariance matrix (n x n). 

40 seed: Random seed for reproducibility. If None, uses the current 

41 random state. 

42 

43 Returns: 

44 A random positive semi-definite n x n covariance matrix. 

45 

46 Example: 

47 Generate a reproducible random covariance matrix: 

48 

49 >>> import numpy as np 

50 >>> from cvx.linalg import rand_cov 

51 >>> cov1 = rand_cov(3, seed=42) 

52 >>> cov2 = rand_cov(3, seed=42) 

53 >>> np.allclose(cov1, cov2) 

54 True 

55 

56 Verify positive definiteness via Cholesky decomposition: 

57 

58 >>> cov = rand_cov(5, seed=123) 

59 >>> # If Cholesky succeeds without error, matrix is positive definite 

60 >>> L = np.linalg.cholesky(cov) 

61 >>> bool(np.allclose(L @ L.T, cov)) 

62 True 

63 

64 Eigenvalue verification: 

65 

66 >>> cov = rand_cov(3, seed=99) 

67 >>> eigenvalues = np.linalg.eigvalsh(cov) 

68 >>> # All eigenvalues should be positive for PD matrix 

69 >>> bool(np.all(eigenvalues > 0)) 

70 True 

71 

72 Different seeds produce different matrices: 

73 

74 >>> cov1 = rand_cov(3, seed=1) 

75 >>> cov2 = rand_cov(3, seed=2) 

76 >>> bool(not np.allclose(cov1, cov2)) 

77 True 

78 

79 Without seed, consecutive calls may differ (random state): 

80 

81 >>> # These may or may not be equal depending on random state 

82 >>> cov_a = rand_cov(2, seed=None) 

83 >>> cov_b = rand_cov(2, seed=None) 

84 >>> cov_a.shape == cov_b.shape == (2, 2) 

85 True 

86 

87 Note: 

88 The generated matrix is guaranteed to be positive semi-definite because 

89 it is constructed as A^T @ A. In practice, it will typically be positive 

90 definite (all eigenvalues strictly positive) unless n is very large. 

91 

92 """ 

93 rng = np.random.default_rng(seed) 

94 a = rng.standard_normal((n, n)) 

95 return np.transpose(a) @ a