Python: Function doesn't receive a value within a for loop - python

I'm using the bisection method from the scipy.optimize package within a for loop.
The idea is to get a value of "sig" with the bisection method for each element (value) in the "eps_komp" vector. I've coded this much:
import numpy as np
import scipy.optimize as optimize
K=300
n = 0.43
E = 210000
Rm = 700
sig_a = []
RO_K = 300
RO_n = 0.43
eps_komp = [0.00012893048999999997,
0.018839115269999998,
0.01230539995,
0.022996934109999999,
-0.0037319012899999999,
0.023293921169999999,
0.0036927752099999997,
0.020621037629999998,
0.0063656587500000002,
0.020324050569999998,
-0.0025439530500000001,
0.018542128209999998,
0.01230539995,
0.019730076449999998,
0.0045837363899999999,
0.015275270549999997,
-0.0040288883499999999,
0.021215011749999999,
-0.0031379271699999997,
0.023590908229999999]
def eps_f(i):
return eps_komp[i]
for j in range(len(eps_komp)):
eps_komp_j = eps_f(j)
if j <= len(eps_komp):
def func(sig):
return eps_komp_j - sig/E - (sig/RO_K)**(1/RO_n)
sig_a.append(optimize.bisect(func, 0, Rm))
else:
break
print(sig_a)
Now if I change the the value of "j" in eps_f(j) to 0:
eps_komp_j = eps_f(0)
it works, and so it does for all other values that I insert by hand, but if I keep it like it is in the for loop, the "j" value doesnt change automatically and I get an error:
f(a) and f(b) must have different signs
Has anyone a clue what is the problem and how could this be solved?
Regards,
L
P.S. I did post another topic on this problem yesterday, but I wasnt very specific with the problem and got negative feedback. However, I do need to solve this today, so I was forced to post it again, however I did manage to get a bit further with the code then I did in the earlier post, so it isn't a repost...

If you read the docs you'll find that:
Basic bisection routine to find a zero of the function f between the arguments a and b. f(a) and f(b) cannot have the same signs. Slow but sure.
In your code:
def func(sig):
return eps_komp_j - sig/Emod - (sig/RO_K)**(1/RO_n)
sig_a.append(optimize.bisect(func, 0, Rm))
You're passing it func(0) and func(700).
By replacing the optimize.bisect line with print(func(0), func(700)) I get the following output:
0.00012893048999999997 -7.177181168628421
0.018839115269999998 -7.158470983848421
0.01230539995 -7.165004699168421
0.02299693411 -7.15431316500842
-0.00373190129 -7.1810420004084206
0.02329392117 -7.154016177948421
0.0036927752099999997 -7.173617323908421
0.02062103763 -7.156689061488421
0.00636565875 -7.17094444036842
0.02032405057 -7.156986048548421
-0.00254395305 -7.17985405216842
0.018542128209999998 -7.15876797090842
0.01230539995 -7.165004699168421
0.019730076449999998 -7.157580022668421
0.00458373639 -7.172726362728421
0.015275270549999997 -7.162034828568421
-0.00402888835 -7.181338987468421
0.02121501175 -7.156095087368421
-0.0031379271699999997 -7.1804480262884205
0.02359090823 -7.153719190888421
Note the multiple pairs that have the same signs. optimize.bisect can't handle those. I don't know what you're trying to accomplish, but this is the wrong approach.

Related

Python Error: "divide by zero encountered in log" in function, how to adjust? (Beginner here)

For my current assignment, I am to establish the stability of intersection/equilibrium points between two nullclines, which I have defined as follows:
def fNullcline(F):
P = (1/k)*((1/beta)*np.log(F/(1-F))-c*F+v)
return P
def pNullcline(P):
F = (1/delta)*(pD-alpha*P+(r*P**2)/(m**2+P**2))
return F
I also have a method "stability" that applies the Hurwitz criteria on the underlying system's Jacobian:
def dPdt(P,F):
return pD-delta*F-alpha*P+(r*P**2)/(m**2+P**2)
def dFdt(P,F):
return s*(1/(1+sym.exp(-beta*(-v+c*F+k*P)))-F)
def stability(P,F):
x = sym.Symbol('x')
ax = sym.diff(dPdt(x, F),x)
ddx = sym.lambdify(x, ax)
a = ddx(P)
# shortening the code here: the same happens for b, c, d
matrix = [[a, b],[c,d]]
eigenvalues, eigenvectors = np.linalg.eig(matrix)
e1 = eigenvalues[0]
e2 = eigenvalues[1]
if(e1 >= 0 or e2 >= 0):
return 0
else:
return 1
The solution I was looking for was later provided. Basically, values became too small! So this code was added to make sure no too small values are being used for checking the stability:
set={0}
for j in range(1,210):
for i in range(1,410):
x=i*0.005
y=j*0.005
x,y=fsolve(System,[x,y])
nexist=1
for i in set:
if(abs(y-i))<0.00001:
nexist=0
if(nexist):
set.add(y)
set.discard(0)
I'm still pretty new to coding so the function in and on itself is still a bit of a mystery to me, but it eventually helped in making the little program run smoothly :) I would again like to express gratitude for all the help I have received on this question. Below, there are still some helpful comments, which is why I will leave this question up in case anyone might run into this problem in the future, and can find a solution thanks to this thread.
After a bit of back and forth, I came to realise that to avoid the log to use unwanted values, I can instead define set as an array:
set = np.arange(0, 2, 0.001)
I get a list of values within this array as output, complete with their according stabilities. This is not a perfect solution as I still get runtime errors (in fact, I now get... three error messages), but I got what I wanted out of it, so I'm counting that as a win?
Edit: I am further elaborating on this in the original post to improve the documentation, however, I would like to point out again here that this solution does not seem to be working, after all. I was too hasty! I apologise for the confusion. It's a very rocky road for me. The correct solution has since been provided, and is documented in the original question.

Python TypeError: 'float' object is not iterable ​

I've been making a model in Python, but I cannot understand where's my mistake.
My IDE says that the problem is with 'rule1' , but I am not sure why. I've read about this typeerror and I found that the function 'range()' helps, but that generated me more problems. After that, I found that use '//' instead of '/' would help, but nothing changed.
I am not sure if I am not using these solutions right or my problem has more that one mistake in the code.
Thank you in advance for your help.
def model3(T,mu,alpha,beta):
d_3=np.zeros((3,3))
for h in range (T):
rew1=1
rew2=1
q1=0.5
q2=0.5
delta1=rew1-q1
delta2=rew2-q2
q1=q1+alpha*delta1
q2=q2+alpha*delta2
Q=range(q1+q2)
rule1=math.exp(beta*q1)//sum(math.exp(beta*(q1+q2)))
rule2=math.exp(beta*q2)//sum(math.exp(beta*(q1+q2)))
p=[rule1,rule2]
#Make choice according to choice probailities
c=random.choice(p)
#Generated reward
r_3=np.random.rand()
d_3[0,1]=1
d_3[0,2]=0
if c==p[0]:
m_3=mu[0]
d_3[1,0]=k[0]
else:
m_3=mu[1]
d_3[2,0]=k[1]
if r_3<m_3:
reward_3=1
if m_3==mu[0]:
d_3[1,1]=d_3[1,1]+1
else:
d_3[2,1]=d_3[2,1]+1
else:
reward_3=0
if m_3==mu[0]:
d_3[1,2]=d_3[1,2]+1
else:
d_3[2,2]=d_3[2,2]+1
return(d_3)
I believe the problem is in this line:
Q=range(q1+q2)
Both q1 and q2 are floats and even though they add up to 1.0 - it is still a float and you can't use range() with floating point arguments.
EDIT:
As per the comment below, you can try doing:
Q = range(int(q1 + q2))
Your code contains 2 range() function
for h in range(T):
and
Q = range(q1 + q2)
The range function can't iterate over floating point numbers so try using this below code lines to solve your issue.
for h in range(int(T)):
and
Q = range(int(q1 + q2))

How to change the domain (i.e. polynomial ring) using sympy in Pytho?

I'm in the middle of a big (and frankly quite hard) project so while this is my first interrogation, it probably won't be the last. Also : english is not my first langage so 'Sorry for bad english' and I'm writing this on my phone so 'Sorry for bad formating'.
Ok so : I'm trying to implement the General Number Field Sieve in Python, and I'm, at least for now, heavily relying on sympy.
Here is a peice of code where I'm struggling. In the code below, gpc(N,m) is a float list.
From sympy import Poly
From sympy.abc import x
g = Poly(gpc(N,m), x) [*]
However, when I do that, I get a polynomial over the domain RR and I would very much like to switch this to another domain D (where D will end up being ZZ['x'] but I would like this function to be general)
I'm aware of the fact that I can slightly modify [*] in
g = Poly(gpc(N,m), x, domain = D)
to get what I want. However, this wouldn't be enough. Somewhere else in my code, I need to be able to change the domain of an already constructed polynomial, and this solution wouldn't help.
When I lookep it up, I found the change_ring method so I tried this :
f = g.change_ring(D)
However, upon execution, I get the error message :
'Poly' object has no attribute 'change_ring'
So I guess that this function don't exist.
Does anyone knows how to change the domain of a polynomial ?
Thanks a lot !
It looks like creating a new Poly instance is the best approach; there are a few class methods that could help (take a look at the Poly.from_* class methods)
For example:
from sympy import Poly
from sympy.abc import x, a
g = Poly(x**3 + a*x*2 - 5*x + 6, x)
print(g) # Poly(x**3 + (2*a - 5)*x + 6, x, domain='ZZ[a]')
f = Poly.from_poly(g, *g.gens, domain='ZZ[a, b]')
print(f) # Poly(x**3 + (2*a - 5)*x + 6, x, domain='ZZ[a,b]')
I also wonder if rationalizing your floats at some point might help - see e.g. nsimplify.

Numpy: Get rid of loop in a concrete example

I'm new to Numpy and already read a few things about it. Repeatedly it says, that you should get rid of loops when using Numpy, because otherwise you have some Python-overhead which slows down your code. So for practicing I tried to implement a simple algorithm in a "numpythonic" way, but I can't manage to get rid of the for loop. Is there a way to improve this solution?
My main problem is, that I have some kind of "cumulative-conditional" situation and I have no idea how I can solve this without a loop.
import numpy as np
def process(data):
r = np.zeros(3)
for d in data:
ru = r / np.linalg.norm(r)
r = np.where(
np.dot(ru, d) < 0.0,
r + np.negative(d),
r + d
)
return r
data = [
[0.99558784, 0.03476669, -0.08715574],
[0.99194152, 0.1217951, -0.0348995],
[0.9864998, 0.08630755, -0.1391731]
]
print(process(data))
# Out: [ 2.97402916 0.24286934 -0.26122834]
(Besides of my main problem I'm open for general criticism or improvement suggestions of course)
A few comments:
In first loop, your ru = ... statement produces warning - it divides by 0.
In this case, np.dot() returns a single float, not an array. Hence, there is no point for using np.where(); if statement would be faster.
For the data you provided, your function is equivalent to np.sum(data, axis=0).

How to count elements in an array withtin a given increasing interval?

I have an array of time values. I want to know how many values are in each 0.05 seconds window.
For example, some values of my array are: -1.9493, -1.9433, -1.911 , -1.8977, -1.8671,..
In the first interval of 0.050 seconds (from -1.9493 to -1.893) I´m expecting to have 3 elements
I already create another array with the 0.050 seconds steps.
a=max(array)
b=min(array)
ventanalinea1=np.arange(b,a,0.05)
v1=np.array(ventanalinea1)
In other words, I would like to compare my original array with this one.
I would like to know if there is a way to ask python to evaluate my array within a given dynamic range.
One of the variants:
import numpy as np
# original array
a = [-1.9493, -1.9433, -1.911 , -1.8977, -1.8671]
step = 0.05
bounds = np.arange(min(a), max(a) + step, step)
result = [
list(filter(lambda x: b[i] <= x <= b[i+1], a))
for i in range(len(b)-1)
]
I have found a cool python library python-intervals that simplify your problem a lot:
Install it with pip install python-intervals and try the code below.
import intervals as I
# This is a recursive function
def counter(timevalues, w=0.050):
if not timevalues:
return "" # stops recursion when timevalues is empty
# Make an interval object that provides convenient interval operations like 'contains'
window = I.closed(
timevalues[0], timevalues[0] + w)
interval = list(
filter(window.contains, timevalues))
count = len(interval)
timevalues = timevalues[count:]
print(f"[{interval[0]} : {interval[-1]}] : {count}")
return counter(timevalues)
if __name__ == "__main__":
times = [-1.9493, -1.9433, -1.911, -1.8977, -1.8671]
print(counter(times))
Adapt it as you wish, for example you might want to return a dictionary rather that a string.
You could still get around this without using the python-intervals library here but i have introduced it here because it will be very likely that you would need other complex operations along the way on your code.

Categories