I am using the following code to solve a cubic equation.
from sympy.solvers import solve
from sympy import Symbol
x = Symbol('x')
print(solve(-0.0643820896731566*x**3 + 0.334816369385245*x**2 + 1.08104426781115*x - 2.05750838005246,x))
As it is a cubic equation with real coefficients, there cannot be three distinct complex roots. But it gives the following results.
[-3.19296319480108 - 0.e-22*I, 1.43925417946882 + 0.e-20*I, 6.95416726521169 - 0.e-20*I]
Could someone please tell me if something goes wrong. Is there other way to solve the equation and gives real roots?
There is a clear code level and interface level separation between
solvers for equations in the complex domain and the real domain. For
example solving 𝑒𝑥=1 when 𝑥 is to be solved in the complex domain,
returns the set of all solutions, that is {2𝑛𝑖𝜋|𝑛∈ℤ}, whereas if
𝑥 is to be solved in the real domain then only {0} is returned.
https://docs.sympy.org/latest/modules/solvers/solveset.html
Instead of solve() you should be using solveset()
from sympy import var, solveset
x = var('x', real=True)
print(solveset(-0.0643820896731566*x**3 + 0.334816369385245*x**2 + 1.08104426781115*x - 2.05750838005246,x))
{-3.19296319480108, 1.43925417946882, 6.95416726521169}
Related
while there are many questions on stackoverflow, how to solve a differential equation analytically, the question how to solve a symbolic equation numerically seems to be more difficult.
I would like to give a formula in its symbolic form - by a Latex string, a sympy equation or anything similiar - and solve it numerically without having to implement
the method (Euler,...) in numpy.
Let us assume, the following equation is given in terms of a sympy equation (damped harmonic oscillator with a source term)
from sympy import Function, dsolve, Eq, Derivative, symbols, lambdify
from sympy.abc import x
f = Function('f')
I = Function('I')
w,k = symbols("w,k")
eq = Eq(Derivative(f(x), x, x) +k*w*Derivative(f(x), x) +(w**2)*f(x),0) - I(x)
an_sol = dsolve(eq, f(x))
For the numeric solution, one would define
the input as a numpy array or a lambda function
the constants w,k
initial conditions that could be given in an equation array in eq
How can I solve this for the cases
a) The analytic solution (an_sol) can be found
b) The analytic solution could not be found?
Any other python package than sympy is welcomed.
I've got this equation from mathematical model to know the thermal behavior of a battery.
dTsdt = Ts * a+ Ta * b + dTadt * c + d
However, i can't get to solve it due to the nested derivatives.
I need to solve the equation for Ts and Ta.
I tried to define it as follows, but python does not like it and several eŕrors show up.
Im using scipy.integrate and the solver ODEint
Since the model takes data from vectors, it has to be solved for every time step and record the output accordingly.
I also tried assinging the derivatives to a variable v1,v2, and then put everything in an equation without derivatives like the second approach shown as follows.
def Tmodel(z,t,a,b,c,d):
Ts,Ta= z
dTsdt = Ts*a+ Ta*b + dTadt*c+ d
dzdt=[dTsdt]
return dzdt
z0=[0,0]
# solve ODE
for i in range(0,n-1):
tspan = [t[i],t[i+1]]
# solve for next step
z = odeint(Tmodel,z0,tspan,arg=(a[i],b[i],c[i],d[i],))
# store solution for plotting
Ts[i] = z[1][0]
Ta[i] = z[1][1]
# next initial condition
z0 = z[1]
def Tmodel(z,t,a,b,c,d):
Ts,v1,Ta,v2= z
# v1= dTsdt
# v2= dTadt
v1 = Ts*a+ Ta*b + v2*c+ d
dzdt=[v1,v2]
return dzdt
That did not work either.I believe there might be a solver capable of solving that equation or the equation must be decouple in a way and solve accordingly.
Any advice on how to solve such eqtn with python would be appreciate it.
Best regards,
MM
Your difficulty seems to be that you are given Ta in a form with no easy derivative, so you do not know where to take it from. One solution is to avoid this derivative completely and solve the system for y=Ts-c*Ta. Substitute Ts=y+c*Ta in the right side to get
dy/dt = y*a + Ta*(b+c*a) + d
Of course, this requires then a post-processing step Ts=y+c*Ta to get to the requested variable.
If Ta is given as function table, use an interpolation function to get values at any odd time t that is demanded by the ODE solver.
Ta_func = interp1d(Ta_times,Ta_values)
def Tmodel(y,t,a,b,c,d):
Ta= Ta_func(t)
dydt = y*a+ Ta*(b+c*a) + d
return dydt
y[0] = Ts0-c*Ta_func(t[0])
for i in range(len(t)-1):
y[i+1] = odeint(Tmodel,y[i],t[i:i+2],arg=(a[i],b[i],c[i],d[i],))[-1,0]
Ts = y + c*Ta_func(t)
(TLDR: SymPy's linsolve function is unable to solve the system of linear equations, generated from applying the finite difference method to an ODE BVP, when you pass the equations as a pure Python list, but is able to do so putting said equations list inside SymPy's Matrix function. This could be a bug that needs to be fixed especially considering that in the SymPy documentation, the example that they give you has them passing a list as an argument to linsolve. )
I have a boundary-value-problem ordinary differential equation that I intend to solve using the finite difference method. My ODE, in SymPy representation, is x*y(x).diff(x,2) + y(x).diff(x) + 500 = 0, with y(1)=600 and y(3.5)=25. Entire code is as follows:
import sympy as sp
from numpy import *
from matplotlib.pyplot import *
y = sp.Function('y')
ti = time.time()
steps = 10
ys = [ y(i) for i in range(steps+1) ]
ys[0], ys[-1] = 600, 25
xs = linspace(1, 3.5, steps+1)
dx = xs[1] - xs[0]
eqns = [ xs[i]*(ys[i-1] - 2*ys[i] + ys[i+1])/dx**2 +
( ys[i+1] - ys[i-1] )/2/dx + 500
for i in range(1, steps) ]
ys[1:-1] = sp.linsolve(eqns, ys[1:-1]).args[0]
scatter(xs, ys)
tf = time.time()
print(f'Time elapsed: {tf-ti}')
For ten steps, this works just fine. However, if I go higher than that immediately like 11 steps, SymPy is no longer able to solve the system of linear equations. Trying to plot the results throws a TypeError: can't convert expression to float. Examining the list of y values ys reveals that one of the variables, specifically the second to the last one, wasn't being solved for by sympy's linsolve, but instead the solutions to the other variables are being solved in terms of this unsolved variable. For example, if I have 50 steps, the second-to-the last variable is y(49) and this is featured on the solutions of the other unknowns and not solved for e.g. 2.02061855670103*y(49) - 26.1340206185567. In contrast, in another BVP ODE that I solved, y(x).diff(x,2) + y(x).diff(x) + y(x) - 1 with y(0)=1.5 and y(3)=2.5, it doesn't have any issue whether I want 10, 50, or 200 steps. It solves all the variables just fine, but this seems to be a peculiar exception as I encountered the aforementioned issue with many other ODEs.
SymPy's inconsistency here was quite frustrating. The only consolation is that before I ran into this problem, I have actually solved it a few times already with the varying number of steps that I wanted. I had enclosed the eqns variable inside sympy's Matrix function as in ys[1:-1] = sp.linsolve(sp.Matrix(eqns), ys[1:-1]).args[0] simply because it displayed better that way in the terminal. But for solving in a script file, I thought that wrapping it inside sp.Matrix is unnecessary and I naturally removed it to simplify things.
It is polite when formatting a question for SO (or anywhere else) to provide a complete code example without missing imports etc. Also you should distil the problem down to the minimum case and remove all of the unnecessary details. With that in mind a better way to demonstrate the issue is by actually figuring out what the arguments to linsolve are and presenting them directly e.g.:
from sympy import *
y = Function('y')
eqns = [
-47.52*y(1) + 25.96*y(2) + 13436.0,
25.96*y(1) - 56.32*y(2) + 30.36*y(3) + 500,
30.36*y(2) - 65.12*y(3) + 34.76*y(4) + 500,
34.76*y(3) - 73.92*y(4) + 39.16*y(5) + 500,
39.16*y(4) - 82.72*y(5) + 43.56*y(6) + 500,
43.56*y(5) - 91.52*y(6) + 47.96*y(7) + 500,
47.96*y(6) - 100.32*y(7) + 52.36*y(8) + 500,
52.36*y(7) - 109.12*y(8) + 56.76*y(9) + 500,
56.76*y(8) - 117.92*y(9) + 61.16*y(10) + 500,
61.16*y(9) - 126.72*y(10) + 2139.0
]
syms = [y(1), y(2), y(3), y(4), y(5), y(6), y(7), y(8), y(9), y(10)]
print(linsolve(eqns, syms))
Here you hoped to get a simple numerical solution for each of the unknowns but instead the result returned (from SymPy 1.8) is:
FiniteSet((5.88050359812056*y(10) - 5.77315239260531, 10.7643116711359*y(10) - 528.13328974178, 14.9403214726998*y(10) - 991.258097567359, 9.85496358613721e+15*y(10) - 1.00932650309452e+18, 7.35110502818395*y(10) - 312.312287998229, 5.84605452313345*y(10) - 217.293922525318, 4.47908204606922*y(10) - 141.418192750506, 3.22698120573309*y(10) - 81.4678489766327, 2.07194244604317*y(10) - 34.9738391105298, 1.0*y(10)))
The linsolve function will return a solution involving one or more unknowns if the system does not have a unique solution. Note also that there are some large numbers like 9.85496358613721e+15 which suggests that there might be numerical problems here.
Actually this is a bug in SymPy and it has already been fixed on the master branch:
https://github.com/sympy/sympy/pull/21527
If you install SymPy from git then you can find the following as output instead:
FiniteSet((596.496767861074, 574.326903264955, 538.901024315178, 493.575084012669, 440.573815245681, 381.447789421181, 317.320815173574, 249.033388036155, 177.23053946471, 102.418085492911))
Also note that it is generally better to avoid using floats in SymPy as it is a library that is designed for exact symbolic computation. Solving a system of floating point equations like this can be done much more efficiently using NumPy or some other fixed precision floating point library that can use BLAS/LAPACK routines. To use rational arithmetic with sympy here you just need to change your linspace line to
xs = [sp.Rational(x) for x in linspace(1, 3.5, steps+1)]
which will then work fine with SymPy 1.8 and is in fact faster (at least if you have gmpy2 installed).
Total Julia noob here (with basic knowledge of Python). I am trying to do linear regression and things I read suggest the GLM package. Here is some sample code I found here:
using DataFrames, GLM
y = 1:10
df = DataFrame(y = y, x1 = y.^2, x2 = y.^3)
sm = GLM.lm( #formula(y ~ x1 + x2), df )
coef(sm)
Can someone explain the syntax here? What does #formula mean? Docs here say #foo means a
macro which I guess is basically just a function, but where do I find the function/macro formula? Just looking at the use here though, I would have thought it is maybe passing y ~ x1 + x2 (whatever that is) as the formula argument to lm? (similar to keyword arguments = in python?)
Next, what is ~ here? General docs say ~ means negation but I'm not seeing how that makes here.
Is there a place in the GLM docs where all of this is explained? I'm not seeing that. Only seeing a few examples but not a full breakdown of each function and all of its arguments.
You have stumbled upon the #formula language that is defined in the StatsModels.jl package and implemented in many statistics/econometrics related packages across the Julia ecosystem.
As you say, #formula is a macro, which transforms the expression given to it (here y ~ x1 + x2) into some other Julia expression. If you want to find out what happens when a macro gets called in Julia - which I admit can often look like magic to new (and sometimes experienced!) users - the #macroexpand macro can help you. In this case:
julia> #macroexpand #formula(y ~ x1 + x2)
:(StatsModels.Term(:y) ~ StatsModels.Term(:x1) + StatsModels.Term(:x2))
The result above is the expression constructed by the #formula macro. We see that the variables in our formula macro are transformed into StatsModels.Term objects. If we were to use StatsModels directly, we could construct this ourselves by doing:
julia> Term(:y) ~ Term(:x1) + Term(:x2)
FormulaTerm
Response:
y(unknown)
Predictors:
x1(unknown)
x2(unknown)
julia> (Term(:y) ~ Term(:x1) + Term(:x2)) == #formula(y ~ x1 + x2)
true
Now what is going on with ~, which as you say can be used for negation in Julia? What has happened here is that StatsModels has defined methods for ~ (which in Julia is and infix operator, that means essentially it is a function that can be written in between its arguments rather than having to be called with its arguments in brackets:
julia> (Term(:y) ~ Term(:x)) == ~(Term(:y), Term(:x))
true
So writing y::Term ~ x::Term is the same as calling ~(y::Term, x::Term), and this method for calling ~ with terms on the left and right hand side is defined by StatsModels (see method no. 6 below):
julia> methods(~)
# 6 methods for generic function "~":
[1] ~(x::BigInt) in Base.GMP at gmp.jl:542
[2] ~(::Missing) in Base at missing.jl:100
[3] ~(x::Bool) in Base at bool.jl:39
[4] ~(x::Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8}) in Base at int.jl:254
[5] ~(n::Integer) in Base at int.jl:138
[6] ~(lhs::Union{AbstractTerm, Tuple{Vararg{AbstractTerm,N}} where N}, rhs::Union{AbstractTerm, Tuple{Vararg{AbstractTerm,N}} where N}) in StatsModels at /home/nils/.julia/packages/StatsModels/pMxlJ/src/terms.jl:397
Note that you also find the general negation meaning here (method 3 above, which defines the behaviour for calling ~ on a boolean argument and is in Base Julia).
I agree that the GLM.jl docs maybe aren't the most comprehensive in the world, but one of the reasons for that is that the whole machinery behind #formula actually isn't a GLM.jl thing - so do check out the StatsModels docs linked above which are quite good I think.
Hello I have a problem using Scipy's fsolve function when I have an equation system.
My code only let's me have as much variables as there are equations in my system, but we all know that in practice, you can have more equations than variables, especially when the solution is not a concrete number but a range of numbers, an additional equation can help narrow down the "haystack" so to speak.
How to insert more equations than variables is my question?
Suppose I have the following non-linear system:
A/B=0.4583(3)
A/C=0.25
A/D=0.72(2)
B/C=0.54(54)
So I have the following code for this:
from scipy.optimize import *
from numpy import *
def FUNC(arg):
A,B,C,D=arg
UNK=empty((4))
UNK[0]= A/B-0.458333333333333
UNK[1]= A/C-0.25
UNK[2]= A/D-0.722222222222222
UNK[3]= B/C-0.545454545454546
return UNK
SOLVED= fsolve(FUNC, [1.0]*4)
print (SOLVED)
The problem is that I also know the following information:
B/D=1.57(57)
C/D=2.8(8)
How can I insert these 2 additional equations into my equation system?
Also how can I display the range of solutions instead of just 1 solution, it seems like fsolve only displays the 1st solution it encounters, not the entire range of possible solutions.
Note that scipy's fsolve is just a Wrapper for the MINPACK's hybrd routine:
The purpose of HYBRD is to find a zero of a system of N non-linear functions in N variables by a modification of the Powell hybrid method. The user must provide a subroutine which calculates the functions. The Jacobian is then calculated by a forward-difference approximation.
If you want to solve a system of 6 equations, your function FUNC needs to be a function of 6 variables. We could do it like this:
import numpy as np
from scipy.optimize import fsolve
def FUNC(arg):
A, B, C, D, E, F = arg
UNK=np.empty((6))
UNK[0]= A/B-0.458333333333333
UNK[1]= A/C-0.25
UNK[2]= A/D-0.722222222222222
UNK[3]= B/C-0.545454545454546
UNK[4]= B/D-1.575757575757575
UNK[5]= C/D-2.888888888888888
return UNK
fsolve(FUNC, x0=[1.0]*6)
It isn't possible to get all solutions with fsolve. But you could try different initial points x0 with the hope to get different solutions.