sympy separate fractions from variables - sympy

Using sympy how do I keep fractions separate from variables
Mul(Fraction(3,5), Pow(K, Integer(2)))
2
3⋅K
────
5
to
3 2
─ K
5
I know this simplified version is not too bad, but when i have really big equations, it gets messy

I'm not very familiar with pretty printing or LaTeX printing but I managed to come up with something. Put UnevaluatedExpr in each of the arguments of Mul:
from sympy import *
from fractions import Fraction
K = symbols("K")
expr1 = Mul(UnevaluatedExpr(Fraction(3,5)), UnevaluatedExpr(Pow(K, Integer(2))))
expr2 = Mul(UnevaluatedExpr(pi/5), UnevaluatedExpr(Pow(K, Integer(2))))
expr3 = ((UnevaluatedExpr(S(1)*3123456789/512345679) * UnevaluatedExpr(Pow(K, Integer(2)))))
pprint(expr1)
pprint(expr2)
pprint(expr3)
Produces:
2
3/5⋅K
π 2
─⋅K
5
1041152263 2
──────────⋅K
170781893
I couldn't find a way to make it print a stacked fraction for the slashed fraction 3/5. Longer fractions seem to work though. If you are printing in LaTeX however, the documentation suggests something like latex(expr1, fold_frac_powers=False) to correct this.
Too bad I couldn't find an elegant solution like putting init_printing(div_stack_symbols=False) at the top of the document.

To elaborate on Maelstrom's Answer, you need to do 2 things to make this work like you want:
Create the separate fraction you want as its own expression.
Prevent the numerator or denominator from being modified when the expression is combined with other expressions.
What Maelstrom showed will work, but it's much more complicated than what's actually needed. Here's a much cleaner solution:
from sympy import *
K = symbols("K")
# Step 1: make the fraction
# This seems to be a weird workaround to prevent fractions from being broken
# apart. See the note after this code block.
lh_frac = UnevaluatedExpr(3) / 5
# Step 2: prevent the fraction from being modified
# Creating a new multiplication expression will normally modify the involved
# expressions as sympy sees fit. Setting evaluate to False prevents that.
expr = Mul(lh_frac , Pow(K, 2), evaluate=False)
pprint(expr)
gives:
3 2
-*K
5
Important Note:
Doing lh_frac = UnevaluatedExpr(3) / 5 is not how fractions involving 2 literal numbers should typically be created. Normally, you would do:
lh_frac = Rational(3, 5)
as shown in the sympy docs. However, that gives undesirable output for our use case right now:
2
3*K
----
5
This outcome is surprising to me; setting evaluate to False inside Mul should be sufficient to do what we want. I have an open question about this.

Related

Error in factor_list with polynomial containing complex exponentials

I want to make a factor list out an expressions and found a weird behaviour in a special case, which I do not understand. One example would be:
The polynomial contains a pre-factor of exp(-4piI/7) and this factor sometimes makes problems. Removing the negative sign makes it work. It works also with the negative sign when used in a different expression. Below is an example with three expressions
import sympy as sym
q=sym.Symbol("q")
# this example works
y1=3*(sym.exp(4*sym.I*sym.pi/7)*q - 1)*sym.exp(2)
print( sym.factor_list(y1,q) )
# this one not
y2=3*(sym.exp(-4*sym.I*sym.pi/7)*q - 1)*sym.exp(2)
print( sym.factor_list(y2,q) )
# this one works too
y3=3*(sym.exp(-4*sym.I*sym.pi/7)*q - 1)**2*sym.exp(2)
print( sym.factor_list(y3,q) )
For the second example I get the error
sympy.polys.polyerrors.PolynomialError: a polynomial expected, got (3*q*exp(-4*I*pi/7) - 3)*exp(2)
For the third expression I get
(3*exp(2), [(exp(6*I*pi/7), 1), (q - exp(4*I*pi/7), 2)])
but the first entry (exp(6Ipi/7), 1) does not contain the variable q
Any ideas whats wrong with the second example and why exp(6Ipi/7) in the third example appears not with the constant factor 3*exp(2)
thanks

Sympy: Simplify small compound fraction with squares and roots

I have got the following situation (in Sympy 1.8):
from sympy import *
u = symbols('u') # not necessarily positive
term = sqrt(1/u**2)/sqrt(u**2)
The term renders as
How can I simplify this to 1/u**2, i.e. ?
I have tried many functions from https://docs.sympy.org/latest/tutorial/simplification.html, and some arguments listed in https://docs.sympy.org/latest/modules/simplify/simplify.html but could not get it to work.
The variable needs to be declared as real number:
u=symbols('u', real=True)
Then the term is auto-simplified.
(I suggested a corresponding Sympy documentation change.)

How to obtain only rational and not floating point results using sympy

I consider following matrices:
M1 = Matrix([[1/7,2/7],[3/7,4/7]])
M2 = Matrix([[1,2],[3,4]])/7
which are evidently identical, but when I determine their determinant I obtain different results:
print(M1.det())
print(M2.det())
giving the following results:
-0.0408163265306122
-2/49
I would like the first result to be expressed as a rational and not as a floating point.
This is an example of one of the gochas and pitfalls from SymPy's documentation. My answer will basically reiterate what is said there. I highly recommend going through it.
When you type 1/7, the Python interpreter changes it into a float before SymPy has a chance to identify it as a rational number. In order for SymPy to evaluate it before Python does, you need to use some other method. You have already shown one of those other methods with M2: divide a SymPy object by 7 instead of a Python int by 7. Here are a few other ways:
from sympy import *
M = Matrix([[Rational(1, 7),Rational(2, 7)],[Rational(3, 7),Rational(4, 7)]]) # create a Rational object
print(det(M))
M = Matrix([[S(1)/7,S(2)/7],[S(3)/7,S(4)/7]]) # divide a SymPy Integer by 7
print(det(M))
M = Matrix([[S("1/7"),S("2/7")],[S("3/7"),S("4/7")]]) # let SymPy interpret it
print(det(M))
M = Matrix([[1,2],[3,4]])/7 # divide a SymPy Matrix by 7
print(det(M))
M = S("Matrix([[1/7,2/7],[3/7,4/7]])") # throw the whole thing into SymPy
print(det(M))
All of the above will give rational determinants. There are probably many more ways to make SymPy identify a rational number.

Pretty print expression as entered

I would like to pretty print an expression to double check that it's what I want, without any manipulations or simplifications. Here's a simple example:
from sympy import *
import abc
init_session()
sigma_1, sigma_0, mu_1, mu_0,x = symbols("sigma_1 sigma_0 mu_1 mu_0 x")
diff = log(1/(sqrt(2*pi*sigma_1**2)) * exp(-(x-mu_1)**2/(2*sigma_1**2))) - log(1/(sqrt(2*pi*sigma_0**2)) * exp(-(x-mu_0)**2/(2*sigma_0**2)))
diff
This has manipulated the expression a bit, but I'd like to see it pretty printed just in the order I entered it, so I can check it easily against the formulas I've got written down.
Is there a way to do that?
You can avoid some simplifications by using
sympify("log(1/(sqrt(2*pi*sigma_1**2)) * exp(-(x-mu_1)**2/(2*sigma_1**2))) - log(1/(sqrt(2*pi*sigma_0**2)) * exp(-(x-mu_0)**2/(2*sigma_0**2)))", evaluate=False)
However, some simplifications can't be avoided. For example, there's no way to keep terms in the same order, and some expressions, like 1/x and x**-1 are internally represented in the same way. With that being said, there are definitely places where sympify(evaluate=False) could be improved.

How to obtain all solutions for cos(x)*cosh(x) == 1 in Sympy?

Following is the best I can get.
map(lambda n: nsolve(cos(x)*cosh(x)-1,x,3.14/2+3.14*n),range(9))
[mpf('0.0039941152964418809'),
mpf('4.730040744862704'),
mpf('7.8532046240958376'),
mpf('10.995607838001671'),
mpf('14.137165491257464'),
mpf('17.278759657399481'),
mpf('20.420352245626061'),
mpf('23.561944902040455'),
mpf('26.703537555508186')]
If you change range(9) to range(10), sympy will return an error.
ValueError: Could not find root within given tolerance. (1.59798e-17 > 2.1684e-1
9)
Try another starting point or tweak arguments.
I have asked this in the Mathematica site, Mathematica seems can provide the solutions quite accurate and fast. Check this out: how-to-obtain-all-solutions-for-cosx-coshx-1
This is a nice example of using an intelligent initial guess. If you just provide a tolerance you can find the additional solution:
>>> len([nsolve(cos(x)*cosh(x)-1,x,3.14/2+3.14*n,tol=1e-12) for n in range(10)])
10
Note, however, that the function is very steep in the region of the roots and it is unlikely that you will every end up with an x value that will make the function value small. If you know that your initial guesses leads to a root and not a discontinuity, you can safely use the verify=False to skip the verification of the solution (and verify it yourself, perhaps by taking the slope into account). I always feel safer using the bisect method, however, in these cases:
>>> f
cos(x)*cosh(x) - 1
>>> bounds = lambda i: (3.14*i, 3.14*(i+1))
>>> root = lambda i: nsolve(f, bounds(i), solver='bisect', verify=False)
>>> root(0)
mpf('0.0')
>>> root(99)
mpf('312.58846903218443')
>>> root(100)
mpf('315.73006168577422')
You can see that the function at this point is very large, but if we normalize by the derivative of the function the answer looks better:
>>> ans = _
>>> f.subs(x, ans).n(2)
2.3e+122
>>> (f/f.diff(x)).subs(x, ans).n(2)
-3.4e-15
Note: currently, it will not work to pass a normalized function to nsolve so it can be used in the solving process: nsolve only works with the numerator of the function you pass.