Using a cover
The examples on this page use the following program and its cover:
julia> using DerivativeCovers, LinearAlgebra, MatrixCovers
julia> f(x) = exp(x[1]) + x[1] * x[2] + x[2] * x[3] + x[3] * x[4];
julia> x0 = [0.5, -1.2, 0.3, 0.8];
julia> t, out = tape_function(f, x0);
julia> h = cover_hessian(t, out).h_sum;
julia> H = reduce(hcat, [hvp(t, out, e) for e in eachcol(Matrix(1.0I, 4, 4))]); # for comparisonWhat a cover bounds
If abs.(H) .<= h .* h', then:
- each entry satisfies
abs(H[i,j]) ≤ h[i] * h[j]; - each Hessian-vector product satisfies
abs.(H * d) .≤ h .* (h' * abs.(d)); - the scaled Hessian
Hhat = H ./ (h .* h')has entries in[-1, 1], so its spectral radius is at most the number of nonzeros in the densest row ofH, and in particular at mostn.
The last is a statement about Hhat rather than H. When the inputs have different units, the entries of H have different units too, and the eigenvalue equation H * v = λ * v cannot be satisfied by a scalar λ with a single unit. Hhat is dimensionless, and because the cover transforms with the units (see Scale covariance), its eigenvalues do not depend on them.
julia> d = [1.0, -2.0, 0.5, 0.0];
julia> all(abs.(H * d) .<= h .* (h' * abs.(d)))
true
julia> Hhat = H ./ (h .* h');
julia> maximum(abs, eigvals(Hhat)) <= maximum(count(!iszero, H; dims=2))
trueScale-invariant quantities
When the inputs are rescaled, x → D*x, the cover transforms as h → inv(D)*h (see Scale covariance). Quantities formed by dividing by the cover are therefore independent of the units of the inputs:
- the scaled Hessian
Hhat = H ./ (h .* h'), whose entries lie in[-1, 1]; - the scaled gradient
g ./ h, whose norm serves as a convergence test; - the scaled step
h .* p, in terms of which the Newton equationH * p = -gbecomesHhat * (h .* p) = -(g ./ h).
Regularizing an indefinite Hessian
Modified Newton methods and Tikhonov regularization shift the Hessian to make it positive definite. A shift by a multiple of the identity, H + τ*I, does not respect units: the same τ perturbs each variable by an amount that depends on its units. The diagonal of the cover respects them. The shift H + τ * Diagonal(h .^ 2) is positive semidefinite when τ ≥ -eigmin(Hhat), so any upper bound on the spectral radius of Hhat gives a sufficient τ.
The number of nonzeros in the densest row of H (see What a cover bounds) is such a bound. A tighter bound comes from scaled_spectral_radius, which estimates the spectral radius of Hhat by power iteration using Hessian-vector products, without forming H:
julia> λ = DerivativeCovers.scaled_spectral_radius(t, out, h);
julia> round(λ; digits=3)
0.559
julia> isposdef(H + 1.1 * λ * Diagonal(h .^ 2))
truePower iteration approaches the spectral radius from below, so the estimate is inflated by a safety factor (here 1.1) before use. When H is available explicitly, the smallest sufficient τ is -eigmin(Hhat).
Tightening a cover
If the spectral radius of Hhat is λ < 1, then every entry of Hhat is at most λ in magnitude, and sqrt(λ) * h is also a cover. The power-iteration estimate of λ needs the same safety factor:
julia> hs = sqrt(1.1 * λ) .* h;
julia> round(max_coverage_ratio(h, H); digits=3), round(max_coverage_ratio(hs, H); digits=3)
(0.449, 0.73)max_coverage_ratio, from MatrixCovers, is the largest value of abs(H[i,j]) / (h[i] * h[j]); values closer to 1 indicate a tighter cover (see Tightness).
Jacobians
For a Jacobian cover (a, b), the scale-invariant matrix is Jhat = J ./ (a .* b'), computed by equilibrate. The Jacobian counterparts of the functions above are scaled_singular_value, which computes the largest singular value of Jhat (by power iteration with jvp and vjp when given a tape). If σ < 1, then (sqrt(σ) * a, sqrt(σ) * b) is also a cover.
julia> using DerivativeCovers
julia> r(x) = [x[1] * x[2], exp(x[1]) - x[3], x[2] + 2x[3]];
julia> t, outs = tape_function(r, [0.5, 2.0, -1.0]);
julia> jc = cover_jacobian(t, outs);
julia> σ = DerivativeCovers.scaled_singular_value(t, outs, jc.a, jc.b_sum);
julia> round(σ; digits=3)
0.866
julia> a, b = sqrt(1.1 * σ) .* jc.a, sqrt(1.1 * σ) .* jc.b_sum;Matrix-free products
The tape also supports exact products with the derivative matrices, each in one or two sweeps over the tape ($O(L)$ time and memory):
hvp: Hessian-vector productH * v;jvp: Jacobian-vector productJ * v;vjp: vector-Jacobian productJ' * u.
The mutating forms hvp!, jvp!, and vjp! write the result into a supplied array.