mirror of
https://github.com/Sunscreen-tech/Sunscreen.git
synced 2026-04-19 03:00:06 -04:00
Merge pull request #39 from Sunscreen-tech/rweber/plaintext
Can divide fractional by constant
This commit is contained in:
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -151,6 +151,24 @@
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug fractional integration tests in library 'sunscreen_compiler'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--package=sunscreen_compiler",
|
||||
],
|
||||
"filter": {
|
||||
"name": "fractional",
|
||||
"kind": "test"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
|
||||
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -304,6 +304,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e"
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "1.1.0"
|
||||
@@ -869,6 +878,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"clap",
|
||||
"float-cmp",
|
||||
"log",
|
||||
"num",
|
||||
"petgraph",
|
||||
|
||||
@@ -19,4 +19,5 @@ seal = { path = "../seal" }
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0.72"
|
||||
serde_json = "1.0.72"
|
||||
float-cmp = "0.9.0"
|
||||
@@ -1,8 +1,10 @@
|
||||
use seal::Plaintext as SealPlaintext;
|
||||
|
||||
use crate::types::{
|
||||
ops::{GraphCipherAdd, GraphCipherPlainAdd},
|
||||
Cipher, GraphCipherMul, GraphCipherSub,
|
||||
ops::{
|
||||
GraphCipherAdd, GraphCipherConstAdd, GraphCipherMul, GraphCipherPlainMul, GraphCipherPlainAdd, GraphCipherSub, GraphCipherConstMul, GraphCipherConstDiv
|
||||
},
|
||||
Cipher,
|
||||
};
|
||||
use crate::{
|
||||
crate_version,
|
||||
@@ -213,7 +215,26 @@ impl<const INT_BITS: usize> GraphCipherPlainAdd for Fractional<INT_BITS> {
|
||||
b: CircuitNode<Self::Right>,
|
||||
) -> CircuitNode<Cipher<Self::Left>> {
|
||||
with_ctx(|ctx| {
|
||||
let n = ctx.add_addition(a.ids[0], b.ids[0]);
|
||||
let n = ctx.add_addition_plaintext(a.ids[0], b.ids[0]);
|
||||
|
||||
CircuitNode::new(&[n])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const INT_BITS: usize> GraphCipherConstAdd for Fractional<INT_BITS> {
|
||||
type Left = Fractional<INT_BITS>;
|
||||
type Right = f64;
|
||||
|
||||
fn graph_cipher_const_add(
|
||||
a: CircuitNode<Cipher<Self::Left>>,
|
||||
b: Self::Right,
|
||||
) -> CircuitNode<Cipher<Self::Left>> {
|
||||
with_ctx(|ctx| {
|
||||
let b = Self::from(b).try_into_plaintext(&ctx.params).unwrap();
|
||||
|
||||
let lit = ctx.add_plaintext_literal(b.inner);
|
||||
let n = ctx.add_addition_plaintext(a.ids[0], lit);
|
||||
|
||||
CircuitNode::new(&[n])
|
||||
})
|
||||
@@ -252,6 +273,61 @@ impl<const INT_BITS: usize> GraphCipherMul for Fractional<INT_BITS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const INT_BITS: usize> GraphCipherPlainMul for Fractional<INT_BITS> {
|
||||
type Left = Fractional<INT_BITS>;
|
||||
type Right = Fractional<INT_BITS>;
|
||||
|
||||
fn graph_cipher_plain_mul(
|
||||
a: CircuitNode<Cipher<Self::Left>>,
|
||||
b: CircuitNode<Self::Right>,
|
||||
) -> CircuitNode<Cipher<Self::Left>> {
|
||||
with_ctx(|ctx| {
|
||||
let n = ctx.add_multiplication_plaintext(a.ids[0], b.ids[0]);
|
||||
|
||||
CircuitNode::new(&[n])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const INT_BITS: usize> GraphCipherConstMul for Fractional<INT_BITS> {
|
||||
type Left = Fractional<INT_BITS>;
|
||||
type Right = f64;
|
||||
|
||||
fn graph_cipher_const_mul(
|
||||
a: CircuitNode<Cipher<Self::Left>>,
|
||||
b: Self::Right,
|
||||
) -> CircuitNode<Cipher<Self::Left>> {
|
||||
with_ctx(|ctx| {
|
||||
let b = Self::from(b).try_into_plaintext(&ctx.params).unwrap();
|
||||
let lit = ctx.add_plaintext_literal(b.inner);
|
||||
|
||||
let n = ctx.add_multiplication_plaintext(a.ids[0], lit);
|
||||
|
||||
CircuitNode::new(&[n])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const INT_BITS: usize> GraphCipherConstDiv for Fractional<INT_BITS> {
|
||||
type Left = Fractional<INT_BITS>;
|
||||
type Right = f64;
|
||||
|
||||
fn graph_cipher_const_div(
|
||||
a: CircuitNode<Cipher<Self::Left>>,
|
||||
b: f64,
|
||||
) -> CircuitNode<Cipher<Self::Left>> {
|
||||
with_ctx(|ctx| {
|
||||
let b = Self::try_from(1. / b).unwrap().try_into_plaintext(&ctx.params).unwrap();
|
||||
|
||||
let lit = ctx.add_plaintext_literal(b.inner);
|
||||
|
||||
let n = ctx.add_multiplication_plaintext(a.ids[0], lit);
|
||||
|
||||
CircuitNode::new(&[n])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const INT_BITS: usize> TryIntoPlaintext for Fractional<INT_BITS> {
|
||||
fn try_into_plaintext(
|
||||
&self,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
use seal::Plaintext as SealPlaintext;
|
||||
|
||||
use crate::types::{ops::{GraphCipherAdd, GraphCipherMul, GraphCipherPlainAdd, GraphCipherPlainMul, GraphCipherConstAdd, GraphCipherConstMul}, Cipher, };
|
||||
use crate::types::{
|
||||
ops::{
|
||||
GraphCipherAdd, GraphCipherConstAdd, GraphCipherConstMul, GraphCipherMul,
|
||||
GraphCipherPlainAdd, GraphCipherPlainMul,
|
||||
},
|
||||
Cipher,
|
||||
};
|
||||
use crate::{
|
||||
types::{intern::CircuitNode, BfvType, FheType, TypeNameInstance},
|
||||
with_ctx, CircuitInputTrait, Params, TypeName as DeriveTypeName, WithContext,
|
||||
@@ -221,4 +227,4 @@ impl GraphCipherPlainMul for Signed {
|
||||
CircuitNode::new(&[n])
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,3 +299,15 @@ where
|
||||
T::graph_cipher_div(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Div<U> for CircuitNode<Cipher<T>>
|
||||
where
|
||||
U: FheLiteral,
|
||||
T: FheType + GraphCipherConstDiv<Left = T, Right = U>,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, rhs: U) -> Self::Output {
|
||||
T::graph_cipher_const_div(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,8 @@
|
||||
* efficiently as [`Signed`](crate::types::bfv::Signed) and
|
||||
* [`Unsigned`](crate::types::bfv::Unsigned) types. This type has complex overflow
|
||||
* conditions. This type intrinsically supports homomorphic addition
|
||||
* multiplication, and negation. Dividing by plaintext is possible by
|
||||
* computing the divisor's reciprical and multiplying. Dividing by ciphertext
|
||||
* is not possible.
|
||||
* multiplication, and negation. Dividing by an [`f64`] constant is supported.
|
||||
* Dividing by ciphertext is not possible.
|
||||
* * The [`Rational`](crate::types::bfv::Rational) type allows quasi fixed-point
|
||||
* representation. This type interally uses 2 ciphertexts, and is thus requires
|
||||
* twice as much space as other types. Its overflow semantics are effectively
|
||||
@@ -49,7 +48,7 @@
|
||||
* | Fractional | 1 | complex | signed decimal | 1 add | 1 mul | 1 sub | 1 neg | 1 mul* |
|
||||
* | Rational | 2 | moderate | signed decimal | 2 muls + 1 sub | 2 muls | 2 muls + 1 sub | 1 neg | 2 muls |
|
||||
*
|
||||
* `* Plaintext division only.`
|
||||
* `* Division by constant only.`
|
||||
*
|
||||
* The set of feasible computations under FHE with BFV is fairly limited. For
|
||||
* example, comparisons, modulus, transcendentals, are generally very difficult
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::types::{intern::CircuitNode, Cipher, FheType};
|
||||
use crate::types::{intern::{CircuitNode, FheLiteral}, Cipher, FheType};
|
||||
|
||||
/**
|
||||
* Called when a circuit encounters a / operation on two encrypted types.
|
||||
@@ -22,3 +22,26 @@ pub trait GraphCipherDiv {
|
||||
b: CircuitNode<Cipher<Self::Right>>,
|
||||
) -> CircuitNode<Cipher<Self::Left>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a circuit encounters a / operation on an encrypted numerator and plaintext denominator.
|
||||
*/
|
||||
pub trait GraphCipherConstDiv {
|
||||
/**
|
||||
* The type of the left operand
|
||||
*/
|
||||
type Left: FheType + From<Self::Right>;
|
||||
|
||||
/**
|
||||
* The type of the right operand
|
||||
*/
|
||||
type Right: FheLiteral;
|
||||
|
||||
/**
|
||||
* Process the + operation
|
||||
*/
|
||||
fn graph_cipher_const_div(
|
||||
a: CircuitNode<Cipher<Self::Left>>,
|
||||
b: Self::Right,
|
||||
) -> CircuitNode<Cipher<Self::Left>>;
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::types::{intern::{CircuitNode, FheLiteral}, Cipher, FheType};
|
||||
use crate::types::{
|
||||
intern::{CircuitNode, FheLiteral},
|
||||
Cipher, FheType,
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when a circuit encounters a * operation on two encrypted types.
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use float_cmp::ApproxEq;
|
||||
use sunscreen_compiler::{
|
||||
circuit,
|
||||
types::{bfv::Fractional, Cipher},
|
||||
Compiler, PlainModulusConstraint, Runtime,
|
||||
CircuitInput, Compiler, PlainModulusConstraint, Runtime,
|
||||
};
|
||||
|
||||
type CipherFractional = Cipher<Fractional<64>>;
|
||||
|
||||
#[test]
|
||||
fn can_add_fractional_numbers() {
|
||||
fn can_add_cipher_cipher() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn add(a: CipherFractional, b: CipherFractional) -> CipherFractional {
|
||||
a + b
|
||||
@@ -52,7 +53,177 @@ fn can_add_fractional_numbers() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_sub_fractional_numbers() {
|
||||
fn can_add_cipher_plain() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn add(a: CipherFractional, b: Fractional<64>) -> CipherFractional {
|
||||
a + b
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(add)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(500))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let do_add = |a: f64, b: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
let b_p = Fractional::<64>::try_from(b).unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into(), b_p.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
assert_eq!(c, (a + b).try_into().unwrap());
|
||||
};
|
||||
|
||||
do_add(3.14, 3.14);
|
||||
do_add(-3.14, 3.14);
|
||||
do_add(0., 0.);
|
||||
do_add(7., 3.);
|
||||
do_add(1e9, 1e9);
|
||||
do_add(1e-8, 1e-7);
|
||||
do_add(-3.14, -3.14);
|
||||
do_add(3.14, -3.14);
|
||||
do_add(-7., -3.);
|
||||
do_add(-1e9, -1e9);
|
||||
do_add(-1e-8, -1e-7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_plain_cipher() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn add(a: CipherFractional, b: Fractional<64>) -> CipherFractional {
|
||||
b + a
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(add)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(500))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let do_add = |a: f64, b: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
let b_p = Fractional::<64>::try_from(b).unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into(), b_p.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
assert_eq!(c, (a + b).try_into().unwrap());
|
||||
};
|
||||
|
||||
do_add(3.14, 3.14);
|
||||
do_add(-3.14, 3.14);
|
||||
do_add(0., 0.);
|
||||
do_add(7., 3.);
|
||||
do_add(1e9, 1e9);
|
||||
do_add(1e-8, 1e-7);
|
||||
do_add(-3.14, -3.14);
|
||||
do_add(3.14, -3.14);
|
||||
do_add(-7., -3.);
|
||||
do_add(-1e9, -1e9);
|
||||
do_add(-1e-8, -1e-7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_cipher_literal() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn add(a: CipherFractional) -> CipherFractional {
|
||||
a + 3.14
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(add)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(500))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let do_add = |a: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
// Allow up to 1 ULP of error
|
||||
assert!(c.approx_eq((a + 3.14).try_into().unwrap(), (0.0, 1)));
|
||||
};
|
||||
|
||||
do_add(3.14);
|
||||
do_add(-3.14);
|
||||
do_add(0.);
|
||||
do_add(7.);
|
||||
do_add(1e9);
|
||||
do_add(1e-8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_literal_cipher() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn add(a: CipherFractional) -> CipherFractional {
|
||||
3.14 + a
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(add)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(500))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let do_add = |a: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
// Allow up to 1 ULP of error
|
||||
assert!(c.approx_eq((a + 3.14).try_into().unwrap(), (0.0, 1)));
|
||||
};
|
||||
|
||||
do_add(3.14);
|
||||
do_add(-3.14);
|
||||
do_add(0.);
|
||||
do_add(7.);
|
||||
do_add(1e9);
|
||||
do_add(1e-8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_sub_cipher_cipher() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn sub(a: Cipher<Fractional<64>>, b: Cipher<Fractional<64>>) -> Cipher<Fractional<64>> {
|
||||
a - b
|
||||
@@ -97,7 +268,7 @@ fn can_sub_fractional_numbers() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_mul_fractional_numbers() {
|
||||
fn can_mul_cipher_cipher() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn mul(a: Cipher<Fractional<64>>, b: Cipher<Fractional<64>>) -> Cipher<Fractional<64>> {
|
||||
a * b
|
||||
@@ -140,3 +311,224 @@ fn can_mul_fractional_numbers() {
|
||||
// can do with 64-bits of precision for the integer.
|
||||
test_mul(4294967295., 4294967296.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_mul_cipher_plain() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn mul(a: Cipher<Fractional<64>>, b: Fractional<64>) -> Cipher<Fractional<64>> {
|
||||
a * b
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(mul)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(100000))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let test_mul = |a: f64, b: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
let b_p = Fractional::<64>::try_from(b).unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into(), b_p.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
assert_eq!(c, (a * b).try_into().unwrap());
|
||||
};
|
||||
|
||||
test_mul(-3.14, -3.14);
|
||||
test_mul(1234., 5678.);
|
||||
test_mul(-1234., 5678.);
|
||||
test_mul(0., -3.14);
|
||||
// Can't multiply by 0 plaintext as this will result in a transparent
|
||||
// ciphetext.
|
||||
test_mul(0., 1.);
|
||||
test_mul(1., -3.14);
|
||||
test_mul(1., 3.14);
|
||||
test_mul(1e-23, 1.234e-4);
|
||||
// 4294967296 is 2^32. This should be about the largest multiplication we
|
||||
// can do with 64-bits of precision for the integer.
|
||||
test_mul(4294967295., 4294967296.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_mul_plain_cipher() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn mul(a: Cipher<Fractional<64>>, b: Fractional<64>) -> Cipher<Fractional<64>> {
|
||||
b * a
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(mul)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(100000))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let test_mul = |a: f64, b: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
let b_p = Fractional::<64>::try_from(b).unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into(), b_p.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
assert_eq!(c, (a * b).try_into().unwrap());
|
||||
};
|
||||
|
||||
test_mul(-3.14, -3.14);
|
||||
test_mul(1234., 5678.);
|
||||
test_mul(-1234., 5678.);
|
||||
test_mul(0., -3.14);
|
||||
// Can't multiply by 0 plaintext as this will result in a transparent
|
||||
// ciphetext.
|
||||
test_mul(0., 1.);
|
||||
test_mul(1., -3.14);
|
||||
test_mul(1., 3.14);
|
||||
test_mul(1e-23, 1.234e-4);
|
||||
// 4294967296 is 2^32. This should be about the largest multiplication we
|
||||
// can do with 64-bits of precision for the integer.
|
||||
test_mul(4294967295., 4294967296.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_mul_cipher_literal() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn mul(a: Cipher<Fractional<64>>) -> Cipher<Fractional<64>> {
|
||||
a * 3.14
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(mul)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(100000))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let test_mul = |a: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
// Allow up to 1 ULP of error in computations.
|
||||
assert!(c.approx_eq((a * 3.14).try_into().unwrap(), (0.0, 1)));
|
||||
};
|
||||
|
||||
test_mul(-3.14);
|
||||
test_mul(1234.);
|
||||
test_mul(-1234.);
|
||||
test_mul(0.);
|
||||
// Can't multiply by 0 plaintext as this will result in a transparent
|
||||
// ciphetext.
|
||||
test_mul(1.);
|
||||
test_mul(1e-23);
|
||||
test_mul(4294967295.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_mul_literal_cipher() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn mul(a: Cipher<Fractional<64>>) -> Cipher<Fractional<64>> {
|
||||
3.14 * a
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(mul)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(100000))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let test_mul = |a: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
// Allow up to 1 ULP of error in computations.
|
||||
assert!(c.approx_eq((a * 3.14).try_into().unwrap(), (0.0, 1)));
|
||||
};
|
||||
|
||||
test_mul(-3.14);
|
||||
test_mul(1234.);
|
||||
test_mul(-1234.);
|
||||
test_mul(0.);
|
||||
// Can't multiply by 0 plaintext as this will result in a transparent
|
||||
// ciphetext.
|
||||
test_mul(1.);
|
||||
test_mul(1e-23);
|
||||
test_mul(4294967295.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_div_cipher_const() {
|
||||
#[circuit(scheme = "bfv")]
|
||||
fn mul(a: Cipher<Fractional<64>>) -> Cipher<Fractional<64>> {
|
||||
a / 3.14
|
||||
}
|
||||
|
||||
let circuit = Compiler::with_circuit(mul)
|
||||
.noise_margin_bits(5)
|
||||
.plain_modulus_constraint(PlainModulusConstraint::Raw(100000))
|
||||
.compile()
|
||||
.unwrap();
|
||||
|
||||
let runtime = Runtime::new(&circuit.metadata.params).unwrap();
|
||||
|
||||
let (public, secret) = runtime.generate_keys().unwrap();
|
||||
|
||||
let test_div = |a: f64| {
|
||||
let a_c = runtime
|
||||
.encrypt(Fractional::<64>::try_from(a).unwrap(), &public)
|
||||
.unwrap();
|
||||
|
||||
let args: Vec<CircuitInput> = vec![a_c.into()];
|
||||
|
||||
let result = runtime.run(&circuit, args, &public).unwrap();
|
||||
|
||||
let c: Fractional<64> = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
assert!(c.approx_eq((a / 3.14).try_into().unwrap(), (0.0, 1)));
|
||||
};
|
||||
|
||||
test_div(-3.14);
|
||||
test_div(1234.);
|
||||
test_div(-1234.);
|
||||
test_div(0.);
|
||||
test_div(1.);
|
||||
test_div(-1.);
|
||||
test_div(1e-23);
|
||||
test_div(4294967295.);
|
||||
}
|
||||
@@ -228,4 +228,4 @@ fn can_multiply_literal_cipher() {
|
||||
let c: Signed = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
assert_eq!(c, (-45).into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,4 +228,4 @@ fn can_add_multiply_literal_cipher() {
|
||||
let c: Unsigned = runtime.decrypt(&result[0], &secret).unwrap();
|
||||
|
||||
assert_eq!(c, 45.into());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user