Efficient 4x4 matrix inverse (affine transform) - math

I was hoping someone can point out an efficient formula for 4x4 affine matrix transform. Currently my code uses cofactor expansion and it allocates a temporary array for each cofactor. It's easy to read, but it's slower than it should be.
Note, this isn't homework and I know how to work it out manually using 4x4 co-factor expansion, it's just a pain and not really an interesting problem for me. Also I've googled and came up with a few sites that give you the formula already (http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm). However this one could probably be optimized further by pre-computing some of the products. I'm sure someone came up with the "best" formula for this at one point or another?

You should be able to exploit the fact that the matrix is affine to speed things up over a full inverse. Namely, if your matrix looks like this
A = [ M b ]
[ 0 1 ]
where A is 4x4, M is 3x3, b is 3x1, and the bottom row is (0,0,0,1), then
inv(A) = [ inv(M) -inv(M) * b ]
[ 0 1 ]
Depending on your situation, it may be faster to compute the result of inv(A) * x instead of actually forming inv(A). In that case, things simplify to
inv(A) * [x] = [ inv(M) * (x - b) ]
[1] = [ 1 ]
where x is a 3x1 vector (usually a 3D point).
Lastly, if M represents a rotation (i.e. its columns are orthonormal), then you can use the fact that inv(M) = transpose(M). Then computing the inverse of A is just a matter of subtracting the translation component, and multiplying by the transpose of the 3x3 part.
Note that whether or not the matrix is orthonormal is something that you should know from the analysis of the problem. Checking it during runtime would be fairly expensive; although you might want to do it in debug builds to check that your assumptions hold.
Hope all of that is clear...

Just in case someone would like to save some typing, here's an AS3 version I wrote based on page 9 (more efficient version of Laplace Expansion Theorem) of the link posted above by phkahler:
public function invert() : Matrix4 {
var m : Matrix4 = new Matrix4();
var s0 : Number = i00 * i11 - i10 * i01;
var s1 : Number = i00 * i12 - i10 * i02;
var s2 : Number = i00 * i13 - i10 * i03;
var s3 : Number = i01 * i12 - i11 * i02;
var s4 : Number = i01 * i13 - i11 * i03;
var s5 : Number = i02 * i13 - i12 * i03;
var c5 : Number = i22 * i33 - i32 * i23;
var c4 : Number = i21 * i33 - i31 * i23;
var c3 : Number = i21 * i32 - i31 * i22;
var c2 : Number = i20 * i33 - i30 * i23;
var c1 : Number = i20 * i32 - i30 * i22;
var c0 : Number = i20 * i31 - i30 * i21;
// Should check for 0 determinant
var invdet : Number = 1 / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0);
m.i00 = (i11 * c5 - i12 * c4 + i13 * c3) * invdet;
m.i01 = (-i01 * c5 + i02 * c4 - i03 * c3) * invdet;
m.i02 = (i31 * s5 - i32 * s4 + i33 * s3) * invdet;
m.i03 = (-i21 * s5 + i22 * s4 - i23 * s3) * invdet;
m.i10 = (-i10 * c5 + i12 * c2 - i13 * c1) * invdet;
m.i11 = (i00 * c5 - i02 * c2 + i03 * c1) * invdet;
m.i12 = (-i30 * s5 + i32 * s2 - i33 * s1) * invdet;
m.i13 = (i20 * s5 - i22 * s2 + i23 * s1) * invdet;
m.i20 = (i10 * c4 - i11 * c2 + i13 * c0) * invdet;
m.i21 = (-i00 * c4 + i01 * c2 - i03 * c0) * invdet;
m.i22 = (i30 * s4 - i31 * s2 + i33 * s0) * invdet;
m.i23 = (-i20 * s4 + i21 * s2 - i23 * s0) * invdet;
m.i30 = (-i10 * c3 + i11 * c1 - i12 * c0) * invdet;
m.i31 = (i00 * c3 - i01 * c1 + i02 * c0) * invdet;
m.i32 = (-i30 * s3 + i31 * s1 - i32 * s0) * invdet;
m.i33 = (i20 * s3 - i21 * s1 + i22 * s0) * invdet;
return m;
}
This successfully produced an identity matrix when I multiplied various 3D transformation matrices by the inverse returned from this method. I'm sure you can search/replace to get this into whatever language you'd like.

To follow-up on pkhaler's and Robin Hilliard's excellent responses above, here is Robin's ActionScript 3 code converted into a C# method. Hopefully this can save some typing for other C# developers, as well as C/C++ and Java developers in need of a 4x4 matrix inversion function:
public static double[,] GetInverse(double[,] a)
{
var s0 = a[0, 0] * a[1, 1] - a[1, 0] * a[0, 1];
var s1 = a[0, 0] * a[1, 2] - a[1, 0] * a[0, 2];
var s2 = a[0, 0] * a[1, 3] - a[1, 0] * a[0, 3];
var s3 = a[0, 1] * a[1, 2] - a[1, 1] * a[0, 2];
var s4 = a[0, 1] * a[1, 3] - a[1, 1] * a[0, 3];
var s5 = a[0, 2] * a[1, 3] - a[1, 2] * a[0, 3];
var c5 = a[2, 2] * a[3, 3] - a[3, 2] * a[2, 3];
var c4 = a[2, 1] * a[3, 3] - a[3, 1] * a[2, 3];
var c3 = a[2, 1] * a[3, 2] - a[3, 1] * a[2, 2];
var c2 = a[2, 0] * a[3, 3] - a[3, 0] * a[2, 3];
var c1 = a[2, 0] * a[3, 2] - a[3, 0] * a[2, 2];
var c0 = a[2, 0] * a[3, 1] - a[3, 0] * a[2, 1];
// Should check for 0 determinant
var invdet = 1.0 / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0);
var b = new double[4, 4];
b[0, 0] = ( a[1, 1] * c5 - a[1, 2] * c4 + a[1, 3] * c3) * invdet;
b[0, 1] = (-a[0, 1] * c5 + a[0, 2] * c4 - a[0, 3] * c3) * invdet;
b[0, 2] = ( a[3, 1] * s5 - a[3, 2] * s4 + a[3, 3] * s3) * invdet;
b[0, 3] = (-a[2, 1] * s5 + a[2, 2] * s4 - a[2, 3] * s3) * invdet;
b[1, 0] = (-a[1, 0] * c5 + a[1, 2] * c2 - a[1, 3] * c1) * invdet;
b[1, 1] = ( a[0, 0] * c5 - a[0, 2] * c2 + a[0, 3] * c1) * invdet;
b[1, 2] = (-a[3, 0] * s5 + a[3, 2] * s2 - a[3, 3] * s1) * invdet;
b[1, 3] = ( a[2, 0] * s5 - a[2, 2] * s2 + a[2, 3] * s1) * invdet;
b[2, 0] = ( a[1, 0] * c4 - a[1, 1] * c2 + a[1, 3] * c0) * invdet;
b[2, 1] = (-a[0, 0] * c4 + a[0, 1] * c2 - a[0, 3] * c0) * invdet;
b[2, 2] = ( a[3, 0] * s4 - a[3, 1] * s2 + a[3, 3] * s0) * invdet;
b[2, 3] = (-a[2, 0] * s4 + a[2, 1] * s2 - a[2, 3] * s0) * invdet;
b[3, 0] = (-a[1, 0] * c3 + a[1, 1] * c1 - a[1, 2] * c0) * invdet;
b[3, 1] = ( a[0, 0] * c3 - a[0, 1] * c1 + a[0, 2] * c0) * invdet;
b[3, 2] = (-a[3, 0] * s3 + a[3, 1] * s1 - a[3, 2] * s0) * invdet;
b[3, 3] = ( a[2, 0] * s3 - a[2, 1] * s1 + a[2, 2] * s0) * invdet;
return b;
}

IIRC you can greatly shrink the code and time by precomputing a bunch (12?) 2x2 determinants. Split the matrix in half vertically and compute every 2x2 in both the upper and lower half. One of these smaller determinants is used in every term you'll need for the bigger computation and they each get reused.
Also, don't use a separate determinant function - reuse the sub-determinants you computed for the adjoint to get the determinant.
Oh, just found
this.
There are some improvements you can make knowing its a certain kind of transform too.

I believe the only way to compute an inverse is to solve n times the equation: A x = y, where y spans the unit vectors, i.e., the first one is (1,0,0,0), the second is (0,1,0,0), etc.
(Using the cofactors (Cramer's rule) is a bad idea, unless you want a symbolic formula for the inverse.)
Most linear algebra libraries will allow you to solve those linear systems, and even to compute an inverse. Example in python (using numpy):
from numpy.linalg import inv
inv(A) # here you go

Related

JuMP constraints involving matrix inverse

I'm attempting to solve for an n*n matrix U, which satisfies a variety of constraints, including some involving inverses of its sub-matrices. However, it seems that JuMP can't handle inverses, at least without some additional specification of invertibility. Here's an example of the problem with n=2.
using JuMP, Ipopt
m = Model(with_optimizer(Ipopt.Optimizer))
A = [5 7; 7 10]
B = [9 13; 13 19]
C = [3 4; 4 6]
nnodes = 2
#variable(m, U[1:nnodes, 1:nnodes])
A1 = U * A * U'
B1 = U * B * U'
C1 = U * C * U'
c1 = A1[1, 1] - 1
c2 = A1[2, 2] - 1
c3 = C1[1, 1] - 1
c4 = unmixed_iv2[1, 2]
a = A1[2, 2] - A1[2, 1] * inv(A1[1, 1]) * A1[2,1] # Schur complement
b = B1[2, 2] - B1[2, 1] * inv(B1[1, 1]) * B1[2,1] # Schur complement
c5 = a - b
#NLconstraint(m, c1 == 0)
#NLconstraint(m, c2 == 0)
#NLconstraint(m, c3 == 0)
#NLconstraint(m, c4 == 0)
#NLconstraint(m, c5 == 0)
solve(m)
This raises the following error:
ERROR: inv is not defined for type GenericQuadExpr. Are you trying to build a nonlinear problem? Make sure you use #NLconstraint/#NLobjective.
Any suggestions on how to solve this problem?
You cannot use inv outside the macros (or more generally, build up any nonlinear expression). Just put it inside like so:
using JuMP
model = Model()
#variable(model, x >= 0.5)
#NLconstraint(model, inv(x) <= 0.5)
p.s., I can't run your example because I don't know what unmixed_iv2 is.

Changing XYZ order when converting Euler-angles to quaternions

I'm using the following code to produce a quaternion from XYZ Euler-Angles in radians:
c1 = Math.cos( x / 2 )
c2 = Math.cos( y / 2 )
c3 = Math.cos( z / 2 )
s1 = Math.sin( x / 2 )
s2 = Math.sin( y / 2 )
s3 = Math.sin( z / 2 )
quaternion = [
c1 * c2 * c3 - s1 * s2 * s3,
s1 * c2 * c3 + c1 * s2 * s3,
c1 * s2 * c3 - s1 * c2 * s3,
c1 * c2 * s3 + s1 * s2 * c3,
]
from:
http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm
This produces a quaternion that first rotates around the z, then y, then finally the x-axis - Z-Y-X. Is it possible to alter this formula so that it rotates around the axes in a different order? What I'm looking for is the opposite, so X-Y-Z.
If anyone's interested...
Yes you can, turns out on this occasion I just needed to swap the plus and minus signs around to get an X-Y-Z order (traditionally written ZYX).
Like so...
[
c1 * c2 * c3 + s1 * s2 * s3,
s1 * c2 * c3 - c1 * s2 * s3,
c1 * s2 * c3 + s1 * c2 * s3,
c1 * c2 * s3 - s1 * s2 * c3
]
Three.js has a full list of the different formulas for various xyz orders - in the function THREE.Quaternion.setFromEuler
https://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/steps/index.htm

How to rearrange the complex algebraic equation in R?

The nonlinear equation is following,A,B,C,D,E are know. I want to rearrange the formation of the equation. Let the X at the left of equation,and let other parameters all at the right of equation. such as X= A*B/D+E^2/C
Is there are some software to do this?such as R.
Try this -- ignore the warnings from that XML package that have started recently.
library(Ryacas)
A <- Sym("A")
B <- Sym("B")
C <- Sym("C")
D <- Sym("D")
E <- Sym("E")
X <- Sym("X")
Solve(E == A * B * (X + C) / (A + B * (X + C)) - A * B * (X + D + C) / (A + B * (X + D + C)), X)
giving:
expression(list(X == (root((2 * (E * A * B) + (2 * (E * B^2 *
C) + E * B^2 * D))^2 - 4 * (E * B^2 * (E * A^2 + (2 * (E *
A * B * C) + E * A * B * D) + (E * B^2 * C^2 + E * B^2 *
C * D) + A^2 * B * D)), 2) - (2 * (E * A * B) + (2 * (E *
B^2 * C) + E * B^2 * D)))/(2 * (E * B^2)), X == -(2 * (E *
A * B) + (2 * (E * B^2 * C) + E * B^2 * D) + root((2 * (E *
A * B) + (2 * (E * B^2 * C) + E * B^2 * D))^2 - 4 * (E *
B^2 * (E * A^2 + (2 * (E * A * B * C) + E * A * B * D) +
(E * B^2 * C^2 + E * B^2 * C * D) + A^2 * B * D)), 2))/(2 *
(E * B^2))))
An alternative to the above if you have specific values for A, B, C, D, E would be to numerically solve it using, for example, uniroot.

Tangent calculation OpenGL

I am trying to calculate the tangent line (needed for bump mapping) for every vertex in my mesh. The v1, v2 and v3 are the vertices in the triangle and the t1, t2 and t3 are the respective texture coords. From what i understand this should output the tangent line for the three vertices of the triangle.
Vec3f va = Vec3f{vertexData[a * 3 + 0], vertexData[a * 3 + 1], vertexData[a * 3 + 2]};
Vec3f vb = Vec3f{vertexData[b * 3 + 0], vertexData[b * 3 + 1], vertexData[b * 3 + 2]};
Vec3f vc = Vec3f{vertexData[c * 3 + 0], vertexData[c * 3 + 1], vertexData[c * 3 + 2]};
Vec2f ta = (Vec2f){texcoordData[a * 2 + 0],texcoordData[a * 2 + 1]};
Vec2f tb = (Vec2f){texcoordData[b * 2 + 0],texcoordData[b * 2 + 1]};
Vec2f tc = (Vec2f){texcoordData[c * 2 + 0],texcoordData[c * 2 + 1]};
Vec3f v1 = subtractVec3f(vb, va);
Vec3f v2 = subtractVec3f(vc, va);
Vec2f t1 = subtractVec2f(tb, ta);
Vec2f t2 = subtractVec2f(tc, ta);
float coef = 1/(t1.u * t2.v - t1.v * t2.u);
Vec3f tangent = Vec3fMake((t2.v * v1.x - t1.v * v2.x) * coef,
(t2.v * v1.y - t1.v * v2.y) * coef,
(t2.v * v1.z - t1.v * v2.z) * coef);
My problem is that the coef variable is sometimes the nan (not a number) value causing the multiplication to be off. My mesh is not super complex, a simple cylinder, but i would like a universal formula to calculate the tangent line to enable bump mapping on all of my meshes.
coef becomming a NaN indicates some numerical problem with your input data, like degenerate triangles or texture coordinates. Make sure that the expression (t1.u * t2.v - t1.v * t2.u) doesn't (nearly) vanish, i.e. its absolute value is larger than some reasonable threshold value.
A good sanity check is |vb-va|>0 ^ |vc-va|>0, |tb-ta|>0 ^ |tc-ta|>0, |normalized(vb-va) . normalized(vc-va)| < 1 and |normalized(tb-ta) . normalized(tc-ta)| < 1.

f# - Recursive formula for kurtosis

I am now adapting my formula for skewness to make a kurtosis function in F#. Unfortunately it is again return incorrect results.
Here is my code
let kurtosis_aux (m, m2, m3, m4, k) x =
m + (x - m)/k,
m2 + ((x - m)*(x - m)*(k - 1.0))/k,
m3 + ((x - m)*(x - m)*(x - m)*(k - 1.0)*(k - 2.0))/(k * k) - (3.0 * (x - m) * m2)/k,
m4 + ((x - m)*(x - m)*(x - m)*(x - m)*(k - 1.0)*(k * k - (3.0 * k) + 3.0))/(k * k * k) + 6.0 * (x - m)*(x - m)* m2/(k * k) - (4.0*(x - m)* m3)/k ,
k + 1.0;;
let kurtosis xs =
let _, m2, m3, m4, n = Seq.fold kurtosis_aux (0.0, 0.0, 0.0, 0.0, 1.0) xs
((n - 1.0) * m4 / ( m2 * m2 )) - 3.0;;
Finally I test on a small vector, and should get approximately 2.94631
kurtosis [|9.0; 2.0; 6.0; 3.0; 29.0|];;
But instead FSI returns -0.05369308728.
The error must be in part m4 of the kurtosis_aux function or in the kurtosis function itself. The other variables are all used in the skewness function and work correctly.
Again I am hugely grateful for any and all help.
remove the -3.0 in the last line.
with -3.0 you are calculating excess kurtosis.

Resources