The essence of perturbation is to find the difference between the high precision values of a function at two nearby points, while using only the low precision value of the difference between the points. In this post I'll write the high precision points in CAPITALS and the low precision deltas in lowercase. There are two auxiliary operations needed to define the perturbation \(P\), \(B\) replaces all variables by their high precision version, and \(W\) replaces all variables by the sum of the high precision version and the low precision delta. Then \(P = W - B\):

\[\begin{aligned} B(f) &= f(X) &\text{ (emBiggen)}\\ W(f) &= f(X + x) &\text{ (Widen)}\\ P(f) &= W(f) - B(f) \\ &= f(X + x) - f(X) &\text{ (Perturb)} \end{aligned}\]

For example, perturbation of \(f(z, c) = z^2 + c\), ie, \(P(f)\), works out like this:

\[\begin{aligned} & P(f) \\ \to & f(Z + z, C + c) - f(z, c) \\ \to & (Z + z)^2 + (C + c) - (Z^2 + C) \\ \to & Z^2 + 2 Z z + z^2 + C + c - Z^2 - C \\ \to & 2 Z z + z^2 + c \end{aligned}\]

where in the final result the additions of \(Z\) and \(z\) have mostly cancelled out and all the terms are "small".

For polynomials, regular algebraic manipulation can lead to successful outcomes, but for other functions it seems some "tricks" are needed. For example, \(|x|\) (over \(\mathbb{R}\)) can be perturbed with a "diffabs" function proceeding via case analysis:

// evaluate |X + x| - |X| without catastrophic cancellation function diffabs(X, x) { if (X >= 0) { if (X + x >= 0) { return x; } else { return -(2 * X + x); } } else { if (X + x > 0) { return 2 * X + x; } else { return -x; } } }

This formulation was developed by laser blaster at fractalforums.com.

For transcendental functions, other tricks are needed. Here for example is a derivation of \(P(\sin)\):

\[\begin{aligned} & P(\sin) \\ \to & \sin(X + x) - \sin(X) \\ \to & \sin(X) \cos(x) + \cos(X) \sin(x) - \sin(X) \\ \to & \sin(X) (\cos(x) - 1) + \cos(X) \sin(x) \\ \to & \sin(X) \left(-2\sin^2\left(\frac{x}{2}\right)\right) + \cos(X) \sin(x) \\ \to & \sin(X) \left(-2\sin^2\left(\frac{x}{2}\right)\right) + \cos(X) \left(2 \cos\left(\frac{x}{2}\right) \sin\left(\frac{x}{2}\right)\right) \\ \to & 2 \sin\left(\frac{x}{2}\right) \left(-\sin(X) \sin\left(\frac{x}{2}\right) + \cos(X) \cos\left(\frac{x}{2}\right)\right) \\ \to & 2 \sin\left(\frac{x}{2}\right) \cos\left(X + \frac{x}{2}\right) \end{aligned}\]

Knowing when to apply the sum- and double-angle-formulae, is a bit of a mystery, especially if the end goal is not known beforehand. This makes implementing a symbolic algebra program that can perform these derivations quite a challenge.

In lieu of a complete symbolic algebra program that does it all on demand, here are a few formulae that I calculated, some by hand, some using Wolfram Alpha:

\[\begin{aligned} P(a) &= 0 \\ P(a f) &= a P(f) \\ P(f + g) &= P(f) + P(g) \\ P(f g) &= P(f) W(g) + B(f) P(g) \\ P\left(\frac{1}{f}\right) &= -\frac{P(f)}{B(f)W(f)} \\ P(|f|) &= \operatorname{diffabs}(B(f), P(f)) \\ P(\exp) &= \exp(X) \operatorname{expm1}(x) \\ P(\log) &= \operatorname{log1p}\left(\frac{x}{X}\right) \\ P(\sin \circ f) &= \phantom{-}2 \sin\left(\frac{P(f)}{2}\right)\cos\left(\frac{W(f)+B(f)}{2}\right) \\ P(\cos \circ f) &= -2 \sin\left(\frac{P(f)}{2}\right)\sin\left(\frac{W(f)+B(f)}{2}\right) \\ P(\tan \circ f) &= \frac{\sin(P(f))}{\cos(B(f))\cos(W(f))} \\ P(\sinh \circ f) &= 2 \sinh\left(\frac{P(f)}{2}\right)\cosh\left(\frac{W(f)+B(f)}{2}\right) \\ P(\cosh \circ f) &= 2 \sinh\left(\frac{P(f)}{2}\right)\sinh\left(\frac{W(f)+B(f)}{2}\right) \\ P(\tanh \circ f) &= \frac{\sinh(P(f))}{\cosh(B(f))\cosh(W(f))} \\ \end{aligned}\]

I hope to find time to add these to et soon.

**EDIT** there is a simpler and more general way to derive \(P(\sin)\)
and so on, using \(\sin(a) \pm \sin(b)\) formulae...

In my work-in-progress et project for escape-time fractals, I currently represent the viewing transform (from pixel grid to complex plane coordinates) by a translation (high precision coordinates of the center of the view), a scaling (high range scale factor), and a 2×2 matrix that accounts for non-uniform scaling and rotation (4 low precision precision numbers, defaulting to the identity [1,0;0,1]) - this matrix should have determinant 1 as any global scaling belongs in the scale factor.

However, editing raw matrix values is not very user friendly, so I plan to
add a friendly user interface based on marking points in the GUI before moving
them around with the mouse (eventually: multi-touch support for tablets etc).
An intermediate step might be to represent the matrix in a more human-relevant
way, decomposing it into rotations and non-uniform scaling (a shear is a
non-uniform scaling conjugated by a rotation, no need to handle it separately).
It so happens that this is a well-known linear algebra problem, called
**polar decomposition**. The matrix M is decomposed into a
rotation V and a stretch P, such that M = V P, and further the stretch P can be
decomposed into a rotation U and a diagonal matrix D, such that
P = U D U^{-1} (though this last decomposition is not unique).

A good description of the problem and examples in higher dimensions is:

Matrix Animation and Polar Decomposition

Ken Shoemake and Tom Duff

AbstractGeneral 3×3 linear or 4×4 homogenous matrices can be formed by composing primitive matrices for translation, rotation, scale, shear, and perspective. Current 3-D computer graphics systems manipulate and interpolate parametric forms of these primitives to generate scenes and motion. For this and other reasons, decomposing a composite matrix in a meaningful way has been a long-standing challenge. This paper presents a theory and method for doing so, proposing that the central issue is rotation extraction, and that the best way to do that is Polar Decomposition. This method also is useful for renormalizing a rotation matrix containing excessive error.

For the 2D case there is a simple explicit formula given in:

Explicit polar decomposition and a near-characteristic polynomial: The 2×2 case

Frank Uhlig

AbstractExplicit algebraic formulas for the polar decomposition of a nonsingular real 2×2 matrix A are given, as well as a classification of all integer 2×2 matrices that admit a rational polar decomposition. These formulas lead to a functional identity which is satisfied by all nonsingular real 2×2 matrices A as well as by exactly one type of exceptional matrix A_{n}, for each n > 2.

Translated into Octave code (presumably Matlab compatible), assuming that M is real with det(M) > 0:

M = [1,-3;2,2] scale = sqrt(det(M)) A = M / scale; B = A + inv(A'); b = sqrt(abs(det(B))); V = B / b; P = (A' * A + eye(2)) / b; [U,D] = eig(P); stretch = D(1); stretchAngle = atan2(U(2,1), U(1,1)); if (stretch < 1) stretch = 1 / stretch stretchAngle = stretchAngle + pi / 2; endif stretchA = mod(stretchAngle, pi) rotation = atan2(V(2,1), V(1,1)) R = [ cos(rotation), -sin(rotation); sin(rotation), cos(rotation) ]; S = [ stretch, 0; 0, 1/stretch ]; T = [ cos(stretchA), -sin(stretchA); sin(stretchA), cos(stretchA) ]; N = scale * R * T * S * T'; error = norm(M - N)

Example output:

M = 1 -3 2 2 scale = 2.8284 stretch = 1.2808 stretchA = 1.4483 rotation = 1.0304 error = 1.0721e-15

Note that eigenvalues and eigenvectors can be found explicitly in 2D, see for example Eigenvalues and eigenvectors of 2x2 matrices.

Final things to note: currently et uses only the view scale factor to choose which number type to use for calculations. This might lead to pixelation artifacts from insufficient precision in highly stretched images near number type thresholds, so the stretch factor should be taken into account too when determining the minimal pixel spacing. Handling reflection (det < 0) is left for future investigation.

]]>