sagemath / sage

Main repository of SageMath
https://www.sagemath.org
Other
1.31k stars 449 forks source link

pickled symbolic function breaks maxima interface #17503

Open a46d73b2-c70f-469a-b7c4-f2ca845e72d5 opened 9 years ago

a46d73b2-c70f-469a-b7c4-f2ca845e72d5 commented 9 years ago

Like this:

$ sage
┌────────────────────────────────────────────────────────────────────┐
│ Sage Version 6.4.1, Release Date: 2014-11-23                       │
│ Type "notebook()" for the browser-based notebook interface.        │
│ Type "help()" for help.                                            │
└────────────────────────────────────────────────────────────────────┘
sage: z = sage.symbolic.function_factory.function('z',nargs=1)
sage: z_expr = solve( z(x) == x/(1-x), x )[0].rhs()
sage: z_expr
z(x)/(z(x) + 1)
sage: limit(loads(dumps(z_expr)), x=0)
limit(z(x)/(z(x) + 1), x, 0)
sage: limit(z_expr, x=0)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-5-a3b6f4b6aa26> in <module>()
----> 1 limit(z_expr, x=Integer(0))

/usr/local/sage/local/lib/python2.7/site-packages/sage/calculus/calculus.pyc in limit(ex, dir, taylor, algorithm, **argv)
   1249     if algorithm == 'maxima':
   1250         if dir is None:
-> 1251             l = maxima.sr_limit(ex, v, a)
   1252         elif dir in ['plus', '+', 'right', 'above']:
   1253             if dir == 'above':

/usr/local/sage/local/lib/python2.7/site-packages/sage/interfaces/maxima_lib.pyc in sr_limit(self, expr, v, a, dir)
    937         """
    938         try:
--> 939             L=[sr_to_max(SR(a)) for a in [expr,v,a]]
    940             if dir == "plus":
    941                 L.append(max_plus)

/usr/local/sage/local/lib/python2.7/site-packages/sage/interfaces/maxima_lib.pyc in sr_to_max(expr)
   1606             max_op_dict[op_max]=op
   1607         return EclObject(([sage_op_dict[op]],
-> 1608                      [sr_to_max(o) for o in expr.operands()]))
   1609     elif expr.is_symbol() or expr.is_constant():
   1610         if not expr in sage_sym_dict:

/usr/local/sage/local/lib/python2.7/site-packages/sage/interfaces/maxima_lib.pyc in sr_to_max(expr)
   1606             max_op_dict[op_max]=op
   1607         return EclObject(([sage_op_dict[op]],
-> 1608                      [sr_to_max(o) for o in expr.operands()]))
   1609     elif expr.is_symbol() or expr.is_constant():
   1610         if not expr in sage_sym_dict:

/usr/local/sage/local/lib/python2.7/site-packages/sage/interfaces/maxima_lib.pyc in sr_to_max(expr)
   1606             max_op_dict[op_max]=op
   1607         return EclObject(([sage_op_dict[op]],
-> 1608                      [sr_to_max(o) for o in expr.operands()]))
   1609     elif expr.is_symbol() or expr.is_constant():
   1610         if not expr in sage_sym_dict:

/usr/local/sage/local/lib/python2.7/site-packages/sage/interfaces/maxima_lib.pyc in sr_to_max(expr)
   1602             op_max=maxima(op).ecl()
   1603             if op_max in max_op_dict:
-> 1604                 raise RuntimeError("Encountered operator mismatch in sr-to-maxima translation")
   1605             sage_op_dict[op]=op_max
   1606             max_op_dict[op_max]=op

RuntimeError: Encountered operator mismatch in sr-to-maxima translation
sage: 

I don't know the code well enough to diagnose, but from poking around in sr_to_max() it looks like we've got multiple operators with the same name 'z', causing a collision in the dictionaries it uses to translate between sage's names and the names it passes into maxima.

Component: interfaces

Keywords: pickle, maxima, symbolic, function, sr_to_max

Issue created by migration from https://trac.sagemath.org/ticket/17503

a46d73b2-c70f-469a-b7c4-f2ca845e72d5 commented 9 years ago
comment:3

17504 suggests this could be caused by nargs=1, but I've just retested it using the identical sequence except with z = sage.symbolic.function_factory.function('z'), and it throws the same exception.

nbruin commented 9 years ago
comment:4

This hints at a problem:

sage: function('z') is function('z')
False
sage: function('z') == function('z')
True
sage: function('z') == loads(dumps(function('z'))) #as expected
True
sage: function('z')(x).operator() == function('z')(x).operator()
True
sage: function('z')(x).operator() == loads(dumps(function('z')(x))).operator() #this should also be true
False

It seems to me that pynac expressions don't properly get pickled:

sage: explain_pickle(dumps(z))
pg_unpickle_function = unpickle_global('sage.symbolic.function_factory', 'unpickle_function')
pg_unpickle_function('z', 0r, None, None, True, [None, None, None, None, None, None, None, None, None, None, None])
sage: explain_pickle(dumps(z(x)))
pg_Expression = unpickle_global('sage.symbolic.expression', 'Expression')
si = unpickle_newobj(pg_Expression, ())
unpickle_build(si, (0r, ['x'], 'GARC\x03\tfunction\x00class\x00symbol\x00x\x00name\x00seq\x00python\x00z\x00sage_ex\x00\x01\x08\x01\x02\x02\n\x02"\x03\x04\n\x00+\x001\x00"\x07'))
si

As you can see, there doesn't seem to be a reference to "unpickle_function" which should really be triggered by pickling an expression that contains 'z'.

Pynac's fiddling with arity probably plays a role:

sage: from sage.symbolic.function_factory import function #to avoid unexpected globals insertions
sage: z=function('z')
sage: z(x)+z(x,x)
z(x, x) + z(x)
sage: z1=loads(dumps(z(x))).operator()
sage: z2=loads(dumps(z(x,x))).operator()
sage: z1(x,x)
TypeError: Symbolic function z takes exactly 1 arguments (2 given)
sage: z2(x)
TypeError: Symbolic function z takes exactly 2 arguments (1 given)
sage: z1 == z2
False
sage: z1 == function('z',nargs=1)
True
sage: z2 == function('z',nargs=2)
True
nbruin commented 9 years ago
comment:5

OK, this is a very nice example. It seems that expression pickling forces functions to have a fixed arity. On the other hand, calling solve goes through the maxima strings interface, so its resulting functions have undeclared arity. That's why in the example given, the "nargs=1" has no effect: After calling "solve" it's stripped. The following does not produce an error:

sage: from sage.symbolic.function_factory import function
sage: z=function('z',nargs=1)
sage: z1=loads(dumps(z(x))).operator()
sage: z==z1
True
sage: z is z1
False
sage: limit(z(x),x=0)
limit(z(x), x, 0)
sage: limit(z1(x),x=0)
limit(z(x), x, 0)

So there are two issues at play in this ticket:

a46d73b2-c70f-469a-b7c4-f2ca845e72d5 commented 9 years ago
comment:6

possibly related: #17558 (pickled function loses its args)