Skip to content

Commit c91a242

Browse files
committed
Optimized forwardSubstitute
1 parent 4a647ad commit c91a242

1 file changed

Lines changed: 51 additions & 7 deletions

File tree

src/FsMath/Algebra/LinearAlgebra.fs

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,46 @@ type LinearAlgebra =
8484

8585
Matrix(m, n, qData), r
8686

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+
87120
/// <summary>Forward substitute to solve L * x = y</summary>
88121
/// <remarks>L is lower triangular</remarks>
89122
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>
123+
and 'T : (new: unit -> 'T)
124+
and 'T : struct
125+
and 'T : comparison
126+
and 'T :> ValueType>
94127
(L : Matrix<'T>)
95128
(y : Vector<'T>) : Vector<'T> =
96129

@@ -103,15 +136,26 @@ type LinearAlgebra =
103136
let cols = L.NumCols
104137
let lData = L.Data
105138

106-
// Again, scalar version; easy to SIMD the inner sum later
139+
// Forward substitution:
140+
// x[i] <- ( y[i] - sum_{j=0..i-1}(L[i,j] * x[j]) ) / L[i,i]
107141
for i = 0 to n - 1 do
108142
let mutable s = y.[i]
109143
let rowOffset = i * cols
110-
for j = 0 to i - 1 do
111-
s <- s - lData.[rowOffset + j] * x.[j]
144+
let len = i // number of entries below diagonal in this row (0..i-1)
145+
146+
if len > 0 then
147+
// row slice: L[i, 0 .. i-1]
148+
let rowSpan = ReadOnlySpan<'T>(lData, rowOffset, len)
149+
// x slice: x[0 .. i-1]
150+
let xSpan = ReadOnlySpan<'T>(x, 0, len)
151+
152+
let dot = SpanMath.dot(rowSpan, xSpan)
153+
s <- s - dot
154+
112155
let diag = lData.[rowOffset + i]
113156
if diag = 'T.Zero then
114157
invalidArg $"Matrix[{i},{i}]" "Diagonal element is zero. Cannot divide."
158+
115159
x.[i] <- s / diag
116160

117161
x

0 commit comments

Comments
 (0)