@@ -159,7 +159,7 @@ def bayesquad(
159159 References
160160 ----------
161161 .. [1] Briol, F.-X., et al., Probabilistic integration: A role in statistical
162- computation?, *Statistical Science 34.1*, 2019, 1-22, 2019
162+ computation?, *Statistical Science 34.1*, 2019, 1-22.
163163 .. [2] Rasmussen, C. E., and Z. Ghahramani, Bayesian Monte Carlo, *Advances in
164164 Neural Information Processing Systems*, 2003, 505-512.
165165 .. [3] Mckay et al., A Comparison of Three Methods for Selecting Values of Input
@@ -168,7 +168,6 @@ def bayesquad(
168168 Examples
169169 --------
170170 >>> import numpy as np
171-
172171 >>> input_dim = 1
173172 >>> domain = (0, 1)
174173 >>> def fun(x):
@@ -299,12 +298,150 @@ def bayesquad_from_data(
299298 return integral_belief , info
300299
301300
301+ def multilevel_bayesquad_from_data (
302+ nodes : Tuple [np .ndarray , ...],
303+ fun_diff_evals : Tuple [np .ndarray , ...],
304+ kernels : Optional [Tuple [Kernel , ...]] = None ,
305+ measure : Optional [IntegrationMeasure ] = None ,
306+ domain : Optional [DomainLike ] = None ,
307+ options : Optional [dict ] = None ,
308+ ) -> Tuple [Normal , Tuple [BQIterInfo , ...]]:
309+ r"""Infer the value of an integral from given sets of nodes and function
310+ evaluations using a multilevel method.
311+
312+ In multilevel Bayesian quadrature, the integral :math:`\int_\Omega f(x) d \mu(x)`
313+ is (approximately) decomposed as a telescoping sum over :math:`L+1` levels:
314+
315+ .. math:: \int_\Omega f(x) d \mu(x) \approx \int_\Omega f_0(x) d
316+ \mu(x) + \sum_{l=1}^L \int_\Omega [f_l(x) - f_{l-1}(x)] d \mu(x),
317+
318+ where :math:`f_l` is an increasingly accurate but also increasingly expensive
319+ approximation to :math:`f`. It is not necessary that the highest level approximation
320+ :math:`f_L` be equal to :math:`f`.
321+
322+ Bayesian quadrature is subsequently applied to independently infer each of the
323+ :math:`L+1` integrals and the outputs are summed to infer
324+ :math:`\int_\Omega f(x) d \mu(x)`. [1]_
325+
326+ Parameters
327+ ----------
328+ nodes
329+ Tuple of length :math:`L+1` containing the locations for each level at which
330+ the functionn evaluations are available as ``fun_diff_evals``. Each element
331+ must be a shape=(n_eval, input_dim) ``np.ndarray``. If a tuple containing only
332+ one element is provided, it is inferred that the same nodes ``nodes[0]`` are
333+ used on every level.
334+ fun_diff_evals
335+ Tuple of length :math:`L+1` containing the evaluations of :math:`f_l - f_{l-1}`
336+ for each level at the nodes provided in ``nodes``. Each element must be a
337+ shape=(n_eval,) ``np.ndarray``. The zeroth element contains the evaluations of
338+ :math:`f_0`.
339+ kernels
340+ Tuple of length :math:`L+1` containing the kernels used for the GP model at each
341+ level. See **Notes** for further details. Defaults to the ``ExpQuad`` kernel for
342+ each level.
343+ measure
344+ The integration measure. Defaults to the Lebesgue measure.
345+ domain
346+ The integration domain. Contains lower and upper bound as scalar or
347+ ``np.ndarray``. Obsolete if ``measure`` is given.
348+ options
349+ A dictionary with the following optional solver settings
350+
351+ scale_estimation : Optional[str]
352+ Estimation method to use to compute the scale parameter. Used
353+ independently on each level. Defaults to 'mle'. Options are
354+
355+ ============================== =======
356+ Maximum likelihood estimation ``mle``
357+ ============================== =======
358+
359+ jitter : Optional[FloatLike]
360+ Non-negative jitter to numerically stabilise kernel matrix
361+ inversion. Same jitter is used on each level. Defaults to 1e-8.
362+
363+ Returns
364+ -------
365+ integral :
366+ The integral belief subject to the provided measure or domain.
367+ infos :
368+ Information on the performance of the method for each level.
369+
370+ Raises
371+ ------
372+ ValueError
373+ If ``nodes``, ``fun_diff_evals`` or ``kernels`` have different lengths.
374+
375+ Warns
376+ -----
377+ UserWarning
378+ When ``domain`` is given but not used.
379+
380+ Notes
381+ -----
382+ The tuple of kernels provided by the ``kernels`` parameter must contain distinct
383+ kernel instances, i.e., ``kernels[i] is kernel[j]`` must return ``False`` for any
384+ :math:`i\neq j`.
385+
386+ References
387+ ----------
388+ .. [1] Li, K., et al., Multilevel Bayesian quadrature, AISTATS, 2023.
389+
390+ Examples
391+ --------
392+ >>> import numpy as np
393+ >>> input_dim = 1
394+ >>> domain = (0, 1)
395+ >>> n_level = 6
396+ >>> def fun(x, l):
397+ ... return x.reshape(-1, ) / (l + 1.0)
398+ >>> nodes = ()
399+ >>> fun_diff_evals = ()
400+ >>> for l in range(n_level):
401+ ... n_l = 2*l + 1
402+ ... nodes += (np.reshape(np.linspace(0, 1, n_l), (n_l, input_dim)),)
403+ ... fun_diff_evals += (np.reshape(fun(nodes[l], l), (n_l,)),)
404+ >>> F, infos = multilevel_bayesquad_from_data(nodes=nodes,
405+ ... fun_diff_evals=fun_diff_evals,
406+ ... domain=domain)
407+ >>> print(np.round(F.mean, 4))
408+ 0.7252
409+ """
410+
411+ n_level = len (fun_diff_evals )
412+ if kernels is None :
413+ kernels = n_level * (None ,)
414+ if len (nodes ) == 1 :
415+ nodes = n_level * (nodes [0 ],)
416+ if not len (nodes ) == len (fun_diff_evals ) == len (kernels ):
417+ raise ValueError (
418+ f"You must provide an equal number of kernels ({ (len (kernels ))} ), "
419+ f"vectors of function evaluations ({ len (fun_diff_evals )} ) "
420+ f"and sets of nodes ({ len (nodes )} )."
421+ )
422+
423+ integer_belief = Normal (mean = 0.0 , cov = 0.0 )
424+ infos = ()
425+ for level in range (n_level ):
426+ integer_belief_l , info_l = bayesquad_from_data (
427+ nodes = nodes [level ],
428+ fun_evals = fun_diff_evals [level ],
429+ kernel = kernels [level ],
430+ measure = measure ,
431+ domain = domain ,
432+ options = options ,
433+ )
434+ integer_belief += integer_belief_l
435+ infos += (info_l ,)
436+
437+ return integer_belief , infos
438+
439+
302440def _check_domain_measure_compatibility (
303441 input_dim : IntLike ,
304442 domain : Optional [DomainLike ],
305443 measure : Optional [IntegrationMeasure ],
306444) -> Tuple [int , Optional [DomainType ], IntegrationMeasure ]:
307-
308445 input_dim = int (input_dim )
309446
310447 # Neither domain nor measure given
0 commit comments