Skip to content

Commit 8c4e1ad

Browse files
twieckijessegrabowski
authored andcommitted
Update doc string. Ask o1-mini to improve test.
1 parent 55313ab commit 8c4e1ad

File tree

2 files changed

+71
-8
lines changed

2 files changed

+71
-8
lines changed

pymc/distributions/transforms.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,57 @@ class CholeskyCorr(Transform):
149149
150150
#### Mathematical Details
151151
152-
[Include detailed mathematical explanations similar to the original TFP bijector.]
152+
This bijector provides a change of variables from unconstrained reals to a
153+
parameterization of the CholeskyLKJ distribution. The CholeskyLKJ distribution
154+
[1] is a distribution on the set of Cholesky factors of positive definite
155+
correlation matrices. The CholeskyLKJ probability density function is
156+
obtained from the LKJ density on n x n matrices as follows:
157+
158+
1 = int p(A | eta) dA
159+
= int Z(eta) * det(A) ** (eta - 1) dA
160+
= int Z(eta) L_ii ** {(n - i - 1) + 2 * (eta - 1)} ^dL_ij (0 <= i < j < n)
161+
162+
where Z(eta) is the normalizer; the matrix L is the Cholesky factor of the
163+
correlation matrix A; and ^dL_ij denotes the wedge product (or differential)
164+
of the strictly lower triangular entries of L. The entries L_ij are
165+
constrained such that each entry lies in [-1, 1] and the norm of each row is
166+
1. The norm includes the diagonal; which is not included in the wedge product.
167+
To preserve uniqueness, we further specify that the diagonal entries are
168+
positive.
169+
170+
The image of unconstrained reals under the `CorrelationCholesky` bijector is
171+
the set of correlation matrices which are positive definite. A [correlation
172+
matrix](https://en.wikipedia.org/wiki/Correlation_and_dependence#Correlation_matrices)
173+
can be characterized as a symmetric positive semidefinite matrix with 1s on
174+
the main diagonal.
175+
176+
For a lower triangular matrix `L` to be a valid Cholesky-factor of a positive
177+
definite correlation matrix, it is necessary and sufficient that each row of
178+
`L` have unit Euclidean norm [1]. To see this, observe that if `L_i` is the
179+
`i`th row of the Cholesky factor corresponding to the correlation matrix `R`,
180+
then the `i`th diagonal entry of `R` satisfies:
181+
182+
1 = R_i,i = L_i . L_i = ||L_i||^2
183+
184+
where '.' is the dot product of vectors and `||...||` denotes the Euclidean
185+
norm.
186+
187+
Furthermore, observe that `R_i,j` lies in the interval `[-1, 1]`. By the
188+
Cauchy-Schwarz inequality:
189+
190+
|R_i,j| = |L_i . L_j| <= ||L_i|| ||L_j|| = 1
191+
192+
This is a consequence of the fact that `R` is symmetric positive definite with
193+
1s on the main diagonal.
194+
195+
We choose the mapping from x in `R^{m}` to `R^{n^2}` where `m` is the
196+
`(n - 1)`th triangular number; i.e. `m = 1 + 2 + ... + (n - 1)`.
197+
198+
L_ij = x_i,j / s_i (for i < j)
199+
L_ii = 1 / s_i
200+
201+
where s_i = sqrt(1 + x_i,0^2 + x_i,1^2 + ... + x_(i,i-1)^2). We can check that
202+
the required constraints on the image are satisfied.
153203
154204
#### Examples
155205

tests/distributions/test_transform.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -704,25 +704,38 @@ def test_lkjcorr_transform_round_trip():
704704

705705
def test_lkjcorr_log_jac_det():
706706
"""
707-
Verify that the computed log determinant of the Jacobian matches the expected closed-form solution.
707+
Verify that the computed log determinant of the Jacobian matches the expected value
708+
obtained from PyTensor's automatic differentiation with a non-trivial input.
708709
"""
709710
n = 3
710711
transform = CholeskyCorr(n=n)
711712

712-
# Create a sample unconstrained vector (all zeros for simplicity)
713-
x = np.zeros(int(n * (n - 1) / 2), dtype=pytensor.config.floatX)
713+
# Create a non-trivial sample unconstrained vector
714+
x = np.random.randn(int(n * (n - 1) / 2)).astype(pytensor.config.floatX)
714715
x_tensor = pt.as_tensor_variable(x)
715716

716717
# Perform forward transform to obtain Cholesky factors
717-
y = transform.forward(x_tensor).eval()
718+
y = transform.forward(x_tensor)
718719

719720
# Compute the log determinant using the transform's method
720721
computed_log_jac_det = transform.log_jac_det(y).eval()
721722

722-
# Expected log determinant: 0 (since row norms are 1)
723-
expected_log_jac_det = 0.0
723+
# Define the backward function
724+
backward = transform.backward
725+
726+
# Compute the Jacobian matrix using PyTensor's automatic differentiation
727+
backward_transformed = backward(y)
728+
jacobian_matrix = pt.jacobian(backward_transformed, y)
729+
730+
# Compile the function to compute the Jacobian matrix
731+
jacobian_func = pytensor.function([], jacobian_matrix)
732+
jacobian_val = jacobian_func()
733+
734+
# Compute the log determinant of the Jacobian matrix
735+
actual_log_jac_det = np.log(np.abs(np.linalg.det(jacobian_val)))
724736

725-
assert_allclose(computed_log_jac_det, expected_log_jac_det, atol=1e-6)
737+
# Compare the two
738+
assert_allclose(computed_log_jac_det, actual_log_jac_det, atol=1e-6)
726739

727740

728741
@pytest.mark.parametrize("n", [2, 4, 5])

0 commit comments

Comments
 (0)