Quadratic forms and positive definiteness
Quadratic forms and positive definiteness (lesson 3.7.1)
0 of 9 correct
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
How to type answers
- Powers with
^:x^7,2^(1/3),a^-2. - Multiply with
*or by writing side by side:3x^2,2(x+1). ln(x)is the natural log,log(x, 2)is log base 2,eis Euler's number,sqrt(x)is a square root.sin(x),cos(x)andtan(x)take radians; writepifor π andsin(x)^2for sin²x.- Your first answer to each question is saved: it counts towards your score and brings the question back for review.
- Exact answers like
ln(20)/ln(3)are best. Decimals need about 4 significant figures.
The Cholesky factorisation
The Cholesky factorisation (lesson 3.7.2)
0 of 7 correct
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
How to type answers
- Powers with
^:x^7,2^(1/3),a^-2. - Multiply with
*or by writing side by side:3x^2,2(x+1). ln(x)is the natural log,log(x, 2)is log base 2,eis Euler's number,sqrt(x)is a square root.sin(x),cos(x)andtan(x)take radians; writepifor π andsin(x)^2for sin²x.- Your first answer to each question is saved: it counts towards your score and brings the question back for review.
- Exact answers like
ln(20)/ln(3)are best. Decimals need about 4 significant figures.
Coding exercise
The Cholesky factorisation
Write cholesky(A) that returns the Cholesky factor of A as a NumPy array, by the column-by-column method of lesson 3.7.2. Do not call np.linalg.cholesky; the tests compare your answer with it.
- Make a new decimal array first (
np.array(A, dtype=float)), so the caller's matrix is left unchanged. - Raise
ValueErrorifAis not square, or not symmetric (test withnp.allclose(A, A.T)). NumPy itself skips this check. - For each column , compute . If it is 0 or negative, raise
ValueError:Ais not positive definite. Otherwise is its square root. - Then fill in the entries below the diagonal, .
The tests use the examples of lesson 3.7.2, a correlation matrix and a random matrix. They also try lesson 3.7.1's indefinite , its semidefinite , and , which fails only at the third step. Two more pass a matrix that is not symmetric and one that is not square.
See the tests
import numpy as np
def _expect_value_error(A, why):
try:
cholesky(A)
except ValueError:
return
raise AssertionError(f"expected ValueError: {why}")
def test_two_by_two():
"""[[4, 2], [2, 5]] factors as L = [[2, 0], [1, 2]]"""
expect_close(cholesky([[4, 2], [2, 5]]), np.array([[2.0, 0.0], [1.0, 2.0]]), what="cholesky([[4, 2], [2, 5]])")
def test_correlation():
"""A correlation of 0.6 gives L = [[1, 0], [0.6, 0.8]]"""
expect_close(cholesky([[1, 0.6], [0.6, 1]]), np.array([[1.0, 0.0], [0.6, 0.8]]), what="cholesky of [[1, 0.6], [0.6, 1]]")
def test_three_by_three():
"""Lesson 3.7.2's 3 × 3 example gives L = [[2, 0, 0], [1, 3, 0], [-1, 1, 2]]"""
A = [[4, 2, -2], [2, 10, 2], [-2, 2, 6]]
expect_close(cholesky(A), np.array([[2.0, 0, 0], [1, 3, 0], [-1, 1, 2]]), what="cholesky of the 3 × 3 example")
def test_shape_of_L():
"""L is lower triangular with a positive diagonal, and L @ L.T gives A back"""
A = np.array([[9.0, 3, 6], [3, 5, 0], [6, 0, 9]])
L = np.asarray(cholesky(A), dtype=float)
expect_equal(L.shape, (3, 3), what="the shape of L")
expect_close(np.triu(L, 1), np.zeros((3, 3)), what="the entries above the diagonal of L")
assert np.all(np.diag(L) > 0), f"the diagonal of L must be positive, got {np.diag(L)}"
expect_close(L @ L.T, A, what="L @ L.T")
def test_matches_numpy():
"""A random positive definite 6 × 6 matrix matches np.linalg.cholesky"""
rng = np.random.default_rng(37)
M = rng.standard_normal((6, 6))
A = M @ M.T + 6 * np.eye(6)
expect_close(cholesky(A), np.linalg.cholesky(A), what="cholesky of the random 6 × 6 matrix")
def test_input_unchanged():
"""The caller's matrix is left unchanged, and whole-number input works"""
A = np.array([[4, 2], [2, 5]])
cholesky(A)
expect_equal(A.tolist(), [[4, 2], [2, 5]], what="A after calling cholesky(A)")
def test_indefinite():
"""The indefinite [[1, 2], [2, 1]] is not positive definite: ValueError"""
_expect_value_error([[1, 2], [2, 1]], "the last step needs the square root of 1 − 4 = −3")
def test_semidefinite():
"""The semidefinite [[1, 1], [1, 1]] is not positive definite: ValueError"""
_expect_value_error([[1, 1], [1, 1]], "the last step needs the square root of 0, and l22 = 0 is not positive")
def test_fails_late():
"""Lesson 3.7.1's T passes two steps and fails at the third: ValueError"""
_expect_value_error([[2, 1, 2], [1, 2, 1], [2, 1, 1]], "det T = −3, so the third step needs the square root of a negative number")
def test_not_symmetric():
"""[[4, 100], [2, 5]] is not symmetric: ValueError, where NumPy answers silently"""
_expect_value_error([[4, 100], [2, 5]], "the matrix is not symmetric (np.linalg.cholesky reads only its lower triangle)")
def test_not_square():
"""A 2 × 3 matrix has no Cholesky factor: ValueError"""
_expect_value_error([[1, 0, 0], [0, 1, 0]], "a 2 × 3 matrix is not square")