Factoring FHE programs
In this section we'll show you how to factor your programs in a specific way that allows for
- Reusing algorithms with different data types.
- Running your algorithm without FHE. This allows you to debug the algorithm without encryption getting in your way and measure FHE's performance overhead.
Let's begin by rewriting our simple_multiply
example with a common implementation (simple_multiply_impl
):
#![allow(unused)] fn main() { use sunscreen::{ fhe_program, types::{bfv::{Signed, Fractional}, Cipher}, }; use std::ops::Mul; fn simple_multiply_impl<T, U>(a: T, b: U) -> T where T: Mul<U, Output=T> + Copy { a * b } #[fhe_program(scheme = "bfv")] fn simple_multiply_signed(a: Cipher<Signed>, b: Cipher<Signed>) -> Cipher<Signed> { simple_multiply_impl(a, b) } #[fhe_program(scheme = "bfv")] fn simple_multiply_fractional(a: Cipher<Fractional<64>>, b: Fractional<64>) -> Cipher<Fractional<64>> { simple_multiply_impl(a, b) } }
The first FHE program multiplies encrypted Signed
values. In the second, a
is an encrypted Fractional
value while b
is an unencrypted Fractional
value. We can run both of these programs using runtime.run()
as normal.
Running your implementation without FHE
If we inspect the trait bounds on simple_multiply_impl
, we'll notice there is no mention of anything Sunscreen related. This means we can directly run our implementation with Rust i64
values by simply calling:
use std::ops::Mul; fn simple_multiply_impl<T, U>(a: T, b: U) -> T where T: Mul<U, Output=T> + Copy { a * b } fn main() { simple_multiply_impl(7, 5); }
It's worth explicitly pointing out that T
and U
may be of the same or different types; the trait bounds merely require that you can multiply T
and U
values.