Minimizing a Composite function - python-3.x

I'm kinda new to python, and I'm trying to minimize the function "fun" that is a composite of "Velo" (also a composite of "area"), but it is giving me de error: TypeError: fun() missing 3 required positional arguments: 'q', 'ro', and 'mi'
from math import pi
import scipy.optimize
def area (r):
return pi*r**2
def velo(a,q):
a = area(r)
return q/a
def fun (r, q, ro, mi):
v = velo (q,a)
fun = (ro*v*r)/mi
return fun
x0 = [2,3,5,6]
res = scipy.optimize.minimize(fun, x0)
print(res)
Can someone help out here?

Many things to say here, I will try to answer properly.
Environment
Which IDE are you using? I advise you VSCode for example, as it underlines some mistakes it can detect, great for typos aswell.
Local variables
In python, variables are defined locally in functions. It means that the variable r in a = area(r) is not defined in your velo function.
The same mistake occurs in v = velo(q,a), as a is not defined in the fun function. I advise you to check these mistakes first.
Answer to your specific question
The error is raised because the array is considered one argument. I will transform your code (with is not working because of the previous points) but solves your error
def fun(array):
r, q, ro, mi = array
v = velo (q,a)
res = (ro*v*r)/mi
return res
x0 = np.array([2,3,4,5])
res = scipy.optimize.minimize(fun, x0)

Related

Is there a way to pass an equation symbolically into a function for sympy?

From this
import sympy as sp
x,y,z = sp.symbols("x y z")
sp.Ep(x,y/z)
To this
#varibles = array
#equation = ????
def solver(variables,equation):
#Looping through variables array and converting variables to sympy objects
for var in variables:
var = sp.symbols(var)
#Generate sympy Equation
equation = sp.Ep(equation)
variables = [x,y,z]
equation = x,y/z #invalid code
solver(variables,equation)
I'm creating a function that is able to take in an equation with x amount of variables and x-1 number of values then solve for the missing variable symbolically then return a numerical answer using the values provided.
I only included a small portion of code where I'm having trouble understanding how to pass through an equation. Any solutions or pointers would be greatly appericated. Thanks.
There are several layers of potential confusion here concerning Python variables and SymPy objects (Symbols) used for variables.
Here is an example of what you are saying:
# 3 variables
syms = x, y, z = var('x:z')
# 2 values
vals = {x:1, y:2}
# an equations
eq = Eq(x, y/z)
# solve for the missing value symbolically
missing = set(syms) - set(vals) # == {z}
solve(eq, missing)
[y/x]
# solve for the missing value after substituting in the known values
solve(eq.subs(vals))
[2]
You could make a solver to accept an equation and then specified values and figure out the missing one and return that value by doing something like this:
>>> def solver(eq, **vals):
... from sympy.core.containers import Dict
... from sympy.solvers.solvers import solve
... free = eq.free_symbols
... vals = Dict(vals)
... x = free - set(vals)
... if len(x) != 1:
... raise ValueError('specify all but one of the values for %s' % free)
... x = x.pop()
... return solve(eq.subs(vals), x, dict=True)
...
>>> solver(eq, x=1, z=2)
[{y: 2}]
Does that give you some ideas of how to continue?

How to evaluate sympy symbolic function with multiple inputs

I am trying to use sympy to solve a function symbolically, then input the values in and solve it numerically. I can do this with just one variable, but can't figure out how to do it with multiple. Here is what I have so far.
v,v0,a,t = sp.var('v v0 a t')
args = [v0,a,t]
arg_vals = [1,-9.81,2]
def get_function():
v = v0 + a*t
return v
def get_derivative(fun,var):
derivative = sp.diff(fun,var)
return derivative
def get_integral(fun,var):
integral = sp.integrate(fun,var)
return integral
def eval_function(fun, args, arg_vals):
i=0
for i in range(len(arg_vals)):
args[i] = arg_vals[i]
return fun.evalf(subs={args})
v = get_function()
a = get_derivative(v,t)
x = get_integral(v,t)
x_eval = eval_function(v,args,arg_vals)
The code runs fine until it hits the eval_function return fun.evalf(subs={args}). Then I get
>>>TypeError: unhashable type: 'list'
I've been trying to figure this out for a while, and guess that someone can just take a few seconds and tell me I'm dumb lol. Thanks for the help!!
(using anaconda, spyder, python 3)

Accommodating empy function parameter without default value

I'm writing a simple function in python 3.6 to find the distance between two points in n-dimensional space. I pass the coordinates as one-dimensional lists to my function. Here is what I have written:
def Distance(a: list[float], b: list[float]):
dimension = len(a)
sum = 0
for i in range(dimension):
sum += (b[i]-a[i])**2
return math.sqrt(sum)
This should work just fine if both a and b are passed explicitly. But I want to add additional functionality: if only a is passed, its distance from the origin will be calculated.
In C++, the language I am most familiar with, I would do this using function overloading. In that case, the two functions would simply have a different number of parameters. But this isn't an option for me in Python.
Any help with this would be greatly appreciated. Thank you in advance.
Edit:
The updated definition
def Distance(a: List[float], b=None):
if b:
arg = sum((x1 - x2)**2 for x1, x2 in zip(a,b))
else:
arg = sum(x**2 for x in a)
return math.sqrt(arg)
works for both cases.
An idiomatic definition could be
def distance(a, b=None):
from math import sqrt
d2=sum((x1-x2)**2 for x1,x2 in zip(a,b)) if b else sum(x**2 for x in a))
return sqrt(d2)
I confide that it's possible to use type hints as well, but I'm not an expert.
As a final remark, you used sum as a name, while it's recommended that you do not mask builtin names (I have used the sum builtin in my function definition)
The prototype must indeed be with None, so you have to change the function types because None is obviously not a list:
def Distance(a: list[float], b=None):
Then:
if not b:
b = [0.] * dimension

Problem with calling a variable from one function into another

I am trying to call a variable from one function into another by using the command return, without success. This is the example code I have:
def G():
x = 2
y = 3
g = x*y
return g
def H():
r = 2*G(g)
print(r)
return r
H()
When I run the code i receive the following error NameError: name 'g' is not defined
Thanks in advance!
Your function def G(): returns a variable. Therefore, when you call it, you assign a new variable for the returned variable.
Therefore you could use the following code:
def H():
G = G()
r = 2*G
print (r)
You don't need to give this statement:
return r
While you've accepted the answer above, I'd like to take the time to help you learn and clean up your code.
NameError: name 'g' is not defined
You're getting this error because g is a local variable of the function G()
Clean Version:
def multiple_two_numbers():
"""
Multiplies two numbers
Args:
none
Returns:
product : the result of multiplying two numbers
"""
x = 2
y = 3
product = x*y
return product
def main():
result = multiple_two_numbers()
answer = 2 * result
print(answer)
if __name__ == "__main__":
# execute only if run as a script
main()
Problems with your code:
Have clear variable and method names. g and G can be quiet confusing to the reader.
Your not using the if __name__ == "__main__":
Your return in H() unnecessary as well as the H() function.
Use docstrings to help make your code more readable.
Questions from the comments:
I have one question what if I had two or more variables in the first
function but I only want to call one of them
Your function can have as many variables as you want. If you want to return more than one variable you can use a dictionary(key,value) List, or Tuple. It all depends on your requirements.
Is it necessary to give different names, a and b, to the new
variables or can I use the same x and g?
Absolutely! Declaring another variable called x or y will cause the previous declaration to be overwritten. This could make it hard to debug and you and readers of your code will be frustrated.

Lambdify A Parametric Integral

I have the following issue: I want to lambdify a sympy expression containing parametric integrals like Integral(tanh(a*x),(x,0,1)). I tried to do a manual implementation like here.
What we want is essentially that the integral gets converted to something like:
lambda theta: quad(lambda x: g(x,theta), a,b)[0]
where
g = sp.lambdify((x,param), f, modules='numpy'))
Consider the following MWE:
import sympy as sp
import numpy as np
from scipy.integrate import quad
def integral_as_quad(function, limits):
x, a, b = limits
param = function.free_symbols - {x}
f = sp.lambdify( (x,*param), function, modules='numpy')
return lambda y: quad(lambda x: f(x,y), a,b)[0]
a, x = sp.symbols('a,x')
I = sp.Integral(sp.tanh(a*x),(x,0,1))
K = integral_as_quad(sp.tanh(a*x),(x,0,1))
L = sp.lambdify(a, I, modules=['numpy', {'Integral':integral_as_quad}] )
Then calling for example K(1) returns the correct value. However L(1) gives
AttributeError: 'Mul' object has no attribute 'tanh'
Does anyone have an idea how to fix this?
NOTE: Doing it manually is no option, since the expressions I deal with are way more complicated and may contain several different integrals. So I really need to get the lambdify working.
I think returning a lambda from integral_as_quad cannot work, because this lambda will never be called, as the Integral object in SymPy is not callable. Instead, the parameter tuple can be passed to quad via its args argument. Another change I made is in the outer lambdification, replacing
modules=['numpy', {'Integral':integral_as_quad}]
with
modules=[{'Integral': integral_as_quad}, 'sympy']
The idea is that at this stage we don't need NumPy functions yet, we just want to replace the Integral by our callable. The order of modules list matters: the dictionary comes first to prevent SymPy from keeping Integral as an Integral.
Now L(1) returns the correct amount.
import sympy as sp
import numpy as np
from scipy.integrate import quad
def integral_as_quad(function, limits):
x, a, b = limits
param = tuple(function.free_symbols - {x})
f = sp.lambdify((x, *param), function, modules=['numpy'])
return quad(f, a, b, args=param)[0]
a, x = sp.symbols('a,x')
I = sp.Integral(sp.tanh(a*x), (x,0,1))
L = sp.lambdify(a, I, modules=[{'Integral': integral_as_quad}, 'sympy'])
So one possible workaround I have found, but I am unhappy with because it is too slow for my application, is the following:
def to_lambda(expr, param):
# Preprocessing
expr = expr.evalf()
f = sp.lambdify([param], expr, modules='sympy')
fun = lambda x: np.array(np.array(f(x).evalf()), dtype='float64')
return fun
So first, expr gets cast into a lambda function using sympy-functions, e.g. we have
f = lambda a: Integral(tanh(a*x),(x,0,1))
and then we use sympy's internal integrator via evalf() (slow!).
Also, don't ask me why theres the double np.array, if one puts the dtype='float64' into the first one, then it returns TypeError: __array__() takes 1 positional argument but 2 were given

Resources