Source code for biogeme.calculator.function_call
"""Module to create callable functions from Biogeme expressions.
This utility allows converting a symbolic Biogeme expression into a callable
function that can be evaluated with a given parameter vector.
Michel Bierlaire
Fri Mar 28 19:03:28 2025
"""
from __future__ import annotations
from typing import Protocol
import numpy as np
from biogeme.function_output import FunctionOutput, NamedFunctionOutput
from .single_formula import CompiledFormulaEvaluator
from ..constants import LOG_LIKE
from ..database import Database
from ..expressions import Expression
from ..model_elements import ModelElements
from ..second_derivatives import SecondDerivativesMode
[docs]
class CallableExpression(Protocol):
def __call__(
self,
x: np.ndarray,
gradient: bool,
hessian: bool,
bhhh: bool,
) -> FunctionOutput: ...
[docs]
class NamedCallableExpression(Protocol):
def __call__(
self,
x: np.ndarray,
gradient: bool,
hessian: bool,
bhhh: bool,
) -> NamedFunctionOutput: ...
[docs]
def function_from_compiled_formula(
the_compiled_function: CompiledFormulaEvaluator,
the_betas: dict[str, float],
named_output: bool = False,
) -> CallableExpression | NamedCallableExpression:
"""Create a callable function from a symbolic Biogeme expression.
:param the_compiled_function: Compiled function evaluator.
:param the_betas: Dictionary mapping parameter names to initial values.
:param named_output: if True, the entries of the derivatives are associated with parameter names
:return: A callable that takes a NumPy array of parameter values
and returns a FunctionOutput.
"""
if named_output:
def the_named_function(
x: np.ndarray,
gradient: bool,
hessian: bool,
bhhh: bool,
) -> NamedFunctionOutput:
"""Evaluate the Biogeme expression with updated parameter values.
:param x: A NumPy array of new parameter values.
:param gradient: If True, compute the gradient of the function.
:param hessian: If True, compute the Hessian of the function.
:param bhhh: If True, compute the BHHH matrix (outer product of gradients).
:return: The evaluated FunctionOutput object.
"""
the_betas.update(dict(zip(the_betas.keys(), x)))
result: FunctionOutput = the_compiled_function.evaluate(
the_betas=the_betas, gradient=gradient, hessian=hessian, bhhh=bhhh
)
named_result = the_compiled_function.model_elements.generate_named_output(
function_output=result
)
return named_result
return the_named_function
def the_function(
x: np.ndarray,
gradient: bool,
hessian: bool,
bhhh: bool,
) -> FunctionOutput:
"""Evaluate the Biogeme expression with updated parameter values.
:param x: A NumPy array of new parameter values.
:param gradient: If True, compute the gradient of the function.
:param hessian: If True, compute the Hessian of the function.
:param bhhh: If True, compute the BHHH matrix (outer product of gradients).
:return: The evaluated FunctionOutput object.
"""
the_betas.update(dict(zip(the_betas.keys(), x)))
return the_compiled_function.evaluate(
the_betas=the_betas, gradient=gradient, hessian=hessian, bhhh=bhhh
)
return the_function
[docs]
def function_from_expression(
expression: Expression,
database: Database,
numerically_safe: bool,
use_jit: bool,
the_betas: dict[str, float],
number_of_draws: int | None = None,
named_output: bool = False,
) -> CallableExpression | NamedCallableExpression:
model_elements = ModelElements(
expressions={LOG_LIKE: expression},
database=database,
number_of_draws=number_of_draws,
use_jit=use_jit,
)
compiled_formula = CompiledFormulaEvaluator(
model_elements=model_elements,
second_derivatives_mode=SecondDerivativesMode.ANALYTICAL,
numerically_safe=numerically_safe,
)
the_function = function_from_compiled_formula(
the_compiled_function=compiled_formula,
the_betas=the_betas,
named_output=named_output,
)
return the_function