PROPOSAL: Dynamic creation of functions

Pearu Peterson pearu at cens.ioc.ee
Mon Aug 20 13:31:55 CEST 2001


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






More information about the GiNaC-devel mailing list