def entropy_inverse(x, q=2): """ Find the inverse of the ``q``-ary entropy function at the point ``x``. INPUT: - ``x`` -- real number in the interval `[0, 1]`. - ``q`` - (default: 2) integer greater than 1. This is the base of the logarithm. OUTPUT: Real number in the interval `[0, 1-1/q]`. The function has multiple values if we include the entire interval `[0, 1]`; hence only the values in the above interval is returned. EXAMPLES:: sage: from sage.coding.code_bounds import entropy_inverse sage: entropy_inverse(0.1) 0.012986862055848683 sage: entropy_inverse(1) 1/2 sage: entropy_inverse(0, 3) 0 sage: entropy_inverse(1, 3) 2/3 """ # No nice way to compute the inverse. We resort to root finding. if x < 0 or x > 1: raise ValueError("The inverse entropy function is defined only for " "x in the interval [0, 1]") q = ZZ(q) # This will error out if q is not an integer if q < 2: # Here we check that q is actually at least 2 raise ValueError("The value q must be an integer greater than 1") eps = 4.5e-16 # find_root has about this as the default xtol ymax = 1 - 1 / q if x <= eps: return 0 if x >= 1 - eps: return ymax # find_root will error out if the root can not be found from sage.numerical.optimize import find_root f = lambda y: entropy(y, q) - x return find_root(f, 0, ymax)
def entropy_inverse(x, q=2): """ Find the inverse of the ``q``-ary entropy function at the point ``x``. INPUT: - ``x`` -- real number in the interval `[0, 1]`. - ``q`` - (default: 2) integer greater than 1. This is the base of the logarithm. OUTPUT: Real number in the interval `[0, 1-1/q]`. The function has multiple values if we include the entire interval `[0, 1]`; hence only the values in the above interval is returned. EXAMPLES:: sage: from sage.coding.code_bounds import entropy_inverse sage: entropy_inverse(0.1) 0.012986862055848683 sage: entropy_inverse(1) 1/2 sage: entropy_inverse(0, 3) 0 sage: entropy_inverse(1, 3) 2/3 """ # No nice way to compute the inverse. We resort to root finding. if x < 0 or x > 1: raise ValueError("The inverse entropy function is defined only for " "x in the interval [0, 1]") q = ZZ(q) # This will error out if q is not an integer if q < 2: # Here we check that q is actually at least 2 raise ValueError("The value q must be an integer greater than 1") eps = 4.5e-16 # find_root has about this as the default xtol ymax = 1 - 1/q if x <= eps: return 0 if x >= 1-eps: return ymax # find_root will error out if the root can not be found from sage.numerical.optimize import find_root f = lambda y: entropy(y, q) - x return find_root(f, 0, ymax)
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 __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 roche_limit_radius(self, rho, rho_unit='solar', mass_bh=None, k_rot=0, r_min=None, r_max=50): r""" Evaluate the orbital radius of the Roche limit for a star of given density and rotation state. The *Roche limit* is defined as the orbit at which the star fills its Roche volume (cf. :meth:`roche_volume`). INPUT: - ``rho`` -- mean density of the star, in units of ``rho_unit`` - ``rho_unit`` -- (default: ``'solar'``) string specifying the unit in which ``rho`` is provided; allowed values are - ``'solar'``: density of the sun (`1.41\times 10^3 \; {\rm kg\, m}^{-3}`) - ``'SI'``: SI unit (`{\rm kg\, m}^{-3}`) - ``'M^{-2}'``: inverse square of the black hole mass `M` (geometrized units) - ``mass_bh`` -- (default: ``None``) black hole mass `M` in solar masses; must be set if ``rho_unit`` = ``'solar'`` or ``'SI'`` - ``k_rot`` -- (default: ``0``) rotational parameter `k_\omega := \omega/\Omega`, where `\omega` is the angular velocity of the star (assumed to be a rigid rotator) with respect to some inertial frame and `\Omega` is the orbital angular velocity (cf. :meth:`roche_volume`) - ``r_min`` -- (default: ``None``) lower bound for the search of the Roche limit radius, in units of `M`; if none is provided, the value of `r` at the prograde ISCO is used - ``r_max`` -- (default: ``50``) upper bound for the search of the Roche limit radius, in units of `M` OUPUT: - Boyer-Lindquist radial coordinate `r` of the circular orbit at which the star fills its Roche volume, in units of the black hole mass `M` EXAMPLES: Roche limit of a non-rotating solar type star around a Schwarzschild black hole of mass equal to that of Sgr A* (`4.1\; 10^6\; M_\odot`):: sage: from kerrgeodesic_gw import KerrBH sage: BH = KerrBH(0) sage: BH.roche_limit_radius(1, mass_bh=4.1e6) # tol 1.0e-13 34.237640374879014 Instead of providing the density in solar units (the default), we can provide it in SI units (`{\rm kg\, m}^{-3}`):: sage: BH.roche_limit_radius(1.41e3, rho_unit='SI', mass_bh=4.1e6) # tol 1.0e-13 34.23628318664114 or in geometrized units (`M^{-2}`), in which case it is not necessary to specify the black hole mass:: sage: BH.roche_limit_radius(3.84e-5, rho_unit='M^{-2}') # tol 1.0e-13 34.22977166547967 Case of a corotating star:: sage: BH.roche_limit_radius(1, mass_bh=4.1e6, k_rot=1) # tol 1.0e-13 37.72273453630254 Case of a brown dwarf:: sage: BH.roche_limit_radius(131., mass_bh=4.1e6) # tol 1.0e-13 7.310533491138797 sage: BH.roche_limit_radius(131., mass_bh=4.1e6, k_rot=1) # tol 1.0e-13 7.858624964105177 Case of a white dwarf:: sage: BH.roche_limit_radius(1.1e6, mass_bh=4.1e6, r_min=0.1) # tol 1.0e-13 0.28481414467302646 sage: BH.roche_limit_radius(1.1e6, mass_bh=4.1e6, k_rot=1, r_min=0.1) # tol 1.0e-13 0.3264830116324442 Roche limits around a rapidly rotating black hole:: sage: BH = KerrBH(0.999) sage: BH.roche_limit_radius(1, mass_bh=4.1e6) # tol 1.0e-13 34.25172708953104 sage: BH.roche_limit_radius(1, mass_bh=4.1e6, k_rot=1) # tol 1.0e-13 37.72473460397109 sage: BH.roche_limit_radius(64.2, mass_bh=4.1e6) # tol 1.0e-13 8.74384867481928 sage: BH.roche_limit_radius(64.2, mass_bh=4.1e6, k_rot=1) # tol 1.0e-13 9.575923395816831 """ from sage.numerical.optimize import find_root from .astro_data import c, G, solar_mass_kg, solar_mean_density_SI if rho_unit == 'M^{-2}': rhoM = rho else: if not mass_bh: raise ValueError("a value for mass_bh must be provided") M2inv = (c**3 / (mass_bh * solar_mass_kg))**2 / G**3 # M^{-2} in kg/m^3 if rho_unit == 'solar': rho = rho * solar_mean_density_SI # rho in kg/m^3 elif rho_unit != 'SI': raise ValueError("unknown value for rho_unit") rhoM = rho / M2inv # M^2 rho def fzero(r): return self.roche_volume(r, k_rot) - 1. / rhoM if r_min is None: r_min = self.isco_radius() return find_root(fzero, r_min, r_max)
def max_detectable_radius(a, mu, theta, psd, BH_time_scale, distance, t_obs_yr=1, snr_threshold=10, r_min=None, r_max=200, m_min=1, m_max=None, approximation=None): r""" Compute the maximum orbital radius `r_{0,\rm max}` at which a particle of given mass is detectable. INPUT: - ``a`` -- BH angular momentum parameter (in units of `M`, the BH mass) - ``mu`` -- mass of the orbiting object, in solar masses - ``theta`` -- Boyer-Lindquist colatitute `\theta` of the observer - ``psd`` -- function with a single argument (`f`) representing the detector's one-sided noise power spectral density `S_n(f)` (see e.g. :func:`.lisa_detector.power_spectral_density`) - ``BH_time_scale`` -- value of `M` in the same time unit as `S_n(f)`; if `S_n(f)` is provided in `\mathrm{Hz}^{-1}`, then ``BH_time_scale`` must be `M` expressed in seconds. - ``distance`` -- distance `r` to the detector, in parsecs - ``t_obs_yr`` -- (default: 1) observation period, in years - ``snr_threshold`` -- (default: 10) signal-to-noise value above which a detection is claimed - ``r_min`` -- (default: ``None``) lower bound of the search interval for `r_{0,\rm max}` (in units of `M`); if ``None`` then the ISCO value is used - ``r_max`` -- (default: 200) upper bound of the search interval for `r_{0,\rm max}` (in units of `M`) - ``m_min`` -- (default: 1) lower bound in the summation over the Fourier mode `m` - ``m_max`` -- (default: ``None``) upper bound in the summation over the Fourier mode `m`; if ``None``, ``m_max`` is set to 10 for `r_0 \leq 20 M` and to 5 for `r_0 > 20 M` - ``approximation`` -- (default: ``None``) string describing the computational method for the signal; allowed values are - ``None``: exact computation - ``'quadrupole'``: quadrupole approximation; see :func:`.gw_particle.h_particle_quadrupole` - ``'1.5PN'`` (only for ``a=0``): 1.5-post-Newtonian expansion following E. Poisson, Phys. Rev. D **47**, 1497 (1993) [:doi:`10.1103/PhysRevD.47.1497`] OUTPUT: - Boyer-Lindquist radius (in units of `M`) of the most remote orbit for which the signal-to-noise ratio is larger than ``snr_threshold`` during the observation time ``t_obs_yr`` EXAMPLES: Maximum orbital radius for LISA detection of a 1 solar-mass object orbiting Sgr A* observed by LISA, assuming a BH spin `a=0.9 M` and a vanishing inclination angle:: sage: from kerrgeodesic_gw import (max_detectable_radius, lisa_detector, ....: astro_data) sage: a = 0.9 sage: mu = 1 sage: theta = 0 sage: psd = lisa_detector.power_spectral_density_RCLfit sage: BH_time_scale = astro_data.SgrA_mass_s sage: distance = astro_data.SgrA_distance_pc sage: max_detectable_radius(a, mu, theta, psd, BH_time_scale, distance) # tol 1.0e-13 46.983486000490934 Lowering the SNR threshold to 5:: sage: max_detectable_radius(a, mu, theta, psd, BH_time_scale, distance, # tol 1.0e-13 ....: snr_threshold=5) 53.504027668563694 Lowering the data acquisition time to 1 day:: sage: max_detectable_radius(a, mu, theta, psd, BH_time_scale, distance, # tol 1.0e-13 ....: t_obs_yr=1./365.25) 27.159049347284462 Assuming an inclination angle of `\pi/2`:: sage: theta = pi/2 sage: max_detectable_radius(a, mu, theta, psd, BH_time_scale, distance) # tol 1.0e-13 39.8187305700897 Using the 1.5-PN approximation (``a`` has to be zero and ``m_max`` has to be at most 5):: sage: a = 0 sage: max_detectable_radius(a, mu, theta, psd, BH_time_scale, # tol 1.0e-13 ....: distance, approximation='1.5PN', m_max=5) 39.743201341922195 """ from sage.numerical.optimize import find_root from .astro_data import yr, pc, solar_mass_m from .kerr_spacetime import KerrBH t_obs = t_obs_yr * yr # observation time in seconds distance_m = distance * pc # distance in meters mu_ov_r = mu * solar_mass_m / distance_m def fsnr(r0): if r0 < 49.99: return signal_to_noise_particle( a, r0, theta, psd, t_obs, BH_time_scale, m_min=m_min, m_max=m_max, scale=mu_ov_r, approximation=approximation) - snr_threshold else: return signal_to_noise_particle( 0, r0, theta, psd, t_obs, BH_time_scale, scale=mu_ov_r, approximation='1.5PN') - snr_threshold if r_min is None: r_min = 1.0001 * KerrBH(a).isco_radius() return find_root(fsnr, r_min, r_max)