Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
    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)
Пример #4
0
    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)
Пример #5
0
    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)
Пример #6
0
    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)
Пример #7
0
    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)
Пример #8
0
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)