solving single variable two sided equation in sympy - python

Can anyone advise on why the below code using Sympy would error with 'Add' object is not callable? I am trying to solve a simple single variable linear expression. It can be solved by hand and Wolfram Alpha gives a solution for 'y'. Not sure how else to input it into Sympy. I might be less well versed in math lingo than others, so I apologize if I missed something obvious in my research.
from sympy import symbols, Eq, solve
y = symbols('y', positive=True)
lhs = ((y + 38916130600092) * (33005327976 + (y * 0.0931622))) / ((3625511843314 - (y * 0.0931622))(364312177802 - (1.02832244 * y)))
rhs = (674255588738 - (1.001892 * y))/(692042851647 + (1.02832244 * y))
eq1 = Eq(lhs, rhs)
sol = solve(eq1)
print(sol)

The problem is not with Sympy, it is simply a missed multiplication sign in your code. In Line 5, there is no multiplication sign between the two expressions which leads Python to think that you are trying to call 'Add' like a function.
Try the following code,
from sympy import symbols, Eq, solve
y = symbols('y', positive=True)
lhs = ((y + 38916130600092) * (33005327976 + (y * 0.0931622))) / ((3625511843314 - (y * 0.0931622))*(364312177802 - (1.02832244 * y)))
rhs = (674255588738 - (1.001892 * y))/(692042851647 + (1.02832244 * y))
eq1 = Eq(lhs, rhs)
sol = solve(eq1)
print(sol)

Related

Equating differential equations in python

I want to equate those differential equations. I know I can solve them easily in the paper but I want to know how to do it in Python:
from sympy import symbols, Eq, solve
P = Function("P")
Q = Symbol('Q')
Q_d = Symbol("Q_d")
Q_s = Symbol("Q_s")
t = Symbol("t")
dy2 = 3 * Derivative(P(t), t,2)
dy1 = Derivative(P(t), t)
eq1 = Eq(dy2 + dy1 - P(t) + 9,Q_d)
display(eq1)
dy2_ = 5 * Derivative(P(t), t,2)
dy1_ = -Derivative(P(t), t)
eq2 = Eq(dy2_ + dy1_ +4* P(t) -1 ,Q_s)
display(eq2)
βˆ’π‘ƒ(𝑑) + 𝑑/𝑑𝑑*𝑃(𝑑)+3*𝑑2/𝑑𝑑2 * 𝑃(𝑑) + 9 = 𝑄𝑑
4𝑃(𝑑) βˆ’ 𝑑/𝑑𝑑*𝑃(𝑑)+5*𝑑2/𝑑𝑑2 * 𝑃(𝑑) βˆ’1 = 𝑄𝑠
These are basically "supply and demand" equations the result is basically:
2 * 𝑑2/𝑑𝑑2 * 𝑃(𝑑) = (2 * 𝑑/𝑑𝑑𝑃(𝑑) - 5𝑃(𝑑) +10)
How can I find this result? I know Sympy "Solve" can do such a thing:
solve((eq1,eq2), (x, y))
But in this case, I don't have any knowledge.
I assume you get what you call the result by setting Qs = Qd and subtracting the equations? It can be rewritten as
(2 * 𝑑/𝑑𝑑𝑃(𝑑) - 5𝑃(𝑑) +10) - 2 * 𝑑2/𝑑𝑑2 * 𝑃(𝑑) = 0
which you can obtain in sympy doing
>>> eq1.lhs - eq2.lhs
-5*P(t) + 2*Derivative(P(t), t) - 2*Derivative(P(t), (t, 2)) + 10
where lhs returns the left-hand side of the equation.

Equilibria of complex non linear system

When i run the following code i get
TypeError: can't multiply sequence by non-int of type "Add'
Can anyone explain why I get this error?
from sympy.core.symbol import symbols
from sympy.solvers.solveset import nonlinsolve
x, y, z, r, R, a, m, n, b, k1, k2 = symbols('x,y,z,r,R,a,m,n,b,k1,k2', positive=True)
f1 = r * x * (1 - x / k1) - (a * z * x ** (n + 1)) / (x ** n + y ** n)
f2 = R * y * (1 - y / k2) - (b * z * y ** (n + 1)) / (x ** n + y ** n)
f3 = z * (a * x ** (n + 1) + b * y ** (n + 1)) / (x ** n + y ** n) - m * z
f = [f1, f2, f3]
nonlinsolve(f, [x, y, z])
The error message is not really descriptive but the full stack trace indicates where the problem was: SymPy tries to work with the expression as if it was a polynomial, and finds that impossible because the exponent n is a symbol rather than a concrete integer.
Simply put, SymPy does not have an algorithm for solving systems like that one (and I'm not sure if any CAS has).
When written in polynomial form, the system has monomials of total degree n+2. So, already for n = 1 this is utterly hopeless: a system of three cubic equations with three unknowns. SymPy can solve the case n = 0, and I wouldn't expect anything more than that.

Fourier Series Summation in Python

I'm attempting to graph a Fourier series summation in Python. Thus far, I have this:
#! /usr/bin/env python
from sympy import *
import numpy
import matplotlib.pyplot as plt
n = Symbol('n')
x = Symbol('x')
L_1 = -1
L_2 = 1
f = -x
a_0 = (1 / L_2) * integrate(f, (x, L_1, L_2))
a_n = (1 / L_2) * integrate(f * cos(n * pi * x / L_2), (x, L_1, L_2))
b_n = (1 / L_2) * integrate(f * sin(n * pi * x / L_2), (x, L_1, L_2))
F = (a_0 / 2) + mpmath.fsum((a_n * cos(n * pi * x / L_2)) + (b_n * sin(n * pi * x / L_2)), [0, 20])
print(F)
However, fsum is throwing up an error:
F = (a_0 / 2) + mpmath.fsum((a_n * cos(n * pi * x / L_2)) + (b_n * sin(n * pi * x / L_2)), [0, 20])
File "C:\Program Files\Python\lib\site-packages\sympy\mpmath\ctx_mp_python.py", line 831, in fsum
for term in terms:
TypeError: 'Mul' object is not iterable
I'm not clear on what a Mul object is, or what my issue actually is. Does someone have any pointers for getting this summation working? Like I said, the end goal is to plug in some values for x and sum over n iterations, in this case 20.
I am not sure about why its throwing Mul object error, the error means the object is single item and not iterable.
Regarding the method you are trying to use mpmath.fsum, the signature is not matching with the documentation.
mpmath.fsum(terms, absolute=False, squared=False)
The method which you are using, seems match with nsum.
mpmath.nsum(ctx, f, *intervals, **options)
If you want to implement fourier transform in python, you can check the implementation here using python and numpy.
SymPy 1.0 has been included with fourier_series function that lets you compute Fourier Series of a function. Docs can be found here.
eg.
>>> f = fourier_series(-x, (x, -1, 1))
>>> f.truncate(5)
-2*sin(pi*x)/pi + sin(2*pi*x)/pi - 2*sin(3*pi*x)/(3*pi)

Python: interpret expression as a text and not calculate it

After a working with SyMpy library I receive an expression (in yprime variable).
from sympy import *
x = Symbol('x')
y = 1 - (0.1 * coeff1) / (x + 2) - sin(x) * (2 * x + coeff1)
yprime = y.diff(x)
Then I try to use yprime in calculations, but Python interpret it as a text: -(2*x + 1.0)*cos(x) - 2*sin(x) + 0.1/(x + 2)**2
How may I calculate -(2*x + 1.0)*cos(x) - 2*sin(x) + 0.1/(x + 2)**2 ?
After some manipulations according to mtadd:
from sympy import *
x, coeff1 = symbols('x coeff1')
y = 1 - (0.1 * coeff1) / (x + 2) - sin(x) * (2 * x + coeff1)
yprime = y.diff(x).evalf(subs={x:0,coeff1:1})
I receive a digital result, but still it's unable to operate with further logic. It says:
TypeError: can't convert expression to float
Call the method subs on your symbolic expression to calculate an analytic expression. You can call evalf on the analytic expression to a numeric solution in a Python float. Below is some example output from isympy after entering your input from above (coeff1 = 1.0).
In [18]: yprime
Out[18]:
0.1
-(2β‹…x + 1.0)β‹…cos(x) - 2β‹…sin(x) + ────────
2
(x + 2)
In [19]: yprime.subs(x,2*pi)
Out[19]:
0.1
-4β‹…Ο€ - 1.0 + ──────────
2
(2 + 2β‹…Ο€)
In [20]: yprime.subs(x,2*pi).evalf()
Out[20]: -13.5649131254944
The following program:
from sympy import *
x, C1 = symbols('x C1')
y = 1-(0.1*C1)/(x+2)-sin(x)*(2*x+C1)
print y.diff(x).evalf(subs={x:pi/2,C1:1})
prints -1.99215722345589 with sympy version 0.7.2 and python 2.7.3.
I think this might help Evaluating a mathematical expression in a string I think yprime is being interpreted as a string and this first answer of the post describes how to evaluate a string as an arithmetic expression.

Factoring polys in sympy

I'm doing a very simple probability calculations of getting subset of X, Y, Z from set of A-Z (with corresponding probabilities x, y, z).
And because of very heavy formulas, in order to handle them, I'm trying to simplify (or collect or factor - I dont know the exact definition) these polynomial expressions using sympy.
So.. having this (a very simple probability calculation expression of getting subset of X,Y,Z from set of A-Z with corresponding probabilities x, y, z)
import sympy as sp
x, y, z = sp.symbols('x y z')
expression = (
x * (1 - x) * y * (1 - x - y) * z +
x * (1 - x) * z * (1 - x - z) * y +
y * (1 - y) * x * (1 - y - x) * z +
y * (1 - y) * z * (1 - y - z) * x +
z * (1 - z) * y * (1 - z - y) * x +
z * (1 - z) * x * (1 - z - x) * y
)
I want to get something like this
x * y * z * (6 * (1 - x - y - z) + (x + y) ** 2 + (y + z) ** 2 + (x + z) ** 2)
a poly, rewritten in way to have as few operations (+, -, *, **, ...) as possible
I tried factor(), collect(), simplify(). But result differs from my expectations. Mostly I get
2*x*y*z*(x**2 + x*y + x*z - 3*x + y**2 + y*z - 3*y + z**2 - 3*z + 3)
I know that sympy can combine polynomials into simple forms:
sp.factor(x**2 + 2*x*y + y**2) # gives (x + y)**2
But how to make sympy to combine polynomials from expressions above?
If this is impossible task in sympy, may be there are any other options?
Putting together some of the methods happens to give a nice answer this time. It would be interesting to see if this strategy works more often than not on the equations you generate or if, as the name implies, this is just a lucky result this time.
def iflfactor(eq):
"""Return the "I'm feeling lucky" factored form of eq."""
e = Mul(*[horner(e) if e.is_Add else e for e in
Mul.make_args(factor_terms(expand(eq)))])
r, e = cse(e)
s = [ri[0] for ri in r]
e = Mul(*[collect(ei.expand(), s) if ei.is_Add else ei for ei in
Mul.make_args(e[0])]).subs(r)
return e
>>> iflfactor(eq) # using your equation as eq
2*x*y*z*(x**2 + x*y + y**2 + (z - 3)*(x + y + z) + 3)
>>> _.count_ops()
15
BTW, a difference between factor_terms and gcd_terms is that factor_terms will work harder to pull out common terms while retaining the original structure of the expression, very much like you would do by hand (i.e. looking for common terms in Adds that can be pulled out).
>>> factor_terms(x/(z+z*y)+x/z)
x*(1 + 1/(y + 1))/z
>>> gcd_terms(x/(z+z*y)+x/z)
x*(y*z + 2*z)/(z*(y*z + z))
For what it's worth,
Chris
As far as I know, there is no function that does exactly that. I believe it is actually a very hard problem. See Reduce the number of operations on a simple expression for some discussion on it.
There are however, quite a few simplification functions in SymPy that you can try. One that you haven't mentioned that gives a different result is gcd_terms, which factorizes out a symbolic gcd without doing an expansions. It gives
>>> gcd_terms(expression)
x*y*z*((-x + 1)*(-x - y + 1) + (-x + 1)*(-x - z + 1) + (-y + 1)*(-x - y + 1) + (-y + 1)*(-y - z + 1) + (-z + 1)*(-x - z + 1) + (-z + 1)*(-y - z + 1))
Another useful function is .count_ops, which counts the number of operations in an expression. For example
>>> expression.count_ops()
47
>>> factor(expression).count_ops()
22
>>> e = x * y * z * (6 * (1 - x - y - z) + (x + y) ** 2 + (y + z) ** 2 + (x + z) ** 2)
>>> e.count_ops()
18
(note that e.count_ops() is not the same as you counted yourself, because SymPy automatically distributes the 6*(1 - x - y - z) to 6 - 6*x - 6*y - 6*z).
Other useful functions:
cse: Performs a common subexpression elimination on the expression. Sometimes you can simplify the individual parts and then put it back together. This also helps in general to avoid duplicate computations.
horner: Applies the Horner scheme to a polynomial. This minimizes the number of operations if the polynomial is in one variable.
factor_terms: Similar to gcd_terms. I'm actually not entirely clear what the difference is.
Note that by default, simplify will try several simplifications, and return the one that is minimized by count_ops.
I have had a similar problem, and ended up implementing my own solution before I stumbled across this one. Mine seems to do a much better job reducing the number of operations. However, mine also does a brute-force style set of collections over all combinations of variables. Thus, it's runtime grows super-exponentially in the number of variables. OTOH, I've managed to run it on equations with 7 variables in a not-unreasonable (but far from real-time) amount of time.
It is possible that there are some ways to prune some of the search branches here, but I haven't bothered with it. Further optimizations are welcome.
def collect_best(expr, measure=sympy.count_ops):
# This method performs sympy.collect over all permutations of the free variables, and returns the best collection
best = expr
best_score = measure(expr)
perms = itertools.permutations(expr.free_symbols)
permlen = np.math.factorial(len(expr.free_symbols))
print(permlen)
for i, perm in enumerate(perms):
if (permlen > 1000) and not (i%int(permlen/100)):
print(i)
collected = sympy.collect(expr, perm)
if measure(collected) < best_score:
best_score = measure(collected)
best = collected
return best
def product(args):
arg = next(args)
try:
return arg*product(args)
except:
return arg
def rcollect_best(expr, measure=sympy.count_ops):
# This method performs collect_best recursively on the collected terms
best = collect_best(expr, measure)
best_score = measure(best)
if expr == best:
return best
if isinstance(best, sympy.Mul):
return product(map(rcollect_best, best.args))
if isinstance(best, sympy.Add):
return sum(map(rcollect_best, best.args))
To illustrate the performance, this paper(paywalled, sorry) has 7 formulae that are 5th degree polynomials in 7 variables with up to 29 terms and 158 operations in the expanded forms. After applying both rcollect_best and #smichr's iflfactor, the number of operations in the 7 formulae are:
[6, 15, 100, 68, 39, 13, 2]
and
[32, 37, 113, 73, 40, 15, 2]
respectively. iflfactor has 433% more operations than rcollect_best for one of the formulae. Also, the number of operations in the expanded formulae are:
[39, 49, 158, 136, 79, 27, 2]

Categories

Resources