Normative: a port must evaluate in this order to produce the same bits. Both precisions (F = binary32, D = binary64) use the same code with the type swapped. Everything follows the rules of evaluation-order.md: only + - * / and the correctly rounded square root, each rounded in the type's precision, no fused operations, products and sums in the written order, sums left to right.
Types: SymmetricEigenF/D, EigenDecompositionF/D, InertiaF/D, MassPropertiesF/D, PrincipalAxesF/D, namespace RecursiveAI.OneMath.Physics.
Input checks
- Any NaN or infinite entry, mass, length or offset throws
ArgumentOutOfRangeExceptionnaming the parameter and the field (matrix.M13 is NaN; a finite value is expected.). A negative mass or length throws the same type. - A tensor must be symmetric: all nine entries finite and each mirrored pair within
64 * eps * max|entry|(eps= 2^-52 for D, 2^-23 for F), elseArgumentException(not symmetric). Functions read the upper triangle only (M11 M12 M13 M22 M23 M33); every tensor they return is exactly symmetric. Combineof an empty span, or of parts whose total mass is zero or not finite, throwsArgumentException. Errors for a part name it (parts[1].Mass).- Rotation matrices and quaternions are checked for finiteness only; they are assumed to be rotations and unit quaternions.
Symmetric eigen decomposition
Cyclic Jacobi (Golub and Van Loan, Matrix Computations, section 8.5) with Rutishauser's stable rotation.
- Scaling.
m= largest absolute upper-triangle entry. Ifm == 0the result is eigenvalues 0 and the identity frame. Otherwisee= the binary exponent ofm(2^e <= m < 2^(e+1), subnormals included),e1 = e >> 1(arithmetic shift),e2 = e - e1. Every entry becomes(x * 2^-e1) * 2^-e2(exact except for entries that underflow, which are negligible againstm). The scaled matrix has largest entry in [1, 2). The eigenvalues are scaled back by(x * 2^e1) * 2^e2. A consequence: scaling the input by a power of two scales the eigenvalues by it and leaves the eigenvectors bit-identical (while nothing leaves the normal range). - Sweeps. At most 16 sweeps; a sweep rotates the pairs (p,q) = (0,1), (0,2), (1,2) in that order; iteration stops after the first sweep in which no pair is rotated. The eigenvector matrix starts as the identity and is updated in the same rotations.
- One pair. With
apqthe pivot,g = 2*|apq|: ifapq == 0or both|app| + g == |app|and|aqq| + g == |aqq|, setapq = aqp = 0and do not rotate. Otherwised = aqq - app. If|d| > B * g(B = 2^60 for D, 2^30 for F) thent = apq / d; elsetheta = d / (apq*2),t = 1 / (|theta| + sqrt(theta*theta + 1)), negated whentheta < 0. Thenc = 1 / sqrt(t*t + 1),s = t*c,tau = s / (1 + c),shift = t*apq,app = app - shift,aqq = aqq + shift,apq = aqp = 0. For the third indexr:arp' = arp - s*(arq + tau*arp),arq' = arq + s*(arp - tau*arq), mirrored to the symmetric positions. Each rowkof V:vkp' = vkp - s*(vkq + tau*vkp),vkq' = vkq + s*(vkp - tau*vkq). - Output order. Eigenvalues ascending, by a stable insertion sort on the indices (0,1,2) with comparison
lambda[order[j]] > lambda[key]; equal values keep the positions the iteration left them (a diagonal input keeps axis order). - Signs. For the first two eigenvectors the component of largest magnitude is made positive (the lowest index wins a tie; a flip is
0 - x, so zeros stay +0). The third eigenvector is flipped ifdot(cross(c0, c1), c2) < 0, so the frame is right-handed (determinant +1). The third vector therefore need not have a positive largest component.
Accuracy
Stated bound, with u = 2^-53 (D) or 2^-24 (F) and A the input with V its eigenvector matrix and L the eigenvalues:
max |V L V^T - A| <= 16 u max|A|max |V^T V - I| <= 10 uand|det V - 1| <= 10 u
It holds when the eigenvalues are in the normal range; a result that lands in the subnormal range is quantized by the format itself (a few subnormal steps are added to the first bound). By Weyl's inequality the first bound also bounds each eigenvalue's absolute error by about 3 * 16 u max|A|. Eigenvalues that overflow the type become infinite (only possible when the largest entry is within a factor of about 3 of the type's maximum).
Measured over 10^6 random matrices per precision (random, wide dynamic range, rank one and two, repeated and nearly repeated eigenvalues, near-singular spreads, diagonal with repeats, small integers, binary scales across the whole exponent range) against a double-double oracle: D worst 12.4 u reconstruction and 7.5 u orthonormality; F worst 10.4 u and 6.2 u.
Inertia
Tensors are row-major Matrix3x3; offsets, centres and tensors are in one frame.
- Rotate
R I R^T:S= the symmetric matrix from the upper triangle,M = R * S(the matrix product ofevaluation-order.md), entry(i, j)fori <= j=Dot(row i of M, row j of R); the lower triangle mirrors the upper. A quaternion first becomesq.ToMatrix()(unit quaternion assumed). - ShiftToPoint (centre of mass to a point at
d= point minus centre;mthe mass):xx = dy*dy + dz*dz,yy = dx*dx + dz*dz,zz = dx*dx + dy*dy,mx = m*dx,my = m*dy;I11 + m*xx,I22 + m*yy,I33 + m*zz,I12 - mx*dy,I13 - mx*dz,I23 - my*dz. ShiftToCenter subtracts the same terms on the diagonal and adds them off it. A round trip is exact only up to rounding. - Combine parts (mass
m_i, centrec_i, tensor aboutc_i): in span order,M = ((0 + m_0) + m_1) + ...,S = Σ m_i * c_iper component the same way,C = S / Mper component; each tensor entry of the result is the left-to-right sum, from 0, ofShiftToPoint(I_i, m_i, c_i - C). The result is the tensor aboutCwith massM. - PrincipalAxes: the eigen decomposition above;
Moments= eigenvalues,Rotation= eigenvector matrix (I = Rotation * diag(Moments) * Rotation^T),Orientation=Quaternion.CreateFromRotationMatrix(Rotation). - Solids (axes along the shape;
hhalf extents): box diagonal(m*(hy*hy + hz*hz))/3,(m*(hx*hx + hz*hz))/3,(m*(hx*hx + hy*hy))/3; sphere((2*m)*(r*r))/5; cylinder along Y,rr = r*r,hq = hh*hhfor half heighthh: Ixx = Izz =(m*(3*rr + 4*hq))/12, Iyy =(m*rr)/2.
Tests and hash-matrix scenarios
tests/OneMath.Tests/Physics/ holds known-answer tests (diagonal, 2- and 3-fold repeats, rank-deficient, tridiagonal, extreme scales, power-of-two commutation, hostile input), the 10^6-case oracle sweeps per precision (ONEMATH_PHYSICS_CASES overrides the count) and the inertia identities. The scenarios physics/{f32,f64}/{eigen,inertia} are in the hash matrix.