Skip to content

Commit

Permalink
Merge pull request #1682 from SasView/ESS_GUI_uncertainties
Browse files Browse the repository at this point in the history
Uncertainties for constrained parameters
  • Loading branch information
krzywon authored Aug 31, 2021
2 parents a18f684 + 23498a5 commit c5467c0
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 18 deletions.
1 change: 1 addition & 0 deletions build_tools/conda_py3_qt5_env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,4 @@ dependencies:
- tinycc==1.1
- webencodings==0.5.1
- xhtml2pdf==0.2.1
- uncertainties==3.1.6
1 change: 1 addition & 0 deletions build_tools/conda_py3_qt5_osx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,4 @@ dependencies:
- reportlab==3.4.0
- webencodings==0.5.1
- xhtml2pdf==0.2.2
- uncertainties==3.1.6
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 @@ -29,5 +29,6 @@ dependencies:
- bumps
- xhtml2pdf==0.2.2
- qt5reactor==0.5
- uncertainties==3.1.6
- pillow==8.1.2
- h5py==3.1
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:
- qt5reactor==0.5
- unittest-xml-reporting
- lxml==4.6.3
- uncertainties==3.1.6
1 change: 1 addition & 0 deletions build_tools/conda_qt5_ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,5 @@ dependencies:
- werkzeug==0.14.1
- xhtml2pdf==0.2.2
- zope.interface==4.5.0
- uncertainties==3.1.6

1 change: 1 addition & 0 deletions build_tools/conda_qt5_win.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ dependencies:
- qt5reactor==0.5
- tinycc==1.1
- pillow==8.1.2
- uncertainties==3.1.6
4 changes: 3 additions & 1 deletion build_tools/conda_qt5_win_commercial.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,7 @@ dependencies:
- ../../../PyQt5_commercial-5.12.2-5.12.3-cp35.cp36.cp37.cp38-none-win_amd64.whl
- qt5reactor==0.5
- tinycc==1.1
- uncertainties==3.1.6
- pillow==8.1.2
- h5py==3.1
- h5py==3.1

79 changes: 62 additions & 17 deletions src/sas/sascalc/fit/BumpsFitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
import traceback

import numpy as np
from uncertainties import ufloat, correlated_values

from bumps import fitters
from bumps.parameter import unique as unique_parameters

try:
from bumps.options import FIT_CONFIG
# Default bumps to use the Levenberg-Marquardt optimizer
Expand Down Expand Up @@ -279,35 +282,73 @@ def fit(self, msg_q=None,
result = run_bumps(problem, handler, curr_thread)
if handler is not None:
handler.update_fit(last=True)
propagate_errors = all(model.constraints for model in models)
uncertainty_error = None
if propagate_errors:
# 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, cov = result['value'], result["stderr"], result[
'covariance']
assert values is not None
# Turn all parameters (fixed and varying) into uncertainty objects with
# zero uncertainty.
for p in unique_parameters(problem.model_parameters()):
p.value = ufloat(p.value, 0)
# then update the computed standard deviation of fitted parameters
if len(varying) < 2:
varying[0].value = ufloat(values[0], errs[0])
else:
fitted = (correlated_values(values, cov) if not errs else
[ufloat(value, np.nan) for value in values])

for p, v in zip(varying, fitted):
p.value = v
try:
problem.setp_hook()
except np.linalg.LinAlgError:
fitted = [ufloat(value, err) for value, err in zip(
values, errs)]
uncertainty_error = True
for p, v in zip(varying, fitted):
p.value = v
problem.setp_hook()
except TypeError:
propagate_errors = False
uncertainty_error = True

# TODO: shouldn't reference internal parameters of fit problem
varying = problem._parameters
# 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]) if
propagate_errors else result['value'])
R.stderr = (np.array([p.value.s for p in pars]) if
propagate_errors else result["stderr"])
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 Expand Up @@ -367,15 +408,19 @@ def abort_test():
success = best is not None
try:
stderr = fitdriver.stderr() if success else None
cov = (fitdriver.cov() if not hasattr(fitdriver.fitter, 'state') else
np.cov(fitdriver.fitter.state.draw().points.T))
except Exception as exc:
errors.append(str(exc))
errors.append(traceback.format_exc())
stderr = None
cov = None
return {
'value': best if success else None,
'stderr': stderr,
'success': success,
'convergence': convergence,
'uncertainty': getattr(fitdriver.fitter, 'state', None),
'errors': '\n'.join(errors),
'covariance': cov,
}

0 comments on commit c5467c0

Please sign in to comment.