These are chat archives for symengine/symengine

12th
May 2015
Sumith Kulal
@Sumith1896
May 12 2015 09:37
Done, sent a PR to planet python here: python/planet#47
Isuru Fernando
@isuruf
May 12 2015 09:53
@certik, I sent a PR for RealDouble class. #439. I did not add any automatic simplification of functions like sin, cos etc. What's the best way to keep it manageable?
I was thinking of an evaluator class which has methods for all the functions introduced. So if sin(x) is called where x is a RealDouble or any Number with is_real true, then x.get_evaluator is called to get an evaluator whose sin method will be called.
Ondřej Čertík
@certik
May 12 2015 18:10
@isuruf good points. Are you thinking of things like sin(0.0)?
Isuru Fernando
@isuruf
May 12 2015 18:11
yes
Ondřej Čertík
@certik
May 12 2015 18:11
The problem with double is that it is essentially never exact. In fact, the compiler gives you a warning if you do things like if (a == 0.0) where a is a double.
In other words, there would never be any simplifications.
Isuru Fernando
@isuruf
May 12 2015 18:12
I'm thinking sin(0.0) is evaluated to 0.0 instead of 0
Ondřej Čertík
@certik
May 12 2015 18:12
You compare doubles by the if (abs(a-5) < eps) idiom.
How do you test that you got 0.0 inside the sin?
Isuru Fernando
@isuruf
May 12 2015 18:13
It's not just 0.0, I'll check for all instances of RealDouble and call std::sin on it
Ondřej Čertík
@certik
May 12 2015 18:13

Here is a session in Sage:

sage: sin(1)
sin(1)
sage: sin(0)
0
sage: sin(0.0)
0.000000000000000
sage: sin(1.0)
0.841470984807897
sage: sin(1.03)
0.857298989188603

So Sage takes a double and just immediately evaluates the function using double precision. We can do the same.

Isuru Fernando
@isuruf
May 12 2015 18:14
Exactly
Ondřej Čertík
@certik
May 12 2015 18:14
Ok, so what's the problem?
Isuru Fernando
@isuruf
May 12 2015 18:15
How do I do it so that all functions are not needed to be updated each time I add a Real class like RealDouble, RealMPFR, etc.?
Ondřej Čertík
@certik
May 12 2015 18:15
Our sin C++ function, which decides what to do, would just call std::sin if the argument is RealDouble.
So essentially, your question is how to keep all this code local, as opposed to scattered all over symengine, correct?
Isuru Fernando
@isuruf
May 12 2015 18:16
yeah.
Ondřej Čertík
@certik
May 12 2015 18:16
I think the answer is to use the single dispatch, that we implemented.
So the sin C++ function would have an array of the callbacks, and it looks up the correct callback based on the argument.
So then the question is how to initialize this array of callbacks --- in eval_double we do it at one place, i.e. the code related to RealDouble would still be scattered all over symengine.
But I think we can write some tools, so that we can just register these callbacks from one file, realdouble.cpp.
In other words, the default callback table/array will be almost empty, or have some default implementation, and then later we add our custom RealDouble implementation from inside realdouble.cpp.
The only problem in C++ is that the translation units, e.g. realdouble.cpp, can be loaded in any order when the program starts, so there is a problem with initializing the global variables from more than one place.
Ondřej Čertík
@certik
May 12 2015 18:22
One workaround is to have "init" functions in symengine --- i.e. that the user would call some kind of symengine_init() function that would initialize all these internal single dispatch callback tables.
For example by calling realdouble_init()internally, and then inside this function you register the proper callbacks --- which would work, since all the translation units will be loaded already. But I don't like this too much, I don't think it's very nice to require the user to call these "initialize" functions.
Isuru Fernando
@isuruf
May 12 2015 18:26
How about the way we implemented eval_double_single_dispatch?
Ondřej Čertík
@certik
May 12 2015 18:31
eval_double_single_dispatch uses table_eval_double which is initialized using init_eval_double(). The last function contains all the implementations of the callbacks.
So we would have sin_init() (to initialize sin callbacks), cos_init(), tan_init(), ...., and inside each of these functions we need to register RealDouble callback and call std::sin, std::cos, std::tan, ...
So all these functions still need to be updated "each time I add a Real class like RealDouble, RealMPFR, etc.".
But I think that's fine, isn't it?
Isuru Fernando
@isuruf
May 12 2015 18:35
Yes.
Another approach would be to have a 2D table and call it like table[SIN][REAL_DOUBLE]
Ondřej Čertík
@certik
May 12 2015 18:37
Sure, I think that's essentially equivalent.
I don't know if we want to support that, but the single dispatch allows the user to register his callbacks for his custom classes as well --- essentially you just create a unique TypeID and append a callback into the std::vector, from user code. Things will just work.
Isuru Fernando
@isuruf
May 12 2015 18:48
Yes, we'll eventually want to support custom classes
Ondřej Čertík
@certik
May 12 2015 18:49
I would avoid the static initialization order fiasco for now, and just implement single dispatch as we did for eval_double. We can try to improve upon it later.
Isuru Fernando
@isuruf
May 12 2015 18:49
One more problem, there are some functions that have two arguments like atan2, lowergamma
Ondřej Čertík
@certik
May 12 2015 18:51
Full solution is "double dispatch"
I.e. dispatch on values of both arguments using a 2D array.
If we end up having ~300 types in symengine, then the 2D array will have 90000 elements, a pointer (8 bytes) each, so that's ~0.7MB per table/function. I think that's fine.
If the size is a problem, we can implement sparse storage (I assume most of the elements will be some kind of "default"), i.e. a little slower lookup for small memory footprint.