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: sage-duplicate/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:

Status badges

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)

functional.py.patch (871 bytes) - added by dgulotta 5 years ago.

Download all attachments as: .zip

Change History (12)

Changed 5 years ago by dgulotta

comment:1 Changed 4 years ago by chapoton

If you take care to use the correct zero, this just works:

sage: R.zero().derivative(x).parent()
Univariate Polynomial Ring in x over Integer Ring

But there is room for improvement, for sure.

comment:2 Changed 3 months ago by charpent

  • Milestone changed from sage-7.3 to sage-duplicate/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 follow-up: Changed 3 months ago by 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?

comment:4 in reply to: ↑ 3 Changed 3 months ago by charpent

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 special-casing...
  • 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 degree-0 monomial, in which case the derivative can be taken to be the null "constant" or degree-0 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 follow-up: Changed 3 months ago by dgulotta

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*(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'

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 charpent

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 follow-up: Changed 3 months ago by 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.

comment:8 in reply to: ↑ 7 Changed 3 months ago by charpent

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 dgulotta

  • 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 vdelecroix

  • 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:

0dc171fderivative: try to find a common parent before casting to SR

comment:11 Changed 3 months ago by tscrim

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
Note: See TracTickets for help on using tickets.