@@ -84,37 +84,115 @@ type LinearAlgebra =
8484
8585 Matrix( m, n, qData), r
8686
87- /// <summary >Back substitute to solve R * x = y</summary >
88- /// <remarks >R is upper triangular</remarks >
87+ /// <summary >Forward substitute to solve L * x = y</summary >
88+ /// <remarks >L is lower triangular</remarks >
89+ static member inline forwardSubstitute < 'T when 'T :> Numerics.INumber < 'T >
90+ and 'T : ( new : unit -> 'T )
91+ and 'T : struct
92+ and 'T : comparison
93+ and 'T :> ValueType >
94+ ( L : Matrix < 'T >)
95+ ( y : Vector < 'T >) : Vector < 'T > =
96+
97+ let n = L.NumRows
98+
99+ if L.NumCols <> n || y.Length <> n then
100+ invalidArg " dimensions" " L must be square and match the length of y"
101+
102+ let x = Array.zeroCreate< 'T> n
103+ let cols = L.NumCols
104+ let lData = L.Data
105+
106+ // Again, scalar version; easy to SIMD the inner sum later
107+ for i = 0 to n - 1 do
108+ let mutable s = y.[ i]
109+ let rowOffset = i * cols
110+ for j = 0 to i - 1 do
111+ s <- s - lData.[ rowOffset + j] * x.[ j]
112+ let diag = lData.[ rowOffset + i]
113+ if diag = 'T.Zero then
114+ invalidArg $" Matrix[{i},{i}]" " Diagonal element is zero. Cannot divide."
115+ x.[ i] <- s / diag
116+
117+ x
118+
119+
120+
121+ ///// <summary>Back substitute to solve R * x = y</summary>
122+ ///// <remarks>R is upper triangular</remarks>
123+ //static member inline backSubstitute<'T when 'T :> Numerics.INumber<'T>
124+ // and 'T : (new: unit -> 'T)
125+ // and 'T : struct
126+ // and 'T : comparison
127+ // and 'T :> ValueType>
128+ // (r: Matrix<'T>)
129+ // (y: Vector<'T>) : Vector<'T> =
130+
131+ // let n = r.NumRows
132+
133+ // if r.NumCols <> n || y.Length <> n then
134+ // invalidArg "dimensions" "R must be square and match the length of y"
135+
136+ // let x = Array.zeroCreate<'T> n
137+
138+ // for i = n - 1 downto 0 do
139+ // let mutable sum = y.[i]
140+ // for j = i + 1 to n - 1 do
141+ // sum <- sum - r.[i, j] * x.[j]
142+ // let diag = r.[i, i]
143+ // if diag = 'T.Zero then
144+ // invalidArg $"Matrix{i},{i}]" "Diagonal element is zero. Cannot divide."
145+ // x.[i] <- sum / diag
146+
147+ // x
148+
149+
89150 static member inline backSubstitute < 'T when 'T :> Numerics.INumber < 'T >
90151 and 'T : ( new : unit -> 'T )
91152 and 'T : struct
92153 and 'T : comparison
93154 and 'T :> ValueType >
94- ( r : Matrix < 'T >)
155+ ( R : Matrix < 'T >)
95156 ( y : Vector < 'T >) : Vector < 'T > =
96157
97- let n = r .NumRows
158+ let n = R .NumRows
98159
99- if r .NumCols <> n || y.Length <> n then
160+ if R .NumCols <> n || y.Length <> n then
100161 invalidArg " dimensions" " R must be square and match the length of y"
101162
102163 let x = Array.zeroCreate< 'T> n
164+ let cols = R.NumCols
165+ let rData = R.Data // row-major underlying array
103166
167+ // Backward substitution
104168 for i = n - 1 downto 0 do
105169 let mutable sum = y.[ i]
106- for j = i + 1 to n - 1 do
107- sum <- sum - r.[ i, j] * x.[ j]
108- let diag = r.[ i, i]
170+
171+ let startJ = i + 1
172+ let len = n - startJ
173+
174+ if len > 0 then
175+ // row slice: r[i, i+1 .. n-1]
176+ let rowOffset = i * cols + startJ
177+ let rowTailSpan = ReadOnlySpan< 'T>( rData, rowOffset, len)
178+
179+ // x slice: x[i+1 .. n-1]
180+ let xTailSpan = ReadOnlySpan< 'T>( x, startJ, len)
181+
182+ // subtract SIMD dot product
183+ let dot = SpanMath.dot( rowTailSpan, xTailSpan)
184+ sum <- sum - dot
185+
186+ let diag = R.[ i, i]
109187 if diag = 'T.Zero then
110- invalidArg $" r[{i},{i}]" " Diagonal element is zero. Cannot divide."
188+ invalidArg $" Matrix[{i},{i}]" " Diagonal element is zero. Cannot divide."
189+
111190 x.[ i] <- sum / diag
112191
113192 x
114193
115194
116195
117-
118196 /// Solve A * x = b for x, where A is a square matrix (n×n) and b is a vector (length n).
119197 static member inline solveLinearQR < 'T when 'T :> Numerics.INumber < 'T >
120198 and 'T : ( new : unit -> 'T )
@@ -249,10 +327,56 @@ type LinearAlgebra =
249327
250328
251329
252- /// Solve K * x = v (triangular system) in-place, returning a copy of x.
330+ ///// Solve K * x = v (triangular system) in-place, returning a copy of x.
331+ ///// K must be n×n, v must be length n.
332+ ///// isLower = true => forward substitution
333+ ///// isLower = false => backward substitution
334+ //static member inline solveTriangularLinearSystem
335+ // (K : Matrix<'T>)
336+ // (v : Vector<'T>)
337+ // (isLower : bool)
338+ // : Vector<'T> =
339+
340+ // let nK, mK = K.NumRows, K.NumCols
341+ // let nV = v.Length
342+ // if nK <> mK || nV <> nK then
343+ // invalidArg (nameof K) "K must be square, and v must match its dimension."
344+
345+ // let x = Array.copy v
346+ // let Kdata = K.Data // row-major flattened
347+
348+ // // Forward or backward substitution
349+ // if isLower then
350+ // // For i in [0..n-1]:
351+ // // x[i] <- ( x[i] - sum_{j=0..i-1}(K[i,j] * x[j]) ) / K[i,i]
352+ // for i = 0 to nK - 1 do
353+ // let mutable s = x.[i]
354+ // let rowOffset = i * nK
355+ // for j = 0 to i - 1 do
356+ // s <- s - (Kdata.[rowOffset + j] * x.[j])
357+ // let diag = Kdata.[rowOffset + i]
358+ // if diag = 'T.Zero then
359+ // invalidArg $"K[{i},{i}]" "Diagonal element is zero. Cannot divide."
360+ // x.[i] <- s / diag
361+ // else
362+ // // For i in [n-1..downto..0]:
363+ // // x[i] <- ( x[i] - sum_{j=i+1..n-1}(K[i,j] * x[j]) ) / K[i,i]
364+ // for i = nK - 1 downto 0 do
365+ // let mutable s = x.[i]
366+ // let rowOffset = i * nK
367+ // for j = i + 1 to nK - 1 do
368+ // s <- s - (Kdata.[rowOffset + j] * x.[j])
369+ // let diag = Kdata.[rowOffset + i]
370+ // if diag = 'T.Zero then
371+ // invalidArg $"K[{i},{i}]" "Diagonal element is zero. Cannot divide."
372+ // x.[i] <- s / diag
373+
374+ // x
375+
376+ /// Solve K * x = v (triangular system), returning a new x.
253377 /// K must be n×n, v must be length n.
254- /// isLower = true => forward substitution
255- /// isLower = false => backward substitution
378+ /// isLower = true => forward substitution (K lower triangular)
379+ /// isLower = false => backward substitution (K upper triangular)
256380 static member inline solveTriangularLinearSystem
257381 ( K : Matrix < 'T >)
258382 ( v : Vector < 'T >)
@@ -264,36 +388,12 @@ type LinearAlgebra =
264388 if nK <> mK || nV <> nK then
265389 invalidArg ( nameof K) " K must be square, and v must match its dimension."
266390
267- let x = Array.copy v
268- let Kdata = K.Data // row-major flattened
269-
270- // Forward or backward substitution
271391 if isLower then
272- // For i in [0..n-1]:
273- // x[i] <- ( x[i] - sum_{j=0..i-1}(K[i,j] * x[j]) ) / K[i,i]
274- for i = 0 to nK - 1 do
275- let mutable s = x.[ i]
276- let rowOffset = i * nK
277- for j = 0 to i - 1 do
278- s <- s - ( Kdata.[ rowOffset + j] * x.[ j])
279- let diag = Kdata.[ rowOffset + i]
280- if diag = 'T.Zero then
281- invalidArg $" K[{i},{i}]" " Diagonal element is zero. Cannot divide."
282- x.[ i] <- s / diag
392+ // L * x = v
393+ LinearAlgebra.forwardSubstitute K v
283394 else
284- // For i in [n-1..downto..0]:
285- // x[i] <- ( x[i] - sum_{j=i+1..n-1}(K[i,j] * x[j]) ) / K[i,i]
286- for i = nK - 1 downto 0 do
287- let mutable s = x.[ i]
288- let rowOffset = i * nK
289- for j = i + 1 to nK - 1 do
290- s <- s - ( Kdata.[ rowOffset + j] * x.[ j])
291- let diag = Kdata.[ rowOffset + i]
292- if diag = 'T.Zero then
293- invalidArg $" K[{i},{i}]" " Diagonal element is zero. Cannot divide."
294- x.[ i] <- s / diag
295-
296- x
395+ // R * x = v
396+ LinearAlgebra.backSubstitute K v
297397
298398
299399
0 commit comments