Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncertainties for constrained parameters #1682

Merged
merged 10 commits into from
Aug 31, 2021
1 change: 1 addition & 0 deletions build_tools/conda_qt5_min_ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ dependencies:
- bumps==0.7.8
- xhtml2pdf==0.2.2
- qt5reactor==0.5
- uncertainties
1 change: 1 addition & 0 deletions build_tools/conda_qt5_osx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,4 @@ dependencies:
- xhtml2pdf==0.2.3
- qt5reactor==0.5
- unittest-xml-reporting
- uncertainties
1 change: 1 addition & 0 deletions build_tools/conda_qt5_win_commercial.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ dependencies:
- ../../../PyQt5_commercial-5.12.2-5.12.3-cp35.cp36.cp37.cp38-none-win_amd64.whl
- qt5reactor==0.5
- tinycc==1.1
- uncertainties
47 changes: 32 additions & 15 deletions src/sas/sascalc/fit/BumpsFitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import traceback

import numpy as np
from uncertainties import ufloat as U

from bumps import fitters
try:
Expand Down Expand Up @@ -282,32 +283,48 @@ def fit(self, msg_q=None,

# TODO: shouldn't reference internal parameters of fit problem
varying = problem._parameters
# Propagate uncertainty through the parameter expressions
# We are going to abuse bumps a little here and stuff uncertainty
# objects into the parameter values, then update all the
# derived parameters with uncertainty propagation. We need to
# avoid triggering a model recalc since the uncertainty objects
# will not be working with sasmodels.
# TODO: if dream then use forward MC to evaluate uncertainty
# TODO: move uncertainty propagation into bumps
# TODO: should scale stderr by sqrt(chisq/DOF) if dy is unknown
values, errs = result['value'], result['stderr']
assert values is not None and errs is not None
# first have the parameter value attribute point towards a
# uncertainty object with 0 standard deviation
for model in problem.model_parameters()["models"]:
for param in model:
model[param].value = U(model[param].value, 0)
# then update the computed standard deviation of fitted parameters
for p, v, s in zip(varying, values, errs):
p.value = U(v, s)
problem.setp_hook()
# collect the results
all_results = []
for M in problem.models:
fitness = M.fitness
fitted_index = [varying.index(p) for p in fitness.fitted_pars]
param_list = fitness.fitted_par_names + fitness.computed_par_names
R = FResult(model=fitness.model, data=fitness.data,
param_list=param_list)
par_names = fitness.fitted_par_names + fitness.computed_par_names
pars = fitness.fitted_pars + fitness.computed_pars
R = FResult(model=fitness.model,
data=fitness.data,
param_list=par_names)
R.theory = fitness.theory()
R.residuals = fitness.residuals()
R.index = fitness.data.idx
R.fitter_id = self.fitter_id
# TODO: should scale stderr by sqrt(chisq/DOF) if dy is unknown
R.success = result['success']
if R.success:
if result['stderr'] is None:
R.stderr = np.NaN*np.ones(len(param_list))
else:
R.stderr = np.hstack((result['stderr'][fitted_index],
np.NaN*np.ones(len(fitness.computed_pars))))
R.pvec = np.hstack((result['value'][fitted_index],
[p.value for p in fitness.computed_pars]))
R.fitness = np.sum(R.residuals**2)/(fitness.numpoints() - len(fitted_index))
R.pvec = np.array([p.value.n for p in pars])
R.stderr = np.array([p.value.s for p in pars])
DOF = max(1, fitness.numpoints() - len(fitness.fitted_pars))
R.fitness = np.sum(R.residuals ** 2) / DOF
else:
R.stderr = np.NaN*np.ones(len(param_list))
R.pvec = np.asarray([p.value for p in fitness.fitted_pars+fitness.computed_pars])
R.pvec = np.asarray([p.value for p in pars])
R.stderr = np.NaN * np.ones(len(pars))
R.fitness = np.NaN
R.convergence = result['convergence']
if result['uncertainty'] is not None:
Expand Down