Skip to content

Commit 27094a5

Browse files
committed
ADd ChiSquare contingency table
1 parent 43d4ecb commit 27094a5

File tree

11 files changed

+1030
-11
lines changed

11 files changed

+1030
-11
lines changed

src/FSharp.Stats/AlgTypes/GenericMath.fs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ module GenericMath =
6161
let inline floor<'T when 'T :> Numerics.IFloatingPoint<'T>> (x: 'T) : 'T =
6262
'T. Floor(x)
6363

64-
64+
/// Generic floor function
65+
let inline epsilon<'T when 'T :> Numerics.IFloatingPoint<'T>> () : 'T =
66+
'T.CreateTruncating System.Double.Epsilon
67+
6568
// let inline min x y = if x < y then x else y
6669
// let inline max x y = if x > y then x else y
6770

src/FSharp.Stats/AlgTypes/SIMDUtils.fs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ open System.Runtime.InteropServices
55
open System.Runtime.CompilerServices
66

77

8+
9+
10+
811
/// <summary>
912
/// A utility class for performing SIMD (Single Instruction, Multiple Data) operations on arrays.
1013
/// </summary>
@@ -155,6 +158,44 @@ type SIMDUtils() =
155158
results
156159

157160

161+
static member inline mapFoldUnchecked<'T
162+
when 'T :> Numerics.INumber<'T>
163+
and 'T : (new: unit -> 'T)
164+
and 'T : struct
165+
and 'T :> ValueType>
166+
(fMapSimd: Numerics.Vector<'T> -> Numerics.Vector<'T>,
167+
fMap: 'T -> 'T,
168+
fReduceSimd: Numerics.Vector<'T> -> Numerics.Vector<'T> -> Numerics.Vector<'T>,
169+
fReduceScalar: 'T -> 'T -> 'T,
170+
zero: 'T,
171+
input: ReadOnlySpan<'T>)
172+
: 'T =
173+
174+
let len = input.Length
175+
let simdSize = Numerics.Vector<'T>.Count
176+
let simdBlocks = len / simdSize
177+
let tailStart = simdBlocks * simdSize
178+
179+
// SIMD map + accumulation
180+
let mutable accVec = Numerics.Vector<'T>(zero)
181+
let inputVec = MemoryMarshal.Cast<'T, Numerics.Vector<'T>>(input)
182+
183+
for i = 0 to simdBlocks - 1 do
184+
let mapped = fMapSimd inputVec[i]
185+
accVec <- fReduceSimd accVec mapped
186+
187+
// Reduce SIMD accumulator to scalar
188+
let mutable acc = accVec.[0]
189+
for i = 1 to simdSize - 1 do
190+
acc <- fReduceScalar acc accVec.[i]
191+
192+
// Scalar tail
193+
for i = tailStart to len - 1 do
194+
acc <- fReduceScalar acc (fMap input[i])
195+
196+
acc
197+
198+
158199

159200
type SIMDRangeUtils =
160201

src/FSharp.Stats/AlgTypes/Vector.fs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,17 @@ type Vector =
211211
let slotCount = length / slotSize
212212
let ceiling = slotSize * slotCount
213213

214-
let mutable result = Numerics.Vector<'T>.Zero
215214
let v1Span = MemoryMarshal.Cast<'T, Numerics.Vector<'T>>(v1.AsSpan())
216215
let v2Span = MemoryMarshal.Cast<'T, Numerics.Vector<'T>>(v2.AsSpan())
217-
let mutable scalarResult = LanguagePrimitives.GenericZero<'T>
216+
let mutable result = Numerics.Vector<'T>.Zero
217+
//let mutable scalarResult = LanguagePrimitives.GenericZero<'T>
218218

219219
for i = 0 to slotCount - 1 do
220220
result <- Numerics.Vector.Add(result, Numerics.Vector.Multiply(v1Span.[i], v2Span.[i]))
221-
scalarResult <- Numerics.Vector.Sum result
222-
221+
//scalarResult <- Numerics.Vector.Sum result
222+
223+
let mutable scalarResult = Numerics.Vector.Sum result
224+
223225
for i = ceiling to v1.Length - 1 do
224226
scalarResult <- scalarResult + (v1.[i] * v2.[i])
225227

src/FSharp.Stats/Algebra/SVD.fs

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,265 @@
11
namespace FSharp.Stats.Algebra
22

3+
4+
open System
35
open FSharp.Stats
6+
open FSharp.Stats.Acceleration
7+
8+
[<Struct>]
9+
type Householder<'T when 'T :> Numerics.INumber<'T>> =
10+
{
11+
V: Vector<'T>
12+
Tau: 'T
13+
Beta: 'T
14+
}
15+
16+
17+
type Householder() =
18+
19+
static member inline create<'T when 'T :> Numerics.INumber<'T>
20+
and 'T : (new: unit -> 'T)
21+
and 'T : struct
22+
and 'T : comparison
23+
and 'T :> ValueType
24+
and 'T :> Numerics.IRootFunctions<'T>>
25+
(x: Vector<'T>) : Householder<'T> =
26+
27+
let xSpan = x.AsSpan()
28+
let alpha = xSpan[0]
29+
let tail = xSpan.Slice(1)
30+
31+
let zero = GenericMath.zero<'T>
32+
let one = GenericMath.one<'T>
33+
34+
let sigma =
35+
SIMDUtils.mapFoldUnchecked(
36+
(fun v -> v * v),
37+
(fun x -> x * x),
38+
(+),
39+
(+),
40+
zero,
41+
tail
42+
)
43+
44+
if sigma.Equals(zero) then
45+
let v = Vector.zeroCreate x.Length
46+
v.[0] <- one
47+
{
48+
V = v
49+
Tau = zero
50+
Beta = alpha
51+
}
52+
else
53+
let sum = alpha * alpha + sigma
54+
let beta = GenericMath.sqrt sum
55+
56+
let v0 =
57+
if alpha <= zero then alpha - beta
58+
else -sigma / (alpha + beta)
59+
60+
let tau =
61+
let v0Sq = v0 * v0
62+
(v0Sq + v0Sq) / (sigma + v0Sq)
63+
64+
let v = Vector.divideScalar v0 x // SIMD-aware scalar division
65+
v.[0] <- one
66+
67+
{
68+
V = v
69+
Tau = tau
70+
Beta = beta
71+
}
72+
73+
static member inline applyLeft<'T when 'T :> Numerics.INumber<'T>
74+
and 'T : (new: unit -> 'T)
75+
and 'T : struct
76+
and 'T : equality
77+
and 'T :> ValueType>
78+
(h: Householder<'T>, A: Matrix<'T>, rowOffset: int) =
79+
80+
let v = h.V
81+
let tau = h.Tau
82+
let m = A.NumRows
83+
let n = A.NumCols
84+
let vLen = v.Length
85+
86+
for j = 0 to n - 1 do
87+
let mutable dot = GenericMath.zero<'T>
88+
for i = 0 to vLen - 1 do
89+
dot <- dot + v.[i] * A.[rowOffset + i, j]
90+
let scale = tau * dot
91+
for i = 0 to vLen - 1 do
92+
A.[rowOffset + i, j] <- A.[rowOffset + i, j] - scale * v.[i]
93+
94+
95+
static member inline applyRight<'T when 'T :> Numerics.INumber<'T>
96+
and 'T : (new: unit -> 'T)
97+
and 'T : struct
98+
and 'T : equality
99+
and 'T :> ValueType>
100+
(h: Householder<'T>, A: Matrix<'T>, colOffset: int) =
101+
102+
let v = h.V
103+
let tau = h.Tau
104+
let m = A.NumRows
105+
let vLen = v.Length
106+
107+
for i = 0 to m - 1 do
108+
let mutable dot = GenericMath.zero<'T>
109+
for j = 0 to vLen - 1 do
110+
dot <- dot + A.[i, colOffset + j] * v.[j]
111+
let scale = tau * dot
112+
for j = 0 to vLen - 1 do
113+
A.[i, colOffset + j] <- A.[i, colOffset + j] - scale * v.[j]
114+
115+
type Bidiagonalization() =
116+
117+
static member inline bidiagonalizeInPlace<'T
118+
when 'T :> Numerics.INumber<'T>
119+
and 'T : (new: unit -> 'T)
120+
and 'T : struct
121+
and 'T : comparison
122+
and 'T :> ValueType
123+
and 'T :> Numerics.IRootFunctions<'T>>
124+
(A: Matrix<'T>) : unit =
125+
126+
let m = A.NumCols
127+
let n = A.NumRows
128+
let minMN = min m n
129+
130+
for k = 0 to minMN - 1 do
131+
// --- LEFT REFLECTION: Column k (zero below diagonal) ---
132+
let colLen = m - k
133+
let colVector = Array.init colLen (fun i -> A.[k + i, k])
134+
let hLeft = Householder.create colVector
135+
136+
Householder.applyLeft(hLeft, A, k)
137+
138+
// Overwrite A[k..,k] with Householder beta at top and zeros below
139+
A.[k, k] <- hLeft.Beta
140+
for i = k + 1 to m - 1 do
141+
A.[i, k] <- GenericMath.zero<'T>
142+
143+
144+
145+
// --- RIGHT REFLECTION: Row k (zero right of superdiagonal) ---
146+
if k < n - 1 then
147+
let rowLen = n - (k + 1)
148+
let rowVector = Array.init rowLen (fun j -> A.[k, k + 1 + j])
149+
let hRight = Householder.create rowVector
150+
151+
Householder.applyRight(hRight, A, k + 1)
152+
153+
// Overwrite A[k,k+1..] with Householder beta at front, zeros right
154+
A.[k, k + 1] <- hRight.Beta
155+
for j = k + 2 to n - 1 do
156+
A.[k, j] <- GenericMath.zero<'T>
157+
158+
159+
[<Struct>]
160+
type Bidiagonal<'T when 'T :> Numerics.INumber<'T>> = {
161+
D : Vector<'T> // main diagonal
162+
E : Vector<'T> // superdiagonal (length n-1)
163+
}
164+
165+
166+
/// Givens Rotation (Generic)
167+
module Givens =
168+
169+
let inline compute<'T
170+
when 'T :> Numerics.INumber<'T>
171+
and 'T : comparison
172+
and 'T :> Numerics.IRootFunctions<'T>
173+
and 'T :> Numerics.IFloatingPointIeee754<'T>>
174+
(a: 'T) (b: 'T) : 'T * 'T =
175+
176+
if b = GenericMath.zero then GenericMath.one, GenericMath.zero
177+
elif GenericMath.abs b > GenericMath.abs a then
178+
let t = a / b
179+
let s = GenericMath.one / GenericMath.sqrt(GenericMath.one + t * t)
180+
s * t, s
181+
else
182+
let t = b / a
183+
let c = GenericMath.one / GenericMath.sqrt(GenericMath.one + t * t)
184+
c, c * t
185+
186+
module GolubKahan =
187+
188+
let inline diagonalize<'T
189+
when 'T :> Numerics.INumber<'T>
190+
and 'T :> Numerics.IRootFunctions<'T>
191+
and 'T :> Numerics.IFloatingPointIeee754<'T>
192+
and 'T : comparison
193+
and 'T : struct
194+
and 'T : (new: unit -> 'T)
195+
and 'T :> ValueType>
196+
(b: Bidiagonal<'T>) : Vector<'T> =
197+
198+
let d = Array.copy b.D
199+
let e = Array.copy b.E
200+
let n = d.Length
201+
let eps = GenericMath.epsilon()
202+
let two = GenericMath.one + GenericMath.one
203+
204+
let mutable iter = 0
205+
let maxIter = 1000
206+
let mutable doneIterating = false
207+
208+
209+
210+
while iter < maxIter && not doneIterating do
211+
let mutable converged = true
212+
213+
for i = 0 to n - 2 do
214+
let tolerance = eps * (GenericMath.abs d.[i] + GenericMath.abs d.[i + 1])
215+
if abs e.[i] > tolerance then
216+
converged <- false
217+
218+
if converged then
219+
doneIterating <- true
220+
else
221+
// Wilkinson shift
222+
let m = n - 1
223+
let dm1 = d.[m - 1]
224+
let dm = d.[m]
225+
let em1 = e.[m - 1]
226+
227+
let delta = (dm1 - dm) / two
228+
let sign =
229+
if delta >= GenericMath.zero then GenericMath.one
230+
else -GenericMath.one
231+
232+
let denom = abs delta + sqrt (delta * delta + em1 * em1)
233+
let mu = dm - sign * (em1 * em1) / denom
234+
235+
// Initial bulge
236+
let mutable x = d.[0] * d.[0] - mu * mu
237+
let mutable z = d.[0] * e.[0]
238+
239+
for k = 0 to n - 2 do
240+
let c, s = Givens.compute x z
241+
242+
let dk = d.[k]
243+
let ek = e.[k]
244+
let dk1 = d.[k + 1]
245+
246+
let tau1 = c * dk + s * ek
247+
let tau2 = -s * dk1
248+
249+
d.[k] <- c * tau1 + s * tau2
250+
e.[k] <- c * ek - s * dk1
251+
d.[k + 1] <- s * tau1 - c * tau2
252+
253+
if k < n - 2 then
254+
x <- e.[k]
255+
z <- -s * e.[k + 1]
256+
e.[k + 1] <- c * e.[k + 1]
257+
258+
iter <- iter + 1
259+
260+
d
261+
262+
4263

5264
module SVD =
6265

src/FSharp.Stats/FSharp.Stats.fsproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
<Compile Include="Quantile.fs" />
5757
<Compile Include="Precision.fs" />
5858
<Compile Include="Geometry.fs" />
59+
<Compile Include="Table.fs" />
5960
<!-- SpecialFunctions -->
6061
<Compile Include="SpecialFunctions\Gamma.fs" />
6162
<Compile Include="SpecialFunctions\Factorial.fs" />
@@ -123,6 +124,7 @@
123124
<!-- Testing -->
124125
<Compile Include="Testing\Tables.fs" />
125126
<Compile Include="Testing\TestStatistics.fs" />
127+
<Compile Include="Testing\ContingencyTable.fs" />
126128
<Compile Include="Testing\ConfusionMatrix.fs" />
127129
<Compile Include="Testing\ComparisonMetrics.fs" />
128130
<Compile Include="Testing\Anova.fs" />

0 commit comments

Comments
 (0)