-
Notifications
You must be signed in to change notification settings - Fork 11
extend rustfft to singlestore DB #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| [package] | ||
| name = "fft" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
|
|
||
| [dependencies] | ||
| wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen.git", rev = "60e3c5b41e616fee239304d92128e117dd9be0a7" } | ||
| rustfft = "6.1.0" | ||
| num-complex = "0.4.3" | ||
| serde_json = "1.0.97" | ||
| serde = { version = "1.0.164", features = ["derive"] } | ||
|
|
||
| [lib] | ||
| crate-type = ["cdylib"] | ||
|
|
||
|
|
||
| [[bin]] | ||
| name = "test" | ||
| path = "bin/main.rs" | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| MODULE := fft | ||
|
|
||
| .PHONY: debug | ||
| debug: $(eval TGT:=debug) | ||
| debug: wasm | ||
|
|
||
| .PHONY: release | ||
| release: $(eval TGT:=release) | ||
| release: RELFLAGS = --release | ||
| release: wasm | ||
|
|
||
| .PHONY: wasm | ||
| wasm: | ||
| cargo wasi build --lib $(RELFLAGS) | ||
|
|
||
| .PHONY: test | ||
| test: debug | ||
| writ-docker --verbose \ | ||
| -e '[{"re":1.0,"im":2.0}]' \ | ||
| --wit $(MODULE).wit target/wasm32-wasi/debug/$(MODULE).wasm st-process-forward \ | ||
| 1 '[{"re":1.0,"im":2.0}]' | ||
| @echo PASS | ||
| writ-docker --verbose \ | ||
| -e '[{"re":1.0,"im":2.0}]' \ | ||
| --wit $(MODULE).wit target/wasm32-wasi/debug/$(MODULE).wasm st-process-inverse \ | ||
| 1 '[{"re":1.0,"im":2.0}]' | ||
| @echo PASS | ||
| writ-docker --verbose \ | ||
| -e '[{"re":1.0,"im":2.0}]' \ | ||
| --wit $(MODULE).wit target/wasm32-wasi/debug/$(MODULE).wasm st-scalar-process-forward \ | ||
| 1 '[{"re":1.0,"im":2.0}]' | ||
| @echo PASS | ||
| writ-docker --verbose \ | ||
| -e '[{"re":1.0,"im":2.0}]' \ | ||
| --wit $(MODULE).wit target/wasm32-wasi/debug/$(MODULE).wasm st-scalar-process-inverse \ | ||
| 1 '[{"re":1.0,"im":2.0}]' | ||
| @echo PASS | ||
| writ-docker --verbose \ | ||
| -e '[{"re":2.5,"im":4.5},{"re":-0.5,"im":-0.5}]' \ | ||
| --wit $(MODULE).wit target/wasm32-wasi/debug/$(MODULE).wasm st-process-forward \ | ||
| 2 '[{"re":1.0,"im":2.0},{"re":1.5,"im":2.5}]' | ||
| @echo PASS | ||
| writ-docker --verbose \ | ||
| -e '[{"re":2.5,"im":4.5},{"re":-0.5,"im":-0.5}]' \ | ||
| --wit $(MODULE).wit target/wasm32-wasi/debug/$(MODULE).wasm st-process-inverse \ | ||
| 2 '[{"re":1.0,"im":2.0},{"re":1.5,"im":2.5}]' | ||
| @echo PASS | ||
| .PHONY: clean | ||
| clean: | ||
| @cargo clean | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # Prerequisite | ||
| The easieast way is to install [dev-shell](https://github.com/singlestore-labs/singlestore-wasm-toolkit/blob/main/scripts/dev-shell) and then run `dev-shell .` from this repository | ||
|
|
||
| # Functions in this module: | ||
| `st_process_forward(l: u8, nums: vector<Complex>)`: `l` is the size of our `Fft` (internally, this is used to define `plan_fft_forward` of `FftPlanner`, this length has to divide the length of `nums`. This will perform forward fourier transform. Same requirement for `st_process_inverse`, which perform inverse fourier transform. | ||
|
|
||
| Refer to [Rustfft documents](https://docs.rs/rustfft/latest/rustfft/struct.FftPlanner.html#method.plan_fft_forward) for more details. | ||
|
|
||
| # Compiling | ||
| ```sh | ||
| cargo build --target wasm32-wasi | ||
| ``` | ||
|
|
||
| # Testing | ||
| ```sh | ||
| cargo run --bin test | ||
| ``` | ||
|
|
||
| # Cleaning | ||
| ```sh | ||
| cargo clean | ||
| ``` | ||
|
|
||
| # Push to database | ||
| After running `dev-shell`: | ||
| ``` | ||
| ../bin/pushwasm tvf \n | ||
| -n st_process_forward \n | ||
| --wasm target/wasm32-wasi/debug/fft.wasm \n | ||
| --wit fft.wit --abi canonical --conn 'mysql://root@ip_address_of_your_singlestore_db/db_name' | ||
| ``` | ||
| # Usage in database | ||
| ``` | ||
| SELECT * FROM (st_process_forward(1, [ROW(1.0, 2.5), ROW(2.0, 2.5)])); | ||
| ``` | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| use rustfft::{FftPlanner}; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment here that just says that this file is a test driver for the module, and is intended to be compiled and run on the native platform?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was going to ask you more about this. It's seems a bit inefficient to compile this with the module. This can be extended further to generate Makefile test (seems to be simple enough if we are going with the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One way we can deal with this is to use the pattern here: Using this approach, we can use Rust's built-in test framework and not require an additional file. Note the conditional compilation of the interface at the top. For this purpose, you can just ignore the "handle" stuff.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Either way, I'm not sure it's possible to completely remove the cognitive overhead of having native tests (other than not to include them). |
||
| use num_complex::{Complex64}; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| #[derive(Serialize, Deserialize)] | ||
| pub struct Complex64Def { | ||
| pub re: f64, | ||
| pub im: f64, | ||
| } | ||
|
|
||
| impl From<Complex64> for Complex64Def { | ||
| fn from(def: Complex64) -> Complex64Def { | ||
| Complex64Def { re : def.re, im : def.im } | ||
| } | ||
| } | ||
|
|
||
| fn from_num_complex(list_num_complex: Vec<Complex64>) -> Vec<Complex64Def> { | ||
| list_num_complex.iter().map(|x| Complex64Def { | ||
| re: x.re, | ||
| im: x.im | ||
| }).collect::<Vec<Complex64Def>>() | ||
| } | ||
|
|
||
| fn print_json_debug<'a, T: Serialize + Deserialize<'a>>(type_with_serialize: T) { | ||
| let json_buffer = serde_json::to_string(&type_with_serialize).unwrap(); | ||
| println!("{}", json_buffer); | ||
| } | ||
|
|
||
| fn test_forward(l: u8, mut buffer: Vec<Complex64>) { | ||
| let mut planner = FftPlanner::new(); | ||
| let fft = planner.plan_fft_forward(usize::from(l)); | ||
| fft.process(&mut buffer); | ||
| print_json_debug(from_num_complex(buffer)); | ||
| } | ||
|
|
||
| fn test_inverse(l: u8, mut buffer: Vec<Complex64>) { | ||
| let mut planner = FftPlanner::new(); | ||
| let fft = planner.plan_fft_inverse(usize::from(l)); | ||
| fft.process(&mut buffer); | ||
| print_json_debug(from_num_complex(buffer)); | ||
| } | ||
|
|
||
| fn main() { | ||
| //test_forward(1, vec![Complex64{ re: 1.0, im: 2.0 }; 1]); | ||
| //test_inverse(1, vec![Complex64{ re: 1.0, im: 2.0 }; 1]); | ||
| let vtor_input = vec![Complex64{ re: 1.0, im: 2.0 }, Complex64{ re: 1.5, im: 2.5 }]; | ||
| let json_input = serde_json::to_string(&from_num_complex(vtor_input.clone())).unwrap(); | ||
| println!("{}", json_input); | ||
| test_forward(1, vtor_input.clone()); | ||
| test_forward(2, vtor_input.clone()); | ||
| test_inverse(2, vtor_input.clone()); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| record stcomplex64 { | ||
| re: float64, | ||
| im: float64 | ||
| } | ||
|
|
||
| st-process-forward: func(l: u8, buf: list<stcomplex64>) -> list<stcomplex64> | ||
| st-process-inverse: func(l: u8, buf: list<stcomplex64>) -> list<stcomplex64> | ||
| st-scalar-process-forward: func(l: u8, buf: list<stcomplex64>) -> list<stcomplex64> | ||
| st-scalar-process-inverse: func(l: u8, buf: list<stcomplex64>) -> list<stcomplex64> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| use rustfft::{FftPlanner, FftPlannerScalar, num_complex::Complex64}; | ||
| use fft::{Stcomplex64}; | ||
|
|
||
| wit_bindgen_rust::export!("fft.wit"); | ||
|
|
||
| struct Fft; | ||
|
|
||
| fn from_num_complex(list_num_complex: Vec<Complex64>) -> Vec<Stcomplex64> { | ||
| list_num_complex.iter().map(|x| Stcomplex64 { | ||
| re: x.re, | ||
| im: x.im | ||
| }).collect::<Vec<Stcomplex64>>() | ||
| } | ||
|
|
||
| fn from_st_complex(list_st_complex: Vec<Stcomplex64>) -> Vec<Complex64> { | ||
| list_st_complex.iter().map(|x| Complex64::new(x.re, x.im)).collect::<Vec<Complex64>>() | ||
| } | ||
|
|
||
| impl crate::fft::Fft for Fft { | ||
| fn st_process_forward(l: u8, buf: Vec<Stcomplex64>) -> Vec<Stcomplex64> { | ||
| let mut planner = FftPlanner::new(); | ||
| let fft = planner.plan_fft_forward(usize::from(l)); | ||
| let mut buffer = from_st_complex(buf); | ||
| fft.process(&mut buffer); | ||
| from_num_complex(buffer) | ||
| } | ||
| fn st_process_inverse(l: u8, buf: Vec<Stcomplex64>) -> Vec<Stcomplex64> { | ||
| let mut planner = FftPlanner::new(); | ||
| let fft = planner.plan_fft_inverse(usize::from(l)); | ||
| let mut buffer = from_st_complex(buf); | ||
| fft.process(&mut buffer); | ||
| from_num_complex(buffer) | ||
| } | ||
| fn st_scalar_process_forward(l: u8, buf: Vec<Stcomplex64>) -> Vec<Stcomplex64> { | ||
| let mut planner = FftPlannerScalar::new(); | ||
| let fft = planner.plan_fft_forward(usize::from(l)); | ||
| let mut buffer = from_st_complex(buf); | ||
| fft.process(&mut buffer); | ||
| from_num_complex(buffer) | ||
| } | ||
| fn st_scalar_process_inverse(l: u8, buf: Vec<Stcomplex64>) -> Vec<Stcomplex64> { | ||
| let mut planner = FftPlannerScalar::new(); | ||
| let fft = planner.plan_fft_inverse(usize::from(l)); | ||
| let mut buffer = from_st_complex(buf); | ||
| fft.process(&mut buffer); | ||
| from_num_complex(buffer) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's flesh this file out a little more. Here's a good template to follow:
https://github.com/singlestore-labs/singlestoredb-extension-bloom-filters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated