File: test_algebraic_constraint.py

package info (click to toggle)
lmfit-py 1.3.3-4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 2,332 kB
  • sloc: python: 13,071; makefile: 130; sh: 30
file content (115 lines) | stat: -rw-r--r-- 4,008 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""Tests for algebraic parameter constraints."""
import numpy as np
import pytest

from lmfit import Minimizer, Model, Parameters
from lmfit.lineshapes import gaussian, lorentzian


@pytest.fixture
def minimizer():
    """Return the Minimizer object."""
    def residual(pars, x, sigma=None, data=None):
        """Define objective function."""
        yg = gaussian(x, pars['amp_g'], pars['cen_g'], pars['wid_g'])
        yl = lorentzian(x, pars['amp_l'], pars['cen_l'], pars['wid_l'])

        model = yg + yl + pars['line_off'] + x * pars['line_slope']

        if data is None:
            return model
        if sigma is None:
            return model - data
        return (model-data) / sigma

    # generate synthetic data
    n = 601
    xmin = 0.
    xmax = 20.0
    x = np.linspace(xmin, xmax, n)

    data = (gaussian(x, 21, 8.1, 1.2) + lorentzian(x, 10, 9.6, 2.4) +
            np.random.normal(scale=0.23, size=n) + x*0.5)

    # create initial Parameters
    pars = Parameters()
    pars.add(name='amp_g', value=10)
    pars.add(name='cen_g', value=9)
    pars.add(name='wid_g', value=1)
    pars.add(name='amp_tot', value=20)
    pars.add(name='amp_l', expr='amp_tot - amp_g')
    pars.add(name='cen_l', expr='1.5+cen_g')
    pars.add(name='wid_l', expr='2*wid_g')
    pars.add(name='line_slope', value=0.0)
    pars.add(name='line_off', value=0.0)

    sigma = 0.021  # estimate of data error (for all data points)

    mini = Minimizer(residual, pars, fcn_args=(x,), fcn_kws={'sigma': sigma,
                                                             'data': data})

    return mini


def test_algebraic_constraints(minimizer):
    """Test algebraic constraints."""
    result = minimizer.minimize(method='leastsq')

    pfit = result.params
    assert pfit['cen_l'].value == 1.5 + pfit['cen_g'].value
    assert pfit['amp_l'].value == pfit['amp_tot'].value - pfit['amp_g'].value
    assert pfit['wid_l'].value == 2.0 * pfit['wid_g'].value


def test_algebraic_constraints_function(minimizer):
    """Test constraints with a user-defined function added to symbol table."""
    def width_func(wpar):
        return 2.5*wpar

    minimizer.params._asteval.symtable['wfun'] = width_func
    minimizer.params.add(name='wid_l', expr='wfun(wid_g)')
    result = minimizer.minimize(method='leastsq')

    pfit = result.params
    assert pfit['cen_l'].value == 1.5 + pfit['cen_g'].value
    assert pfit['amp_l'].value == pfit['amp_tot'].value - pfit['amp_g'].value
    assert pfit['wid_l'].value == 2.5 * pfit['wid_g'].value


def test_constraints_function_call():
    """Test a constraint with simple function call in Model class."""

    x = np.array([1723, 1773, 1823, 1523, 1773, 1033.03078, 1042.98077,
                  1047.90937, 1053.95899, 1057.94906, 1063.13788, 1075.74218,
                  1086.03102])
    y = np.array([0.79934, -0.31876, -0.46852, 0.05, -0.21, 11.1708, 10.31844,
                  9.73069, 9.21319, 9.12457, 9.05243, 8.66407, 8.29664])

    def VFT(T, ninf=-3, A=5e3, T0=800):
        return ninf + A/(T-T0)

    vftModel = Model(VFT)
    vftModel.set_param_hint('D', vary=False, expr=r'A*log(10)/T0')
    result = vftModel.fit(y, T=x)

    assert 2600.0 < result.params['A'].value < 2650.0
    assert 7.0 < result.params['D'].value < 7.5


def test_constraints(minimizer):
    """Test changing of algebraic constraints."""
    result = minimizer.minimize(method='leastsq')

    pfit = result.params
    assert pfit['cen_l'].value == 1.5 + pfit['cen_g'].value
    assert pfit['amp_l'].value == pfit['amp_tot'].value - pfit['amp_g'].value
    assert pfit['wid_l'].value == 2.0*pfit['wid_g'].value

    # now, change fit slightly and re-run
    minimizer.params['wid_l'].expr = '1.25*wid_g'
    result = minimizer.minimize(method='leastsq')
    pfit = result.params

    assert pfit['cen_l'].value == 1.5 + pfit['cen_g'].value
    assert pfit['amp_l'].value == pfit['amp_tot'].value - pfit['amp_g'].value
    assert pfit['wid_l'].value == 1.25*pfit['wid_g'].value