Context.h
1 /* Copyright (C) 2012-2020 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 #ifndef HELIB_CONTEXT_H
13 #define HELIB_CONTEXT_H
14 
18 #include <helib/PAlgebra.h>
19 #include <helib/CModulus.h>
20 #include <helib/IndexSet.h>
21 #include <helib/recryption.h>
22 #include <helib/primeChain.h>
23 #include <helib/powerful.h>
24 #include <helib/apiAttributes.h>
25 #include <helib/range.h>
26 
27 #include <NTL/Lazy.h>
28 
29 namespace helib {
30 
53 inline double lweEstimateSecurity(int n, double log2AlphaInv, int hwt)
54 {
55  if (hwt < 0 || (hwt > 0 && hwt < 120)) {
56  throw LogicError("Cannot estimate security for keys of weight<120");
57  }
58 
59  // clang-format off
60  constexpr double hwgts[] =
61  {120, 150, 180, 210, 240, 270, 300, 330, 360, 390, 420, 450};
62  constexpr double slopes[] =
63  {2.4, 2.67, 2.83, 3.0, 3.1, 3.3, 3.3, 3.35, 3.4, 3.45, 3.5, 3.55};
64  constexpr double cnstrms[] =
65  {19, 13, 10, 6, 3, 1, -3, -4, -5, -7, -10, -12};
66  // clang-format on
67 
68  constexpr size_t numWghts = sizeof(hwgts) / sizeof(hwgts[0]);
69 
70  const size_t idx = (hwt - 120) / 30; // index into the array above
71  double slope = 0, consterm = 0;
72  if (hwt == 0) { // dense keys
73  slope = 3.8;
74  consterm = -20;
75  } else if (idx < numWghts - 1) {
76  // estimate prms on a line from prms[i] to prms[i+1]
77  // how far into this interval
78  double a = double(hwt - hwgts[idx]) / (hwgts[idx + 1] - hwgts[idx]);
79  slope = slopes[idx] + a * (slopes[idx + 1] - slopes[idx]);
80  consterm = cnstrms[idx] + a * (cnstrms[idx + 1] - cnstrms[idx]);
81  } else {
82  // Use the params corresponding to largest weight (450 above)
83  slope = slopes[numWghts - 1];
84  consterm = cnstrms[numWghts - 1];
85  }
86 
87  double x = n / log2AlphaInv;
88  double ret = slope * x + consterm;
89 
90  return ret < 0.0 ? 0.0 : ret; // If ret is negative then return 0.0
91 }
92 
105 long FindM(long k,
106  long nBits,
107  long c,
108  long p,
109  long d,
110  long s,
111  long chosen_m,
112  bool verbose = false);
113 
114 class EncryptedArray;
115 struct PolyModRing;
120 class Context
121 {
122  std::vector<Cmodulus> moduli; // Cmodulus objects for the different primes
123  // This is private since the implementation assumes that the list of
124  // primes only grows and no prime is ever modified or removed.
125 
126 public:
127  // Context is meant for convenience, not encapsulation: Most data
128  // members are public and can be initialized by the application program.
129 
132 
135 
137  std::shared_ptr<const EncryptedArray> ea;
138 
139  std::shared_ptr<const PowerfulDCRT> pwfl_converter;
140 
145  std::shared_ptr<PolyModRing> slotRing;
146 
148  NTL::xdouble stdev;
149 
150  //======================= high probability bounds ================
151  double scale; // default = 10
152 
160 
170 
171  //=======================================
172 
179 
180  // NOTE: this is a bit heuristic: we assume that if we evaluate
181  // f at a primitive root of unity, then we get something that well
182  // approximates a normal random variable with the same variance,
183  // which is equal to the sum of the variances of the individual
184  // f_i's, which is (2*magBound)^2/12 = magBound^2/3.
185  // We then multiply the sqrt of the variance by scale to get
186  // the high probability bound.
187 
188  double noiseBoundForUniform(double magBound, long degBound) const
189  {
190  return scale * std::sqrt(double(degBound) / 3.0) * magBound;
191  }
192 
193  NTL::xdouble noiseBoundForUniform(NTL::xdouble magBound, long degBound) const
194  {
195  return scale * std::sqrt(double(degBound) / 3.0) * magBound;
196  }
197 
198  //=======================================
199 
206 
207  // NOTE: for odd modulus, this means each f_i is uniformly distributed
208  // over { -floor(modulus/2), ..., floor(modulus/2) }.
209  // For even modulus, this means each f_i is uniformly distributed
210  // over { modulus/2, ..., modulus/2 }, except that the two endpoints
211  // (which represent the same residue class) occur with half the
212  // probability of the others.
213 
214  // NOTE: this is a bit heuristic: we assume that if we evaluate
215  // f at a primitive root of unity, then we get something that well
216  // approximates a normal random variable with the same variance,
217  // which is equal to the sum of the variances of the individual
218  // f_i's, which is (modulus)^2/12 + 1/6 for even modulus,
219  // and is at most (modulus^2)/12 for odd modulus.
220  // We then multiply the sqrt of the variance by scale to get
221  // the high probability bound.
222 
223  // NOTE: this is slightly more accurate that just calling
224  // noiseBoundForUniform with magBound=modulus/2.
225 
226  double noiseBoundForMod(long modulus, long degBound) const
227  {
228  double var = fsquare(modulus) / 12.0;
229  if (modulus % 2 == 0)
230  var += 1.0 / 6.0;
231 
232  return scale * std::sqrt(degBound * var);
233  }
234 
235  //=======================================
236 
243 
244  // NOTE: if we evaluate f at a primitive root of unity,
245  // then we get a normal random variable variance degBound * sigma^2.
246  // We then multiply the sqrt of the variance by scale to get
247  // the high probability bound.
248 
249  double noiseBoundForGaussian(double sigma, long degBound) const
250  {
251  return scale * std::sqrt(double(degBound)) * sigma;
252  }
253 
254  //=======================================
255 
262 
263  // NOTE: this is a bit heuristic: we assume that if we evaluate
264  // f at a primitive root of unity, then we get something that
265  // well approximates a normal random variable with the same variance,
266  // which is equal to the sum of the individual variances,
267  // which is degBound*prob.
268  // We then multiply the sqrt of the variance by scale to get
269  // the high probability bound.
270 
271  double noiseBoundForSmall(double prob, long degBound) const
272  {
273  return scale * std::sqrt(double(degBound)) * std::sqrt(prob);
274  }
275 
276  //=======================================
277 
283 
284  // NOTE: this is a bit heuristic: we assume that if we evaluate
285  // f at a primitive root of unity, then we get something that
286  // well approximates a normal random variable with the same variance,
287  // which is hwt.
288  // We then multiply the sqrt of the variance by scale to get
289  // the high probability bound.
290 
291  // NOTE: degBound is not used here, but I include it
292  // for consistency with the other noiseBound routines
293 
294  double noiseBoundForHWt(long hwt, UNUSED long degBound) const
295  {
296  return scale * std::sqrt(double(hwt));
297  }
298 
299  //=======================================
300 
308 
310 
312 
313  double stdDevForRecryption(long skHwt = 0) const
314  {
315  if (!skHwt)
316  skHwt = rcData.skHwt;
317  // the default reverts to rcData.skHwt, *not* rcData.defSkHwt
318 
319  long k = zMStar.getNFactors();
320  // number of prime factors of m
321 
322  long m = zMStar.getM();
323  long phim = zMStar.getPhiM();
324 
325  double mrat = double(phim) / double(m);
326 
327  return std::sqrt(mrat * double(skHwt) * double(1L << k) / 3.0) * 0.5;
328  }
329 
330  double boundForRecryption(long skHwt = 0) const
331  {
332  double c_m = zMStar.get_cM();
333  // multiply by this fudge factor
334 
335  return 0.5 + c_m * scale * stdDevForRecryption(skHwt);
336  }
337 
344 
349 
354 
357  void setModSizeTable() { modSizes.init(*this); }
358 
375  // Digits of ctxt/columns of key-switching matrix
376  std::vector<IndexSet> digits;
377 
379  // includes both thin and thick
381 
382  /******************************************************************/
383  // constructor
384  Context(unsigned long m,
385  unsigned long p,
386  unsigned long r,
387  const std::vector<long>& gens = std::vector<long>(),
388  const std::vector<long>& ords = std::vector<long>());
389 
390  void makeBootstrappable(const NTL::Vec<long>& mvec,
391  long skWht = 0,
392  bool build_cache = false,
393  bool alsoThick = true)
394  {
395  rcData.init(*this, mvec, alsoThick, skWht, build_cache);
396  }
397 
398  bool isBootstrappable() const { return rcData.alMod != nullptr; }
399 
401 
403  {
405  }
406 
407  // returns first nprimes ctxtPrimes
408  IndexSet getCtxtPrimes(long nprimes) const
409  {
410  long first = ctxtPrimes.first();
411  long last = std::min(ctxtPrimes.last(), first + nprimes - 1);
412  return IndexSet(first, last);
413  }
414 
415  // FIXME: replacement for bitsPerLevel...placeholder for now
416  long BPL() const { return 30; }
417 
418  bool operator==(const Context& other) const;
419  bool operator!=(const Context& other) const { return !(*this == other); }
420 
422  long ithPrime(unsigned long i) const
423  {
424  return (i < moduli.size()) ? moduli[i].getQ() : 0;
425  }
426 
428  const Cmodulus& ithModulus(unsigned long i) const { return moduli[i]; }
429 
431  long numPrimes() const { return moduli.size(); }
432 
434  bool isZeroDivisor(const NTL::ZZ& num) const
435  {
436  for (long i : range(moduli.size()))
437  if (divide(num, moduli[i].getQ()))
438  return true;
439  return false;
440  }
441 
443  bool inChain(long p) const
444  {
445  for (long i : range(moduli.size()))
446  if (p == moduli[i].getQ())
447  return true;
448  return false;
449  }
450 
453  void productOfPrimes(NTL::ZZ& p, const IndexSet& s) const;
454  NTL::ZZ productOfPrimes(const IndexSet& s) const
455  {
456  NTL::ZZ p;
457  productOfPrimes(p, s);
458  return p;
459  }
461 
462  // FIXME: run-time error when ithPrime(i) returns 0
464  double logOfPrime(unsigned long i) const { return log(ithPrime(i)); }
465 
467  double logOfProduct(const IndexSet& s) const
468  {
469  if (s.last() >= numPrimes())
470  throw RuntimeError("Context::logOfProduct: IndexSet has too many rows");
471 
472  double ans = 0.0;
473  for (long i : s)
474  ans += logOfPrime(i);
475  return ans;
476  }
477 
479  long bitSizeOfQ() const
480  {
481  IndexSet primes = ctxtPrimes | specialPrimes;
482  return std::ceil(logOfProduct(primes) / log(2.0));
483  }
484 
495  double securityLevel(int hwt = 0) const
496  {
497  IndexSet primes = ctxtPrimes | specialPrimes;
498  if (primes.card() == 0) {
499  throw LogicError(
500  "Security level cannot be determined as modulus chain is empty.");
501  }
502 
503  double s = to_double(stdev);
504  if (zMStar.getPow2() == 0) { // not power of two
505  s *= sqrt(zMStar.getM());
506  }
507  double log2AlphaInv = (logOfProduct(primes) - log(s)) / log(2.0);
508  return lweEstimateSecurity(zMStar.getPhiM(), log2AlphaInv, hwt);
509  }
510 
512  void printout(std::ostream& out = std::cout) const;
513 
515  void AddSmallPrime(long q);
516  void AddCtxtPrime(long q);
517  void AddSpecialPrime(long q);
518 
520 
555  friend void writeContextBase(std::ostream& str, const Context& context);
557 
559  friend std::ostream& operator<<(std::ostream& str, const Context& context);
560 
562  friend void readContextBase(std::istream& str,
563  unsigned long& m,
564  unsigned long& p,
565  unsigned long& r,
566  std::vector<long>& gens,
567  std::vector<long>& ords);
568 
570  friend std::istream& operator>>(std::istream& str, Context& context);
572 
573  friend void writeContextBinary(std::ostream& str, const Context& context);
574  friend void readContextBinary(std::istream& str, Context& context);
575 };
576 
578 void writeContextBase(std::ostream& s, const Context& context);
580 void readContextBase(std::istream& s,
581  unsigned long& m,
582  unsigned long& p,
583  unsigned long& r,
584  std::vector<long>& gens,
585  std::vector<long>& ords);
586 std::unique_ptr<Context> buildContextFromAscii(std::istream& str);
587 
589 void writeContextBaseBinary(std::ostream& str, const Context& context);
590 void writeContextBinary(std::ostream& str, const Context& context);
591 
593 void readContextBaseBinary(std::istream& s,
594  unsigned long& m,
595  unsigned long& p,
596  unsigned long& r,
597  std::vector<long>& gens,
598  std::vector<long>& ords);
599 std::unique_ptr<Context> buildContextFromBinary(std::istream& str);
600 void readContextBinary(std::istream& str, Context& context);
601 
602 // Build modulus chain with nBits worth of ctxt primes,
603 // using nDgts digits in key-switching.
604 // The willBeBootstrappable and skHwt parameters are needed to get around some
605 // some circularity when making the context boostrappable.
606 // If you later call context.makeBootstrappable with a given value
607 // of skHwt, you should first buildModChain with willBeBootstrappable
608 // set to true and the given value of skHwt.
609 // FIXME: We should really have a simpler way to do this.
610 // resolution ... FIXME
611 
612 void buildModChain(Context& context,
613  long nBits,
614  long nDgts = 3,
615  bool willBeBootstrappable = false,
616  long skHwt = 0,
617  long resolution = 3,
618  long bitsInSpecialPrimes = 0);
619 // should be called if after you build the mod chain in some way
620 // *other* than calling buildModChain.
621 void endBuildModChain(Context& context);
622 
624 // Should point to the "current" context
625 extern Context* activeContext;
626 
627 } // namespace helib
628 
629 #endif // ifndef HELIB_CONTEXT_H
Same as above, but for "thin" bootstrapping, where the slots are assumed to contain constants.
Definition: recryption.h:123
void setModSizeTable()
Definition: Context.h:357
long getM() const
Returns m.
Definition: PAlgebra.h:162
Inherits from Exception and std::runtime_error.
Definition: exceptions.h:105
ModuliSizes modSizes
A helper table to map required modulo-sizes to primeSets.
Definition: Context.h:356
void endBuildModChain(Context &context)
Definition: primeChain.cpp:723
double noiseBoundForUniform(double magBound, long degBound) const
Definition: Context.h:188
long ithPrime(unsigned long i) const
The ith small prime in the modulus chain.
Definition: Context.h:422
ThinRecryptData rcData
Bootstrapping-related data in the context.
Definition: Context.h:380
void writeContextBaseBinary(std::ostream &str, const Context &context)
write [m p r gens ords] data
Definition: Context.cpp:225
void buildModChain(Context &context, long nBits, long nDgts=3, bool willBeBootstrappable=false, long skHwt=0, long resolution=3, long bitsInSpecialPrimes=0)
Definition: primeChain.cpp:748
IndexSet allPrimes() const
Definition: Context.h:402
std::shared_ptr< const PowerfulDCRT > pwfl_converter
Definition: Context.h:139
long last() const
Returns the last element, -1 if the set is empty.
Definition: IndexSet.h:71
double noiseBoundForHWt(long hwt, UNUSED long degBound) const
Definition: Context.h:294
bool isBootstrappable() const
Definition: Context.h:398
long card() const
The cardinality of the set.
Definition: IndexSet.h:80
void writeContextBase(std::ostream &s, const Context &context)
write [m p r gens ords] data
Definition: Context.cpp:405
Inherits from Exception and std::logic_error.
Definition: exceptions.h:68
double boundForRecryption(long skHwt=0) const
Definition: Context.h:330
void init(const Context &context, const NTL::Vec< long > &mvec_, bool alsoThick, long t=0, bool build_cache=false, bool minimal=false)
Initialize the recryption data in the context.
Definition: recryption.cpp:772
friend void readContextBase(std::istream &str, unsigned long &m, unsigned long &p, unsigned long &r, std::vector< long > &gens, std::vector< long > &ords)
read [m p r] data, needed to construct context
Definition: Context.cpp:464
bool isZeroDivisor(const NTL::ZZ &num) const
Is num divisible by any of the primes in the chain?
Definition: Context.h:434
double lweEstimateSecurity(int n, double log2AlphaInv, int hwt)
An estimate for the security-level. This has a lower bound of 0.
Definition: Context.h:53
void readContextBaseBinary(std::istream &s, unsigned long &m, unsigned long &p, unsigned long &r, std::vector< long > &gens, std::vector< long > &ords)
read [m p r gens ords] data, needed to construct context
Definition: Context.cpp:254
Context * activeContext
Definition: Context.cpp:176
long skHwt
Hamming weight of recryption secret key.
Definition: recryption.h:49
const Cmodulus & ithModulus(unsigned long i) const
Cmodulus object corresponding to ith small prime in the chain.
Definition: Context.h:428
double noiseBoundForMod(long modulus, long degBound) const
Definition: Context.h:226
double get_cM() const
Definition: PAlgebra.h:196
Context(unsigned long m, unsigned long p, unsigned long r, const std::vector< long > &gens=std::vector< long >(), const std::vector< long > &ords=std::vector< long >())
Definition: Context.cpp:569
friend void writeContextBase(std::ostream &str, const Context &context)
write [m p r] data
Definition: Context.cpp:405
NTL::ZZ productOfPrimes(const IndexSet &s) const
Definition: Context.h:454
The structure of (Z/mZ)* /(p)
Definition: PAlgebra.h:77
void writeContextBinary(std::ostream &str, const Context &context)
Definition: Context.cpp:284
void init(const Context &context)
initialize helper table for a given chain
Definition: primeChain.cpp:58
PAlgebraMod alMod
The structure of Z[X]/(Phi_m(X),p^r)
Definition: Context.h:134
A dynamic set of non-negative integers.
Definition: IndexSet.h:31
long getPow2() const
if m = 2^k, then pow2 == k; otherwise, pow2 == 0
Definition: PAlgebra.h:189
IndexSet smallPrimes
Definition: Context.h:353
double noiseBoundForGaussian(double sigma, long degBound) const
Definition: Context.h:249
IndexSet getCtxtPrimes(long nprimes) const
Definition: Context.h:408
void readContextBase(std::istream &s, unsigned long &m, unsigned long &p, unsigned long &r, std::vector< long > &gens, std::vector< long > &ords)
read [m p r gens ords] data, needed to construct context
Definition: Context.cpp:464
std::shared_ptr< const PAlgebraMod > alMod
for plaintext space p^{e-e'+r}
Definition: recryption.h:52
long BPL() const
Definition: Context.h:416
bool operator==(const Context &other) const
Definition: Context.cpp:185
IndexSet specialPrimes
Definition: Context.h:348
Provides FFT and iFFT routines modulo a single-precision prime.
Definition: CModulus.h:43
long numPrimes() const
Total number of small prime in the chain.
Definition: Context.h:431
void AddSmallPrime(long q)
Just add the given prime to the chain.
Definition: primeChain.cpp:450
void AddSpecialPrime(long q)
Definition: primeChain.cpp:466
long FindM(long k, long nBits, long c, long p, long d, long s, long chosen_m, bool verbose=false)
Returns smallest parameter m satisfying various constraints:
Definition: Context.cpp:24
double logOfProduct(const IndexSet &s) const
Returns the natural logarithm of productOfPrimes(s)
Definition: Context.h:467
friend std::ostream & operator<<(std::ostream &str, const Context &context)
Write all other data.
Definition: Context.cpp:426
void readContextBinary(std::istream &str, Context &context)
Definition: Context.cpp:332
long getNFactors() const
The number of distinct prime factors of m.
Definition: PAlgebra.h:174
Definition: apiAttributes.h:21
friend void readContextBinary(std::istream &str, Context &context)
Definition: Context.cpp:332
long first() const
Returns the first element, 0 if the set is empty.
Definition: IndexSet.h:68
NTL::xdouble noiseBoundForUniform(NTL::xdouble magBound, long degBound) const
Definition: Context.h:193
void productOfPrimes(NTL::ZZ &p, const IndexSet &s) const
The product of all the primes in the given set.
Definition: Context.cpp:178
double scale
Definition: Context.h:151
double logOfPrime(unsigned long i) const
Returns the natural logarithm of the ith prime.
Definition: Context.h:464
A helper class to map required modulo-sizes to primeSets.
Definition: primeChain.h:28
double noiseBoundForSmall(double prob, long degBound) const
Definition: Context.h:271
long getPhiM() const
Returns phi(m)
Definition: PAlgebra.h:168
double stdDevForRecryption(long skHwt=0) const
NOTE: this is a bit heuristic. See design document for details.
Definition: Context.h:313
long bitSizeOfQ() const
Size in bits of Q.
Definition: Context.h:479
Maintaining the parameters.
Definition: Context.h:121
NTL::xdouble stdev
sqrt(variance) of the LWE error (default=3.2)
Definition: Context.h:148
The structure of Z[X]/(Phi_m(X), p)
Definition: PAlgebra.h:799
bool inChain(long p) const
Is p already in the chain?
Definition: Context.h:443
std::unique_ptr< Context > buildContextFromAscii(std::istream &str)
Definition: Context.cpp:480
friend std::istream & operator>>(std::istream &str, Context &context)
read all other data associated with context
Definition: Context.cpp:488
std::unique_ptr< Context > buildContextFromBinary(std::istream &str)
Definition: Context.cpp:276
double fsquare(double x)
Return the square of a number as a double.
Definition: NumbTh.h:148
friend void writeContextBinary(std::ostream &str, const Context &context)
Definition: Context.cpp:284
std::shared_ptr< PolyModRing > slotRing
The structure of a single slot of the plaintext space.
Definition: Context.h:145
bool operator!=(const Context &other) const
Definition: Context.h:419
void AddCtxtPrime(long q)
Definition: primeChain.cpp:458
void printout(std::ostream &out=std::cout) const
print out algebra and other important info
Definition: Context.cpp:590
void makeBootstrappable(const NTL::Vec< long > &mvec, long skWht=0, bool build_cache=false, bool alsoThick=true)
Definition: Context.h:390
general_range< long > range(long n)
Definition: range.h:57
IndexSet ctxtPrimes
Definition: Context.h:343
IndexSet fullPrimes() const
Definition: Context.h:400
double securityLevel(int hwt=0) const
An estimate for the security-level. This has a lower bound of 0.
Definition: Context.h:495
PAlgebra zMStar
The structure of Zm*.
Definition: Context.h:131
std::vector< IndexSet > digits
The set of primes for the digits.
Definition: Context.h:376
std::shared_ptr< const EncryptedArray > ea
A default EncryptedArray.
Definition: Context.h:137