Last active
May 15, 2026 10:59
-
-
Save magicoal-nerb/70dda16be54b4d1a00938a24b844a90f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --!strict | |
| -- Matrices are in row-major order | |
| -- magicoal_nerb :3 | |
| local function entry(a: number, b: number, n: number): number | |
| return (a-1)*n + b | |
| end | |
| local function forwardSubstitution(L: { number }, b: { number }): { number } | |
| -- Let A = LU. Ax = b <=> LUx = b | |
| -- Ux = L^-1 b. Let c = L^-1 b | |
| -- <=> Lc = b. To solve: | |
| -- c_i = (b_i - sum j=0,i-1 L_ij*c_j)/L_ii | |
| local n = math.sqrt(#L) | |
| local c = table.create(n, 0) | |
| for i = 1, n do | |
| -- L_11 c_1 = b_1 | |
| -- L_21 c_1 + L_22 c_2 = b_2 | |
| -- L_31 c_1 + L_32 c_2 + ... + L_n c_n = b_n | |
| -- Get the general c_n | |
| local sum = b[i] | |
| for j = 1, i - 1 do | |
| sum -= L[entry(i, j, n)] * c[j] | |
| end | |
| c[i] = sum / L[entry(i, i, n)] | |
| end | |
| return c | |
| end | |
| local function backwardSubstitution(U: { number }, c: { number }): { number } | |
| -- Let A = LU. Ax = b <=> LUx = b | |
| -- From doing forward substitution, we get: | |
| -- <=> Ux = c. To solve: | |
| -- x_i = (c_i - sum j=i+1,n U_ij*c_j)/U_ii | |
| local n = math.sqrt(#U) | |
| local x = table.create(n, 0) | |
| for i = 1, n do | |
| -- U_nn x_n = b_n | |
| -- U_(n-1)(n-1) x_(n-1) + U_nn x_n = b_n ; find x_(n-1) | |
| -- This happens recursively. Same as forward substitution | |
| -- but the order is flipped (bottom-up) | |
| local k = n - i + 1 | |
| local sum = c[k] | |
| for j = 1, i - 1 do | |
| sum -= U[entry(k, n-j+1, n)] * x[n-j+1] | |
| end | |
| x[k] = sum / U[entry(k, k, n)] | |
| end | |
| return x | |
| end | |
| local function lu(a: { number }): ({ number }, { number }) | |
| local n = math.sqrt(#a) | |
| local l = table.create(#a, 0) | |
| local u = table.clone(a) | |
| -- Do a LU decomposition where L is a lower | |
| -- triangular matrix and U is an upper triangular matrix. | |
| -- Let E be an elimination matrix. Then, find EA = U, where U is | |
| -- upper triangular. This is done by simply doing gaussian elimination | |
| -- below the diagonal element in the matrix. | |
| for i = 1, n do | |
| -- Lower triangular matrix must have | |
| -- 1s along the diagonal | |
| l[entry(i, i, n)] = 1 | |
| for j = i + 1, n do | |
| -- lambda = u_ji/u_ii | |
| -- E_ij = -lambda ; but note that | |
| -- (E^-1 = L)_ij = lambda | |
| -- L_ij = lambda | |
| -- r_j -= r_i * lambda | |
| local lambda = u[entry(j, i, n)] / u[entry(i, i, n)] | |
| l[entry(j, i, n)] = lambda | |
| for k = 1, n do | |
| u[entry(j, k, n)] -= lambda * u[entry(i, k, n)] | |
| end | |
| end | |
| end | |
| return l, u | |
| end | |
| local function directSubstitution(L: { number }, D: { number }, b: { number }): { number } | |
| -- LDL^T x = b | |
| -- DL^T x = L^-1 b ; def c <=> Lc = b | |
| local c = forwardSubstitution(L, b) | |
| local n = #D | |
| local z = table.create(n, 0) | |
| for i = 1, n do | |
| -- L^T x = D^-1 * c ; def z <=> Dz = c | |
| z[i] = c[i] / D[i] | |
| end | |
| -- Let A = L^T U. Ax = b <=> L^T Ux = b | |
| -- From doing forward substitution, we get: | |
| -- <=> Ux = c. To solve: | |
| -- x_i = (c_i - sum j=i+1,n U_ij*c_j)/U_ii | |
| local x = table.create(n, 0) | |
| for i = 1, n do | |
| -- This happens recursively. Same as backwards substitution | |
| -- but the entry order is flipped due to the transpose | |
| local k = n - i + 1 | |
| local sum = z[k] | |
| for j = 1, i - 1 do | |
| sum -= L[entry(n-j+1, k, n)] * x[n-j+1] | |
| end | |
| x[k] = sum / L[entry(k, k, n)] | |
| end | |
| return x | |
| end | |
| local function ldlt(a: { number }): ({ number }, { number }) | |
| -- Assume A is a symmetric definite matrix | |
| local n = math.sqrt(#a) | |
| local l = table.create(#a, 0) | |
| local d = table.create(n, 0) | |
| local u = table.clone(a) | |
| -- Do a LDL^T decomposition where L is a lower | |
| -- triangular matrix and D is a diagonal matrix. | |
| -- L is the same as in LU(A). flops ~ O(1/3 n^3). | |
| for i = 1, n do | |
| -- Lower triangular matrix must have | |
| -- 1s along the diagonal | |
| l[entry(i, i, n)] = 1 | |
| -- D_11 = A_11 => D_22 = A_22 - D_11 * L_21^2 | |
| -- D_nn = A_nn - sum k=1,n L_(n-1)k^2 * d_k | |
| local dii = a[entry(i, i, n)] | |
| for j = 1, i - 1 do | |
| local Ljk = l[entry(i, j, n)] | |
| dii -= Ljk*Ljk * d[j] | |
| end | |
| for j = i + 1, n do | |
| -- lambda = u_ji/u_ii | |
| -- E_ij = -lambda ; but note that | |
| -- (E^-1 = L)_ij = lambda | |
| -- L_ij = lambda | |
| -- r_j -= r_i * lambda | |
| local lambda = u[entry(j, i, n)] / u[entry(i, i, n)] | |
| l[entry(j, i, n)] = lambda | |
| for k = 1, j do | |
| u[entry(j, k, n)] -= lambda * u[entry(i, k, n)] | |
| end | |
| end | |
| d[i] = dii | |
| end | |
| return l, d | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --!strict | |
| -- Explicitly solves the matrices (for N<=4) in the form Ax=b | |
| -- magicoal_nerb :3 | |
| local function solveMat2( | |
| a11: number, a21: number, a22: number, | |
| b1: number, b2: number | |
| ): (number, number) | |
| -- Solves a 2x2 matrix with Ax = b | |
| local invDet = 1.0 / (a11*a22 - a21*a21) | |
| local r1 = (a22*b1 - a21*b2)*invDet | |
| local r2 = (a11*b2 - a21*b1)*invDet | |
| return r1, r2 | |
| end | |
| local function solveLdlt3( | |
| a11: number, a22: number, a33: number, | |
| a21: number, a32: number, a31: number, | |
| b1: number, b2: number, b3: number | |
| ): (number, number, number) | |
| --[[ | |
| a11 b1 | |
| a21 a22 b2 | |
| a31 a32 a33 b3 | |
| ]] | |
| -- LDL^T x = b | |
| -- From doing the LDL^T by hand | |
| -- 6div, 14mul, 11sub, | |
| local invA11 = 1.0 / a11 | |
| local l21 = a21 * invA11 | |
| local l31 = a31 * invA11 | |
| -- Second elimination | |
| local d2 = a22 - l21*l21*a11 | |
| local invD2 = 1.0 / d2 | |
| local l32 = (a32 - l31 * l21 * a11) * invD2 | |
| local d3 = a33 - l31*l31*a11 - l32*l32*d2 | |
| -- DL^T x = L^-1 b <=> DL^T x = c | |
| -- Lc = b | |
| local c2 = b2 - l21*b1 | |
| local c3 = b3 - l31*b1 - l32*c2 | |
| -- L^T x = D^-1 c ; first term on LHS | |
| -- L^T x = e | |
| local r3 = (c3 / d3) | |
| local r2 = (c2 * invD2) - l32*r3 | |
| local r1 = (b1 * invA11) - l31*r3 - l21*r2 | |
| return r1, r2, r3 | |
| end | |
| local function solveLdlt4( | |
| a11: number, a22: number, a33: number, a44: number, | |
| a21: number, a32: number, a43: number, | |
| a31: number, a42: number, | |
| a41: number, | |
| b1: number, b2: number, b3: number, b4: number | |
| ): (number, number, number, number) | |
| --[[ | |
| a11 b1 | |
| a21 a22 b2 | |
| a31 a32 a33 b3 | |
| a41 a42 a43 a44 b4 | |
| ]] | |
| -- First elimination | |
| local invA11 = 1/a11 | |
| local l21 = a21*invA11 | |
| local l31 = a31*invA11 | |
| local l41 = a41*invA11 | |
| -- Second elimination | |
| local d2 = a22 - l21*l21*a11 | |
| local invD2 = 1.0 / d2 | |
| local l32 = (a32 - l31*l21*a11)*invD2 | |
| local l42 = (a42 - l41*l21*a11)*invD2 | |
| -- Third elimination | |
| local d3 = a33 - l31*l31*a11 - l32*l32*d2 | |
| local invD3 = 1.0 / d3 | |
| local l43 = (a43 - l41*l31*a11 - l42*l32*d2) * invD3 | |
| local d4 = a44 - l41*l41*a11 - l42*l42*d2 - l43*l43*d3 | |
| -- DL^T x = L^-1 b <=> DL^T x = c | |
| -- Lc = b | |
| local c1 = b1 | |
| local c2 = b2 - l21 * c1 | |
| local c3 = b3 - l31 * c1 - l32 * c2 | |
| local c4 = b4 - l41 * c1 - l42 * c2 - l43 * c3 | |
| -- L^T x = D^-1 c <=> De = c | |
| -- Provided: e ; first term on LHS | |
| -- L^T x = e | |
| local r4 = (c4 / d4) | |
| local r3 = (c3 * invD3) - l43 * r4 | |
| local r2 = (c2 * invD2) - l42 * r4 - l32 * r3 | |
| local r1 = (c1 * invA11) - l41 * r4 - l31 * r3 - l21 * r2 | |
| return r1, r2, r3, r4 | |
| end | |
| local function ldlt(matrix: { number }, b: { number }): (Vector3, Vector3) | |
| -- Extract elements from lower triangle storage | |
| local A11, A21, A31, A41, A51, A61 = matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6] | |
| local A22, A32, A42, A52, A62 = matrix[7], matrix[8], matrix[9], matrix[10], matrix[11] | |
| local A33, A43, A53, A63 = matrix[12], matrix[13], matrix[14], matrix[15] | |
| local A44, A54, A64 = matrix[16], matrix[17], matrix[18] | |
| local A55, A65 = matrix[19], matrix[20] | |
| local A66 = matrix[21] | |
| -- LDL^T decomposition | |
| -- First elimination | |
| local L21 = A21 / A11 | |
| local L31 = A31 / A11 | |
| local L41 = A41 / A11 | |
| local L51 = A51 / A11 | |
| local L61 = A61 / A11 | |
| local D1 = A11 | |
| -- Second elimination | |
| local D2 = A22 - L21 * L21 * D1 | |
| local L32 = (A32 - L21 * L31 * D1) / D2 | |
| local L42 = (A42 - L21 * L41 * D1) / D2 | |
| local L52 = (A52 - L21 * L51 * D1) / D2 | |
| local L62 = (A62 - L21 * L61 * D1) / D2 | |
| -- Third elimination | |
| local D3 = A33 - (L31 * L31 * D1 + L32 * L32 * D2) | |
| local L43 = (A43 - L31 * L41 * D1 - L32 * L42 * D2) / D3 | |
| local L53 = (A53 - L31 * L51 * D1 - L32 * L52 * D2) / D3 | |
| local L63 = (A63 - L31 * L61 * D1 - L32 * L62 * D2) / D3 | |
| -- Fourth elimination | |
| local D4 = A44 - (L41 * L41 * D1 + L42 * L42 * D2 + L43 * L43 * D3) | |
| local L54 = (A54 - L41 * L51 * D1 - L42 * L52 * D2 - L43 * L53 * D3) / D4 | |
| local L64 = (A64 - L41 * L61 * D1 - L42 * L62 * D2 - L43 * L63 * D3) / D4 | |
| -- Fifth elimination | |
| local D5 = A55 - (L51 * L51 * D1 + L52 * L52 * D2 + L53 * L53 * D3 + L54 * L54 * D4) | |
| -- Sixth elimination | |
| local L65 = (A65 - L51 * L61 * D1 - L52 * L62 * D2 - L53 * L63 * D3 - L54 * L64 * D4) / D5 | |
| local D6 = A66 - (L61 * L61 * D1 + L62 * L62 * D2 + L63 * L63 * D3 + L64 * L64 * D4 + L65 * L65 * D5) | |
| -- DL^T x = L^-1 b <=> DL^T x = y | |
| -- Ly = b | |
| local y1 = b[1] | |
| local y2 = b[2] - L21 * y1 | |
| local y3 = b[3] - L31 * y1 - L32 * y2 | |
| local y4 = b[4] - L41 * y1 - L42 * y2 - L43 * y3 | |
| local y5 = b[5] - L51 * y1 - L52 * y2 - L53 * y3 - L54 * y4 | |
| local y6 = b[6] - L61 * y1 - L62 * y2 - L63 * y3 - L64 * y4 - L65 * y5 | |
| -- Pretty easy, Dz = y | |
| local z1 = y1 / D1 | |
| local z2 = y2 / D2 | |
| local z3 = y3 / D3 | |
| local z4 = y4 / D4 | |
| local z5 = y5 / D5 | |
| local z6 = y6 / D6 | |
| -- L^T x = D^-1 c <=> De = z | |
| -- Provided: e ; first term on LHS | |
| -- L^T x = z | |
| local x6 = z6 | |
| local x5 = z5 - L65 * x6 | |
| local x4 = z4 - L54 * x5 - L64 * x6 | |
| local x3 = z3 - L43 * x4 - L53 * x5 - L63 * x6 | |
| local x2 = z2 - L32 * x3 - L42 * x4 - L52 * x5 - L62 * x6 | |
| local x1 = z1 - L21 * x2 - L31 * x3 - L41 * x4 - L51 * x5 - L61 * x6 | |
| return Vector3.new(x1, x2, x3), Vector3.new(x4, x5, x6) | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment