lmfit: constraining parameters with respect to other ones - python

I am having troubles setting the min and max values of a parameter to be fitted in lmfit. Basically, I have two parameters, a and b which need to obey the following constraints:
0 < b < 1
0 < a+b < 1
-b < a < 1
While the first two are rather easy to implement as:
fit_params.add('b', min = 0, max = 1)
fit_params.add('a_plus_b', min = 0, max = 1)
fit_params.add('a', expr = 'a_plus_b-b')
I am missing now an idea on how to include in the system my third condition. Can you suggest a further expression?
Thank you very much,
Leo

The solution is pretty simple, and I should only have read better the lmfit manual here: https://lmfit.github.io/lmfit-py/constraints.html
Trying anyway to be helpful, the solution is given by implementing an if sentence in the parameter expression.
fit_params.add('b', min = 0, max = 1)
fit_params.add('a_plus_b', min = 0, max = 1)
fit_params.add('a', expr = 'a_plus_b-b if a_plus_b-b > 0 else 0.')

I think that you don't actually need your third condition here, as
-b < a < 1
can be re-written as
0 < a+b < 1+b
but since b>0, this condition will always be met with 0 < a+b < 1.
But, to answer the question more generally, you would probably need to introduce another "difference-or-sum variable" as you did with a_plus_b and use that.

Related

Is there a faster way to know if it's a real triangle?

The following code is intended to return 0 if it's not possible to form a triangle with three given sides and return 1 if it is possible. Taking into account that it's not possible for a triangle to have an area of zero or less, first I calculated the semiperimeter of this hypothetical triangle and then return 1 if the result is greater than the greatest value in the given list. I did the previous because if the semiperimeter is less than the greatest value in the list, then when calculating the area with the Heron formula it would be an undefined root.
def isTriangle(aList):
semiperimeter = (aList[0] + aList[1] + aList[2]) / 2
if semiperimeter > max(aList):
return 1
else:
return 0
So I want to know if there is a faster way in Python to perform this (or at least with fewer lines of code).
You can check if the sum of the two smaller sides is at least as large as the longest side:
def check(aList)
return sum(aList) - max(aList) > max(aList)
With python 3.8+, you can avoid recomputing the maximum twice using a walrus operator:
def check(aList):
return (m := max(aList)) < sum(aList) - m
Both solutions rely on numerical comparisons returning a big boolean. In python bool is a subclass of int that has only two possible singleton values: True == 1 and False == 0. It's standard to return a bool rather than 0 or 1 for something like this. In fact, your original conditional could be replaced by
return semiperimeter > max(aList)
Or, absorbing the previous line into the return:
return sum(aList) / 2 > max(aList)
You could also write
return sum(aList) > 2 * max(aList)
This version avoids any type conversions, regardless if whether you pass in floats or integers (/ 2 always results in a float).
You asked for shorter code. That one is possible to do as a oneliner.
def isTriangle(aList):
return ((aList[0] + aList[1] + aList[2]) / 2) > max(aList) and 1 or 0
Exatly the same code, but just written shorter
To determine if given 3 sides can form a valid triangle, I would suggest using Triangle Inequality Theorem. It roughly states that the sum of any two sides must always be equal or greater than the other side to be able to form a valid triangle.
Therefore, I would instead write something along this line. [Edited: initialization according to Avinash]
def isTriangle(aList):
a,b,c = aList
return (a + b >= c and a + c >= b and b + c >= a)
In case you really want to make it return 0 or 1 instead of boolean then you can use this return instead.
return 1 if a + b >= c and a + c >= b and b + c >= a else 0

Count Instances in One Array Based on Another Array

I am wondering if there is an efficient way to perform the following. I have two (numpy) arrays, and I would like to count the number of instances of a value occurring in one based on the criteria of another another array. For example:
a = np.array([1,-1,1,1,-1,-1])
b = np.array([.75,.35,.7,.8,.2,.6])
I would like to calculate c as the number of 1's in a that occur when b > .5, so in this case `c = 3'. My current solution is ugly and would appreciate any suggestions.
You can use numpy.sum for this:
a = np.array([1,-1,1,1,-1,-1])
b = np.array([.75,.35,.7,.8,.2,.6])
np.sum((a == 1) & (b > .5)) # 3
This works because bool is a subclass of int.
If it's only one condition you are after, try:
np.count_nonzero((a == 1) & (b > .5))

Check if two values are both non-negative or both negative

Given a pair of integer values, I need to check if both are non-negative or both are negative.
The trivial way is:
def func(a, b):
return (a >= 0 and b >= 0) or (a < 0 and b < 0)
But I am looking for something "neater", which I believe to be possible, so I came up with this:
def func(a, b):
return (a >= 0) ^ (b >= 0) == 0
However, this one feels a little "obscure to the average reader".
Is there a cleaner way?
Multiply them and test against 0:
def func(a, b):
return a * b >= 0
This is Python. We're not about the most concise efficient possible way to do things in all cases - that's for the C++ lads to do.
If (a >= 0 and b >= 0) or (a < 0 and b < 0) is "fast enough", (which would surprise me if not the case), then stick with it. It works, and is VERY obvious what it does.
For either your second solution, or #coldspeed's, I personally would have to write some stuff down on paper to figure out what it's doing, unless the function name was a LOT better than func.

Python gives me different outputs if I add a print() later on

I'm super new to programming and just wanted to make something to find the approximate value of x for the equation x^3 - 1 = x. I know that -2 is greater than 0 and -1 is less. My thought is that if I find the average and check if it's greater or less than 0, I can redefine a and b and repeat until I get an approximate value. I've been having a hard time getting it to act right though. For example if I run this block without the print(i), I'll get -1.5 which would be the average, but when I put a print(total) within the function equation(n) to see if it's working right, it doesn't even show me that and just outputs -8.881784197001252e-16. If I put print(i) at the end of the if/else possibilities, such as this, it prints 16 and then -8.881784197001252e-16. I'm using PyCharm CE.
Beyond this glitch, is my logic correct? By setting the placeholder to 1 it will run the while loop. The while loop will get a new value of n and run the function, compare it to 0, then reassign a or b depending on that comparison? Thanks in advance.
a = float(-2)
b = float(-1)
n = ((a+b)/2)
print(n)
def equation(n):
total = float((n - n**3 - 1))
return total
i = 1
while i != 0:
n = ((a + b) / 2)
if (equation(n)) > 0.0:
a = n
i = equation(n)
print(i)
else:
b = n
i = equation(n)
print(i)
Moving all of the equation x^3 - 1 = x to one side should give x^3 - 1 - x = 0 or x - x^3 + 1 = 0. You have a different equation in your function.
Another problem is that there is no intersection between the two equations between x=-2 and x=-1 (see here). You'll need to expand your window to x=2 before you'll see an intersection.
Something that often happens in numerical analysis (where you'll see this type of problem) is that rather than trying to find x that actually makes the equation give 0, we look for a value of x that produces below an acceptable level of error. Another approach is to test for while b - a > tol:
If we use all of this to tweak what you've got, you'll have
a = float(-2)
b = float(2)
tol = 0.001
def equation(n):
return float(n - n**3 + 1)
n = (a + b) / 2
iter = 0
while abs(equation(n) - 0) > tol and iter < 100:
iter+=1
if equation(n) > 0.0:
a = n
else:
b = n
n = (a + b) / 2
print(iter,a,b,equation(n))
Note: this works fine if you remove the floats and just do
a = -2
b = 2
#...etc
because python already recasts values as necessary. Try
>>> type(3)
<class 'int'>
>>> type(3.5)
<class 'float'>
>>> type(float(3))
<class 'float'>
>>> type(3/5)
<class 'float'>
so python will store the result as a float as soon as it is necessary.
The immediate problem is the limited precision of floats. If you print a and b after a hundred iterations, you get:
a, b = -1.324717957244746, -1.3247179572447458
print((a + b) / 2 # -1.3247179572447458, the same as b
So at some point, b never changes, which is why you get an infinite loop. If we evaluate equation at the average of a and b, you'll get -8.881784197001252e-16, the value you were always seeing.
But this will never converge to exactly zero because the solution is irrational, so even if you had infinite precision, equation would never equal zero.
The common way to resolve this issue is to avoid comparing floats:
if a == b # don't do this
if abs(a - b) < epsilon # do this, where epsilon is some small value
(Note: what you're describing is the bisection method, which is slower than higher order algorithms e.g. Newton's method, which you can use since you can get the derivative)

Python - writing print

I'm trying to learn Python and going through some exercises, it’s all going good and I’ve learned some new stuff.
But I came across this code over the internet and it wants me to write out what the new Max and Min is supposed to become also B will be after the run.
I have tried to look for the correct answer but no one has printed it. I’ve tried to run it in my IDE but actually I never get an output. I’ve also tried writing “print(b)” but I get no result.
min = 5
max = 10
b = 0
a = -2
if a < min:
min = a
elif a > max:
max = a
else:
b += 1
What I’ve thought the answer should be is Min will be -2 since a is declared as -2 and the first statement “if a < min:” says if -2 is less than 5, 5 will become -2.
Other statement “a > max:” asks if -2 is higher than 10, 10 will be –2 but since -2 is not higher we go to the else which says that we should plus 1 to 0 and that will give b = 1. So my answer is min = -2 , max = 10 , and b = 1.
Is my answer correct and if not what have I done wrong? Also, is there any way I can run it in python and get the answer through print(b, max, min) I’ve tried that but it’s not working.
I guess the second condition should be if not elif. An if-elif-else block exits as soon as any of them satisfies the condition. So, as in your code the first condition if a < min is True, the next conditions are not going to be checked.
min = 5
max = 10
b = 0
a = -2
if a < min:
min = a
if a > max:
max = a
else: <-- this is executed only if: `a > max`: is `False`
b += 1
print b, max, min
#1 10 -2

Categories