Source code for cbx.dynamics.cbs

import warnings
import numpy as np
from .cbo import CBO, cbo_update
from ..scheduler import scheduler
#%%
[docs] class CBS(CBO): r"""Consensus-Based Sampling (CBS) class. Implements CBS as described in [1]_. Two discretisation schemes are available: * ``scheme='EM'`` — Euler-Maruyama (inherited CBO step): .. math:: X^{k+1} = X^k - dt\,(X^k - c_\alpha) + \text{noise} * ``scheme='exponential'`` — exponential integrator (exact linear solve): .. math:: X^{k+1} = c_\alpha + e^{-dt}(X^k - c_\alpha) + \text{noise} In both cases the noise uses the covariance noise model with the matching factor (:math:`\sqrt{2\,dt/\lambda}` for EM and :math:`\sqrt{(1-e^{-2dt})/\lambda}` for the exponential integrator), with :math:`\lambda = 1 + \alpha`. Parameters ---------- f : callable The objective function. mode : str, optional Passed to :class:`covariance_noise <cbx.noise.covariance_noise>`. Default: ``'sampling'``. noise : str, optional Noise model. Default: ``'covariance'``. scheme : str, optional Discretisation scheme: ``'EM'`` or ``'exponential'``. Default: ``'EM'``. References ---------- .. [1] Carrillo, J. A., Hoffmann, F., Stuart, A. M., & Vaes, U. (2022). Consensus-based sampling. Studies in Applied Mathematics, 148(3), 1069-1140. """ def __init__(self, f, mode='sampling', noise='covariance', scheme='EM', M=1, track_args=None, **kwargs): track_args = track_args if track_args is not None else {'names': []} super().__init__(f, track_args=track_args, noise=noise, M=M, **kwargs) self.sigma = 1. if self.batched: raise NotImplementedError('Batched mode not implemented for CBS!') if self.x.ndim > 3: raise NotImplementedError('Multi dimensional domains not implemented for CBS! The particle should have the dimension M x N x d, where d is an integer!') if noise not in ['covariance', 'sampling']: warnings.warn('For CBS usually covariance or sampling noise is used!', stacklevel=2) if scheme not in ('EM', 'exponential'): raise ValueError(f"scheme must be 'EM' or 'exponential', got '{scheme}'") self.scheme = scheme self.noise_callable.mode = mode self.noise_callable.scheme = scheme
[docs] def inner_step(self): self.compute_consensus() self.drift = self.x[self.particle_idx] - self.consensus self.s = self.sigma * self.noise() if self.scheme == 'exponential': self.x[self.particle_idx] = ( self.consensus + np.exp(-self.dt) * self.drift + self.s ) else: # EM self.x[self.particle_idx] += cbo_update( self.correction(self.drift), self.lamda, self.dt, 1.0, self.s )
def run(self, sched = 'default'): if self.verbosity > 0: print('.'*20) print('Starting Run with dynamic: ' + self.__class__.__name__) print('.'*20) if sched is None: sched = scheduler([]) elif sched == 'default': sched = self.default_sched() else: if not isinstance(sched, scheduler): raise RuntimeError('Unknonw scheduler specified!') while not self.terminate(): self.step() sched.update(self)
[docs] def optimize(self, sched = 'default'): self.run(sched=sched)
[docs] def default_sched(self,): return scheduler([])
def process_particles(self,): pass