solving dynamic number of non-linear equations in python - python

Fsolve in Scipy seems to be the right candidate for this, I just need help passing equations dynamically. I appreciate any thoughts in advance.
By dynamic I mean number of equations differ from one run to another for example one situation i have :
alpha*x + (1-alpha)*x*y - y = 0
beta*x + (1- beta)*x*z - z = 0
A*x + B*y + C*z = D
and another situation i have:
alpha*x + (1-alpha)*x*y - y = 0
beta*x + (1- beta)*x*z - z = 0
gama*x + (1 -gama)*x*w - w =0
A*x + B*y + C*z + D*w = E
alpha, beta, A, B, C, D and E are all constants. x, y, z, w are variables.

I haven't used Fsolve myself, but according to its documentation it takes a callable function.
Something like this handles multiple functions with unknown number of variables. Bear in mind that the args must be ordered correctly here, but each function simply takes a list.
def f1(argList):
x = argList[0]
return x**2
def f2(argList):
x = argList[0]
y = argList[1]
return (x+y)**2
def f3(argList):
x = argList[0]
return x/3
fs = [f1,f2,f3]
args = [3,5]
for f in fs:
print f(args)
For Fsolve, you could try something like this (untested):
def func1(argList, constList):
x = argList[0]
y = argList[1]
alpha = constList[0]
return alpha*x + (1-alpha)*x*y - y
def func2(argList, constList):
x = argList[0]
y = argList[1]
z = argList[2]
beta = constList[1]
return beta*x + (1- beta)*x*z - z
def func3(argList, constList):
x = argList[0]
w = argList[1] ## or, if you want to pass the exact same list to each function, make w argList[4]
gamma = constList[2]
return gama*x + (1 -gama)*x*w - w
def func4(argList, constList):
return A*x + B*y + C*z + D*w -E ## note that I moved E to the left hand side
functions = []
functions.append((func1, argList1, constList1, args01))
# args here can be tailored to fit your function structure
# Just make sure to align it with the way you call your function:
# args = [argList, constLit]
# args0 can be null.
functions.append((func1, argList2, constList2, args02))
functions.append((func1, argList3, constList3, args03))
functions.append((func1, argList4, constList4, args04))
for func,argList, constList, args0 in functions: ## argList is the (Vector) variable you're solving for.
Fsolve(func = func, x0 = ..., args = constList, ...)

Related

Python `for` loop doesn't update `numpy` array values

I'm trying to make a simple numerical gradient function and part of it is a for loop updating parameter values that would later be evaluated. The code is as follows:
import numpy as np
def target_gradient(theta):
e = 10
for i in range(theta.shape[0]):
theta_upper = theta
theta_lower = theta
theta_upper[i] = theta[i] + e
theta_lower[i] = theta[i] - e
print(f"theta_upper {theta_upper}")
print(f"theta_lower {theta_lower}")
return theta_upper, theta_lower
u, l = target_gradient(np.array([1, 1, 1, 1, 1]))
However, instead of the anticipated output, I get [1 1 1 1 1] for both arrays. Print statements are there for monitoring and they show that throughout the loop the arrays didn't change (i.e. were [1 1 1 1 1]).e=10 is so that the effect is more pronounced. I also tried the enumerate() approach, but get the same result.
The full gradient funtion would look something like this
def target_gradient(theta, x, y):
e = 0.01
gradient = np.zeros(theta.shape[0])
for i in range(theta.shape[0]):
theta_upper = theta
theta_lower = theta
theta_upper[i] = theta[i] + e
theta_lower[i] = theta[i] - e
gradient[i] = (
foo(theta=theta_upper, x=x, y=y) - foo(theta=theta_lower, x=x, y=y)
) / (2 * e)
return gradient
Therefore, I am intentionally declaring theta_upper = theta inside the loop because I want to calculate the gradient for which I need partial (numerical) derivatives.
The best approach depends on what foo is:
If foo can take vector arguments and return vector values, e.g.
def foo(theta, x, y):
return x * y * np.sin(theta)
Then you can simply do:
def target_gradient(theta, x, y, e=0.01):
foo_upper = foo(theta + e, x, y) # Add e to the entire theta vector, and call foo
foo_lower = foo(theta - e, x, y) # Subtract e from the entire theta vector, and call foo
return (foo_upper - foo_lower) / (2 * e)
Based on your code, where you pass a vector theta_upper to foo, I suspect this is the case.
If foo can't take vector arguments and return vector values, e.g.
def foo(theta, x, y):
return x * y * math.sin(theta)
then you need to iterate over theta, and call foo for each value.
def target_gradient(theta, x, y, e=0.01):
gradient = np.zeros(theta.shape[0])
for i in range(theta.shape[0]):
foo_upper = foo(theta[i] + e, x[i], y[i]) # Take a single value of theta, and add e
foo_lower = foo(theta[i] - e, x[i], y[i]) # Take a single value of theta, and subtract e
gradient[i] = (foo_upper - foo_lower) / (2 * e)
return gradient
The reason why theta_upper and theta_lower are not changing inside the loop is because you are creating copies of theta and assigning them to theta_upper and theta_lower. Therefore, when you modify theta_upper[i] or theta_lower[i], you are not modifying the original theta array.
To fix this, you can use the copy() method to create a copy of theta that you can modify inside the loop, like this:
def target_gradient(theta):
e = 10
for i in range(theta.shape[0]):
theta_upper = theta.copy()
theta_lower = theta.copy()
theta_upper[i] = theta[i] + e
theta_lower[i] = theta[i] - e
print(f"theta_upper {theta_upper}")
print(f"theta_lower {theta_lower}")
return theta_upper, theta_lower

How to calculate coefficients, solutions etc. in quadratic function with limited inputs in Python

I have a problem. I want to calculate everything in the quadratic function. equations for reference:
ax^2 + bx + c
a(x-p)^2 + q
I made 8 possible inputs in tkinter and I want my program to try and calculate everything if possible. Otherwise to return sth like not enough data.
Equations:
delta = b^2-4ac
p = -b/(2a)
p = (x1+x2)/2
q = -delta/(4a)
#if delta>0
x2 = (-b-sqrt(delta))/(2a)
x1 = (-b+sqrt(delta))/(2a)
#if delta=0
x0 = -b/(2a)
#if delta<0 no solutions
#a, b, c are the coefficients.
b = -2ap
c = p^2*a+q
Example:
Input:
p = 3
q = -9
x1 = 0.877868
a = 2
Output:
b = -12
c = 9
x2 = 5.12132
delta = 72
So, for example, I give it [x1, x2, a] and it will calculate [q, p, b, c, and delta] if possible.
Is there a function that I can give all different formulas to and it will try to calculate everything?
For now, my only idea is to brute force it in 'try' or with 'ifs', but I feel like it would take thousands of lines of code, so I won't do that.
I found out that you can use sympy's solve. This is my solution. I used Eq for defining equations and then solved them.
def missingVariables(dictOfVariables):
symbols = dict(a=sym('a'), b=sym('b'), c=sym('c'), p=sym('p'), q=sym('q'), x1=sym('x1'), x2=sym('x2'),
delta=sym('delta'))
var = mergeDicts(symbols, dictOfVariables)
deltaEQ = Eq((var['b'] ** 2 - 4 * var['a'] * var['c']), var['delta'])
x1EQ = Eq(((-var['b'] - sqrt(var['delta'])) / (2 * var['a'])), var['x1'])
x2EQ = Eq(((-var['b'] + sqrt(var['delta'])) / (2 * var['a'])), var['x2'])
pEQ = Eq((-var['b']) / (2 * var['a']), var['p'])
pEQ2 = Eq(((var['x1'] + var['x2']) / 2), var['p'])
qEQ = Eq(((-var['delta']) / (4 * var['a'])), var['q'])
bEQ = Eq((-2 * var['a'] * var['p']), var['b'])
cEQ = Eq((var['a'] * var['p'] ** 2 + var['q']), var['c'])
solution = solve((deltaEQ, x1EQ, x2EQ, pEQ, pEQ2, qEQ, bEQ, cEQ))
solution = solution[0]
new_dict = {}
for k, v in solution.items():
try:
new_dict[str(k)] = round(float(v), 4)
except TypeError:
new_dict[str(k)] = v
return new_dict

How to apply a function several times?

For given function (with one parameter, parameter type and return value type are equal) generate another function that applies original fuction multiple times.
Example:
from math import sin
f1 = fn(lambda x: "sin(%s)" % x, 5)
f2 = fn(lambda x: sin(x), 5)
print("%s = %f" % (f1("1"), f2(1)))
>>sin(sin(sin(sin(sin(1))))) = 0.587181
print("%s = %f" % (f1("2"), f2(2)))
>>sin(sin(sin(sin(sin(2))))) = 0.606464
print(fn(lambda x: sin(x), 0)(1000))
>> 1000
Function signature: def fn (f, n)
Using 'fn' (see first part of the task) generate function that calculates golden ratio approximation.
final formula:
1 + 1 / (1 + 1 / (1 + 1 / (...)))
some results:
golden_ratio(0) = 1
golden_ratio(1) = 2
golden_ratio(2) = 1.5
golden_ratio(100) = 1.6180...
Function signature: def golden_ratio (n), where 'n' is number of invocations (and number of terms in continued fraction)
That's what I did, the golden ratio calculates everything well. But with the first part of the task, something is wrong, as in the example ...
first function fn which will apply a similar function a given number of times
second function - golden ratio which should use fn to calculate
code:
n = 100
def golden_ratio(f,n):
def wrap(*arg):
for _ in range(n+1):
arg = f(*arg)
return arg
return wrap
def fib(q, w):
q, w = w, q + w
return q, w
f3 = golden_ratio(fib, n)
q, w = f3(0, 1)
# gold section
print(w / q)
it is executed, but not quite correctly, as in the example
Try to use this:
def fn(f, n):
r = 1/2
for i in range(n):
r = f(r)
return r
f = lambda x: 1+(1/x)
fn(f, 20) # answer: 1.6180339789779863
If you want a general function which can apply any type of function.
from math import sin
def fn(f, n):
def wrap(*arg):
for _ in range(n):
if isinstance(arg, tuple):
arg = f(*arg)
else:
arg = f(arg)
return arg
return wrap
f1 = fn(sin, 5)
f1(1)
def fib(q, w):
q, w = w, q + w
return q, w
f2 = fn(fib, 100)
q, w = f2(0, 1)
print(w/q)
Golden ratio
def golden_ratio(n):
f = fn(fib, n+1)
q, w = f(0, 1)
return w/q
print(golden_ratio(0))
print(golden_ratio(1))
print(golden_ratio(2))
print(golden_ratio(100))
Output
1.0
2.0
1.5
1.618033988749895
Use Lambda
def golden_ratio(n):
f = fn(lambda x, y: (y, x+y), n+1)
q, w = f(0, 1)
return w/q
Or
def golden_ratio(n):
f = fn(lambda x: 1 + (1/x) if x != 0 else 1, n+1)
return f(0)

How to put a derivation as a boundary in "scipy.optimize.curve_fit"

I'm trying to get the coefficients of a function p(T,x). I provided the data for p, T and x from excel sheets via panda. The following Code works quite nice for me:
import pandas as pd
import os
from scipy.optimize import curve_fit
import numpy as np
df = pd.read_excel(os.path.join(os.path.dirname(__file__), "./Data.xlsx"))
T = np.array(df['T'], dtype=float)
x = np.array(df['x'], dtype=float)
p = np.array(df['p'], dtype=float)
p_s = 67.17
def func(X, a, b, c, d, e, f):
T, x = X
return x * p_s + x * (1 - x) * (a + b * T + c * T ** 2 + d * x + e * x * T + f * x * T ** 2) * p_s
popt, pcov = curve_fit(func, (T, x), p)
print("a = %s , b = %s, c = %s, d = %s, e = %s, f = %s" % (popt[0], popt[1], popt[2], popt[3], popt[4], popt[5]))
My acutal problem is, that the function is swinging a little bit in the end.
Because of this behaviour i get two x values for one p value, which i dont want.
So to avoid this little swing i want to accomplish a boundary condition for the fitting that say something like dp/dx (for constant T) > 0. With dp/dx I mean the derivation of the function after x.
Is this possible with the normal bound paramter of curve_fit? How can I do this?
EDIT:
As suggested I've messed a little bit around with least_square function but I guess that I came to a point where I don't realy understand what I'm doing or have to do.
T = np.array(df['T'], dtype=float)
x = np.array(df['x'], dtype=float)
p = np.array(df['p'], dtype=float)
p_s = 67
def f(X, z):
T, x = X
return x * p_s + x * (1 - x) * (z[0] + z[1] * T + z[2] * T ** 2 + z[3] * x + z[4] * x * T + z[5] * x * T ** 2) * p_s
def g(X,p,z):
return p - f(X ,z)
z0 = np.array([0,0,0,0,0,0], dtype=float)
res, flag = least_squares(g,z0, args=(T,x,p))
print(res)
With this code I get the following error:
TypeError: g() takes 3 positional arguments but 4 were given

Need basic understanding of python arguments in order to be able code system of ODE's solution

I am an old Fortran programmer and need to help a young person to numerically solve an ODE system using Heun's method. He only knows Python so I have to learn the minimum python to get this done.
Here is what I came up with. The test code is for a simple 2 degree of freedom system with exponential growth for each degree of freedom.
The error I get is:
Traceback (most recent call last):
File "program.py", line 28, in <module>
heun (imax, y, dt, t)
File "program.py", line 7, in heun
rhs(y, ydot)
NameError: global name 'ydot' is not defined
Here is the code:
# Test RHS of ODE system:
def rhs (y, ydot):
ydot[0] = y[0]
ydot[1] = y[1]
return;
# Does one step of Heun's method:
def heun (imax, y, dt, t):
rhs(y, ydot)
for i in range(0, imax):
y_tilde[i] = y[i] + dt * ydot[i]
rhs(y_tilde, ydot_at_tilde)
for i in range(0, imax):
y[i] = y[i] + dt/2 * (ydot[i] + ydot_at_tilde[i])
t = t + dt
return;
# Initial condition
y = [0, 0]
t = 0
dt = 0.01
nsteps = 100
imax = 1
istep = 1
while istep <= nsteps:
heun (imax, y, dt, t)
print istep, y[0], y[1]
istep = istep + 1
Question: Why does python think that the object ydot in routine heun is global? Even if it were global, why can't I pass it as an argument?
The problem is here:
def heun(imax, y, dt, t):
rhs(y, ydot)
You're calling your rhs function, with the input arguments y and ydot. But ydot doesn't exist inside the scope of your heun function. Only imax, y, dt and t do.
Similarly you never define the variables ydot_tilde or ydot_at_tilde
Also, you're going to need your functions to return some values.
OK thank-you all. Here is a code that works with comments to indicate what I learnt:
def rhs (y, ydot):
ydot[0] = y[0]
ydot[1] = y[1]
return ydot;
def heun (ndof, dt, y, t):
# These initializations of local arrays take the place of Fortran declarations:
ydot = [0] * (ndof)
y_tilde = [0] * (ndof)
ydot_at_tilde = [0] * (ndof)
ydot = rhs(y, ydot)
# Note: In python range means:
# range (first element, upto but not including last element)
for i in range(0, ndof):
y_tilde[i] = y[i] + dt * ydot[i]
ydot_at_tilde = rhs(y_tilde, ydot_at_tilde)
for i in range(0, ndof):
y[i] = y[i] + dt/2 * (ydot[i] + ydot_at_tilde[i])
t = t + dt
# Note: This lists the output arguments:
return y, t;
# Initial condition
y = [1, 1]
t = 0
dt = 0.01
nsteps = 100
ndof = 2
istep = 1
while istep <= nsteps:
# Note: This is how you get the output arguments:
y, t = heun (ndof, dt, y, t)
istep = istep + 1
print t, y[0], y[1]
This is what I would do:
import numpy
def heun(ndof, dt, y, t):
ydot = numpy.zeros(ndof)
y_tilde = numpy.zeros(ndof)
ydot_at_tilde = numpy.zeros(ndof)
# Replacing first two elements does not need a function `rhs()`
ydot[:1] = y[:1]
# Vectorized operation, numpy does this loop for you at C speeds
y_tilde = y + dt * ydot
ydot_at_tilde[:1] = y_tilde[:1]
y = y + dt/2 * (ydot + ydot_at_tilde)
t = t + dt
return y, t
y = numpy.ones(2)
t = 0
dt = 0.01
nsteps = 100
ndof = 2
for istep in range(nsteps):
y, t = heun(ndof, dt, y, t)
print(t, y[0], y[1])
return; From this it is clear that you're modifying the ydot but not returning anything.
And in rhs(y, ydot) you're passing in ydot without first specifying what it should be. Hence declare a function variable ydot first with ydot = [] and hold the result in ydot as ydot = rhs(y,ydot) in heun and use
ydot.append(y[0])
ydot.append(y[1])
in 'rhs'.
return ydot in rhs and return y in heun.
Fix all the not defined errors in a similar manner.

Categories

Resources