def __init__(self, N, delta=0.01, m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP11]_. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is used (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLindnerPeikert sage: RingLindnerPeikert(N=16) RingLWE(16, 1031, DiscreteGaussianPolynomialSamplerRejection(8, 2.803372, 53, 4), x^8 + 1, 'noise', 24) """ n = euler_phi(N) if m is None: m = 3 * n # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = var('c') c = find_root(2 * n * log(c) + n * (1 - c**2) + 40 * log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2 * n * log(2 / delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP11]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP11]_ s = sqrt(s_t_bound * floor(q / 4)) # Transform s into stddev stddev = s / sqrt(2 * pi.n()) D = DiscreteGaussianPolynomialSampler(n, stddev) RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m)
def __init__(self, n, delta=0.01, m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP11]_. INPUT: - ``n`` - security parameter (integer > 0) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``m=2*n + 128`` as in [LP11]_ (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import LindnerPeikert sage: LindnerPeikert(n=20) LWE(20, 2053, DiscreteGaussianSamplerRejection(3.600954, 53, 4), 'noise', 168) """ if m is None: m = 2 * n + 128 # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # (c*exp((1-c**2)/2))**(2*n) == 2**-40 # log((c*exp((1-c**2)/2))**(2*n)) == -40*log(2) # (2*n)*log(c*exp((1-c**2)/2)) == -40*log(2) # 2*n*(log(c)+log(exp((1-c**2)/2))) == -40*log(2) # 2*n*(log(c)+(1-c**2)/2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = var('c') c = find_root(2 * n * log(c) + n * (1 - c**2) + 40 * log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2 * n * log(2 / delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP11]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP11]_ s = sqrt(s_t_bound * floor(q / 4)) # Transform s into stddev stddev = s / sqrt(2 * pi.n()) D = DiscreteGaussianSampler(stddev) LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
def __init__(self, n, delta=0.01, m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``n`` - security parameter (integer > 0) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``m=2*n + 128`` as in [LP2011]_ (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import LindnerPeikert sage: LindnerPeikert(n=20) LWE(20, 2053, Discrete Gaussian sampler over the Integers with sigma = 3.600954 and c = 0, 'noise', 168) """ if m is None: m = 2*n + 128 # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # (c*exp((1-c**2)/2))**(2*n) == 2**-40 # log((c*exp((1-c**2)/2))**(2*n)) == -40*log(2) # (2*n)*log(c*exp((1-c**2)/2)) == -40*log(2) # 2*n*(log(c)+log(exp((1-c**2)/2))) == -40*log(2) # 2*n*(log(c)+(1-c**2)/2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = SR.var('c') c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP2011]_ s = sqrt(s_t_bound*floor(q/4)) # Transform s into stddev stddev = s/sqrt(2*pi.n()) D = DiscreteGaussianDistributionIntegerSampler(stddev) LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
def random_polygon_2d(num_vertices, **kwargs): r"""Generate a random polygon (2d) obtained by uniform sampling over the unit circle. INPUT: * ``num_vertices`` - the number of vertices of the generated polyhedron. * ``base_ring`` - (default: ``QQ``). The ring passed to the constructor of Polyhedron. alid options are ``QQ`` and ``RDF``. * ``scale`` - (default: 1). The scale factor; each vertex is chosen randomly from the unit circle, and then multiplied by scale. OUTPUT: A random polygon (object of type Polyhedron), whose vertices belong to a circle of radius ``scale``. NOTES: - If ``RDF`` is chosen as ``base_ring``, sometimes there are exceptions related to numerical errors, and show up as ``'FrozenSet'`` exceptions. This occurs particularly frequently for a large number of vertices (more than 30). """ from sage.functions.log import exp from sage.symbolic.constants import pi from sage.symbolic.all import I base_ring = kwargs['base_ring'] if 'base_ring' in kwargs else QQ scale = kwargs['scale'] if 'scale' in kwargs else 1 angles = [ random.uniform(0, 2 * pi.n(digits=5)) for i in range(num_vertices) ] vert = [[ scale * exp(I * angles[i]).real(), scale * exp(I * angles[i]).imag() ] for i in range(num_vertices)] return Polyhedron(vertices=vert, base_ring=base_ring)
def __init__(self, n, secret_dist='uniform', m=None): """ Construct LWE instance parameterised by security paramter ``n`` where the modulus ``q`` and the ``stddev`` of the noise are chosen as in [Reg09]_. INPUT: - ``n`` - security paramter (integer > 0) - ``secret_dist`` - distribution of the secret. See documentation of :class:`LWE` for details (default='uniform') - ``m`` - number of allowed samples or ``None`` if no such limit exists (default: ``None``) EXAMPLES:: sage: Regev(n=20) LWE(20, 401, DiscreteGaussianSamplerRejection(1.915069, 401, 4), 'uniform', None) """ q = ZZ(next_prime(n**2)) s = RR(1 / (RR(n).sqrt() * log(n, 2)**2) * q) D = DiscreteGaussianSampler(s / sqrt(2 * pi.n()), q) LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m)
def __init__(self, N, delta=0.01, m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is used (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLindnerPeikert sage: RingLindnerPeikert(N=16) RingLWE(16, 1031, Discrete Gaussian sampler for polynomials of degree < 8 with σ=2.803372 in each component, x^8 + 1, 'noise', 24) """ n = euler_phi(N) if m is None: m = 3*n # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = SR.var('c') c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP2011]_ s = sqrt(s_t_bound*floor(q/4)) # Transform s into stddev stddev = s/sqrt(2*pi.n()) D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, stddev) RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m)
def __init__(self, n, secret_dist='uniform', m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise are chosen as in [Reg09]_. INPUT: - ``n`` - security parameter (integer > 0) - ``secret_dist`` - distribution of the secret. See documentation of :class:`LWE` for details (default='uniform') - ``m`` - number of allowed samples or ``None`` if no such limit exists (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import Regev sage: Regev(n=20) LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 1.915069 and c = 401, 'uniform', None) """ q = ZZ(next_prime(n**2)) s = RR(1/(RR(n).sqrt() * log(n, 2)**2) * q) D = DiscreteGaussianDistributionIntegerSampler(s/sqrt(2*pi.n()), q) LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m)
# Fundamental constants in SI units c = constants.c G = constants.G # Astronomical constants in SI units pc = constants.parsec yr = constants.year au = constants.au # Sun solar_mass_kg = 1.98848e30 # Particle Data Group, PRD 98, 030001 (2018); http://pdg.lbl.gov/ solar_mass_m = G * solar_mass_kg / c**2 solar_mass_s = solar_mass_m / c solar_eq_radius_m = 6.957e8 # IAU Resolution B3; arXiv:1510.07674 solar_mean_density_SI = solar_mass_kg / (4 * pi.n() / 3 * solar_eq_radius_m**3) # aliases: Msol = solar_mass_kg Msol_m = solar_mass_m Msol_s = solar_mass_s # Earth Earth_mass_kg = 5.9724e24 # Particle Data Group, PRD 98, 030001 (2018); http://pdg.lbl.gov/ Earth_mass_m = G * Earth_mass_kg / c**2 Earth_mass_s = Earth_mass_m / c Earth_mass_sol = Earth_mass_kg / solar_mass_kg Earth_eq_radius_m = 6.3781e6 # IAU Resolution B3; arXiv:1510.07674 Earth_mean_density_SI = Earth_mass_kg / (4 * pi.n() / 3 * Earth_eq_radius_m**3) Earth_mean_density_sol = Earth_mean_density_SI / solar_mean_density_SI # Jupiter
def compute_flowpipe(A=None, X0=None, B=None, U=None, **kwargs): r"""Implements LGG reachability algorithm for the linear continuous system dx/dx = Ax + Bu. INPUTS: * ``A`` -- coefficient matrix of the system * ``X0`` -- initial set * ``B`` -- transformation of the input * ``U`` -- input set * ``time_step`` -- (default = 1e-2) time step * ``initial_time`` -- (default = 0) the initial time * ``time_horizon`` -- (default = 1) the final time * ``number_of_time_steps`` -- (default = ceil(T/tau)) number of time steps * "directions" -- (default: random, and a box) dictionary * ``solver`` -- LP solver. Valid options are: * 'GLPK' (default). * 'Gurobi' * ``base_ring`` -- base ring where polyhedral computations are performed Valid options are: * QQ - (default) rational field * RDF - real double field OUTPUTS: * ``flowpipe`` """ # ################ # Parse input # # ################ if A is None: raise ValueError('System matrix A is missing.') else: if 'sage.matrix' in str(type(A)): n = A.ncols() elif type(A) == np.ndarray: n = A.shape[0] base_ring = kwargs['base_ring'] if 'base_ring' in kwargs else QQ if X0 is None: raise ValueError('Initial state X0 is missing.') elif 'sage.geometry.polyhedron' not in str(type(X0)) and type(X0) == list: # If X0 is not some type of polyhedron, set an initial point X0 = Polyhedron(vertices = [X0], base_ring = base_ring) elif 'sage.geometry.polyhedron' not in str(type(X0)) and X0.is_vector(): X0 = Polyhedron(vertices = [X0], base_ring = base_ring) elif 'sage.geometry.polyhedron' in str(type(X0)): # ensure that all input sets are on the same ring # not sure about this if 1==0: if X0.base_ring() != base_ring: [F, g] = polyhedron_to_Hrep(X0) X0 = polyhedron_from_Hrep(F, g, base_ring=base_ring) else: raise ValueError('Initial state X0 not understood') if B is None: # the system is homogeneous: dx/dt = Ax got_homogeneous = True else: got_homogeneous = False if U is None: raise ValueError('Input range U is missing.') tau = kwargs['time_step'] if 'time_step' in kwargs else 1e-2 t0 = kwargs['initial_time'] if 'initial_time' in kwargs else 0 T = kwargs['time_horizon'] if 'time_horizon' in kwargs else 1 global N N = kwargs['number_of_time_steps'] if 'number_of_time_steps' in kwargs else ceil(T/tau) directions = kwargs['directions'] if 'directions' in kwargs else {'select':'box'} global solver solver = kwargs['solver'] if 'solver' in kwargs else 'GLPK' global verbose verbose = kwargs['verbose'] if 'verbose' in kwargs else 0 # this involves the convex hull of X0 and a Minkowski sum #first_element_evaluation = kwargs['first_element_evaluation'] if 'first_element_evaluation' in kwargs else 'approximate' # ####################################################### # Generate template directions # # ####################################################### if directions['select'] == 'box': if n==2: theta = [0,pi/2,pi,3*pi/2] # box dList = [vector(RR,[cos(t), sin(t)]) for t in theta] else: # directions of hypercube dList = [] dList += [-identity_matrix(n).column(i) for i in range(n)] dList += [identity_matrix(n).column(i) for i in range(n)] elif directions['select'] == 'oct': if n != 2: raise NotImplementedError('Directions select octagon not implemented for n other than 2. Try box.') theta = [i*pi/4 for i in range(8)] # octagon dList = [vector(RR,[cos(t), sin(t)]) for t in theta] elif directions['select'] == 'random': order = directions['order'] if 'order' in directions else 12 if n == 2: theta = [random.uniform(0, 2*pi.n(digits=5)) for i in range(order)] dList = [vector(RR,[cos(theta[i]), sin(theta[i])]) for i in range(order)] else: raise NotImplementedError('Directions select random not implemented for n greater than 2. Try box.') elif directions['select'] == 'custom': dList = directions['dList'] else: raise TypeError('Template directions not understood.') # transform directions to numpy array, and get number of directions dArray = np.array(dList) k = len(dArray) global Phi_tau, expX0, alpha_tau_B if got_homogeneous: # dx/dx = Ax # ####################################################### # Compute first element of the approximating sequence # # ####################################################### # compute matrix exponential exp(A*tau) Phi_tau = expm(np.multiply(A, tau)) # compute exp(tau*A)X0 expX0 = Phi_tau * X0 # compute the bloating factor Ainfty = A.norm(Infinity) RX0 = radius(X0) unitBall = BoxInfty(center = zero_vector(n), radius = 1, base_ring = base_ring) alpha_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RX0) alpha_tau_B = (alpha_tau*np.identity(n)) * unitBall # now we have that: # Omega0 = X0.convex_hull(expX0.Minkowski_sum(alpha_tau_B)) # compute the first element of the approximating sequence, Omega_0 #if first_element_evaluation == 'exact': # Omega0 = X0.convex_hull(expX0.Minkowski_sum(alpha_tau_B)) #elif first_element_evaluation == 'approximate': # NOT TESTED!!! #Omega0_A = dArray # Omega0_b = np.zeros(k) # for i, d in enumerate(dArray): # rho_X0_d = supp_fun_polyhedron(X0, d, solver=solver, verbose=verbose) # rho_expX0_d = supp_fun_polyhedron(expX0, d, solver=solver, verbose=verbose) # rho_alpha_tau_B_d = supp_fun_polyhedron(alpha_tau_B, d, solver=solver, verbose=verbose) # Omega0_b[i] = max(rho_X0_d, rho_expX0_d + rho_alpha_tau_B_d); # Omega0 = PolyhedronFromHSpaceRep(dArray, Omega0_b); #W_tau = Polyhedron(vertices = [], ambient_dim=n) # since W_tau = [], supp_fun_polyhedron returns 0 # ################################################ # Build the sequence of approximations Omega_i # # ################################################ Omega_i_Family_SF = [_Omega_i_supports_hom(d, X0) for d in dArray] else: # dx/dx = Ax + Bu global tau_V, beta_tau_B # compute range of the input under B, V = BU V = B * U # compute matrix exponential exp(A*tau) Phi_tau = expm(np.multiply(A, tau)) # compute exp(tau*A)X0 expX0 = Phi_tau * X0 # compute the initial over-approximation tau_V = (tau*np.identity(n)) * V # compute the bloating factor Ainfty = A.norm(Infinity) RX0 = radius(X0) RV = radius(V) unitBall = BoxInfty(center = zero_vector(n), radius = 1, base_ring = base_ring) alpha_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RX0 + RV/Ainfty) alpha_tau_B = (alpha_tau*np.identity(n)) * unitBall # compute the first element of the approximating sequence, Omega_0 #aux = expX0.Minkowski_sum(tau_V) #Omega0 = X0.convex_hull(aux.Minkowski_sum(alpha_tau_B)) beta_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RV/Ainfty) beta_tau_B = (beta_tau*np.identity(n)) * unitBall #W_tau = tau_V.Minkowski_sum(beta_tau_B) # ################################################ # Build the sequence of approximations Omega_i # # ################################################ Omega_i_Family_SF = [_Omega_i_supports_inhom(d, X0) for d in dArray] # ################################################ # Build the approximating polyhedra # # ################################################ # each polytope is built using the support functions over-approximation Omega_i_Poly = list() # This loop can be vectorized (?) for i in range(N): # we have N polytopes # for each one, use all directions A = matrix(base_ring, k, n); b = vector(base_ring, k) for j in range(k): #run over directions s_fun = Omega_i_Family_SF[j][i] A.set_row(j, dList[j]) b[j] = s_fun Omega_i_Poly.append( polyhedron_from_Hrep(A, b, base_ring = base_ring) ) return Omega_i_Poly