Opened 5 years ago
Last modified 3 months ago
#20812 needs_review enhancement
derivative of integer wrt to variable in polynomial ring should belong to that ring, not symbolic ring
Reported by:  dgulotta  Owned by:  

Priority:  major  Milestone:  sageduplicate/invalid/wontfix 
Component:  calculus  Keywords:  
Cc:  Merged in:  
Authors:  Reviewers:  
Report Upstream:  N/A  Work issues:  
Branch:  u/dgulotta/derivative_of_integer_wrt_to_variable_in_polynomial_ring_should_belong_to_that_ring__not_symbolic_ring (Commits, GitHub, GitLab)  Commit:  0dc171fcd64c77aa3e80e958312f42b4f11c31ee 
Dependencies:  Stopgaps: 
Description
If I try to take the derivative of an integer (or nonzero rational, or integer mod n), then the result is an element of the symbolic ring:
sage: R.<x>=ZZ[] sage: derivative(0,x).parent() Symbolic Ring
It seems like it would be more natural for the returned value to belong to the ring containing x instead.
This may seem kind of pedantic, but it did trip me up when I was working with a list of polynomials, some of which were constant, and things were getting cast to Expression unexpectedly.
I am not particularly familiar with the Sage codebase, but I am attaching a patch that seems to fix the issue.
Attachments (1)
Change History (12)
Changed 5 years ago by
comment:1 Changed 4 years ago by
comment:2 Changed 3 months ago by
 Milestone changed from sage7.3 to sageduplicate/invalid/wontfix
 Status changed from new to needs_review
The derivative of a symbolic expression is a symbolic expression :
sage: R1.<t>=ZZ[] sage: derivative(x^2+x+1,t).parent() Symbolic Ring
Therefore this :
sage: derivative(0,t).parent() Symbolic Ring
is a special case, indicating that 0 is cast to a symbolic expression (probably by diff
...).
However :
sage: derivative(R1(0),t).parent() Univariate Polynomial Ring in t over Integer Ring
conforms to the requirement that the derivative of an object belongs its parent ring.
Pedantism works both ways...
==> marking as invalid and requesting review in order to close.
comment:3 followup: ↓ 4 Changed 3 months ago by
This behavior is confusing. I think it's reasonable to expect that the derivative(f,g) will lie in the smallest ring containing f and g. Why not fix this?
comment:4 in reply to: ↑ 3 Changed 3 months ago by
Replying to dgulotta:
This behavior is confusing. I think it's reasonable to expect that the derivative(f,g) will lie in the smallest ring containing f and g. Why not fix this?
In order
 to avoid introducing a lot of specialcasing...
 to keep (at least an appearance of) reason : the differential of a "constant" does not make sense, whereas the differential of a function, expression or polynomial being respectively a function, expression or polynomial does...
...even when this function, expression or polynomial happens to be a "constant" or degree0 monomial, in which case the derivative can be taken to be the null "constant" or degree0 monomial.
Your remark may be more relevant in the reverse case:
sage: R1.<t>=QQbar[] sage: foo=t^2 sage: integral(foo,t).parent() Univariate Polynomial Ring in t over Algebraic Field
So far, so good. But
sage: integral(0,t).parent() Symbolic Ring
is nonsensical, unless we mean to do implicitly :
sage: integral(R1(0),t).parent() Univariate Polynomial Ring in t over Algebraic Field
In other words, take note that
sage: R1(0) is 0 False
even if
sage: R1(0).is_zero() True
HTH,
comment:5 followup: ↓ 6 Changed 3 months ago by
It is difficult to do the right thing in all cases but I think that the patch that I submitted improves the situation for derivatives. I could write something similar for integrals if there is agreement that this would be useful.
The reason why I don't like casting things into the symbolic ring is that it leads to errors that are very difficult to track down. For example:
sage: R.<x>=QQ[] sage: l = [1,x,x*(x1),x*(x1)*(x2)] sage: [derivative(f,x).monomial_coefficient(x) for f in l]  AttributeError Traceback (most recent call last) <ipythoninput16ff4d7a390725> in <module> > 1 [derivative(f,x).monomial_coefficient(x) for f in l] <ipythoninput16ff4d7a390725> in <listcomp>(.0) > 1 [derivative(f,x).monomial_coefficient(x) for f in l] /ext/sage/sage9.2/local/lib/python3.8/sitepackages/sage/structure/element.pyx in sage.structure.element.Element.__getattr__ (build/cythonized/sage/structure/element.c:4703)() 491 AttributeError: 'LeftZeroSemigroup_with_category.element_class' object has no attribute 'blah_blah' 492 """ > 493 return self.getattr_from_category(name) 494 495 cdef getattr_from_category(self, name): /ext/sage/sage9.2/local/lib/python3.8/sitepackages/sage/structure/element.pyx in sage.structure.element.Element.getattr_from_category (build/cythonized/sage/structure/element.c:4815)() 504 else: 505 cls = P._abstract_element_class > 506 return getattr_from_other_class(self, cls, name) 507 508 def __dir__(self): /ext/sage/sage9.2/local/lib/python3.8/sitepackages/sage/cpython/getattr.pyx in sage.cpython.getattr.getattr_from_other_class (build/cythonized/sage/cpython/getattr.c:2620)() 370 dummy_error_message.cls = type(self) 371 dummy_error_message.name = name > 372 raise AttributeError(dummy_error_message) 373 attribute = <object>attr 374 # Check for a descriptor (__get__ in Python) AttributeError: 'sage.symbolic.expression.Expression' object has no attribute 'monomial_coefficient'
It is not at all clear from the error message that 1
needs to be replaced with R(1)
. And this is just a toy example; in real code the failure could occur much later down the line. So I think it is bad for a function like derivative
that sometimes returns polynomials to implicitly cast things into the symbolic ring when none of the arguments live in the symbolic ring. The patch that I submitted should significantly reduce these types of errors.
In principle I think it is better to raise an error with a detailed message than to implicitly cast into the symbolic ring. I guess it may be too late to make that change since it might break existing code. Attempting to cast into a polynomial ring when possible seems like a reasonable compromise.
comment:6 in reply to: ↑ 5 Changed 3 months ago by
Replying to dgulotta:
[ Snip... ]
The patch that I submitted should significantly reduce these types of errors.
Which patch ? I find no patch in the ticket.
More generally, I think that the problem is to define and compute "the smallest ring containing f and g".
To illustrate :
sage: R1.<t>=QQ[] sage: t.derivative(t) 1 sage: t.derivative(t).parent() Univariate Polynomial Ring in t over Rational Field sage: 1.derivative(t).parent() ## [ Snip... ] AttributeError: 'sage.rings.integer.Integer' object has no attribute 'derivative' sage: t.parent()(1).derivative(t).parent() Univariate Polynomial Ring in t over Rational Field
This cast is reasonable and might be expected (i. e. one can reasonably expect 1.differential(t)
to return R1
's 0
.
Harder :
sage: R2.<u>=QQ[] sage: t.derivative(u) ## [ Snip... ] ValueError: cannot differentiate with respect to u
One might expect the 0
with :
 this zero belonging to
PolynomialRing(QQ,"v1,v2")
, and  some "automagic glue" realizing
v1==t, v2==u
.
I'm not sure that this can be expressed in Sage...
For the integrals :
sage: 1.integral(t) ## [ Snip] AttributeError: 'sage.rings.integer.Integer' object has no attribute 'integral' sage: t.parent()(1).integral(t) t
Again, a "reasonable" cast.
The case t.integral(u)
leads to the same conclusion as for t.differentiate(u)
.
And we might have worse difficulties : what should be the "smallest ring" containing Zmod(3)['v']
and QQbar['w']
? Ditto for ring of matrices...
Casting to SR is, indeed, far from ideal, but it seems tome that the possible enhancements are fraught with more difficulties than they solve.
Your thoughts ?
comment:7 followup: ↓ 8 Changed 3 months ago by
There is an attachment to this ticket, which is a patch. The patch uses sage.structure.element.get_coercion_model
. I don't claim to be an expert on this function but it seems to do the right thing in cases that would come up in practice.
comment:8 in reply to: ↑ 7 Changed 3 months ago by
Replying to dgulotta:
There is an attachment to this ticket, which is a patch. The patch uses
sage.structure.element.get_coercion_model
. I don't claim to be an expert on this function but it seems to do the right thing in cases that would come up in practice.
Would you mind submitting a branch, as described in Sagemath's developer's guide ?
comment:9 Changed 3 months ago by
 Branch set to u/dgulotta/derivative_of_integer_wrt_to_variable_in_polynomial_ring_should_belong_to_that_ring__not_symbolic_ring
comment:10 Changed 3 months ago by
 Commit set to 0dc171fcd64c77aa3e80e958312f42b4f11c31ee
It is a bad idea to put a lot of code inside the try
block. Only keep there the minimal amount of code that could potentially raise an error. For example neither elts.append(f)
nor cm = get_coercion_model()
should be there.
New commits:
0dc171f  derivative: try to find a common parent before casting to SR

comment:11 Changed 3 months ago by
This might be an interesting data point:
sage: R.<x> = ZZ[] sage: S.<y> = ZZ[] sage: derivative(S.zero(), x).parent() Univariate Polynomial Ring in y over Integer Ring
If you take care to use the correct zero, this just works:
But there is room for improvement, for sure.