Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
518 changes: 518 additions & 0 deletions docs/docs/tutorials/fitting-bayesian.ipynb

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docs/docs/tutorials/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ The tutorials are organized into the following categories:
- [Fitting SANS](fitting-sans.ipynb) – A tutorial demonstrating how to
fit a small-angle neutron scattering (SANS) data using EasyScience
framework.
- [Bayesian analysis](fitting-bayesian.ipynb) – A tutorial showing how
to run Bayesian MCMC sampling on a model with EasyScience to obtain
full posterior distributions for the parameters.
- [Progress Callback](progress-callback.ipynb) – A tutorial showing how
to monitor fitting progress across minimizer backends and update a
notebook UI while a fit is running.
3 changes: 2 additions & 1 deletion docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,11 @@ nav:
- User Guide: user-guide/index.md
- Tutorials:
- Tutorials: tutorials/index.md
- Bayesian analysis: tutorials/fitting-bayesian.ipynb
- Progress Callback: tutorials/progress-callback.ipynb
- Workshops & Schools:
- Fitting QENS: tutorials/fitting-qens.ipynb
- Fitting SANS: tutorials/fitting-sans.ipynb
- Progress Callback: tutorials/progress-callback.ipynb
- API Reference:
- API Reference: api-reference/index.md
- base_classes: api-reference/base_classes.md
Expand Down
102 changes: 102 additions & 0 deletions src/easyscience/fitting/fitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,3 +412,105 @@ def _post_compute_reshaping(
fit_result.y_calc = np.reshape(fit_result.y_calc, y.shape)
fit_result.y_err = np.reshape(fit_result.y_err, y.shape)
return fit_result

def mcmc_sample(
self,
x: np.ndarray,
y: np.ndarray,
weights: np.ndarray,
samples: int = 10000,
burn: int = 2000,
thin: int = 10,
population: Optional[int] = None,
vectorized: bool = False,
sampler_kwargs: Optional[dict] = None,
progress_callback: Optional[Callable[[dict], Optional[bool]]] = None,
abort_test: Optional[Callable[[], bool]] = None,
) -> dict:
"""Run Bayesian MCMC sampling using the BUMPS DREAM sampler.

Works with both a plain ``Fitter`` (single dataset) and a
``MultiFitter`` (multiple datasets) via polymorphic dispatch:
``_precompute_reshaping`` and ``_fit_function_wrapper`` are resolved
on the concrete subclass at call time, so multi-dataset flattening
is handled automatically when called on a ``MultiFitter`` instance.

Parameters
----------
x : np.ndarray
Independent variable array (or list of arrays for ``MultiFitter``).
y : np.ndarray
Dependent variable array (or list of arrays for ``MultiFitter``).
weights : np.ndarray
Weight array (or list of arrays for ``MultiFitter``).
samples : int, default=10000
Number of retained DREAM samples requested from BUMPS.
burn : int, default=2000
Burn-in steps to discard before collecting samples.
thin : int, default=10
Thinning interval — only every ``thin``-th sample is kept,
which reduces autocorrelation between consecutive draws.
population : Optional[int], default=None
BUMPS DREAM population count (number of parallel chains).
vectorized : bool, default=False
When ``True``, each x array may be multi-dimensional (e.g. an
``(N, M, 2)`` grid for a 2D model) and is left as-is. When
``False`` (default), each x array is expected to be 1-D.
sampler_kwargs : Optional[dict], default=None
Additional keyword arguments forwarded to the BUMPS DREAM sampler.
progress_callback : Optional[Callable[[dict], Optional[bool]]], default=None
Optional callback invoked at each DREAM generation. The payload
dict includes ``iteration`` and ``sampling: True``.
abort_test : Optional[Callable[[], bool]], default=None
Optional callable that returns ``True`` to abort sampling early.

Returns
-------
dict
Dictionary with keys ``'draws'``, ``'param_names'``,
``'internal_bumps_object'``, and ``'logp'``.

Raises
------
ValueError
If ``samples``, ``burn``, or ``thin`` are invalid.
RuntimeError
If the active minimizer is not a BUMPS instance.
"""
if not isinstance(samples, int) or samples <= 0:
raise ValueError('samples must be a positive integer.')
if not isinstance(burn, int) or burn < 0:
raise ValueError('burn must be a non-negative integer.')
if not isinstance(thin, int) or thin < 1:
raise ValueError('thin must be a positive integer.')

x_fit, x_new, y_new, w_new, dims = self._precompute_reshaping(x, y, weights, vectorized)
self._dependent_dims = dims

original_fit_func = self._fit_function
self.fit_function = self._fit_function_wrapper(x_new, flatten=True)

try:
minimizer = self.minimizer
if not (hasattr(minimizer, 'package') and minimizer.package == 'bumps'):
raise RuntimeError(
'Bayesian sampling requires a BUMPS minimizer. '
'Use ``fitter.switch_minimizer(AvailableMinimizers.Bumps)`` first.'
)

result = minimizer.mcmc_sample(
x=x_fit,
y=y_new,
weights=w_new,
samples=samples,
burn=burn,
thin=thin,
population=population,
sampler_kwargs=sampler_kwargs,
progress_callback=progress_callback,
abort_test=abort_test,
)
finally:
self.fit_function = original_fit_func

return result
Loading
Loading