I gather that you can already tell us something about the speed of the pure-Python SymPy compared to GiNaC. We are so curious!
Well, it's slow. For example expanding (x+y)**10 takes 2s, (x+y)**20 takes 20s etc. In GiNaC if I remember correctly, you can easily expand (x+y)**300 or more in a moment. Python generally is around 2000 times slower than C/C++, but I think I am using some bad algorithm in expand(). On the other hand, I have a code for calculating a Riemann and Ricci tensor from a given metric in 4D, I tried that on the Schwarzschild solution of Einstein equations and it produces the correct differential equations in about 2s (and it can be done even faster). Doing this by hand would take me 3 hours. The metric tensor contains 2 unknown functions, I don't know if GiNaC can do it as well.
In the end, it all boils down to the question how much you are willing to do statically (at compile time) as opposed to dynamically (at run time). If you want the degree of flexibility of GiNaC or a language like Python that features duck typing, where x-x can evaluate to a simple number at run time, then I don't see how macros, templates, covariant return types, and all the tricks you can read about in books on C++ can ever be helpful. Think about the eval() trick as GiNaC's way of doing duck typing in the C++ language! Please drop me a note if you happen to know a more straightforward way.
I think all the simplification is happening at runtime in both GiNaC and SymPy. But in GiNaC you are concerned with speed, that's why it's not easy to say which solution is the best. For example I believe that GiNaC is slower than FORM, but GiNaC is certainly more flexible. In SymPy I decided I want to have the simplest code possible.
I may be very biased, but I see two ways for you: Stick with Python, live happy, and don't be concerned about an occasional performance drop. Or reimplement (a possibly better) GiNaC and do Python bindings (again). But implementing the core of SymPy in C++ just for the sake of speed is probably not worth worrying about.
That is of course correct. I also implemented symbolic limits, and SymPy can do almost all of them below 1s. Currently we are just playing with the interface and algorithms, so it certainly doesn't make sense to rewrite it in C++ now. I want to do it in the future though, but only in exactly the same simple way as in SymPy (otherwise I could use GiNaC directly) if it is possible and also because that would answer your question, if there is a more straightforward way than the GiNaC's way. I think there is - using the solution from my last email + a garbage collector in C++. This should make the code as simple as SymPy is - but it's probably going to be slower (but who knows).
But back to the original topic: I do hope that the sum-of-matrices evaluation thingie is no reason for despair. Way back, that was a deliberte decision. But maybe it was a mistake to place matrices in the type hierarchy at all. Maybe they would be less confusing if they were not subtypes of basic, managed by ex, but classes standing apart from the rest of the hierarchy. That would duplicate some code, however. From past discussions, my impression is that this idea would have some followers on this list.
Exactly. I decided not to subclass a Matrix class from the Basic from precisely these reasons. Cheers, Ondrej