PROPOSAL: Dynamic creation of functions
Dear GiNaC developers, In "What doesn't belong into GiNaC" section of GiNaC tutorial you encourage using GiNaC as a bottom of a complete CAS. I have used GiNaC in connection with Python language and found that such a combination can be very close to a perfect environment that has powerful programming capabilities with efficient symbolic algebra support. However, GiNaC lacks one important feature, that is, creating new functions in runtime. It is clear that within C++ environment it is not desired (if not impossible) as users can define their functions at the compilation stage. In scripting languages, however, new functions can be defined only in runtime. Therefore, I have been looking ways how to create new functions to GiNaC without forcing re-compilations of C++ codes. One can find different approaches for that but they all require some modifications to GiNaC sources. So, I am writing to you with a hope that these modifications could be done in GiNaC repository, rather than by me or anybody else with a similar projects, each time as a new version of GiNaC is released. After trying out many approaches, I have reached to the following conclusion: It is impossible to define dynamic functions in GiNaC because the variables function_options::eval_f,evalf_f,series_f,derivative_f must point to static functions where is impossible to determine what function called it (that is, with what serial number). Now, let me describe a solution to this problem: 1) (Interface authors) define special functions dynfunc_eval_f,etc as follows (I'll use Python C/API for illustrations): ex dynfunc_eval_f(const exvector & seq) { unsigned serial = function::current_serial; //see below (3) PyObject *func = dynfunc_eval_cache[serial]; //see below (2) /* Implement call: result = func(seq), specific to different languages */ return result; } 2) Dynamic functions are created by calling the following (a minimal implementation): void build_function(const string & name, unsigned nparams, PyObject* py_eval_func, //pointers to Python functions PyObject* py_evalf_func, ...) { function_options opts; opts = set_name(name).eval_func(dynfunc_eval_f); // NOTE: all dynamically created functions have the same eval_f. // There, using cache and serial, appropriate Python function is called. // See above (1) opts.test_and_set_params(nparams); unsigned serial = function::register_new(opts); // Store Python function in cache: dynfunc_eval_cache[serial] = py_eval_func; } where std::map<unsigned, PyObject*> dynfunc_eval_cache; is defined in the interface space (similarly are defined hooks for evalf,series,derivative). 3) The following definitions would be new to GiNaC: typedef ex (* GiNaC::eval_funcp_v)(const exvector &); function_options& function_options::eval_func(eval_funcp_v e) { use_vector_args = true; eval_f = eval_funcp(e); return *this; } class function_options { ... protected: bool use_vector_args; /* are to be set false by default */ }; class function { ... public: static unsigned current_serial; }; and in GiNaC::function::eval the following lines are added just before the 'switch (opt.nparams) {..}' statement: if (registered_functions()[serial].use_vector_args) { current_serial = serial; eval_result = ((eval_funcp_v)(registered_functions()[serial].eval_f))(seq); } else Similar hooks are added to functions GiNaC::function::eval,series,pderivative. What do you think? Can I can implement/test these modifications and send you them as a patch? Thanks, Pearu
On Mon, 20 Aug 2001, Pearu Peterson wrote:
What do you think? Can I can implement/test these modifications and send you them as a patch?
Meanwhile I implemented these hooks, and it works perfectly. Please find the patch attached to this message. To apply it, 'cd ginac/' and hit 'patch -p0 < function.pl.diff-0.9.3'. As a result, I can now construct GiNaC functions directly in Python. Here is an example of the corresponding Python session: import ginac class mysin: nparams = 1 def eval(self, x): return self(x, hold=1) def derivative(self, x, diff_param): return ginac.cos(x) f = ginac.build_function(mysin) x = ginac.symbol('x') print f(x) # outputs: mysin(x) print f(x).diff(x) # outputs: cos(x) I hope that you find my patch useful for creating interfaces between GiNaC and various scripting languages. Regards, Pearu
On Mon, 20 Aug 2001, Pearu Peterson wrote:
Meanwhile I implemented these hooks, and it works perfectly. Please find the patch attached to this message.
Thanks a lot for your patch, I am currently investigating it. This won't be finished today, hopefully tomorrow. Just one question: You know that this issue has been discussed already with no final consensus? The last rant is archived at <http://www.ginac.de/lists/ginac-devel/msg00259.html>. (I don't see clear yet. This email is just to make sure we don't miss any opportunity.) Regards -richy. -- Richard Kreckel <Richard.Kreckel@Uni-Mainz.DE> <http://wwwthep.physik.uni-mainz.de/~kreckel/>
On Mon, 20 Aug 2001, Richard B. Kreckel wrote:
Thanks a lot for your patch, I am currently investigating it. This won't be finished today, hopefully tomorrow. Just one question: You know that this issue has been discussed already with no final consensus? The last rant is archived at <http://www.ginac.de/lists/ginac-devel/msg00259.html>. (I don't see clear yet. This email is just to make sure we don't miss any opportunity.)
At the time I overlooked this message and then forgot, though it seemed relevant. However, my patch will not certainly solve your problem. In Python one can define functions on fly --- that is what you are trying to establish in C++, if I understand this correctly. My patch just helps connecting Python and GiNaC in such a way that the problem is transformed to the Python side where it is not an acctual problem anymore. Thanks, Pearu
On Mon, 20 Aug 2001, Richard B. Kreckel wrote:
On Mon, 20 Aug 2001, Pearu Peterson wrote:
Meanwhile I implemented these hooks, and it works perfectly. Please find the patch attached to this message.
Thanks a lot for your patch, I am currently investigating it. This won't be finished today, hopefully tomorrow. Just one question: You know that this issue has been discussed already with no final consensus? The last rant is archived at <http://www.ginac.de/lists/ginac-devel/msg00259.html>. (I don't see clear yet. This email is just to make sure we don't miss any opportunity.)
Okay, I've looked at Peru's patch now. It is indeed quite trivial. Let me restate it: where we have foofunction_eval(const ex &x, const ex &y) with a fixed number of arguments he proposes to add an alternative where there is foofunction_eval(exvector) directly, i.e. not to do the translating effort from seq[0] to x and seq[1] to y. His patch simply adds additional support for this into function.pl along the old named arguments. Interstingly, this seems to solve a bunch of problems of scripting-interface maintainers. Alex (and Christian): May I ask you for a braindump of yours as to why we haven't done this all the time? Is it just a matter of being able to access `x' and `y' inside the implementations of foofunction_eval() or were there any other less trivial considerations? (This way all the function.{h,cpp} being generated from function.pl would be unnecessary. Not that I want to throw it out, though...) Hasta luego -richy. -- Richard Kreckel <Richard.Kreckel@Uni-Mainz.DE> <http://wwwthep.physik.uni-mainz.de/~kreckel/>
participants (2)
-
Pearu Peterson
-
Richard B. Kreckel