I keep getting errors when I tried to solve a system of three equations using the following code in python3:
import sympy
from sympy import Symbol, solve, nsolve
x = Symbol('x')
y = Symbol('y')
z = Symbol('z')
eq1 = x - y + 3
eq2 = x + y
eq3 = z - y
print(nsolve( (eq1, eq2, eq3), (x,y,z), (-50,50)))
Here is the error message:
Traceback (most recent call last):
File
"/usr/lib/python3/dist-packages/mpmath/calculus/optimization.py", line
928, in findroot
fx = f(*x0)
TypeError: () missing 1 required positional argument:
'_Dummy_15'
During handling of the above exception, another exception occurred:
Traceback (most recent call last): File "", line 1, in
File "", line 12, in File
"/usr/lib/python3/dist-packages/sympy/solvers/solvers.py", line 2498,
in nsolve
x = findroot(f, x0, J=J, **kwargs)
File
"/usr/lib/python3/dist-packages/mpmath/calculus/optimization.py", line
931, in findroot
fx = f(x0[0])
TypeError: () missing 2 required positional arguments:
'_Dummy_14' and '_Dummy_15'
The strange thing is, the error message goes away if I only solve the first two equation --- by changing the last line of the code to
print(nsolve( (eq1, eq2), (x,y), (-50,50)))
output:
exec(open('bug444.py').read())
[-1.5]
[ 1.5]
I'm baffled; your help is most appreciated!
A few pieces of additional info:
I'm using python3.4.0 + sympy 0.7.6-3 on ubuntu 14.04. I got the same error in python2
I could solve this system using
solve( [eq1,eq2,eq3], [x,y,z] )
but this system is just a toy example; in the actual applications the system is non-linear and I need higher precision, and I don't see how to adjust the precision for solve, whereas for nsolve I could use nsolve(... , prec=100)
THANKS!
In your print statement, you are missing your guess for z
print(nsolve((eq1, eq2, eq3), (x, y, z), (-50, 50)))
try this (in most cases, using 1 for all the guesses is fine):
print(nsolve((eq1, eq2, eq3), (x, y, z), (1, 1, 1)))
Output:
[-1.5]
[ 1.5]
[ 1.5]
You can discard the initial guesses/dummies if you use linsolve:
>>> from sympy import linsolve
>>> print(linsolve((eq1, eq2, eq3), x,y,z))
{(-3/2, 3/2, 3/2)}
And then you can use nonlinsolve for your non linear problem set.
The Problem is number of variables should be equal to the number of guess vectors,
print(nsolve((eq1, eq2, eq3), (x,y,z), (-50,50,50)))
If you're using a numerical solver on a multidimensional problem, it wants to start from somewhere and follow a gradient to the solution.
the guess vector is where you start.
if there are multiple local minima / maxima in the space, different guess vectors can lead to diffierent outputs.
Or an unfortunate guess vector may not converge at all.
For a one-dimensional problem the guess vector is just x0.
For most functions you can write down easily, almost any vector will converge to the one global solutions.
so (1,1,1) guess vectors here is as good as (-50,50,50)
Just don't leave a null space for the sake of program
your code should be:
nsolve([eq1, eq2, eq3], [x,y,z], [1,1,1])
your code was:
nsolve([eq1, eq2, eq3], [x,y,z], [1,1])
you were mising one guess value in the last argument.
point is: if you are solving for n unknown terms you provide a guess for each unknown term (n guesses in the last argument)
Related
The steady-state solution to a diffusion equation in cylindrical geometry using FiPy is rather different from the solution obtained from another software, eg., Mathematica.
The equation is:
$0 = \frac{1}{r}\frac{d}{dr}\left(\frac{r}{T^{1/2}}\frac{dT}{dr}\right) + cte*T^{3/2}$
which means that, by using a CylindricalGrid1D mesh, we can write the equation as:
mesh = CylindricalGrid1D(nr=100, dr=0.01, origin=0.0)
T = CellVariable(name='temperature', mesh=mesh, hasOld=True)
r = mesh.cellCenters()
#BC's
T.constrain(0., mesh.facesRight)
T.faceGrad.constrain(0.,mesh.facesLeft)
#initial temperature profile
T.setValue( 1-r**2)
eq = 0 == DiffusionTerm( coeff=T**(-1/2), var=T) + 20*ImplicitSourceTerm(coeff=T**(1/2), var=T)
viewer = Viewer(vars=T)
eq.solve()
viewer.plot()
raw_input(" Press <enter> to proceed...")
In here I have set cte=20, but the problem remains whatever this value is. I get the solution on the left whereas the solution given by Mathematica is the one on the right:
plots
I then tried to sweep as recommended for such non-linear equation like this. So instead of eq.solve(), I did:
current_residual = 1.0e100
desired_residual = 1e-5
while current_residual > desired_residual:
current_residual = eq.sweep()
T.updateOld()
But I get the error:
/home/antonio/.local/lib/python2.7/site-packages/fipy/solvers/scipy/linearLUSolver.py:66: RuntimeWarning: overflow encountered in square
error0 = numerix.sqrt(numerix.sum((L * x - b)**2))
/home/antonio/.local/lib/python2.7/site-packages/fipy/solvers/scipy/linearLUSolver.py:71: RuntimeWarning: overflow encountered in square
if (numerix.sqrt(numerix.sum(errorVector**2)) / error0) <= self.tolerance:
/home/antonio/.local/lib/python2.7/site-packages/fipy/solvers/scipy/linearLUSolver.py:71: RuntimeWarning: invalid value encountered in double_scalars
if (numerix.sqrt(numerix.sum(errorVector**2)) / error0) <= self.tolerance:
/home/antonio/.local/lib/python2.7/site-packages/fipy/tools/numerix.py:966: RuntimeWarning: overflow encountered in square
return sqrt(add.reduce(arr**2))
/home/antonio/.local/lib/python2.7/site-packages/fipy/solvers/scipy/linearLUSolver.py:58: RuntimeWarning: overflow encountered in multiply
b = b * (1 / maxdiag)
Traceback (most recent call last):
File "stack.py", line 26, in <module>
current_residual = eq.sweep()
File "/home/antonio/.local/lib/python2.7/site-packages/fipy/terms/term.py", line 254, in sweep
solver._solve()
File "/home/antonio/.local/lib/python2.7/site-packages/fipy/solvers/scipy/scipySolver.py", line 61, in _solve
self.var[:] = numerix.reshape(self._solve_(self.matrix, self.var.ravel(), numerix.array(self.RHSvector)), self.var.shape)
File "/home/antonio/.local/lib/python2.7/site-packages/fipy/solvers/scipy/linearLUSolver.py", line 64, in _solve_
permc_spec=3)
File "/usr/lib/python2.7/dist-packages/scipy/sparse/linalg/dsolve/linsolve.py", line 257, in splu
ilu=False, options=_options)
RuntimeError: Factor is exactly singular
Finally, I re-wrote the initial equation in an equivalente form by using the variable V=T^{1/2}. It's easy to see that with V the equation becomes
$0 = \frac{1}{r}\frac{d}{dr}\left(r\frac{dV}{dr}\right) + \frac{cte}{2}V^3$
So I then used the code:
mesh = CylindricalGrid1D(nr=100, dr=0.01, origin=0.0)
V = CellVariable(name='V', mesh=mesh, hasOld = True)
r = mesh.cellCenters()
#BC's
V.constrain(0., mesh.facesRight)
V.faceGrad.constrain(0.,mesh.facesLeft)
#initial V profile
V.setValue( 1-r**2)
eqV = 0 == DiffusionTerm( coeff=1., var=V) + 20*0.5*ImplicitSourceTerm(coeff=V*V, var=V)
T = V*V
viewer = Viewer(vars=T)
eqV.solve()
viewer.plot()
raw_input(" Press <enter> to proceed...")
and the profile obtained is quite similar, but the values in the y-axis are not the same either from the first FiPy solution or Mathematica one! Sweeping is giving the same error as before.
I'm not convinced this problem has any solution other than T = 0. Further, that solution seems to be unstable against different values of the initial condition and/or of cte. That instability is not altogether surprising given that the equation in T form would have an infinite diffusivity when T = 0.
I suspect that Mathematica is doing roughly what FiPy is doing in your first set of figures, which is to show the first sweep of this non-linear problem. It's not the answer; just the first guess. I don't know anything about solving PDEs with Mma, though, either analytically or numerically.
The plot after one sweep after your V solution looks different, btw, because you didn't adjust the initial condition. It should be:
V.setValue( numerix.sqrt(1-r**2))
We have the x and y values, and I am taking their log, by logx = np.log10(x) and logy = np.log10(y). I am trying to compute the derivative of logy w.r.t logx, so dlogy/dlogx. I used to do this successfully using numpy gradient, more precisely
derivy = np.gradient(logy,np.gradient(logx))
but for some strange reason it doesn't seem to work anymore yielding the error: "Traceback (most recent call last):
File "derivlog.py", line 79, in <module>
grady = np.gradient(logy,np.gradient(logx))
File "/usr/lib/python2.7/dist-packages/numpy/lib/function_base.py", line 1598, in gradient
raise ValueError("distances must be scalars")
ValueError: distances must be scalars"
Context: When trying to detect power-laws, of the kind y ~ x^t, given the values of y as a function of x, one wants to exctract essentially the power t, so we take logs which gives log y ~ t*log x and then take the derivative in order to extract t.
Here's a minimal example for recreating the problem: x=[ 3. 4. 5. 6. 7. 8. 9. 10. 11.]
y = [ 1.05654 1.44989 1.7939 2.19024 2.62387 3.01583 3.32106 3.51618
3.68153]
Are there other (more suited) methods in python for taking such numerical derivatives?
Thanks to the discussions in the comments the problem with np.gradient has been solved by updating the numpy package from version 1.12.1 to 1.13.3. This update is specially relevant if you are also getting the ValueError "distances must be scalars" when using gradient. Thus, in order to extract the order of the power-law, computing np.gradient(logy,logx) remains a valid option of going about it.
Using Python 3.6, I am trying to minimize a function using scipy.optimize.minimize. My minimization problem as two constraints, and I can find a solution. So far, I have the following:
import numpy as np
from scipy.optimize import minimize
array = np.array([[3.0, 0.25, 0.75],
[0.1, 0.65, 2.50],
[0.80, 2.5, 1.20],
[0.0, 0.25, 0.15],
[1.2, 2.40, 3.60]])
matrix = np.array([[1.0, 1.5, -2.],
[0.5, 3.0, 2.5],
[1.0, 0.25, 0.75]])
def fct1(x):
return -sum(x.dot(array.T))
def fct2(x):
return x.dot(matrix).dot(x)
x0 = np.ones(3) / 3
cons = ({'type': 'eq', 'fun': lambda x: x.sum() - 1.0},
{'type': 'eq', 'fun': lambda x: fct2(x) - tgt})
tgt = 0.15
w = minimize(fct1, x0, method='SLSQP', constraints=cons)['x']
res1 = fct1(w)
res2 = fct2(w)
I am now trying to get my optimizer to run faster as this is only a simplified problem. In the end, my arrays and matrices are way bigger. In a previous question, somebody came up with the idea of defining the jacobian of my function to optimize, so I added the following:
def fct1_deriv(x):
return -sum(np.ones_like(x).dot(array.T))
w = minimize(fct1, x0, method='SLSQP', jac=fct1_deriv, constraints=cons)['x']
Problem is I get the following error message when trying to run:
0-th dimension must be fixed to 4 but got 2
Traceback (most recent call last):
File "C:\Anaconda2\envs\py36\lib\site-packages\IPython\core\interactiveshell.py", line 2881, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-111-d1b854178c13>", line 1, in <module>
w = minimize(fct1, x0, method='SLSQP', jac=fct1_deriv, constraints=cons)['x']
File "C:\Anaconda2\envs\py36\lib\site-packages\scipy\optimize\_minimize.py", line 458, in minimize
constraints, callback=callback, **options)
File "C:\Anaconda2\envs\py36\lib\site-packages\scipy\optimize\slsqp.py", line 410, in _minimize_slsqp
slsqp(m, meq, x, xl, xu, fx, c, g, a, acc, majiter, mode, w, jw)
_slsqp.error: failed in converting 8th argument `g' of _slsqp.slsqp to C/Fortran array
Any ideas on what the problem could be? The link to my previous answer is here:
What is the fastest way to minimize a function in python?
Your function to minimize takes a 3 vector as input, so your Jacobian should correspondingly be a 3 vector as well, each component being the partial derivative with regard to the corresponding input component. SciPy is complaining about not knowing what to do with the single value you are giving it.
In your case, I think this is what you want:
def fct1_deriv(x):
return -np.sum(array, axis=1)
Also, if speed is a concern you probably want to use np.sum, not sum, in fct1.
I think I finally found the answer, and will post here so people can use it or correct me:
In an optimization problem of the form y = x^2, the solution can be found by differentiating y with respect to x, and solving by setting the derivative equal to 0. Therefore, the solution can be found with 2x = 0.0 (solving for x=0.0). Therefore, I have the feeling that passing the Jacobian (first derivative) of a function to optimize helps the optimizer in finding a solution.
In the problem I am trying to optimize, my function is of the form y = x. This function can't be optimize (besides giving it constraints) by differentiating y with respect to x. That would lead to the following equation : 1.0 = 0.0. Therefore giving the Jacobian of my function to my optimizer is probably causing the problem because of the above.
I use the function leastsq from scipy.optimize to fit sphere coordinates and radius from 3D coordinates.
So my code looks like this :
def distance(pc,point):
xc,yc,zc,rd = pc
x ,y ,z = point
return np.sqrt((xc-x)**2+(yc-y)**2+(zc-z)**2)
def sphere_params(coords):
from scipy import optimize
err = lambda pc,point : distance(pc,point) - pc[3]
pc = [0, 0, 0, 1]
pc, success = optimize.leastsq(err, pc[:], args=(coords,))
return pc
(Built thanks to : how do I fit 3D data.)
I started working with the variable coords as a list of tuples (each tuple being an x,y,z coordinate):
>> coords
>> [(0,0,0),(0,0,1),(-1,0,0),(0.57,0.57,0.57),...,(1,0,0),(0,1,0)]
Which lead me to an error :
>> pc = sphere_params(coords)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/michel/anaconda/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 374, in leastsq
raise TypeError('Improper input: N=%s must not exceed M=%s' % (n, m))
TypeError: Improper input: N=4 must not exceed M=3
Where N is the number of parameters stored in pc, and M the number of data points. Which makes it look like I haven't given enough data points while my list coords actually regroups 351 tuples versus 4 parameters in pc !
From what I read in minipack the actual culprit seems to be this line (from _check_func()) :
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
Unless i'm mistaken, in my case it translates into
res = atleast_1d(distance(*(pc[:len(pc)],) + args)
But I'm having a terrible time trying to understand what this mean alongs with the rest of the _check_func() function.
I ended up changing coords into an array before giving it as an argument to sphere_param() : coords = np.asarray(coords).T and it started working just fine. I would really like to understand why the data format was giving me trouble though !
In advance, many thanks for your answers!
EDIT : I notice my use of coords for the "distance" and "err" functions was really unwise and misleading, it wasn't so in my original code so it was not the core of the problem. Now make more sense.
Your err function must take the full list of coords and return a full list of distances. leastsq will then take the list of errors, square and sum them, and minimize that squared sum.
There are also distance functions in scipy.spatial.distance, so I would recommend that:
from scipy.spatial.distance import cdist
from scipy.optimize import leastsq
def distance_cdist(pc, coords):
return cdist([pc], coords).squeeze()
def distance_norm(pc, points):
""" pc must be shape (D+1,) array
points can be (N, D) or (D,) array """
c = np.asarray(pc[:3])
points = np.atleast_2d(points)
return np.linalg.norm(points-c, axis=1)
def sphere_params(coords):
err = lambda pc, coords: distance(pc[:3], coords) - pc[3]
pc = [0, 0, 0, 1]
pc, success = leastsq(err, pc, args=(coords,))
return pc
coords = [(0,0,0),(0,0,1),(-1,0,0),(0.57,0.57,0.57),(1,0,0),(0,1,0)]
sphere_params(coords)
While I haven't used this function much, as best I can tell, coords is passed as is to your distance function. At least it would if the error checking allowed. In fact it is likely that the error checking tries to do that, and raises an error if distance raises an error. So lets try that.
In [91]: coords=[(0,0,0),(0,0,1),(-1,0,0),(0.57,0.57,0.57),(1,0,0),(0,1,0)]
In [92]: distance([0,0,0,0],coords)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-92-113da104affb> in <module>()
----> 1 distance([0,0,0,0],coords)
<ipython-input-89-64c557cd95e0> in distance(pc, coords)
2
3 xc,yx,zx,rd = pc
----> 4 x ,y ,z = coords
5 return np.sqrt((xc-x)**2+(yc-y)**2+(zc-z)**2)
6
ValueError: too many values to unpack (expected 3)
So that's where the 3 comes from - your x, y, z = coords.
distance([0,0,0,0],np.array(coords))
same error.
distance([0,0,0,0],np.array(coords).T)
gets past that issue (3 rows that can be split into 3 variables), raises another error: NameError: name 'yc' is not defined
That looks like a typo in the code you gave us, !naughty, naughty!.
Correcting that:
In [97]: def distance(pc,coords):
xc,yc,zc,rd = pc
x ,y ,z = coords
return np.sqrt((xc-x)**2+(yc-y)**2+(zc-z)**2)
....:
In [98]: distance([0,0,0,0],np.array(coords).T)
Out[98]: array([ 0. , 1. , 1. , 0.98726896, 1. , 1. ])
# and wrapping the array in a tuple, as `leastsq` does
In [102]: distance([0,0,0,0],*(np.array(coords).T,))
Out[102]: array([ 0. , 1. , 1. , 0.98726896, 1. , 1. ])
I get a 5 element array, one value for each 'point' in coords. Is that what you want?
Where did you get the idea that leastsq feeds your coords one tuple at a time to your lambda?
args : tuple
Any extra arguments to func are placed in this tuple.
In general with these optimize functions, it you want to perform the operation on a set of conditions, then you need to iterate over those conditions, calling the optimize on each one. Or if you want to optimize over the whole set at once, then you need to write your function (err,etc) to work with the whole set at once.
So here is what I came up with from previous help :
import numpy as np
from scipy.optimize import leastsq
def a_dist(a,B):
# works with - a : reference point - B : coordinates matrix
return np.linalg.norm(a-B, axis=1)
def parametric(coords):
err = lambda pc,point : a_dist(pc,point) - 18
pc = [0, 0, 0] # Initial guess for the parameters
pc, success = leastsq(err, pc[:], args=(coords,))
return pc
It definitely works with both a list of tuples and an array of shape (N,3)
>> cluster #it's more than 6000 point you won't have the same result
>> [(4, 30, 19), (3, 30, 19), (5, 30, 19), ..., (4, 30, 3), (4, 30, 35)]
>> sphere_params(cluster)
>> array([ -5.25734467, 20.73419249, 9.73428766])
>> np.asarray(cluster).shape
>> (6017,3)
>> sphere_params(np.asarray(cluster))
>> array([ -5.25734467, 20.73419249, 9.73428766])
Combining this version with Askewchan's, ie having :
def sphere_params(coords):
err = lambda pc, coords: distance(pc[:3], coords) - pc[3]
pc = [0, 0, 0, 1]
pc, success = leastsq(err, pc, args=(coords,))
return pc
Also works fine, to be honest I didn't take the time to try your solution. I definitely stopped taking the radius as a fit parameter however. I found it not robust at all (even 6000 -noisy- data points were not enough to get the right curvature !).
When comparing to my first code I'm still not quite sure what was wrong though, I probably messed up with global/local variables although I really don't recall using any "global" statement in any of my functions.
I used fsolve to find the zeros of an example sinus function, and worked great. However, I wanted to do the same with a dataset. Two lists of floats, later converted to arrays with numpy.asarray(), containing the (x,y) values, namely 't' and 'ys'.
Although I found some related questions, I failed to implement the code provided in them, as I try to show here. Our arrays of interest are stored in a 2D list (data[i][j], where 'i' corresponds to a variable (e.g. data[0]==t==time==x values) and 'j' are the values of said variable along the x axis (e.g. data[1]==Force). Keep in mind that each data[i] is an array of floats.
Could you offer an example code that takes two inputs (the two mentioned arrays) and returns its intersecting points with a defined function (e.g. 'y=0').
I include some testing I made regarding the other related question. ( #HYRY 's answer)
I do not think it is relevant, but I'm using Spyder through Anaconda.
Thanks in advance!
"""
Following the answer provided by #HYRY in the 'related questions' (see link above).
At this point of the code, the variable 'data' has already been defined as stated before.
"""
from scipy.optimize import fsolve
def tfun(x):
return data[0][x]
def yfun(x):
return data[14][x]
def findIntersection(fun1, fun2, x0):
return [fsolve(lambda x:fun1(x)-fun2(x, y), x0) for y in range(1, 10)]
print findIntersection(tfun, yfun, 0)
Which returns the next error
File "E:/Data/Anaconda/[...]/00-Latest/fsolvestacktest001.py", line 36, in tfun
return data[0][x]
IndexError: arrays used as indices must be of integer (or boolean) type
The full output is as it follows:
Traceback (most recent call last):
File "<ipython-input-16-105803b235a9>", line 1, in <module>
runfile('E:/Data/Anaconda/[...]/00-Latest/fsolvestacktest001.py', wdir='E:/Data/Anaconda/[...]/00-Latest')
File "C:\Anaconda\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 580, in runfile
execfile(filename, namespace)
File "E:/Data/Anaconda/[...]/00-Latest/fsolvestacktest001.py", line 44, in <module>
print findIntersection(tfun, yfun, 0)
File "E:/Data/Anaconda/[...]/00-Latest/fsolvestacktest001.py", line 42, in findIntersection
return [fsolve(lambda x:fun1(x)-fun2(x, y), x0) for y in range(1, 10)]
File "C:\Anaconda\lib\site-packages\scipy\optimize\minpack.py", line 140, in fsolve
res = _root_hybr(func, x0, args, jac=fprime, **options)
File "C:\Anaconda\lib\site-packages\scipy\optimize\minpack.py", line 209, in _root_hybr
ml, mu, epsfcn, factor, diag)
File "E:/Data/Anaconda/[...]/00-Latest/fsolvestacktest001.py", line 42, in <lambda>
return [fsolve(lambda x:fun1(x)-fun2(x, y), x0) for y in range(1, 10)]
File "E:/Data/Anaconda/[...]/00-Latest/fsolvestacktest001.py", line 36, in tfun
return data[0][x]
IndexError: arrays used as indices must be of integer (or boolean) type
You can 'convert' a datasets (arrays) to continuous functions by means of interpolation. scipy.interpolate.interp1d is a factory that provides you with the resulting function, which you could then use with your root finding algorithm.
--edit-- an example for computing an intersection of sin and cos from 20 samples (I've used cubic spline interpolation, as piecewise linear gives warnings about the smoothness):
>>> import numpy, scipy.optimize, scipy.interpolate
>>> x = numpy.linspace(0,2*numpy.pi, 20)
>>> x
array([ 0. , 0.33069396, 0.66138793, 0.99208189, 1.32277585,
1.65346982, 1.98416378, 2.31485774, 2.64555171, 2.97624567,
3.30693964, 3.6376336 , 3.96832756, 4.29902153, 4.62971549,
4.96040945, 5.29110342, 5.62179738, 5.95249134, 6.28318531])
>>> y1sampled = numpy.sin(x)
>>> y2sampled = numpy.cos(x)
>>> y1int = scipy.interpolate.interp1d(x,y1sampled,kind='cubic')
>>> y2int = scipy.interpolate.interp1d(x,y2sampled,kind='cubic')
>>> scipy.optimize.fsolve(lambda x: y1int(x) - y2int(x), numpy.pi)
array([ 3.9269884])
>>> scipy.optimize.fsolve(lambda x: numpy.sin(x) - numpy.cos(x), numpy.pi)
array([ 3.92699082])
Note that interpolation will give you 'guesses' about what data should be between the sampling points. No way to tell how good these guesses are. (but for my example, you can see it's a pretty good estimation)