Source code for EXOSIMS.util.InverseTransformSampler
import numpy as np
from scipy.interpolate import interp1d
import numbers
from EXOSIMS.util._numpy_compat import copy_if_needed
[docs]
class InverseTransformSampler:
"""
Approximate Inverse Transform Sampler for arbitrary distributions
defined via a PDF encoded as a function (or lambda function)
Args:
f (function):
Probability density function. Must be able to operate
on numpy ndarrays. Function does not need to be
normalized over the sampling interval.
xMin (float):
Minimum of interval to sample (inclusive).
xMax (float):
Maximum of interval to sample (inclusive).
nints (int):
Number of intervals to use in approximating CDF.
Defaults to 10000
Attributes:
f, xMin, xMax
As above
Notes:
If xMin == xMax, return values will all exactly equal xMin.
To sample call the object with the desired number of samples.
"""
def __init__(self, f, xMin, xMax, nints=10000):
# validate inputs
assert (
isinstance(xMin, numbers.Number)
and isinstance(xMax, numbers.Number)
and isinstance(nints, numbers.Number)
), "xMin, xMax, and nints must be numbers."
self.xMin = float(xMin)
self.xMax = float(xMax)
nints = int(nints)
assert hasattr(f, "__call__"), "f must be callable."
if self.xMin != self.xMax:
ints = np.linspace(self.xMin, self.xMax, nints + 1) # interval edges
x = np.diff(ints) / 2.0 + ints[:-1] # interval midpoints
fX = f(x)
if not isinstance(fX, np.ndarray):
fX = np.array(fX, copy=copy_if_needed, ndmin=1)
if len(fX) == 1:
fX = fX[0] * np.ones(x.shape)
F = np.hstack([0, np.cumsum(fX)])
F /= F[-1]
self.Finv = interp1d(F, ints)
def __call__(self, numTest=1):
"""
A call to the object with the number of samples will
return the sampled distribution.
"""
assert isinstance(numTest, numbers.Number), "numTest must be an integer."
numTest = int(numTest)
if self.xMin == self.xMax:
return np.zeros(numTest) + self.xMin
return self.Finv(np.random.uniform(size=numTest))