I have a simultaneous equation calculator that takes in a lst which contains the strings of the two equations and is supposed to output the solutions to X and Y. Here is the code for it.
x, y = symbols('x,y')
transformations=(standard_transformations + (implicit_multiplication_application,))
eqs_sympy = [Eq(parse_expr(e.split('=')[0], transformations=transformations),
parse_expr(e.split('=')[1], transformations=transformations))
for e in final_lst]
sol = solve(eqs_sympy)
An example of final_lst : ["5x^2 + 1y^5 = 12", "5x^3 + 18y^2 = 42"] (Replace ^ with **)
However, sol just outputs a blank list, why is this so?
These highly nonlinear equations are too high of order for SymPy to give an explicit solution. You can get a numerical solution, however, if you have a good initial guess for the solution. You can get a reasonable guess by graphing the equations or by solving the related equations that don't have the highest powered terms. Use nsolve (e.g. here). Here is one of the 3 roots:
>>> from sympy import nsolve
>>> tuple(nsolve(eqs,(x,y),(-1,1)))
(-0.731937744431011, 1.56277202986898)
Related
I am having issues with getting units to cleanly simplify. The program I am writing is involved with the calculation of spring properties, requiring 10/11 user controlled variables and about a dozen equations, all of which deal with mixed sets of units. The one missing variable can be any of the non-material properties (1/5), so I am trying to use symbolic equations that can solve for whatever my one missing variable is. I tried setting the variables with pint, which could reduce properly and did not have this problem, but they could not Sympify properly, so I had to switch back to SymPy's unit system.
Here is some code that demonstrates the problem:
from sympy.physics.units import *
from sympy import pi, sqrt, N, Eq, symbols, solve
lbf=Quantity('lbf', abbrev='lbf')
lbf.set_global_relative_scale_factor((convert_to(pound*acceleration_due_to_gravity,newton))/newton, newton)
F, Y, L, A, m, ns, xi, d = symbols('F Y L A m ns xi d')
ssy, alpha, beta, C = symbols('ssy alpha beta C')
F= 20*lbf
Y= 2*inch
L= 3.25*inch
d= .08*inch
m= .145
A= 201.00
A*=kilo*psi*inch**m
ns= 1.20
xi= .15
eqSsy=Eq(ssy,.45*A/(d**m))
ssy=solve(eqSsy)[0]
eqAlpha=Eq(alpha,eqSsy.rhs/ns)
alpha=solve(eqAlpha)[0]
eqBeta=Eq(beta,(8*(1+xi)*F)/(N(pi)*(d**2)))
beta=solve(eqBeta)[0]
eqC=Eq(C,((2*eqAlpha.rhs-eqBeta.rhs)/(4*eqBeta.rhs))+sqrt((((2*eqAlpha.rhs-eqBeta.rhs)/(4*eqBeta.rhs))**2)-(3*eqAlpha.rhs)/(4*eqBeta.rhs)))
C=solve(eqC)[0]
print(ssy, '\n', alpha, '\n', beta, '\n', C)
This issue is not related to the lbf unit I had to create, it still happened when I had it using the raw units before I cleaned it up. This leads to C coming out as 1.62e-28*(3.66645442100299e+28*inch**2*psi - 1.54320987654321e+27*lbf + 3.666454421003e+28*sqrt(-0.252539870841386*inch**2*lbf*psi + (inch**2*psi - 0.0420899784735643*lbf)**2))/lbf instead of 10.5334875999498, because none of the units are cancelled through the calculation process.
The "fix" to this problem that I want to avoid is changing line 27, the creation of eqBeta, I have to hard convert the output units to be in psi to prevent the units from coming out as lbf/inch**2 instead of the pressure unit.
eqBeta=Eq(beta,convert_to((8*(1+xi)*F)/(N(pi)*(d**2)),psi))
Is there any way I can make beta automatically reduce to the appropriate pressure unit? The input values are given through a PyQt5 program, not like in this demo, and they can be given either imperial or metric units, so I don't want to be manually forcing a conversion into psi (or forcibly converting C into being unitless).
I would also appreciate if someone knew of a cleaner or better way to do these calculations, as I have just been bashing my head against SymPy because I haven't found another solution. These equations and variable names are taken from a machine design textbook, and I don't want to have to manually create a step-by-step solving process for each possible missing variable.
You might try listing the base units as the quantities to be reduced to:
>>> convert_to(C, [kg, meter, second]).n(2)
11.0
Or if you don't know which ones to use,
>>> from sympy.physics.units import UnitSystem
>>> sibase = UnitSystem.get_unit_system("SI")._base_units
>>> convert_to(C, sibase).n(2)
11.0
cf this issue
I am trying to solve this ordinary linear differential equation of second order with SymPy and get an unexpected result.
import sympy as sym
k, t = sym.symbols('k, t')
s = sym.Function('s')
diff_eq = sym.Eq(s(t).diff(t, 2) + s(t) * k**2, 0) # everything fine here, when I print this I get what I expected.
solution_diff_eq = sym.dsolve(diff_eq, s(t))
print(solution_diff_eq)
Which prints
Eq(s(t), C1*exp(-I*k*t) + C2*exp(I*k*t))
However, the solution I expected is
Any ideas what I have done wrong?
The result prints as
Eq(s(t), C1*exp(-I*k*t) + C2*exp(I*k*t))
which is correct, as I is the imaginary unit. You might prefer the real form, but sympy was not notified of that and produced the most simple form as sum of exponential terms, especially as it is not clear if k is actually real.
If you make it explicit that k is a positive real number via
k = sym.Symbol('k', real=True, positive=True)
the solution is actually in real form, as you were expecting
Eq(s(t), C1*sin(k*t) + C2*cos(k*t))
I have this line of MATLAB code:
a/b
I am using these inputs:
a = [1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9]
b = ones(25, 18)
This is the result (a 1x25 matrix):
[5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
What is MATLAB doing? I am trying to duplicate this behavior in Python, and the mrdivide documentation in MATLAB was unhelpful. Where does the 5 come from, and why are the rest of the values 0?
I have tried this with other inputs and receive similar results, usually just a different first element and zeros filling the remainder of the matrix. In Python when I use linalg.lstsq(b.T,a.T), all of the values in the first matrix returned (i.e. not the singular one) are 0.2. I have already tried right division in Python and it gives something completely off with the wrong dimensions.
I understand what a least square approximation is, I just need to know what mrdivide is doing.
Related:
Array division- translating from MATLAB to Python
MRDIVIDE or the / operator actually solves the xb = a linear system, as opposed to MLDIVIDE or the \ operator which will solve the system bx = a.
To solve a system xb = a with a non-symmetric, non-invertible matrix b, you can either rely on mridivide(), which is done via factorization of b with Gauss elimination, or pinv(), which is done via Singular Value Decomposition, and zero-ing of the singular values below a (default) tolerance level.
Here is the difference (for the case of mldivide): What is the difference between PINV and MLDIVIDE when I solve A*x=b?
When the system is overdetermined, both algorithms provide the
same answer. When the system is underdetermined, PINV will return the
solution x, that has the minimum norm (min NORM(x)). MLDIVIDE will
pick the solution with least number of non-zero elements.
In your example:
% solve xb = a
a = [1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9];
b = ones(25, 18);
the system is underdetermined, and the two different solutions will be:
x1 = a/b; % MRDIVIDE: sparsest solution (min L0 norm)
x2 = a*pinv(b); % PINV: minimum norm solution (min L2)
>> x1 = a/b
Warning: Rank deficient, rank = 1, tol = 2.3551e-014.
ans =
5.0000 0 0 ... 0
>> x2 = a*pinv(b)
ans =
0.2 0.2 0.2 ... 0.2
In both cases the approximation error of xb-a is non-negligible (non-exact solution) and the same, i.e. norm(x1*b-a) and norm(x2*b-a) will return the same result.
What is MATLAB doing?
A great break-down of the algorithms (and checks on properties) invoked by the '\' operator, depending upon the structure of matrix b is given in this post in scicomp.stackexchange.com. I am assuming similar options apply for the / operator.
For your example, MATLAB is most probably doing a Gaussian elimination, giving the sparsest solution amongst a infinitude (that's where the 5 comes from).
What is Python doing?
Python, in linalg.lstsq uses pseudo-inverse/SVD, as demonstrated above (that's why you get a vector of 0.2's). In effect, the following will both give you the same result as MATLAB's pinv():
from numpy import *
a = array([1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9])
b = ones((25, 18))
# xb = a: solve b.T x.T = a.T instead
x2 = linalg.lstsq(b.T, a.T)[0]
x2 = dot(a, linalg.pinv(b))
TL;DR: A/B = np.linalg.solve(B.conj().T, A.conj().T).conj().T
I did not find the earlier answers to create a satisfactory substitute, so I dug into Matlab's reference documents for mrdivide further and found the solution. I cannot explain the actual mathematics here or take credit for coming up with the answer. I'm just following Matlab's explanation. Additionally, I wanted to post the actual detail from Matlab to give credit. If it's a copyright issue, someone tell me and I'll remove the actual text.
%/ Slash or right matrix divide.
% A/B is the matrix division of B into A, which is roughly the
% same as A*INV(B) , except it is computed in a different way.
% More precisely, A/B = (B'\A')'. See MLDIVIDE for details.
%
% C = MRDIVIDE(A,B) is called for the syntax 'A / B' when A or B is an
% object.
%
% See also MLDIVIDE, RDIVIDE, LDIVIDE.
% Copyright 1984-2005 The MathWorks, Inc.
Note that the ' symbol indicates the complex conjugate transpose. In python using numpy, that requires .conj().T chained together.
Per this handy "cheat sheet" of numpy for matlab users, linalg.lstsq(b,a) -- linalg is numpy.linalg.linalg, a light-weight version of the full scipy.linalg.
a/b finds the least square solution to the system of linear equations bx = a
if b is invertible, this is a*inv(b), but if it isn't, the it is the x which minimises norm(bx-a)
You can read more about least squares on wikipedia.
according to matlab documentation, mrdivide will return at most k non-zero values, where k is the computed rank of b. my guess is that matlab in your case solves the least squares problem given by replacing b by b(:1) (which has the same rank). In this case the moore-penrose inverse b2 = b(1,:); inv(b2*b2')*b2*a' is defined and gives the same answer
I want to find numerical solutions to the following exponential equation where a,b,c,d are constants and I want to solve for r, which is not equal to 1.
a^r + b^r = c^r + d^r (Equation 1)
I define a function in order to use Scipy.optimize.fsolve:
from scipy.optimize import fsolve
def func(r,a,b,c,d):
if r==1:
return 10**5
else:
return ( a**(1-r) + b**(1-r) ) - ( c**(1-r) + d**(1-r) )
fsolve(funcp,0.1, args=(5,5,4,7))
However, the fsolve always returns 1 as the solution, which is not what I want. Can someone help me with this issue? Or in general, tell me how to solve (Equation 1). I used an online numerical solver long time ago, but I cannot find it anymore. That's why I am trying to figure it out using Python.
You need to apply some mathematical reasoning when choosing the initial guess. Consider your problem f(r) = (51-r + 51-r) − (41-r + 71-r)
When r ≤ 1, f(r) is always negative and decreasing (since 71-r is growing much faster than other terms). Therefore, all root-finding algorithms will be pushed to right towards 1 until reaching this local solution.
You need to pick a point far away from 1 on the right to find the nontrivial solution:
>>> scipy.optimize.fsolve(lambda r: 5**(1-r)+5**(1-r)-4**(1-r)-7**(1-r), 2.0)
array([ 2.48866034])
Simply setting f(1) = 105 is not going to have any effect, as the root-finding algorithm won't check f(1) until the very last step(note).
If you wish to apply a penalty, the penalty must be applied to a range of value around 1. One way to do so, without affecting the position of other roots, is to divide the whole function by (r − 1):
>>> scipy.optimize.fsolve(lambda r: (5**(1-r)+5**(1-r)-4**(1-r)-7**(1-r)) / (r-1), 0.1)
array([ 2.48866034])
(note): they may climb like f(0.1) → f(0.4) → f(0.7) → f(0.86) → f(0.96) → f(0.997) → … and stop as soon as |f(x)| < 10-5, so your f(1) is never evaluated
First of your code seems to uses a different equation than your question: 1-r instead of just r.
Valid answers to the equation is 1 and 2.4886 approximately as can be seen here. With the second argument of fsolve you specify a starting estimate. I think due to 0.1 being close to 1 you get that result. Using the 2.1 as starting estimate I get the other answer 2.4886.
from scipy.optimize import fsolve
def func(r,a,b,c,d):
if r==1:
return 10**5
else:
return ( a**(1-r) + b**(1-r) ) - ( c**(1-r) + d**(1-r) )
print(fsolve(func, 2.1, args=(5,5,4,7)))
Chosing a starting estimate is tricky as many give the following error: ValueError: Integers to negative integer powers are not allowed.
I tried sympy.solve to solve an nonlinear equation system. It gave me a complex solution set.
Then I tried this equation system in matlab, and got a real solution set which I think is correct, because this is actually a geometry problem and I tested this solution in CAD software.
So...why sympy.solve gave me a complex solution set? where on earth did I make a mistake... or mistakes?
here is the code i wrote:
import sympy
x1=0
y1=-620
r1=920
zqua=126
yqua=276
x3=51
rm=205
r3=104
x0 = sympy.Symbol('x0')
y0 = sympy.Symbol('y0')
r0 = sympy.Symbol('r0')
f1=r0+((x0-x1)**2+(y0-y1)**2)**0.5-r1
f2=(zqua-x0)**2+(yqua-y0)**2-r0**2
f3=r0+((x0-x3)**2+(y0-rm)**2)**0.5-r3
A=sympy.solve((f1,f2,f3), (x0, y0, r0))
print A
and here is the solution it gave:
[(132.229058631742 - 3.4301208813066*I, 282.298802365236 + 1.7767794177989*I, -8.07109966646592 + 1.26065122532955*I), (132.229058631742 + 3.4301208813066*I, 282.298802365236 - 1.7767794177989*I, -8.07109966646592 - 1.26065122532955*I)]
Although you had the right sign of the difference of r in f1 and f3, if you write in terms of squares (where the sign no longer matters) as is already done in f2 you obtain 2 real answers:
>>> f1=(x0-x1)**2+(y0-y1)**2-(r0 - r1)**2
>>> f2=(zqua-x0)**2+(yqua-y0)**2-r0**2
>>> f3=(x0-x3)**2+(y0-rm)**2-(r3 - r0)**2
>>>
>>> A=sympy.solve((f1,f2,f3), (x0, y0, r0))
>>> [[i.n(2) for i in w] for w in A]
[[73., 2.2e+2, 79.], [88., 2.5e+2, 48.]]
>>>
SymPy should have found the roots with the other representation, it seems, but it selected -79 and -48 instead, and those results did not satisfy the original equations and were thus excluded from the reported solution.