Is this a bug or intended behavior?

Hello, SageMath Community!

I was trying to define the function $\tau(n)=$ "number of divisors of $n$" in Sage, so I tried this:

tau(n) = len(divisors(n))


After pressing ENTER, I get the following traceback:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-7aac193e89de> in <module>()
----> 1 __tmp__=var("n"); tau = symbolic_expression(len(divisors(n))).function(n)

/Scientific/SageMath/local/lib/python2.7/site-packages/sage/arith/misc.pyc in divisors(n)
1494             one = parent(n)(1)
1495             output = [one]
-> 1496             for p, e in f:
1497                 prev = output[:]
1498                 pn = one

TypeError: 'sage.symbolic.expression.Expression' object is not iterable


However, I can use exactly the same definition of the function by means of Python's standard function definition mechanism:

def tau(n):
return len(divisors(n))


I am not completely sure, but the following behaviour seems to be related. An alternative definition of $\tau$ would be:

tau(n) = sigma(n, 0)


However, this produces the following traceback:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-016584cdbd3f> in <module>()
----> 1 __tmp__=var("n"); tau = symbolic_expression(sigma(n,Integer(0))).function(n)

/Scientific/SageMath/local/lib/python2.7/site-packages/sage/arith/misc.pyc in __call__(self, n, k)
1595             130
1596         """
-> 1597         n = ZZ(n)
1598         k = ZZ(k)
1599         one = ZZ(1)

/Scientific/SageMath/local/lib/python2.7/site-packages/sage/structure/parent.pyx in sage.structure.parent.Parent.__call__ (build/cythonized/sage/structure/parent.c:9197)()
898         if mor is not None:
899             if no_extra_args:
--> 900                 return mor._call_(x)
901             else:
902                 return mor._call_with_args(x, args, kwds)

/Scientific/SageMath/local/lib/python2.7/site-packages/sage/structure/coerce_maps.pyx in sage.structure.coerce_maps.NamedConvertMap._call_ (build/cythonized/sage/structure/coerce_maps.c:5942)()
286             raise TypeError("Cannot coerce {} to {}".format(x, C))
287         cdef Map m
--> 288         cdef Element e = method(C)
289         if e is None:
290             raise RuntimeError("BUG in coercion model: {} method of {} returned None".format(self.method_name, type(x)))

/Scientific/SageMath/local/lib/python2.7/site-packages/sage/symbolic/expression.pyx in sage.symbolic.expression.Expression._integer_ (build/cythonized/sage/symbolic/expression.cpp:8690)()
1085             n = self.pyobject()
1086         except TypeError:
-> 1087             raise TypeError("unable to convert %r to an integer" % self)
1088         if isinstance(n, sage.rings.integer.Integer):
1089             return n

TypeError: unable to convert n to an integer


However, I can do:

def tau(n):
return sigma(n, 0)


Of course, it is possible to evaluate the formulas len(divisors(n)) and sigma(n, 0), when $n$ has a particular value---n=50, for example.

OS: Ubuntu 18.04.2 LTS

SageMath version: 8.8, dated 2019-06-26

Python version: 2.7.15

Possible explanation: SageMath seems to be trying to evaluate part of the body of the function during compile-time, instead of defering it to execution-time, as Python does.

edit retag close merge delete

Sort by » oldest newest most voted

You have discovered the horror of the syntax for callable symbolic expressions.

• Numbers confusion:

sage: f(x) = x^2
sage: f(2).factor()
4

• Polynomial confusion:

sage: R.<z,w> = PolynomialRing(QQ)
sage: f(x) = x^2
sage: f(z+w).coefficient({z : 1})
...
TypeError: no canonical coercion from <type 'dict'> to Symbolic Ring


• Matrix confusion:

sage: B(x) = matrix([[x, 0], [0, 0]])
sage: B(12)
[x 0]
[0 0]


• List confusion:

sage: f(x) = [x,x]
sage: f(2).parent()
Vector space of dimension 2 over Symbolic Ring


• Derivative confusion (and argument confusion):

sage: f(x) = x.derivative()
sage: f(x^2)
1
sage: f(y) = y.derivative(x)
sage: f(x^2)
0


• Matrix argument confusion:

sage: f(x) = x^2
sage: f(2*identity_matrix(2))
...
TypeError: no canonical coercion from Full MatrixSpace of 2 by 2 dense matrices over Integer Ring to Callable function ring with argument x


sage: f(x) = x^2
sage: g(x) = x^2
sage: var('t')
sage: h(t) = t^2
sage: f+g
x |--> 2*x^2
sage: f+h
(t, x) |--> t^2 + x^2


• Non-symbolic function confusion (your question)

I've seen this too many times now. I went ahead and opened a ticket for it: #28434: Syntax for callable symbolic expressions causes too much confusion.

You should define your function in the way you did, or alternatively with a lambda:

 tau = lambda n: len(divisors(n))

more

Hello, @rburing and @Bruno. I really appreciate your answers. They were very informative! However, the system only allows me to select one question, so I selected @rburing for being more informative for me. This behavior you describe is amazingly misleading. Makes me wonder why not use Python's def mechanism exclusively, although I find Sage's mechanism more convenient. Maybe I'll post a question about the differences between both methods.

( 2019-08-31 07:52:39 -0500 )edit

I'm not sure it is absolutely intended but it is at least an expected behavior: When you write f(x) = <expr>, SageMath actually creates a "callable symbolic expression" from <expr> (in which x is considered as a symbolic variable). This means that <expr> itself must initially be a symbolic expression (or anything that can be transformed into a symbolic expression). But this is not the case of divisors(n) since divisors is not a symbolic function applicable to a symbolic variable. Thus you must use the def ... return ... construction, or the equivalent lambda-expression f = lambda n: divisors(n).

In other words, there is a difference between a symbolic function, which is a Sage object made to represent mathematical functions (thus you can work with it, for instance derive it, integrate, etc.) and a Python function which is a function in the computer science sense, that is a subroutine. The shorthand f(x) = <expr> is a construction for symbolic functions and not for Python functions.

more

To be precise: neither divisors(x) nor len(x) are symbolic expressions.

( 2019-08-30 12:05:55 -0500 )edit