Skip to content

Commit df723bc

Browse files
committed
Update doc string. Ask o1-mini to improve test.
1 parent 408adba commit df723bc

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

tests/distributions/test_transform.py

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

713713
def test_lkjcorr_log_jac_det():
714714
"""
715-
Verify that the computed log determinant of the Jacobian matches the expected closed-form solution.
715+
Verify that the computed log determinant of the Jacobian matches the expected value
716+
obtained from PyTensor's automatic differentiation with a non-trivial input.
716717
"""
717718
n = 3
718719
transform = CholeskyCorr(n=n)
719720

720-
# Create a sample unconstrained vector (all zeros for simplicity)
721-
x = np.zeros(int(n * (n - 1) / 2), dtype=pytensor.config.floatX)
721+
# Create a non-trivial sample unconstrained vector
722+
x = np.random.randn(int(n * (n - 1) / 2)).astype(pytensor.config.floatX)
722723
x_tensor = pt.as_tensor_variable(x)
723724

724725
# Perform forward transform to obtain Cholesky factors
725-
y = transform.forward(x_tensor).eval()
726+
y = transform.forward(x_tensor)
726727

727728
# Compute the log determinant using the transform's method
728729
computed_log_jac_det = transform.log_jac_det(y).eval()
729730

730-
# Expected log determinant: 0 (since row norms are 1)
731-
expected_log_jac_det = 0.0
731+
# Define the backward function
732+
backward = transform.backward
733+
734+
# Compute the Jacobian matrix using PyTensor's automatic differentiation
735+
backward_transformed = backward(y)
736+
jacobian_matrix = pt.jacobian(backward_transformed, y)
737+
738+
# Compile the function to compute the Jacobian matrix
739+
jacobian_func = pytensor.function([], jacobian_matrix)
740+
jacobian_val = jacobian_func()
741+
742+
# Compute the log determinant of the Jacobian matrix
743+
actual_log_jac_det = np.log(np.abs(np.linalg.det(jacobian_val)))
732744

733-
assert_allclose(computed_log_jac_det, expected_log_jac_det, atol=1e-6)
745+
# Compare the two
746+
assert_allclose(computed_log_jac_det, actual_log_jac_det, atol=1e-6)
734747

735748

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

0 commit comments

Comments
 (0)