Skip to content

Commit 8cec08f

Browse files
authored
FFT module revamp + updating dune-lang (#680)
* FFT module revamp. - Added several new functionnalities to the FFT module by changing the dependency from FFTPACK to POCKETFTT. - new optionnal parameters for the API (nthreads, norm, ...) - new functions for cosine and sine transforms (dct, dst, ...) - Switched from dune 2.0 to dune 3.16 (this was required as I ran throught issues with linking while using 2.0) * Adding abstract types and documentation. - Added the ttrig_transform type to specify the DCT and DST types - Added the tnorm type to specify the normalization option for the FFTs. * Adding 2 FFT example usage. - One "complex" usage: computing a PSD spectrogram from input data - One "simple" usage: computing the maximum frequency peak in time series data using rfft. * Adding tests, fixing minor issues. - Fixed an issue where the DCT/DST parameters weren't passed in the right order - Fixed an issue where the norm factor of the DCT wasn't computed correctly (incorrect delta) - Generated a unit_fft file that tests various parameters of FFT. Values are generated using scipy.fft module for the FFT, DCT and DST functions - Changed pocketfft::detail:: namespace usage to just pocketfft:: in the C++ code for more readability * Remove pocketfft submodule from .gitmodules * Convert pocketfft submodule to regular directory * Ubuntu 4.x build fix. MacOS fix attempt. * Moved the definition outside extern C. - Kept the declaration inside the extern C but moved away the declaration, as an attempt to fix MacOS builds. * owl_core preprocessor for cpp compiling. * Adapted the fft2 and ifft2 prototypes to match new FFT module. - Added norm and nthreads parameters. * Fixing documentation strings. * Little code change for more similarities with scipy's implementation.
1 parent f8aeeea commit 8cec08f

30 files changed

+7072
-1987
lines changed

.gitmodules

Whitespace-only changes.

dune-project

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
(lang dune 2.0)
1+
(lang dune 3.16)
22

33
(name owl)

examples/dune

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(executables
2-
(names
2+
(names
33
backprop
44
checkpoint
55
cifar10_vgg
@@ -19,12 +19,23 @@
1919
lazy_mnist
2020
linear_algebra
2121
lstm
22+
max_freq
2223
mnist_cnn
2324
mnist_lenet
2425
newton_method
26+
specgram
2527
squeezenet
2628
test_log
2729
tfidf
28-
vgg16
29-
)
30-
(libraries owl))
30+
vgg16)
31+
(libraries owl)
32+
(flags ; in order to make the examples compile correctly even with the warnings.
33+
(:standard
34+
-warn-error
35+
-unused-value-declaration
36+
-warn-error
37+
-unused-var-strict
38+
-warn-error
39+
-unused-var
40+
-warn-error
41+
-unused-field)))

examples/max_freq.ml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(** This example shows how to compute the maximum peak frequency in time series data using the FFT module. *)
2+
3+
module G = Owl.Dense.Ndarray.Generic
4+
module FFT = Owl.Fft.Generic
5+
6+
let max_freq signal sampling_rate =
7+
(* Apply FFT *)
8+
let fft_result = FFT.rfft ~otyp:Bigarray.Complex32 signal in
9+
(* Get magnitude spectrum *)
10+
let magnitudes = G.abs fft_result in
11+
(* Find peak frequency *)
12+
let max_idx = ref 0 in
13+
let max_val = ref (G.get magnitudes [|0|]) in
14+
for i = 0 to G.numel magnitudes - 1 do
15+
let curr_val = G.get magnitudes [|i|] in
16+
if curr_val > !max_val then (
17+
max_val := curr_val ;
18+
max_idx := i )
19+
done ;
20+
(* Convert index to frequency *)
21+
float_of_int !max_idx *. sampling_rate /. float_of_int (G.numel signal)

examples/specgram.ml

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
(** This example, extracted from the SoundML library, shows how to compute a specgram using the FFT module *)
2+
3+
module G = Owl.Dense.Ndarray.Generic
4+
5+
(* helper to compute the fft frequencies *)
6+
let fftfreq (n : int) (d : float) =
7+
let nslice = ((n - 1) / 2) + 1 in
8+
let fhalf =
9+
G.linspace Bigarray.Float32 0. (float_of_int nslice) nslice
10+
in
11+
let shalf =
12+
G.linspace Bigarray.Float32 (-.float_of_int nslice) (-1.) nslice
13+
in
14+
let v = G.concatenate ~axis:0 [|fhalf; shalf|] in
15+
Owl.Arr.(1. /. (d *. float_of_int n) $* v)
16+
17+
(* Computes a one-sided PSD spectrogram with no padding and no detrend *)
18+
let specgram (nfft : int) (fs : int) ?(noverlap : int = 0) (x : (float, Bigarray.float32_elt) G.t) =
19+
let window = Owl.Signal.hann in (* we're using hann window *)
20+
assert (noverlap < nfft) ;
21+
(* we're making copies of the data from x and y to then use in place padding
22+
and operations *)
23+
let x = G.copy x in
24+
(* We're making sure the arrays are at least of size nfft *)
25+
let xshp = G.shape x in
26+
( if Array.get xshp 0 < nfft then
27+
let delta = nfft - Array.get xshp 0 in
28+
(* we're doing this in place in hope to gain a little bit of speed *)
29+
G.pad_ ~out:x ~v:0. [[0; delta - 1]; [0; 0]] x ) ;
30+
let scale_by_freq = true in
31+
let pad_to = nfft in
32+
let scaling_factor = 2. in
33+
let window = window nfft |> G.cast_d2s in
34+
let window =
35+
G.reshape G.(window * ones Bigarray.float32 [|nfft|]) [|-1; 1|]
36+
in
37+
let res =
38+
G.slide ~window:nfft ~step:(nfft - noverlap) x |> G.transpose
39+
in
40+
(* if we'd add a detrend, we'd need to do it before applying the window *)
41+
let res = G.(res * window) in
42+
(* here comes the rfft compute, if you're processing large audio data, you might want to set ~nthreads to
43+
something that suits both your hardware and your needs. *)
44+
let res = Owl.Fft.S.rfft res ~axis:0 in
45+
let freqs = fftfreq pad_to (1. /. float_of_int fs) in
46+
let conj = G.conj res in
47+
(* using in-place operations to avoid array copy *)
48+
G.mul_ ~out:res conj res;
49+
let slice = if nfft mod 2 = 0 then [[1; -1]; []] else [[1]; []] in
50+
let gslice = G.get_slice slice res in
51+
G.mul_scalar_ ~out:gslice gslice Complex.{re= scaling_factor; im= 0.} ;
52+
G.set_slice slice res gslice ;
53+
if scale_by_freq then (
54+
let window = G.abs window in
55+
G.div_scalar_ ~out:res res Complex.{re= float_of_int fs; im= 0.} ;
56+
let n = G.sum' (G.pow_scalar window (float_of_int 2)) in
57+
G.div_scalar_ ~out:res res Complex.{re= n; im= 0.} )
58+
else (
59+
let window = G.abs window in
60+
let n = Float.pow (G.sum' window) 2. in
61+
G.div_scalar_ ~out:res res Complex.{re= n; im= 0.} ) ;
62+
(res, freqs)

src/base/core/owl_graph.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type 'a node =
1313
mutable next : 'a node array
1414
; (* children of the node *)
1515
mutable attr : 'a (* indicate the validity *)
16-
}
16+
} [@@warning "-69"]
1717

1818
type order =
1919
| BFS

0 commit comments

Comments
 (0)