2

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

binnev
  • 1,658
  • 1
  • 14
  • 16

4 Answers4

1

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.

Francesco Bonazzi
  • 1,917
  • 9
  • 10
  • OK this certainly seems to work, but I want to make sure I understand what's going on before I trust it completely. If I understand correctly, you (1) create substitutions `x = h/l` and `y = t/l`, (2) use `solve` to rearrange these such that `t` or `l` is the subject, (3) substitute into `expr` and use `simplify` to cancel out any non-`x`/`y` variables, and (4) substitute `x = h/l` and `y = t/l`. I will try this on some of the more complicated expressions I'm working with, and if it works I will mark this answer as accepted. – binnev Sep 17 '15 at 11:54
  • By the way, I didn't know that `solve` could take a whole list of equations, and I didn't know that you could use dictionaries with `subs`. I've been using nested lists this whole time! Mind blown! – binnev Sep 17 '15 at 11:57
  • Yeah that's what I meant. Though I did the solve bit by hand. (BTW it's strubbly). – strubbly Sep 17 '15 at 12:13
  • Works on the more complex expressions too. Requires a bit more playing around with `expand`, `simplify`, etc., but sympy handles that pretty well when we're using `x,y,z`. Thanks! – binnev Sep 17 '15 at 12:24
  • Opened as issue proposing to add a feature to make this procedure simpler: https://github.com/sympy/sympy/issues/9925 – Francesco Bonazzi Sep 18 '15 at 09:23
0

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

strubbly
  • 3,347
  • 3
  • 24
  • 36
  • Could you elaborate on how you performed the substitution? From your answer it looks like you substituted `x=h/l` and `y=t/l` into the second expression, but that isn't what I meant. I want to *get to* the second expression, and we must assume that I don't know what it looks like. – binnev Sep 17 '15 at 11:31
0

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

smichr
  • 16,948
  • 2
  • 27
  • 34
-1

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.

R Nar
  • 5,465
  • 1
  • 16
  • 32
  • It's not just a case of string substitution; it has to make mathematical sense too. I suppose I could hack it such that I add `/l` to both sides of the fraction, but that's not really what I'm looking for. There must be a way to do this within sympy. – binnev Sep 16 '15 at 16:53