Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 169afc2

Browse files
committed
Add is_trivial_zero() to coordinate functions and scalar fields and use it in tensor calculus
1 parent effcedb commit 169afc2

14 files changed

+220
-47
lines changed

src/sage/manifolds/coord_func.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,35 @@ def __bool__(self):
322322
323323
"""
324324

325+
@abstract_method
326+
def is_trivial_zero(self):
327+
r"""
328+
Check if ``self`` is trivially equal to zero without any
329+
simplification.
330+
331+
This method is supposed to be fast as compared with
332+
``self.is_zero()`` or ``self == 0`` and is intended to be
333+
used in library code where trying to obtain a mathematically
334+
correct result by applying potentially expensive rewrite rules
335+
is not desirable.
336+
337+
TESTS:
338+
339+
This method must be implemented by derived classes; it is not
340+
implemented here::
341+
342+
sage: M = Manifold(2, 'M', structure='topological')
343+
sage: X.<x,y> = M.chart()
344+
sage: from sage.manifolds.coord_func import CoordFunction
345+
sage: f = CoordFunction(X.function_ring())
346+
sage: f.is_trivial_zero()
347+
Traceback (most recent call last):
348+
...
349+
NotImplementedError: <abstract method is_trivial_zero at 0x...>
350+
351+
"""
352+
353+
325354
@abstract_method
326355
def copy(self):
327356
r"""

src/sage/manifolds/coord_func_symb.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,7 @@ class CoordFunctionSymb(CoordFunction):
189189
1
190190
191191
Another difference regards the display of partial derivatives:
192-
for callable symbolic functions, it relies on Pynac notation
193-
``D[0]``, ``D[1]``, etc.::
192+
for callable symbolic functions, it involves ``diff``::
194193
195194
sage: g = function('g')(x, y)
196195
sage: f0(x,y) = diff(g, x) + diff(g, y)
@@ -211,13 +210,13 @@ class CoordFunctionSymb(CoordFunction):
211210
\frac{\partial\,g}{\partial x} + \frac{\partial\,g}{\partial y}
212211
213212
Note that this regards only the display of coordinate functions:
214-
internally, the Pynac notation is still used, as we can check by asking
213+
internally, the ``diff`` notation is still used, as we can check by asking
215214
for the symbolic expression stored in ``f``::
216215
217216
sage: f.expr()
218217
diff(g(x, y), x) + diff(g(x, y), y)
219218
220-
One can switch to Pynac notation by changing the options::
219+
One can switch to the standard symbolic notation by changing the options::
221220
222221
sage: Manifold.options.textbook_output=False
223222
sage: latex(f)
@@ -519,6 +518,54 @@ def __bool__(self):
519518

520519
__nonzero__ = __bool__ # For Python2 compatibility
521520

521+
def is_trivial_zero(self):
522+
r"""
523+
Check if ``self`` is trivially equal to zero without any
524+
simplification.
525+
526+
This method is supposed to be fast as compared with
527+
``self.is_zero()`` or ``self == 0`` and is intended to be
528+
used in library code where trying to obtain a mathematically
529+
correct result by applying potentially expensive rewrite rules
530+
is not desirable.
531+
532+
EXAMPLES::
533+
534+
sage: M = Manifold(2, 'M', structure='topological')
535+
sage: X.<x,y> = M.chart()
536+
sage: f = X.function(0)
537+
sage: f.is_trivial_zero()
538+
True
539+
sage: f = X.function(float(0.0))
540+
sage: f.is_trivial_zero()
541+
True
542+
sage: f = X.function(x-x)
543+
sage: f.is_trivial_zero()
544+
True
545+
sage: X.zero_function().is_trivial_zero()
546+
True
547+
548+
No simplification is attempted, so that ``False`` is returned for
549+
non-trivial cases::
550+
551+
sage: f = X.function(cos(x)^2 + sin(x)^2 - 1)
552+
sage: f.is_trivial_zero()
553+
False
554+
555+
On the contrary, the method
556+
:meth:`~sage.structure.element.Element.is_zero` and the direct
557+
comparison to zero involve some simplification algorithms and
558+
return ``True``::
559+
560+
sage: f.is_zero()
561+
True
562+
sage: f == 0
563+
True
564+
565+
"""
566+
return self._express.is_trivial_zero()
567+
568+
522569
def copy(self):
523570
r"""
524571
Return an exact copy of the object.
@@ -1742,7 +1789,7 @@ def characteristic(self):
17421789
0
17431790
"""
17441791
return self._chart.manifold().base_field().characteristic()
1745-
1792+
17461793
def from_base_ring(self, r):
17471794
"""
17481795
Return the canonical embedding of ``r`` into ``self``.

src/sage/manifolds/differentiable/diff_form_module.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from sage.structure.unique_representation import UniqueRepresentation
3939
from sage.structure.parent import Parent
4040
from sage.categories.modules import Modules
41+
from sage.rings.integer import Integer
4142
from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule
4243
from sage.manifolds.differentiable.diff_form import DiffForm, DiffFormParal
4344
from sage.manifolds.differentiable.tensorfield import TensorField
@@ -318,7 +319,7 @@ def _element_constructor_(self, comp=[], frame=None, name=None,
318319
True
319320
320321
"""
321-
if comp == 0:
322+
if isinstance(comp, (int, Integer)) and comp == 0:
322323
return self.zero()
323324
if isinstance(comp, (DiffForm, DiffFormParal)):
324325
# coercion by domain restriction
@@ -767,7 +768,7 @@ def _element_constructor_(self, comp=[], frame=None, name=None,
767768
True
768769
769770
"""
770-
if comp == 0:
771+
if isinstance(comp, (int, Integer)) and comp == 0:
771772
return self.zero()
772773
if isinstance(comp, (DiffForm, DiffFormParal)):
773774
# coercion by domain restriction

src/sage/manifolds/differentiable/tensorfield.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,8 +1778,6 @@ def _add_(self, other):
17781778
True
17791779
17801780
"""
1781-
if other == 0:
1782-
return +self
17831781
resu_rst = {}
17841782
for dom in self._common_subdomains(other):
17851783
resu_rst[dom] = self._restrictions[dom] + other._restrictions[dom]
@@ -1843,8 +1841,6 @@ def _sub_(self, other):
18431841
True
18441842
18451843
"""
1846-
if other == 0:
1847-
return +self
18481844
resu_rst = {}
18491845
for dom in self._common_subdomains(other):
18501846
resu_rst[dom] = self._restrictions[dom] - other._restrictions[dom]
@@ -2240,13 +2236,13 @@ def __call__(self, *args):
22402236
self_rr = self_r._restrictions[dom]
22412237
args_rr = [args_r[i]._restrictions[dom] for i in range(p)]
22422238
resu_rr = self_rr(*args_rr)
2243-
if resu_rr == 0:
2239+
if resu_rr.is_trivial_zero():
22442240
for chart in resu_rr._domain._atlas:
22452241
resu._express[chart] = chart._zero_function
22462242
else:
22472243
for chart, expr in resu_rr._express.items():
22482244
resu._express[chart] = expr
2249-
if resu == 0:
2245+
if resu.is_trivial_zero():
22502246
return dom_resu._zero_scalar_field
22512247
# Name of the output:
22522248
res_name = None

src/sage/manifolds/differentiable/tensorfield_module.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from sage.structure.unique_representation import UniqueRepresentation
4242
from sage.structure.parent import Parent
4343
from sage.categories.modules import Modules
44+
from sage.rings.integer import Integer
4445
from sage.tensor.modules.tensor_free_module import TensorFreeModule
4546
from sage.manifolds.differentiable.tensorfield import TensorField
4647
from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal
@@ -303,10 +304,10 @@ def _element_constructor_(self, comp=[], frame=None, name=None,
303304
True
304305
305306
"""
306-
if comp == 0:
307+
if isinstance(comp, (int, Integer)) and comp == 0:
307308
return self.zero()
308309
if isinstance(comp, DiffForm):
309-
# coercion of a p-form to a type-(0,p) tensor:
310+
# coercion of a p-form to a type-(0,p) tensor field:
310311
form = comp # for readability
311312
p = form.degree()
312313
if self._tensor_type != (0,p) or self._vmodule != form.base_module():
@@ -747,7 +748,7 @@ def _element_constructor_(self, comp=[], frame=None, name=None,
747748
True
748749
749750
"""
750-
if comp == 0:
751+
if isinstance(comp, (int, Integer)) and comp == 0:
751752
return self.zero()
752753
if isinstance(comp, DiffFormParal):
753754
# coercion of a p-form to a type-(0,p) tensor field:

src/sage/manifolds/differentiable/vectorfield_module.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from sage.structure.parent import Parent
4343
from sage.categories.modules import Modules
4444
from sage.misc.cachefunc import cached_method
45+
from sage.rings.integer import Integer
4546
from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule
4647
from sage.manifolds.differentiable.vectorfield import (VectorField, VectorFieldParal)
4748

@@ -255,7 +256,7 @@ def _element_constructor_(self, comp=[], frame=None, name=None,
255256
True
256257
257258
"""
258-
if comp == 0:
259+
if isinstance(comp, (int, Integer)) and comp == 0:
259260
return self.zero()
260261
if isinstance(comp, VectorField):
261262
if (self._domain.is_subset(comp._domain)
@@ -1241,7 +1242,7 @@ def _element_constructor_(self, comp=[], basis=None, name=None,
12411242
True
12421243
12431244
"""
1244-
if comp == 0:
1245+
if isinstance(comp, (int, Integer)) and comp == 0:
12451246
return self.zero()
12461247
if isinstance(comp, VectorField):
12471248
if (self._domain.is_subset(comp._domain)

src/sage/manifolds/scalarfield.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,66 @@ def __bool__(self):
697697

698698
__nonzero__ = __bool__ # For Python2 compatibility
699699

700+
def is_trivial_zero(self):
701+
r"""
702+
Check if ``self`` is trivially equal to zero without any
703+
simplification.
704+
705+
This method is supposed to be fast as compared with
706+
``self.is_zero()`` or ``self == 0`` and is intended to be
707+
used in library code where trying to obtain a mathematically
708+
correct result by applying potentially expensive rewrite rules
709+
is not desirable.
710+
711+
EXAMPLES::
712+
713+
sage: M = Manifold(2, 'M', structure='topological')
714+
sage: X.<x,y> = M.chart()
715+
sage: f = M.scalar_field({X: 0})
716+
sage: f.is_trivial_zero()
717+
True
718+
sage: f = M.scalar_field(0)
719+
sage: f.is_trivial_zero()
720+
True
721+
sage: M.zero_scalar_field().is_trivial_zero()
722+
True
723+
sage: f = M.scalar_field({X: x+y})
724+
sage: f.is_trivial_zero()
725+
False
726+
727+
Scalar field defined by means of two charts::
728+
729+
sage: U1 = M.open_subset('U1'); X1.<x1,y1> = U1.chart()
730+
sage: U2 = M.open_subset('U2'); X2.<x2,y2> = U2.chart()
731+
sage: f = M.scalar_field({X1: 0, X2: 0})
732+
sage: f.is_trivial_zero()
733+
True
734+
sage: f = M.scalar_field({X1: 0, X2: 1})
735+
sage: f.is_trivial_zero()
736+
False
737+
738+
No simplification is attempted, so that ``False`` is returned for
739+
non-trivial cases::
740+
741+
sage: f = M.scalar_field({X: cos(x)^2 + sin(x)^2 - 1})
742+
sage: f.is_trivial_zero()
743+
False
744+
745+
On the contrary, the method
746+
:meth:`~sage.structure.element.Element.is_zero` and the direct
747+
comparison to zero involve some simplification algorithms and
748+
return ``True``::
749+
750+
sage: f.is_zero()
751+
True
752+
sage: f == 0
753+
True
754+
755+
"""
756+
if self._is_zero:
757+
return True
758+
return all(func.is_trivial_zero() for func in self._express.values())
759+
700760
def __eq__(self, other):
701761
r"""
702762
Comparison (equality) operator.

src/sage/tensor/modules/alternating_contr_tensor.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,9 +408,13 @@ def display(self, basis=None, format_spec=None):
408408
for ind in comp.non_redundant_index_generator():
409409
ind_arg = ind + (format_spec,)
410410
coef = comp[ind_arg]
411-
if not (coef == 0): # NB: coef != 0 would return False for
412-
# cases in which Sage cannot conclude
413-
# see :trac:`22520`
411+
# Check whether the coefficient is zero, preferably via
412+
# the fast method is_trivial_zero():
413+
if hasattr(coef, 'is_trivial_zero'):
414+
zero_coef = coef.is_trivial_zero()
415+
else:
416+
zero_coef = coef == 0
417+
if not zero_coef:
414418
bases_txt = []
415419
bases_latex = []
416420
for k in range(self._tensor_rank):

0 commit comments

Comments
 (0)