Collecting a fraction expression within a larger fraction (sympy) - python

I am using IPython (Anaconda distribution) with the sympy symbolic maths library.
I have the following expression:
t⋅(h + l)
───────────────────────
l⋅(h + l⋅sin(θ))⋅cos(θ)
I would like to rearrange this to get it in terms of (h/l) and (t/l):
(t/l)⋅((h/l)+1)
─────────────────────
((h/l)+sin(θ))⋅cos(θ)
This is quite easy to do by hand; just divide both sides of the fraction by l and rearrange.
So far I have had no luck with sympy's built in functions.
I have tried using expand followed by collect(expr,h/l), but it doesn't change the expression. I suspect this doesn't work because there are no h/l terms for it to collect in the first place.
How do I get sympy to do this?
Python code for the first expression to save you time:
t*(h + l)/(l*(h + l*sin(theta))*cos(theta))

Building on strubbly's idea:
In [2]: expr = t *(h +l )/(l *(h +l *sin (theta ))*cos (theta ))
In [3]: expr
Out[3]:
t*(h + l)
-------------------------------
l*(h + l*sin(theta))*cos(theta)
In [4]: repl1 = [x-h/l, y-t/l]
In [7]: repl2 = solve(repl1, t, l)
In [8]: repl2
Out[8]:
h h*y
{l: -, t: ---}
x x
In [9]: simplify(expr.subs(repl2)).subs({x: h/l, y: t/l})
Out[9]:
/h \
t*|- + 1|
\l /
-----------------------------
/h \
l*|- + sin(theta)|*cos(theta)
\l /
That is, introduce two variables x and y to replace h/l and t/l (In[4]), invert the equalities in order to get the replacement dictionary (In[7]). Replace, simplify (to get rid of the l), then substitute back the original values for x and y. One variable gets still simplified away.
One should tell .subs( ... ) not to evaluate the expression after substitution. I don't know whether that's currently supported.

So I used x = h/l and y = t/l and substituted. Then simplified. This gave me
x*(y + 1)/((y + sin(theta))*cos(theta))
Which I think is what you want. I can't see how to simplify "with respect to" h/l but this works...

Unless you glom the ratios together, the leading fraction will split across the division line. There are two ways to glom: with an UnevaluatedExpr and with "symbol trickery". First the UnevaluatedExpr:
>>> from sympy import UnevaluatedExpr as UE
>>> eq
t*(h + l)*cos(th)/(l*(h + l*sin(th)))
>>> factor_terms(_.subs(t, UE(t/l)*l).subs(h, UE(h/l)*l))
cos(th)*(sin(th) + h/l)**(-1)*(1 + h/l)*(t/l)
Notice how the order is not as you hoped. So now replace that UE with a symbol that looks like a UE:
>>> _.replace(lambda x: isinstance(x, UE), lambda x: Symbol(str(x)))
t/l*(h/l + 1)*cos(th)/(h/l + sin(th))
So that look like you wanted. The t/l and h/l are actually symbols with a complex name. (You can even use latex for the symbol names.)

I dont really know if you can uses regex, but if you can, you can use re.sub to replace all instances of h with (h/1). or if the expression is a string, you can use str.replace to do the same thing.

Related

Extracting the numbers out of a SymPy expression

How can I extract all the numerical values from a SymPy expression?
For example, for this expression: sin(a/pi + a) + 0.629116159212, I want pi, -1, and 0.629116159212.
I found the srepr function in SymPy, which returns for the example:
Add(sin(Add(Mul(Pow(pi, Integer(-1)), Symbol('a')), Symbol('a'))), Float('0.62911615921200004', precision=53))
How can I extract all the numbers from this, i.e., everything that is not a Symbol?
The method atoms returns a set of all atomic (i.e., unsplittable) components of an expression.
The attribute is_number tells you whether some expression (or atom) is a number.
Combined:
from sympy import sin, pi
from sympy.abc import a
expr = sin(a/pi + a) + 0.629116159212
numbers = {atom for atom in expr.atoms() if atom.is_number}
Now, if you need to preserve the count of appearances, things get a bit more complicated, since atoms returns a set.
Here, we additionally can use:
Alternative 1:
sympy.preorder_traversal (or postorder_traversal) which iterates through all subexpressions of an expression.
(Thanks to Oscar Benjamin and A.S. Meurer.)
Alternative2:
The method find of expressions, which returns all expressions matching some criterion.
The attribute is_Atom.
from sympy import sin, pi, preorder_traversal
from sympy.abc import a
expr = sin(a/pi + 1/a) + 0.629116159212
is_atomic_number = lambda expr: expr.is_Atom and expr.is_number
# Alternative 1:
[
subexpression
for subexpression in preorder_traversal(expr)
if is_atomic_number(subexpression)
]
# Alternative 2:
expr.find(is_atomic_number,group=True)

Sympy: replacing "-1" in expressions?

I am currently working with Sympy 1.0 and Python 2.7, and my aim is to replace every numerical parameter in an expression with a symbol.
For example:
2 * x + 3 * y - 10 * z -> a * x + b * y - c * z
So far I used expression.atoms(Number) to obtain the list of numerical parameters, to later replace them either using .subs or by writing everything to string and using .replace. However, I am facing an issue with expressions such as:
expression = sympy.simpify("x - z")
Where -z is actually -1 * z if I analyze the expression as a tree...but -1 does not appear among the result of expression.atoms(Number), so I cannot replace it.
Even converting the expression to a string to then use .replace does not really help, as -1 * z is always written out as -z.
There is probably some solution that I overlooked, but so far I am stuck. Can you help me? Thank you in advance for your time.
In fact, it turns out I probably did a moronic error when I first tried to use .subs; as Patrick noted in the comments, .subs works perfectly in this case.

Getting a better answer from sympy inverse laplace transform

Trying to compute the following lines I'm getting a realy complex result.
from sympy import *
s = symbols("s")
t = symbols("t")
h = 1/(s**3 + s**2/5 + s)
inverse_laplace_transform(h,s,t)
The result is the following:
(-(I*exp(-t/10)*sin(3*sqrt(11)*t/10) - exp(-t/10)*cos(3*sqrt(11)*t/10))*gamma(-3*sqrt(11)*I/5)*gamma(-1/10 - 3*sqrt(11)*I/10)/(gamma(9/10 - 3*sqrt(11)*I/10)*gamma(1 - 3*sqrt(11)*I/5)) + (I*exp(-t/10)*sin(3*sqrt(11)*t/10) + exp(-t/10)*cos(3*sqrt(11)*t/10))*gamma(3*sqrt(11)*I/5)*gamma(-1/10 + 3*sqrt(11)*I/10)/(gamma(9/10 + 3*sqrt(11)*I/10)*gamma(1 + 3*sqrt(11)*I/5)) + gamma(1/10 - 3*sqrt(11)*I/10)*gamma(1/10 + 3*sqrt(11)*I/10)/(gamma(11/10 - 3*sqrt(11)*I/10)*gamma(11/10 + 3*sqrt(11)*I/10)))*Heaviside(t)
However the answer should be simpler, Wolframalpha proves it.
Is there any way to simplify this result?
I tried a bit with this one and the way I could find a simpler solution is using something like:
from sympy import *
s = symbols("s")
t = symbols("t", positive=True)
h = 1/(s**3 + s**2/5 + s)
inverse_laplace_transform(h,s,t).evalf().simplify()
Notice that I define t as a positive variable, otherwise the sympy function returns a large term followed by the Heaviaside function. The result still contains many gamma functions that I could not reduce to the expression returned by Wolfram. Using evalf() some of those are converted to their numeric value and then after simplification you get a expression similar like the one in Wolfram but with floating numbers.
Unfortunately this part of Sympy is not quite mature. I also tried with Maxima and the result is quite close to the one in Wolfram. So it seems that Wolfram is not doing anything really special there.

plus/minus operator for python ±

I am looking for a way to do a plus/minus operation in python 2 or 3. I do not know the command or operator, and I cannot find a command or operator to do this.
Am I missing something?
If you are looking to print the ± symbol, just use:
print(u"\u00B1")
Another possibility: uncertainties is a module for doing calculations with error tolerances, ie
(2.1 +/- 0.05) + (0.6 +/- 0.05) # => (2.7 +/- 0.1)
which would be written as
from uncertainties import ufloat
ufloat(2.1, 0.05) + ufloat(0.6, 0.05)
Edit: I was getting some odd results, and after a bit more playing with this I figured out why: the specified error is not a tolerance (hard additive limits as in engineering blueprints) but a standard-deviation value - which is why the above calculation results in
ufloat(2.7, 0.07071) # not 0.1 as I expected!
If you happen to be using matplotlib, you can print mathematical expressions similar as one would with Latex. For the +/- symbol, you would use:
print( r"value $\pm$ error" )
Where the r converts the string to a raw format and the $-signs are around the part of the string that is a mathematical equation. Any words that are in this part will be in a different font and will have no whitespace between them unless explicitly noted with the correct code. This can be found on the relavent page of the matplotlib documentation.
Sorry if this is too niche, but I stumbeled across this question trying to find this very answer.
Instead of computing expressions like
s1 = sqrt((125 + 10 * sqrt(19)) / 366)
s2 = sqrt((125 - 10 * sqrt(19)) / 366)
you could use
import numpy as np
pm = np.array([+1, -1])
s1, s2 = sqrt((125 + pm * 10 * sqrt(19)) / 366)
I think you want that for an equation like this;
Well there is no operator for that unless you don't use SymPy, only you can do is make an if statement and find each multiplier.
There is no such object in SymPy yet (as you saw, there is an issue suggesting one https://github.com/sympy/sympy/issues/5305). It's not hard to emulate, though. Just create a Symbol, and swap it out with +1 and -1 separately at the end. Like
pm = Symbol(u'±') # The u is not needed in Python 3. I used ± just for pretty printing purposes. It has no special meaning.
expr = 1 + pm*x # Or whatever
# Do some stuff
exprpos = expr.subs(pm, 1)
exprneg = expr.subs(pm, -1)
You could also just keep track of two equations from the start.
Instead of computing expressions like
s1 = sqrt((125.0 + 10.0*sqrt(19)) / 366.0)
s2 = sqrt((125.0 - 10.0*sqrt(19)) / 366.0)
you could use
r = 10.0*sqrt(19)
s1, s2 = (sqrt((125.0 + i) / 366.0) for i in (r, -r))
This is based on Nico's answer, but using a generator expression instead of NumPy
A plus/minus tolerance test can be done using a difference and absolute against the tolerance you wish to test for. Something like:
tst_data = Number you wish to test
norm = Target number
tolerance = Whatever the allowed tolerance is.
if abs(tst_data - norm) <= tolerance:
do stuff
Using the abs function allows the test to return a +/- within tolerance as True

Sympy won't evaluate 2x but will evaluate x*2

I'm using Sympy's sympify function to simplify 2 expressions so I can compare them for equality.
For example:
expr1 = sympify("(2 * x) + (x + 10)")
expr2 = sympify("(x + 10) + (x * 2)")
if expr1 == expr2:
print "Congrats those are essentially the same!"
However when using the form 2x as apposed to x*2 i get a parse exception eg:
expr1 = sympify("2x + (x + 10)")
Is there any way I can get sympy to understand the 2x form ?
If not, is there any other library that will allow this form ?
Well, you could modify the sympy lexer (or parser / grammar / whatever).
You could also wrap it with a function that transformed your input strings for you, using something like this:
>>> import re
>>> expr = '2x + 1'
>>> re.sub(r"(\d+)(\w+)", r"(\1 * \2)", expr)
'(2 * x) + 1'
But ask yourself why this notation isn't there to begin with.
For example, all of these are valid python, and though it's been a long while since I messed with sympy, I bet they mean something besides multiplication in sympy too:
0x32 # hex for 50
5e-3 # 0.005
2j # 2 * sqrt(-1) (so that one *is* multiplication, but by 1j, not j!)
15L # 15 (L used to represent long integers in python)
And what does x2 mean? Is it a variable named x2 or does it mean (x * 2). I purposely left this case out of the regular expression above because it's so ambiguous.
The development version of SymPy has the ability to parse such expressions. See http://docs.sympy.org/dev/modules/parsing#sympy.parsing.sympy_parser.implicit_multiplication_application. It is still not enabled by default in sympify, because sympify only does very basic extensions to the Python syntax (i.e., wrapping of numeric literals and undefined names, and converting ^ to **). But there is an example there that shows how to use it.
Note that this currently also applies implicit function application as well. Probably the two functionalities should be split up.
EDIT: Those functions are being split up in a pull request.

Categories

Resources