2

How to put conditions on parameters while using lmfit.minimize?

from lmfit import Parameters,minimize, fit_report
import numpy as np

x = np.linspace(0,10,100)
y = 2.39645 * x**2 + np.random.normal(0, 2, 100)

def fun1(params,x,y):
    k1 = params['k1']
    k2 = params['k2']
    k3 = params['k3']
    y_fit = k1*x**2 + k2*x + k3
    return y_fit-y 

# Defining the various parameters
params = Parameters()
params.add('k1', value = 1)
params.add('k2', value = 1)
params.add('k3', value = 1)

fitted_params = minimize(fun1, params, args=(x,y,), method='least_squares')

This gives optimal k1, k2 and k3

name    value   standard error  relative error  initial value   min max vary
k1  2.42637846  0.02748469  (1.13%) 1   -inf    inf True
k2  -0.43530957 0.28403716  (65.25%)    1   -inf    inf True
k3  1.02895856  0.61456597  (59.73%)    1   -inf    inf True

I want to put constraints on k1, k2 and k3 such that: k1>k2>k3. How can I do that?

EDIT

I have introduced 2 parameters delta1 and delta2 as below-


# Defining the various parameters
params = Parameters()
params.add('k1', value = 1)
params.add('delta1', value = 1, min = 0)
params.add('k2', value = 1, expr = "k1 - delta1")
params.add('k2', value = 1)
params.add('delta2', value = 1, min = 0)
params.add('k3', value = 1, expr = "k2 - delta2")

#fitting function
fitted_params = minimize(fun1, params, args=(x,y,), method='least_squares')

#Printing parameters
fitted_params.params.pretty_print()

The new parameters are as below-

Name       Value      Min      Max   Stderr     Vary     Expr Brute_Step
delta1     1.726        0      inf     None     True     None     None
delta2  2.833e-11        0      inf     None     True     None     None
k1         2.398     -inf      inf     None     True     None     None
k2      -0.01788     -inf      inf     None     True     None     None
k3      -0.01788     -inf      inf     None    False k2 - delta2     None

We have k1>k2>k3 in new paramters, but the k2 and k2 are almost the same. Is there something can be done to avoid this, or these are the optimal solutions based on the given constrains?

lsr729
  • 752
  • 2
  • 11
  • 25

1 Answers1

1

Try the following using the ref. https://lmfit.github.io/lmfit-py/constraints.html

...

# Defining the various parameters
params = Parameters()
params.add('k3', value=1)
params.add('delta1', value=1, vary=False)
params.add('k2', expr='delta1+k3')
params.add('delta2', value=1, vary=False)
params.add('k1', expr='delta2+k2')

fitted_params = minimize(fun1, params, args=(x,y,), method='least_squares')
print(fitted_params.params.pretty_print())

Typical output

Name       Value      Min      Max   Stderr     Vary     Expr Brute_Step
delta1         1     -inf      inf        0    False     None     None
delta2         1     -inf      inf        0    False     None     None
k1         2.241     -inf      inf        0    False delta2+k2     None
k2         1.241     -inf      inf 0.005612    False delta1+k3     None
k3         0.241     -inf      inf 0.005612     True     None     None

Vary delta2

# Defining the various parameters
params = Parameters()
params.add('k3', value=1)
params.add('delta1', value=1, vary=False)
params.add('k2', expr='delta1+k3')
params.add('delta2', value=0, vary=True)
params.add('k1', expr='delta2+k2')

Output

Name       Value      Min      Max   Stderr     Vary     Expr Brute_Step
delta1         1     -inf      inf        0    False     None     None
delta2     2.051     -inf      inf   0.1028     True     None     None
k1         2.363     -inf      inf   0.1028    False delta2+k2     None
k2        0.3113     -inf      inf  0.09006    False delta1+k3     None
k3       -0.6887     -inf      inf  0.09006     True     None     None
ferdy
  • 4,396
  • 2
  • 4
  • 16
  • Probably the minimum value of delta1 should be set to zero and it should vary. Your solution makes k2-k3=1. – lsr729 Nov 16 '21 at 04:04
  • My first concern is the specified constrains `k1>k2>k3` which has been satisfied. – ferdy Nov 16 '21 at 04:34
  • But your solution limits the k2 + k3 to be 1, and k1+k2 to be 0, which may ignore other possible solutions? – lsr729 Nov 16 '21 at 14:50
  • Where is this `k2 + k3 to be 1, and k1+k2 to be 0` coming from? See my example outputs. – ferdy Nov 16 '21 at 16:01
  • In this case, your solution works, but if you change the y as `y = 0.2 * x**2 + 108*np.random.normal(0, 2, 100)`, the constrain will not stand. To make your solution general, you should fix the minimum value of delta1 and delta2 to be 0, and both can vary. – lsr729 Nov 16 '21 at 17:11
  • With your new `y` definition of `y = 0.2 * x**2 + 108*np.random.normal(0, 2, 100)`, varying the delta2 as in `params.add('delta2', value=0, vary=True)` will fail, but fixing the delta2 as in `params.add('delta2', value=1, vary=False)` will not fail. And your suggestion of fixing the deltas to 0 and varying it will fail on `contraints k1 > k2 > k3`, understandable because when all variables can vary, there is no longer a guarantee that `k1 > k2 > k3` will be satisfied. Remember we are also minimizing the `fun1()`, if you change `y` something must be done too on delta definitions. – ferdy Nov 17 '21 at 00:02