Example #1
0
def search_true_instance(f, a, b, precision_digits=3, maxiter=10, log=None):
    """Find x in [a, b] where f is True, limiting search to values with precision_digits significant figures.
    Returns x, low_bound, high_bound where low_bound and high_bound are either the search bounds a or b, or closer
    values to x where f was still found to be False.
    # TODO: If asked for precision_digits=5, first search with precision_digits=1, then 2, etc.

    print(search_true_instance(lambda x: 11 < x < 13, 0, 40))
    print(search_true_instance(lambda x: x < 13, 0, 1000))
    """
    log = logging.getLogger('search_true_instance')

    values_searched = [a, b]
    log.debug("Starting exploratory search in [%s, %s]" % (a, b))

    for iter_i in range(maxiter):
        # First test halfway, point then 1/4 and 3/4, then 1/8, 3/8, 5/8, 7/8, etc.
        fractions = 2**(iter_i + 1)
        search_points = [round_to_digits(a + (b - a)*fr, precision_digits)
                         for fr in np.arange(1, fractions, 2)/fractions]
        log.debug("Searching %s - %s (%d points)" % (search_points[0], search_points[-1], len(search_points)))

        for x_i, x in enumerate(search_points):
            if f(x):
                values_searched = np.array(values_searched)
                return x, np.max(values_searched[values_searched < x]), np.min(values_searched[values_searched > x])
            else:
                values_searched.append(x)

        if len(search_points) > 1 and np.any(np.diff(search_points) == 0):
            raise SearchFailedException("No true value found in search region [%s, %s], "
                                        "but search depth now lower than precision digits (%s). "
                                        "Iteration %d." % (a, b, precision_digits, iter_i))

    raise ValueError("Exploratory search failed to converge or terminate - bug? excessive precision?")
Example #2
0
 def get_values_and_likelihoods(self, mu, precision_digits=None):
     if mu == 0:
         # Return two possible values: 0 and 1, with probability 1 and 0
         # There must be more than one possible value to avoid problems
         # e.g. upper limit should return infinite interval, lower limit single point at 0
         # for this we need to check if 1 is included or not
         return np.array([0, 1]), np.array([1, 0])
     # Step size
     step_size = max(1, 10 ** (int(np.log10(mu)) - precision_digits))
     max_mu = round_to_digits(4 + mu + 4 * np.sqrt(mu), precision_digits)
     values = np.arange(0, max_mu, step_size)
     return values, stats.poisson.pmf(values, mu=mu)
Example #3
0
 def get_values_and_likelihoods(self, mu, precision_digits=None):
     if mu == 0:
         # Return two possible values: 0 and 1, with probability 1 and 0
         # There must be more than one possible value to avoid problems
         # e.g. upper limit should return infinite interval, lower limit single point at 0
         # for this we need to check if 1 is included or not
         return np.array([0, 1]), np.array([1, 0])
     # Step size
     step_size = max(1, 10 **(int(np.log10(mu)) - precision_digits))
     max_mu = round_to_digits(4 + mu + 4 * np.sqrt(mu), precision_digits)
     values = np.arange(0, max_mu, step_size)
     return values, stats.poisson.pmf(values, mu=mu)
Example #4
0
 def __call__(self, observation, precision_digits=None, search_region=None):
     """Perform Neynman construction to get confidence interval on event rate for observation.
     """
     if precision_digits is None:
         precision_digits = self.precision_digits
     if search_region is None:
         search_region = [0, round_to_digits(10 + 3 * len(observation), precision_digits)]
     if self.statistic.mu_dependent:
         value = self.statistic(observation, self.statistic.mus)
     else:
         value = self.statistic(observation, None)
     self.log.debug("Statistic evaluates to %s" % value)
     return self.get_confidence_interval(value, precision_digits=precision_digits, search_region=search_region)
Example #5
0
 def __call__(self, observation, hypothesis=None):
     n_sparsest = self.observation_to_n_sparsest(observation)
     child_limits = np.zeros(self.n_bins)
     precision_digits = self.child_statistics[0].precision_digits
     child_limit_search_region = [0, round_to_digits(10 + 6 * len(observation),
                                                     precision_digits)]
     for interval_size in range(self.n_bins):
         # print("Getting child limit for seeing %d events in interval size %d" % (n_sparsest[interval_size],
         #                                                                         interval_size))
         child_limits[interval_size] = self.child_statistics[interval_size].get_confidence_interval(
             n_sparsest[interval_size],
             precision_digits=precision_digits,
             search_region=child_limit_search_region)[1]
         # print("Result is %s" % child_limits[interval_size])
     self.last_limit_was_set_on_thisplusone_bins = np.argmin(child_limits)
     return child_limits[self.last_limit_was_set_on_thisplusone_bins]
Example #6
0
def bisect_search(f, a, b, precision_digits=2, maxiter=1e2):
    """Find x in [a, b] where f changes from True to False by bisection,
    limiting search to values with precision_digits significant figures.
    This is useful if f can cache its results: otherwise just use e.g. scipy.optimize.brentq with rtol.
    Avoid scipy.optimize.bisect with rtol, results seem seem to depend heavily on initial bounds: bug??
    # TODO: If asked for precision_digits=5, first search with precision_digits=1, then 2, etc.
    """
    log = logging.getLogger('bisect_search')

    # Which of the bounds gives True? Can't be both!
    if f(a) == f(b):
        raise ValueError("f must not be true or false on both bounds")
    true_on_a = f(a)

    log.debug("Starting search between %s (%s) and %s (%s)"
              " with %d precision digits" % (a, f(a), b, f(b), precision_digits))

    # Do a bisection search, sticking to precision_digits
    for iter_i in range(int(maxiter)):

        # Find the new bisection point
        x = (a + b) / 2
        x = round_to_digits(x, precision_digits)

        # If we are down to a single point, return that
        if x == a or x == b:
            return x
        true_on_x = f(x)

        # Update the appropriate bound
        if true_on_a:
            if true_on_x:
                a = x
            else:
                b = x
        else:
            if true_on_x:
                b = x
            else:
                a = x

        log.debug("Iteration %d, searching between [%s and %s], last x was %s (%s)" % (iter_i, a, b, x, true_on_x))

    else:
        raise RuntimeError("Infinite loop encountered in bisection search!")
Example #7
0
 def __call__(self, observation, hypothesis=None):
     n_sparsest = self.observation_to_n_sparsest(observation)
     child_limits = np.zeros(self.n_bins)
     precision_digits = self.child_statistics[0].precision_digits
     child_limit_search_region = [
         0, round_to_digits(10 + 6 * len(observation), precision_digits)
     ]
     for interval_size in range(self.n_bins):
         # print("Getting child limit for seeing %d events in interval size %d" % (n_sparsest[interval_size],
         #                                                                         interval_size))
         child_limits[interval_size] = self.child_statistics[
             interval_size].get_confidence_interval(
                 n_sparsest[interval_size],
                 precision_digits=precision_digits,
                 search_region=child_limit_search_region)[1]
         # print("Result is %s" % child_limits[interval_size])
     self.last_limit_was_set_on_thisplusone_bins = np.argmin(child_limits)
     return child_limits[self.last_limit_was_set_on_thisplusone_bins]