A learning project in which I defined a polynomial struct and implemented FFT for polynomial multiplication, with everything written in Rust.
x axis is degree, and y axis is in ms. Performance will differ depending on the specs of each machine.
- Results on my Linux Laptop.
- Results on my Windows desktop.
It is clear that, apart from the random spikes, the regular polynomial multiplication follows a O(n^2) trend, while FFT multiplication is O(nlog(n)). The reason for the "step-function" look is that we have to zero-fill polynomials so that the degree is a power of 2 in FFT. 2-threaded version is consistently faster than single-threaded FFT, with the exception of very small degs.
This is my summary of things I did in this project.
-
Algorithms
- Basic mathematical operations for polynomials, including long division.
- Raise any polynomial to a power n, where n is a non-negative integer, by the use of the "squaring" algorithm.
- FFT for polynomial multiplication.
- Pretty print a polynomial.
-
Rust
- Unit testing.
- Polars basic in Rust.
- Use of trait bounds to make general polynomials.
- Other Rust syntax and concepts.
- Getting more familiar with ndarray.
- Using scoped threads to further speed up the code.
-
Polynomials<T>, polynomials over T, where T: Num + Clone + Copy + Display
- T is like a mathematical field.
- Mathematically speaking, T can be more abstract, say T can be any mathematical ring. But then divide_by will not make sense and has to be rewritten.
- If we ditch the Div trait bound in Num(NumOps), then we will get something close to a mathematical ring.
-
Implemented plus, minus, multiply, divide_by (long division) for polynomials, and you may use p1 + p2, p1 - p2, and p1 * p2.
-
The normal multiply method on the Polynomial struct uses the O(n^2) way. FFT for multiplication follows the Cooley–Tukey FFT algorithm. FFT is implemented for Polynomial<f64>. It can be extended to Polynomial<Complex64> easily. If one wants to extend this to other more general fields, one has to define more complicated structures and define n-th roots of unity in those fields (e.g. Finite fields, etc...), which is very beyond this project.
-
FFT has float point precision issues, so I rounded everything to 5 decimal places. You may change the decimal places.
-
Formal derivative of a polynomial is implemented using same logic as the pow function. It might seem stupid to do this, but how can we properly cast usize into T? Formal derivative of a polynomial can be quite general, and T may not even be real or complex.
-
It is built for fun and I am not aiming for the utmost speed, as this is more of a learning project.
-
Depending on your machine, some tests might fail due to numerical errors, e.g. 0.9999999 != 1.0
-
cargo run -r 5000
- Learning FFT
- FFT Lecture Notes
- Documentation of ndarray, num_traits, etc.
- Medium article
- More detailed explanation