From c4240c8957deb91f3daa6f5765699a386dd1d8d4 Mon Sep 17 00:00:00 2001 From: AranMesquita Date: Sat, 19 Jul 2025 14:15:26 +0200 Subject: [PATCH 1/2] merged fibonacci-code branch to main --- .gitignore | 2 ++ src/lib.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/.gitignore b/.gitignore index 198dc01..f17ff89 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target /.venv Cargo.lock +__pycache__ +.vscode \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a492c32..1e1b1f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,5 +10,42 @@ fn sum_as_string(a: usize, b: usize) -> PyResult { #[pymodule] fn test_rust_in_python(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(sum_as_string, m)?)?; + m.add_function(wrap_pyfunction!(rust_nth_fibonacci_using_matrix_exponentiation, m)?)?; Ok(()) } + +// Calculates the nth Fibonacci number using matrix exponentiation. +// The Fibonacci sequence is defined as: +// F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2) for n >= 2 +// The nth Fibonacci number can be computed using matrix exponentiation in O(log n) time. +#[pyfunction] +fn rust_nth_fibonacci_using_matrix_exponentiation(n: u64) -> u64 { + if n == 0 { + 0 + } else { + let base: [u64; 4] = [1, 1, 1, 0]; + let result: [u64; 4] = _power(base, n - 1); + result[0] + } +} + +fn _power(mut m: [u64; 4], mut n: u64) -> [u64; 4] { + let mut result: [u64; 4] = [1, 0, 0, 1]; + while n > 0 { + if n % 2 == 1 { + result = _multiply(result, m); + } + m = _multiply(m, m); + n /= 2; + } + result +} + +fn _multiply(a: [u64; 4], b: [u64; 4]) -> [u64; 4] { + [ + a[0] * b[0] + a[1] * b[2], + a[0] * b[1] + a[1] * b[3], + a[2] * b[0] + a[3] * b[2], + a[2] * b[1] + a[3] * b[3], + ] +} \ No newline at end of file From f833eb346548e08d645d3ff58c5410f197908dfc Mon Sep 17 00:00:00 2001 From: AranMesquita Date: Sat, 19 Jul 2025 15:01:49 +0200 Subject: [PATCH 2/2] added benchmark to test the time it takes rust and python to calculate all fibonacci numbers up untill the 93rd term --- src-py/benchmark_funcs.py | 10 ++++++++++ src-py/runtime_bench_mark.py | 20 ++++++++++++++++++++ src/lib.rs | 11 ++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src-py/benchmark_funcs.py create mode 100644 src-py/runtime_bench_mark.py diff --git a/src-py/benchmark_funcs.py b/src-py/benchmark_funcs.py new file mode 100644 index 0000000..ac7f8d6 --- /dev/null +++ b/src-py/benchmark_funcs.py @@ -0,0 +1,10 @@ +import test_rust_in_python +from fibonacci_matrix_exponentiation import python_nth_fibonacci_using_matrix_exponentiation + +def python_runtime_benchmark(nth_term: int): + for n in range(nth_term): + python_nth_fibonacci_using_matrix_exponentiation(n) + + +def rust_runtime_benchmark(nth_term: int): + test_rust_in_python.rust_runtime_benchmark(nth_term) # type: ignore \ No newline at end of file diff --git a/src-py/runtime_bench_mark.py b/src-py/runtime_bench_mark.py new file mode 100644 index 0000000..307ca80 --- /dev/null +++ b/src-py/runtime_bench_mark.py @@ -0,0 +1,20 @@ +import timeit +from benchmark_funcs import python_runtime_benchmark, rust_runtime_benchmark # noqa: F401 + +# reason why we only have NTH_TERM = 93 as 93rd Fibonacci number is the largest that fits in a u64 +# and 94th Fibonacci number is the first that exceeds u64::MAX +NTH_TERM = 93 +NUMBER_OF_CALLS = 100000 + +rust_time = timeit.timeit( + stmt=f"rust_runtime_benchmark({NTH_TERM})", + globals=globals(), + number=NUMBER_OF_CALLS +) +python_time = timeit.timeit( + stmt=f"python_runtime_benchmark({NTH_TERM})", + globals=globals(), + number=NUMBER_OF_CALLS +) + +print(f"Rust avg: {rust_time * 1_000:.2f} µs, Python avg: {python_time * 1_000:.2f} µs (per call, over a total of {NTH_TERM * NUMBER_OF_CALLS:,} iterations)") \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 1e1b1f7..b709c36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ fn sum_as_string(a: usize, b: usize) -> PyResult { fn test_rust_in_python(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(sum_as_string, m)?)?; m.add_function(wrap_pyfunction!(rust_nth_fibonacci_using_matrix_exponentiation, m)?)?; + m.add_function(wrap_pyfunction!(rust_runtime_benchmark, m)?)?; Ok(()) } @@ -48,4 +49,12 @@ fn _multiply(a: [u64; 4], b: [u64; 4]) -> [u64; 4] { a[2] * b[0] + a[3] * b[2], a[2] * b[1] + a[3] * b[3], ] -} \ No newline at end of file +} + +#[pyfunction] +fn rust_runtime_benchmark(nth_term: u64) { + for n in 0..nth_term { + rust_nth_fibonacci_using_matrix_exponentiation(n); + } +} +