mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-11 07:38:08 -05:00
Compare commits
29 Commits
al/fix_lon
...
0.2.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb1a95e20d | ||
|
|
1d19fcfdb9 | ||
|
|
c93bb51714 | ||
|
|
b1788cc9df | ||
|
|
6000ef39ab | ||
|
|
c087858f65 | ||
|
|
62d6852d07 | ||
|
|
85e8988f29 | ||
|
|
ac04ed0893 | ||
|
|
60385f1489 | ||
|
|
7c8926b645 | ||
|
|
e5cf51230a | ||
|
|
cdc25d6c60 | ||
|
|
ab59514b0d | ||
|
|
7c2fa7529c | ||
|
|
945ce4617f | ||
|
|
ff84e70ca9 | ||
|
|
956c4080f5 | ||
|
|
fea1b4db92 | ||
|
|
13c1fcb6e7 | ||
|
|
69b9bd3860 | ||
|
|
747693e889 | ||
|
|
e452d5d6d2 | ||
|
|
5425ba5199 | ||
|
|
aeda381f12 | ||
|
|
9b2cccfee6 | ||
|
|
b534f6a406 | ||
|
|
7a72dd2619 | ||
|
|
343f31e070 |
2
.github/workflows/aws_tfhe_integer_tests.yml
vendored
2
.github/workflows/aws_tfhe_integer_tests.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
echo "Type: ${{ github.event.inputs.instance_type }}"
|
||||
echo "Request ID: ${{ github.event.inputs.request_id }}"
|
||||
|
||||
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
|
||||
- name: Set up home
|
||||
run: |
|
||||
|
||||
2
.github/workflows/aws_tfhe_tests.yml
vendored
2
.github/workflows/aws_tfhe_tests.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
echo "Type: ${{ github.event.inputs.instance_type }}"
|
||||
echo "Request ID: ${{ github.event.inputs.request_id }}"
|
||||
|
||||
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
|
||||
- name: Set up home
|
||||
run: |
|
||||
|
||||
40
.github/workflows/boolean_benchmark.yml
vendored
40
.github/workflows/boolean_benchmark.yml
vendored
@@ -5,19 +5,19 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
instance_id:
|
||||
description: 'Instance ID'
|
||||
description: "Instance ID"
|
||||
type: string
|
||||
instance_image_id:
|
||||
description: 'Instance AMI ID'
|
||||
description: "Instance AMI ID"
|
||||
type: string
|
||||
instance_type:
|
||||
description: 'Instance product type'
|
||||
description: "Instance product type"
|
||||
type: string
|
||||
runner_name:
|
||||
description: 'Action runner name'
|
||||
description: "Action runner name"
|
||||
type: string
|
||||
request_id:
|
||||
description: 'Slab request ID'
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
|
||||
env:
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -57,9 +57,9 @@ jobs:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
|
||||
- name: Run benchmarks
|
||||
- name: Run benchmarks with AVX512
|
||||
run: |
|
||||
make bench_boolean
|
||||
make AVX512_SUPPORT=ON bench_boolean
|
||||
|
||||
- name: Parse results
|
||||
run: |
|
||||
@@ -73,24 +73,8 @@ jobs:
|
||||
--commit-date "${COMMIT_DATE}" \
|
||||
--bench-date "${{ env.BENCH_DATE }}" \
|
||||
--walk-subdirs \
|
||||
--throughput
|
||||
|
||||
- name: Remove previous raw results
|
||||
run: |
|
||||
rm -rf target/criterion
|
||||
|
||||
- name: Run benchmarks with AVX512
|
||||
run: |
|
||||
make AVX512_SUPPORT=ON bench_boolean
|
||||
|
||||
- name: Parse AVX512 results
|
||||
run: |
|
||||
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
|
||||
--hardware ${{ inputs.instance_type }} \
|
||||
--name-suffix avx512 \
|
||||
--walk-subdirs \
|
||||
--throughput \
|
||||
--append-results
|
||||
--throughput
|
||||
|
||||
- name: Measure key sizes
|
||||
run: |
|
||||
@@ -101,7 +85,7 @@ jobs:
|
||||
python3 ./ci/benchmark_parser.py tfhe/boolean_key_sizes.csv ${{ env.RESULTS_FILENAME }} \
|
||||
--key-sizes \
|
||||
--append-results
|
||||
|
||||
|
||||
- name: Upload parsed results artifact
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
|
||||
with:
|
||||
@@ -109,7 +93,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
@@ -118,7 +102,7 @@ jobs:
|
||||
- name: Send data to Slab
|
||||
shell: bash
|
||||
env:
|
||||
COMPRESSED_RESULTS : ${{ env.RESULTS_FILENAME }}.gz
|
||||
COMPRESSED_RESULTS: ${{ env.RESULTS_FILENAME }}.gz
|
||||
run: |
|
||||
echo "Computing HMac on results file"
|
||||
SIGNATURE="$(slab/scripts/hmac_calculator.sh ${{ env.RESULTS_FILENAME }} '${{ secrets.JOB_SECRET }}')"
|
||||
|
||||
2
.github/workflows/cargo_build.yml
vendored
2
.github/workflows/cargo_build.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
|
||||
- name: Run pcc checks
|
||||
run: |
|
||||
|
||||
36
.github/workflows/integer_benchmark.yml
vendored
36
.github/workflows/integer_benchmark.yml
vendored
@@ -5,19 +5,19 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
instance_id:
|
||||
description: 'Instance ID'
|
||||
description: "Instance ID"
|
||||
type: string
|
||||
instance_image_id:
|
||||
description: 'Instance AMI ID'
|
||||
description: "Instance AMI ID"
|
||||
type: string
|
||||
instance_type:
|
||||
description: 'Instance product type'
|
||||
description: "Instance product type"
|
||||
type: string
|
||||
runner_name:
|
||||
description: 'Action runner name'
|
||||
description: "Action runner name"
|
||||
type: string
|
||||
request_id:
|
||||
description: 'Slab request ID'
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
|
||||
env:
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -57,9 +57,9 @@ jobs:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
|
||||
- name: Run benchmarks
|
||||
- name: Run benchmarks with AVX512
|
||||
run: |
|
||||
make bench_integer
|
||||
make AVX512_SUPPORT=ON bench_integer
|
||||
|
||||
- name: Parse results
|
||||
run: |
|
||||
@@ -73,24 +73,8 @@ jobs:
|
||||
--commit-date "${COMMIT_DATE}" \
|
||||
--bench-date "${{ env.BENCH_DATE }}" \
|
||||
--walk-subdirs \
|
||||
--throughput
|
||||
|
||||
- name: Remove previous raw results
|
||||
run: |
|
||||
rm -rf target/criterion
|
||||
|
||||
- name: Run benchmarks with AVX512
|
||||
run: |
|
||||
make AVX512_SUPPORT=ON bench_integer
|
||||
|
||||
- name: Parse AVX512 results
|
||||
run: |
|
||||
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
|
||||
--hardware ${{ inputs.instance_type }} \
|
||||
--walk-subdirs \
|
||||
--name-suffix avx512 \
|
||||
--throughput \
|
||||
--append-results
|
||||
--throughput
|
||||
|
||||
- name: Upload parsed results artifact
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
|
||||
@@ -99,7 +83,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
2
.github/workflows/m1_tests.yml
vendored
2
.github/workflows/m1_tests.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ["self-hosted", "m1mac"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
|
||||
- name: Install latest stable
|
||||
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af
|
||||
|
||||
52
.github/workflows/make_release.yml
vendored
Normal file
52
.github/workflows/make_release.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# Publish new release of tfhe-rs on various platform.
|
||||
name: Publish release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: "Dry-run"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
publish_release:
|
||||
name: Publish Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Publish crate.io package
|
||||
env:
|
||||
CRATES_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
DRY_RUN: ${{ inputs.dry_run && '--dry-run' || '' }}
|
||||
run: |
|
||||
cargo publish -p tfhe --token ${{ env.CRATES_TOKEN }} ${{ env.DRY_RUN }}
|
||||
|
||||
- name: Build web package
|
||||
run: |
|
||||
make build_web_js_api
|
||||
|
||||
- name: Publish web package
|
||||
uses: JS-DevTools/npm-publish@541aa6b21b4a1e9990c95a92c21adc16b35e9551
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
package: tfhe/pkg/package.json
|
||||
dry-run: ${{ inputs.dry_run }}
|
||||
|
||||
- name: Build Node package
|
||||
run: |
|
||||
rm -rf tfhe/pkg
|
||||
|
||||
make build_node_js_api
|
||||
sed -i 's/"tfhe"/"node-tfhe"/g' tfhe/pkg/package.json
|
||||
|
||||
- name: Publish Node package
|
||||
uses: JS-DevTools/npm-publish@541aa6b21b4a1e9990c95a92c21adc16b35e9551
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
package: tfhe/pkg/package.json
|
||||
dry-run: ${{ inputs.dry_run }}
|
||||
15
.github/workflows/pbs_benchmark.yml
vendored
15
.github/workflows/pbs_benchmark.yml
vendored
@@ -5,22 +5,21 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
instance_id:
|
||||
description: 'Instance ID'
|
||||
description: "Instance ID"
|
||||
type: string
|
||||
instance_image_id:
|
||||
description: 'Instance AMI ID'
|
||||
description: "Instance AMI ID"
|
||||
type: string
|
||||
instance_type:
|
||||
description: 'Instance product type'
|
||||
description: "Instance product type"
|
||||
type: string
|
||||
runner_name:
|
||||
description: 'Action runner name'
|
||||
description: "Action runner name"
|
||||
type: string
|
||||
request_id:
|
||||
description: 'Slab request ID'
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RESULTS_FILENAME: parsed_benchmark_results_${{ github.sha }}.json
|
||||
@@ -43,7 +42,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -84,7 +83,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
37
.github/workflows/shortint_benchmark.yml
vendored
37
.github/workflows/shortint_benchmark.yml
vendored
@@ -5,22 +5,21 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
instance_id:
|
||||
description: 'Instance ID'
|
||||
description: "Instance ID"
|
||||
type: string
|
||||
instance_image_id:
|
||||
description: 'Instance AMI ID'
|
||||
description: "Instance AMI ID"
|
||||
type: string
|
||||
instance_type:
|
||||
description: 'Instance product type'
|
||||
description: "Instance product type"
|
||||
type: string
|
||||
runner_name:
|
||||
description: 'Action runner name'
|
||||
description: "Action runner name"
|
||||
type: string
|
||||
request_id:
|
||||
description: 'Slab request ID'
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RESULTS_FILENAME: parsed_benchmark_results_${{ github.sha }}.json
|
||||
@@ -43,7 +42,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -58,9 +57,9 @@ jobs:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
|
||||
- name: Run benchmarks
|
||||
- name: Run benchmarks with AVX512
|
||||
run: |
|
||||
make bench_shortint
|
||||
make AVX512_SUPPORT=ON bench_shortint
|
||||
|
||||
- name: Parse results
|
||||
run: |
|
||||
@@ -74,24 +73,8 @@ jobs:
|
||||
--commit-date "${COMMIT_DATE}" \
|
||||
--bench-date "${{ env.BENCH_DATE }}" \
|
||||
--walk-subdirs \
|
||||
--throughput
|
||||
|
||||
- name: Remove previous raw results
|
||||
run: |
|
||||
rm -rf target/criterion
|
||||
|
||||
- name: Run benchmarks with AVX512
|
||||
run: |
|
||||
make AVX512_SUPPORT=ON bench_shortint
|
||||
|
||||
- name: Parse AVX512 results
|
||||
run: |
|
||||
python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \
|
||||
--hardware ${{ inputs.instance_type }} \
|
||||
--walk-subdirs \
|
||||
--name-suffix avx512 \
|
||||
--throughput \
|
||||
--append-results
|
||||
--throughput
|
||||
|
||||
- name: Measure key sizes
|
||||
run: |
|
||||
@@ -110,7 +93,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
2
.github/workflows/start_benchmarks.yml
vendored
2
.github/workflows/start_benchmarks.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
2
.github/workflows/sync_on_push.yml
vendored
2
.github/workflows/sync_on_push.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Save repo
|
||||
|
||||
10
Makefile
10
Makefile
@@ -52,6 +52,12 @@ install_cargo_nextest: install_rs_build_toolchain
|
||||
cargo $(CARGO_RS_BUILD_TOOLCHAIN) install cargo-nextest --locked || \
|
||||
( echo "Unable to install cargo nextest, unknown error." && exit 1 )
|
||||
|
||||
.PHONY: install_wasm_pack # Install wasm-pack to build JS packages
|
||||
install_wasm_pack: install_rs_build_toolchain
|
||||
@wasm-pack --version > /dev/null 2>&1 || \
|
||||
cargo $(CARGO_RS_BUILD_TOOLCHAIN) install wasm-pack || \
|
||||
( echo "Unable to install cargo wasm-pack, unknown error." && exit 1 )
|
||||
|
||||
.PHONY: fmt # Format rust code
|
||||
fmt: install_rs_check_toolchain
|
||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt
|
||||
@@ -174,14 +180,14 @@ build_c_api: install_rs_check_toolchain
|
||||
-p tfhe
|
||||
|
||||
.PHONY: build_web_js_api # Build the js API targeting the web browser
|
||||
build_web_js_api: install_rs_build_toolchain
|
||||
build_web_js_api: install_rs_build_toolchain install_wasm_pack
|
||||
cd tfhe && \
|
||||
RUSTFLAGS="$(WASM_RUSTFLAGS)" rustup run "$(RS_BUILD_TOOLCHAIN)" \
|
||||
wasm-pack build --release --target=web \
|
||||
-- --features=boolean-client-js-wasm-api,shortint-client-js-wasm-api
|
||||
|
||||
.PHONY: build_node_js_api # Build the js API targeting nodejs
|
||||
build_node_js_api: install_rs_build_toolchain
|
||||
build_node_js_api: install_rs_build_toolchain install_wasm_pack
|
||||
cd tfhe && \
|
||||
RUSTFLAGS="$(WASM_RUSTFLAGS)" rustup run "$(RS_BUILD_TOOLCHAIN)" \
|
||||
wasm-pack build --release --target=nodejs \
|
||||
|
||||
@@ -58,7 +58,7 @@ tfhe = { version = "*", features = ["boolean", "shortint", "integer", "x86_64"]
|
||||
Note: aarch64-based machines are not yet supported for Windows as it's currently missing an entropy source to be able to seed the [CSPRNGs](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) used in TFHE-rs
|
||||
|
||||
Note that when running code that uses `tfhe-rs`, it is highly recommended
|
||||
to run in release mode with cargo`s `--release` flag to have the best performances possible,
|
||||
to run in release mode with cargo's `--release` flag to have the best performances possible,
|
||||
eg: `cargo run --release`.
|
||||
|
||||
Here is a full example evaluating a Boolean circuit:
|
||||
|
||||
@@ -6,7 +6,7 @@ THIS_SCRIPT_NAME="$(basename "$0")"
|
||||
|
||||
TMP_FILE="$(mktemp)"
|
||||
|
||||
COUNT="$(git grep -rniI "thfe" . | grep -v "${THIS_SCRIPT_NAME}" | \
|
||||
COUNT="$(git grep -rniI "thfe\|tfhr\|thfr" . | grep -v "${THIS_SCRIPT_NAME}" | \
|
||||
tee "${TMP_FILE}" | wc -l | tr -d '[:space:]')"
|
||||
|
||||
cat "${TMP_FILE}"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tfhe"
|
||||
version = "0.2.0"
|
||||
version = "0.2.4"
|
||||
edition = "2021"
|
||||
readme = "../README.md"
|
||||
keywords = ["fully", "homomorphic", "encryption", "fhe", "cryptography"]
|
||||
|
||||
@@ -32,6 +32,34 @@ int uint128_client_key(const ClientKey *client_key) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
int uint128_encrypt_trivial(const ClientKey *client_key) {
|
||||
int ok;
|
||||
FheUint128 *lhs = NULL;
|
||||
FheUint128 *rhs = NULL;
|
||||
FheUint128 *result = NULL;
|
||||
|
||||
ok = fhe_uint128_try_encrypt_trivial_u128(10, 20, &lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_uint128_try_encrypt_trivial_u128(1, 2, &rhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_uint128_sub(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
uint64_t w0, w1;
|
||||
ok = fhe_uint128_decrypt(result, client_key, &w0, &w1);
|
||||
assert(ok == 0);
|
||||
|
||||
assert(w0 == 9);
|
||||
assert(w1 == 18);
|
||||
|
||||
fhe_uint128_destroy(lhs);
|
||||
fhe_uint128_destroy(rhs);
|
||||
fhe_uint128_destroy(result);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int uint128_public_key(const ClientKey *client_key, const PublicKey *public_key) {
|
||||
int ok;
|
||||
FheUint128 *lhs = NULL;
|
||||
@@ -79,6 +107,7 @@ int main(void) {
|
||||
set_server_key(server_key);
|
||||
|
||||
uint128_client_key(client_key);
|
||||
uint128_encrypt_trivial(client_key);
|
||||
uint128_public_key(client_key, public_key);
|
||||
|
||||
client_key_destroy(client_key);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
int uint256_client_key(const ClientKey *client_key) {
|
||||
int ok;
|
||||
FheUint256 *lhs = NULL;
|
||||
@@ -49,6 +48,50 @@ int uint256_client_key(const ClientKey *client_key) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
int uint256_encrypt_trivial(const ClientKey *client_key) {
|
||||
int ok;
|
||||
FheUint256 *lhs = NULL;
|
||||
FheUint256 *rhs = NULL;
|
||||
FheUint256 *result = NULL;
|
||||
U256 *lhs_clear = NULL;
|
||||
U256 *rhs_clear = NULL;
|
||||
U256 *result_clear = NULL;
|
||||
|
||||
ok = u256_from_u64_words(1, 2, 3, 4, &lhs_clear);
|
||||
assert(ok == 0);
|
||||
ok = u256_from_u64_words(5, 6, 7, 8, &rhs_clear);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_uint256_try_encrypt_trivial_u256(lhs_clear, &lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_uint256_try_encrypt_trivial_u256(rhs_clear, &rhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_uint256_add(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_uint256_decrypt(result, client_key, &result_clear);
|
||||
assert(ok == 0);
|
||||
|
||||
uint64_t w0, w1, w2, w3;
|
||||
ok = u256_to_u64_words(result_clear, &w0, &w1, &w2, &w3);
|
||||
assert(ok == 0);
|
||||
|
||||
assert(w0 == 6);
|
||||
assert(w1 == 8);
|
||||
assert(w2 == 10);
|
||||
assert(w3 == 12);
|
||||
|
||||
u256_destroy(lhs_clear);
|
||||
u256_destroy(rhs_clear);
|
||||
u256_destroy(result_clear);
|
||||
fhe_uint256_destroy(lhs);
|
||||
fhe_uint256_destroy(rhs);
|
||||
fhe_uint256_destroy(result);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int uint256_public_key(const ClientKey *client_key, const PublicKey *public_key) {
|
||||
int ok;
|
||||
FheUint256 *lhs = NULL;
|
||||
@@ -112,6 +155,7 @@ int main(void) {
|
||||
set_server_key(server_key);
|
||||
|
||||
uint256_client_key(client_key);
|
||||
uint256_encrypt_trivial(client_key);
|
||||
uint256_public_key(client_key, public_key);
|
||||
|
||||
client_key_destroy(client_key);
|
||||
|
||||
@@ -67,6 +67,37 @@ int public_key_test(const ClientKey *client_key, const PublicKey *public_key) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
int trivial_encrypt_test(const ClientKey *client_key) {
|
||||
int ok;
|
||||
FheBool *lhs = NULL;
|
||||
FheBool *rhs = NULL;
|
||||
FheBool *result = NULL;
|
||||
|
||||
bool lhs_clear = 0;
|
||||
bool rhs_clear = 1;
|
||||
|
||||
ok = fhe_bool_try_encrypt_trivial_bool(lhs_clear, &lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_bool_try_encrypt_trivial_bool(rhs_clear, &rhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_bool_bitand(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
bool clear;
|
||||
ok = fhe_bool_decrypt(result, client_key, &clear);
|
||||
assert(ok == 0);
|
||||
|
||||
assert(clear == (lhs_clear & rhs_clear));
|
||||
|
||||
fhe_bool_destroy(lhs);
|
||||
fhe_bool_destroy(rhs);
|
||||
fhe_bool_destroy(result);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
@@ -88,6 +119,7 @@ int main(void)
|
||||
|
||||
client_key_test(client_key);
|
||||
public_key_test(client_key, public_key);
|
||||
trivial_encrypt_test(client_key);
|
||||
|
||||
client_key_destroy(client_key);
|
||||
public_key_destroy(public_key);
|
||||
|
||||
@@ -9,7 +9,7 @@ Welcome to this tutorial about TFHE-rs `core_crypto` module.
|
||||
To use `TFHE-rs`, first it has to be added as a dependency in the `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
tfhe = { version = "0.2.0", features = [ "x86_64-unix" ] }
|
||||
tfhe = { version = "0.2.4", features = [ "x86_64-unix" ] }
|
||||
```
|
||||
|
||||
This enables the `x86_64-unix` feature to have efficient implementations of various algorithms for `x86_64` CPUs on a Unix-like system. The 'unix' suffix indicates that the `UnixSeeder`, which uses `/dev/random` to generate random numbers, is activated as a fallback if no hardware number generator is available, like `rdseed` on `x86_64` or if the [`Randomization Services`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc) on Apple platforms are not available. To avoid having the `UnixSeeder` as a potential fallback or to run on non-Unix systems (e.g., Windows), the `x86_64` feature is sufficient.
|
||||
@@ -19,19 +19,19 @@ For Apple Silicon, the `aarch64-unix` or `aarch64` feature should be enabled. `a
|
||||
In short: For x86\_64-based machines running Unix-like OSes:
|
||||
|
||||
```toml
|
||||
tfhe = { version = "0.2.0", features = ["x86_64-unix"] }
|
||||
tfhe = { version = "0.2.4", features = ["x86_64-unix"] }
|
||||
```
|
||||
|
||||
For Apple Silicon or aarch64-based machines running Unix-like OSes:
|
||||
|
||||
```toml
|
||||
tfhe = { version = "0.2.0", features = ["aarch64-unix"] }
|
||||
tfhe = { version = "0.2.4", features = ["aarch64-unix"] }
|
||||
```
|
||||
|
||||
For x86\_64-based machines with the [`rdseed instruction`](https://en.wikipedia.org/wiki/RDRAND) running Windows:
|
||||
|
||||
```toml
|
||||
tfhe = { version = "0.2.0", features = ["x86_64"] }
|
||||
tfhe = { version = "0.2.4", features = ["x86_64"] }
|
||||
```
|
||||
|
||||
### Commented code to double a 2-bits message in a leveled fashion and using a PBS with the `core_crypto` module.
|
||||
|
||||
@@ -59,10 +59,10 @@ To ensure predictable timings, the operation flavor is the `default` one: a carr
|
||||
|
||||
| Plaintext size | add | mul | greater\_than (gt) | min |
|
||||
| -------------------| -------------- | ------------------- | --------- | ------- |
|
||||
| 8 bits | 129.0 ms | 178.2 ms | 111.9 ms | 287.7 ms |
|
||||
| 16 bits | 256.3 ms | 328.0 ms | 145.3 ms | 437.4 ms |
|
||||
| 32 bits | 469.4 ms | 645.5 ms | 192.0 ms | 776.4 ms |
|
||||
| 40 bits | 608.0 ms | 849.3 ms | 228.4 ms | 953.5 ms |
|
||||
| 64 bits | 959.9 ms | 1.49 s | 249.0 ms | 1.36 s |
|
||||
| 128 bits | 1.88 s | 3.25 s | 294.7 ms | 2.37 s |
|
||||
| 256 bits | 3.66 s | 8.38 s | 361.8 ms | 4.51 s |
|
||||
| 8 bits | 129.0 ms | 227.2 ms | 111.9 ms | 186.8 ms |
|
||||
| 16 bits | 256.3 ms | 756.0 ms | 145.3 ms | 233.1 ms |
|
||||
| 32 bits | 469.4 ms | 2.10 s | 192.0 ms | 282.9 ms |
|
||||
| 40 bits | 608.0 ms | 3.37 s | 228.4 ms | 318.6 ms |
|
||||
| 64 bits | 959.9 ms | 5.53 s | 249.0 ms | 336.5 ms |
|
||||
| 128 bits | 1.88 s | 14.1 s | 294.7 ms | 398.6 ms |
|
||||
| 256 bits | 3.66 s | 29.2 s | 361.8 ms | 509.1 ms |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
To use `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
tfhe = { version = "0.2.0", features = [ "boolean", "shortint", "integer", "x86_64-unix" ] }
|
||||
tfhe = { version = "0.2.4", features = [ "boolean", "shortint", "integer", "x86_64-unix" ] }
|
||||
```
|
||||
|
||||
{% hint style="info" %}
|
||||
|
||||
@@ -11,7 +11,7 @@ To serialize our data, a [data format](https://serde.rs/#data-formats) should be
|
||||
|
||||
[dependencies]
|
||||
# ...
|
||||
tfhe = { version = "0.2.0", features = ["integer"]}
|
||||
tfhe = { version = "0.2.4", features = ["integer","x86_64-unix"]}
|
||||
bincode = "1.3.3"
|
||||
```
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ The basic steps for using the high-level API of TFHE-rs are:
|
||||
3. Client-side: Encrypting data;
|
||||
4. Server-side: Setting the server key;
|
||||
5. Server-side: Computing over encrypted data;
|
||||
6. Client-side: Encrypting data.
|
||||
6. Client-side: Decrypting data.
|
||||
|
||||
Here is the full example (mixing client and server parts):
|
||||
|
||||
@@ -44,10 +44,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Default configuration for x86 Unix machines:
|
||||
```toml
|
||||
tfhe = { version = "0.2.0", features = ["integer"]}
|
||||
tfhe = { version = "0.2.4", features = ["integer", "x86_64-unix"]}
|
||||
```
|
||||
|
||||
Other configurations can be found [here](../getting_started/installation.md).
|
||||
|
||||
### Imports.
|
||||
|
||||
`tfhe` uses `traits` to have a consistent API for creating FHE types and enable users to write generic functions. To be able to use associated functions and methods of a trait, the trait has to be in scope.
|
||||
@@ -64,13 +67,8 @@ The first step is the creation of the configuration. The configuration is used t
|
||||
|
||||
Creating a configuration is done using the ConfigBuilder type.
|
||||
|
||||
In this example, 8-bit unsigned integers with default parameters are used. The `integers` feature must also be enabled, as per the table on the Getting Started page.
|
||||
|
||||
{% hint style="info" %}
|
||||
```toml
|
||||
tfhe = { version = "0.2.0", features = ["integer"]}
|
||||
```
|
||||
{% endhint %}
|
||||
In this example, 8-bit unsigned integers with default parameters are used. The `integers`
|
||||
feature must also be enabled, as per the table on the [Getting Started page](../getting_started/installation.md).
|
||||
|
||||
The config is done by first creating a builder with all types deactivated. Then, the `uint8` type with default parameters is activated.
|
||||
|
||||
@@ -191,10 +189,13 @@ To use the `FheUint8` type, the `integer` feature must be activated:
|
||||
# Cargo.toml
|
||||
|
||||
[dependencies]
|
||||
# ...
|
||||
tfhe = { version = "0.2.0", features = ["integer"]}
|
||||
# Default configuration for x86 Unix machines:
|
||||
tfhe = { version = "0.2.4", features = ["integer", "x86_64-unix"]}
|
||||
```
|
||||
|
||||
Other configurations can be found [here](../getting_started/installation.md).
|
||||
|
||||
|
||||
```rust
|
||||
use tfhe::{FheUint8, ConfigBuilder, generate_keys, set_server_key, ClientKey};
|
||||
use tfhe::prelude::*;
|
||||
@@ -317,11 +318,13 @@ To use Booleans, the `booleans` feature in our Cargo.toml must be enabled:
|
||||
```toml
|
||||
# Cargo.toml
|
||||
|
||||
[dependencies]
|
||||
# ...
|
||||
tfhe = { version = "0.2.0", features = ["booleans"]}
|
||||
# Default configuration for x86 Unix machines:
|
||||
tfhe = { version = "0.2.4", features = ["boolean", "x86_64-unix"]}
|
||||
```
|
||||
|
||||
Other configurations can be found [here](../getting_started/installation.md).
|
||||
|
||||
|
||||
#### function definition
|
||||
|
||||
First, the verification function is defined.
|
||||
@@ -461,7 +464,7 @@ To make the `compute_parity_bit` function compatible with both `FheBool` and `bo
|
||||
|
||||
Writing a generic function that accepts `FHE` types as well as clear types can help test the function to see if it is correct. If the function is generic, it can run with clear data, allowing the use of print-debugging or a debugger to spot errors.
|
||||
|
||||
Writing generic functions that use operator overloading for our FHE types can be trickier than normal, since, as explained in [Generic Bounds How To](../how\_to/generic\_bounds.md), `FHE` types are not copy. So using the reference `&` is mandatory, even though this is not the case when using native types, which are all `Copy`.
|
||||
Writing generic functions that use operator overloading for our FHE types can be trickier than normal, since `FHE` types are not copy. So using the reference `&` is mandatory, even though this is not the case when using native types, which are all `Copy`.
|
||||
|
||||
This will make the generic bounds trickier at first.
|
||||
|
||||
@@ -549,7 +552,7 @@ help: consider adding an explicit lifetime bound...
|
||||
|
|
||||
```
|
||||
|
||||
The way to fix this is to use `Higher-Rank Trait Bounds`, as shown in the [Generic Bounds How To](../how\_to/generic\_bounds.md):
|
||||
The way to fix this is to use `Higher-Rank Trait Bounds`:
|
||||
|
||||
```Rust
|
||||
where
|
||||
|
||||
@@ -18,6 +18,9 @@ use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, Seeder
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::seeders::new_seeder;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub(crate) trait BinaryGatesEngine<L, R, K> {
|
||||
fn and(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
|
||||
fn nand(&mut self, ct_left: L, ct_right: R, server_key: &K) -> Ciphertext;
|
||||
@@ -257,6 +260,37 @@ impl Default for BooleanEngine {
|
||||
}
|
||||
|
||||
impl BooleanEngine {
|
||||
/// Replace the thread_local BooleanEngine
|
||||
///
|
||||
/// `new_engine` will replace the already_existing
|
||||
/// `thread_local` engine.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::boolean::engine::BooleanEngine;
|
||||
/// use tfhe::core_crypto::commons::generators::DeterministicSeeder;
|
||||
/// use tfhe::core_crypto::commons::math::random::Seed;
|
||||
/// use tfhe::core_crypto::prelude::ActivatedRandomGenerator;
|
||||
///
|
||||
/// // WARNING: Using a deterministic seed is not recommended
|
||||
/// // as it renders the random generation insecure
|
||||
///
|
||||
/// let deterministic_seed = Seed(0);
|
||||
///
|
||||
/// let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(deterministic_seed);
|
||||
/// let boolean_engine = BooleanEngine::new_from_seeder(&mut seeder);
|
||||
/// BooleanEngine::replace_thread_local(boolean_engine);
|
||||
///
|
||||
/// // This uses the engine create earlier
|
||||
/// let (cks, sks) = tfhe::boolean::gen_keys();
|
||||
/// ```
|
||||
pub fn replace_thread_local(new_engine: Self) {
|
||||
Self::with_thread_local_mut(|local_engine| {
|
||||
let _ = std::mem::replace(local_engine, new_engine);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut root_seeder = new_seeder();
|
||||
|
||||
|
||||
54
tfhe/src/boolean/engine/tests.rs
Normal file
54
tfhe/src/boolean/engine/tests.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
#[test]
|
||||
fn test_replacing_thread_local_engine() {
|
||||
use crate::boolean::engine::BooleanEngine;
|
||||
use crate::core_crypto::commons::generators::DeterministicSeeder;
|
||||
use crate::core_crypto::commons::math::random::Seed;
|
||||
use crate::core_crypto::prelude::ActivatedRandomGenerator;
|
||||
|
||||
let deterministic_seed = Seed(0);
|
||||
|
||||
// We change the engine in the main thread
|
||||
// then generate a client key, and then encrypt
|
||||
// a boolean value and serialize it to compare
|
||||
// it with other ciphertext
|
||||
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(deterministic_seed);
|
||||
let boolean_engine = BooleanEngine::new_from_seeder(&mut seeder);
|
||||
BooleanEngine::replace_thread_local(boolean_engine);
|
||||
|
||||
let (cks, _) = crate::boolean::gen_keys();
|
||||
let ct = cks.encrypt(false);
|
||||
let main_thread_data = bincode::serialize(&ct).unwrap();
|
||||
|
||||
// In this thread, we don't change the engine
|
||||
// and so we expect the encrypted value to be
|
||||
// different compared with the one from the main thread
|
||||
//
|
||||
// This also "proves" that a thread is not affected
|
||||
// by engine changes from other thread as engines are
|
||||
// thread_local
|
||||
let second_thread_data = std::thread::spawn(|| {
|
||||
let (cks, _) = crate::boolean::gen_keys();
|
||||
let ct = cks.encrypt(false);
|
||||
bincode::serialize(&ct).unwrap()
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
assert_ne!(second_thread_data, main_thread_data);
|
||||
|
||||
// In this thread, we change the engine,
|
||||
// with a new engine that has the same seed
|
||||
// as the one in the main thread
|
||||
// So we expect the encrypted value to be the same
|
||||
// compared with the one from the main thread
|
||||
let third_thread_data = std::thread::spawn(move || {
|
||||
let mut seeder = DeterministicSeeder::<ActivatedRandomGenerator>::new(deterministic_seed);
|
||||
let boolean_engine = BooleanEngine::new_from_seeder(&mut seeder);
|
||||
BooleanEngine::replace_thread_local(boolean_engine);
|
||||
let (cks, _) = crate::boolean::gen_keys();
|
||||
let ct = cks.encrypt(false);
|
||||
bincode::serialize(&ct).unwrap()
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
assert_eq!(third_thread_data, main_thread_data);
|
||||
}
|
||||
@@ -11,6 +11,7 @@ impl_binary_fn_on_type!(FheBool => bitand, bitor, bitxor);
|
||||
impl_unary_fn_on_type!(FheBool => not);
|
||||
|
||||
impl_decrypt_on_type!(FheBool, bool);
|
||||
impl_try_encrypt_trivial_on_type!(FheBool{crate::high_level_api::FheBool}, bool);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheBool{crate::high_level_api::FheBool}, bool);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheBool{crate::high_level_api::FheBool}, bool);
|
||||
|
||||
|
||||
@@ -81,40 +81,62 @@ create_integer_wrapper_type!(name: FheUint128, clear_scalar_type: u64);
|
||||
create_integer_wrapper_type!(name: FheUint256, clear_scalar_type: u64);
|
||||
|
||||
impl_decrypt_on_type!(FheUint8, u8);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint8{crate::high_level_api::CompressedFheUint8}, u8);
|
||||
|
||||
impl_decrypt_on_type!(FheUint10, u16);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint10{crate::high_level_api::CompressedFheUint10}, u16);
|
||||
|
||||
impl_decrypt_on_type!(FheUint12, u16);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint12{crate::high_level_api::CompressedFheUint12}, u16);
|
||||
|
||||
impl_decrypt_on_type!(FheUint14, u16);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint14{crate::high_level_api::CompressedFheUint14}, u16);
|
||||
|
||||
impl_decrypt_on_type!(FheUint16, u16);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint16{crate::high_level_api::CompressedFheUint16}, u16);
|
||||
|
||||
impl_decrypt_on_type!(FheUint32, u32);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint32{crate::high_level_api::CompressedFheUint32}, u32);
|
||||
|
||||
impl_decrypt_on_type!(FheUint64, u64);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint64{crate::high_level_api::CompressedFheUint64}, u64);
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint128_try_encrypt_trivial_u128(
|
||||
low_word: u64,
|
||||
high_word: u64,
|
||||
result: *mut *mut FheUint128,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let value = ((high_word as u128) << 64u128) | low_word as u128;
|
||||
|
||||
let inner = <crate::high_level_api::FheUint128>::try_encrypt_trivial(value).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint128(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_client_key_u128(
|
||||
low_word: u64,
|
||||
@@ -189,6 +211,18 @@ pub unsafe extern "C" fn fhe_uint128_decrypt(
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint256_try_encrypt_trivial_u256(
|
||||
value: *const U256,
|
||||
result: *mut *mut FheUint256,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let inner = <crate::high_level_api::FheUint256>::try_encrypt_trivial((*value).0).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint256(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_client_key_u256(
|
||||
value: *const U256,
|
||||
|
||||
@@ -59,6 +59,23 @@ macro_rules! impl_try_encrypt_with_public_key_on_type {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_try_encrypt_trivial_on_type {
|
||||
($wrapper_type:ty{$wrapped_type:ty}, $input_type:ty) => {
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<$wrapper_type:snake _try_encrypt_trivial_ $input_type:snake>](
|
||||
value: $input_type,
|
||||
result: *mut *mut $wrapper_type,
|
||||
) -> ::std::os::raw::c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let inner = <$wrapped_type>::try_encrypt_trivial(value).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new($wrapper_type(inner)));
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! impl_decrypt_on_type {
|
||||
($wrapper_type:ty, $output_type:ty) => {
|
||||
::paste::paste! {
|
||||
|
||||
@@ -25,7 +25,8 @@ use dyn_stack::{PodStack, SizeOverflow, StackReq};
|
||||
|
||||
/// Perform a blind rotation given an input [`LWE ciphertext`](`LweCiphertext`), modifying a look-up
|
||||
/// table passed as a [`GLWE ciphertext`](`GlweCiphertext`) and an [`LWE bootstrap
|
||||
/// key`](`LweBootstrapKey`) in the fourier domain.
|
||||
/// key`](`LweBootstrapKey`) in the fourier domain see [`fourier LWE bootstrap
|
||||
/// key`](`FourierLweBootstrapKey`).
|
||||
///
|
||||
/// If you want to manage the computation memory manually you can use
|
||||
/// [`blind_rotate_assign_mem_optimized`].
|
||||
@@ -811,7 +812,8 @@ pub fn cmux_assign_mem_optimized_requirement<Scalar>(
|
||||
|
||||
/// Perform a programmable bootstrap given an input [`LWE ciphertext`](`LweCiphertext`), a
|
||||
/// look-up table passed as a [`GLWE ciphertext`](`GlweCiphertext`) and an [`LWE bootstrap
|
||||
/// key`](`LweBootstrapKey`) in the fourier domain. The result is written in the provided output
|
||||
/// key`](`LweBootstrapKey`) in the fourier domain see [`fourier LWE bootstrap
|
||||
/// key`](`FourierLweBootstrapKey`). The result is written in the provided output
|
||||
/// [`LWE ciphertext`](`LweCiphertext`).
|
||||
///
|
||||
/// If you want to manage the computation memory manually you can use
|
||||
@@ -1119,11 +1121,12 @@ pub fn programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement<Scalar>(
|
||||
|
||||
/// Perform a programmable bootstrap given an input [`LWE ciphertext`](`LweCiphertext`), a
|
||||
/// look-up table passed as a [`GLWE ciphertext`](`GlweCiphertext`) and an [`LWE bootstrap
|
||||
/// key`](`LweBootstrapKey`) in the fourier domain using f128. The result is written in the provided
|
||||
/// key`](`LweBootstrapKey`) in the fourier domain using f128 see [`fourier LWE bootstrap
|
||||
/// key`](`Fourier128LweBootstrapKey`). The result is written in the provided
|
||||
/// output [`LWE ciphertext`](`LweCiphertext`).
|
||||
///
|
||||
/// If you want to manage the computation memory manually you can use
|
||||
/// [`programmable_bootstrap_lwe_ciphertext_mem_optimized`].
|
||||
/// [`programmable_bootstrap_f128_lwe_ciphertext_mem_optimized`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
||||
@@ -229,6 +229,10 @@ impl<G: ByteRandomGenerator> EncryptionRandomGenerator<G> {
|
||||
where
|
||||
Scalar: UnsignedInteger + RandomGenerable<Gaussian<f64>, CustomModulus = f64>,
|
||||
{
|
||||
if custom_modulus.is_native_modulus() {
|
||||
return self.random_noise(std);
|
||||
}
|
||||
|
||||
let custom_modulus_f64: f64 = custom_modulus.get().cast_into();
|
||||
Scalar::generate_one_custom_modulus(
|
||||
&mut self.noise,
|
||||
@@ -552,7 +556,7 @@ fn noise_bytes_per_pfpksk(
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::dispersion::Variance;
|
||||
use crate::core_crypto::commons::dispersion::{StandardDev, Variance};
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
CiphertextModulus, DecompositionBaseLog, DecompositionLevelCount, GlweSize, LweDimension,
|
||||
PolynomialSize,
|
||||
@@ -560,6 +564,7 @@ mod test {
|
||||
use crate::core_crypto::commons::test_tools::{
|
||||
new_encryption_random_generator, new_secret_random_generator,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::UnsignedTorus;
|
||||
|
||||
#[test]
|
||||
fn test_gaussian_sampling_margin_factor_does_not_panic() {
|
||||
@@ -598,4 +603,264 @@ mod test {
|
||||
&mut enc_generator,
|
||||
);
|
||||
}
|
||||
|
||||
fn noise_gen_native<Scalar: UnsignedTorus>() {
|
||||
let mut gen = new_encryption_random_generator();
|
||||
|
||||
let bits = (Scalar::BITS / 2) as i32;
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut retries = 100;
|
||||
|
||||
let mut val = Scalar::ZERO;
|
||||
while retries >= 0 {
|
||||
val = gen.random_noise(StandardDev(2.0f64.powi(-bits)));
|
||||
if val != Scalar::ZERO {
|
||||
break;
|
||||
}
|
||||
retries -= 1;
|
||||
}
|
||||
|
||||
assert!(retries != 0);
|
||||
assert!(val != Scalar::ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_native_u32() {
|
||||
noise_gen_native::<u32>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_native_u64() {
|
||||
noise_gen_native::<u64>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_native_u128() {
|
||||
noise_gen_native::<u128>();
|
||||
}
|
||||
|
||||
fn noise_gen_custom_mod<Scalar: UnsignedTorus>(ciphertext_modulus: CiphertextModulus<Scalar>) {
|
||||
let mut gen = new_encryption_random_generator();
|
||||
|
||||
let bits = (Scalar::BITS / 2) as i32;
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut retries = 100;
|
||||
|
||||
let mut val = Scalar::ZERO;
|
||||
while retries >= 0 {
|
||||
val = gen
|
||||
.random_noise_custom_mod(StandardDev(2.0f64.powi(-bits)), ciphertext_modulus);
|
||||
if val != Scalar::ZERO {
|
||||
break;
|
||||
}
|
||||
retries -= 1;
|
||||
}
|
||||
|
||||
assert!(retries != 0);
|
||||
assert!(val != Scalar::ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_custom_mod_u32() {
|
||||
noise_gen_custom_mod::<u32>(CiphertextModulus::try_new_power_of_2(31).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_custom_mod_u64() {
|
||||
noise_gen_custom_mod::<u64>(CiphertextModulus::try_new_power_of_2(63).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_custom_mod_u128() {
|
||||
noise_gen_custom_mod::<u128>(CiphertextModulus::try_new_power_of_2(127).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_native_custom_mod_u32() {
|
||||
noise_gen_custom_mod::<u32>(CiphertextModulus::new_native());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_native_custom_mod_u64() {
|
||||
noise_gen_custom_mod::<u64>(CiphertextModulus::new_native());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_native_custom_mod_u128() {
|
||||
noise_gen_custom_mod::<u128>(CiphertextModulus::new_native());
|
||||
}
|
||||
|
||||
fn noise_gen_slice_native<Scalar: UnsignedTorus>() {
|
||||
let mut gen = new_encryption_random_generator();
|
||||
|
||||
let bits = (Scalar::BITS / 2) as i32;
|
||||
|
||||
let mut vec = vec![Scalar::ZERO; 1000];
|
||||
let mut retries = 100;
|
||||
while retries >= 0 {
|
||||
gen.fill_slice_with_random_noise(&mut vec, StandardDev(2.0f64.powi(-bits)));
|
||||
if vec.iter().all(|&x| x != Scalar::ZERO) {
|
||||
break;
|
||||
}
|
||||
|
||||
retries -= 1;
|
||||
}
|
||||
assert!(retries != 0);
|
||||
assert!(vec.iter().all(|&x| x != Scalar::ZERO));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_native_u32() {
|
||||
noise_gen_slice_native::<u32>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_native_u64() {
|
||||
noise_gen_slice_native::<u64>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_native_u128() {
|
||||
noise_gen_slice_native::<u128>();
|
||||
}
|
||||
|
||||
fn noise_gen_slice_custom_mod<Scalar: UnsignedTorus>(
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) {
|
||||
let mut gen = new_encryption_random_generator();
|
||||
|
||||
let bits = (Scalar::BITS / 2) as i32;
|
||||
|
||||
let mut vec = vec![Scalar::ZERO; 1000];
|
||||
let mut retries = 100;
|
||||
while retries >= 0 {
|
||||
gen.fill_slice_with_random_noise_custom_mod(
|
||||
&mut vec,
|
||||
StandardDev(2.0f64.powi(-bits)),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
if vec.iter().all(|&x| x != Scalar::ZERO) {
|
||||
break;
|
||||
}
|
||||
|
||||
retries -= 1;
|
||||
}
|
||||
assert!(retries != 0);
|
||||
assert!(vec.iter().all(|&x| x != Scalar::ZERO));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_custom_mod_u32() {
|
||||
noise_gen_slice_custom_mod::<u32>(CiphertextModulus::try_new_power_of_2(31).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_custom_mod_u64() {
|
||||
noise_gen_slice_custom_mod::<u64>(CiphertextModulus::try_new_power_of_2(63).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_custom_mod_u128() {
|
||||
noise_gen_slice_custom_mod::<u128>(CiphertextModulus::try_new_power_of_2(127).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_native_custom_mod_u32() {
|
||||
noise_gen_slice_custom_mod::<u32>(CiphertextModulus::new_native());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_native_custom_mod_u64() {
|
||||
noise_gen_slice_custom_mod::<u64>(CiphertextModulus::new_native());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noise_gen_slice_native_custom_mod_u128() {
|
||||
noise_gen_slice_custom_mod::<u128>(CiphertextModulus::new_native());
|
||||
}
|
||||
|
||||
fn mask_gen_slice_native<Scalar: UnsignedTorus>() {
|
||||
let mut gen = new_encryption_random_generator();
|
||||
|
||||
let mut vec = vec![Scalar::ZERO; 1000];
|
||||
let mut retries = 100;
|
||||
while retries >= 0 {
|
||||
gen.fill_slice_with_random_mask(&mut vec);
|
||||
if vec.iter().all(|&x| x != Scalar::ZERO) {
|
||||
break;
|
||||
}
|
||||
|
||||
retries -= 1;
|
||||
}
|
||||
assert!(retries != 0);
|
||||
assert!(vec.iter().all(|&x| x != Scalar::ZERO));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_native_u32() {
|
||||
mask_gen_slice_native::<u32>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_native_u64() {
|
||||
mask_gen_slice_native::<u64>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_native_u128() {
|
||||
mask_gen_slice_native::<u128>();
|
||||
}
|
||||
|
||||
fn mask_gen_slice_custom_mod<Scalar: UnsignedTorus>(
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) {
|
||||
let mut gen = new_encryption_random_generator();
|
||||
|
||||
let mut vec = vec![Scalar::ZERO; 1000];
|
||||
let mut retries = 100;
|
||||
while retries >= 0 {
|
||||
gen.fill_slice_with_random_mask_custom_mod(&mut vec, ciphertext_modulus);
|
||||
if vec.iter().all(|&x| x != Scalar::ZERO) {
|
||||
break;
|
||||
}
|
||||
|
||||
retries -= 1;
|
||||
}
|
||||
assert!(retries != 0);
|
||||
assert!(vec.iter().all(|&x| x != Scalar::ZERO));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_slice_custom_mod_u32() {
|
||||
mask_gen_slice_custom_mod::<u32>(CiphertextModulus::try_new_power_of_2(31).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_slice_custom_mod_u64() {
|
||||
mask_gen_slice_custom_mod::<u64>(CiphertextModulus::try_new_power_of_2(63).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_slice_custom_mod_u128() {
|
||||
mask_gen_slice_custom_mod::<u128>(CiphertextModulus::try_new_power_of_2(127).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_slice_native_custom_mod_u32() {
|
||||
mask_gen_slice_custom_mod::<u32>(CiphertextModulus::new_native());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_slice_native_custom_mod_u64() {
|
||||
mask_gen_slice_custom_mod::<u64>(CiphertextModulus::new_native());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mask_gen_slice_native_custom_mod_u128() {
|
||||
mask_gen_slice_custom_mod::<u128>(CiphertextModulus::new_native());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,8 +175,9 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
/// use concrete_csprng::seeders::Seed;
|
||||
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
|
||||
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
|
||||
/// let mut vec = vec![1u32; 100];
|
||||
/// let mut vec = vec![0u32; 1000];
|
||||
/// generator.fill_slice_with_random_uniform(&mut vec);
|
||||
/// assert!(vec.iter().any(|&x| x != 0));
|
||||
/// ```
|
||||
pub fn fill_slice_with_random_uniform<Scalar>(&mut self, output: &mut [Scalar])
|
||||
where
|
||||
@@ -196,11 +197,12 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
|
||||
/// use tfhe::core_crypto::commons::parameters::CiphertextModulus;
|
||||
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
|
||||
/// let mut vec = vec![1u32; 100];
|
||||
/// let mut vec = vec![0u32; 1000];
|
||||
/// generator.fill_slice_with_random_uniform_custom_mod(
|
||||
/// &mut vec,
|
||||
/// CiphertextModulus::try_new_power_of_2(31).unwrap(),
|
||||
/// );
|
||||
/// assert!(vec.iter().any(|&x| x != 0));
|
||||
/// ```
|
||||
pub fn fill_slice_with_random_uniform_custom_mod<Scalar>(
|
||||
&mut self,
|
||||
@@ -243,8 +245,9 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
/// use concrete_csprng::seeders::Seed;
|
||||
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
|
||||
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
|
||||
/// let mut vec = vec![2u32; 100];
|
||||
/// let mut vec = vec![0u32; 1000];
|
||||
/// generator.fill_slice_with_random_uniform_binary(&mut vec);
|
||||
/// assert!(vec.iter().any(|&x| x != 0));
|
||||
/// ```
|
||||
pub fn fill_slice_with_random_uniform_binary<Scalar>(&mut self, output: &mut [Scalar])
|
||||
where
|
||||
@@ -371,8 +374,9 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
/// use concrete_csprng::seeders::Seed;
|
||||
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
|
||||
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
|
||||
/// let mut vec = vec![1000f32; 100];
|
||||
/// let mut vec = vec![0f32; 1000];
|
||||
/// generator.fill_slice_with_random_gaussian(&mut vec, 0., 1.);
|
||||
/// assert!(vec.iter().any(|&x| x != 0.));
|
||||
/// ```
|
||||
pub fn fill_slice_with_random_gaussian<Float, Scalar>(
|
||||
&mut self,
|
||||
@@ -404,13 +408,14 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
|
||||
/// use tfhe::core_crypto::commons::parameters::CiphertextModulus;
|
||||
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
|
||||
/// let mut vec = vec![1000u64; 100];
|
||||
/// let mut vec = vec![0u64; 1000];
|
||||
/// generator.fill_slice_with_random_gaussian_custom_mod(
|
||||
/// &mut vec,
|
||||
/// 0.,
|
||||
/// 1.,
|
||||
/// CiphertextModulus::try_new_power_of_2(63).unwrap(),
|
||||
/// );
|
||||
/// assert!(vec.iter().any(|&x| x != 0));
|
||||
/// ```
|
||||
pub fn fill_slice_with_random_gaussian_custom_mod<Float, Scalar>(
|
||||
&mut self,
|
||||
@@ -423,6 +428,11 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
Scalar: UnsignedInteger,
|
||||
(Scalar, Scalar): RandomGenerable<Gaussian<Float>, CustomModulus = Float>,
|
||||
{
|
||||
if custom_modulus.is_native_modulus() {
|
||||
self.fill_slice_with_random_gaussian(output, mean, std);
|
||||
return;
|
||||
}
|
||||
|
||||
let custom_modulus_float: Float = custom_modulus.get().cast_into();
|
||||
output.chunks_mut(2).for_each(|s| {
|
||||
let (g1, g2) = <(Scalar, Scalar)>::generate_one_custom_modulus(
|
||||
@@ -448,8 +458,9 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
/// use concrete_csprng::seeders::Seed;
|
||||
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
|
||||
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
|
||||
/// let mut vec = vec![1000u32; 100];
|
||||
/// let mut vec = vec![0u32; 1000];
|
||||
/// generator.unsigned_torus_slice_wrapping_add_random_gaussian_assign(&mut vec, 0., 1.);
|
||||
/// assert!(vec.iter().any(|&x| x != 0));
|
||||
/// ```
|
||||
pub fn unsigned_torus_slice_wrapping_add_random_gaussian_assign<Float, Scalar>(
|
||||
&mut self,
|
||||
@@ -479,9 +490,16 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
/// use concrete_csprng::generators::SoftwareRandomGenerator;
|
||||
/// use concrete_csprng::seeders::Seed;
|
||||
/// use tfhe::core_crypto::commons::math::random::RandomGenerator;
|
||||
/// use tfhe::core_crypto::commons::parameters::CiphertextModulus;
|
||||
/// let mut generator = RandomGenerator::<SoftwareRandomGenerator>::new(Seed(0));
|
||||
/// let mut vec = vec![1000u32; 100];
|
||||
/// generator.unsigned_torus_slice_wrapping_add_random_gaussian_assign(&mut vec, 0., 1.);
|
||||
/// let mut vec = vec![0u32; 1000];
|
||||
/// generator.unsigned_torus_slice_wrapping_add_random_gaussian_custom_mod_assign(
|
||||
/// &mut vec,
|
||||
/// 0.,
|
||||
/// 1.,
|
||||
/// CiphertextModulus::try_new_power_of_2(31).unwrap(),
|
||||
/// );
|
||||
/// assert!(vec.iter().any(|&x| x != 0));
|
||||
/// ```
|
||||
pub fn unsigned_torus_slice_wrapping_add_random_gaussian_custom_mod_assign<Float, Scalar>(
|
||||
&mut self,
|
||||
@@ -494,6 +512,11 @@ impl<G: ByteRandomGenerator> RandomGenerator<G> {
|
||||
Float: FloatingPoint + CastFrom<u128>,
|
||||
(Scalar, Scalar): RandomGenerable<Gaussian<Float>, CustomModulus = Float>,
|
||||
{
|
||||
if custom_modulus.is_native_modulus() {
|
||||
self.unsigned_torus_slice_wrapping_add_random_gaussian_assign(output, mean, std);
|
||||
return;
|
||||
}
|
||||
|
||||
let custom_modulus_float: Float = custom_modulus.get().cast_into();
|
||||
output.chunks_mut(2).for_each(|s| {
|
||||
let (g1, g2) = <(Scalar, Scalar)>::generate_one_custom_modulus(
|
||||
|
||||
@@ -124,7 +124,7 @@ fn f128_floor(x: f128) -> f128 {
|
||||
let f128(x0, x1) = x;
|
||||
let x0_floor = x0.floor();
|
||||
if x0_floor == x0 {
|
||||
f128(x0_floor, x1.floor())
|
||||
f128::add_f64_f64(x0_floor, x1.floor())
|
||||
} else {
|
||||
f128(x0_floor, 0.0)
|
||||
}
|
||||
@@ -225,7 +225,7 @@ fn u128_to_signed_to_f128(x: u128) -> f128 {
|
||||
|
||||
#[inline(always)]
|
||||
fn u128_from_torus_f128(x: f128) -> u128 {
|
||||
let mut x = x - f128_floor(x);
|
||||
let mut x = f128::sub_estimate_f128_f128(x, f128_floor(x));
|
||||
|
||||
let normalization = 2.0f64.powi(128);
|
||||
x.0 *= normalization;
|
||||
|
||||
@@ -166,7 +166,7 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn bootstrap_u128<ContLweOut, ContLweIn, ContAcc>(
|
||||
pub fn bootstrap_u128<ContLweOut, ContLweIn, ContAcc>(
|
||||
&self,
|
||||
lwe_out: &mut LweCiphertext<ContLweOut>,
|
||||
lwe_in: &LweCiphertext<ContLweIn>,
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
pub mod bootstrap;
|
||||
pub mod ggsw;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
279
tfhe/src/core_crypto/fft_impl/fft128_u128/crypto/tests.rs
Normal file
279
tfhe/src/core_crypto/fft_impl/fft128_u128/crypto/tests.rs
Normal file
@@ -0,0 +1,279 @@
|
||||
use dyn_stack::{GlobalPodBuffer, PodStack, ReborrowMut};
|
||||
|
||||
use super::super::super::{fft128, fft128_u128};
|
||||
use super::super::math::fft::{Fft128, Fft128View};
|
||||
use crate::core_crypto::prelude::*;
|
||||
use aligned_vec::CACHELINE_ALIGN;
|
||||
|
||||
fn sqr(x: f64) -> f64 {
|
||||
x * x
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_external_product() {
|
||||
let small_lwe_dimension = LweDimension(742);
|
||||
let glwe_dimension = GlweDimension(1);
|
||||
let polynomial_size = PolynomialSize(2048);
|
||||
let pbs_base_log = DecompositionBaseLog(23);
|
||||
let pbs_level = DecompositionLevelCount(1);
|
||||
let glwe_modular_std_dev = StandardDev(sqr(0.00000000000000029403601535432533));
|
||||
let ciphertext_modulus = CiphertextModulus::<u128>::new_native();
|
||||
let ciphertext_modulus_split = CiphertextModulus::<u64>::new_native();
|
||||
|
||||
let mut glwe = GlweCiphertext::new(
|
||||
0u128,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
for x in glwe.as_mut() {
|
||||
*x = rand::random();
|
||||
}
|
||||
|
||||
let mut boxed_seeder = new_seeder();
|
||||
let seeder = boxed_seeder.as_mut();
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
|
||||
let small_lwe_sk =
|
||||
LweSecretKey::generate_new_binary(small_lwe_dimension, &mut secret_generator);
|
||||
let glwe_sk =
|
||||
GlweSecretKey::generate_new_binary(glwe_dimension, polynomial_size, &mut secret_generator);
|
||||
|
||||
let std_bootstrapping_key = par_allocate_and_generate_new_lwe_bootstrap_key(
|
||||
&small_lwe_sk,
|
||||
&glwe_sk,
|
||||
pbs_base_log,
|
||||
pbs_level,
|
||||
glwe_modular_std_dev,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut fourier_bsk = Fourier128LweBootstrapKey::new(
|
||||
std_bootstrapping_key.input_lwe_dimension(),
|
||||
std_bootstrapping_key.glwe_size(),
|
||||
std_bootstrapping_key.polynomial_size(),
|
||||
std_bootstrapping_key.decomposition_base_log(),
|
||||
std_bootstrapping_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
let fft = Fft128::new(polynomial_size);
|
||||
let fft = fft.as_view();
|
||||
fourier_bsk.fill_with_forward_fourier(&std_bootstrapping_key, fft);
|
||||
let ggsw = fourier_bsk.as_view().into_ggsw_iter().next().unwrap();
|
||||
|
||||
let mut glwe_lo = GlweCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus_split,
|
||||
);
|
||||
let mut glwe_hi = GlweCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus_split,
|
||||
);
|
||||
|
||||
for ((lo, hi), val) in glwe_lo
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(glwe_hi.as_mut())
|
||||
.zip(glwe.as_ref())
|
||||
{
|
||||
*lo = *val as u64;
|
||||
*hi = (*val >> 64) as u64;
|
||||
}
|
||||
|
||||
let mut out = GlweCiphertext::new(
|
||||
0u128,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
fft128::crypto::ggsw::add_external_product_assign(
|
||||
&mut out,
|
||||
&ggsw,
|
||||
&glwe,
|
||||
fft,
|
||||
PodStack::new(&mut GlobalPodBuffer::new(
|
||||
fft128::crypto::ggsw::add_external_product_assign_scratch::<u128>(
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
fft,
|
||||
)
|
||||
.unwrap(),
|
||||
)),
|
||||
);
|
||||
|
||||
let mut out_lo = GlweCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus_split,
|
||||
);
|
||||
let mut out_hi = GlweCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus_split,
|
||||
);
|
||||
|
||||
fft128_u128::crypto::ggsw::add_external_product_assign_split(
|
||||
&mut out_lo,
|
||||
&mut out_hi,
|
||||
&ggsw,
|
||||
&glwe_lo,
|
||||
&glwe_hi,
|
||||
fft,
|
||||
PodStack::new(&mut GlobalPodBuffer::new(
|
||||
fft128::crypto::ggsw::add_external_product_assign_scratch::<u128>(
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
fft,
|
||||
)
|
||||
.unwrap(),
|
||||
)),
|
||||
);
|
||||
|
||||
for ((lo, hi), val) in out_lo
|
||||
.as_ref()
|
||||
.iter()
|
||||
.zip(out_hi.as_ref())
|
||||
.zip(out.as_ref())
|
||||
{
|
||||
assert_eq!(*val as u64, *lo);
|
||||
assert_eq!((*val >> 64) as u64, *hi);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_pbs() {
|
||||
let small_lwe_dimension = LweDimension(742);
|
||||
let glwe_dimension = GlweDimension(1);
|
||||
let polynomial_size = PolynomialSize(2048);
|
||||
let pbs_base_log = DecompositionBaseLog(23);
|
||||
let pbs_level = DecompositionLevelCount(1);
|
||||
let glwe_modular_std_dev = StandardDev(sqr(0.00000000000000029403601535432533));
|
||||
let ciphertext_modulus = CiphertextModulus::<u128>::new_native();
|
||||
|
||||
let mut boxed_seeder = new_seeder();
|
||||
let seeder = boxed_seeder.as_mut();
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
|
||||
let small_lwe_sk =
|
||||
LweSecretKey::generate_new_binary(small_lwe_dimension, &mut secret_generator);
|
||||
let glwe_sk =
|
||||
GlweSecretKey::generate_new_binary(glwe_dimension, polynomial_size, &mut secret_generator);
|
||||
|
||||
let std_bootstrapping_key = par_allocate_and_generate_new_lwe_bootstrap_key(
|
||||
&small_lwe_sk,
|
||||
&glwe_sk,
|
||||
pbs_base_log,
|
||||
pbs_level,
|
||||
glwe_modular_std_dev,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut fourier_bsk = Fourier128LweBootstrapKey::new(
|
||||
std_bootstrapping_key.input_lwe_dimension(),
|
||||
std_bootstrapping_key.glwe_size(),
|
||||
std_bootstrapping_key.polynomial_size(),
|
||||
std_bootstrapping_key.decomposition_base_log(),
|
||||
std_bootstrapping_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
let fft = Fft128::new(polynomial_size);
|
||||
let fft = fft.as_view();
|
||||
fourier_bsk.fill_with_forward_fourier(&std_bootstrapping_key, fft);
|
||||
|
||||
let mut lwe_in =
|
||||
LweCiphertext::new(0u128, small_lwe_dimension.to_lwe_size(), ciphertext_modulus);
|
||||
let mut accumulator = GlweCiphertext::new(
|
||||
0u128,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
for x in lwe_in.as_mut() {
|
||||
*x = rand::random();
|
||||
}
|
||||
for x in accumulator.as_mut() {
|
||||
*x = rand::random();
|
||||
}
|
||||
|
||||
let mut mem = GlobalPodBuffer::new(
|
||||
fft128::crypto::bootstrap::bootstrap_scratch::<u128>(
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
fft,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let mut stack = PodStack::new(&mut mem);
|
||||
|
||||
let mut lwe_out_non_split = LweCiphertext::new(
|
||||
0u128,
|
||||
LweDimension(polynomial_size.0 * glwe_dimension.0).to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
// Needed as the basic bootstrap function dispatches to the more efficient split version for
|
||||
// u128
|
||||
fn bootstrap_non_split<Scalar: UnsignedTorus + CastInto<usize>>(
|
||||
this: Fourier128LweBootstrapKey<&[f64]>,
|
||||
mut lwe_out: LweCiphertext<&mut [Scalar]>,
|
||||
lwe_in: LweCiphertext<&[Scalar]>,
|
||||
accumulator: GlweCiphertext<&[Scalar]>,
|
||||
fft: Fft128View<'_>,
|
||||
stack: PodStack<'_>,
|
||||
) {
|
||||
let (mut local_accumulator_data, stack) =
|
||||
stack.collect_aligned(CACHELINE_ALIGN, accumulator.as_ref().iter().copied());
|
||||
let mut local_accumulator = GlweCiphertextMutView::from_container(
|
||||
&mut *local_accumulator_data,
|
||||
accumulator.polynomial_size(),
|
||||
accumulator.ciphertext_modulus(),
|
||||
);
|
||||
this.blind_rotate_assign(&mut local_accumulator.as_mut_view(), &lwe_in, fft, stack);
|
||||
extract_lwe_sample_from_glwe_ciphertext(
|
||||
&local_accumulator,
|
||||
&mut lwe_out,
|
||||
MonomialDegree(0),
|
||||
);
|
||||
}
|
||||
|
||||
bootstrap_non_split(
|
||||
fourier_bsk.as_view(),
|
||||
lwe_out_non_split.as_mut_view(),
|
||||
lwe_in.as_view(),
|
||||
accumulator.as_view(),
|
||||
fft,
|
||||
stack.rb_mut(),
|
||||
);
|
||||
|
||||
let mut lwe_out_split = LweCiphertext::new(
|
||||
0u128,
|
||||
LweDimension(polynomial_size.0 * glwe_dimension.0).to_lwe_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
fourier_bsk.bootstrap_u128(
|
||||
&mut lwe_out_split,
|
||||
&lwe_in,
|
||||
&accumulator,
|
||||
fft,
|
||||
stack.rb_mut(),
|
||||
);
|
||||
|
||||
assert_eq!(lwe_out_split, lwe_out_non_split);
|
||||
}
|
||||
@@ -206,10 +206,11 @@ pub fn wrapping_add_avx2(
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
#[inline(always)]
|
||||
pub fn wrapping_neg_avx2(simd: V3, (lo, hi): (u64x4, u64x4)) -> (u64x4, u64x4) {
|
||||
let diff_lo = pulp::cast(simd.wrapping_sub_u64x4(simd.splat_u64x4(0), lo));
|
||||
let overflow = pulp::cast(simd.cmp_lt_u64x4(simd.splat_u64x4(0), lo));
|
||||
let diff_hi = simd.wrapping_sub_u64x4(overflow, hi);
|
||||
(diff_lo, diff_hi)
|
||||
wrapping_add_avx2(
|
||||
simd,
|
||||
(simd.splat_u64x4(1), simd.splat_u64x4(0)),
|
||||
(simd.not_u64x4(lo), simd.not_u64x4(hi)),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
@@ -246,10 +247,11 @@ pub fn wrapping_add_avx512(
|
||||
#[cfg(feature = "nightly-avx512")]
|
||||
#[inline(always)]
|
||||
pub fn wrapping_neg_avx512(simd: V4, (lo, hi): (u64x8, u64x8)) -> (u64x8, u64x8) {
|
||||
let diff_lo = simd.wrapping_sub_u64x8(simd.splat_u64x8(0), lo);
|
||||
let overflow = simd.convert_mask_b8_to_u64x8(simd.cmp_lt_u64x8(simd.splat_u64x8(0), lo));
|
||||
let diff_hi = simd.wrapping_sub_u64x8(overflow, hi);
|
||||
(diff_lo, diff_hi)
|
||||
wrapping_add_avx512(
|
||||
simd,
|
||||
(simd.splat_u64x8(1), simd.splat_u64x8(0)),
|
||||
(simd.not_u64x8(lo), simd.not_u64x8(hi)),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -385,9 +387,10 @@ fn f64_to_i128_avx2(simd: V3, f: f64x4) -> (u64x4, u64x4) {
|
||||
simd.shr_dyn_u64x4(hi, shift),
|
||||
);
|
||||
let neg = wrapping_neg_avx2(simd, abs);
|
||||
let mask = simd.cmp_eq_u64x4(simd.and_u64x4(sign_bit, f), sign_bit);
|
||||
(
|
||||
simd.select_u64x4(simd.cmp_eq_u64x4(f, sign_bit), neg.0, abs.0),
|
||||
simd.select_u64x4(simd.cmp_eq_u64x4(f, sign_bit), neg.1, abs.1),
|
||||
simd.select_u64x4(mask, neg.0, abs.0),
|
||||
simd.select_u64x4(mask, neg.1, abs.1),
|
||||
)
|
||||
};
|
||||
(
|
||||
@@ -554,7 +557,7 @@ fn f128_floor(x: f128) -> f128 {
|
||||
let f128(x0, x1) = x;
|
||||
let x0_floor = x0.floor();
|
||||
if x0_floor == x0 {
|
||||
f128(x0_floor, x1.floor())
|
||||
f128::add_f64_f64(x0_floor, x1.floor())
|
||||
} else {
|
||||
f128(x0_floor, 0.0)
|
||||
}
|
||||
@@ -566,7 +569,8 @@ fn f128_floor_avx2(simd: V3, (x0, x1): (f64x4, f64x4)) -> (f64x4, f64x4) {
|
||||
let x0_floor = simd.floor_f64x4(x0);
|
||||
let x1_floor = simd.floor_f64x4(x1);
|
||||
|
||||
(
|
||||
two_sum_f64x4(
|
||||
simd,
|
||||
x0_floor,
|
||||
simd.select_f64x4(
|
||||
simd.cmp_eq_f64x4(x0_floor, x0),
|
||||
@@ -583,7 +587,8 @@ fn f128_floor_avx512(simd: V4, (x0, x1): (f64x8, f64x8)) -> (f64x8, f64x8) {
|
||||
let x0_floor = simd.floor_f64x8(x0);
|
||||
let x1_floor = simd.floor_f64x8(x1);
|
||||
|
||||
(
|
||||
two_sum_f64x8(
|
||||
simd,
|
||||
x0_floor,
|
||||
simd.select_f64x8(
|
||||
simd.cmp_eq_f64x8(x0_floor, x0),
|
||||
@@ -1338,3 +1343,43 @@ impl<'a> Fft128View<'a> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// copied from the standard library
|
||||
fn next_up(this: f64) -> f64 {
|
||||
// We must use strictly integer arithmetic to prevent denormals from
|
||||
// flushing to zero after an arithmetic operation on some platforms.
|
||||
const TINY_BITS: u64 = 0x1; // Smallest positive f64.
|
||||
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
|
||||
|
||||
let bits = this.to_bits();
|
||||
if this.is_nan() || bits == f64::INFINITY.to_bits() {
|
||||
return this;
|
||||
}
|
||||
|
||||
let abs = bits & CLEAR_SIGN_MASK;
|
||||
let next_bits = if abs == 0 {
|
||||
TINY_BITS
|
||||
} else if bits == abs {
|
||||
bits + 1
|
||||
} else {
|
||||
bits - 1
|
||||
};
|
||||
f64::from_bits(next_bits)
|
||||
}
|
||||
|
||||
fn ulp(x: f64) -> f64 {
|
||||
next_up(x.abs()) - x.abs()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_f128_floor() {
|
||||
let a = f128(-11984547.0, -1.0316078675142442e-10);
|
||||
let b = f128_floor(a);
|
||||
|
||||
assert!(b.1.abs() <= 0.5 * ulp(b.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,3 +190,14 @@ fn test_compressed_bool() {
|
||||
assert_eq!(a.decrypt(&keys), true);
|
||||
assert_eq!(b.decrypt(&keys), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trivial_bool() {
|
||||
let keys = setup_static_default();
|
||||
|
||||
let a = FheBool::encrypt_trivial(true);
|
||||
let b = FheBool::encrypt_trivial(false);
|
||||
|
||||
assert_eq!(a.decrypt(&keys), true);
|
||||
assert_eq!(b.decrypt(&keys), false);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::marker::PhantomData;
|
||||
|
||||
use crate::high_level_api::integers::parameters::EvaluationIntegerKey;
|
||||
|
||||
use super::client_key::GenericIntegerClientKey;
|
||||
use super::client_key::{GenericIntegerClientKey, RadixClientKey};
|
||||
use super::parameters::IntegerParameter;
|
||||
|
||||
use crate::integer::wopbs::WopbsKey;
|
||||
@@ -11,12 +11,16 @@ use crate::integer::wopbs::WopbsKey;
|
||||
pub struct GenericIntegerServerKey<P: IntegerParameter> {
|
||||
pub(in crate::high_level_api::integers) inner: P::InnerServerKey,
|
||||
pub(in crate::high_level_api::integers) wopbs_key: WopbsKey,
|
||||
// To know if we have to encrypt into a big or small when trivial encrypting
|
||||
pub(in crate::high_level_api::integers) pbs_order: crate::shortint::PBSOrder,
|
||||
// To know the num block when trivial encrypting
|
||||
pub(in crate::high_level_api::integers) num_block: usize,
|
||||
_marker: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P> GenericIntegerServerKey<P>
|
||||
where
|
||||
P: IntegerParameter,
|
||||
P: IntegerParameter<InnerClientKey = RadixClientKey>,
|
||||
P::InnerServerKey: EvaluationIntegerKey<P::InnerClientKey>,
|
||||
{
|
||||
pub(super) fn new(client_key: &GenericIntegerClientKey<P>) -> Self {
|
||||
@@ -29,6 +33,8 @@ where
|
||||
Self {
|
||||
inner,
|
||||
wopbs_key,
|
||||
pbs_order: client_key.inner.pbs_order,
|
||||
num_block: client_key.inner.inner.num_blocks(),
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,3 +201,39 @@ fn test_integer_compressed_public_key() {
|
||||
let clear: u8 = a.decrypt(&client_key);
|
||||
assert_eq!(clear, 213u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trivial_fhe_uint8() {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_uint8().build();
|
||||
let (client_key, sks) = generate_keys(config);
|
||||
|
||||
set_server_key(sks);
|
||||
|
||||
let a = FheUint8::try_encrypt_trivial(234u8).unwrap();
|
||||
assert!(matches!(
|
||||
&*a.ciphertext.borrow(),
|
||||
crate::high_level_api::integers::server_key::RadixCiphertextDyn::Big(_)
|
||||
));
|
||||
|
||||
let clear: u8 = a.decrypt(&client_key);
|
||||
assert_eq!(clear, 234);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trivial_fhe_uint256_small() {
|
||||
let config = ConfigBuilder::all_disabled()
|
||||
.enable_default_uint256_small()
|
||||
.build();
|
||||
let (client_key, sks) = generate_keys(config);
|
||||
|
||||
set_server_key(sks);
|
||||
|
||||
let clear_a = U256::from(u128::MAX);
|
||||
let a = FheUint256::try_encrypt_trivial(clear_a).unwrap();
|
||||
assert!(matches!(
|
||||
&*a.ciphertext.borrow(),
|
||||
crate::high_level_api::integers::server_key::RadixCiphertextDyn::Small(_)
|
||||
));
|
||||
let clear: U256 = a.decrypt(&client_key);
|
||||
assert_eq!(clear, clear_a);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ use crate::high_level_api::keys::{
|
||||
CompressedPublicKey, RefKeyFromCompressedPublicKeyChain, RefKeyFromKeyChain,
|
||||
RefKeyFromPublicKeyChain,
|
||||
};
|
||||
use crate::high_level_api::traits::{FheBootstrap, FheDecrypt, FheEq, FheOrd, FheTryEncrypt};
|
||||
use crate::high_level_api::traits::{
|
||||
FheBootstrap, FheDecrypt, FheEq, FheOrd, FheTrivialEncrypt, FheTryEncrypt, FheTryTrivialEncrypt,
|
||||
};
|
||||
use crate::high_level_api::{ClientKey, PublicKey};
|
||||
use crate::integer::U256;
|
||||
|
||||
@@ -166,6 +168,47 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, T> FheTryTrivialEncrypt<T> for GenericInteger<P>
|
||||
where
|
||||
T: Into<U256>,
|
||||
P: IntegerParameter<
|
||||
InnerCiphertext = RadixCiphertextDyn,
|
||||
InnerServerKey = crate::integer::ServerKey,
|
||||
>,
|
||||
P::Id: WithGlobalKey<Key = GenericIntegerServerKey<P>> + Default,
|
||||
{
|
||||
type Error = crate::high_level_api::errors::Error;
|
||||
|
||||
fn try_encrypt_trivial(value: T) -> Result<Self, Self::Error> {
|
||||
let value = value.into();
|
||||
let id = P::Id::default();
|
||||
let ciphertext = id.with_global(|key| match key.pbs_order {
|
||||
crate::shortint::PBSOrder::KeyswitchBootstrap => {
|
||||
RadixCiphertextDyn::Big(key.inner.create_trivial_radix(value, key.num_block))
|
||||
}
|
||||
crate::shortint::PBSOrder::BootstrapKeyswitch => {
|
||||
RadixCiphertextDyn::Small(key.inner.create_trivial_radix(value, key.num_block))
|
||||
}
|
||||
})?;
|
||||
Ok(Self::new(ciphertext, id))
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, T> FheTrivialEncrypt<T> for GenericInteger<P>
|
||||
where
|
||||
T: Into<U256>,
|
||||
P: IntegerParameter<
|
||||
InnerCiphertext = RadixCiphertextDyn,
|
||||
InnerServerKey = crate::integer::ServerKey,
|
||||
>,
|
||||
P::Id: WithGlobalKey<Key = GenericIntegerServerKey<P>> + Default,
|
||||
{
|
||||
#[track_caller]
|
||||
fn encrypt_trivial(value: T) -> Self {
|
||||
Self::try_encrypt_trivial(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> GenericInteger<P>
|
||||
where
|
||||
P: IntegerParameter,
|
||||
|
||||
@@ -413,7 +413,7 @@ static_int_type! {
|
||||
parameters: Radix {
|
||||
big_block_parameters: crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2,
|
||||
small_block_parameters: crate::shortint::parameters::PARAM_SMALL_MESSAGE_2_CARRY_2,
|
||||
num_block: 32,
|
||||
num_block: 16,
|
||||
wopbs_block_parameters: crate::shortint::parameters::parameters_wopbs_message_carry::WOPBS_PARAM_MESSAGE_2_CARRY_2,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::high_level_api::prelude::*;
|
||||
use crate::high_level_api::{generate_keys, CompressedFheUint2, ConfigBuilder, FheUint2};
|
||||
use crate::high_level_api::{
|
||||
generate_keys, set_server_key, CompressedFheUint2, ConfigBuilder, FheUint2,
|
||||
};
|
||||
use crate::CompressedPublicKey;
|
||||
|
||||
#[test]
|
||||
@@ -24,3 +26,15 @@ fn test_shortint_compressed_public_key() {
|
||||
let clear = a.decrypt(&client_key);
|
||||
assert_eq!(clear, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trivial_shortint() {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_uint2().build();
|
||||
let (client_key, sks) = generate_keys(config);
|
||||
|
||||
set_server_key(sks);
|
||||
|
||||
let a = FheUint2::try_encrypt_trivial(2).unwrap();
|
||||
let clear = a.decrypt(&client_key);
|
||||
assert_eq!(clear, 2);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ use crate::high_level_api::keys::{
|
||||
};
|
||||
use crate::high_level_api::shortints::public_key::compressed::GenericShortIntCompressedPublicKey;
|
||||
use crate::high_level_api::traits::{
|
||||
FheBootstrap, FheDecrypt, FheEq, FheNumberConstant, FheOrd, FheTryEncrypt, FheTryTrivialEncrypt,
|
||||
FheBootstrap, FheDecrypt, FheEq, FheNumberConstant, FheOrd, FheTrivialEncrypt, FheTryEncrypt,
|
||||
FheTryTrivialEncrypt,
|
||||
};
|
||||
use crate::high_level_api::PublicKey;
|
||||
|
||||
@@ -311,6 +312,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Clear, P> FheTrivialEncrypt<Clear> for GenericShortInt<P>
|
||||
where
|
||||
Clear: TryInto<u8>,
|
||||
P: StaticShortIntegerParameter,
|
||||
P::Id: Default + WithGlobalKey<Key = GenericShortIntServerKey<P>>,
|
||||
{
|
||||
#[track_caller]
|
||||
fn encrypt_trivial(value: Clear) -> Self {
|
||||
Self::try_encrypt_trivial(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> GenericShortInt<P>
|
||||
where
|
||||
P: ShortIntegerParameter,
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::shortint::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Structure containing a ciphertext in radix decomposition.
|
||||
#[derive(Serialize, Clone, Deserialize)]
|
||||
#[derive(Serialize, Clone, Deserialize, PartialEq, Eq, Debug)]
|
||||
pub struct BaseRadixCiphertext<Block> {
|
||||
/// The blocks are stored from LSB to MSB
|
||||
pub(crate) blocks: Vec<Block>,
|
||||
|
||||
@@ -109,29 +109,35 @@ impl AsLittleEndianWords for U256 {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait BlockEncryptionKey {
|
||||
fn parameters(&self) -> &crate::shortint::Parameters;
|
||||
pub(crate) trait KnowsMessageModulus {
|
||||
fn message_modulus(&self) -> MessageModulus;
|
||||
}
|
||||
|
||||
impl BlockEncryptionKey for crate::shortint::ClientKey {
|
||||
fn parameters(&self) -> &crate::shortint::Parameters {
|
||||
&self.parameters
|
||||
impl KnowsMessageModulus for crate::shortint::ClientKey {
|
||||
fn message_modulus(&self) -> MessageModulus {
|
||||
self.parameters.message_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<OpOrder: crate::shortint::PBSOrderMarker> BlockEncryptionKey
|
||||
impl<OpOrder: crate::shortint::PBSOrderMarker> KnowsMessageModulus
|
||||
for crate::shortint::PublicKeyBase<OpOrder>
|
||||
{
|
||||
fn parameters(&self) -> &crate::shortint::Parameters {
|
||||
&self.parameters
|
||||
fn message_modulus(&self) -> MessageModulus {
|
||||
self.parameters.message_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<OpOrder: crate::shortint::PBSOrderMarker> BlockEncryptionKey
|
||||
impl<OpOrder: crate::shortint::PBSOrderMarker> KnowsMessageModulus
|
||||
for crate::shortint::CompressedPublicKeyBase<OpOrder>
|
||||
{
|
||||
fn parameters(&self) -> &crate::shortint::Parameters {
|
||||
&self.parameters
|
||||
fn message_modulus(&self) -> MessageModulus {
|
||||
self.parameters.message_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl KnowsMessageModulus for crate::shortint::ServerKey {
|
||||
fn message_modulus(&self) -> MessageModulus {
|
||||
self.message_modulus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +157,7 @@ pub(crate) fn encrypt_words_radix_impl<BlockKey, Block, RadixCiphertextType, T,
|
||||
) -> RadixCiphertextType
|
||||
where
|
||||
T: AsLittleEndianWords,
|
||||
BlockKey: BlockEncryptionKey,
|
||||
BlockKey: KnowsMessageModulus,
|
||||
F: Fn(&BlockKey, u64) -> Block,
|
||||
RadixCiphertextType: From<Vec<Block>>,
|
||||
{
|
||||
@@ -165,8 +171,8 @@ where
|
||||
// | bit values are not valid and should not be encrypted)
|
||||
// |-> current_power (start of next block of bits to encrypt (inclusive))
|
||||
|
||||
let mask = (encrypting_key.parameters().message_modulus.0 - 1) as u128;
|
||||
let block_modulus = encrypting_key.parameters().message_modulus.0 as u128;
|
||||
let mask = (encrypting_key.message_modulus().0 - 1) as u128;
|
||||
let block_modulus = encrypting_key.message_modulus().0 as u128;
|
||||
|
||||
let mut blocks = Vec::with_capacity(num_blocks);
|
||||
|
||||
|
||||
@@ -1107,7 +1107,9 @@ impl<'a> Comparator<'a> {
|
||||
};
|
||||
|
||||
let mut res = self.unchecked_max_parallelized(lhs, rhs);
|
||||
self.server_key.full_propagate_parallelized(&mut res);
|
||||
res.blocks
|
||||
.par_iter_mut()
|
||||
.for_each(|block| self.server_key.key.message_extract_assign(block));
|
||||
res
|
||||
}
|
||||
|
||||
@@ -1143,7 +1145,9 @@ impl<'a> Comparator<'a> {
|
||||
};
|
||||
|
||||
let mut res = self.unchecked_min_parallelized(lhs, rhs);
|
||||
self.server_key.full_propagate_parallelized(&mut res);
|
||||
res.blocks
|
||||
.par_iter_mut()
|
||||
.for_each(|block| self.server_key.key.message_extract_assign(block));
|
||||
res
|
||||
}
|
||||
}
|
||||
@@ -1333,7 +1337,7 @@ mod tests {
|
||||
assert!(super::has_non_zero_carries(&ct_0));
|
||||
assert!(super::has_non_zero_carries(&ct_1));
|
||||
let encrypted_result = default_comparator_method(&comparator, &ct_0, &ct_1);
|
||||
// assert!(!super::has_non_zero_carries(&encrypted_result));
|
||||
assert!(!super::has_non_zero_carries(&encrypted_result));
|
||||
|
||||
// Sanity decryption checks
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@ mod sub;
|
||||
use super::ServerKey;
|
||||
|
||||
use crate::integer::ciphertext::RadixCiphertext;
|
||||
use crate::integer::encryption::{encrypt_words_radix_impl, AsLittleEndianWords};
|
||||
use crate::shortint::PBSOrderMarker;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -49,6 +50,44 @@ impl ServerKey {
|
||||
RadixCiphertext::from(vec_res)
|
||||
}
|
||||
|
||||
/// Create a trivial radix ciphertext
|
||||
///
|
||||
/// Trivial means that the value is not encrypted
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::integer::{gen_keys_radix, RadixCiphertextBig};
|
||||
/// use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
|
||||
///
|
||||
/// let num_blocks = 4;
|
||||
///
|
||||
/// // Generate the client key and the server key:
|
||||
/// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
|
||||
///
|
||||
/// let ctxt: RadixCiphertextBig = sks.create_trivial_radix(212u64, num_blocks);
|
||||
///
|
||||
/// // Decrypt:
|
||||
/// let dec: u64 = cks.decrypt(&ctxt);
|
||||
/// assert_eq!(212, dec);
|
||||
/// ```
|
||||
pub fn create_trivial_radix<T, PBSOrder>(
|
||||
&self,
|
||||
value: T,
|
||||
num_blocks: usize,
|
||||
) -> RadixCiphertext<PBSOrder>
|
||||
where
|
||||
PBSOrder: PBSOrderMarker,
|
||||
T: AsLittleEndianWords,
|
||||
{
|
||||
encrypt_words_radix_impl(
|
||||
&self.key,
|
||||
value,
|
||||
num_blocks,
|
||||
crate::shortint::ServerKey::create_trivial,
|
||||
)
|
||||
}
|
||||
|
||||
/// Propagate the carry of the 'index' block to the next one.
|
||||
///
|
||||
/// # Example
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::integer::ciphertext::RadixCiphertext;
|
||||
use crate::integer::ServerKey;
|
||||
use crate::shortint::PBSOrderMarker;
|
||||
@@ -196,24 +194,25 @@ impl ServerKey {
|
||||
} else {
|
||||
// we repeatedly divide the number of terms by two by iteratively reducing
|
||||
// consecutive terms in the array
|
||||
let num_blocks = ct_seq[0].as_mut().blocks.len();
|
||||
while ct_seq.len() > 1 {
|
||||
let results = Mutex::new(Vec::<RadixCiphertext<PBSOrder>>::with_capacity(
|
||||
ct_seq.len() / 2,
|
||||
));
|
||||
let mut results =
|
||||
vec![sks.create_trivial_radix(0u64, num_blocks); ct_seq.len() / 2];
|
||||
|
||||
// if the number of elements is odd, we skip the first element
|
||||
let untouched_prefix = ct_seq.len() % 2;
|
||||
let ct_seq_slice = &mut ct_seq[untouched_prefix..];
|
||||
|
||||
ct_seq_slice.par_chunks_mut(2).for_each(|chunk| {
|
||||
let (first, second) = chunk.split_at_mut(1);
|
||||
let first = &mut first[0];
|
||||
let second = &mut second[0];
|
||||
let result = op(sks, first.as_mut(), second.as_mut());
|
||||
results.lock().unwrap().push(result);
|
||||
});
|
||||
results
|
||||
.par_iter_mut()
|
||||
.zip(ct_seq_slice.par_chunks_exact_mut(2))
|
||||
.for_each(|(ct_res, chunk)| {
|
||||
let (first, second) = chunk.split_at_mut(1);
|
||||
let first = first[0].as_mut();
|
||||
let second = second[0].as_mut();
|
||||
*ct_res = op(sks, first, second);
|
||||
});
|
||||
|
||||
let results = results.into_inner().unwrap();
|
||||
ct_seq.truncate(untouched_prefix);
|
||||
ct_seq.extend(results.into_iter().map(CiphertextCow::Owned));
|
||||
}
|
||||
@@ -281,24 +280,23 @@ impl ServerKey {
|
||||
} else {
|
||||
// we repeatedly divide the number of terms by two by iteratively reducing
|
||||
// consecutive terms in the array
|
||||
let num_blocks = ct_seq[0].as_ref().blocks.len();
|
||||
while ct_seq.len() > 1 {
|
||||
let results = Mutex::new(Vec::<RadixCiphertext<PBSOrder>>::with_capacity(
|
||||
ct_seq.len() / 2,
|
||||
));
|
||||
|
||||
let mut results =
|
||||
vec![sks.create_trivial_radix(0u64, num_blocks); ct_seq.len() / 2];
|
||||
// if the number of elements is odd, we skip the first element
|
||||
let untouched_prefix = ct_seq.len() % 2;
|
||||
let ct_seq_slice = &mut ct_seq[untouched_prefix..];
|
||||
|
||||
ct_seq_slice.par_chunks(2).for_each(|chunk| {
|
||||
let (first, second) = chunk.split_at(1);
|
||||
let first = &first[0];
|
||||
let second = &second[0];
|
||||
let result = op(sks, first.as_ref(), second.as_ref());
|
||||
results.lock().unwrap().push(result);
|
||||
});
|
||||
results
|
||||
.par_iter_mut()
|
||||
.zip(ct_seq_slice.par_chunks_exact(2))
|
||||
.for_each(|(ct_res, chunk)| {
|
||||
let first = chunk[0].as_ref();
|
||||
let second = chunk[1].as_ref();
|
||||
*ct_res = op(sks, first, second);
|
||||
});
|
||||
|
||||
let results = results.into_inner().unwrap();
|
||||
ct_seq.truncate(untouched_prefix);
|
||||
ct_seq.extend(results.into_iter().map(CiphertextCow::Owned));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::integer::ciphertext::RadixCiphertext;
|
||||
use crate::integer::ServerKey;
|
||||
use crate::shortint::PBSOrderMarker;
|
||||
@@ -335,17 +333,18 @@ impl ServerKey {
|
||||
) -> RadixCiphertext<PBSOrder> {
|
||||
let mut result = self.create_trivial_zero_radix(ct1.blocks.len());
|
||||
|
||||
let terms = Mutex::new(Vec::new());
|
||||
let num_blocks = ct1.blocks.len();
|
||||
let mut terms = vec![self.create_trivial_zero_radix(num_blocks); num_blocks];
|
||||
|
||||
ct2.blocks.par_iter().enumerate().for_each(|(i, ct2_i)| {
|
||||
let term = self.unchecked_block_mul_parallelized(ct1, ct2_i, i);
|
||||
terms.lock().unwrap().push(term);
|
||||
});
|
||||
terms
|
||||
.par_iter_mut()
|
||||
.zip(ct2.blocks.par_iter().enumerate())
|
||||
.for_each(|(term, (i, ct2_i))| {
|
||||
*term = self.unchecked_block_mul_parallelized(ct1, ct2_i, i);
|
||||
});
|
||||
|
||||
let terms = terms.into_inner().unwrap();
|
||||
|
||||
for term in terms {
|
||||
self.unchecked_add_assign(&mut result, &term);
|
||||
for term in terms.iter_mut() {
|
||||
self.smart_add_assign(&mut result, term);
|
||||
}
|
||||
|
||||
result
|
||||
@@ -407,12 +406,15 @@ impl ServerKey {
|
||||
|| self.full_propagate_parallelized(ct2),
|
||||
);
|
||||
|
||||
let terms = Mutex::new(Vec::new());
|
||||
ct2.blocks.par_iter().enumerate().for_each(|(i, ct2_i)| {
|
||||
let term = self.unchecked_block_mul_parallelized(ct1, ct2_i, i);
|
||||
terms.lock().unwrap().push(term);
|
||||
});
|
||||
let mut terms = terms.into_inner().unwrap();
|
||||
let num_blocks = ct1.blocks.len();
|
||||
let mut terms = vec![self.create_trivial_zero_radix(num_blocks); num_blocks];
|
||||
|
||||
terms
|
||||
.par_iter_mut()
|
||||
.zip(ct2.blocks.par_iter().enumerate())
|
||||
.for_each(|(term, (i, ct2_i))| {
|
||||
*term = self.unchecked_block_mul_parallelized(ct1, ct2_i, i);
|
||||
});
|
||||
|
||||
self.smart_binary_op_seq_parallelized(&mut terms, ServerKey::smart_add_parallelized)
|
||||
.unwrap_or_else(|| self.create_trivial_zero_radix(ct1.blocks.len()))
|
||||
@@ -495,7 +497,20 @@ impl ServerKey {
|
||||
(ct1, &tmp_rhs)
|
||||
}
|
||||
};
|
||||
self.unchecked_mul_assign_parallelized(lhs, rhs);
|
||||
|
||||
let num_blocks = lhs.blocks.len();
|
||||
let mut terms = vec![self.create_trivial_zero_radix(num_blocks); num_blocks];
|
||||
terms
|
||||
.par_iter_mut()
|
||||
.zip(rhs.blocks.par_iter().enumerate())
|
||||
.for_each(|(term, (i, rhs_i))| {
|
||||
*term = self.unchecked_block_mul_parallelized(lhs, rhs_i, i);
|
||||
});
|
||||
|
||||
*lhs = self
|
||||
.smart_binary_op_seq_parallelized(&mut terms, ServerKey::smart_add_parallelized)
|
||||
.unwrap_or_else(|| self.create_trivial_zero_radix(num_blocks));
|
||||
|
||||
self.full_propagate_parallelized(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ use crate::integer::server_key::CheckError::CarryFull;
|
||||
use crate::integer::ServerKey;
|
||||
use crate::shortint::PBSOrderMarker;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
impl ServerKey {
|
||||
/// Computes homomorphically a multiplication between a scalar and a ciphertext.
|
||||
@@ -363,53 +361,68 @@ impl ServerKey {
|
||||
return zero;
|
||||
}
|
||||
|
||||
let num_tasks = self.key.message_modulus.0;
|
||||
let b = self.key.message_modulus.0 as u64;
|
||||
let n = ct.blocks.len();
|
||||
let num_blocks = ct.blocks.len();
|
||||
|
||||
//Propagate the carries before doing the multiplications
|
||||
self.full_propagate_parallelized(ct);
|
||||
let ct = &*ct;
|
||||
|
||||
// key is the small scalar we multiply by
|
||||
// value is the vector of blockshifts
|
||||
let mut task_map = HashMap::<u64, Vec<usize>>::new();
|
||||
// index is the small scalar we multiply by, value is the vector of blockshifts
|
||||
let mut task_vec: Vec<Vec<usize>> =
|
||||
vec![Vec::with_capacity((u64::BITS / b.ilog2()) as usize); num_tasks];
|
||||
|
||||
// Divide scalar progressively towards zero
|
||||
let mut scalar_i = scalar;
|
||||
for i in 0..n {
|
||||
for i in 0..num_blocks {
|
||||
let u_i = scalar_i % b;
|
||||
task_map.entry(u_i).or_insert_with(Vec::new).push(i);
|
||||
task_vec[u_i as usize].push(i);
|
||||
scalar_i /= b;
|
||||
if scalar_i == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let terms = Mutex::new(Vec::<RadixCiphertext<PBSOrder>>::new());
|
||||
task_map.par_iter().for_each(|(&u_i, blockshifts)| {
|
||||
if u_i == 0 {
|
||||
return;
|
||||
}
|
||||
let task_vec: Vec<_> = task_vec
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.skip(1) // skip u_i == 0, multiplying by 0 yielding 0
|
||||
.filter(|(_u_i, blockshifts)| !blockshifts.is_empty())
|
||||
.collect();
|
||||
|
||||
let blockshifts = &**blockshifts;
|
||||
let min_blockshift = *blockshifts.iter().min().unwrap();
|
||||
let mut terms: Vec<_> = task_vec
|
||||
.iter()
|
||||
.map(|(_, blockshifts)| {
|
||||
vec![self.create_trivial_zero_radix(num_blocks); blockshifts.len()]
|
||||
})
|
||||
.collect();
|
||||
terms
|
||||
.par_iter_mut()
|
||||
.zip(task_vec.par_iter())
|
||||
.for_each(|(term_vec, (u_i, blockshifts))| {
|
||||
let min_blockshift = blockshifts.iter().min().unwrap();
|
||||
|
||||
let mut tmp = ct.clone();
|
||||
if u_i != 1 {
|
||||
tmp.blocks[0..n - min_blockshift]
|
||||
let u_i = *u_i;
|
||||
let mut tmp = ct.clone();
|
||||
if u_i != 1 {
|
||||
tmp.blocks[0..num_blocks - *min_blockshift]
|
||||
.par_iter_mut()
|
||||
.for_each(|ct_i| self.key.unchecked_scalar_mul_assign(ct_i, u_i as u8));
|
||||
}
|
||||
|
||||
term_vec
|
||||
.par_iter_mut()
|
||||
.for_each(|ct_i| self.key.unchecked_scalar_mul_assign(ct_i, u_i as u8));
|
||||
}
|
||||
|
||||
let tmp = &tmp;
|
||||
blockshifts.par_iter().for_each(|&shift| {
|
||||
let term = self.blockshift(tmp, shift);
|
||||
terms.lock().unwrap().push(term);
|
||||
.zip(blockshifts.par_iter())
|
||||
.for_each(|(term, &shift)| {
|
||||
*term = self.blockshift(&tmp, shift);
|
||||
});
|
||||
});
|
||||
});
|
||||
let mut terms = terms.into_inner().unwrap();
|
||||
self.smart_binary_op_seq_parallelized(&mut terms, ServerKey::smart_add_parallelized)
|
||||
.unwrap_or(zero)
|
||||
self.smart_binary_op_seq_parallelized(
|
||||
terms.iter_mut().flatten(),
|
||||
ServerKey::smart_add_parallelized,
|
||||
)
|
||||
.unwrap_or(zero)
|
||||
}
|
||||
|
||||
pub fn smart_scalar_mul_assign_parallelized<PBSOrder: PBSOrderMarker>(
|
||||
@@ -475,51 +488,67 @@ impl ServerKey {
|
||||
return;
|
||||
}
|
||||
|
||||
let num_tasks = self.key.message_modulus.0;
|
||||
let b = self.key.message_modulus.0 as u64;
|
||||
let n = ct.blocks.len();
|
||||
let num_blocks = ct.blocks.len();
|
||||
|
||||
//Propagate the carries before doing the multiplications
|
||||
self.full_propagate_parallelized(ct);
|
||||
|
||||
// key is the small scalar we multiply by
|
||||
// value is the vector of blockshifts
|
||||
let mut task_map = HashMap::<u64, Vec<usize>>::new();
|
||||
// index is the small scalar we multiply by, value is the vector of blockshifts
|
||||
let mut task_vec: Vec<Vec<usize>> =
|
||||
vec![Vec::with_capacity((u64::BITS / b.ilog2()) as usize); num_tasks];
|
||||
|
||||
// Divide scalar progressively towards zero
|
||||
let mut scalar_i = scalar;
|
||||
for i in 0..n {
|
||||
for i in 0..num_blocks {
|
||||
let u_i = scalar_i % b;
|
||||
task_map.entry(u_i).or_insert_with(Vec::new).push(i);
|
||||
task_vec[u_i as usize].push(i);
|
||||
scalar_i /= b;
|
||||
if scalar_i == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let terms = Mutex::new(Vec::<RadixCiphertext<PBSOrder>>::new());
|
||||
task_map.par_iter().for_each(|(&u_i, blockshifts)| {
|
||||
if u_i == 0 {
|
||||
return;
|
||||
}
|
||||
let task_vec: Vec<_> = task_vec
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.skip(1) // skip u_i == 0, multiplying by 0 yielding 0
|
||||
.filter(|(_u_i, blockshifts)| !blockshifts.is_empty())
|
||||
.collect();
|
||||
|
||||
let blockshifts = &**blockshifts;
|
||||
let min_blockshift = *blockshifts.iter().min().unwrap();
|
||||
let mut terms: Vec<_> = task_vec
|
||||
.iter()
|
||||
.map(|(_, blockshifts)| {
|
||||
vec![self.create_trivial_zero_radix(num_blocks); blockshifts.len()]
|
||||
})
|
||||
.collect();
|
||||
terms
|
||||
.par_iter_mut()
|
||||
.zip(task_vec.par_iter())
|
||||
.for_each(|(term_vec, (u_i, blockshifts))| {
|
||||
let min_blockshift = blockshifts.iter().min().unwrap();
|
||||
|
||||
let mut tmp = ct.clone();
|
||||
if u_i != 1 {
|
||||
tmp.blocks[0..n - min_blockshift]
|
||||
let u_i = *u_i;
|
||||
let mut tmp = ct.clone();
|
||||
if u_i != 1 {
|
||||
tmp.blocks[0..num_blocks - *min_blockshift]
|
||||
.par_iter_mut()
|
||||
.for_each(|ct_i| self.key.unchecked_scalar_mul_assign(ct_i, u_i as u8));
|
||||
}
|
||||
|
||||
term_vec
|
||||
.par_iter_mut()
|
||||
.for_each(|ct_i| self.key.unchecked_scalar_mul_assign(ct_i, u_i as u8));
|
||||
}
|
||||
|
||||
let tmp = &tmp;
|
||||
blockshifts.par_iter().for_each(|&shift| {
|
||||
let term = self.blockshift(tmp, shift);
|
||||
terms.lock().unwrap().push(term);
|
||||
.zip(blockshifts.par_iter())
|
||||
.for_each(|(term, &shift)| {
|
||||
*term = self.blockshift(&tmp, shift);
|
||||
});
|
||||
});
|
||||
});
|
||||
let terms = terms.into_inner().unwrap();
|
||||
*ct = self
|
||||
.default_binary_op_seq_parallelized(&terms, ServerKey::add_parallelized)
|
||||
.smart_binary_op_seq_parallelized(
|
||||
terms.iter_mut().flatten(),
|
||||
ServerKey::smart_add_parallelized,
|
||||
)
|
||||
.unwrap_or(zero);
|
||||
self.full_propagate_parallelized(ct);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ fn integer_smart_add(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let mut ctxt_1 = cks.encrypt(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.smart_add_parallelized(&mut ctxt_0, &mut ctxt_1);
|
||||
|
||||
clear = (clear_0 + clear_1) % modulus;
|
||||
@@ -141,7 +140,6 @@ fn integer_smart_add_sequence_multi_thread(param: Parameters) {
|
||||
.map(|clear| cks.encrypt(clear))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// add the ciphertexts
|
||||
let ct_res = sks
|
||||
.smart_binary_op_seq_parallelized(&mut ctxts, ServerKey::smart_add_parallelized)
|
||||
.unwrap();
|
||||
@@ -176,7 +174,6 @@ fn integer_smart_add_sequence_single_thread(param: Parameters) {
|
||||
.map(|clear| cks.encrypt(clear))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// add the ciphertexts
|
||||
let threadpool = rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(1)
|
||||
.build()
|
||||
@@ -217,9 +214,10 @@ fn integer_default_add(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_1 = cks.encrypt(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.add_parallelized(&ctxt_0, &ctxt_1);
|
||||
let tmp_ct = sks.add_parallelized(&ctxt_0, &ctxt_1);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp_ct);
|
||||
|
||||
clear = (clear_0 + clear_1) % modulus;
|
||||
|
||||
@@ -263,11 +261,14 @@ fn integer_default_add_sequence_multi_thread(param: Parameters) {
|
||||
.map(|clear| cks.encrypt(clear))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// add the ciphertexts
|
||||
let ct_res = sks
|
||||
.default_binary_op_seq_parallelized(&ctxts, ServerKey::add_parallelized)
|
||||
.unwrap();
|
||||
let tmp_ct = sks
|
||||
.default_binary_op_seq_parallelized(&ctxts, ServerKey::add_parallelized)
|
||||
.unwrap();
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp_ct);
|
||||
let ct_res: u64 = cks.decrypt(&ct_res);
|
||||
let clear = clears.iter().sum::<u64>() % modulus;
|
||||
|
||||
@@ -299,7 +300,6 @@ fn integer_default_add_sequence_single_thread(param: Parameters) {
|
||||
.map(|clear| cks.encrypt(clear))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// add the ciphertexts
|
||||
let threadpool = rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(1)
|
||||
.build()
|
||||
@@ -341,7 +341,6 @@ fn integer_smart_bitand(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let mut ctxt_1 = cks.encrypt(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.smart_bitand_parallelized(&mut ctxt_0, &mut ctxt_1);
|
||||
|
||||
clear = clear_0 & clear_1;
|
||||
@@ -387,7 +386,6 @@ fn integer_smart_bitor(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let mut ctxt_1 = cks.encrypt(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.smart_bitor_parallelized(&mut ctxt_0, &mut ctxt_1);
|
||||
|
||||
clear = (clear_0 | clear_1) % modulus;
|
||||
@@ -433,7 +431,6 @@ fn integer_smart_bitxor(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let mut ctxt_1 = cks.encrypt(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.smart_bitxor_parallelized(&mut ctxt_0, &mut ctxt_1);
|
||||
|
||||
clear = (clear_0 ^ clear_1) % modulus;
|
||||
@@ -479,7 +476,6 @@ fn integer_default_bitand(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_1 = cks.encrypt(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.bitand_parallelized(&ctxt_0, &ctxt_1);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
|
||||
@@ -491,8 +487,10 @@ fn integer_default_bitand(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_2 = cks.encrypt(clear_2);
|
||||
|
||||
let tmp = sks.bitand_parallelized(&ct_res, &ctxt_2);
|
||||
ct_res = sks.bitand_parallelized(&ct_res, &ctxt_2);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
clear &= clear_2;
|
||||
|
||||
// decryption of ct_res
|
||||
@@ -527,7 +525,6 @@ fn integer_default_bitor(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_1 = cks.encrypt(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.bitor_parallelized(&ctxt_0, &ctxt_1);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
|
||||
@@ -539,8 +536,10 @@ fn integer_default_bitor(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_2 = cks.encrypt(clear_2);
|
||||
|
||||
let tmp = sks.bitor_parallelized(&ct_res, &ctxt_2);
|
||||
ct_res = sks.bitor_parallelized(&ct_res, &ctxt_2);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
clear = (clear | clear_2) % modulus;
|
||||
|
||||
// decryption of ct_res
|
||||
@@ -575,7 +574,6 @@ fn integer_default_bitxor(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_1 = cks.encrypt(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.bitxor_parallelized(&ctxt_0, &ctxt_1);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
|
||||
@@ -587,8 +585,10 @@ fn integer_default_bitxor(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_2 = cks.encrypt(clear_2);
|
||||
|
||||
let tmp = sks.bitxor_parallelized(&ct_res, &ctxt_2);
|
||||
ct_res = sks.bitxor_parallelized(&ct_res, &ctxt_2);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
clear = (clear ^ clear_2) % modulus;
|
||||
|
||||
// decryption of ct_res
|
||||
@@ -620,7 +620,6 @@ fn integer_unchecked_small_scalar_mul(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ct = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.unchecked_small_scalar_mul_parallelized(&ct, scalar);
|
||||
|
||||
// decryption of ct_res
|
||||
@@ -696,8 +695,10 @@ fn integer_default_small_scalar_mul(param: Parameters) {
|
||||
clear_res = clear * scalar;
|
||||
for _ in 0..NB_TEST_SMALLER {
|
||||
// scalar multiplication
|
||||
let tmp = sks.small_scalar_mul_parallelized(&ct_res, scalar);
|
||||
ct_res = sks.small_scalar_mul_parallelized(&ct_res, scalar);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(tmp, ct_res);
|
||||
clear_res *= scalar;
|
||||
}
|
||||
|
||||
@@ -758,7 +759,9 @@ fn integer_default_scalar_mul(param: Parameters) {
|
||||
|
||||
// scalar mul
|
||||
let ct_res = sks.scalar_mul_parallelized(&ct, scalar);
|
||||
let tmp = sks.scalar_mul_parallelized(&ct, scalar);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res: u64 = cks.decrypt(&ct_res);
|
||||
@@ -790,7 +793,6 @@ fn integer_unchecked_scalar_left_shift(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ct = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.unchecked_scalar_left_shift_parallelized(&ct, scalar);
|
||||
|
||||
// decryption of ct_res
|
||||
@@ -823,9 +825,10 @@ fn integer_default_scalar_left_shift(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ct = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.scalar_left_shift_parallelized(&ct, scalar);
|
||||
let tmp = sks.scalar_left_shift_parallelized(&ct, scalar);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res: u64 = cks.decrypt(&ct_res);
|
||||
@@ -857,7 +860,6 @@ fn integer_unchecked_scalar_right_shift(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ct = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.unchecked_scalar_right_shift_parallelized(&ct, scalar);
|
||||
|
||||
// decryption of ct_res
|
||||
@@ -890,9 +892,10 @@ fn integer_default_scalar_right_shift(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ct = cks.encrypt(clear);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.scalar_right_shift_parallelized(&ct, scalar);
|
||||
let tmp = sks.scalar_right_shift_parallelized(&ct, scalar);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
|
||||
// decryption of ct_res
|
||||
let dec_res: u64 = cks.decrypt(&ct_res);
|
||||
@@ -950,11 +953,13 @@ fn integer_default_neg(param: Parameters) {
|
||||
let ctxt = cks.encrypt(clear);
|
||||
|
||||
// Negates the ctxt
|
||||
let ct_tmp = sks.neg_parallelized(&ctxt);
|
||||
assert!(ct_tmp.block_carries_are_empty());
|
||||
let ct_res = sks.neg_parallelized(&ctxt);
|
||||
let tmp = sks.neg_parallelized(&ctxt);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
|
||||
// Decrypt the result
|
||||
let dec: u64 = cks.decrypt(&ct_tmp);
|
||||
let dec: u64 = cks.decrypt(&ct_res);
|
||||
|
||||
// Check the correctness
|
||||
let clear_result = clear.wrapping_neg() % modulus;
|
||||
@@ -1022,8 +1027,10 @@ fn integer_default_sub(param: Parameters) {
|
||||
|
||||
//subtract multiple times to raise the degree
|
||||
for _ in 0..NB_TEST_SMALLER {
|
||||
let tmp = sks.sub_parallelized(&res, &ctxt_2);
|
||||
res = sks.sub_parallelized(&res, &ctxt_2);
|
||||
assert!(res.block_carries_are_empty());
|
||||
assert_eq!(res, tmp);
|
||||
clear = (clear.wrapping_sub(clear2)) % modulus;
|
||||
// println!("clear = {}, clear2 = {}", clear, cks.decrypt(&res));
|
||||
}
|
||||
@@ -1057,7 +1064,6 @@ fn integer_unchecked_block_mul(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ct_one = cks.encrypt_one_block(clear_1);
|
||||
|
||||
// add the two ciphertexts
|
||||
let ct_res = sks.unchecked_block_mul_parallelized(&ct_zero, &ct_one, 0);
|
||||
|
||||
// decryption of ct_res
|
||||
@@ -1133,8 +1139,10 @@ fn integer_default_block_mul(param: Parameters) {
|
||||
res = sks.block_mul_parallelized(&res, &ctxt_2, 0);
|
||||
assert!(res.block_carries_are_empty());
|
||||
for _ in 0..5 {
|
||||
let tmp = sks.block_mul_parallelized(&res, &ctxt_2, 0);
|
||||
res = sks.block_mul_parallelized(&res, &ctxt_2, 0);
|
||||
assert!(res.block_carries_are_empty());
|
||||
assert_eq!(res, tmp);
|
||||
clear = (clear * clear2) % modulus;
|
||||
}
|
||||
let dec: u64 = cks.decrypt(&res);
|
||||
@@ -1211,8 +1219,10 @@ fn integer_default_mul(param: Parameters) {
|
||||
res = sks.mul_parallelized(&res, &ctxt_2);
|
||||
assert!(res.block_carries_are_empty());
|
||||
for _ in 0..5 {
|
||||
let tmp = sks.mul_parallelized(&res, &ctxt_2);
|
||||
res = sks.mul_parallelized(&res, &ctxt_2);
|
||||
assert!(res.block_carries_are_empty());
|
||||
assert_eq!(res, tmp);
|
||||
clear = (clear * clear2) % modulus;
|
||||
}
|
||||
let dec: u64 = cks.decrypt(&res);
|
||||
@@ -1245,7 +1255,6 @@ fn integer_smart_scalar_add(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let mut ctxt_0 = cks.encrypt(clear_0);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.smart_scalar_add_parallelized(&mut ctxt_0, clear_1);
|
||||
|
||||
clear = (clear_0 + clear_1) % modulus;
|
||||
@@ -1287,7 +1296,6 @@ fn integer_default_scalar_add(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_0 = cks.encrypt(clear_0);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.scalar_add_parallelized(&ctxt_0, clear_1);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
|
||||
@@ -1296,8 +1304,10 @@ fn integer_default_scalar_add(param: Parameters) {
|
||||
// println!("clear_0 = {}, clear_1 = {}", clear_0, clear_1);
|
||||
//add multiple times to raise the degree
|
||||
for _ in 0..NB_TEST_SMALLER {
|
||||
let tmp = sks.scalar_add_parallelized(&ct_res, clear_1);
|
||||
ct_res = sks.scalar_add_parallelized(&ct_res, clear_1);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
clear = (clear + clear_1) % modulus;
|
||||
|
||||
// decryption of ct_res
|
||||
@@ -1331,7 +1341,6 @@ fn integer_smart_scalar_sub(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let mut ctxt_0 = cks.encrypt(clear_0);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.smart_scalar_sub_parallelized(&mut ctxt_0, clear_1);
|
||||
|
||||
clear = (clear_0 - clear_1) % modulus;
|
||||
@@ -1373,7 +1382,6 @@ fn integer_default_scalar_sub(param: Parameters) {
|
||||
// encryption of an integer
|
||||
let ctxt_0 = cks.encrypt(clear_0);
|
||||
|
||||
// add the two ciphertexts
|
||||
let mut ct_res = sks.scalar_sub_parallelized(&ctxt_0, clear_1);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
|
||||
@@ -1382,8 +1390,10 @@ fn integer_default_scalar_sub(param: Parameters) {
|
||||
// println!("clear_0 = {}, clear_1 = {}", clear_0, clear_1);
|
||||
//add multiple times to raise the degree
|
||||
for _ in 0..NB_TEST_SMALLER {
|
||||
let tmp = sks.scalar_sub_parallelized(&ct_res, clear_1);
|
||||
ct_res = sks.scalar_sub_parallelized(&ct_res, clear_1);
|
||||
assert!(ct_res.block_carries_are_empty());
|
||||
assert_eq!(ct_res, tmp);
|
||||
clear = (clear.wrapping_sub(clear_1)) % modulus;
|
||||
|
||||
// decryption of ct_res
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Welcome to the TFHR-rs API documentation!
|
||||
//! Welcome to the TFHE-rs API documentation!
|
||||
//!
|
||||
//! TFHE-rs is a fully homomorphic encryption (FHE) library that implements Zama's variant of TFHE.
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ pub trait PBSOrderMarker: seal::Sealed + Debug + Clone + Copy + Send + Sync {
|
||||
fn pbs_order() -> PBSOrder;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct KeyswitchBootstrap;
|
||||
|
||||
impl PBSOrderMarker for KeyswitchBootstrap {
|
||||
@@ -118,7 +118,7 @@ impl Degree {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[must_use]
|
||||
pub struct CiphertextBase<OpOrder: PBSOrderMarker> {
|
||||
pub ct: LweCiphertextOwned<u64>,
|
||||
|
||||
Reference in New Issue
Block a user