from pyhf.tensor import BackendRetriever as tensor
from pyhf.optimize import OptimizerRetriever as optimize
from pyhf._version import version as __version__
from pyhf.exceptions import InvalidBackend, InvalidOptimizer, Unsupported
from pyhf import events
tensorlib = None
optimizer = None
[docs]def get_backend():
"""
Get the current backend and the associated optimizer
Example:
>>> import pyhf
>>> backend, optimizer = pyhf.get_backend()
>>> backend
<pyhf.tensor.numpy_backend.numpy_backend object at 0x...>
>>> optimizer
<pyhf.optimize.scipy_optimizer object at 0x...>
Returns:
backend, optimizer
"""
global tensorlib
global optimizer
return tensorlib, optimizer
tensorlib = tensor.numpy_backend()
default_backend = tensorlib
optimizer = optimize.scipy_optimizer()
default_optimizer = optimizer
[docs]@events.register('change_backend')
def set_backend(backend, custom_optimizer=None, precision=None):
"""
Set the backend and the associated optimizer
Example:
>>> import pyhf
>>> pyhf.set_backend("tensorflow")
>>> pyhf.tensorlib.name
'tensorflow'
>>> pyhf.tensorlib.precision
'64b'
>>> pyhf.set_backend(b"pytorch", precision="32b")
>>> pyhf.tensorlib.name
'pytorch'
>>> pyhf.tensorlib.precision
'32b'
>>> pyhf.set_backend(pyhf.tensor.numpy_backend())
>>> pyhf.tensorlib.name
'numpy'
>>> pyhf.tensorlib.precision
'64b'
Args:
backend (:obj:`str` or `pyhf.tensor` backend): One of the supported pyhf backends: NumPy, TensorFlow, PyTorch, and JAX
custom_optimizer (`pyhf.optimize` optimizer): Optional custom optimizer defined by the user
precision (:obj:`str`): Floating point precision to use in the backend: ``64b`` or ``32b``. Default is backend dependent.
Returns:
None
"""
global tensorlib
global optimizer
_supported_precisions = ["32b", "64b"]
backend_kwargs = {}
if isinstance(precision, (str, bytes)):
if isinstance(precision, bytes):
precision = precision.decode("utf-8")
precision = precision.lower()
if isinstance(backend, (str, bytes)):
if isinstance(backend, bytes):
backend = backend.decode("utf-8")
backend = backend.lower()
if precision is not None:
backend_kwargs["precision"] = precision
try:
backend = getattr(tensor, f"{backend:s}_backend")(**backend_kwargs)
except TypeError:
raise InvalidBackend(
f"The backend provided is not supported: {backend:s}. Select from one of the supported backends: numpy, tensorflow, pytorch"
)
_name_supported = getattr(tensor, f"{backend.name:s}_backend")
if _name_supported:
if not isinstance(backend, _name_supported):
raise AttributeError(
f"'{backend.name:s}' is not a valid name attribute for backend type {type(backend)}\n Custom backends must have names unique from supported backends"
)
if backend.precision not in _supported_precisions:
raise Unsupported(
f"The backend precision provided is not supported: {backend.precision:s}. Select from one of the supported precisions: {', '.join([str(v) for v in _supported_precisions])}"
)
# If "precision" arg passed, it should always win
# If no "precision" arg, defer to tensor backend object API if set there
if precision is not None:
if backend.precision != precision:
backend_kwargs["precision"] = precision
backend = getattr(tensor, f"{backend.name:s}_backend")(**backend_kwargs)
# need to determine if the tensorlib changed or the optimizer changed for events
tensorlib_changed = bool(
(backend.name != tensorlib.name) | (backend.precision != tensorlib.precision)
)
optimizer_changed = False
if custom_optimizer:
if isinstance(custom_optimizer, (str, bytes)):
if isinstance(custom_optimizer, bytes):
custom_optimizer = custom_optimizer.decode("utf-8")
try:
new_optimizer = getattr(
optimize, f"{custom_optimizer.lower()}_optimizer"
)()
except TypeError:
raise InvalidOptimizer(
f"The optimizer provided is not supported: {custom_optimizer}. Select from one of the supported optimizers: scipy, minuit"
)
else:
_name_supported = getattr(optimize, f"{custom_optimizer.name:s}_optimizer")
if _name_supported:
if not isinstance(custom_optimizer, _name_supported):
raise AttributeError(
f"'{custom_optimizer.name}' is not a valid name attribute for optimizer type {type(custom_optimizer)}\n Custom optimizers must have names unique from supported optimizers"
)
new_optimizer = custom_optimizer
else:
new_optimizer = optimize.scipy_optimizer()
optimizer_changed = bool(optimizer != new_optimizer)
# set new backend
tensorlib = backend
optimizer = new_optimizer
# trigger events
if tensorlib_changed:
events.trigger("tensorlib_changed")()
if optimizer_changed:
events.trigger("optimizer_changed")()
# set up any other globals for backend
tensorlib._setup()
from pyhf.pdf import Model
from pyhf.workspace import Workspace
from pyhf import simplemodels
from pyhf import infer
from pyhf import compat
from pyhf.patchset import PatchSet
__all__ = [
"Model",
"PatchSet",
"Workspace",
"__version__",
"compat",
"exceptions",
"get_backend",
"infer",
"interpolators",
"modifiers",
"optimizer",
"parameters",
"patchset",
"pdf",
"probability",
"set_backend",
"simplemodels",
"tensor",
"tensorlib",
"utils",
"workspace",
]
def __dir__():
return __all__