def __init__(self, X, P, codomain = None, check = False): r""" Create the discrete probability space with probabilities on the space X given by the dictionary P with values in the field real_field. EXAMPLES:: sage: S = [ i for i in range(16) ] sage: P = {} sage: for i in range(15): P[i] = 2^(-i-1) sage: P[15] = 2^-16 sage: X = DiscreteProbabilitySpace(S,P) sage: X.domain() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) sage: X.set() {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} sage: X.entropy() 1.9997253418 A probability space can be defined on any list of elements. EXAMPLES:: sage: AZ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' sage: S = [ AZ[i] for i in range(26) ] sage: P = { 'A':1/2, 'B':1/4, 'C':1/4 } sage: X = DiscreteProbabilitySpace(S,P) sage: X Discrete probability space defined by {'A': 1/2, 'C': 1/4, 'B': 1/4} sage: X.entropy() 1.5 """ if codomain is None: codomain = RealField() if not is_RealField(codomain) and not is_RationalField(codomain): raise TypeError, "Argument codomain (= %s) must be the reals or rationals" % codomain if check: one = sum([ P[x] for x in P.keys() ]) if is_RationalField(codomain): if not one == 1: raise TypeError, "Argument P (= %s) does not define a probability function" else: if not Abs(one-1) < 2^(-codomain.precision()+1): raise TypeError, "Argument P (= %s) does not define a probability function" ProbabilitySpace_generic.__init__(self, X, codomain) DiscreteRandomVariable.__init__(self, self, P, codomain, check)
def quadratic_L_function__numerical(n, d, num_terms=1000): """ Evaluate the Dirichlet L-function (for quadratic character) numerically (in a very naive way). EXAMPLES: First, let us test several values for a given character:: sage: RR = RealField(100) sage: for i in range(5): ....: print("L({}, (-4/.)): {}".format(1+2*i, RR(quadratic_L_function__exact(1+2*i, -4)) - quadratic_L_function__numerical(RR(1+2*i),-4, 10000))) L(1, (-4/.)): 0.000049999999500000024999996962707 L(3, (-4/.)): 4.99999970000003...e-13 L(5, (-4/.)): 4.99999922759382...e-21 L(7, (-4/.)): ...e-29 L(9, (-4/.)): ...e-29 This procedure fails for negative special values, as the Dirichlet series does not converge here:: sage: quadratic_L_function__numerical(-3,-4, 10000) Traceback (most recent call last): ... ValueError: the Dirichlet series does not converge here Test for several characters that the result agrees with the exact value, to a given accuracy :: sage: for d in range(-20,0): # long time (2s on sage.math 2014) ....: if abs(RR(quadratic_L_function__numerical(1, d, 10000) - quadratic_L_function__exact(1, d))) > 0.001: ....: print("Oops! We have a problem at d = {}: exact = {}, numerical = {}".format(d, RR(quadratic_L_function__exact(1, d)), RR(quadratic_L_function__numerical(1, d)))) """ # Set the correct precision if it is given (for n). if is_RealField(n.parent()): R = n.parent() else: R = RealField() if n < 0: raise ValueError('the Dirichlet series does not converge here') d1 = fundamental_discriminant(d) ans = R.zero() for i in range(1,num_terms): ans += R(kronecker_symbol(d1,i) / R(i)**n) return ans
def at1(self, k=None, prec=None): r""" Compute `L(E,1)` using `k` terms of the series for `L(E,1)` as explained in Section 7.5.3 of Henri Cohen's book "A Course in Computational Algebraic Number Theory". If the argument `k` is not specified, then it defaults to `\sqrt(N)`, where `N` is the conductor. INPUT: - ``k`` -- number of terms of the series. If zero or ``None``, use `k = \sqrt(N)`, where `N` is the conductor. - ``prec`` -- numerical precision in bits. If zero or ``None``, use a reasonable automatic default. OUTPUT: A tuple of real numbers ``(L, err)`` where ``L`` is an approximation for `L(E,1)` and ``err`` is a bound on the error in the approximation. This function is disjoint from the PARI ``elllseries`` command, which is for a similar purpose. To use that command (via the PARI C library), simply type ``E.pari_mincurve().elllseries(1)``. ALGORITHM: - Compute the root number eps. If it is -1, return 0. - Compute the Fourier coefficients `a_n`, for `n` up to and including `k`. - Compute the sum .. MATH:: 2 * sum_{n=1}^{k} (a_n / n) * exp(-2*pi*n/Sqrt(N)), where `N` is the conductor of `E`. - Compute a bound on the tail end of the series, which is .. MATH:: 2 e^{-2 \pi (k+1) / \sqrt{N}} / (1 - e^{-2 \pi/\sqrt{N}}). For a proof see [Grigov-Jorza-Patrascu-Patrikis-Stein]. EXAMPLES:: sage: L, err = EllipticCurve('11a1').lseries().at1() sage: L, err (0.253804, 0.000181444) sage: parent(L) Real Field with 24 bits of precision sage: E = EllipticCurve('37b') sage: E.lseries().at1() (0.7257177, 0.000800697) sage: E.lseries().at1(100) (0.7256810619361527823362055410263965487367603361763, 1.52469e-45) sage: L,err = E.lseries().at1(100, prec=128) sage: L 0.72568106193615278233620554102639654873 sage: parent(L) Real Field with 128 bits of precision sage: err 1.70693e-37 sage: parent(err) Real Field with 24 bits of precision and rounding RNDU Rank 1 through 3 elliptic curves:: sage: E = EllipticCurve('37a1') sage: E.lseries().at1() (0.0000000, 0.000000) sage: E = EllipticCurve('389a1') sage: E.lseries().at1() (-0.001769566, 0.00911776) sage: E = EllipticCurve('5077a1') sage: E.lseries().at1() (0.0000000, 0.000000) """ sqrtN = sqrt(self.__E.conductor()) if k: k = int(k) else: k = int(ceil(sqrtN)) if prec: prec = int(prec) else: # Use the same precision as deriv_at1() below for # consistency prec = int(9.065*k/sqrtN + 1.443*log(k)) + 12 R = RealField(prec) # Compute error term with bounded precision of 24 bits and # round towards +infinity Rerror = RealField(24, rnd='RNDU') if self.__E.root_number() == -1: return (R.zero(), Rerror.zero()) an = self.__E.anlist(k) # list of Sage Integers pi = R.pi() sqrtN = R(self.__E.conductor()).sqrt() z = (-2*pi/sqrtN).exp() zpow = z # Compute series sum and accumulate floating point errors L = R.zero() error = Rerror.zero() for n in xrange(1,k+1): term = (zpow * an[n])/n zpow *= z L += term # We express relative error in units of epsilon, where # epsilon is a number divided by 2^precision. # Instead of multiplying the error by 2 after the loop # (to account for L *= 2), we already multiply it now. # # For multiplication and division, the relative error # in epsilons is bounded by (1+e)^n - 1, where n is the # number of operations (assuming exact inputs). # exp(x) additionally multiplies this error by abs(x) and # adds one epsilon. The inputs pi and sqrtN each contribute # another epsilon. # Assuming that 2*pi/sqrtN <= 2, the relative error for z is # 7 epsilon. This implies a relative error of (8n-1) epsilon # for zpow. We add 2 for the computation of term and 1/2 to # compensate for the approximation (1+e)^n = 1+ne. # # The error of the addition is at most half an ulp of the # result. # # Multiplying everything by two gives: error += term.epsilon(Rerror)*(16*n + 3) + L.ulp(Rerror) L *= 2 # Add series error (we use (-2)/(z-1) instead of 2/(1-z) # because this causes 1/(1-z) to be rounded up) error += ((-2)*Rerror(zpow)) / Rerror(z - 1) return (L, error)
def _sage_(self): """ Convert self to a Sage object. EXAMPLES:: sage: a = axiom(1/2); a #optional - axiom 1 - 2 sage: a.sage() #optional - axiom 1/2 sage: _.parent() #optional - axiom Rational Field sage: gp(axiom(1/2)) #optional - axiom 1/2 DoubleFloat's in Axiom are converted to be in RDF in Sage. :: sage: axiom(2.0).as_type('DoubleFloat').sage() #optional - axiom 2.0 sage: _.parent() #optional - axiom Real Double Field sage: axiom(2.1234)._sage_() #optional - axiom 2.12340000000000 sage: _.parent() #optional - axiom Real Field with 53 bits of precision sage: a = RealField(100)(pi) sage: axiom(a)._sage_() #optional - axiom 3.1415926535897932384626433833 sage: _.parent() #optional - axiom Real Field with 100 bits of precision sage: axiom(a)._sage_() == a #optional - axiom True sage: axiom(2.0)._sage_() #optional - axiom 2.00000000000000 sage: _.parent() #optional - axiom Real Field with 53 bits of precision We can also convert Axiom's polynomials to Sage polynomials. sage: a = axiom(x^2 + 1) #optional - axiom sage: a.type() #optional - axiom Polynomial Integer sage: a.sage() #optional - axiom x^2 + 1 sage: _.parent() #optional - axiom Univariate Polynomial Ring in x over Integer Ring sage: axiom('x^2 + y^2 + 1/2').sage() #optional - axiom y^2 + x^2 + 1/2 sage: _.parent() #optional - axiom Multivariate Polynomial Ring in y, x over Rational Field """ P = self._check_valid() type = str(self.type()) if type in ["Type", "Domain"]: return self._sage_domain() if type == "Float": from sage.rings.all import RealField, ZZ prec = max(self.mantissa().length()._sage_(), 53) R = RealField(prec) x, e, b = self.unparsed_input_form().lstrip('float(').rstrip( ')').split(',') return R(ZZ(x) * ZZ(b)**ZZ(e)) elif type == "DoubleFloat": from sage.rings.all import RDF return RDF(repr(self)) elif type in ["PositiveInteger", "Integer"]: from sage.rings.all import ZZ return ZZ(repr(self)) elif type.startswith('Polynomial'): from sage.rings.all import PolynomialRing base_ring = P(type.lstrip('Polynomial '))._sage_domain() vars = str(self.variables())[1:-1] R = PolynomialRing(base_ring, vars) return R(self.unparsed_input_form()) elif type.startswith('Fraction'): return self.numer().sage() / self.denom().sage() #If all else fails, try using the unparsed input form try: import sage.misc.sage_eval vars = sage.symbolic.ring.var(str(self.variables())[1:-1]) if isinstance(vars, tuple): return sage.misc.sage_eval.sage_eval( self.unparsed_input_form(), locals={str(x): x for x in vars}) else: return sage.misc.sage_eval.sage_eval( self.unparsed_input_form(), locals={str(vars): vars}) except Exception: raise NotImplementedError
def frequency_distribution(self, length=1, prec=0): """ Returns the probability space of character frequencies. The output of this method is different from that of the method :func:`characteristic_frequency() <sage.monoids.string_monoid.AlphabeticStringMonoid.characteristic_frequency>`. One can think of the characteristic frequency probability of an element in an alphabet `A` as the expected probability of that element occurring. Let `S` be a string encoded using elements of `A`. The frequency probability distribution corresponding to `S` provides us with the frequency probability of each element of `A` as observed occurring in `S`. Thus one distribution provides expected probabilities, while the other provides observed probabilities. INPUT: - ``length`` -- (default ``1``) if ``length=1`` then consider the probability space of monogram frequency, i.e. probability distribution of single characters. If ``length=2`` then consider the probability space of digram frequency, i.e. probability distribution of pairs of characters. This method currently supports the generation of probability spaces for monogram frequency (``length=1``) and digram frequency (``length=2``). - ``prec`` -- (default ``0``) a non-negative integer representing the precision (in number of bits) of a floating-point number. The default value ``prec=0`` means that we use 53 bits to represent the mantissa of a floating-point number. For more information on the precision of floating-point numbers, see the function :func:`RealField() <sage.rings.real_mpfr.RealField>` or refer to the module :mod:`real_mpfr <sage.rings.real_mpfr>`. EXAMPLES: Capital letters of the English alphabet:: sage: M = AlphabeticStrings().encoding("abcd") sage: L = M.frequency_distribution().function() sage: sorted(L.items()) <BLANKLINE> [(A, 0.250000000000000), (B, 0.250000000000000), (C, 0.250000000000000), (D, 0.250000000000000)] The binary number system:: sage: M = BinaryStrings().encoding("abcd") sage: L = M.frequency_distribution().function() sage: sorted(L.items()) [(0, 0.593750000000000), (1, 0.406250000000000)] The hexadecimal number system:: sage: M = HexadecimalStrings().encoding("abcd") sage: L = M.frequency_distribution().function() sage: sorted(L.items()) <BLANKLINE> [(1, 0.125000000000000), (2, 0.125000000000000), (3, 0.125000000000000), (4, 0.125000000000000), (6, 0.500000000000000)] Get the observed frequency probability distribution of digrams in the string "ABCD". This string consists of the following digrams: "AB", "BC", and "CD". Now find out the frequency probability of each of these digrams as they occur in the string "ABCD":: sage: M = AlphabeticStrings().encoding("abcd") sage: D = M.frequency_distribution(length=2).function() sage: sorted(D.items()) [(AB, 0.333333333333333), (BC, 0.333333333333333), (CD, 0.333333333333333)] """ if not length in (1, 2): raise NotImplementedError("Not implemented") if prec == 0: RR = RealField() else: RR = RealField(prec) S = self.parent() n = S.ngens() if length == 1: Alph = S.gens() else: Alph = tuple([ x*y for x in S.gens() for y in S.gens() ]) X = {} N = len(self)-length+1 eps = RR(Integer(1)/N) for i in range(N): c = self[i:i+length] if c in X: X[c] += eps else: X[c] = eps # Return a dictionary of probability distribution. This should # allow for easier parsing of the dictionary. from sage.probability.random_variable import DiscreteProbabilitySpace return DiscreteProbabilitySpace(Alph, X, RR)
def an_numerical(self, prec=None, use_database=True, proof=None): r""" Return the numerical analytic order of `Sha`, which is a floating point number in all cases. INPUT: - ``prec`` - integer (default: 53) bits precision -- used for the L-series computation, period, regulator, etc. - ``use_database`` - whether the rank and generators should be looked up in the database if possible. Default is ``True`` - ``proof`` - bool or ``None`` (default: ``None``, see proof.[tab] or sage.structure.proof) proof option passed onto regulator and rank computation. .. note:: See also the :meth:`an` command, which will return a provably correct integer when the rank is 0 or 1. .. WARNING:: If the curve's generators are not known, computing them may be very time-consuming. Also, computation of the L-series derivative will be time-consuming for large rank and large conductor, and the computation time for this may increase substantially at greater precision. However, use of very low precision less than about 10 can cause the underlying PARI library functions to fail. EXAMPLES:: sage: EllipticCurve('11a').sha().an_numerical() 1.00000000000000 sage: EllipticCurve('37a').sha().an_numerical() 1.00000000000000 sage: EllipticCurve('389a').sha().an_numerical() 1.00000000000000 sage: EllipticCurve('66b3').sha().an_numerical() 4.00000000000000 sage: EllipticCurve('5077a').sha().an_numerical() 1.00000000000000 A rank 4 curve:: sage: EllipticCurve([1, -1, 0, -79, 289]).sha().an_numerical() # long time (3s on sage.math, 2011) 1.00000000000000 A rank 5 curve:: sage: EllipticCurve([0, 0, 1, -79, 342]).sha().an_numerical(prec=10, proof=False) # long time (22s on sage.math, 2011) 1.0 See :trac:`1115`:: sage: sha = EllipticCurve('37a1').sha() sage: [sha.an_numerical(prec) for prec in range(40,100,10)] # long time (3s on sage.math, 2013) [1.0000000000, 1.0000000000000, 1.0000000000000000, 1.0000000000000000000, 1.0000000000000000000000, 1.0000000000000000000000000] """ if prec is None: prec = RealField().precision() RR = RealField(prec) prec2 = prec + 2 RR2 = RealField(prec2) try: an = self.__an_numerical if an.parent().precision() >= prec: return RR(an) else: # cached precision too low pass except AttributeError: pass # it's critical to switch to the minimal model. E = self.Emin r = Integer(E.rank(use_database=use_database, proof=proof)) L = E.lseries().dokchitser(prec=prec2) Lr = RR2(L.derivative(1, r)) # L.derivative() returns a Complex Om = RR2(E.period_lattice().omega(prec2)) Reg = E.regulator(use_database=use_database, proof=proof, precision=prec2) T = E.torsion_order() cp = E.tamagawa_product() Sha = RR((Lr * T * T) / (r.factorial() * Om * cp * Reg)) self.__an_numerical = Sha return Sha
def deriv_at1(self, k=None, prec=None): r""" Compute `L'(E,1)` using `k` terms of the series for `L'(E,1)`, under the assumption that `L(E,1) = 0`. The algorithm used is from Section 7.5.3 of Henri Cohen's book *A Course in Computational Algebraic Number Theory*. INPUT: - ``k`` -- number of terms of the series. If zero or ``None``, use `k = \sqrt{N}`, where `N` is the conductor. - ``prec`` -- numerical precision in bits. If zero or ``None``, use a reasonable automatic default. OUTPUT: A tuple of real numbers ``(L1, err)`` where ``L1`` is an approximation for `L'(E,1)` and ``err`` is a bound on the error in the approximation. .. WARNING:: This function only makes sense if `L(E)` has positive order of vanishing at 1, or equivalently if `L(E,1) = 0`. ALGORITHM: - Compute the root number eps. If it is 1, return 0. - Compute the Fourier coefficients `a_n`, for `n` up to and including `k`. - Compute the sum .. MATH:: 2 \cdot \sum_{n=1}^{k} (a_n / n) \cdot E_1(2 \pi n/\sqrt{N}), where `N` is the conductor of `E`, and `E_1` is the exponential integral function. - Compute a bound on the tail end of the series, which is .. MATH:: 2 e^{-2 \pi (k+1) / \sqrt{N}} / (1 - e^{-2 \pi/\sqrt{N}}). For a proof see [Grigorov-Jorza-Patrascu-Patrikis-Stein]. This is exactly the same as the bound for the approximation to `L(E,1)` produced by :meth:`at1`. EXAMPLES:: sage: E = EllipticCurve('37a') sage: E.lseries().deriv_at1() (0.3059866, 0.000801045) sage: E.lseries().deriv_at1(100) (0.3059997738340523018204836833216764744526377745903, 1.52493e-45) sage: E.lseries().deriv_at1(1000) (0.305999773834052301820483683321676474452637774590771998..., 2.75031e-449) With less numerical precision, the error is bounded by numerical accuracy:: sage: L,err = E.lseries().deriv_at1(100, prec=64) sage: L,err (0.305999773834052302, 5.55318e-18) sage: parent(L) Real Field with 64 bits of precision sage: parent(err) Real Field with 24 bits of precision and rounding RNDU Rank 2 and rank 3 elliptic curves:: sage: E = EllipticCurve('389a1') sage: E.lseries().deriv_at1() (0.0000000, 0.000000) sage: E = EllipticCurve((1, 0, 1, -131, 558)) # curve 59450i1 sage: E.lseries().deriv_at1() (-0.00010911444, 0.142428) sage: E.lseries().deriv_at1(4000) (6.990...e-50, 1.31318e-43) """ sqrtN = sqrt(self.__E.conductor()) if k: k = int(k) else: k = int(ceil(sqrtN)) if prec: prec = int(prec) else: # Estimate number of bits for the computation, based on error # estimate below (the denominator of that error is close enough # to 1 that we can ignore it). # 9.065 = 2*Pi/log(2) # 1.443 = 1/log(2) # 12 is an arbitrary extra number of bits (it is chosen # such that the precision is 24 bits when the conductor # equals 11 and k is the default value 4) prec = int(9.065 * k / sqrtN + 1.443 * log(k)) + 12 R = RealField(prec) # Compute error term with bounded precision of 24 bits and # round towards +infinity Rerror = RealField(24, rnd='RNDU') if self.__E.root_number() == 1: # Order of vanishing at 1 of L(E) is even and assumed to be # positive, so L'(E,1) = 0. return (R.zero(), Rerror.zero()) an = self.__E.anlist(k) # list of Sage Integers pi = R.pi() sqrtN = R(self.__E.conductor()).sqrt() v = exp_integral.exponential_integral_1(2 * pi / sqrtN, k) # Compute series sum and accumulate floating point errors L = R.zero() error = Rerror.zero() # Sum of |an[n]|/n sumann = Rerror.zero() for n in range(1, k + 1): term = (v[n - 1] * an[n]) / n L += term error += term.epsilon(Rerror) * 5 + L.ulp(Rerror) sumann += Rerror(an[n].abs()) / n L *= 2 # Add error term for exponential_integral_1() errors. # Absolute error for 2*v[i] is 4*max(1, v[0])*2^-prec if v[0] > 1.0: sumann *= Rerror(v[0]) error += (sumann >> (prec - 2)) # Add series error (we use (-2)/(z-1) instead of 2/(1-z) # because this causes 1/(1-z) to be rounded up) z = (-2 * pi / sqrtN).exp() zpow = ((-2 * (k + 1)) * pi / sqrtN).exp() error += ((-2) * Rerror(zpow)) / Rerror(z - 1) return (L, error)
""" Complex Elliptic Curve L-series """ from sage.structure.sage_object import SageObject from sage.rings.all import (RealField, RationalField, ComplexField) from math import sqrt, exp, ceil import sage.functions.exp_integral as exp_integral R = RealField() Q = RationalField() C = ComplexField() import sage.misc.all as misc class Lseries_ell(SageObject): """ An elliptic curve $L$-series. EXAMPLES: """ def __init__(self, E): """ Create an elliptic curve $L$-series. EXAMPLES: sage: EllipticCurve([1..5]).lseries() Complex L-series of the Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field """
def double_integral_zero_infty(Phi, tau1, tau2): p = Phi.parent().prime() K = tau1.parent() R = PolynomialRing(K, 'x') x = R.gen() R1 = PowerSeriesRing(K, 'r1') r1 = R1.gen() try: R1.set_default_prec(Phi.precision_absolute()) except AttributeError: R1.set_default_prec(Phi.precision_relative()) level = Phi._map._manin.level() E0inf = [M2Z([0, -1, level, 0])] E0Zp = [M2Z([p, a, 0, 1]) for a in range(p)] predicted_evals = num_evals(tau1, tau2) a, b, c, d = find_center(p, level, tau1, tau2).list() h = M2Z([a, b, c, d]) E = [h * e0 for e0 in E0Zp + E0inf] resadd = 0 resmul = 1 total_evals = 0 percentage = QQ(0) ii = 0 f = (x - tau2) / (x - tau1) while len(E) > 0: ii += 1 increment = QQ((100 - percentage) / len(E)) verbose( 'remaining %s percent (and done %s of %s evaluations)' % (RealField(10)(100 - percentage), total_evals, predicted_evals)) newE = [] for e in E: a, b, c, d = e.list() assert ZZ(c) % level == 0 try: y0 = f((a * r1 + b) / (c * r1 + d)) val = y0(y0.parent().base_ring()(0)) if all([xx.valuation(p) > 0 for xx in (y0 / val - 1).list()]): if total_evals % 100 == 0: Phi._map._codomain.clear_cache() pol = val.log(p_branch=0) + ( (y0.derivative() / y0).integral()) V = [0] * pol.valuation() + pol.shift( -pol.valuation()).list() try: phimap = Phi._map(M2Z([b, d, a, c])) except OverflowError: print(a, b, c, d) raise OverflowError('Matrix too large?') # mu_e0 = ZZ(phimap.moment(0).rational_reconstruction()) mu_e0 = ZZ(Phi._liftee._map(M2Z([b, d, a, c])).moment(0)) mu_e = [mu_e0] + [ phimap.moment(o).lift() for o in range(1, len(V)) ] resadd += sum(starmap(mul, izip(V, mu_e))) resmul *= val**mu_e0 percentage += increment total_evals += 1 else: newE.extend([e * e0 for e0 in E0Zp]) except ZeroDivisionError: #raise RuntimeError,'Probably not enough working precision...' newE.extend([e * e0 for e0 in E0Zp]) E = newE verbose('total evaluations = %s' % total_evals) val = resmul.valuation() return p**val * K.teichmuller(p**(-val) * resmul) * resadd.exp()
def __init__(self): r""" Create free alphabetic string monoid on generators A-Z. EXAMPLES:: sage: S = AlphabeticStrings(); S Free alphabetic string monoid on A-Z sage: S.gen(0) A sage: S.gen(25) Z sage: S([ i for i in range(26) ]) ABCDEFGHIJKLMNOPQRSTUVWXYZ """ from sage.rings.all import RealField RR = RealField() # The characteristic frequency probability distribution of # Robert Edward Lewand. self._characteristic_frequency_lewand = { "A": RR(0.08167), "B": RR(0.01492), "C": RR(0.02782), "D": RR(0.04253), "E": RR(0.12702), "F": RR(0.02228), "G": RR(0.02015), "H": RR(0.06094), "I": RR(0.06966), "J": RR(0.00153), "K": RR(0.00772), "L": RR(0.04025), "M": RR(0.02406), "N": RR(0.06749), "O": RR(0.07507), "P": RR(0.01929), "Q": RR(0.00095), "R": RR(0.05987), "S": RR(0.06327), "T": RR(0.09056), "U": RR(0.02758), "V": RR(0.00978), "W": RR(0.02360), "X": RR(0.00150), "Y": RR(0.01974), "Z": RR(0.00074) } # The characteristic frequency probability distribution of # H. Beker and F. Piper. self._characteristic_frequency_beker_piper = { "A": RR(0.082), "B": RR(0.015), "C": RR(0.028), "D": RR(0.043), "E": RR(0.127), "F": RR(0.022), "G": RR(0.020), "H": RR(0.061), "I": RR(0.070), "J": RR(0.002), "K": RR(0.008), "L": RR(0.040), "M": RR(0.024), "N": RR(0.067), "O": RR(0.075), "P": RR(0.019), "Q": RR(0.001), "R": RR(0.060), "S": RR(0.063), "T": RR(0.091), "U": RR(0.028), "V": RR(0.010), "W": RR(0.023), "X": RR(0.001), "Y": RR(0.020), "Z": RR(0.001) } alph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' StringMonoid_class.__init__(self, 26, [alph[i] for i in range(26)])
def __init__(self, base, p, discriminant, abtuple=None, level=1, grouptype=None, seed=None, outfile=None, magma=None, timeout=0, use_shapiro=True, character=None, nscartan=None, matrix_group=False): self.seed = seed self.magma = magma self._use_shapiro = use_shapiro self._matrix_group = matrix_group if seed is not None: verbose('Setting Magma seed to %s' % seed) self.magma.eval('SetSeed(%s)' % seed) self.F = base if self.F != QQ: Fideal = self.F.maximal_order().ideal self.ideal_p = Fideal(p) self.norm_p = ZZ(p.norm()) self.discriminant = Fideal(discriminant) self.level = Fideal(level) else: self.ideal_p = ZZ(p) self.norm_p = ZZ(p) self.discriminant = ZZ(discriminant) self.level = ZZ(level) if nscartan is not None: self.level *= nscartan self.p = self.norm_p.prime_divisors()[0] if not self.ideal_p.is_prime(): raise ValueError('p (=%s) must be prime' % self.p) if self._use_shapiro: covol = covolume(self.F, self.discriminant, self.level) else: covol = covolume(self.F, self.discriminant, self.ideal_p * self.level) verbose('Estimated Covolume = %s' % covol) difficulty = covol**2 verbose('Estimated Difficulty = %s' % difficulty) verbose('Initializing arithmetic group G(pn)...') t = walltime() lev = self.ideal_p * self.level if character is not None: lev = [lev, character] self.Gpn = ArithGroup(self.F, self.discriminant, abtuple, lev, grouptype=grouptype, magma=magma, compute_presentation=not self._use_shapiro, timeout=timeout, nscartan=nscartan) self.Gpn.get_embedding = self.get_embedding self.Gpn.embed = self.embed self.Gpn.embed_matrix = self.embed_matrix verbose('Initializing arithmetic group G(n)...') lev = self.level if character is not None: lev = [lev, character] self.Gn = ArithGroup(self.F, self.discriminant, abtuple, lev, info_magma=self.Gpn, grouptype=grouptype, magma=magma, compute_presentation=True, timeout=timeout, nscartan=nscartan) self.Gn.embed_matrix = self.embed_matrix t = walltime(t) verbose('Time for calculation T = %s' % t) verbose('T = %s x difficulty' % RealField(25)(t / difficulty)) self.Gn.get_embedding = self.get_embedding self.Gn.embed = self.embed if hasattr(self.Gn.B, 'is_division_algebra'): fwrite( '# B = F<i,j,k>, with i^2 = %s and j^2 = %s' % (self.Gn.B.gens()[0]**2, self.Gn.B.gens()[1]**2), outfile) else: fwrite('# B = M_2(F)', outfile) try: basis_data_1 = list(self.Gn.Obasis) if not self.use_shapiro(): basis_data_p = list(self.Gpn.Obasis) except AttributeError: try: basis_data_1 = self.Gn.basis_invmat.inverse().columns() if not self.use_shapiro(): basis_data_p = self.Gpn.basis_invmat.inverse().columns() except AttributeError: basis_data_1 = '?' basis_data_p = '?' self._prec = -1 self.get_embedding(200) fwrite('# R with basis %s' % basis_data_1, outfile) self.Gn.get_Up_reps = self.get_Up_reps if not self.use_shapiro(): fwrite('# R(p) with basis %s' % basis_data_p, outfile) self.Gpn.get_Up_reps = self.get_Up_reps self.Gn.wp = self.wp self.Gpn.wp = self.wp verbose('Done initializing arithmetic groups') verbose('Done initialization of BigArithmeticGroup')
def in_fundamental_domain(self,P): A = self._M * Matrix(RealField(),2,1,[P.x,P.y]) return A[0,0] >= 0 and A[0,0] < 1 and A[1,0] >= 0 and A[1,0] < 1
def gghlite_params(n, kappa, target_lambda=_sage_const_80, xi=None, rerand=False, gddh_hard=False): """ Return GGHLite parameter estimates for a given dimension ‘n‘ and multilinearity level ‘κ‘. :param n: lattice dimension, must be power of two :param kappa: multilinearity level ‘κ>1‘ :param target_lambda: target security level :param xi: pick ‘ξ‘ manually :param rerand:is the instance supposed to support re-randomisation This should be true for ‘N‘-partite DH key exchange and false for iO and friends. :param gddh_hard:should the GDDH problem be hard :returns: parameter choices for a GGHLite-like graded-encoding scheme """ n = ZZ(n) kappa = ZZ(kappa) RR = RealField(_sage_const_2 * target_lambda) sigma = RR(_sage_const_4 * pi * n * sqrt(e * log(_sage_const_8 * n) / pi)) ell_g = RR(_sage_const_4 * sqrt(pi * e * n) / (sigma)) sigma_p = RR(_sage_const_7 * n**(_sage_const_2p5) * log(n)**(_sage_const_1p5) * sigma) ell_b = RR(_sage_const_1p0 / (_sage_const_2p0 * sqrt(pi * e * n)) * sigma_p) eps = RR(log(target_lambda) / kappa) ell = RR(log(_sage_const_8 * n * sigma, _sage_const_2)) m = RR(_sage_const_2) if rerand: sigma_s = RR(n**(_sage_const_1p5) * sigma_p**_sage_const_2 * sqrt(_sage_const_8 * pi / eps) / ell_b) if gddh_hard: sigma_s *= _sage_const_2**target_lambda * sqrt( kappa) * target_lambda / n else: sigma_s = _sage_const_1 normk = sqrt(n)**(kappa - _sage_const_1) * ( (sigma_p)**_sage_const_2 * n**RR(_sage_const_1p5) + _sage_const_2 * sigma_s * sigma_p * n**RR(_sage_const_1p5))**kappa q_base = RR(n * ell_g * normk) if xi is None: log_negl = target_lambda xivar = var('xivar') f = (ell + log_negl) == (_sage_const_2 * xivar / (_sage_const_1 - _sage_const_2 * xivar)) * log( q_base, _sage_const_2) xi = RR(f.solve(xivar)[_sage_const_0].rhs()) q = q_base**(ZZ(_sage_const_2) / (_sage_const_1 - _sage_const_2 * xi)) t = q**xi * _sage_const_2**(-ell + _sage_const_2) assert (q > _sage_const_2 * t * n * sigma**(_sage_const_1 / xi)) assert (abs(xi * log(q, _sage_const_2) - log_negl - ell) <= _sage_const_0p1) else: q = q_base**(ZZ(_sage_const_2) / (_sage_const_1 - _sage_const_2 * xi)) t = q**xi * _sage_const_2**(-ell + _sage_const_2) params = OrderedDict() params[u"κ"] = kappa params["n"] = n params[u"σ"] = sigma params[u"σ’"] = sigma_p if rerand: params[u"σ^*"] = sigma_s params[u"lnorm_κ"] = normk params[ u"unorm_κ"] = normk # if we had re-rand at higher levels this could be bigger params[u"ℓ_g"] = ell_g params[u"ℓ_b"] = ell_b params[u"ǫ"] = eps params[u"m"] = m params[u"ξ"] = xi params["q"] = q params["|enc|"] = RR(log(q, _sage_const_2) * n) if rerand: params["|par|"] = (_sage_const_2 + _sage_const_1 + _sage_const_1) * RR( log(q, _sage_const_2) * n) else: params["|par|"] = RR(log(q, _sage_const_2) * n) return params
def __init__(self, B, sigma=1, c=None, precision=None): r""" Construct a discrete Gaussian sampler over the lattice `Λ(B)` with parameter ``sigma`` and center `c`. INPUT: - ``B`` -- a basis for the lattice, one of the following: - an integer matrix, - an object with a ``matrix()`` method, e.g. ``ZZ^n``, or - an object where ``matrix(B)`` succeeds, e.g. a list of vectors. - ``sigma`` -- Gaussian parameter `σ>0`. - ``c`` -- center `c`, any vector in `\ZZ^n` is supported, but `c ∈ Λ(B)` is faster. - ``precision`` -- bit precision `≥ 53`. EXAMPLE:: sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: n = 2; sigma = 3.0; m = 5000 sage: D = DiscreteGaussianDistributionLatticeSampler(ZZ^n, sigma) sage: f = D.f sage: c = D._normalisation_factor_zz(); c 56.2162803067524 sage: l = [D() for _ in xrange(m)] sage: v = vector(ZZ, n, (-3,-3)) sage: l.count(v), ZZ(round(m*f(v)/c)) (39, 33) sage: target = vector(ZZ, n, (0,0)) sage: l.count(target), ZZ(round(m*f(target)/c)) (116, 89) sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: qf = QuadraticForm(matrix(3, [2, 1, 1, 1, 2, 1, 1, 1, 2])) sage: D = DiscreteGaussianDistributionLatticeSampler(qf, 3.0); D Discrete Gaussian sampler with σ = 3.000000, c=(0, 0, 0) over lattice with basis <BLANKLINE> [2 1 1] [1 2 1] [1 1 2] sage: D() (0, 1, -1) """ precision = DiscreteGaussianDistributionLatticeSampler.compute_precision(precision, sigma) self._RR = RealField(precision) self._sigma = self._RR(sigma) try: B = matrix(B) except ValueError: pass try: B = B.matrix() except AttributeError: pass self.B = B self._G = B.gram_schmidt()[0] try: c = vector(ZZ, B.ncols(), c) except TypeError: try: c = vector(QQ, B.ncols(), c) except TypeError: c = vector(RR, B.ncols(), c) self._c = c self.f = lambda x: exp(-(vector(ZZ, B.ncols(), x)-c).norm()**2/(2*self._sigma**2)) # deal with trivial case first, it is common if self._G == 1 and self._c == 0: self._c_in_lattice = True D = DiscreteGaussianDistributionIntegerSampler(sigma=sigma) self.D = tuple([D for _ in range(self.B.nrows())]) self.VS = FreeModule(ZZ, B.nrows()) return w = B.solve_left(c) if w in ZZ**B.nrows(): self._c_in_lattice = True D = [] for i in range(self.B.nrows()): sigma_ = self._sigma/self._G[i].norm() D.append( DiscreteGaussianDistributionIntegerSampler(sigma=sigma_) ) self.D = tuple(D) self.VS = FreeModule(ZZ, B.nrows()) else: self._c_in_lattice = False
def in_fundamental_domain(self,P): #assert(isinstance(P,Pointxy)) A=self._M*Matrix(RealField(),_sage_const_2 ,_sage_const_1 ,[P.x,P.y]) return A[_sage_const_0 ,_sage_const_0 ]>=_sage_const_0 and A[_sage_const_0 ,_sage_const_0 ]<_sage_const_1 and A[_sage_const_1 ,_sage_const_0 ]>=_sage_const_0 and A[_sage_const_1 ,_sage_const_0 ]<_sage_const_1
def deriv_at1(self, k=None, prec=None): r""" Compute `L'(E,1)` using `k` terms of the series for `L'(E,1)`, under the assumption that `L(E,1) = 0`. The algorithm used is from Section 7.5.3 of Henri Cohen's book ``A Course in Computational Algebraic Number Theory.'' INPUT: - ``k`` -- number of terms of the series. If zero or ``None``, use `k = \sqrt(N)`, where `N` is the conductor. - ``prec`` -- numerical precision in bits. If zero or ``None``, use a reasonable automatic default. OUTPUT: A tuple of real numbers ``(L1, err)`` where ``L1`` is an approximation for `L'(E,1)` and ``err`` is a bound on the error in the approximation. .. WARNING:: This function only makes sense if `L(E)` has positive order of vanishing at 1, or equivalently if `L(E,1) = 0`. ALGORITHM: - Compute the root number eps. If it is 1, return 0. - Compute the Fourier coefficients `a_n`, for `n` up to and including `k`. - Compute the sum .. MATH:: 2 * \sum_{n=1}^{k} (a_n / n) * E_1(2 \pi n/\sqrt{N}), where `N` is the conductor of `E`, and `E_1` is the exponential integral function. - Compute a bound on the tail end of the series, which is .. MATH:: 2 e^{-2 \pi (k+1) / \sqrt{N}} / (1 - e^{-2 \pi/\sqrt{N}}). For a proof see [Grigorov-Jorza-Patrascu-Patrikis-Stein]. This is exactly the same as the bound for the approximation to `L(E,1)` produced by :meth:`at1`. EXAMPLES:: sage: E = EllipticCurve('37a') sage: E.lseries().deriv_at1() (0.3059866, 0.000801045) sage: E.lseries().deriv_at1(100) (0.3059997738340523018204836833216764744526377745903, 1.52493e-45) sage: E.lseries().deriv_at1(1000) (0.305999773834052301820483683321676474452637774590771998..., 2.75031e-449) With less numerical precision, the error is bounded by numerical accuracy:: sage: L,err = E.lseries().deriv_at1(100, prec=64) sage: L,err (0.305999773834052302, 5.55318e-18) sage: parent(L) Real Field with 64 bits of precision sage: parent(err) Real Field with 24 bits of precision and rounding RNDU Rank 2 and rank 3 elliptic curves:: sage: E = EllipticCurve('389a1') sage: E.lseries().deriv_at1() (0.0000000, 0.000000) sage: E = EllipticCurve((1, 0, 1, -131, 558)) # curve 59450i1 sage: E.lseries().deriv_at1() (-0.00010911444, 0.142428) sage: E.lseries().deriv_at1(4000) (6.9902290...e-50, 1.31318e-43) """ sqrtN = sqrt(self.__E.conductor()) if k: k = int(k) else: k = int(ceil(sqrtN)) if prec: prec = int(prec) else: # Estimate number of bits for the computation, based on error # estimate below (the denominator of that error is close enough # to 1 that we can ignore it). # 9.065 = 2*Pi/log(2) # 1.443 = 1/log(2) # 12 is an arbitrary extra number of bits (it is chosen # such that the precision is 24 bits when the conductor # equals 11 and k is the default value 4) prec = int(9.065*k/sqrtN + 1.443*log(k)) + 12 R = RealField(prec) # Compute error term with bounded precision of 24 bits and # round towards +infinity Rerror = RealField(24, rnd='RNDU') if self.__E.root_number() == 1: # Order of vanishing at 1 of L(E) is even and assumed to be # positive, so L'(E,1) = 0. return (R.zero(), Rerror.zero()) an = self.__E.anlist(k) # list of Sage Integers pi = R.pi() sqrtN = R(self.__E.conductor()).sqrt() v = exp_integral.exponential_integral_1(2*pi/sqrtN, k) # Compute series sum and accumulate floating point errors L = R.zero() error = Rerror.zero() # Sum of |an[n]|/n sumann = Rerror.zero() for n in xrange(1,k+1): term = (v[n-1] * an[n])/n L += term error += term.epsilon(Rerror)*5 + L.ulp(Rerror) sumann += Rerror(an[n].abs())/n L *= 2 # Add error term for exponential_integral_1() errors. # Absolute error for 2*v[i] is 4*max(1, v[0])*2^-prec if v[0] > 1.0: sumann *= Rerror(v[0]) error += (sumann >> (prec - 2)) # Add series error (we use (-2)/(z-1) instead of 2/(1-z) # because this causes 1/(1-z) to be rounded up) z = (-2*pi/sqrtN).exp() zpow = ((-2*(k+1))*pi/sqrtN).exp() error += ((-2)*Rerror(zpow)) / Rerror(z - 1) return (L, error)
def at1(self, k=None, prec=None): r""" Compute `L(E,1)` using `k` terms of the series for `L(E,1)` as explained in Section 7.5.3 of Henri Cohen's book *A Course in Computational Algebraic Number Theory*. If the argument `k` is not specified, then it defaults to `\sqrt{N}`, where `N` is the conductor. INPUT: - ``k`` -- number of terms of the series. If zero or ``None``, use `k = \sqrt{N}`, where `N` is the conductor. - ``prec`` -- numerical precision in bits. If zero or ``None``, use a reasonable automatic default. OUTPUT: A tuple of real numbers ``(L, err)`` where ``L`` is an approximation for `L(E,1)` and ``err`` is a bound on the error in the approximation. This function is disjoint from the PARI ``elllseries`` command, which is for a similar purpose. To use that command (via the PARI C library), simply type ``E.pari_mincurve().elllseries(1)``. ALGORITHM: - Compute the root number eps. If it is -1, return 0. - Compute the Fourier coefficients `a_n`, for `n` up to and including `k`. - Compute the sum .. MATH:: 2 \cdot \sum_{n=1}^{k} \frac{a_n}{n} \cdot \exp(-2*pi*n/\sqrt{N}), where `N` is the conductor of `E`. - Compute a bound on the tail end of the series, which is .. MATH:: 2 e^{-2 \pi (k+1) / \sqrt{N}} / (1 - e^{-2 \pi/\sqrt{N}}). For a proof see [Grigov-Jorza-Patrascu-Patrikis-Stein]. EXAMPLES:: sage: L, err = EllipticCurve('11a1').lseries().at1() sage: L, err (0.253804, 0.000181444) sage: parent(L) Real Field with 24 bits of precision sage: E = EllipticCurve('37b') sage: E.lseries().at1() (0.7257177, 0.000800697) sage: E.lseries().at1(100) (0.7256810619361527823362055410263965487367603361763, 1.52469e-45) sage: L,err = E.lseries().at1(100, prec=128) sage: L 0.72568106193615278233620554102639654873 sage: parent(L) Real Field with 128 bits of precision sage: err 1.70693e-37 sage: parent(err) Real Field with 24 bits of precision and rounding RNDU Rank 1 through 3 elliptic curves:: sage: E = EllipticCurve('37a1') sage: E.lseries().at1() (0.0000000, 0.000000) sage: E = EllipticCurve('389a1') sage: E.lseries().at1() (-0.001769566, 0.00911776) sage: E = EllipticCurve('5077a1') sage: E.lseries().at1() (0.0000000, 0.000000) """ sqrtN = sqrt(self.__E.conductor()) if k: k = int(k) else: k = int(ceil(sqrtN)) if prec: prec = int(prec) else: # Use the same precision as deriv_at1() below for # consistency prec = int(9.065 * k / sqrtN + 1.443 * log(k)) + 12 R = RealField(prec) # Compute error term with bounded precision of 24 bits and # round towards +infinity Rerror = RealField(24, rnd='RNDU') if self.__E.root_number() == -1: return (R.zero(), Rerror.zero()) an = self.__E.anlist(k) # list of Sage Integers pi = R.pi() sqrtN = R(self.__E.conductor()).sqrt() z = (-2 * pi / sqrtN).exp() zpow = z # Compute series sum and accumulate floating point errors L = R.zero() error = Rerror.zero() for n in range(1, k + 1): term = (zpow * an[n]) / n zpow *= z L += term # We express relative error in units of epsilon, where # epsilon is a number divided by 2^precision. # Instead of multiplying the error by 2 after the loop # (to account for L *= 2), we already multiply it now. # # For multiplication and division, the relative error # in epsilons is bounded by (1+e)^n - 1, where n is the # number of operations (assuming exact inputs). # exp(x) additionally multiplies this error by abs(x) and # adds one epsilon. The inputs pi and sqrtN each contribute # another epsilon. # Assuming that 2*pi/sqrtN <= 2, the relative error for z is # 7 epsilon. This implies a relative error of (8n-1) epsilon # for zpow. We add 2 for the computation of term and 1/2 to # compensate for the approximation (1+e)^n = 1+ne. # # The error of the addition is at most half an ulp of the # result. # # Multiplying everything by two gives: error += term.epsilon(Rerror) * (16 * n + 3) + L.ulp(Rerror) L *= 2 # Add series error (we use (-2)/(z-1) instead of 2/(1-z) # because this causes 1/(1-z) to be rounded up) error += ((-2) * Rerror(zpow)) / Rerror(z - 1) return (L, error)
def __init__(self, B, sigma=1, c=None, precision=None): r""" Construct a discrete Gaussian sampler over the lattice `Λ(B)` with parameter ``sigma`` and center `c`. INPUT: - ``B`` -- a basis for the lattice, one of the following: - an integer matrix, - an object with a ``matrix()`` method, e.g. ``ZZ^n``, or - an object where ``matrix(B)`` succeeds, e.g. a list of vectors. - ``sigma`` -- Gaussian parameter `σ>0`. - ``c`` -- center `c`, any vector in `\ZZ^n` is supported, but `c ∈ Λ(B)` is faster. - ``precision`` -- bit precision `≥ 53`. EXAMPLES:: sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: n = 2; sigma = 3.0 sage: D = DiscreteGaussianDistributionLatticeSampler(ZZ^n, sigma) sage: f = D.f sage: c = D._normalisation_factor_zz(); c 56.2162803067524 sage: from collections import defaultdict sage: counter = defaultdict(Integer) sage: m = 0 sage: def add_samples(i): ....: global counter, m ....: for _ in range(i): ....: counter[D()] += 1 ....: m += 1 sage: v = vector(ZZ, n, (-3, -3)) sage: v.set_immutable() sage: while v not in counter: add_samples(1000) sage: while abs(m*f(v)*1.0/c/counter[v] - 1.0) >= 0.1: add_samples(1000) sage: v = vector(ZZ, n, (0, 0)) sage: v.set_immutable() sage: while v not in counter: add_samples(1000) sage: while abs(m*f(v)*1.0/c/counter[v] - 1.0) >= 0.1: add_samples(1000) sage: from sage.stats.distributions.discrete_gaussian_lattice import DiscreteGaussianDistributionLatticeSampler sage: qf = QuadraticForm(matrix(3, [2, 1, 1, 1, 2, 1, 1, 1, 2])) sage: D = DiscreteGaussianDistributionLatticeSampler(qf, 3.0); D Discrete Gaussian sampler with σ = 3.000000, c=(0, 0, 0) over lattice with basis <BLANKLINE> [2 1 1] [1 2 1] [1 1 2] sage: D().parent() is D.c.parent() True """ precision = DiscreteGaussianDistributionLatticeSampler.compute_precision( precision, sigma) self._RR = RealField(precision) self._sigma = self._RR(sigma) try: B = matrix(B) except (TypeError, ValueError): pass try: B = B.matrix() except AttributeError: pass self.B = B self._G = B.gram_schmidt()[0] try: c = vector(ZZ, B.ncols(), c) except TypeError: try: c = vector(QQ, B.ncols(), c) except TypeError: c = vector(RR, B.ncols(), c) self._c = c self.f = lambda x: exp(-(vector(ZZ, B.ncols(), x) - c).norm()**2 / (2 * self._sigma**2)) # deal with trivial case first, it is common if self._G == 1 and self._c == 0: self._c_in_lattice = True D = DiscreteGaussianDistributionIntegerSampler(sigma=sigma) self.D = tuple([D for _ in range(self.B.nrows())]) self.VS = FreeModule(ZZ, B.nrows()) return w = B.solve_left(c) if w in ZZ**B.nrows(): self._c_in_lattice = True D = [] for i in range(self.B.nrows()): sigma_ = self._sigma / self._G[i].norm() D.append( DiscreteGaussianDistributionIntegerSampler(sigma=sigma_)) self.D = tuple(D) self.VS = FreeModule(ZZ, B.nrows()) else: self._c_in_lattice = False