"""Arithmetic expressions accepted by Biogeme: numerical integrationMichel Bierlaire10.04.2025 09:27"""from__future__importannotationsimportloggingimportjaximportjax.numpyasjnpfromnumpy.polynomial.hermiteimporthermgaussfrombiogeme.floating_pointimportJAX_FLOATfrom.base_expressionsimportExpressionOrNumericfrom.elementary_expressionsimport(TypeOfElementaryExpression,)from.jax_utilsimportJaxFunctionTypefrom.unary_expressionsimportUnaryOperatorfrom..exceptionsimportBiogemeErrorlogger=logging.getLogger(__name__)
[docs]classIntegrateNormal(UnaryOperator):""" Numerical integration """def__init__(self,child:ExpressionOrNumeric,name:str,number_of_quadrature_points:int=30,):"""Constructor :param child: first arithmetic expression :type child: biogeme.expressions.Expression :param name: name of the random variable for the integration. :type name: string """super().__init__(child)self.random_variable_name:str=nameself.random_variable_id:int|None=(None# Index of the element in its own array.)self.number_of_quadrature_points:int=number_of_quadrature_pointsself._is_complex=True
[docs]defdeep_flat_copy(self)->IntegrateNormal:"""Provides a copy of the expression. It is deep in the sense that it generates copies of the children. It is flat in the sense that any `MultipleExpression` is transformed into the currently selected expression. The flat part is irrelevant for this expression. """copy_child=self.child.deep_flat_copy()returntype(self)(child=copy_child,name=self.random_variable_name,number_of_quadrature_points=self.number_of_quadrature_points,)
[docs]defset_specific_id(self,name,specific_id,the_type:TypeOfElementaryExpression):"""The elementary IDs identify the position of each element in the corresponding datab"""ifname==self.random_variable_name:ifthe_type!=TypeOfElementaryExpression.RANDOM_VARIABLE:error_msg=f'Elementary expression {name} is not a random variable to be used for integration.'raiseBiogemeError(error_msg)self.random_variable_id=specific_idforchildinself.get_children():child.set_specific_id(name,specific_id,the_type)
@propertydefsafe_rv_id(self)->int:"""Check the presence of the random variable ID before its usage"""ifself.random_variable_idisNone:raiseBiogemeError(f"No id defined for random variable {self.random_variable_name} inside integration expression.")returnself.random_variable_iddef__str__(self)->str:returnf'Integrate({self.child}, "{self.random_variable_name}")'def__repr__(self)->str:returnf'Integrate({self.child}, "{self.random_variable_name}")'