Skip to content

Commit 202b3e1

Browse files
Tutorial tweaks
Co-authored-by: victorshoup <[email protected]> Co-authored-by: Jack Crawford <[email protected]>
1 parent a1d3ac6 commit 202b3e1

File tree

10 files changed

+515
-92
lines changed

10 files changed

+515
-92
lines changed

examples/tutorial/01_ckks_basics.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (C) 2020 IBM Corp.
1+
/* Copyright (C) 2020-2021 IBM Corp.
22
* This program is Licensed under the Apache License, Version 2.0
33
* (the "License"); you may not use this file except in compliance
44
* with the License. You may obtain a copy of the License at
@@ -10,11 +10,6 @@
1010
* limitations under the License. See accompanying LICENSE file.
1111
*/
1212

13-
#include <helib/helib.h>
14-
15-
using namespace std;
16-
using namespace helib;
17-
1813
// In the CKKS encryption scheme, plaintexts are vectors of real or complex
1914
// numbers. The length, n, of these vectors is determined by the choice of
2015
// parameters, as discussed below. We often refer to the components of these
@@ -24,11 +19,16 @@ using namespace helib;
2419
// means Single Instruction Multiple Data), since we can effectively perform
2520
// the same scalar operation in parallel on all n slots.
2621

22+
#include <helib/helib.h>
23+
24+
using namespace std;
25+
using namespace helib;
26+
2727
int main(int argc, char* argv[])
2828
{
2929
// To get started, we need to choose some parameters. This is done by
3030
// initializing a Context object. Since there are a lot of parameters, many
31-
// of them optional, HElib provides a "builder pattern" then lets you provide
31+
// of them optional, HElib provides a "builder pattern" than lets you provide
3232
// these parameters "by name".
3333

3434
Context context =
@@ -194,6 +194,9 @@ int main(int argc, char* argv[])
194194
// When this is done, if we denote the i-th slot of a ciphertext c by c[i],
195195
// then we have c3[i] = c0[i]*c1[i] + c2[i]*1.5 for i = 0..n-1.
196196

197+
// More generally, for a Ctxt c, one can perform c *= d, c += d, or c -= d,
198+
// where d can be (among other things) a long, a double, or even a PtxtArray.
199+
197200
//===========================================================================
198201

199202
// Next we decrypt c3.
@@ -220,7 +223,7 @@ int main(int argc, char* argv[])
220223

221224
// Then, we compute the distance between p3 (computed on plaintexts) and pp3
222225
// (computed homomorphically on ciphertexts). This is computed as
223-
// max{ |p3[i]-pp3[i]| : i = 0..n-1 }
226+
// max{ |p3[i]-pp3[i]| : i = 0..n-1 }
224227
double distance = Distance(p3, pp3);
225228

226229
cout << "distance=" << distance << "\n";

examples/tutorial/02_ckks_depth.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (C) 2020 IBM Corp.
1+
/* Copyright (C) 2020-2021 IBM Corp.
22
* This program is Licensed under the Apache License, Version 2.0
33
* (the "License"); you may not use this file except in compliance
44
* with the License. You may obtain a copy of the License at
@@ -38,15 +38,15 @@ using namespace helib;
3838
// Given a ciphertext c, one can obtain its capacity by invoking c.capacity(),
3939
// and one can obtain a bound on its absolute error by invoking c.errorBound().
4040

41+
#include <helib/helib.h>
42+
43+
using namespace std;
44+
using namespace helib;
45+
4146
int main(int argc, char* argv[])
4247
{
4348
Context context =
44-
ContextBuilder<CKKS>()
45-
.m(32 * 1024)
46-
.bits(358)
47-
.precision(20)
48-
.c(6)
49-
.build();
49+
ContextBuilder<CKKS>().m(32 * 1024).bits(358).precision(20).c(6).build();
5050

5151
cout << "securityLevel=" << context.securityLevel() << "\n";
5252

examples/tutorial/03_ckks_data_movement.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (C) 2020 IBM Corp.
1+
/* Copyright (C) 2020-2021 IBM Corp.
22
* This program is Licensed under the Apache License, Version 2.0
33
* (the "License"); you may not use this file except in compliance
44
* with the License. You may obtain a copy of the License at
@@ -10,24 +10,19 @@
1010
* limitations under the License. See accompanying LICENSE file.
1111
*/
1212

13+
// In the CKKS encryption scheme, besides SIMD operations that act on the slots
14+
// of a ciphertext in parallel, it is also possible to move data around among
15+
// the slots of a ciphertext.
16+
1317
#include <helib/helib.h>
1418

1519
using namespace std;
1620
using namespace helib;
1721

18-
// In the CKKS encryption scheme, besides SIMD operations that act on the slots
19-
// of a ciphertext in parallel, it is also possible to move data around among
20-
// the slots of a ciphertext.
21-
2222
int main(int argc, char* argv[])
2323
{
2424
Context context =
25-
ContextBuilder<CKKS>()
26-
.m(32 * 1024)
27-
.bits(358)
28-
.precision(30)
29-
.c(6)
30-
.build();
25+
ContextBuilder<CKKS>().m(32 * 1024).bits(358).precision(30).c(6).build();
3126

3227
cout << "securityLevel=" << context.securityLevel() << "\n";
3328

examples/tutorial/04_ckks_matmul.cpp

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (C) 2020 IBM Corp.
1+
/* Copyright (C) 2020-2021 IBM Corp.
22
* This program is Licensed under the Apache License, Version 2.0
33
* (the "License"); you may not use this file except in compliance
44
* with the License. You may obtain a copy of the License at
@@ -10,16 +10,16 @@
1010
* limitations under the License. See accompanying LICENSE file.
1111
*/
1212

13+
// In the CKKS encryption scheme, since a ciphertext encrypts a vector of
14+
// slots, it makes sense to multiply that vector by a matrix. HElib provides
15+
// highly optimized routines for multiplying an encrypted vector by a plaintext
16+
// matrix.
17+
1318
#include <helib/helib.h>
1419

1520
using namespace std;
1621
using namespace helib;
1722

18-
// In the CKKS encryption scheme, since a ciphertext encrypts a vector
19-
// of slots, it makes sense to multiply that vector by a matrix.
20-
// HElib provides highly optimized routines for multiplying
21-
// an encrypted vector by a plaintext matrix.
22-
2323
// To use these routines, we need to include an extra file:
2424
#include <helib/matmul.h>
2525

@@ -43,12 +43,7 @@ void printMemoryUsage() {}
4343
int main(int argc, char* argv[])
4444
{
4545
Context context =
46-
ContextBuilder<CKKS>()
47-
.m(16 * 1024)
48-
.bits(119)
49-
.precision(30)
50-
.c(2)
51-
.build();
46+
ContextBuilder<CKKS>().m(16 * 1024).bits(119).precision(30).c(2).build();
5247

5348
cout << "securityLevel=" << context.securityLevel() << "\n";
5449

@@ -76,14 +71,15 @@ int main(int argc, char* argv[])
7671

7772
//===========================================================================
7873

79-
// We define a plaintext matrix as follows:
74+
// We define an n x n plaintext matrix as follows:
8075
MatMul_CKKS mat(context,
8176
[n](long i, long j) { return ((i + j) % n) / double(n); });
8277

8378
// Note that the second parameter of the MatMul_CKKS constructor is of type
8479
// std::function<double(long, long)>, meaning that it should be a
8580
// function-like object that takes two long's and returns a double. In this
86-
// example, the actual parameter is a C++ "lambda" object.
81+
// example, the actual parameter is a C++ "lambda" object. In input (i, j),
82+
// this should return the value of the matrix in row i and column j.
8783

8884
// We now multiply ciphertext c by this matrix:
8985
c *= mat;

examples/tutorial/05_ckks_multlowlvl.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (C) 2020 IBM Corp.
1+
/* Copyright (C) 2020-2021 IBM Corp.
22
* This program is Licensed under the Apache License, Version 2.0
33
* (the "License"); you may not use this file except in compliance
44
* with the License. You may obtain a copy of the License at
@@ -10,11 +10,6 @@
1010
* limitations under the License. See accompanying LICENSE file.
1111
*/
1212

13-
#include <helib/helib.h>
14-
15-
using namespace std;
16-
using namespace helib;
17-
1813
// In the CKKS encryption scheme (as well as in BGV), ciphertext multiplication
1914
// is a two-step process. The operation ctxt1 *= ctxt2 is equivalent to the
2015
// following:
@@ -30,15 +25,15 @@ using namespace helib;
3025
// can sometimes be exploited to achieve significant speedups, as illustrated
3126
// here.
3227

28+
#include <helib/helib.h>
29+
30+
using namespace std;
31+
using namespace helib;
32+
3333
int main(int argc, char* argv[])
3434
{
3535
Context context =
36-
ContextBuilder<CKKS>()
37-
.m(16 * 1024)
38-
.bits(119)
39-
.precision(20)
40-
.c(2)
41-
.build();
36+
ContextBuilder<CKKS>().m(16 * 1024).bits(119).precision(20).c(2).build();
4237

4338
cout << "securityLevel=" << context.securityLevel() << "\n";
4439

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/* Copyright (C) 2020-2021 IBM Corp.
2+
* This program is Licensed under the Apache License, Version 2.0
3+
* (the "License"); you may not use this file except in compliance
4+
* with the License. You may obtain a copy of the License at
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
* Unless required by applicable law or agreed to in writing, software
7+
* distributed under the License is distributed on an "AS IS" BASIS,
8+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
* See the License for the specific language governing permissions and
10+
* limitations under the License. See accompanying LICENSE file.
11+
*/
12+
13+
// In the CKKS encryption scheme, ciphertexts can encrypt vectors of complex
14+
// numbers.
15+
16+
#include <helib/helib.h>
17+
18+
#include <helib/matmul.h>
19+
// This is only needed if you want to do matrix multiplication
20+
21+
using namespace std;
22+
using namespace helib;
23+
24+
int main(int argc, char* argv[])
25+
{
26+
Context context =
27+
ContextBuilder<CKKS>().m(32 * 1024).bits(358).precision(30).c(6).build();
28+
29+
cout << "securityLevel=" << context.securityLevel() << "\n";
30+
31+
long n = context.getNSlots();
32+
33+
SecKey secretKey(context);
34+
secretKey.GenSecKey();
35+
36+
addSome1DMatrices(secretKey);
37+
// This only needs to be done if you want to do matrix multiplication
38+
39+
addSomeFrbMatrices(secretKey);
40+
// This only needs to be done if you want to do conjugation
41+
42+
const PubKey& publicKey = secretKey;
43+
44+
//===========================================================================
45+
46+
// Let's encrypt something!
47+
vector<std::complex<double>> v0(n);
48+
for (long i = 0; i < n; i++)
49+
v0[i] = std::complex<double>(cos(2.0 * PI * i / n), sin(2.0 * PI * i / n));
50+
51+
// A PtxtArray can be initialized with a vector of complex numbers
52+
PtxtArray p0(context, v0);
53+
54+
// Encryption works the same as with real numbers
55+
Ctxt c0(publicKey);
56+
p0.encrypt(c0);
57+
58+
//===========================================================================
59+
60+
// We next create another ciphertext that encrypts random complex numbers:
61+
62+
PtxtArray p1(context);
63+
p1.randomComplex();
64+
// this fills each entry of p1 with a random number in the complex
65+
// unit circle
66+
67+
Ctxt c1(publicKey);
68+
p1.encrypt(c1);
69+
70+
//===========================================================================
71+
72+
// We can perform homomorphic computations in the same way as we did before:
73+
74+
Ctxt c2 = c0;
75+
c2 *= 2.5;
76+
c2 += c1;
77+
78+
// Note that there is no direct support for combining a ciphertext with a
79+
// complex scalar. This can be achieved, however, by first converting the
80+
// complex scaler to a PtxtArray. For example:
81+
82+
PtxtArray I(context, std::complex<double>(0.0, 1.0));
83+
// I has the imaginary unit in each slot
84+
85+
c2 *= I;
86+
87+
cout << "c2.capacity=" << c2.capacity() << " ";
88+
cout << "c2.errorBound=" << c2.errorBound() << "\n";
89+
90+
// Data movement operations, like rotate and shift, work exactly as before.
91+
92+
// There is also support for multiplying a ciphertext by a plaintext matrix
93+
// of complex numbers. In 04_ckks_matmul.cpp, we showed how you could
94+
// specify an n x n matrix of real numbers using the class MatMul_CKKS. One
95+
// can specify an n x n matrix of complex numbers as follows:
96+
97+
MatMul_CKKS_Complex mat(context, [n](long i, long j) {
98+
return std::complex<double>(i, j) / double(n);
99+
});
100+
101+
c2 *= mat;
102+
103+
cout << "c2.capacity=" << c2.capacity() << " ";
104+
cout << "c2.errorBound=" << c2.errorBound() << "\n";
105+
106+
//===========================================================================
107+
108+
// One can homomorphically compute the complex conjugate of each slot
109+
// of a ciphertext as follows:
110+
111+
conjugate(c2);
112+
113+
cout << "c2.capacity=" << c2.capacity() << " ";
114+
cout << "c2.errorBound=" << c2.errorBound() << "\n";
115+
116+
//===========================================================================
117+
118+
// Let's decrypt the results:
119+
120+
PtxtArray pp2(context);
121+
pp2.decryptComplex(c2, secretKey);
122+
123+
// Note that if one just writes pp2.decrypt(c2, secretKey) instead of
124+
// pp2.decryptComplex(c2, secretKey), the imaginary part will be discarded.
125+
126+
// We can store pp2 in a standard vector, as usual:
127+
128+
std::vector<std::complex<double>> v2;
129+
pp2.store(v2);
130+
131+
//===========================================================================
132+
133+
// We can also perform the computation on plaintexts and compare:
134+
135+
PtxtArray p2 = p0;
136+
p2 *= 2.5;
137+
p2 += p1;
138+
p2 *= I;
139+
p2 *= mat;
140+
conjugate(p2);
141+
142+
double distance = Distance(p2, pp2);
143+
cout << "distance=" << distance << "\n";
144+
}

0 commit comments

Comments
 (0)