How to find roots for a numpy array - python

I am wondering how to find foots for an array. What I have now is:
import numpy as np
from scipy.optimize import brentq as find_root
t = np.linspace(0, 100)
def f(x):
return x ** 2 - t
a = find_root(f, -400, 400)
print(a)
It gives me a type array saying that:
TypeError: only size-1 arrays can be converted to Python scalars.
I know the reason is that find_root can only take a scalar in its argument. What I want is to make “a” a bumpy array that finds root for the function given each possible value of t. Does that mean I need to write a loop for find_root? Or do I need to write a loop before I define the function? What’s the easiest way to do it?
Thank you very much for helping.

Yes, in this case it might be easiest to just loop over the arguments.
import numpy as np
from scipy.optimize import brentq as find_root
def f(x, t):
return x ** 2 - t
a = [find_root(f, 0, 400,args=(i,)) for i in np.linspace(1,10,10)]
print(a)
Note that I introduced an argument t to your function f to which you can pass the value using the args parameter of find_root.

Related

How to format the argument of scipy.optimize.fsolve for arrays of data

I'd like to use a solver (scipy.optimize.fsolve) to solve for the root of a function, fun(x,y). In this case, I want the argument (y) to be an array (e.g. an array of data). I would also like to avoid using for-loops to call fsolve for each value in y.
In specifying arg (y) as an array, I am getting an error that the result from the function call is not a proper array of floats. Other errors occur if I make "data" a tuple instead of an array.
Here is an MWE of the problem:
import numpy as np
from scipy.optimize import fsolve
def fun(x, y):
return x+y
data = np.array([1, 2, 3, 4])
x = fsolve(fun, x0=0, args=data)
print(x)
How can the input to fsolve be corrected so that it solves for the root of fun(x,y) for each value of y in the array (without using a for-loop)?
The function, that is passed to fsolve, takes at least one (possibly vector) argument and returns a value of the same length as mentioned here.
In your case, you are passing x0=0 and args=np.array([1,2,3,4]) to fsolve. The return value of fun has a different length to x0 (x0 is a scalar and args is an array having shape (4,)).
The following code solves your problem:
import numpy as np
from scipy.optimize import fsolve
def fun(x, y):
return x+y
data = np.array([1, 2, 3, 4])
x = fsolve(fun, x0=np.array([0,0,0,0]), args=data)
print(x)

Python - Scipy Multivariate normal generalized to 1 dimension

When running y = multivariate_normal(np.zeros(d), np.eye(d)).rvs() we obtain a sample of dimension (d, ). However, when d=1 we obtain a scalar, which makes sense since it's 1 dimensional. Unfortunately, I have some piece of code that must work for any number of dimensions, including d=1, and basically takes the dot product of a d dimensional vector x with y. This breaks for d=1. How can I fix it?
import numpy as np
from scipy.stats import multivariate_normal as MVN
def mwe_function(d, x):
"""Minimal Working Example"""
y = MVN(np.zeros(d), np.eye(d)).rvs()
return x # y
mwe_function(2, np.ones(2)) # This works
mwe_function(1, np.ones(1)) # This doesn't
IMPORTANT: I want to avoid if statements. One could simply use scipy.stats.norm in that case, but I want to avoid if statements as they would slow down the code.
You can use np.reshape to fix the shape of your sample. By using -1 to specify the length of the first dimension, you will always get a 1-dimensional array and no scalar.
import numpy as np
from scipy.stats import multivariate_normal as MVN
def mwe_function(d, x):
"""Minimal Working Example"""
y = MVN(np.zeros(d), np.eye(d)).rvs().reshape([-1])
return x # y
v0 = mwe_function(2, np.ones(2)) # This works
print(v0) # -0.5718013906409207
v1 = mwe_function(1, np.ones(1)) # This works as well :-)
print(v1) # -0.20196038784485093
where .reshape([-1]) does the job.
Personally, I prefer reshaping over using np.atleast_1d, since the effect is directly visible - but in the end it is a matter of taste.

Error for a given function: only size-1 arrays can be converted to Python scalars

i am a newbie and currently trying to plot this function so I can choose a range of x values for which I should perform my experiments in the ChemistryLab.
I found different articles on plotting functions and it all worked with generic functions like sin(x).
But once I input my function it does not work. The problem already occurs after the first two lines:
import numpy as np
import math
X = np.linspace(0, 512, 256)
f = ((x+22)- math.sqrt((x+22)**2-4*2*x))
--> TypeError: only size-1 arrays can be converted to Python scalars
I found threads saying X should be vectorize, however I could not come up with a solution.
Thanks for help in advance!
Create a vectorized function. For example:
equation_func = np.vectorize(math.sqrt)
new_f = equation_func(X)
Swap math.sqrt with a function containing your more sophisticated equation and you're home.
A good way to implement a mathematical function in Python is, perhaps unsurprisingly, as a Python function. Then you can, for example, use that function in a list comprehension to get an array of values:
import numpy as np
import math
import matplotlib.pyplot as plt
X = np.linspace(0, 512, 256)
def f(x):
return x + 22 - math.sqrt((x + 22)**2 - 8 * x)
Y = np.array([f(x) for x in X])
plt.plot(X, Y);

How do I use sympy.lambdify with Max function to substitute numpy.maximum instead of numpy.amax?

I'm trying to lambdify big analytic expression with sp.Max(x, 0) inside. I want to use numpy to vectorize my calculations, so x is going to be an array. I need element-wise maximum values of x and 0. Still, sympy changes sp.Max to np.amax by default. It finds maximum along the axis, that's not what I need. "modules" keyword in lambdify doesn't work as I expect.
I've tried:
import numpy as np
import sympy as sp
arr = np.array([1, 2, 3])
expr = sp.sin(x) + sp.Max(x, 0)
f = sp.lambdify(x, expr, modules=[{'Max': np.maximum}, 'numpy']) # docs say, priority of modules matters
help(f)
It gives:
Help on function _lambdifygenerated:
_lambdifygenerated(x)
Created with lambdify. Signature:
func(x)
Expression:
sin(x) + Max(0, x)
Source code:
def _lambdifygenerated(x):
return (sin(x) + amax((0,x)))
Imported modules:
sp.Max changed to amax for some reason.
If 'numpy' is not included into 'modules' list, it simply skips all other functions. I've also tried to swap dict and 'numpy' in list, but it haven't helped. Please, clarify, what's wrong? Is it a bug in sympy?
When using lambdify to create numpy functions intended to work vectorized, there often are subtle problems, especially when variables (x) and constants (0) are mixed.
In this case, sp.max supposes all of its possible many parameters being single values. np.amax gets the maximum of one flattened array. np.maximum gets the element-wise maximum of two arrays. The problem here is that the constant 0 doesn't automatically get expanded to a numpy array.
My workaround would be to replace sp.max with a custom function based on sp.Piecewise. Note that you would need a separate function if there would be more than 2 arguments to sp.max.
import numpy as np
import sympy as sp
from sympy.abc import x
def sympy_max2(a, b):
return sp.Piecewise((b, a < b), (a, True))
arr = np.array([11, 22, 33, -1, -2])
expr = sp.sin(x) + sympy_max2(0, x)
f = sp.lambdify(x, expr, modules=['numpy'])
print(f(arr)) # [10.00000979 21.99114869 33.99991186 -0.84147098 -0.90929743]
In the current version of SymPy I get return (sin(x) + amax((0,x), axis=0)) in the signature. Is this what you want?
To use the np.maximum function instead of np.amax, I found that specifying the amax method instead of the Max works. The np.maximum function also requires some tweaking, to receive the arguments that are used for the amax function.
import numpy as np
import sympy as sp
arr = np.array([11, 22, 33, -1, -2])
expr = sp.sin(x) + sp.Max(x, 0)
def custom_amax(x,**kwargs):
return np.maximum(x[0],x[1])
f = sp.lambdify(x, expr, modules=[{'amax': custom_amax}, 'numpy'])
f(arr) # [10.00000979, 21.99114869, 33.99991186, -0.84147098, -0.90929743]

How to fix the error: The array return by func must be one-dimensional, but got ndim=2

Here is my code:
import numpy as np
from scipy.integrate import odeint
import math
y0=np.array([1,3,2,3,5])
b=np.array([[1],[3],[4],[2],[5]])
'''generate matrix'''
B=np.dot(b,b.T)
def g(t,y,B):
return np.exp(np.dot(y,B))
t=np.linspace(0,1,100)
y= odeint(g, y0, t, args=(B,))
The error is
"The array return by func must be one-dimensional, but got ndim=2."
Try changing the order of the arguments of your function to def g(y,t,B). That made the error message go away. You'll want to verify that the math is doing what you expect.
The documentation of odeint says, "The first two arguments of f(t, y, ...) are in the opposite order..."
The output array from the function depends on what is the input.
To ensure that your initial condition is 1-dimensional, you can reshape the y0 array.
y0_reshaped = y0.reshape(n,)
where n is the number of elements in your y0 vector

Categories