PremierLangage / premierlangage

Server for auto-evaluating exercices
Other
18 stars 9 forks source link

Sympy objects created in the "before" are not available in the "evaluator" #117

Closed ddoyen closed 4 years ago

ddoyen commented 5 years ago

The Sympy library uses many specific classes (for numbers, mathematical expressions, sets, etc.). When I create a Sympy class object in the "before" block of an exercise, I cannot retrieve it in the "evaluator" block.

qcoumes commented 5 years ago

Currently, classes cannot be retrieve from any builder script, they are converted to dictionnary representing their fields, for example, for a class like this:

class Foo:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def method(self):
        return self.a**3

An instance such as Foo(2, 3) would be converted to {"a": 2, "b": 3).


We tried different kind of serializer, such as pickle, to solve this problem, but it still need the class to be define in the current script in order to unserialize properly.

I am currently looking at dill, which solve this problem (allowing to serialize any python object, and its definition), but I must ensure it'll now break anything or that the serialization does not become too ressources intensive.

ddoyen commented 5 years ago

Actually, all SymPy objects can be converted to a string and this string can in turn be converted to a SymPy object. So it's quite easy (if not fully satisfactory) to pass a SymPy object from the builder to the grader using a string. That's two more lines in the exercise source file.

If it can help, note that there is a metaclass for all SymPy objects : https://docs.sympy.org/latest/modules/core.html#module-sympy.core.basic

ddoyen commented 5 years ago

I have modified the builder "before" and the grader "evaluator". Now they accept keys "headbefore", "footerbefore", "headevaluator", "footerevaluator". These keys can contain python scripts which are executed before or after the "before" script and the "evaluator" script.

Then I have included in a template "mathbasic.pl" a piece of code which converts SymPy objects into strings after the "before" and a piece of code which reconverts these strings into SymPy objects before the "evaluator".

footerbefore ==
_strsympyvar={}
for _namevar in list(locals().keys()):
    if isinstance(locals()[_namevar],(Basic,Matrix)):
        _strsympyvar[_namevar]=str(locals()[_namevar])
==
headevaluator ==
for _namevar in list(_strsympyvar.keys()):
    with evaluate(False):
        locals()[_namevar]=sympify(_strsympyvar[_namevar])
==

It works fine and it's transparent.

esandier commented 5 years ago

I suggest a variant of ddoyen's scheme, in order for it to allow for lists/dicts/tuples of sympy objects to go through.

In footerbefore

def _sympy_to_str(arg):
# returns arg with sympy items converted to strings, otherwise they don't go through 
# to the evaluator tag.
# applies recursively to items if arg is a list, dict, tuple
    if isinstance(arg,(Basic,Matrix)):
        return '_converted_'+str(arg)
    elif isinstance(arg, dict):
        return {k: _sympy_to_str(v) for k, v in arg.items()}
    elif isinstance(arg, list):
        return list(map(_sympy_to_str,arg))
    elif isinstance(arg, tuple):
        return tuple(map(_sympy_to_str,arg))
    else : 
        return arg

_strsympyvar={}
_k = None
_v = None
for _k,_v in locals().items():
    if isinstance(_v,(Basic,Matrix, list, tuple, dict)):
        _strsympyvar[_k]=_sympy_to_str(_v)

In headevaluator

def _str_to_sympy(arg):
# inverse of _sympy_to_str in footerbefore.
    if isinstance(arg,str):
        if arg[:11] == '_converted_':
            with evaluate(False):
                return sympify(arg[11:])
        else:
            return arg
    elif isinstance(arg, dict):
        return {k: _str_to_sympy(v) for k, v in arg.items()}
    elif isinstance(arg, list):
        return list(map(_str_to_sympy,arg))
    elif isinstance(arg, tuple):
        return tuple(map(_str_to_sympy,arg))
    else : return arg

for _k,_v in _strsympyvar.items():
    locals()[_k]=_str_to_sympy(_v)
mciissee commented 4 years ago

Closed due to inactivity