I am trying to calculate a velocity tensor from a time dependent rotationmatrix RE(t)
(Namely the earth rotation at latitude 48.3°). This is achieved by determining the skew symmetric matrix SE(t) = dRE(t)/dt * RE.T
. I am obtaining incorrect results when utilizing a float instead of a Sympy expression, as shown in the following example:
from IPython.display import display
import sympy as sy
sy.init_printing() # LaTeX like pretty printing for IPython
def mk_rotmatrix(alpha, coord_ax="x"):
""" Rotation matrix around coordinate axis """
ca, sa = sy.cos(alpha), sy.sin(alpha)
if coord_ax == "x":
return sy.Matrix([[1, 0, 0],
[0, ca, -sa],
[0, sa, +ca]])
elif coord_ax == 'y':
return sy.Matrix([[+ca, 0, sa],
[0, 1, 0],
[-sa, 0, ca]])
elif coord_ax == 'z':
return sy.Matrix([[ca, -sa, 0],
[sa, +ca, 0],
[0, 0, 1]])
else:
raise ValueError("Parameter coord_ax='" + coord_ax +
"' is not in ['x', 'y', 'z']!")
t, lat = sy.symbols("t, lat", real=True) # time and latitude
omE = 7.292115e-5 # rad/s -- earth rotation rate (15.04107 °/h)
lat_sy = 48.232*sy.pi/180 # latitude in rad
lat_fl = float(lat_sy) # latitude as float
print("\nlat_sy - lat_fl = {}".format((lat_sy - lat_fl).evalf()))
# earth rotation matrix at latitiude 48.232°:
RE = (mk_rotmatrix(omE*t, "z") * mk_rotmatrix(lat - sy.pi/2, "y"))
# substitute latitude with sympy and float value:
RE_sy, RE_fl = RE.subs(lat, lat_sy), RE.subs(lat, lat_fl)
# Angular velocity in world coordinates as skew symmetric matrix:
SE_sy = sy.simplify(RE_sy.diff(t) * RE_sy.T)
SE_fl = sy.simplify(RE_fl.diff(t) * RE_fl.T)
print("\nAngular velocity with Sympy latitude ({}):".format(lat_sy))
display(SE_sy) # correct result
print("\nAngular velocity with float latitude ({}):".format(lat_fl))
display(SE_fl) # incorrect result
The result is:
For the float latitude the result is totally wrong in spite of the difference of only -3e-17 to the Sympy value. It is not clear to me, why this happens. Numerically, this calculation does not seem to be problematic.
My question is, how to work around such deficits. Should I avoid mixing Sympy and float/Numpy data types? They are quite difficult to detect for more complex settings.
PS: The Sympy version is 0.7.6.
TL; DR
It is a bug. If you don't believe it, try this:
Two years ago, the default value for the parameter
tol
inRealField.__init__
was changed fromNone
toFalse
in commit polys: Disabled automatic reduction to zero in RR and CC.Later,
tol
was reverted back toNone
to fix a simplification issue, in commit Changed tol on Complex and Real field to None.It seems the developers didn't expect this reversion would bring some other issue.
If you modify
tol=None
atRealField.__init__
inrealfield.py
, totol=False
, you will get the correct result forSE_fl
.The change of
tol
can explain why you've got a wrong result, but I don't thint it is the root of the issue.IMHO, there is a deficiency in the polynomial factorization in SymPy. I'll illustrate this deficiency.
For convenience, let us do some preparation work.
Add the followings to your example.
We don't need to study the whole matrices. Let us focus on one element, element at row 1 and column 2. It has already shown the result is incorrect.
When we call
simplify
, in this case, it's almost equivalent to a combination ofTR6
andfactor
.Now, we know wrong results would be produced during the invocation of
factor()
.Actually,
factor
is just a wrapper, the major work is done by_symbolic_factor_list
.Let us take a look at
_symbolic_factor_list
. The key part is:We use the above
my_symbolic_factor_list
to simulate this procedure.By design, the constant polynomial should execute
except PolificationFailed as exc:
suite, while the other polynomials should executeelse:
suite.expr_sy
, which is a number afterexpand()
, and1
are both constant polynomials, thusPolificationFailed
s were thrown.poly_fl
is-7.292115e-5 * sin(7.292115e-5*t) ** 0
, namely,-7.292115e-5
, a constant polynomial, whereasexpr_fl
is not. They were supposed to be the same polynomial, just different representation. Now they are not.This is the deficiency I mentioned.
Where is the missing
1.35525271560688e-20*sin(7.292115e-5*t)**2
?Let us recall:
tol
was reverted back toNone
, which means automatic reduction to zero inRR
is enabled again.1.35525271560688e-20
was reduced to zero. Thus,poly_fl
became a constant polynomial.If
tol
isFalse
, this won't happen.Now, we can explain why you've got
-2785579325.0
. In theelse:
suite,Poly.factor_list
is called.According to docs:
poly_fl
is supposed to be a non constant polynomial, but it is just a number. Thus, SymPy was tring to use a rational number to approximatepoly_fl
. The numerator is kept, while the denominator is discarded.I don't think we should blame mixing Sympy and float/Numpy data types. This problem is not caused by those pitfalls SymPy mentioned.
Even a very simple factorization can produce a counterintuitive result.
So it is a bug. Just let the developers fix it.