Skip to content

Commit a03ac0f

Browse files
authored
Merge pull request #860 from lmfit/param_vary_setter
make Parameter.vary a property
2 parents 52012fd + 8e5c5d2 commit a03ac0f

File tree

4 files changed

+53
-9
lines changed

4 files changed

+53
-9
lines changed

doc/whatsnew.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ New features:
2626

2727
Bug fixes/enhancements:
2828

29+
- fix bug when setting ``param.vary=True`` for a constrained parameter (Issue #859; PR #860)
2930
- fix bug in reported uncertainties for constrained parameters by better propating uncertainties (Issue #855; PR #856)
3031
- Coercing of user input data and independent data for ``Model`` to float64 ndarrays is somewhat less aggressive and
3132
will not increase the precision of numpy ndarrays (see :ref:`model_data_coercion_section` for details). The resulting

examples/doc_builtinmodels_peakmodels.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@
4949
axes[0].plot(x, out.best_fit, '-', label='Voigt Model\ngamma constrained')
5050
axes[0].legend()
5151

52-
# free gamma parameter
53-
pars['gamma'].set(value=0.7, vary=True, expr='')
52+
# allow the gamma parameter to vary in the fit
53+
pars['gamma'].vary = True
5454
out_gamma = mod.fit(y, pars, x=x)
55+
print(out.fit_report(correl_mode='table'))
56+
5557
axes[1].plot(x, y, '-')
5658
axes[1].plot(x, out_gamma.best_fit, '-', label='Voigt Model\ngamma unconstrained')
5759
axes[1].legend()

lmfit/parameter.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ def __init__(self, name, value=None, vary=True, min=-inf, max=inf,
639639
self.min = min
640640
self.max = max
641641
self.brute_step = brute_step
642-
self.vary = vary
642+
self._vary = vary
643643
self._expr = expr
644644
self._expr_ast = None
645645
self._expr_eval = None
@@ -702,7 +702,7 @@ def set(self, value=None, vary=None, min=None, max=None, expr=None,
702702
703703
"""
704704
if vary is not None:
705-
self.vary = vary
705+
self._vary = vary
706706
if vary:
707707
self.__set_expression('')
708708

@@ -751,13 +751,13 @@ def _init_bounds(self):
751751

752752
def __getstate__(self):
753753
"""Get state for pickle."""
754-
return (self.name, self.value, self.vary, self.expr, self.min,
754+
return (self.name, self.value, self._vary, self.expr, self.min,
755755
self.max, self.brute_step, self.stderr, self.correl,
756756
self.init_value, self.user_data)
757757

758758
def __setstate__(self, state):
759759
"""Set state for pickle."""
760-
(self.name, _value, self.vary, self.expr, self.min, self.max,
760+
(self.name, _value, self._vary, self.expr, self.min, self.max,
761761
self.brute_step, self.stderr, self.correl, self.init_value,
762762
self.user_data) = state
763763
self._expr_ast = None
@@ -772,7 +772,7 @@ def __repr__(self):
772772
"""Return printable representation of a Parameter object."""
773773
s = []
774774
sval = f"value={repr(self._getval())}"
775-
if not self.vary and self._expr is None:
775+
if not self._vary and self._expr is None:
776776
sval += " (fixed)"
777777
elif self.stderr is not None:
778778
sval += f" +/- {self.stderr:.3g}"
@@ -882,6 +882,18 @@ def value(self, val):
882882
if self._expr_eval is not None:
883883
self._expr_eval.symtable[self.name] = self._val
884884

885+
@property
886+
def vary(self):
887+
"""Return whether the parameter is variable"""
888+
return self._vary
889+
890+
@vary.setter
891+
def vary(self, val):
892+
"""Set whether a parameter is varied"""
893+
self._vary = val
894+
if val:
895+
self.__set_expression('')
896+
885897
@property
886898
def expr(self):
887899
"""Return the mathematical expression used to constrain the value in fit."""
@@ -901,7 +913,7 @@ def __set_expression(self, val):
901913
val = None
902914
self._expr = val
903915
if val is not None:
904-
self.vary = False
916+
self._vary = False
905917
if not hasattr(self, '_expr_eval'):
906918
self._expr_eval = None
907919
if val is None:

tests/test_parameters.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
"""Tests for the Parameters class."""
22

3-
43
from copy import copy, deepcopy
4+
import os
55
import pickle
66

77
import numpy as np
88
from numpy.testing import assert_allclose
99
import pytest
1010

1111
import lmfit
12+
from lmfit.models import VoigtModel
1213

1314

1415
@pytest.fixture
@@ -605,3 +606,31 @@ def test_create_params():
605606
assert pars1['d'].value == 11
606607
assert pars1['e'].value == 10000
607608
assert pars1['e'].brute_step == 4
609+
610+
611+
def test_unset_constrained_param():
612+
"""test 'unsetting' a constrained parameter by
613+
just setting `param.vary = True`
614+
615+
"""
616+
data = np.loadtxt(os.path.join(os.path.dirname(__file__), '..',
617+
'examples', 'test_peak.dat'))
618+
x = data[:, 0]
619+
y = data[:, 1]
620+
621+
# initial fit
622+
mod = VoigtModel()
623+
params = mod.guess(y, x=x)
624+
out1 = mod.fit(y, params, x=x)
625+
626+
assert out1.nvarys == 3
627+
assert out1.chisqr < 20.0
628+
629+
# now just gamma to vary
630+
params['gamma'].vary = True
631+
out2 = mod.fit(y, params, x=x)
632+
633+
assert out2.nvarys == 4
634+
assert out2.chisqr < out1.chisqr
635+
assert out2.rsquared > out1.rsquared
636+
assert out2.params['gamma'].correl['sigma'] < -0.6

0 commit comments

Comments
 (0)