"""
===========================
Framework Utility Functions
===========================
Collection of utility functions shared by the ``vivarium`` framework.
"""
import functools
from bdb import BdbQuit
from importlib import import_module
from typing import Any, Callable
import numpy as np
[docs]def from_yearly(value, time_step):
return value * (time_step.total_seconds() / (60 * 60 * 24 * 365.0))
[docs]def to_yearly(value, time_step):
return value / (time_step.total_seconds() / (60 * 60 * 24 * 365.0))
[docs]def rate_to_probability(rate):
# encountered underflow from rate > 30k
# for rates greater than 250, exp(-rate) evaluates to 1e-109
# beware machine-specific floating point issues
rate[rate > 250] = 250.0
return 1 - np.exp(-rate)
[docs]def probability_to_rate(probability):
return -np.log(1 - probability)
[docs]def collapse_nested_dict(d, prefix=None):
results = []
for k, v in d.items():
cur_prefix = prefix + "." + k if prefix else k
if isinstance(v, dict):
results.extend(collapse_nested_dict(v, prefix=cur_prefix))
else:
results.append((cur_prefix, v))
return results
[docs]def import_by_path(path: str) -> Callable:
"""Import a class or function given it's absolute path.
Parameters
----------
path:
Path to object to import
"""
module_path, _, class_name = path.rpartition(".")
return getattr(import_module(module_path), class_name)
[docs]def handle_exceptions(func: Callable, logger: Any, with_debugger: bool) -> Callable:
"""Drops a user into an interactive debugger if func raises an error."""
@functools.wraps(func)
def wrapped(*args, **kwargs):
try:
return func(*args, **kwargs)
except (BdbQuit, KeyboardInterrupt):
raise
except Exception as e:
logger.exception("Uncaught exception {}".format(e))
if with_debugger:
import pdb
import traceback
traceback.print_exc()
pdb.post_mortem()
else:
raise
return wrapped