Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions examples/rust/fft/Cargo.toml
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"

52 changes: 52 additions & 0 deletions examples/rust/fft/Makefile
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


36 changes: 36 additions & 0 deletions examples/rust/fft/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Prerequisite
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

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)]));
```

53 changes: 53 additions & 0 deletions examples/rust/fft/bin/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use rustfft::{FftPlanner};
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 make test approach, I'm not sure). However, the main point is this can be kept separately, maybe a generate_tests library to generate tests for this (and potentially other modules we will be writing) but I don't know if it's a good idea or the best way to do this.

Copy link
Contributor

Choose a reason for hiding this comment

The 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:
https://github.com/singlestore-labs/singlestoredb-extension-bloom-filters/blob/main/src/lib.rs

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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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());
}

9 changes: 9 additions & 0 deletions examples/rust/fft/fft.wit
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>
48 changes: 48 additions & 0 deletions examples/rust/fft/src/lib.rs
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)
}
}