// Find the IndexSet such that modDown to that set of primes makes the // additive term due to rounding into the dominant noise term void Ctxt::findBaseSet(IndexSet& s) const { if (getNoiseVar()<=0.0) { // an empty ciphertext s = context.ctxtPrimes; return; } assert(verifyPrimeSet()); bool halfSize = context.containsSmallPrime(); double curNoise = log(getNoiseVar())/2; double firstNoise = context.logOfPrime(0); double noiseThreshold = log(modSwitchAddedNoiseVar())*0.55; // FIXME: The above should have been 0.5. Making it a bit more means // that we will mod-switch a little less frequently, whether this is // a good thing needs to be tested. // remove special primes, if they are included in this->primeSet s = getPrimeSet(); if (!s.disjointFrom(context.specialPrimes)) { // scale down noise curNoise -= context.logOfProduct(context.specialPrimes); s.remove(context.specialPrimes); } /* We compare below to noiseThreshold+1 rather than to noiseThreshold * to make sure that if you mod-switch down to c.findBaseSet() and * then immediately call c.findBaseSet() again, it will not tell you * to mod-switch further down. Note that mod-switching adds close to * noiseThreshold to the scaled noise, so if the scaled noise was * equal to noiseThreshold then after mod-switchign you would have * roughly twice as much noise. Since we're mesuring the log, it means * that you may have as much as noiseThreshold+log(2), which we round * up to noiseThreshold+1 in the test below. */ if (curNoise<=noiseThreshold+1) return; // no need to mod down // if the first prime in half size, begin by removing it if (halfSize && s.contains(0)) { curNoise -= firstNoise; s.remove(0); } // while noise is larger than threshold, scale down by the next prime while (curNoise>noiseThreshold && !empty(s)) { curNoise -= context.logOfPrime(s.last()); s.remove(s.last()); } // Add 1st prime if s is empty or if this does not increase noise too much if (empty(s) || (!s.contains(0) && curNoise+firstNoise<=noiseThreshold)) { s.insert(0); curNoise += firstNoise; } if (curNoise>noiseThreshold && log_of_ratio()>-0.5) cerr << "Ctxt::findBaseSet warning: already at lowest level\n"; }
// Find the IndexSet such that modDown to that set of primes makes the // additive term due to rounding into the dominant noise term void Ctxt::findBaseSet(IndexSet& s) const { if (getNoiseVar()<=0.0) { // an empty ciphertext s = context.ctxtPrimes; return; } assert(verifyPrimeSet()); bool halfSize = context.containsSmallPrime(); double addedNoise = log(modSwitchAddedNoiseVar())/2; double curNoise = log(getNoiseVar())/2; double firstNoise = context.logOfPrime(0); // remove special primes, if they are included in this->primeSet s = getPrimeSet(); if (!s.disjointFrom(context.specialPrimes)) { // scale down noise curNoise -= context.logOfProduct(context.specialPrimes); s.remove(context.specialPrimes); } if (curNoise<=2*addedNoise) return; // no need to mod down // if the first prime in half size, begin by removing it if (halfSize && s.contains(0)) { curNoise -= firstNoise; s.remove(0); } // while noise is larger than added term, scale down by the next prime while (curNoise>addedNoise && card(s)>1) { curNoise -= context.logOfPrime(s.last()); s.remove(s.last()); } if (halfSize) { // If noise is still too big, drop last big prime and insert half-size prime if (curNoise>addedNoise) { curNoise = firstNoise; s = IndexSet(0); } // Otherwise check if you can add back the half-size prime else if (curNoise+firstNoise <= addedNoise) { curNoise += firstNoise; s.insert(0); } } if (curNoise>addedNoise && log_of_ratio()>-0.5) cerr << "Ctxt::findBaseSet warning: already at lowest level\n"; }
// mod-switch down to primeSet \intersect s, after this call we have // primeSet<=s. s must contain either all special primes or none of them. void Ctxt::modDownToSet(const IndexSet &s) { IndexSet intersection = primeSet & s; // assert(!empty(intersection)); // some primes must be left if (empty(intersection)) { cerr << "modDownToSet called from "<<primeSet<<" to "<<s<<endl; exit(1); } if (intersection==primeSet) return; // nothing to do, removing no primes FHE_TIMER_START; IndexSet setDiff = primeSet / intersection; // set-minus // Scale down all the parts: use either a simple "drop down" (just removing // primes, i.e., reducing the ctxt modulo the samaller modulus), or a "real // modulus switching" with rounding, basically whichever yeilds smaller // noise. Recall that we keep the invariant that a ciphertext mod Q is // decrypted to Q*m (mod p), so if we just "drop down" we still need to // multiply by (Q^{-1} mod p). // Get an estimate for the added noise term for modulus switching xdouble addedNoiseVar = modSwitchAddedNoiseVar(); if (noiseVar*ptxtSpace*ptxtSpace < addedNoiseVar) { // just "drop down" long prodInv = InvMod(rem(context.productOfPrimes(setDiff),ptxtSpace), ptxtSpace); for (size_t i=0; i<parts.size(); i++) { parts[i].removePrimes(setDiff); // remove the primes not in s parts[i] *= prodInv; // WARNING: the following line is written just so to prevent overflow noiseVar = noiseVar*prodInv*prodInv; } // cerr << "DEGENERATE DROP\n"; } else { // do real mod switching for (size_t i=0; i<parts.size(); i++) parts[i].scaleDownToSet(intersection, ptxtSpace); // update the noise estimate double f = context.logOfProduct(setDiff); noiseVar /= xexp(2*f); noiseVar += addedNoiseVar; } primeSet.remove(setDiff); // remove the primes not in s assert(verifyPrimeSet()); // sanity-check: ensure primeSet is still valid FHE_TIMER_STOP; }