Sage: Ticket #20812: derivative of integer wrt to variable in polynomial ring should belong to that ring, not symbolic ring
https://trac.sagemath.org/ticket/20812
<p>
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:
</p>
<pre class="wiki">sage: R.<x>=ZZ[]
sage: derivative(0,x).parent()
Symbolic Ring
</pre><p>
It seems like it would be more natural for the returned value to belong to the ring containing x instead.
</p>
<p>
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.
</p>
<p>
I am not particularly familiar with the Sage codebase, but I am attaching a patch that seems to fix the issue.
</p>
en-usSagehttps://trac.sagemath.org/chrome/site/logo_sagemath_trac.png
https://trac.sagemath.org/ticket/20812
Trac 1.1.6dgulottaSun, 12 Jun 2016 02:51:56 GMTattachment set
https://trac.sagemath.org/ticket/20812
https://trac.sagemath.org/ticket/20812
<ul>
<li><strong>attachment</strong>
set to <em>functional.py.patch</em>
</li>
</ul>
TicketchapotonThu, 02 Mar 2017 17:11:11 GMT
https://trac.sagemath.org/ticket/20812#comment:1
https://trac.sagemath.org/ticket/20812#comment:1
<p>
If you take care to use the correct zero, this just works:
</p>
<pre class="wiki">sage: R.zero().derivative(x).parent()
Univariate Polynomial Ring in x over Integer Ring
</pre><p>
But there is room for improvement, for sure.
</p>
TicketcharpentSat, 13 Mar 2021 13:33:50 GMTstatus, milestone changed
https://trac.sagemath.org/ticket/20812#comment:2
https://trac.sagemath.org/ticket/20812#comment:2
<ul>
<li><strong>status</strong>
changed from <em>new</em> to <em>needs_review</em>
</li>
<li><strong>milestone</strong>
changed from <em>sage-7.3</em> to <em>sage-duplicate/invalid/wontfix</em>
</li>
</ul>
<p>
The derivative of a symbolic expression is a symbolic expression :
</p>
<pre class="wiki">sage: R1.<t>=ZZ[]
sage: derivative(x^2+x+1,t).parent()
Symbolic Ring
</pre><p>
Therefore this :
</p>
<pre class="wiki">sage: derivative(0,t).parent()
Symbolic Ring
</pre><p>
is a special case, indicating that 0 is cast to a symbolic expression (probably by <code>diff</code>...).
</p>
<p>
However :
</p>
<pre class="wiki">sage: derivative(R1(0),t).parent()
Univariate Polynomial Ring in t over Integer Ring
</pre><p>
conforms to the requirement that the derivative of an object belongs its parent ring.
</p>
<p>
Pedantism works both ways...
</p>
<p>
==> marking as invalid and requesting review in order to close.
</p>
TicketdgulottaSat, 13 Mar 2021 13:49:07 GMT
https://trac.sagemath.org/ticket/20812#comment:3
https://trac.sagemath.org/ticket/20812#comment:3
<p>
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?
</p>
TicketcharpentMon, 15 Mar 2021 04:10:17 GMT
https://trac.sagemath.org/ticket/20812#comment:4
https://trac.sagemath.org/ticket/20812#comment:4
<p>
Replying to <a class="ticket" href="https://trac.sagemath.org/ticket/20812#comment:3" title="Comment 3">dgulotta</a>:
</p>
<blockquote class="citation">
<p>
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?
</p>
</blockquote>
<p>
In order
</p>
<ul><li>to avoid introducing a <em>lot</em> of special-casing...
</li></ul><ul><li>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...
</li></ul><p>
...even when this function, expression or polynomial happens to be a "constant" or degree-0 monomial, in which case the derivative can be taken to be the null "constant" or degree-0 monomial.
</p>
<p>
Your remark may be more relevant in the reverse case:
</p>
<pre class="wiki">sage: R1.<t>=QQbar[]
sage: foo=t^2
sage: integral(foo,t).parent()
Univariate Polynomial Ring in t over Algebraic Field
</pre><p>
So far, so good. But
</p>
<pre class="wiki">sage: integral(0,t).parent()
Symbolic Ring
</pre><p>
is nonsensical, unless we mean to do implicitly :
</p>
<pre class="wiki">sage: integral(R1(0),t).parent()
Univariate Polynomial Ring in t over Algebraic Field
</pre><p>
In other words, take note that
</p>
<pre class="wiki">sage: R1(0) is 0
False
</pre><p>
even if
</p>
<pre class="wiki">sage: R1(0).is_zero()
True
</pre><p>
HTH,
</p>
TicketdgulottaMon, 15 Mar 2021 14:06:40 GMT
https://trac.sagemath.org/ticket/20812#comment:5
https://trac.sagemath.org/ticket/20812#comment:5
<p>
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.
</p>
<p>
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:
</p>
<pre class="wiki">sage: R.<x>=QQ[]
sage: l = [1,x,x*(x-1),x*(x-1)*(x-2)]
sage: [derivative(f,x).monomial_coefficient(x) for f in l]
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-16-ff4d7a390725> in <module>
----> 1 [derivative(f,x).monomial_coefficient(x) for f in l]
<ipython-input-16-ff4d7a390725> in <listcomp>(.0)
----> 1 [derivative(f,x).monomial_coefficient(x) for f in l]
/ext/sage/sage-9.2/local/lib/python3.8/site-packages/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/sage-9.2/local/lib/python3.8/site-packages/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/sage-9.2/local/lib/python3.8/site-packages/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'
</pre><p>
It is not at all clear from the error message that <code>1</code> needs to be replaced with <code>R(1)</code>. 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 <code>derivative</code> 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.
</p>
<p>
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.
</p>
TicketcharpentMon, 15 Mar 2021 17:04:35 GMT
https://trac.sagemath.org/ticket/20812#comment:6
https://trac.sagemath.org/ticket/20812#comment:6
<p>
Replying to <a class="ticket" href="https://trac.sagemath.org/ticket/20812#comment:5" title="Comment 5">dgulotta</a>:
</p>
<p>
[ Snip... ]
</p>
<blockquote class="citation">
<p>
The patch that I submitted should significantly reduce these types of errors.
</p>
</blockquote>
<p>
Which patch ? I find no patch in the ticket.
</p>
<p>
More generally, I think that the problem is to <em>define</em> and <em>compute</em> "the smallest ring containing f and g".
</p>
<p>
To illustrate :
</p>
<pre class="wiki">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
</pre><p>
This cast is reasonable and might be expected (i. e. one can reasonably expect <code>1.differential(t)</code> to return <code>R1</code>'s <code>0</code>.
</p>
<p>
Harder :
</p>
<pre class="wiki">sage: R2.<u>=QQ[]
sage: t.derivative(u)
## [ Snip... ]
ValueError: cannot differentiate with respect to u
</pre><p>
One might expect the <code>0</code> with :
</p>
<ul><li>this zero belonging to <code>PolynomialRing(QQ,"v1,v2")</code>, <em>and</em>
</li><li>some "automagic glue" realizing <code>v1==t, v2==u</code>.
</li></ul><p>
I'm not sure that this can be expressed in Sage...
</p>
<p>
For the integrals :
</p>
<pre class="wiki">sage: 1.integral(t)
## [ Snip]
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'integral'
sage: t.parent()(1).integral(t)
t
</pre><p>
Again, a "reasonable" cast.
</p>
<p>
The case <code>t.integral(u)</code> leads to the same conclusion as for <code>t.differentiate(u)</code>.
</p>
<p>
And we might have worse difficulties : what should be the "smallest ring" containing <code>Zmod(3)['v']</code> and <code>QQbar['w']</code> ? Ditto for ring of matrices...
</p>
<p>
Casting to SR is, indeed, far from ideal, but it seems tome that the possible enhancements are fraught with more difficulties than they solve.
</p>
<p>
Your thoughts ?
</p>
TicketdgulottaMon, 15 Mar 2021 17:15:18 GMT
https://trac.sagemath.org/ticket/20812#comment:7
https://trac.sagemath.org/ticket/20812#comment:7
<p>
There is an attachment to this ticket, which is a patch. The patch uses <code>sage.structure.element.get_coercion_model</code>. 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.
</p>
TicketcharpentMon, 15 Mar 2021 17:23:04 GMT
https://trac.sagemath.org/ticket/20812#comment:8
https://trac.sagemath.org/ticket/20812#comment:8
<p>
Replying to <a class="ticket" href="https://trac.sagemath.org/ticket/20812#comment:7" title="Comment 7">dgulotta</a>:
</p>
<blockquote class="citation">
<p>
There is an attachment to this ticket, which is a patch. The patch uses <code>sage.structure.element.get_coercion_model</code>. 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.
</p>
</blockquote>
<p>
Would you mind submitting a branch, as described in Sagemath's <a class="ext-link" href="https://doc.sagemath.org/html/en/developer/index.html"><span class="icon"></span>developer's guide</a> ?
</p>
TicketdgulottaTue, 16 Mar 2021 17:31:51 GMTbranch set
https://trac.sagemath.org/ticket/20812#comment:9
https://trac.sagemath.org/ticket/20812#comment:9
<ul>
<li><strong>branch</strong>
set to <em>u/dgulotta/derivative_of_integer_wrt_to_variable_in_polynomial_ring_should_belong_to_that_ring__not_symbolic_ring</em>
</li>
</ul>
TicketvdelecroixWed, 17 Mar 2021 20:04:13 GMTcommit set
https://trac.sagemath.org/ticket/20812#comment:10
https://trac.sagemath.org/ticket/20812#comment:10
<ul>
<li><strong>commit</strong>
set to <em>0dc171fcd64c77aa3e80e958312f42b4f11c31ee</em>
</li>
</ul>
<p>
It is a bad idea to put a lot of code inside the <code>try</code> block. Only keep there the minimal amount of code that could potentially raise an error. For example neither <code>elts.append(f)</code> nor <code>cm = get_coercion_model()</code> should be there.
</p>
<hr />
<p>
New commits:
</p>
<table class="wiki">
<tr><td><a class="ext-link" href="https://git.sagemath.org/sage.git/commit?id=0dc171fcd64c77aa3e80e958312f42b4f11c31ee"><span class="icon"></span>0dc171f</a></td><td><code>derivative: try to find a common parent before casting to SR</code>
</td></tr></table>
TickettscrimFri, 19 Mar 2021 03:55:48 GMT
https://trac.sagemath.org/ticket/20812#comment:11
https://trac.sagemath.org/ticket/20812#comment:11
<p>
This might be an interesting data point:
</p>
<pre class="wiki">sage: R.<x> = ZZ[]
sage: S.<y> = ZZ[]
sage: derivative(S.zero(), x).parent()
Univariate Polynomial Ring in y over Integer Ring
</pre>
Ticket