Module firesong.Luminosity

Classes to help with various luminosity functions

Expand source code
#!/usr/bin/python

"""Classes to help with various luminosity functions"""

# imports
import numpy as np
from scipy.stats import lognorm


class LuminosityFunction(object):
    """Luminosity Function class.
    """

    def __init__(self, mean_luminosity):
        """Luminosity function.

        Args:
            - mean luminosity (Not not median and not log(mean))
        """
        self.mean = mean_luminosity

    def sample_distribution(self, nsources, rng=None):
        raise NotImplementedError("Abstract Class")

    def pdf(self, lumi):
        raise NotImplementedError("Abstract Class")

    def cdf(self, lumi):
        raise NotImplementedError("Abstract Class")


class SC_LuminosityFunction(LuminosityFunction):
    """ Standard Candle Luminosity functions.
    All sources have same luminosity.
    """

    def sample_distribution(self, nsources=None, rng=None):
        """ Samples from the Luminosity Function nsource times

        Args:
            number of sources
        """
        return self.mean

    def pdf(self, lumi):
        """ Gives the value of the PDF at lumi.

        Args:
            lumi: float or array-like, point where PDF is evaluated.

        Notes:
            PDF for SC not defined. This just works if an array is given.
            PDF is zero every where except where bin edges include the SC flux.
        """
        lumi = np.atleast_1d(lumi)
        if len(lumi) < 2:
            raise NotImplementedError("""This is a delta function.
                PDF not defined.
                We only can return something meaning full for a nice array.
                """)
        pdf = np.zeros_like(lumi)
        pdf[:-1][np.logical_and(self.mean > lumi[:-1],
                 self.mean < lumi[1:])] = 1
        return pdf

    def cdf(self, lumi):
        """ Gives the value of the CDF at lumi.

        Args:
            lumi: float or array-like, point where CDF is evaluated.

        Notes:
            0    lumi < mean
            0.5  lumi = mean
            1    lumi > mean
        """
        lumi = np.atleast_1d(lumi)
        cdf = np.zeros_like(lumi)
        cdf[lumi == self.mean] = .5
        cdf[lumi > self.mean] = 1.
        if len(lumi) == 1:
            return cdf[0]
        return cdf


class LG_LuminosityFunction(LuminosityFunction):

    def __init__(self, mean_luminosity, width):
        """ Log Normal Luminosity function.

        Args:
            - mean luminosity (Not median and not log(mean))
            - width in dex (width in log10)

        Note:
            Relation between mean and median luminosity:
            log(median) = log(mean) - sigma^2/2
            Relation between width and simga:
            sigma = log(10^width)
        """
        self.mean = mean_luminosity
        self.width = width                     # width is given in log10
        self.logmean = np.log(self.mean)       # log mean luminosity
        self.sigma = np.log(10**self.width)    # sigma is given in ln
        self.mu = self.logmean-self.sigma**2./2.  # log median luminosity

    def sample_distribution(self, nsources=None, rng=None):
        """ Samples from the Luminosity Function nsource times

        Args:
            number of sources
        """
        if rng is None:
            rng = np.random.RandomState()
        return rng.lognormal(self.mu, self.sigma, nsources)

    def pdf(self, lumi):
        r""" Gives the value of the PDF at lumi.

        Args:
            lumi: float or array-like, point where PDF is evaluated.

        Notes:
            PDF given by:

            $$ \frac{1}{x\sigma \sqrt{2\pi}} \times 
            \exp{-\frac{(\ln(x)-\mu)^2}{2\sigma^1}}$$
        """
        return lognorm.pdf(lumi, s=self.sigma, scale=np.exp(self.mu))

    def cdf(self, lumi):
        r""" Gives the value of the CDF at lumi.

        Args:
            lumi: float or array-like, point where CDF is evaluated.

        Notes:
            CDF given by:

            $$ \frac{1}{2} + \frac{1}{2} \times 
            \mathrm{erf}\left( \frac{(\ln(x)-\mu)^2}{\sqrt{2}\sigma}\right)$$
        """
        return lognorm.cdf(lumi, s=self.sigma, scale=np.exp(self.mu))


class PL_LuminosityFunction(LuminosityFunction):
    r""" Power-law distribution defined between x_min and x_max.

        PDF:
        $$\frac{1-\alpha}{x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}\times
        x^{-\alpha}$$

        CDF:
        $$\frac{x^{1-\alpha}-x_{min}^{1-\alpha}}
        {x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}$$

        inv.CDF:
        $$ P^{-1}(x) = (x_{min}^{\beta} + (x_{max}^{\beta}
         -x_{min}^{\beta})*x)^{1/\beta}$$

        Formulars from: http://up-rs-esp.github.io/bpl/
        and: Clauset, A., Shalizi, C. R., Newman, M. E. J.
        "Power-law Distributions in Empirical Data".
        SFI Working Paper: 2007-12-049 (2007)  arxiv:0706.1062
    """

    def __init__(self, mean_luminosity, index, width):
        """ Power-law distribution defined between x_min and x_max.
        x_min and x_max are calculated from mean and width of distribution.

        Note:
            f_mean ~= width*ln(10)*F_min,          index = -2
            f_mean ~= (index+1)/(index+2)*F_min    index < -2
        """
        self.mean = mean_luminosity
        self.index = index
        self.width = width

        if self.index == -2:
            self.Fmin = self.mean / (self.width*np.log(10))
        else:
            self.Fmin = (self.index+2.)/(self.index+1.)*self.mean

        self.Fmax = self.Fmin*10**self.width

    def sample_distribution(self, nsources=None, rng=None):
        """Samples from the Luminosity Function nsource times

        Args:
            number of sources
        """
        if rng is None:
            rng = np.random.RandomState()
        x = rng.uniform(0, 1, nsources)
        beta = (1.+self.index)
        return (self.Fmin**beta + (self.Fmax**beta -
                                   self.Fmin**beta)*x)**(1./beta)

    def pdf(self, lumi):
        r"""Gives the value of the PDF at lumi.

        Args:
            lumi: float or array-like, point where PDF is evaluated.

        Notes:
            PDF given by:
            $$\frac{1-\alpha}{x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}
            \times x^{-\alpha}$$
        """

        norm = (1+self.index)/(self.Fmax**(1+self.index) -
                               self.Fmin**(1+self.index))
        pdf = norm*lumi**self.index
        pdf[np.logical_or(lumi < self.Fmin, lumi > self.Fmax)] = 0
        return pdf

    def cdf(self, lumi):
        r"""Gives the value of the CDF at lumi.

        Args:
            lumi: float or array-like, point where CDF is evaluated.

        Note:
            CDF given by:
            $$\frac{x^{1-\alpha}-x_{min}^{1-\alpha}}
            {x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}$$
        """
        return (lumi**(1+self.index) - self.Fmin**(1+self.index)) / \
               ((self.Fmax**(1+self.index) - self.Fmin**(1+self.index)))


def get_LuminosityFunction(mean_luminosity, LF, **kwargs):
    """Returns a Luminosity Function based on an abbreviation.
    Known abbreviations are:
        - SC - Standard Candle
        - LG - Log-Normal
        - PL - Power-Law

    Args:
        - options: Namespace with LF and needed parameters to
                   construct luminosity function.
        - mean_luminosity: mean luminosity.

    Raises 'NotImplementedError' for unknown abbreviation.
    """

    if LF == "SC":
        return SC_LuminosityFunction(mean_luminosity)
    if LF == "LG":
        return LG_LuminosityFunction(mean_luminosity,
                                     kwargs["sigma"])
    if LF == "PL":
        return PL_LuminosityFunction(mean_luminosity,
                                     kwargs["index"],
                                     kwargs["sigma"])
    raise NotImplementedError("The luminosity function " +
                              LF + " is not implemented.")

Functions

def get_LuminosityFunction(mean_luminosity, LF, **kwargs)

Returns a Luminosity Function based on an abbreviation. Known abbreviations are: - SC - Standard Candle - LG - Log-Normal - PL - Power-Law

Args

  • options: Namespace with LF and needed parameters to construct luminosity function.
  • mean_luminosity: mean luminosity. Raises 'NotImplementedError' for unknown abbreviation.
Expand source code
def get_LuminosityFunction(mean_luminosity, LF, **kwargs):
    """Returns a Luminosity Function based on an abbreviation.
    Known abbreviations are:
        - SC - Standard Candle
        - LG - Log-Normal
        - PL - Power-Law

    Args:
        - options: Namespace with LF and needed parameters to
                   construct luminosity function.
        - mean_luminosity: mean luminosity.

    Raises 'NotImplementedError' for unknown abbreviation.
    """

    if LF == "SC":
        return SC_LuminosityFunction(mean_luminosity)
    if LF == "LG":
        return LG_LuminosityFunction(mean_luminosity,
                                     kwargs["sigma"])
    if LF == "PL":
        return PL_LuminosityFunction(mean_luminosity,
                                     kwargs["index"],
                                     kwargs["sigma"])
    raise NotImplementedError("The luminosity function " +
                              LF + " is not implemented.")

Classes

class LG_LuminosityFunction (mean_luminosity, width)

Luminosity Function class.

Log Normal Luminosity function.

Args

  • mean luminosity (Not median and not log(mean))
  • width in dex (width in log10)

Note

Relation between mean and median luminosity: log(median) = log(mean) - sigma^2/2 Relation between width and simga: sigma = log(10^width)

Expand source code
class LG_LuminosityFunction(LuminosityFunction):

    def __init__(self, mean_luminosity, width):
        """ Log Normal Luminosity function.

        Args:
            - mean luminosity (Not median and not log(mean))
            - width in dex (width in log10)

        Note:
            Relation between mean and median luminosity:
            log(median) = log(mean) - sigma^2/2
            Relation between width and simga:
            sigma = log(10^width)
        """
        self.mean = mean_luminosity
        self.width = width                     # width is given in log10
        self.logmean = np.log(self.mean)       # log mean luminosity
        self.sigma = np.log(10**self.width)    # sigma is given in ln
        self.mu = self.logmean-self.sigma**2./2.  # log median luminosity

    def sample_distribution(self, nsources=None, rng=None):
        """ Samples from the Luminosity Function nsource times

        Args:
            number of sources
        """
        if rng is None:
            rng = np.random.RandomState()
        return rng.lognormal(self.mu, self.sigma, nsources)

    def pdf(self, lumi):
        r""" Gives the value of the PDF at lumi.

        Args:
            lumi: float or array-like, point where PDF is evaluated.

        Notes:
            PDF given by:

            $$ \frac{1}{x\sigma \sqrt{2\pi}} \times 
            \exp{-\frac{(\ln(x)-\mu)^2}{2\sigma^1}}$$
        """
        return lognorm.pdf(lumi, s=self.sigma, scale=np.exp(self.mu))

    def cdf(self, lumi):
        r""" Gives the value of the CDF at lumi.

        Args:
            lumi: float or array-like, point where CDF is evaluated.

        Notes:
            CDF given by:

            $$ \frac{1}{2} + \frac{1}{2} \times 
            \mathrm{erf}\left( \frac{(\ln(x)-\mu)^2}{\sqrt{2}\sigma}\right)$$
        """
        return lognorm.cdf(lumi, s=self.sigma, scale=np.exp(self.mu))

Ancestors

Methods

def cdf(self, lumi)

Gives the value of the CDF at lumi.

Args

lumi
float or array-like, point where CDF is evaluated.

Notes

CDF given by:

\frac{1}{2} + \frac{1}{2} \times \mathrm{erf}\left( \frac{(\ln(x)-\mu)^2}{\sqrt{2}\sigma}\right)

Expand source code
def cdf(self, lumi):
    r""" Gives the value of the CDF at lumi.

    Args:
        lumi: float or array-like, point where CDF is evaluated.

    Notes:
        CDF given by:

        $$ \frac{1}{2} + \frac{1}{2} \times 
        \mathrm{erf}\left( \frac{(\ln(x)-\mu)^2}{\sqrt{2}\sigma}\right)$$
    """
    return lognorm.cdf(lumi, s=self.sigma, scale=np.exp(self.mu))
def pdf(self, lumi)

Gives the value of the PDF at lumi.

Args

lumi
float or array-like, point where PDF is evaluated.

Notes

PDF given by:

\frac{1}{x\sigma \sqrt{2\pi}} \times \exp{-\frac{(\ln(x)-\mu)^2}{2\sigma^1}}

Expand source code
def pdf(self, lumi):
    r""" Gives the value of the PDF at lumi.

    Args:
        lumi: float or array-like, point where PDF is evaluated.

    Notes:
        PDF given by:

        $$ \frac{1}{x\sigma \sqrt{2\pi}} \times 
        \exp{-\frac{(\ln(x)-\mu)^2}{2\sigma^1}}$$
    """
    return lognorm.pdf(lumi, s=self.sigma, scale=np.exp(self.mu))
def sample_distribution(self, nsources=None, rng=None)

Samples from the Luminosity Function nsource times

Args

number of sources

Expand source code
def sample_distribution(self, nsources=None, rng=None):
    """ Samples from the Luminosity Function nsource times

    Args:
        number of sources
    """
    if rng is None:
        rng = np.random.RandomState()
    return rng.lognormal(self.mu, self.sigma, nsources)
class LuminosityFunction (mean_luminosity)

Luminosity Function class.

Luminosity function.

Args

  • mean luminosity (Not not median and not log(mean))
Expand source code
class LuminosityFunction(object):
    """Luminosity Function class.
    """

    def __init__(self, mean_luminosity):
        """Luminosity function.

        Args:
            - mean luminosity (Not not median and not log(mean))
        """
        self.mean = mean_luminosity

    def sample_distribution(self, nsources, rng=None):
        raise NotImplementedError("Abstract Class")

    def pdf(self, lumi):
        raise NotImplementedError("Abstract Class")

    def cdf(self, lumi):
        raise NotImplementedError("Abstract Class")

Subclasses

Methods

def cdf(self, lumi)
Expand source code
def cdf(self, lumi):
    raise NotImplementedError("Abstract Class")
def pdf(self, lumi)
Expand source code
def pdf(self, lumi):
    raise NotImplementedError("Abstract Class")
def sample_distribution(self, nsources, rng=None)
Expand source code
def sample_distribution(self, nsources, rng=None):
    raise NotImplementedError("Abstract Class")
class PL_LuminosityFunction (mean_luminosity, index, width)

Power-law distribution defined between x_min and x_max.

PDF: \frac{1-\alpha}{x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}\times x^{-\alpha}

CDF: \frac{x^{1-\alpha}-x_{min}^{1-\alpha}} {x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}

inv.CDF: P^{-1}(x) = (x_{min}^{\beta} + (x_{max}^{\beta} -x_{min}^{\beta})*x)^{1/\beta}

Formulars from: http://up-rs-esp.github.io/bpl/ and: Clauset, A., Shalizi, C. R., Newman, M. E. J. "Power-law Distributions in Empirical Data". SFI Working Paper: 2007-12-049 (2007) arxiv:0706.1062

Power-law distribution defined between x_min and x_max. x_min and x_max are calculated from mean and width of distribution.

Note

f_mean ~= widthln(10)F_min, index = -2 f_mean ~= (index+1)/(index+2)*F_min index < -2

Expand source code
class PL_LuminosityFunction(LuminosityFunction):
    r""" Power-law distribution defined between x_min and x_max.

        PDF:
        $$\frac{1-\alpha}{x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}\times
        x^{-\alpha}$$

        CDF:
        $$\frac{x^{1-\alpha}-x_{min}^{1-\alpha}}
        {x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}$$

        inv.CDF:
        $$ P^{-1}(x) = (x_{min}^{\beta} + (x_{max}^{\beta}
         -x_{min}^{\beta})*x)^{1/\beta}$$

        Formulars from: http://up-rs-esp.github.io/bpl/
        and: Clauset, A., Shalizi, C. R., Newman, M. E. J.
        "Power-law Distributions in Empirical Data".
        SFI Working Paper: 2007-12-049 (2007)  arxiv:0706.1062
    """

    def __init__(self, mean_luminosity, index, width):
        """ Power-law distribution defined between x_min and x_max.
        x_min and x_max are calculated from mean and width of distribution.

        Note:
            f_mean ~= width*ln(10)*F_min,          index = -2
            f_mean ~= (index+1)/(index+2)*F_min    index < -2
        """
        self.mean = mean_luminosity
        self.index = index
        self.width = width

        if self.index == -2:
            self.Fmin = self.mean / (self.width*np.log(10))
        else:
            self.Fmin = (self.index+2.)/(self.index+1.)*self.mean

        self.Fmax = self.Fmin*10**self.width

    def sample_distribution(self, nsources=None, rng=None):
        """Samples from the Luminosity Function nsource times

        Args:
            number of sources
        """
        if rng is None:
            rng = np.random.RandomState()
        x = rng.uniform(0, 1, nsources)
        beta = (1.+self.index)
        return (self.Fmin**beta + (self.Fmax**beta -
                                   self.Fmin**beta)*x)**(1./beta)

    def pdf(self, lumi):
        r"""Gives the value of the PDF at lumi.

        Args:
            lumi: float or array-like, point where PDF is evaluated.

        Notes:
            PDF given by:
            $$\frac{1-\alpha}{x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}
            \times x^{-\alpha}$$
        """

        norm = (1+self.index)/(self.Fmax**(1+self.index) -
                               self.Fmin**(1+self.index))
        pdf = norm*lumi**self.index
        pdf[np.logical_or(lumi < self.Fmin, lumi > self.Fmax)] = 0
        return pdf

    def cdf(self, lumi):
        r"""Gives the value of the CDF at lumi.

        Args:
            lumi: float or array-like, point where CDF is evaluated.

        Note:
            CDF given by:
            $$\frac{x^{1-\alpha}-x_{min}^{1-\alpha}}
            {x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}$$
        """
        return (lumi**(1+self.index) - self.Fmin**(1+self.index)) / \
               ((self.Fmax**(1+self.index) - self.Fmin**(1+self.index)))

Ancestors

Methods

def cdf(self, lumi)

Gives the value of the CDF at lumi.

Args

lumi
float or array-like, point where CDF is evaluated.

Note

CDF given by: \frac{x^{1-\alpha}-x_{min}^{1-\alpha}} {x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}

Expand source code
def cdf(self, lumi):
    r"""Gives the value of the CDF at lumi.

    Args:
        lumi: float or array-like, point where CDF is evaluated.

    Note:
        CDF given by:
        $$\frac{x^{1-\alpha}-x_{min}^{1-\alpha}}
        {x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}$$
    """
    return (lumi**(1+self.index) - self.Fmin**(1+self.index)) / \
           ((self.Fmax**(1+self.index) - self.Fmin**(1+self.index)))
def pdf(self, lumi)

Gives the value of the PDF at lumi.

Args

lumi
float or array-like, point where PDF is evaluated.

Notes

PDF given by: \frac{1-\alpha}{x_{max}^{1-\alpha}-x_{min}^{1-\alpha}} \times x^{-\alpha}

Expand source code
def pdf(self, lumi):
    r"""Gives the value of the PDF at lumi.

    Args:
        lumi: float or array-like, point where PDF is evaluated.

    Notes:
        PDF given by:
        $$\frac{1-\alpha}{x_{max}^{1-\alpha}-x_{min}^{1-\alpha}}
        \times x^{-\alpha}$$
    """

    norm = (1+self.index)/(self.Fmax**(1+self.index) -
                           self.Fmin**(1+self.index))
    pdf = norm*lumi**self.index
    pdf[np.logical_or(lumi < self.Fmin, lumi > self.Fmax)] = 0
    return pdf
def sample_distribution(self, nsources=None, rng=None)

Samples from the Luminosity Function nsource times

Args

number of sources

Expand source code
def sample_distribution(self, nsources=None, rng=None):
    """Samples from the Luminosity Function nsource times

    Args:
        number of sources
    """
    if rng is None:
        rng = np.random.RandomState()
    x = rng.uniform(0, 1, nsources)
    beta = (1.+self.index)
    return (self.Fmin**beta + (self.Fmax**beta -
                               self.Fmin**beta)*x)**(1./beta)
class SC_LuminosityFunction (mean_luminosity)

Standard Candle Luminosity functions. All sources have same luminosity.

Luminosity function.

Args

  • mean luminosity (Not not median and not log(mean))
Expand source code
class SC_LuminosityFunction(LuminosityFunction):
    """ Standard Candle Luminosity functions.
    All sources have same luminosity.
    """

    def sample_distribution(self, nsources=None, rng=None):
        """ Samples from the Luminosity Function nsource times

        Args:
            number of sources
        """
        return self.mean

    def pdf(self, lumi):
        """ Gives the value of the PDF at lumi.

        Args:
            lumi: float or array-like, point where PDF is evaluated.

        Notes:
            PDF for SC not defined. This just works if an array is given.
            PDF is zero every where except where bin edges include the SC flux.
        """
        lumi = np.atleast_1d(lumi)
        if len(lumi) < 2:
            raise NotImplementedError("""This is a delta function.
                PDF not defined.
                We only can return something meaning full for a nice array.
                """)
        pdf = np.zeros_like(lumi)
        pdf[:-1][np.logical_and(self.mean > lumi[:-1],
                 self.mean < lumi[1:])] = 1
        return pdf

    def cdf(self, lumi):
        """ Gives the value of the CDF at lumi.

        Args:
            lumi: float or array-like, point where CDF is evaluated.

        Notes:
            0    lumi < mean
            0.5  lumi = mean
            1    lumi > mean
        """
        lumi = np.atleast_1d(lumi)
        cdf = np.zeros_like(lumi)
        cdf[lumi == self.mean] = .5
        cdf[lumi > self.mean] = 1.
        if len(lumi) == 1:
            return cdf[0]
        return cdf

Ancestors

Methods

def cdf(self, lumi)

Gives the value of the CDF at lumi.

Args

lumi
float or array-like, point where CDF is evaluated.

Notes

0 lumi < mean 0.5 lumi = mean 1 lumi > mean

Expand source code
def cdf(self, lumi):
    """ Gives the value of the CDF at lumi.

    Args:
        lumi: float or array-like, point where CDF is evaluated.

    Notes:
        0    lumi < mean
        0.5  lumi = mean
        1    lumi > mean
    """
    lumi = np.atleast_1d(lumi)
    cdf = np.zeros_like(lumi)
    cdf[lumi == self.mean] = .5
    cdf[lumi > self.mean] = 1.
    if len(lumi) == 1:
        return cdf[0]
    return cdf
def pdf(self, lumi)

Gives the value of the PDF at lumi.

Args

lumi
float or array-like, point where PDF is evaluated.

Notes

PDF for SC not defined. This just works if an array is given. PDF is zero every where except where bin edges include the SC flux.

Expand source code
def pdf(self, lumi):
    """ Gives the value of the PDF at lumi.

    Args:
        lumi: float or array-like, point where PDF is evaluated.

    Notes:
        PDF for SC not defined. This just works if an array is given.
        PDF is zero every where except where bin edges include the SC flux.
    """
    lumi = np.atleast_1d(lumi)
    if len(lumi) < 2:
        raise NotImplementedError("""This is a delta function.
            PDF not defined.
            We only can return something meaning full for a nice array.
            """)
    pdf = np.zeros_like(lumi)
    pdf[:-1][np.logical_and(self.mean > lumi[:-1],
             self.mean < lumi[1:])] = 1
    return pdf
def sample_distribution(self, nsources=None, rng=None)

Samples from the Luminosity Function nsource times

Args

number of sources

Expand source code
def sample_distribution(self, nsources=None, rng=None):
    """ Samples from the Luminosity Function nsource times

    Args:
        number of sources
    """
    return self.mean