feat: atomic_pattern optimizer, with v0_parameters table generation

Resolve #11
This commit is contained in:
rudy
2022-03-03 16:20:59 +01:00
committed by rudy-6-4
parent 9689e52097
commit b7a7264257
12 changed files with 1055 additions and 53 deletions

View File

@@ -44,7 +44,7 @@ jobs:
RUSTFLAGS: -D warnings
with:
command: build
args: ${{ env.CARGO_ARGS }}
args: ${{ env.CARGO_ARGS }} --lib --examples --tests
- name: cargo clippy
uses: actions-rs/cargo@v1
@@ -56,4 +56,4 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: ${{ env.CARGO_ARGS }} --no-fail-fast
args: ${{ env.CARGO_ARGS }} --no-fail-fast --all-targets

View File

@@ -10,7 +10,15 @@ git-fetch-with-cli = true
[dependencies]
concrete-commons = { git = "ssh://git@github.com/zama-ai/concrete_internal.git", branch = "fix/optimizer_compat" }
concrete-npe = { git = "ssh://git@github.com/zama-ai/concrete_internal.git", branch = "fix/optimizer_compat" }
statrs = "0.15.0"
[dev-dependencies]
approx = "0.5.1"
pretty_assertions = "1"
clap = { version = "3.1.2", features = ["derive"] }
rayon-cond = "0.2" # to avoid rayon code coloring
# pprof = { version = "0.4", features = ["flamegraph"] }
rayon = "1.5.1"
text-diff = "0.4.0"
[[example]]
name = "v0_parameters"

View File

@@ -0,0 +1,240 @@
{ /* 6.3e-5 errors */
{ /* precision 1 */
/* 0 */ V0Parameter( 1, 10, 538, 2, 8, 3, 3), // 46 mops, 5.9e-5 errors
/* 1 */ V0Parameter( 1, 10, 514, 2, 8, 5, 2), // 46 mops, 5.4e-5 errors
/* 2 */ V0Parameter( 1, 10, 519, 2, 8, 5, 2), // 46 mops, 6.0e-5 errors
/* 3 */ V0Parameter( 1, 10, 515, 3, 6, 5, 2), // 59 mops, 4.9e-5 errors
/* 4 */ V0Parameter( 1, 10, 523, 3, 6, 5, 2), // 60 mops, 5.4e-5 errors
/* 5 */ V0Parameter( 1, 11, 563, 1, 23, 3, 3), // 71 mops, 5.4e-5 errors
/* 6 */ V0Parameter( 1, 11, 563, 1, 23, 3, 3), // 71 mops, 5.5e-5 errors
/* 7 */ V0Parameter( 1, 11, 563, 1, 23, 3, 3), // 71 mops, 5.8e-5 errors
/* 8 */ V0Parameter( 1, 11, 564, 1, 23, 3, 3), // 71 mops, 5.8e-5 errors
/* 9 */ V0Parameter( 1, 11, 569, 1, 23, 3, 3), // 72 mops, 5.5e-5 errors
/* 10 */ V0Parameter( 1, 11, 555, 1, 23, 5, 2), // 75 mops, 5.7e-5 errors
/* 11 */ V0Parameter( 1, 11, 532, 2, 16, 5, 2), // 101 mops, 6.2e-5 errors
/* 12 */ V0Parameter( 1, 11, 532, 2, 16, 5, 2), // 101 mops, 6.2e-5 errors
/* 13 */ V0Parameter( 1, 11, 532, 2, 16, 5, 2), // 101 mops, 6.3e-5 errors
/* 14 */ V0Parameter( 1, 11, 533, 2, 16, 5, 2), // 101 mops, 4.9e-5 errors
/* 15 */ V0Parameter( 1, 11, 533, 2, 16, 5, 2), // 101 mops, 5.5e-5 errors
/* 16 */ V0Parameter( 1, 11, 535, 2, 16, 5, 2), // 102 mops, 5.3e-5 errors
/* 17 */ V0Parameter( 1, 11, 544, 2, 16, 5, 2), // 103 mops, 5.6e-5 errors
/* 18 */ V0Parameter( 1, 11, 533, 3, 12, 5, 2), // 130 mops, 5.0e-5 errors
/* 19 */ V0Parameter( 1, 11, 533, 3, 12, 5, 2), // 130 mops, 6.0e-5 errors
/* 20 */ V0Parameter( 1, 11, 536, 3, 12, 5, 2), // 131 mops, 5.6e-5 errors
/* 21 */ V0Parameter( 1, 11, 552, 3, 12, 5, 2), // 135 mops, 5.7e-5 errors
/* 22 */ V0Parameter( 1, 11, 536, 4, 9, 5, 2), // 160 mops, 6.1e-5 errors
/* 23 */ V0Parameter( 1, 11, 555, 4, 9, 5, 2), // 166 mops, 5.7e-5 errors
/* 24 */ V0Parameter( 1, 11, 539, 5, 8, 5, 2), // 191 mops, 5.2e-5 errors
/* 25 */ V0Parameter( 1, 11, 593, 5, 8, 5, 2), // 209 mops, 6.2e-5 errors
/* 26 */ V0Parameter( 1, 11, 541, 7, 6, 5, 2), // 251 mops, 6.3e-5 errors
/* 27 */ V0Parameter( 1, 11, 569, 8, 5, 5, 2), // 294 mops, 6.1e-5 errors
/* 28 */ V0Parameter( 1, 11, 549, 11, 4, 5, 2), // 374 mops, 5.5e-5 errors
/* 29 */ V0Parameter( 1, 11, 559, 15, 3, 5, 2), // 503 mops, 5.8e-5 errors
/* 30 */ V0Parameter( 1, 11, 542, 44, 1, 10, 1), // 1358 mops, 6.0e-5 errors
/* 31 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
},
{ /* precision 2 */
/* 0 */ V0Parameter( 1, 10, 557, 2, 8, 5, 2), // 49 mops, 5.2e-5 errors
/* 1 */ V0Parameter( 1, 10, 564, 2, 8, 5, 2), // 50 mops, 5.7e-5 errors
/* 2 */ V0Parameter( 1, 10, 558, 3, 6, 5, 2), // 63 mops, 5.1e-5 errors
/* 3 */ V0Parameter( 1, 10, 569, 3, 6, 5, 2), // 65 mops, 5.7e-5 errors
/* 4 */ V0Parameter( 1, 11, 579, 1, 23, 5, 2), // 78 mops, 6.0e-5 errors
/* 5 */ V0Parameter( 1, 11, 579, 1, 23, 5, 2), // 78 mops, 6.1e-5 errors
/* 6 */ V0Parameter( 1, 11, 580, 1, 23, 5, 2), // 78 mops, 5.3e-5 errors
/* 7 */ V0Parameter( 1, 11, 581, 1, 23, 5, 2), // 78 mops, 5.4e-5 errors
/* 8 */ V0Parameter( 1, 11, 585, 1, 23, 5, 2), // 78 mops, 6.3e-5 errors
/* 9 */ V0Parameter( 1, 11, 639, 1, 23, 5, 2), // 85 mops, 6.3e-5 errors
/* 10 */ V0Parameter( 1, 11, 579, 2, 16, 5, 2), // 109 mops, 6.0e-5 errors
/* 11 */ V0Parameter( 1, 11, 579, 2, 16, 5, 2), // 109 mops, 6.0e-5 errors
/* 12 */ V0Parameter( 1, 11, 579, 2, 16, 5, 2), // 109 mops, 6.1e-5 errors
/* 13 */ V0Parameter( 1, 11, 579, 2, 16, 5, 2), // 109 mops, 6.3e-5 errors
/* 14 */ V0Parameter( 1, 11, 580, 2, 16, 5, 2), // 109 mops, 5.9e-5 errors
/* 15 */ V0Parameter( 1, 11, 583, 2, 16, 5, 2), // 110 mops, 5.7e-5 errors
/* 16 */ V0Parameter( 1, 11, 599, 2, 16, 5, 2), // 113 mops, 6.3e-5 errors
/* 17 */ V0Parameter( 1, 11, 580, 3, 12, 5, 2), // 141 mops, 5.2e-5 errors
/* 18 */ V0Parameter( 1, 11, 581, 3, 12, 5, 2), // 141 mops, 5.3e-5 errors
/* 19 */ V0Parameter( 1, 11, 585, 3, 12, 5, 2), // 142 mops, 5.7e-5 errors
/* 20 */ V0Parameter( 1, 11, 622, 3, 12, 5, 2), // 151 mops, 6.3e-5 errors
/* 21 */ V0Parameter( 1, 11, 585, 4, 9, 5, 2), // 174 mops, 6.3e-5 errors
/* 22 */ V0Parameter( 1, 11, 639, 4, 9, 5, 2), // 190 mops, 6.3e-5 errors
/* 23 */ V0Parameter( 1, 11, 589, 5, 8, 5, 2), // 208 mops, 6.1e-5 errors
/* 24 */ V0Parameter( 1, 11, 591, 6, 7, 5, 2), // 241 mops, 6.3e-5 errors
/* 25 */ V0Parameter( 1, 11, 595, 7, 6, 5, 2), // 275 mops, 5.7e-5 errors
/* 26 */ V0Parameter( 1, 11, 627, 8, 5, 11, 2), // 338 mops, 6.3e-5 errors
/* 27 */ V0Parameter( 1, 11, 611, 11, 4, 5, 2), // 416 mops, 6.2e-5 errors
/* 28 */ V0Parameter( 1, 11, 617, 15, 3, 11, 2), // 569 mops, 5.8e-5 errors
/* 29 */ V0Parameter( 1, 11, 600, 44, 1, 22, 1), // 1531 mops, 5.8e-5 errors
/* 30 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 31 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
},
{ /* precision 3 */
/* 0 */ V0Parameter( 1, 10, 673, 2, 8, 5, 4), // 59 mops, 5.8e-5 errors
/* 1 */ V0Parameter( 1, 10, 641, 3, 6, 5, 2), // 72 mops, 6.3e-5 errors
/* 2 */ V0Parameter( 1, 10, 649, 3, 6, 7, 3), // 76 mops, 6.2e-5 errors
/* 3 */ V0Parameter( 1, 11, 679, 1, 23, 5, 4), // 90 mops, 6.2e-5 errors
/* 4 */ V0Parameter( 1, 11, 679, 1, 23, 5, 4), // 90 mops, 6.3e-5 errors
/* 5 */ V0Parameter( 1, 11, 680, 1, 23, 5, 4), // 90 mops, 5.0e-5 errors
/* 6 */ V0Parameter( 1, 11, 681, 1, 23, 5, 4), // 90 mops, 4.8e-5 errors
/* 7 */ V0Parameter( 1, 11, 684, 1, 23, 5, 4), // 90 mops, 5.9e-5 errors
/* 8 */ V0Parameter( 1, 11, 710, 1, 23, 5, 4), // 94 mops, 6.0e-5 errors
/* 9 */ V0Parameter( 1, 11, 679, 2, 16, 5, 4), // 127 mops, 6.2e-5 errors
/* 10 */ V0Parameter( 1, 11, 679, 2, 16, 5, 4), // 127 mops, 6.2e-5 errors
/* 11 */ V0Parameter( 1, 11, 679, 2, 16, 5, 4), // 127 mops, 6.3e-5 errors
/* 12 */ V0Parameter( 1, 11, 680, 2, 16, 5, 4), // 127 mops, 4.8e-5 errors
/* 13 */ V0Parameter( 1, 11, 680, 2, 16, 5, 4), // 127 mops, 5.7e-5 errors
/* 14 */ V0Parameter( 1, 11, 682, 2, 16, 5, 4), // 127 mops, 6.0e-5 errors
/* 15 */ V0Parameter( 1, 11, 694, 2, 16, 5, 4), // 130 mops, 5.7e-5 errors
/* 16 */ V0Parameter( 1, 11, 652, 3, 12, 7, 3), // 163 mops, 4.8e-5 errors
/* 17 */ V0Parameter( 1, 11, 652, 3, 12, 7, 3), // 163 mops, 6.0e-5 errors
/* 18 */ V0Parameter( 1, 11, 655, 3, 12, 7, 3), // 164 mops, 6.3e-5 errors
/* 19 */ V0Parameter( 1, 11, 675, 3, 12, 7, 3), // 168 mops, 6.2e-5 errors
/* 20 */ V0Parameter( 1, 11, 656, 4, 9, 7, 3), // 200 mops, 5.4e-5 errors
/* 21 */ V0Parameter( 1, 11, 679, 4, 9, 7, 3), // 206 mops, 6.3e-5 errors
/* 22 */ V0Parameter( 1, 11, 659, 5, 8, 7, 3), // 237 mops, 5.1e-5 errors
/* 23 */ V0Parameter( 1, 11, 660, 6, 7, 7, 3), // 273 mops, 5.7e-5 errors
/* 24 */ V0Parameter( 1, 11, 638, 7, 6, 11, 2), // 309 mops, 6.2e-5 errors
/* 25 */ V0Parameter( 1, 11, 675, 8, 5, 11, 2), // 364 mops, 6.3e-5 errors
/* 26 */ V0Parameter( 1, 11, 647, 11, 4, 11, 2), // 455 mops, 5.7e-5 errors
/* 27 */ V0Parameter( 1, 11, 660, 15, 3, 11, 2), // 608 mops, 5.9e-5 errors
/* 28 */ V0Parameter( 1, 11, 641, 44, 1, 22, 1), // 1635 mops, 6.3e-5 errors
/* 29 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 30 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 31 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
},
{ /* precision 4 */
/* 0 */ V0Parameter( 1, 10, 691, 3, 6, 7, 3), // 80 mops, 6.2e-5 errors
/* 1 */ V0Parameter( 1, 10, 731, 3, 6, 7, 3), // 85 mops, 6.3e-5 errors
/* 2 */ V0Parameter( 1, 11, 720, 1, 23, 5, 4), // 95 mops, 5.5e-5 errors
/* 3 */ V0Parameter( 1, 11, 720, 1, 23, 5, 4), // 95 mops, 5.6e-5 errors
/* 4 */ V0Parameter( 1, 11, 720, 1, 23, 5, 4), // 95 mops, 6.0e-5 errors
/* 5 */ V0Parameter( 1, 11, 721, 1, 23, 5, 4), // 95 mops, 6.0e-5 errors
/* 6 */ V0Parameter( 1, 11, 726, 1, 23, 5, 4), // 95 mops, 5.4e-5 errors
/* 7 */ V0Parameter( 1, 11, 764, 1, 23, 5, 4), // 100 mops, 6.3e-5 errors
/* 8 */ V0Parameter( 1, 11, 720, 2, 16, 5, 4), // 134 mops, 5.5e-5 errors
/* 9 */ V0Parameter( 1, 11, 720, 2, 16, 5, 4), // 134 mops, 5.5e-5 errors
/* 10 */ V0Parameter( 1, 11, 720, 2, 16, 5, 4), // 134 mops, 5.6e-5 errors
/* 11 */ V0Parameter( 1, 11, 720, 2, 16, 5, 4), // 134 mops, 5.8e-5 errors
/* 12 */ V0Parameter( 1, 11, 721, 2, 16, 5, 4), // 134 mops, 5.3e-5 errors
/* 13 */ V0Parameter( 1, 11, 723, 2, 16, 5, 4), // 135 mops, 6.2e-5 errors
/* 14 */ V0Parameter( 1, 11, 738, 2, 16, 5, 4), // 137 mops, 5.9e-5 errors
/* 15 */ V0Parameter( 1, 11, 692, 3, 12, 7, 3), // 172 mops, 5.6e-5 errors
/* 16 */ V0Parameter( 1, 11, 693, 3, 12, 7, 3), // 173 mops, 5.4e-5 errors
/* 17 */ V0Parameter( 1, 11, 697, 3, 12, 7, 3), // 174 mops, 5.3e-5 errors
/* 18 */ V0Parameter( 1, 11, 724, 3, 12, 7, 3), // 180 mops, 6.1e-5 errors
/* 19 */ V0Parameter( 1, 11, 697, 4, 9, 7, 3), // 212 mops, 6.0e-5 errors
/* 20 */ V0Parameter( 1, 11, 731, 4, 9, 7, 3), // 222 mops, 6.2e-5 errors
/* 21 */ V0Parameter( 1, 11, 700, 5, 8, 7, 3), // 251 mops, 6.3e-5 errors
/* 22 */ V0Parameter( 1, 11, 702, 6, 7, 7, 3), // 290 mops, 6.1e-5 errors
/* 23 */ V0Parameter( 1, 11, 681, 7, 6, 11, 2), // 329 mops, 5.8e-5 errors
/* 24 */ V0Parameter( 1, 11, 686, 9, 5, 11, 2), // 407 mops, 5.6e-5 errors
/* 25 */ V0Parameter( 1, 11, 692, 11, 4, 11, 2), // 486 mops, 6.2e-5 errors
/* 26 */ V0Parameter( 1, 11, 715, 15, 3, 11, 2), // 658 mops, 6.3e-5 errors
/* 27 */ V0Parameter( 1, 11, 690, 44, 1, 22, 1), // 1760 mops, 6.2e-5 errors
/* 28 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 29 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 30 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 31 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
},
{ /* precision 5 */
/* 0 */ V0Parameter( 1, 11, 774, 1, 23, 5, 4), // 101 mops, 5.6e-5 errors
/* 1 */ V0Parameter( 1, 11, 774, 1, 23, 5, 4), // 101 mops, 5.7e-5 errors
/* 2 */ V0Parameter( 1, 11, 774, 1, 23, 5, 4), // 101 mops, 5.8e-5 errors
/* 3 */ V0Parameter( 1, 11, 774, 1, 23, 5, 4), // 101 mops, 6.2e-5 errors
/* 4 */ V0Parameter( 1, 11, 776, 1, 23, 5, 4), // 101 mops, 6.2e-5 errors
/* 5 */ V0Parameter( 1, 11, 787, 1, 23, 5, 4), // 103 mops, 6.0e-5 errors
/* 6 */ V0Parameter( 1, 11, 774, 2, 16, 5, 4), // 144 mops, 5.6e-5 errors
/* 7 */ V0Parameter( 1, 11, 774, 2, 16, 5, 4), // 144 mops, 5.6e-5 errors
/* 8 */ V0Parameter( 1, 11, 774, 2, 16, 5, 4), // 144 mops, 5.7e-5 errors
/* 9 */ V0Parameter( 1, 11, 774, 2, 16, 5, 4), // 144 mops, 5.7e-5 errors
/* 10 */ V0Parameter( 1, 11, 774, 2, 16, 5, 4), // 144 mops, 6.0e-5 errors
/* 11 */ V0Parameter( 1, 11, 775, 2, 16, 5, 4), // 144 mops, 6.2e-5 errors
/* 12 */ V0Parameter( 1, 11, 781, 2, 16, 5, 4), // 145 mops, 6.0e-5 errors
/* 13 */ V0Parameter( 1, 11, 745, 3, 12, 7, 3), // 185 mops, 5.6e-5 errors
/* 14 */ V0Parameter( 1, 11, 745, 3, 12, 7, 3), // 185 mops, 6.0e-5 errors
/* 15 */ V0Parameter( 1, 11, 747, 3, 12, 7, 3), // 186 mops, 5.8e-5 errors
/* 16 */ V0Parameter( 1, 11, 755, 3, 12, 7, 3), // 187 mops, 6.2e-5 errors
/* 17 */ V0Parameter( 1, 11, 747, 4, 9, 7, 3), // 226 mops, 6.0e-5 errors
/* 18 */ V0Parameter( 1, 11, 756, 4, 9, 7, 3), // 229 mops, 6.3e-5 errors
/* 19 */ V0Parameter( 1, 11, 748, 5, 8, 7, 3), // 268 mops, 6.2e-5 errors
/* 20 */ V0Parameter( 1, 11, 766, 5, 8, 7, 3), // 274 mops, 6.0e-5 errors
/* 21 */ V0Parameter( 1, 11, 773, 6, 7, 7, 3), // 319 mops, 6.0e-5 errors
/* 22 */ V0Parameter( 1, 11, 756, 7, 6, 11, 2), // 365 mops, 6.2e-5 errors
/* 23 */ V0Parameter( 1, 11, 729, 11, 4, 11, 2), // 512 mops, 6.0e-5 errors
/* 24 */ V0Parameter( 1, 11, 745, 14, 3, 11, 2), // 645 mops, 6.1e-5 errors
/* 25 */ V0Parameter( 1, 11, 746, 22, 2, 11, 2), // 972 mops, 6.2e-5 errors
/* 26 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 27 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 28 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 29 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 30 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 31 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
},
{ /* precision 6 */
/* 0 */ V0Parameter( 1, 12, 833, 1, 23, 5, 4), // 230 mops, 5.9e-5 errors
/* 1 */ V0Parameter( 1, 12, 833, 1, 23, 5, 4), // 230 mops, 6.2e-5 errors
/* 2 */ V0Parameter( 1, 12, 835, 1, 23, 5, 4), // 231 mops, 5.9e-5 errors
/* 3 */ V0Parameter( 1, 12, 843, 1, 23, 5, 4), // 233 mops, 5.9e-5 errors
/* 4 */ V0Parameter( 1, 12, 833, 2, 15, 5, 4), // 328 mops, 5.8e-5 errors
/* 5 */ V0Parameter( 1, 12, 833, 2, 15, 5, 4), // 328 mops, 5.8e-5 errors
/* 6 */ V0Parameter( 1, 12, 833, 2, 15, 5, 4), // 328 mops, 5.8e-5 errors
/* 7 */ V0Parameter( 1, 12, 833, 2, 15, 5, 4), // 328 mops, 5.8e-5 errors
/* 8 */ V0Parameter( 1, 12, 833, 2, 15, 5, 4), // 328 mops, 6.1e-5 errors
/* 9 */ V0Parameter( 1, 12, 834, 2, 15, 5, 4), // 328 mops, 6.2e-5 errors
/* 10 */ V0Parameter( 1, 12, 840, 2, 15, 5, 4), // 331 mops, 6.0e-5 errors
/* 11 */ V0Parameter( 1, 12, 843, 2, 15, 11, 2), // 371 mops, 6.3e-5 errors
/* 12 */ V0Parameter( 1, 12, 804, 3, 12, 7, 3), // 424 mops, 6.2e-5 errors
/* 13 */ V0Parameter( 1, 12, 807, 3, 12, 7, 3), // 425 mops, 5.6e-5 errors
/* 14 */ V0Parameter( 1, 12, 818, 3, 12, 7, 3), // 431 mops, 6.3e-5 errors
/* 15 */ V0Parameter( 1, 12, 806, 4, 9, 7, 3), // 519 mops, 5.6e-5 errors
/* 16 */ V0Parameter( 1, 12, 813, 4, 9, 7, 3), // 524 mops, 5.8e-5 errors
/* 17 */ V0Parameter( 1, 12, 809, 5, 8, 7, 3), // 616 mops, 5.8e-5 errors
/* 18 */ V0Parameter( 1, 12, 839, 5, 8, 7, 3), // 638 mops, 6.2e-5 errors
/* 19 */ V0Parameter( 1, 12, 828, 6, 7, 11, 2), // 753 mops, 6.3e-5 errors
/* 20 */ V0Parameter( 1, 12, 793, 8, 5, 11, 2), // 908 mops, 6.2e-5 errors
/* 21 */ V0Parameter( 1, 12, 791, 11, 4, 11, 2), // 1184 mops, 6.3e-5 errors
/* 22 */ V0Parameter( 1, 12, 805, 14, 3, 11, 2), // 1487 mops, 6.0e-5 errors
/* 23 */ V0Parameter( 1, 12, 821, 22, 2, 11, 2), // 2286 mops, 6.3e-5 errors
/* 24 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 25 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 26 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 27 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 28 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 29 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 30 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 31 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
},
{ /* precision 7 */
/* 0 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 1 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 2 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 3 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 4 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 5 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 6 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 7 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 8 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 9 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 10 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 11 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 12 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 13 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 14 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 15 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 16 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 17 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 18 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 19 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 20 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 21 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 22 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 23 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 24 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 25 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 26 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 27 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 28 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 29 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 30 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
/* 31 : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),
},
}

154
examples/v0_parameters.rs Normal file
View File

@@ -0,0 +1,154 @@
use clap::Parser;
use rayon_cond::CondIterator;
use concrete_optimizer::optimisation::atomic_pattern as optimize_atomic_pattern;
const _4_SIGMA: f64 = 1.0 - 0.999_936_657_516;
/// Simple program to greet a person
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
#[clap(long, default_value_t = 1, help = "1..16")]
min_precision: u64,
#[clap(long, default_value_t = 7, help = "1..16")]
max_precision: u64,
#[clap(long, default_value_t = _4_SIGMA)]
p_error: f64,
#[clap(long, default_value_t = 128, help = "Only 128 is supported")]
security_level: u64,
#[clap(long, default_value_t = 10, help = "8..16")]
min_log_poly_size: u64,
#[clap(long, default_value_t = 12, help = "8..16")]
max_log_poly_size: u64,
#[clap(long, default_value_t = 1, help = "EXPERIMENTAL")]
min_glwe_dim: u64,
#[clap(long, default_value_t = 1, help = "EXPERIMENTAL")]
// only usefull for very low precision, some parts are not correcte if used with k > 1
max_glwe_dim: u64,
#[clap(long, default_value_t = 512)]
min_intern_lwe_dim: u64,
#[clap(long, default_value_t = 1024)]
max_intern_lwe_dim: u64, // 16bits needs around 1300
#[clap(long, default_value_t = 4096)]
sum_size: u64,
#[clap(long)]
no_parallelize: bool,
}
fn main() {
let args = Args::parse();
let sum_size = args.sum_size;
let p_error = args.p_error;
let security_level = args.security_level;
if security_level != 128 {
panic!("Only 128bits of security is supported")
}
let glwe_log_polynomial_sizes: Vec<_> =
(args.min_log_poly_size..=args.max_log_poly_size).collect();
let glwe_dimensions: Vec<_> = (args.min_glwe_dim..=args.max_glwe_dim).collect();
let internal_lwe_dimensions: Vec<_> =
(args.min_intern_lwe_dim..=args.max_intern_lwe_dim).collect();
let precisions = args.min_precision..=args.max_precision;
let manps = 0..=31;
// let guard = pprof::ProfilerGuard::new(100).unwrap();
let precisions_iter = CondIterator::new(precisions.clone(), !args.no_parallelize);
#[rustfmt::skip]
let all_results = precisions_iter.map(|precision| {
let mut last_solution = None;
manps.clone().map(|manp| {
let noise_scale = 2_f64.powi(manp);
let result = optimize_atomic_pattern::optimise_one::<u64>(
sum_size,
precision,
security_level,
noise_scale,
p_error,
&glwe_log_polynomial_sizes,
&glwe_dimensions,
&internal_lwe_dimensions,
last_solution, // 33% gains
);
last_solution = result.best_solution;
result
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
/*
if let Ok(report) = guard.report().build() {
let file = std::fs::File::create("flamegraph.svg").unwrap();
let mut options = pprof::flamegraph::Options::default();
options.image_width = Some(32000);
report.flamegraph_with_options(file, &mut options).unwrap();
};
*/
println!("{{ /* {:1.1e} errors */", p_error);
for (precision_i, precision) in precisions.enumerate() {
println!("{{ /* precision {:2} */", precision);
for (manp_i, manp) in manps.clone().enumerate() {
let solution = all_results[precision_i][manp_i].best_solution;
if let Some(solution) = solution {
println!(" /* {:2} */ V0Parameter({:2}, {:2}, {:4}, {:2}, {:2}, {:2}, {:2}), \t\t // {:4} mops, {:1.1e} errors",
manp, solution.glwe_dimension, (solution.glwe_polynomial_size as f64).log2() as u64,
solution.internal_ks_output_lwe_dimension,
solution.br_decomposition_level_count, solution.br_decomposition_base_log,
solution.ks_decomposition_level_count, solution.ks_decomposition_base_log,
(solution.complexity / (1024.0 * 1024.0)) as u64,
solution.p_error
)
} else {
println!(
" /* {:2} : NO SOLUTION */ V0Parameter(0,0,0,0,0,0,0),",
manp
);
}
}
println!(" }},");
}
println!("}}");
}
#[cfg(test)]
mod tests {
#[test]
fn test_reference_output() {
const REF_FILE: &str = "examples/v0_parameters.ref-02-03-2022";
const V0_PARAMETERS_EXE: &str = "target/debug/examples/v0_parameters";
const CMP_LINES: &str = "\n";
const EXACT_EQUALITY: i32 = 0;
let _ = std::process::Command::new("cargo")
.args(["build", "-q", "--example", "v0_parameters"])
.status()
.expect("Can't build");
assert!(std::path::Path::new(V0_PARAMETERS_EXE).exists());
let actual_output = std::process::Command::new(V0_PARAMETERS_EXE)
.output()
.expect("failed to execute process");
let actual_output = std::str::from_utf8(&actual_output.stdout).expect("Bad content");
let expected_output = std::fs::read_to_string(REF_FILE).expect("Can't read reference file");
text_diff::assert_diff(&expected_output, &actual_output, CMP_LINES, EXACT_EQUALITY);
}
}

View File

@@ -18,5 +18,7 @@ pub mod computing_cost;
pub mod global_parameters;
pub mod graph;
pub mod noise_estimator;
pub mod optimisation;
pub mod parameters;
pub mod security;
pub mod weight;

View File

@@ -0,0 +1,40 @@
pub fn sigma_scale_of_error_probability(p_error: f64) -> f64 {
// https://en.wikipedia.org/wiki/Error_function#Applications
let p_in = 1.0 - p_error;
statrs::function::erf::erf_inv(p_in) * 2_f64.sqrt()
}
pub fn error_probability_of_sigma_scale(sigma_scale: f64) -> f64 {
// https://en.wikipedia.org/wiki/Error_function#Applications
1.0 - statrs::function::erf::erf(sigma_scale / 2_f64.sqrt())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sigmas() {
// https://en.wikipedia.org/wiki/Normal_distribution#Standard_deviation_and_coverage
let reference = &[
0.682_689_492_137, // +- 1 sigma
0.954_499_736_104, // 2
0.997_300_203_937, // ...
0.999_936_657_516,
0.999_999_426_697,
];
for (i, &p_in) in reference.iter().enumerate() {
let p_out = 1.0 - p_in;
let expected_scale = (i + 1) as f64;
approx::assert_relative_eq!(
expected_scale,
sigma_scale_of_error_probability(p_out),
max_relative = 1e-8
);
approx::assert_relative_eq!(
p_out,
error_probability_of_sigma_scale(sigma_scale_of_error_probability(p_out)),
max_relative = 1e-8
);
}
}
}

View File

@@ -1,3 +1,3 @@
pub mod error;
pub mod operators;
pub mod security;
pub mod utils;

View File

@@ -5,7 +5,7 @@ use concrete_commons::parameters::{
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
};
use super::super::security;
use crate::security;
/// Additional noise generated by the keyswitch step.
pub fn variance_keyswitch<W: UnsignedInteger>(
@@ -30,17 +30,18 @@ pub fn variance_keyswitch<W: UnsignedInteger>(
)
}
/// Compute the variance paramater for `variance_keyswitch`
/// Compute the variance parameter of the keyswitch key.
pub fn variance_ksk(
internal_ks_output_lwe_dimension: u64,
ciphertext_modulus_log: u64,
security_level: u64,
) -> Variance {
let glwe_poly_size = 1;
let glwe_dim = internal_ks_output_lwe_dimension;
security::variance_ksk(
glwe_poly_size,
glwe_dim,
let glwe_polynomial_size = 1;
let glwe_dimension = internal_ks_output_lwe_dimension;
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/keyswitch.py#L13
security::glwe::minimal_variance(
glwe_polynomial_size,
glwe_dimension,
ciphertext_modulus_log,
security_level,
)
@@ -51,8 +52,8 @@ pub fn fft_noise<W: UnsignedInteger>(
internal_ks_output_lwe_dimension: u64, //n_small
glwe_polynomial_size: u64, //N
_glwe_dimension: u64, //k, unused
br_decomposition_level_count: u64, //l(KS)
br_decomposition_base_log: u64, //b(ks)
br_decomposition_level_count: u64,
br_decomposition_base_log: u64,
) -> Variance {
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L25
let n = internal_ks_output_lwe_dimension as f64;
@@ -206,7 +207,8 @@ where
D: DispersionParameter,
W: UnsignedInteger,
{
let variance_bsk = security::variance_bsk(
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L66
let variance_bsk = security::glwe::minimal_variance(
glwe_polynomial_size,
glwe_dimension,
ciphertext_modulus_log,
@@ -288,7 +290,7 @@ mod tests {
let br_decomposition_base_log = 24;
let ciphertext_modulus_log = 64;
let security = 128;
let variance_bsk = security::variance_bsk(
let variance_bsk = security::glwe::minimal_variance(
glwe_polynomial_size,
glwe_dimension,
ciphertext_modulus_log,
@@ -318,7 +320,7 @@ mod tests {
let br_decomposition_base_log = 5;
let ciphertext_modulus_log = 128;
let security = 128;
let variance_bsk = security::variance_bsk(
let variance_bsk = security::glwe::minimal_variance(
glwe_polynomial_size,
glwe_dimension,
ciphertext_modulus_log,

View File

@@ -0,0 +1,586 @@
use concrete_commons::dispersion::{DispersionParameter, Variance};
use concrete_commons::numeric::UnsignedInteger;
use crate::computing_cost::operators::atomic_pattern as complexity_atomic_pattern;
use complexity_atomic_pattern::AtomicPatternComplexity;
use crate::computing_cost::operators::keyswitch_lwe::KeySwitchLWEComplexity;
use crate::computing_cost::operators::pbs::PbsComplexity;
use crate::noise_estimator::error::{
error_probability_of_sigma_scale, sigma_scale_of_error_probability,
};
use crate::noise_estimator::operators::atomic_pattern as noise_atomic_pattern;
use crate::security;
#[rustfmt::skip]
const BR_BL: &[(u64, u64); 35] = &[
(12, 1), (23, 1), (8, 2), (15, 2), (16, 2), (3, 3), (6, 3), (12, 3), (2, 4),
(5, 4), (9, 4), (4, 5), (8, 5), (7, 6), (3, 7), (6, 7), (1, 8), (5, 8), (1, 9),
(5, 9), (2, 10), (4, 10), (2, 11), (4, 11), (3, 14), (3, 15), (1, 21), (2, 21), (1, 22),
(2, 22), (2, 23), (1, 43), (1, 44), (1, 45), (1, 46)
];
#[rustfmt::skip]
const KS_BL: &[(u64, u64); 46] = &[
(5, 1), (12, 1), (26, 1), (31, 1), (4, 2), (8, 2), (17, 2), (21, 2), (3, 3),
(6, 3), (13, 3), (15, 3), (2, 4), (5, 4), (10, 4), (12, 4), (2, 5), (4, 5),
(9, 5), (10, 5), (4, 6), (8, 6), (3, 7), (7, 7), (3, 8), (6, 8), (1, 9), (5, 9), (1, 10),
(5, 10), (2, 11), (2, 12), (4, 12), (4, 13), (3, 16), (3, 17), (1, 22), (1, 23), (2, 24),
(2, 25), (2, 26), (1, 48), (1, 49), (1, 50), (1, 51), (1, 52)
];
fn square(v: f64) -> f64 {
v * v
}
/* enable to debug */
const CHECKS: bool = false;
/* disable to debug */
// Ref time for v0 table 1 thread: 950ms
const CUTS: bool = true; // 80ms
const PARETO_CUTS: bool = true; // 75ms
const CROSS_PARETO_CUTS: bool = PARETO_CUTS && true; // 70ms
#[derive(Debug, Clone, Copy)]
pub struct Solution {
pub input_lwe_dimension: u64, //n_big
pub internal_ks_output_lwe_dimension: u64, //n_small
pub ks_decomposition_level_count: u64, //l(KS)
pub ks_decomposition_base_log: u64, //b(KS)
pub glwe_polynomial_size: u64, //N
pub glwe_dimension: u64, //k
pub br_decomposition_level_count: u64, //l(BR)
pub br_decomposition_base_log: u64, //b(BR)
pub complexity: f64,
pub noise_max: f64,
pub p_error: f64, // error probability
}
// Constants during optimisation of decompositions
struct OptimizationDecompositionsConsts {
kappa: f64,
sum_size: u64,
security_level: u64,
noise_factor: f64,
ciphertext_modulus_log: u64,
keyswitch_decompositions: Vec<(u64, u64)>,
blind_rotate_decompositions: Vec<(u64, u64)>,
variance_max: f64,
}
#[derive(Clone, Copy)]
struct ComplexityNoise {
index: usize,
complexity: f64,
noise: f64,
}
impl ComplexityNoise {
const ZERO: Self = Self {
index: 0,
complexity: 0.0,
noise: 0.0,
};
}
fn blind_rotate_quantities<W: UnsignedInteger>(
consts: &OptimizationDecompositionsConsts,
internal_dim: u64,
glwe_poly_size: u64,
glwe_dim: u64,
cut_complexity: f64,
cut_noise: f64,
) -> Vec<ComplexityNoise> {
let br_decomp_len = consts.blind_rotate_decompositions.len();
let mut quantities = vec![ComplexityNoise::ZERO; br_decomp_len];
let ciphertext_modulus_log = consts.ciphertext_modulus_log;
let security_level = consts.security_level;
let variance_bsk = security::glwe::minimal_variance(
glwe_poly_size,
glwe_dim,
ciphertext_modulus_log,
security_level,
);
let mut increasing_complexity = 0.0;
let mut decreasing_variance = f64::INFINITY;
let mut size = 0;
for (i_br, &(br_b, br_l)) in consts.blind_rotate_decompositions.iter().enumerate() {
let complexity_pbs = complexity_atomic_pattern::DEFAULT.pbs.complexity(
internal_dim,
glwe_poly_size,
glwe_dim,
br_l,
br_b,
consts.ciphertext_modulus_log,
);
if cut_complexity < complexity_pbs && CUTS {
break; // complexity is increasing
}
let base_noise = noise_atomic_pattern::variance_bootstrap::<W>(
internal_dim,
glwe_poly_size,
glwe_dim,
br_l,
br_b,
consts.ciphertext_modulus_log,
variance_bsk,
);
let noise_in = base_noise.get_variance() * square(consts.noise_factor);
if cut_noise < noise_in && CUTS {
continue; // noise is decreasing
}
if decreasing_variance < noise_in && PARETO_CUTS {
// the current case is dominated
continue;
}
let delta_complexity = complexity_pbs - increasing_complexity;
size -= if delta_complexity == 0.0 && PARETO_CUTS {
1 // the previous case is dominated
} else {
0
};
quantities[size] = ComplexityNoise {
index: i_br,
complexity: complexity_pbs,
noise: noise_in,
};
assert!(
0.0 <= delta_complexity,
"blind_rotate_decompositions should be by increasing complexity"
);
increasing_complexity = complexity_pbs;
decreasing_variance = noise_in;
size += 1;
}
assert!(!PARETO_CUTS || size < 64);
quantities.truncate(size);
quantities
}
fn keyswitch_quantities<W: UnsignedInteger>(
consts: &OptimizationDecompositionsConsts,
in_dim: u64,
internal_dim: u64,
cut_complexity: f64,
cut_noise: f64,
) -> Vec<ComplexityNoise> {
let ks_decomp_len = consts.keyswitch_decompositions.len();
let mut quantities = vec![ComplexityNoise::ZERO; ks_decomp_len];
let ciphertext_modulus_log = consts.ciphertext_modulus_log;
let security_level = consts.security_level;
let variance_ksk =
noise_atomic_pattern::variance_ksk(internal_dim, ciphertext_modulus_log, security_level);
let mut increasing_complexity = 0.0;
let mut decreasing_variance = f64::INFINITY;
let mut size = 0;
for (i_ks, &(ks_b, ks_l)) in consts.keyswitch_decompositions.iter().enumerate() {
let complexity_keyswitch = complexity_atomic_pattern::DEFAULT.ks_lwe.complexity(
in_dim,
internal_dim,
ks_l,
ks_b,
ciphertext_modulus_log,
);
if cut_complexity < complexity_keyswitch && CUTS {
break;
}
let noise_keyswitch = noise_atomic_pattern::variance_keyswitch::<W>(
in_dim,
ks_l,
ks_b,
ciphertext_modulus_log,
variance_ksk,
)
.get_variance();
if cut_noise < noise_keyswitch && CUTS {
continue; // noise is decreasing
}
if decreasing_variance < noise_keyswitch && PARETO_CUTS {
// the current case is dominated
continue;
}
let delta_complexity = complexity_keyswitch - increasing_complexity;
size -= if delta_complexity == 0.0 && PARETO_CUTS {
1
} else {
0
};
quantities[size] = ComplexityNoise {
index: i_ks,
complexity: complexity_keyswitch,
noise: noise_keyswitch,
};
assert!(
0.0 <= delta_complexity,
"keyswitch_decompositions should be by increasing complexity"
);
increasing_complexity = complexity_keyswitch;
decreasing_variance = noise_keyswitch;
size += 1;
}
assert!(!PARETO_CUTS || size < 64);
quantities.truncate(size);
quantities
}
pub struct OptimizationState {
pub best_solution: Option<Solution>,
pub count_domain: usize,
}
#[allow(clippy::too_many_lines)]
fn update_state_with_best_decompositions<W: UnsignedInteger>(
state: &mut OptimizationState,
consts: &OptimizationDecompositionsConsts,
internal_dim: u64,
glwe_poly_size: u64,
glwe_dim: u64,
) {
let input_lwe_dimension = glwe_dim * glwe_poly_size;
let noise_modulus_switching =
noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key::<W>(
internal_dim,
glwe_poly_size,
)
.get_variance();
let variance_max = consts.variance_max;
if CUTS && noise_modulus_switching > variance_max {
return;
}
let mut best_complexity = state.best_solution.map_or(f64::INFINITY, |s| s.complexity);
let mut best_variance = state.best_solution.map_or(f64::INFINITY, |s| s.noise_max);
let complexity_multisum = (consts.sum_size * input_lwe_dimension) as f64;
let mut cut_complexity = best_complexity - complexity_multisum;
let mut cut_noise = variance_max - noise_modulus_switching;
let br_quantities = blind_rotate_quantities::<W>(
consts,
internal_dim,
glwe_poly_size,
glwe_dim,
cut_complexity,
cut_noise,
);
if br_quantities.is_empty() {
return;
}
if PARETO_CUTS {
cut_noise -= br_quantities[br_quantities.len() - 1].noise;
cut_complexity -= br_quantities[0].complexity;
}
let ks_quantities = keyswitch_quantities::<W>(
consts,
input_lwe_dimension,
internal_dim,
cut_complexity,
cut_noise,
);
if ks_quantities.is_empty() {
return;
}
let i_max_ks = ks_quantities.len() - 1;
let mut i_current_max_ks = i_max_ks;
for br_quantity in br_quantities {
// increasing complexity, decreasing variance
let noise_in = br_quantity.noise;
let noise_max = noise_in + noise_modulus_switching;
if noise_max > variance_max && CUTS {
continue;
}
let complexity_pbs = br_quantity.complexity;
let complexity = complexity_multisum + complexity_pbs;
if complexity > best_complexity {
// As best can evolves it is complementary to blind_rotate_quantities cuts.
if PARETO_CUTS {
break;
} else if CUTS {
continue;
}
}
for i_ks_pareto in (0..=i_current_max_ks).rev() {
// increasing variance, decreasing complexity
let ks_quantity = ks_quantities[i_ks_pareto];
let noise_keyswitch = ks_quantity.noise;
let noise_max = noise_in + noise_keyswitch + noise_modulus_switching;
let complexity_keyswitch = ks_quantity.complexity;
let complexity = complexity_multisum + complexity_keyswitch + complexity_pbs;
if CHECKS {
assert_checks::<W>(
consts,
internal_dim,
glwe_poly_size,
glwe_dim,
input_lwe_dimension,
ks_quantity,
br_quantity,
noise_max,
complexity_multisum,
complexity,
);
}
if noise_max > variance_max {
if CROSS_PARETO_CUTS {
// the pareto of 2 added pareto is scanned linearly
// but with all cuts, pre-computing => no gain
i_current_max_ks = usize::min(i_ks_pareto + 1, i_max_ks);
break;
// it's compatible with next i_br but with the worst complexity
} else if PARETO_CUTS {
// increasing variance => we can skip all remaining
break;
}
continue;
} else if complexity > best_complexity {
continue;
}
// feasible and at least as good complexity
if complexity < best_complexity || noise_max < best_variance {
let sigma = Variance(variance_max).get_standard_dev() * consts.kappa;
let sigma_scale = sigma / Variance(noise_max).get_standard_dev();
let p_error = error_probability_of_sigma_scale(sigma_scale);
let i_br = br_quantity.index;
let i_ks = ks_quantity.index;
let (br_b, br_l) = consts.blind_rotate_decompositions[i_br];
let (ks_b, ks_l) = consts.keyswitch_decompositions[i_ks];
best_complexity = complexity;
best_variance = noise_max;
state.best_solution = Some(Solution {
input_lwe_dimension,
internal_ks_output_lwe_dimension: internal_dim,
ks_decomposition_level_count: ks_l,
ks_decomposition_base_log: ks_b,
glwe_polynomial_size: glwe_poly_size,
glwe_dimension: glwe_dim,
br_decomposition_level_count: br_l,
br_decomposition_base_log: br_b,
noise_max,
complexity,
p_error,
});
}
}
} // br ks
}
// This function provides reference values with unoptimised code, until we have non regeression tests
#[allow(clippy::float_cmp)]
fn assert_checks<W: UnsignedInteger>(
consts: &OptimizationDecompositionsConsts,
internal_dim: u64,
glwe_poly_size: u64,
glwe_dim: u64,
input_lwe_dimension: u64,
ks_c_n: ComplexityNoise,
br_c_n: ComplexityNoise,
noise_max: f64,
complexity_multisum: f64,
complexity: f64,
) {
let i_ks = ks_c_n.index;
let i_br = br_c_n.index;
let noise_in = br_c_n.noise;
let noise_keyswitch = ks_c_n.noise;
let complexity_keyswitch = ks_c_n.complexity;
let complexity_pbs = br_c_n.complexity;
let ciphertext_modulus_log = consts.ciphertext_modulus_log;
let security_level = consts.security_level;
let (br_b, br_l) = consts.blind_rotate_decompositions[i_br];
let (ks_b, ks_l) = consts.keyswitch_decompositions[i_ks];
let variance_bsk = security::glwe::minimal_variance(
glwe_poly_size,
glwe_dim,
ciphertext_modulus_log,
security_level,
);
let base_noise_ = noise_atomic_pattern::variance_bootstrap::<W>(
internal_dim,
glwe_poly_size,
glwe_dim,
br_l,
br_b,
ciphertext_modulus_log,
variance_bsk,
);
let noise_in_ = base_noise_.get_variance() * square(consts.noise_factor);
let complexity_pbs_ = complexity_atomic_pattern::DEFAULT.pbs.complexity(
internal_dim,
glwe_poly_size,
glwe_dim,
br_l,
br_b,
ciphertext_modulus_log,
);
assert!(complexity_pbs == complexity_pbs_);
assert!(noise_in == noise_in_);
let variance_ksk =
noise_atomic_pattern::variance_ksk(internal_dim, ciphertext_modulus_log, security_level);
let noise_keyswitch_ = noise_atomic_pattern::variance_keyswitch::<W>(
input_lwe_dimension,
ks_l,
ks_b,
ciphertext_modulus_log,
variance_ksk,
)
.get_variance();
let complexity_keyswitch_ = complexity_atomic_pattern::DEFAULT.ks_lwe.complexity(
input_lwe_dimension,
internal_dim,
ks_l,
ks_b,
ciphertext_modulus_log,
);
assert!(complexity_keyswitch == complexity_keyswitch_);
assert!(noise_keyswitch == noise_keyswitch_);
let check_max_noise = noise_atomic_pattern::maximal_noise::<Variance, W>(
Variance(noise_in),
input_lwe_dimension,
internal_dim,
ks_l,
ks_b,
glwe_poly_size,
ciphertext_modulus_log,
security_level,
)
.get_variance();
assert!(f64::abs(noise_max - check_max_noise) / check_max_noise < 0.00000000001);
let check_complexity = complexity_atomic_pattern::DEFAULT.complexity(
consts.sum_size,
input_lwe_dimension,
internal_dim,
ks_l,
ks_b,
glwe_poly_size,
glwe_dim,
br_l,
br_b,
ciphertext_modulus_log,
);
let diff_complexity = f64::abs(complexity - check_complexity) / check_complexity;
if diff_complexity > 0.0001 {
println!(
"{} + {} + {} != {}",
complexity_multisum, complexity_keyswitch, complexity_pbs, check_complexity,
);
}
assert!(diff_complexity < 0.0001);
}
const BITS_CARRY: u64 = 1;
const BITS_PADDING_WITHOUT_NOISE: u64 = 1;
#[allow(clippy::too_many_lines)]
pub fn optimise_one<W: UnsignedInteger>(
sum_size: u64,
precision: u64,
security_level: u64,
noise_factor: f64,
maximum_acceptable_error_probability: f64,
glwe_log_polynomial_sizes: &[u64],
glwe_dimensions: &[u64],
internal_lwe_dimensions: &[u64],
restart_at: Option<Solution>,
) -> OptimizationState {
assert!(0 < precision && precision <= 16);
assert!(security_level == 128);
assert!(1.0 <= noise_factor);
assert!(0.0 < maximum_acceptable_error_probability);
assert!(maximum_acceptable_error_probability < 1.0);
// this assumed the noise level is equal at input/output
// the security of the noise level of ouput is controlled by
// the blind rotate decomposition
let ciphertext_modulus_log = W::BITS as u64;
let no_noise_bits = BITS_CARRY + precision + BITS_PADDING_WITHOUT_NOISE;
let noise_bits = ciphertext_modulus_log - no_noise_bits;
let fatal_noise_limit = (1_u64 << noise_bits) as f64;
// Now we search for P(x not in [-+fatal_noise_limit] | σ = safe_sigma) = p_error
// P(x not in [-+kappa] | σ = 1) = p_error
let kappa: f64 = sigma_scale_of_error_probability(maximum_acceptable_error_probability);
let safe_sigma = fatal_noise_limit / kappa;
let variance_max = Variance::from_modular_variance::<W>(square(safe_sigma));
let consts = OptimizationDecompositionsConsts {
kappa,
sum_size,
security_level,
noise_factor,
ciphertext_modulus_log,
keyswitch_decompositions: KS_BL.to_vec(),
blind_rotate_decompositions: BR_BL.to_vec(),
variance_max: variance_max.get_variance(),
};
let mut state = OptimizationState {
best_solution: None,
count_domain: glwe_dimensions.len()
* glwe_log_polynomial_sizes.len()
* internal_lwe_dimensions.len()
* KS_BL.len()
* BR_BL.len(),
};
// cut only on glwe_poly_size based of modulus switching noise
// assume this noise is increasing with lwe_intern_dim
let min_internal_lwe_dimensions = internal_lwe_dimensions[0];
let lower_bound_cut = |glwe_poly_size| {
// TODO: cut if min complexity is higher than current best
CUTS && noise_atomic_pattern::estimate_modulus_switching_noise_with_binary_key::<W>(
min_internal_lwe_dimensions,
glwe_poly_size,
)
.get_variance()
> consts.variance_max
};
let skip = |glwe_dim, glwe_poly_size| match restart_at {
Some(solution) => {
(glwe_dim, glwe_poly_size) < (solution.glwe_dimension, solution.glwe_polynomial_size)
}
None => false,
};
for &glwe_dim in glwe_dimensions {
assert!(1 <= glwe_dim);
assert!(glwe_dim < 4);
for &glwe_log_poly_size in glwe_log_polynomial_sizes {
assert!(8 < glwe_log_poly_size);
assert!(glwe_log_poly_size < 18);
let glwe_poly_size = 1 << glwe_log_poly_size;
if lower_bound_cut(glwe_poly_size) {
continue;
}
if skip(glwe_dim, glwe_poly_size) {
continue;
}
for &internal_dim in internal_lwe_dimensions {
assert!(256 < internal_dim);
update_state_with_best_decompositions::<W>(
&mut state,
&consts,
internal_dim,
glwe_poly_size,
glwe_dim,
);
}
}
}
state
}

1
src/optimisation/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod atomic_pattern;

View File

@@ -2,7 +2,7 @@ use concrete_commons::dispersion::Variance;
/// Noise ensuring security
// It was 128 bits of security on the 30th August 2021 with https://bitbucket.org/malb/lwe-estimator/commits/fb7deba98e599df10b665eeb6a26332e43fb5004
pub fn variance_glwe(
pub fn minimal_variance(
glwe_polynomial_size: u64,
glwe_dimension: u64,
ciphertext_modulus_log: u64,
@@ -23,52 +23,20 @@ pub fn variance_glwe(
Variance(f64::exp2(log2_var))
}
/// Noise ensuring ksk security
pub fn variance_ksk(
glwe_polynomial_size: u64,
glwe_dimension: u64,
ciphertext_modulus_log: u64,
security_level: u64,
) -> Variance {
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/keyswitch.py#L13
variance_glwe(
glwe_polynomial_size,
glwe_dimension,
ciphertext_modulus_log,
security_level,
)
}
/// Noise ensuring bsk security
pub fn variance_bsk(
glwe_polynomial_size: u64,
glwe_dimension: u64,
ciphertext_modulus_log: u64,
security_level: u64,
) -> Variance {
// https://github.com/zama-ai/concrete-optimizer/blob/prototype/python/optimizer/noise_formulas/bootstrap.py#L66
variance_glwe(
glwe_polynomial_size,
glwe_dimension,
ciphertext_modulus_log,
security_level,
)
}
#[cfg(test)]
mod tests {
use super::*;
use concrete_commons::dispersion::DispersionParameter;
#[test]
fn golden_python_prototype_security_variance_glwe_low() {
fn golden_python_prototype_security_security_glwe_variance_low() {
// python securityFunc(10,14,64)= 0.3120089883926036
let log_poly_size = 14;
let glwe_dimension = 10;
let integer_size = 64;
let golden_std_dev = 0.312_008_988_392_6036;
let security_level = 128;
let actual = variance_glwe(log_poly_size, glwe_dimension, integer_size, security_level);
let actual = minimal_variance(log_poly_size, glwe_dimension, integer_size, security_level);
approx::assert_relative_eq!(
golden_std_dev,
actual.get_standard_dev(),
@@ -77,14 +45,14 @@ mod tests {
}
#[test]
fn golden_python_prototype_security_variance_glwe_high() {
fn golden_python_prototype_security_security_glwe_variance_high() {
// python securityFunc(3,8,32)= 2.6011445832514504
let log_poly_size = 8;
let glwe_dimension = 3;
let integer_size = 32;
let golden_std_dev = 2.6011445832514504;
let security_level = 128;
let actual = variance_glwe(log_poly_size, glwe_dimension, integer_size, security_level);
let actual = minimal_variance(log_poly_size, glwe_dimension, integer_size, security_level);
approx::assert_relative_eq!(
golden_std_dev,
actual.get_standard_dev(),

1
src/security/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod glwe;