Collecting a fraction expression within a larger fraction (sympy)

424 Views Asked by At

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))

4

There are 4 best solutions below

5
Francesco Bonazzi On BEST ANSWER

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.

1
R Nar On

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.

1
strubbly On

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...

0
smichr On

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.)