From kreckel at thep.physik.uni-mainz.de Fri Jun 1 20:26:28 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Fri, 1 Jun 2001 20:26:28 +0200 (CEST) Subject: Two little announcements Message-ID: Hi, First, I'ld like to point out that yesterday we have unleashed CLN-1.1.1 onto the world. No big changes, but a couple of important bugs were fixed that do affect GiNaC on non-Intel hardware and on the upcoming GCC-3.0. Oh, thank you very much for the applause! ;-) Second, I would like to move the ./cint subdirectory out of GiNaC. The reason is that Cint is too disappointing right now for GiNaC-cint to be really useful. I have here a nice list of open bugs that won't go away in the near future and that make using it a pain in the neck. The main problem is, however, Cint's portability: it wants to eat your harddisk on Linux/m68k, it has weird linking bugs on Linux/arm and despite my efforts to point out to the ROOT folks at Cern that they will be in a hell of a trouble soon if they don't work on the GCC-3.0 port nothing really happens on this front. (Try compiling Cint on GCC-3.0: it fails in a very spectacular way but doesn't even seem to notice so!) The real problem is of course less the new compiler bug the up-to-date STL. Cint's approach to STL based on ancient sources by HP definitely won't work much longer. Bottom line: the intersection of the set of platforms supported by CLN and GiNaC on the one side and of Cint on the other seems to be quite small. This makes the current situation a headache for distribution packagers: if you configure --with-cint you must exactly know your platform, specifying "Architecture: any" would otherwise work fine for both GiNaC and CLN. Instead, starting with GiNaC-0.9.0 we'll have a separate package GiNaC-cint that will be kicked along and updated when need should arise. Packagers can then throw this at their build-daemons and whatever comes out will probably `work', but that won't have any side-effects on the building of the GiNaC library. Sex, drugs and Unix -richy. -- Richard Kreckel From kreckel at thep.physik.uni-mainz.de Thu Jun 7 22:19:19 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Thu, 7 Jun 2001 22:19:19 +0200 (CEST) Subject: Release 0.9.0 just hit the web Message-ID: Go, grab your copy now, as long as supply lasts! Don't let others snatch away the few copies left at ! Hurry!!! Pronto!! ?Rapido! Los! The NEWS file says: * In the output and in ginsh, lists are now delimited by { } braces, and matrices are delimited by single [ ] brackets. * simplify_indexed() renames dummy indices so, e.g., "a.i*a.i+a.j*a.j" gets simplified to "2*a.i*a.i" (or "2*a.j*a.j"). * New functions/methods: - canonicalize_clifford() (helpful when comparing expressions containing Dirac matrices) - symmetrize() and antisymmetrize() - numer_denom() (return numerator and denominator in one call) - map() (apply function to subexpressions) - evalm() (evaluate sums, products and integer powers of matrices) * Added a new function match() for performing pattern matching. subs() and has() also accept patterns as arguments. A pattern can be any expression, optionally containing wildcard objects. These are constructed with the call "wild()" and are denoted as "$0", "$1" etc. in the output and in ginsh. * Positive integer powers of non-commutative expressions (except matrices) are automatically expanded. * Removed cint subdirectory, ginaccint is a separate package now due to packaging considerations. * Several little bugfixes. * More exclamation marks than ever in the announcement. From kreckel at thep.physik.uni-mainz.de Fri Jun 15 17:22:40 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Fri, 15 Jun 2001 17:22:40 +0200 (CEST) Subject: Macros are evil (tm) Message-ID: Hi all, This is gonna be long. If you just want to know what you soon need to do to your source files and are not interested in why, please jump to the end of this email. The usual way of implementing polymorphic methods accepting ex arguments in GiNaC involves checking the arguments for their types. This results in switch-like statements of the form ex mul::somemethod(const ex & other) { if (is_ex_of_type(add)) { // do this... } else if (is_ex_of_type(mul)) { // do that... } else { // do something else... } } Okay, language lawyers (*) usually construe those switch statements as bad. But since C++ does not have generic multiple dispatch the usual answer is that they offer some variation of home-grown double dispatch, maybe by bloating the add and mul classes with overloaded methods like this: class basic { // ... virtual ex somemethod(const add & other); virtual ex somemethod(const mul & other); ex somemethod(const ex & other); }; // same for add and mul mul::somemethod(const ex & other) { return ex.bp.somemethod(*this); } So the proper implementation is called by two subsequent lookups in the virtual function table. Nothing prohibits us from doing it this way in GiNaC but for the objects we are dealing with in a CAS so far we have always chosen the switch way of implementing and found this much more accessible and readable. Just consider the various print functions that recently were changed to accept an object of type print_context or derived to format the output properly. The different ways need to be implemented somewhere and why not deal with all of them in foo::print(). What is this guy ranting about?!? Who cares? Cool, if you don't care. The only thing that really bothered me so far is the use of macros at this place. The definition of is_of_type(obj, type) by a macro 1) lives outside the namespace and may collide some time, 2) accepts all kind of funny arguments with barely and possibility for compile-time checking, 3) does not allow overloading, so there is an is_of_type and another is_ex_of_type with the same semantics and 4) is generally crap with respect to readabilty and makes steam come out of the ears of language lawyers. A better approach would be to use a template here. We could write is_a(foo) where foo is either something derived from basic or an ex and it will produce the expected outcome. There will of course also be an is_exactly_a(bar) matching only tensors and not classes derived from tensor. We can implement it in exactly the same way as the macros were implmented, for instance like this: template inline bool is_a(const basic & obj) { return dynamic_cast(&obj)!=0; } template inline bool is_a(const ex & obj) { return is_a(*obj.bp); } The only cause for concern is template inline bool is_exactly_a(const class basic & obj) { const T foo; return foo.tinfo()==obj.tinfo(); } because it has to allocate a temporary. But this is not a big deal, since we are allowed to specify template specializations, for instance in file add.h we would write down template<> inline bool is_exactly_a(const basic & obj) { return obj.tinfo()==TINFO_add; } giving us all the performance back. We all know that "An Inline Function is As Fast As a Macro" (an actual section title in GCC's info page). So we should do it. Now. Surprise! The inliner in GCC-2.95 does some very poor job at flow analysis inside if statements when inlined functions return some boolean (or integer, no matter). Consider this code: struct ABC { virtual ~ABC() {} }; struct A : public ABC {}; template inline bool is_a(const ABC & obj) { return (dynamic_cast(&obj)!=0); } #define is_of_type(OBJ,TYPE) \ (dynamic_cast(&OBJ)!=0) #ifdef USE_MACRO int test(const ABC & e) { if (is_of_type(e,A)) return 1; return 0; } #else int test(const ABC & e) { if (is_a(e)) return 1; return 0; } #endif The compiler generates in the case where USE_MACRO is defined at preprocessing level: 00000000 : 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 08 sub $0x8,%esp 6: 8b 45 08 mov 0x8(%ebp),%eax 9: 85 c0 test %eax,%eax b: 74 28 je 35 d: 83 c4 f8 add $0xfffffff8,%esp 10: 50 push %eax 11: 68 00 00 00 00 push $0x0 12: R_386_32 ABC type_info function 16: 8b 10 mov (%eax),%edx 18: 03 02 add (%edx),%eax 1a: 50 push %eax 1b: 6a 01 push $0x1 1d: 68 00 00 00 00 push $0x0 1e: R_386_32 A type_info function 22: ff 72 04 pushl 0x4(%edx) 25: e8 fc ff ff ff call 26 26: R_386_PC32 __dynamic_cast 2a: 85 c0 test %eax,%eax 2c: 74 07 je 35 2e: b8 01 00 00 00 mov $0x1,%eax 33: eb 02 jmp 37 35: 31 c0 xor %eax,%eax 37: c9 leave 38: c3 ret while in the inline case it generates this entirely contorted code: 00000000 : 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 08 sub $0x8,%esp 6: 8b 45 08 mov 0x8(%ebp),%eax 9: 85 c0 test %eax,%eax b: 74 24 je 31 d: 83 c4 f8 add $0xfffffff8,%esp 10: 50 push %eax 11: 68 00 00 00 00 push $0x0 12: R_386_32 ABC type_info function 16: 8b 10 mov (%eax),%edx 18: 03 02 add (%edx),%eax 1a: 50 push %eax 1b: 6a 01 push $0x1 1d: 68 00 00 00 00 push $0x0 1e: R_386_32 A type_info function 22: ff 72 04 pushl 0x4(%edx) 25: e8 fc ff ff ff call 26 26: R_386_PC32 __dynamic_cast 2a: 85 c0 test %eax,%eax 2c: 0f 95 c0 setne %al 2f: eb 02 jmp 33 31: b0 00 mov $0x0,%al 33: 84 c0 test %al,%al 35: 75 09 jne 40 37: 31 c0 xor %eax,%eax 39: eb 0a jmp 45 3b: 90 nop 3c: 8d 74 26 00 lea 0x0(%esi,1),%esi 40: b8 01 00 00 00 mov $0x1,%eax 45: c9 leave 46: c3 ret This turned out to be the performance hammer of about 25% that I saw when I first tried substituting the macros by templates. And it also turns out that the new GCC-3.0 produces better and roughly equivalent code in both cases, the templated one being even slightly superior as far as I can see. So, this is what we'll do: (Remember that GCC-3.0 is going to be released today.) In all cases the old macros will be supplemented by the template functions and specializations for is_exactly_a<>() for all critical cases. Inside the library we'll stick with the macros for some while in the time critical parts until GCC-3.0 catches on. (BTW: GCC-3.0 produces code that is roughly 10%-30% faster at the GiNaC benchmarks. Rejoice and upgrade!) Eventually these macros will be entirely phased out. I just finished checking the changes in to CVS. In my applications I used a Perl script to convert from the macros to the new inline template functions which is supplied herewith WITHOUT ANY WARRANTY WHATSOEVER. It actually works for converting the GiNaC library but you should of course make a backup first. Once GiNaC 0.9.1 rolls out (or if you are running from CVS) please apply this converter to your source files. ----------8<------------------8<---------------------8<----------------- #! /usr/bin/perl -w my $file; my $tmpfile; if ($file = $ARGV[0]) { print STDERR "replacing in file $file\n"; $tmpfile = "tmp${file}tmp"; } else { print STDERR "*** no file given\n"; exit; } open (CPPFILE, $file) or die "Can't open source file: $!\n"; open (TMPFILE, "> $tmpfile") or die "Can't open temporary file: $!\n"; while ($_ = ) { # is_exactly_of_type(foo,bar) -> is_exactly_a(foo) s/is_exactly_of_type\(([\*\.a-zA-Z_0-9\(\)\[\]\+\-\>]+)[, ]+([a-zA-Z_0-9]+)\)/is_exactly_a\<$2\>\($1\)/g; # is_ex_exactly_of_type(foo,bar) -> is_exactly_a(foo) s/is_ex_exactly_of_type\(([\*\.a-zA-Z_0-9\(\)\[\]\+\-\>]+)[, ]+([a-zA-Z_0-9]+)\)/is_exactly_a\<$2\>\($1\)/g; # is_of_type(foo,bar) -> is_a(foo) s/is_of_type\(([\*\.a-zA-Z_0-9\(\)\[\]\+\-\>]+)[, ]+([a-zA-Z_0-9]+)\)/is_a\<$2\>\($1\)/g; # is_ex_of_type(foo,bar) -> is_a(foo) s/is_ex_of_type\(([\*\.a-zA-Z_0-9\(\)\[\]\+\-\>]+)[, ]+([a-zA-Z_0-9]+)\)/is_a\<$2\>\($1\)/g; print TMPFILE "$_"; } close CPPFILE; close TMPFILE; `mv $tmpfile $file`; ---------->8------------------>8--------------------->8----------------- Regards -richy. (*) "The first thing we do, let's kill all the language lawyers." Henry VI, part II, taken from TC++PL-3, chapt 2. -- Richard Kreckel From dhson at thep.physik.uni-mainz.de Mon Jun 18 15:15:17 2001 From: dhson at thep.physik.uni-mainz.de (Do Hoang Son) Date: Mon, 18 Jun 2001 15:15:17 +0200 (CEST) Subject: Pattern matching in GiNaC CVS In-Reply-To: <20010523232748.A9739@iphcip1.physik.uni-mainz.de> Message-ID: Hi Christian, Look at this from ginsh > b=log(x*y)*z; > c=subs(b,log($1)==Log($1)); unknown function 'Log' parse error at ) Of course, Log() is undefined one. It would be nice if such a substitution is accepted in ginsh. Cheers, Son From cbauer at student.physik.uni-mainz.de Mon Jun 18 15:26:59 2001 From: cbauer at student.physik.uni-mainz.de (Christian Bauer) Date: Mon, 18 Jun 2001 15:26:59 +0200 Subject: Pattern matching in GiNaC CVS In-Reply-To: References: Message-ID: <20010618152659.A10372@iphcip1.physik.uni-mainz.de> Hi! On Mon, Jun 18, 2001 at 03:15:17PM +0200, Do Hoang Son wrote: > > c=subs(b,log($1)==Log($1)); > unknown function 'Log' > ... > Of course, Log() is undefined one. Correct. So? Bye, Christian -- / Coding on PowerPC and proud of it \/ http://www.uni-mainz.de/~bauec002/ From dhson at thep.physik.uni-mainz.de Mon Jun 18 15:36:39 2001 From: dhson at thep.physik.uni-mainz.de (Do Hoang Son) Date: Mon, 18 Jun 2001 15:36:39 +0200 (CEST) Subject: Pattern matching in GiNaC CVS In-Reply-To: <20010618152659.A10372@iphcip1.physik.uni-mainz.de> Message-ID: Hi! On Mon, 18 Jun 2001, Christian Bauer wrote: > Hi! > > On Mon, Jun 18, 2001 at 03:15:17PM +0200, Do Hoang Son wrote: > > > c=subs(b,log($1)==Log($1)); > > unknown function 'Log' > > ... > > Of course, Log() is undefined one. > > Correct. So? It would be nice if such a substitution is accepted in ginsh. Son. From cbauer at student.physik.uni-mainz.de Mon Jun 18 15:41:33 2001 From: cbauer at student.physik.uni-mainz.de (Christian Bauer) Date: Mon, 18 Jun 2001 15:41:33 +0200 Subject: Pattern matching in GiNaC CVS In-Reply-To: References: Message-ID: <20010618154133.C10372@iphcip1.physik.uni-mainz.de> Hi! On Mon, Jun 18, 2001 at 03:36:39PM +0200, Do Hoang Son wrote: > > > > c=subs(b,log($1)==Log($1)); > > > unknown function 'Log' > > > ... > > > Of course, Log() is undefined one. > > > > Correct. So? > > It would be nice if such a substitution is accepted in ginsh. But there is no function named "Log" in GiNaC. What is ginsh supposed to do? Modify inifcns.h/.cpp and recompile GiNaC? Bye, Christian -- / Coding on PowerPC and proud of it \/ http://www.uni-mainz.de/~bauec002/ From dhson at thep.physik.uni-mainz.de Mon Jun 18 15:56:50 2001 From: dhson at thep.physik.uni-mainz.de (Do Hoang Son) Date: Mon, 18 Jun 2001 15:56:50 +0200 (CEST) Subject: Pattern matching in GiNaC CVS In-Reply-To: <20010618154133.C10372@iphcip1.physik.uni-mainz.de> Message-ID: On Mon, 18 Jun 2001, Christian Bauer wrote: > On Mon, Jun 18, 2001 at 03:36:39PM +0200, Do Hoang Son wrote: > > > > > c=subs(b,log($1)==Log($1)); > > > > unknown function 'Log' > > > > ... > > > > Of course, Log() is undefined one. > > > > > > Correct. So? > > > > It would be nice if such a substitution is accepted in ginsh. > > But there is no function named "Log" in GiNaC. What is ginsh supposed to do? > Modify inifcns.h/.cpp and recompile GiNaC? Hmmm, I don't know. Just look at Maple > c:=ln(x*y)*z; > subs(ln=foo,c); foo(x*y)*z or in Mathematica In[1]:= c=Log[x*y]*z Out[1]= z Log[x y] In[2]:= c//. Log[a__]-> foo[a] Out[2]= z foo[x y] There is not any problem there. One offent uses this trick to get rid of troubles that caused by a difined/internal functions. Don't tell me if it is stupid or not. Just want to know if this well known feature is possible to be implemented in ginsh? Son From kreckel at thep.physik.uni-mainz.de Mon Jun 18 16:04:03 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Mon, 18 Jun 2001 16:04:03 +0200 (CEST) Subject: Pattern matching in GiNaC CVS In-Reply-To: Message-ID: On Mon, 18 Jun 2001, Do Hoang Son wrote: [...] > Just want to know if this well known feature is possible to be implemented > in ginsh? Why ginsh? How on earth would you express this in C++? Regards -richy. -- Richard Kreckel From cbauer at student.physik.uni-mainz.de Mon Jun 18 16:04:06 2001 From: cbauer at student.physik.uni-mainz.de (Christian Bauer) Date: Mon, 18 Jun 2001 16:04:06 +0200 Subject: Pattern matching in GiNaC CVS In-Reply-To: References: Message-ID: <20010618160406.F10372@iphcip1.physik.uni-mainz.de> Hi! On Mon, Jun 18, 2001 at 03:56:50PM +0200, Do Hoang Son wrote: > Just want to know if this well known feature is possible to be implemented > in ginsh? This has nothing to do with ginsh. GiNaC itself doesn't implement arbitrarily named undefined functions. Bye, Christian -- / Coding on PowerPC and proud of it \/ http://www.uni-mainz.de/~bauec002/ From kreckel at thep.physik.uni-mainz.de Tue Jun 19 12:41:04 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Tue, 19 Jun 2001 12:41:04 +0200 (CEST) Subject: GiNaC/ginac symmetry.cpp symmetry.h In-Reply-To: <200106111949.f5BJnpR22934@doraemon.physik.uni-mainz.de> Message-ID: > Update of /home/cvs/GiNaC/ginac > > Added Files: > symmetry.cpp symmetry.h > Log Message: > introduced new class for constructing symmetry tree definitions This patch entirely breaks ginaccint! An analysis shows that along with a funny new bug it triggers four long-standing, many times reported and never truly fixed problems in Cint: 1) failure to recognize `unsigned' as `unsigned int', 2) occassional ommission of namespace qualifiers in generated G__cpp_foobar.C, 3) the whole std-container<..., malloc_alloc> disaster and 4) not standard-conforming *ancient* HP-derived STL. (*) The problem was isolated and reported but I don't hold my breath on this. I wonder how the Cern folks can be so proud of this product and I am now quite happy that we made the decision to separate ginaccint out of GiNaC. It only holds up development. I can hear Fermilab C++-hocho Walter Brown's upset voice: "If you have something that's broken, you either fix it or you throw it away!". Happy hacking -richy. (*) As an aside, I am under the impression that you can easily detect HP-derived headers on a compiler by triggering this bug: #include struct foo { list children; }; Incidentally, Micro$oft VC++-6.0 doesn't eat it for the same reason the HP-STL doesn't eat it. It's amazing how many compilers barf at this seemingly innocent piece of code and when you look at the list header you almost certainly find that it was forked from STL before it's development went over to SGI. -- Richard Kreckel From kreckel at thep.physik.uni-mainz.de Tue Jun 19 12:58:43 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Tue, 19 Jun 2001 12:58:43 +0200 (CEST) Subject: Pattern matching in GiNaC CVS In-Reply-To: Message-ID: On Mon, 18 Jun 2001, Do Hoang Son wrote: > Hmmm, I don't know. > Just look at Maple > > > c:=ln(x*y)*z; > > subs(ln=foo,c); > foo(x*y)*z > > or in Mathematica > > In[1]:= c=Log[x*y]*z > > Out[1]= z Log[x y] > > In[2]:= c//. Log[a__]-> foo[a] > > Out[2]= z foo[x y] > > There is not any problem there. > One offent uses this trick to get rid of troubles that caused by a > difined/internal functions. No, there is not any problem since you haven't really shown us anything that's going to happen later to this new foo() function. Remember last time we talked about such `obvious' transformations? They turned out to be blatant bugs in both Maple and Mathematica. Seriously: please send us a simple example where something like this is needed in order to get around divergences or such and we can discuss alternatives. Cheers -richy. -- Richard Kreckel From cbauer at student.physik.uni-mainz.de Tue Jun 19 13:14:21 2001 From: cbauer at student.physik.uni-mainz.de (Christian Bauer) Date: Tue, 19 Jun 2001 13:14:21 +0200 Subject: GiNaC/ginac symmetry.cpp symmetry.h In-Reply-To: References: Message-ID: <20010619131421.C1958@iphcip1.physik.uni-mainz.de> Hi! On Tue, Jun 19, 2001 at 12:41:04PM +0200, Richard B. Kreckel wrote: > 1) failure to recognize `unsigned' as `unsigned int', This is used in many places in GiNaC. Why has it not been a problem before? > #include > struct foo { list children; }; This is supposed to work? Methinks that to make a list, the compiler would need to know at least sizeof(foo) which it can't because the struct definition is not closed yet. Bye, Christian -- / Coding on PowerPC and proud of it \/ http://www.uni-mainz.de/~bauec002/ From kreckel at thep.physik.uni-mainz.de Tue Jun 19 13:22:57 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Tue, 19 Jun 2001 13:22:57 +0200 (CEST) Subject: GiNaC/ginac symmetry.cpp symmetry.h In-Reply-To: <20010619131421.C1958@iphcip1.physik.uni-mainz.de> Message-ID: On Tue, 19 Jun 2001, Christian Bauer wrote: > On Tue, Jun 19, 2001 at 12:41:04PM +0200, Richard B. Kreckel wrote: > > 1) failure to recognize `unsigned' as `unsigned int', > > This is used in many places in GiNaC. Why has it not been a problem before? It is a problem sometimes, then again it is not, then it is a warning... I don't know. > > #include > > struct foo { list children; }; > > This is supposed to work? Methinks that to make a list, the compiler > would need to know at least sizeof(foo) which it can't because the struct > definition is not closed yet. Nonsense. It's heavily used for building up all sorts of trees where it really becomes convenient. BTW, on those compilers plagued by the abovementioned problem `struct foo { vector children; };' works like a charm but according to your reasoning it would be a problem as well. Regards -richy. -- Richard Kreckel From stefanw at fis.unipr.it Fri Jun 22 17:51:20 2001 From: stefanw at fis.unipr.it (Stefan Weinzierl) Date: Fri, 22 Jun 2001 17:51:20 +0200 (CEST) Subject: Series expansion Message-ID: Gentlemen, what's wrong with the following code in GiNaC 0.8.3: int main() { using namespace GiNaC; varidx mu(symbol("mu"),4); symbol eps("eps"); ex g = 1 + eps;; cout << g.series(eps==0,5) << endl; ex h = 1 + eps*dirac_gamma(mu); cout << h.series(eps==0,5) << endl; return 0; } The program crashes when it tries to expand h. Is the series member function actually defined somewhere for an object like dirac_gamma ? Stefan From cbauer at student.physik.uni-mainz.de Fri Jun 22 20:54:42 2001 From: cbauer at student.physik.uni-mainz.de (Christian Bauer) Date: Fri, 22 Jun 2001 20:54:42 +0200 Subject: Series expansion In-Reply-To: References: Message-ID: <20010622205442.A3685@iphcip1.physik.uni-mainz.de> Hi! On Fri, Jun 22, 2001 at 05:51:20PM +0200, Stefan Weinzierl wrote: > Gentlemen, > what's wrong with the following code in GiNaC 0.8.3: > > [...] > ex h = 1 + eps*dirac_gamma(mu); > cout << h.series(eps==0,5) << endl; > [...] > > The program crashes when it tries to expand h. Someone set up us the bomb... It doesn't actually crash. It throws an exception that informs you that diracgamma doesn't have a derivative. This nonsense has been fixed in the CVS. Bye, Christian -- / Coding on PowerPC and proud of it \/ http://www.uni-mainz.de/~bauec002/ From kreckel at thep.physik.uni-mainz.de Sun Jun 24 15:48:46 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Sun, 24 Jun 2001 15:48:46 +0200 (CEST) Subject: Class function revisited: IMHO we are badly confused about two concepts here Message-ID: Dear all, In the meeting last friday I was announcing that we could easily have functions generated on the fly (as demanded by some people for doing clever tricks with manual cancellations of divergenct subterms). The issues involved were partly those raised by Roland Richter in . For those that were at the meeting: scratch my claim that it could easily be done. The idea was this: Have a functor class with an overloaded operator() to return the corresponding object of class GiNaC::function. There would have to be a number of such functors, all derived from a base class, say `fncall', with the proper argument number, basically: class fncall {}; class fncall_1 : public fncall { public: const function operator()(const ex & x1) const { return function(index, x1); } private: unsigned index; }; Remember, that objects of class function (we could call them pseudofunctions) are distinguised by the sequence of arguments they hold and their serial number. All we *want* is a clean way of writing `sin(x)', `x' being an arbitrary `ex'. Right now, this is being done by declaring a global function with the right function name: const function sin(const ex & x) { return function(index, x); } This is just a sort of entrance into the GiNaC pseudofunctions. The functor per se ideally suited to be another such entrance. Pseudofunctions are registered at program startup time. This is when their name, eval function, diff function etc. are set up in a static std::vector, subscripted by a plain unsigned which happens to correspond exactly to the index. In order to do this dynamically in a clean way, we would really have to abandon this static vector and instead set the data up in a std::map. Note that lookup-time in such a structure is not constant but rather logarithmic, however with a rather high offset penalty (since internally a map is a RB-tree which has left and right node pointer and an additional color state). It can still be done without any visible impact on efficiency. So, the new gateway into the pseudofunctions would defined as such: const fncall_1 sin = fncall_1(function::register_new(...... )); which still does not look very much different from the setup macro we use now. We can then still write sin(x), but now fncall_1::operator()(const ex &) is being called. It worked okay until I tried to roll this code in, when an old friend problem reappeared: The conflict of above object with this function: const numeric sin(const numeric & x); The most obvious solution out of this would be to remove these and instead add an const numeric operator()(const numeric &); to our fncall_1 class. However, the class cannot know which arithmetic function the object `sin' is supposed to call! We would have to set up the arithmetic function at construction of the `fncall' object. It could be done with passing a pointer to such a function. That pointer would then have to be stored twice: Once in the pseudofunction object corresponding to `sin' as its `evalf_funcp' (because class function can never call the object's `operator()(const numeric &)' -- the class function cannot know about it's programmatric entrance `sin'. And once again in the functor object `sin', so that the operator looks something like this: const numeric operatir()(const numeric &x) { if (arithmetic_funcp!=0) return arithmetic_funcp(x); throw (std::runtime_error("buaaaaaaahhhh")); } I hope everyone agrees that this is just crap. The problem is that the class doesn't know exactly what the object is supposed to do. So, alternatively, we could move that information directly inside the class by declaring one entrance class per entrance object, as such: class fncall_sin : public fncall { public: const function operator()(const ex & x) const { return function(index, x); } const function operator()(const numeric & x) const { return _numeric_sin(x); } private: unsigned index; }; In order to make this really sematically reasonable, however, fncall_sin would have to be a singleton class. Ugh! The way people define their own classes also becomes quite messy. So this isn't good either. The basic problem is always that we are mixing functions and pseudofunctions (objects of class function) in a non-orthogonal way. We could choose to disentangle the pseudofunctions from the arithmetic functions. There are really two different concepts here: Once, the symbolic pseudofunctions that may eventually `.hold()' and second the ones that are supposed to be doing something straightforwad, like mapping from numeric to numeric. We could wrap the latter ones into a namespace `arithmetic' sitting inside the overall namespace `GiNaC' and disambiguate the calls by using declarations. All functions which could ever make sense to be `.hold()' should be handled like this. This may include real(numeric) and imag(numeric) though they do not (yet?) have a corresponding real(ex) and imag(ex) but it would not include gcd(numeric, numeric) since that will never by treated as a symbolic function. I strongly invite you to look at the declarations in : It seems to me like there is a very clear cut between normal functions and such functions that may eventually lead to collisions with pseudofunctions! This seems to work so far and I really start to like the idea to some extent. However, there this is still not entirely satisfactory. There are two problems here, one is a permanent one, another one is a temporary one. First the temporary one: #include using namespace std; using namespace GiNaC; int main(void) { symbol x("x"); cout << sin(x) << endl; return 0; } Alas, we cannot do this yet. It breaks GCC-2.95.2 which treats `sin', `cos', etc. as mysterious internal entities: :6: first declared as `double sin (double)' here GiNaC/ginac/inifcns.h:41: also declared as `const GiNaC::fncall_1 GiNaC::sin' here The three-times-cursed GCC-2.96 is affected as well, GCC-3.0 and SGI's compiler on IRIX (which I love!) handle this one correct. Now the permanent problem: #include using namespace std; #include using namespace GiNaC; int main(void) { symbol x("x"); cout << sin(x) << endl; return 0; } This will always leave us with ambiguous usages of `sin' because the object clashes with the function definition in . It will, of course, also also clash if a using directive for GiNaC::arithmetic is specified. This can never work well with using directives, only with using declarations like using GiNaC::sin; by the language rules. To summarize: In my opinion, the whole confusion is caused by the fact that we hazardously mix functions with our concept of pseudofunctions. As long as we do not steer clear of the potential clashes triggered by this, we are going to have trouble. We should really start thinking about these two as totally different entities. Functions are well suited for arithmetic mappings, functors are excellently suited as entrances to our pseudofunctions, making them more flexible. But we must not mix these concepts and the language forces us to not mix their names. In the long run, we could consider making all entrances to pseudofunctions uppercase, thus writing Sin(x) to distinguish between these two concepts, but that will require some discussion. [Remember that GiNaC is Not a CAS and the wish to write sin(1.2) to return 0.932 and sin(x) to return sin(x) is conventional in CAS but we should be allowed to raise the question if such a wish can really be justified as orthogonal to system design.] Okay, and if anybody has another suggestion or a comment or a wish, I'ld very much like to hear about it. Cheers -richy. -- Richard Kreckel From stefanw at fis.unipr.it Mon Jun 25 16:47:28 2001 From: stefanw at fis.unipr.it (Stefan Weinzierl) Date: Mon, 25 Jun 2001 16:47:28 +0200 (CEST) Subject: expand() Message-ID: Hi Christian, thanks for your prompt answer from last Friday. Unfortunately I have another little problem: The following lines symbol eps("eps"); idx a(symbol("a"),8) ,b(symbol("b"),8), c(symbol("c"),8),d(symbol("d"),8); ex g = (1+eps*color_T(a)+pow(eps,2)*color_T(b))*(1-eps*color_T(c)+pow(eps,2)*color_T(d)); cout << expand(g) << endl; prints out eps^4*(T.b*T.d)+(T.a*T.d)*eps^3-eps^3*(T.b*T.c)-eps^2*(T.a*T.c) e.g. the terms which would have a "1" from either one of the two factors do not show up. Stefan From cbauer at student.physik.uni-mainz.de Mon Jun 25 17:35:30 2001 From: cbauer at student.physik.uni-mainz.de (Christian Bauer) Date: Mon, 25 Jun 2001 17:35:30 +0200 Subject: expand() In-Reply-To: References: Message-ID: <20010625173530.A19927@iphcip1.physik.uni-mainz.de> Hi! On Mon, Jun 25, 2001 at 04:47:28PM +0200, Stefan Weinzierl wrote: > ex g = (1+eps*color_T(a)+pow(eps,2)*color_T(b))*(1-eps*color_T(c)+pow(eps,2)*color_T(d)); > cout << expand(g) << endl; You have to use color_ONE() instead of just "1" where appropriate (terms of a sum must all be of the same type): ex g = (color_ONE()+eps*color_T(a)+pow(eps,2)*color_T(b)) * (color_ONE()-eps*color_T(c)+pow(eps,2)*color_T(d)); This will produce the right expression on expand(). Bye, Christian -- / Coding on PowerPC and proud of it \/ http://www.uni-mainz.de/~bauec002/ From stefanw at fis.unipr.it Tue Jun 26 15:00:42 2001 From: stefanw at fis.unipr.it (Stefan Weinzierl) Date: Tue, 26 Jun 2001 15:00:42 +0200 (CEST) Subject: expand() Message-ID: > You have to use color_ONE() instead of just "1" where appropriate (terms > of a sum must all be of the same type): > ex g = (color_ONE()+eps*color_T(a)+pow(eps,2)*color_T(b)) > * (color_ONE()-eps*color_T(c)+pow(eps,2)*color_T(d)); > This will produce the right expression on expand(). Hi Christian, thanks for your reply. This is certainly a workaround, but I'm not too comfortable with it. My original problem occured in a user-defined algebra, I took the colour matrices only as an example. The question is what "expand()" should return for an expression like ( 1 + A ) * B where A and B are in some algebra. You say, that all terms of a sum must be of the same type, so that would involve to define a unit element in the algebra called for example "ONE" and write ( ONE + A ) * B instead. This works. But in this case I would think it's better if ( 1 + A ) * B throws an exception instead of just returning A*B. Otherwise you introduce a possible source of errors which is hard to track in complex programs. Stefan From cbauer at student.physik.uni-mainz.de Tue Jun 26 17:24:32 2001 From: cbauer at student.physik.uni-mainz.de (Christian Bauer) Date: Tue, 26 Jun 2001 17:24:32 +0200 Subject: expand() In-Reply-To: References: Message-ID: <20010626172432.A7888@iphcip1.physik.uni-mainz.de> Hi! On Tue, Jun 26, 2001 at 03:00:42PM +0200, Stefan Weinzierl wrote: > You say, that all terms of a sum must be of the same type, so that would > involve to define a unit element in the algebra called for example "ONE" > and write > > ( ONE + A ) * B > > instead. This is how it is intended to be used (and also algebraically most correct). > But in this case I would think it's better if > > ( 1 + A ) * B > > throws an exception instead of just returning A*B. Maybe add::eval() should throw an exception as soon as it encounters a constant numeric term and a noncommutative object. This would be easy to implement. However, it gets worse when someone writes something like ( m + A ) * B instead of ( m*ONE + A ) * B where "m" is a symbol. In this case you may get totally screwed results because the "*" could end up being a _commutative_ product (this happens if add::return_type() sees the "m" first). To track these kind of errors would require the add class to check whether all terms really are of the same commutativity class, which could cause a severe slowdown (and a totally unnecessary one for people who only use commutative algebra). So it would never be perfect... Bye, Christian -- / Coding on PowerPC and proud of it \/ http://www.uni-mainz.de/~bauec002/ From cbauer at thep.physik.uni-mainz.de Thu Jun 28 14:17:03 2001 From: cbauer at thep.physik.uni-mainz.de (Christian Bauer) Date: Thu, 28 Jun 2001 14:17:03 +0200 Subject: GiNaC 0.9.1 released Message-ID: <20010628141703.A13085@higgs.physik.uni-mainz.de> MAIN SCREEN TURN ON. HOW ARE YOU GENTLEMEN!! WHAT HAPPEN? YOU ARE ON THE WAY TO NEW FEATURES: * Ctors of class numeric are not explicit any more. All built-in callers for pseudofunctions are now templated and default to ex arguments which relaxes the need for explicit ctors. * New functions/methods: - find() - remove_first(), remove_last(), sort() and unique() for lists - symmetrize_cyclic() - decomp_rational() * Instead of just totally symmetric or antisymmetric, complex symmetries can now be defined for indexed objects. Symmetries are described by a tree of "symmetry" objects that is constructed with the sy_none(), sy_symm(), sy_anti() and sy_cycl() functions. The symmetry of a function with respect to its arguments can also be defined (this is currently only used for the Beta function). * Generalized map() to take a function object instead of a function pointer. This allows passing an arbitrary number of additional state to the function being called. * color_trace(), dirac_trace(), diff(), expand(), evalf() and normal() work better with container classes, e.g. using color_trace() on a relation will take the trace on both sides, using diff() on a matrix differentiates every element etc. * diff() works properly with non-commutative products and indexed objects. * New option flag "expand_function_args" for expand(). * Supplement some (now deprecated) macros by inlined template functions: - is_of_type(foo, type) -> is_a(foo) - is_ex_of_type(foo, type) -> is_a(foo) - is_exaclty_of_type(foo, type) -> is_exaclty_a(foo) - is_ex_exaclty_of_type(foo, type) -> is_exaclty_a(foo) - ex_to_foobar(baz) -> ex_to(baz) * rem(c, p[x], x) (c: numeric, p[x]: polynomial) erroneously returned p[x] instead of c. * Small bugfixes in pattern matching. * Updated libtool to version 1.4. WHAT YOU SAY? DOWNLOAD EVERY 'GINAC': ftp://ftpthep.physik.uni-mainz.de/pub/GiNaC FOR GREAT JUSTICE, Christian -- / Coding on PowerPC and proud of it \/ http://www.uni-mainz.de/~bauec002/ From stefanw at fis.unipr.it Fri Jun 29 11:38:49 2001 From: stefanw at fis.unipr.it (Stefan Weinzierl) Date: Fri, 29 Jun 2001 11:38:49 +0200 (CEST) Subject: Wishlist Message-ID: Gentlemen, this mail is a little bit related to the mail from Richy last Friday (class function revisited ...). If you are planning to revise this issue, I have some suggestions what could be useful in the future. Suppose I use symbolic manipulations to arrive at a formula like ex f = sin(x) + tgamma(1+x) + pow(x,5) + more complicated stuff and I would like to do a Monte Carlo integration int( f , x=0..1) and I would like to get an accuracy of 2 or 3 digits in a reasonable amount of time. The fastest way would certainly be to print f as C-code, edit the file and compile it with the Monte-Carlo integration routine. But suppose I'm too lazy to do this print/edit/compile cycle. I just want to do in ONE program a) calculate the function by symbolic manipulations b) evaluate f a few thousand times. This will certainly be never as fast (in CPU time) as the print/edit/compile method, but if the additional CPU time is of the same size as the time I would need to edit and compile the thing, it would be more convenient. For point b) double precision would be more than enough. Now at the moment, some functions are evaluated in CLN with arbitrary precision. This is probably overkill. Worse, some functions (like tgamma) do not have a numerical evaluation at all at the moment. To implement a numerical evaluation for these functions to arbitrary precission requires a fair amount of work, but for double precision algorithms are likely to exist (for example the GNU Scientific Library). My suggestion would therefore be, -that class numeric gets a status flag, which signifies "only interested in double precision" - and class function gets a pointer "evalf_with_double_precision", which points to the user-supplied evaluation routine. What do you think ? Best wishes, Stefan From kreckel at thep.physik.uni-mainz.de Fri Jun 29 16:00:31 2001 From: kreckel at thep.physik.uni-mainz.de (Richard B. Kreckel) Date: Fri, 29 Jun 2001 16:00:31 +0200 (CEST) Subject: Wishlist In-Reply-To: Message-ID: Hi, Thanks very much for your suggestion, Stefan. On Fri, 29 Jun 2001, Stefan Weinzierl wrote: > this mail is a little bit related to the mail from Richy last Friday > (class function revisited ...). Well, I was almost beaten up by C.B. for my suggestion to write Sin(x)... > Suppose I use symbolic manipulations to arrive at a formula like > > ex f = sin(x) + tgamma(1+x) + pow(x,5) + more complicated stuff > > and I would like to do a Monte Carlo integration > > int( f , x=0..1) > > and I would like to get an accuracy of 2 or 3 digits in a reasonable > amount of time. > > The fastest way would certainly be to print f as C-code, edit the file > and compile it with the Monte-Carlo integration routine. Alternatively emit the file, automatically add the necessary boilerplate, compile it and link it back in using dlopen(3). On systems that support dlopen, such as Linux, with a little effort the whole procedure can be entirely autmated, as far as I can see. > But suppose I'm too lazy to do this print/edit/compile cycle. > I just want to do in ONE program > a) calculate the function by symbolic manipulations > b) evaluate f a few thousand times. > > This will certainly be never as fast (in CPU time) as the > print/edit/compile method, but if the additional CPU time is of the same > size as the time I would need to edit and compile the thing, it would be > more convenient. > > For point b) double precision would be more than enough. > Now at the moment, some functions are evaluated in CLN > with arbitrary precision. This is probably overkill. > > Worse, some functions (like tgamma) do not have a numerical evaluation > at all at the moment. > > > To implement a numerical evaluation for these functions to arbitrary > precission requires a fair amount of work, but for double precision > algorithms are likely to exist (for example the GNU Scientific Library). > > My suggestion would therefore be, > > -that class numeric gets a status flag, which signifies "only interested > in double precision" > > - and class function gets a pointer "evalf_with_double_precision", which > points to the user-supplied evaluation routine. I am not exactly sure how this is supposed to eventually work out. Remember that a datatype derived from basic has already a sizeof of at least 5 words (including vptr), i.e. 20 Bytes or so. If I multiply some of these I have several function calls and lots of code to roll throuh my CPU. Contrast this with an operation in double precision, which occupies two words for each operand and the operation is done without calls at the speed of hardware. Further, I have only vague ideas where this should be stored. CLN has several data types for fixed-sized floats -- short floats (cl_SF), floats (cl_FF), double floats (cl_DF) -- as well as one data type for arbitrary precision, cl_LF which is the one we are using. cl_SF (and on 64-bit architectures also cl_FF) are immediate, i.e. are not heap allocated. CLN does this by using a union representation that either holds a pointer to the heap or an immediate object and distinguishing between these by cleverly tagging some bits. Ideally, we would use this flexibility to store doubles in class `numeric'. This could probably be done, even without introducing a status flag since we could directly use the tests provided by CLN. Since a double is usually 64 bits, CLN unfortunately has no chance to do the same with machine doubles. But we use these in order to be able to support arbitrary precision! CLN has more than one implementation for each numerical function. In fact, it usually has four of them, one for each numerical class since it is braindead to trigger some infinite precision algorithm of sin(x) if only a fixed size float is requested. So, maybe what should be done is augment these functions by incomplete sets of quadruples? Given that there is no clear way of getting rid of the overall bloat, is it really worth doing this for a double since a directly compiled program will always execute 1000 times faster? I don't know... Regards -richy. -- Richard B. Kreckel From frink at thep.physik.uni-mainz.de Fri Jun 29 17:21:38 2001 From: frink at thep.physik.uni-mainz.de (Alexander Frink) Date: Fri, 29 Jun 2001 17:21:38 +0200 (CEST) Subject: Wishlist In-Reply-To: Message-ID: On Fri, 29 Jun 2001, Richard B. Kreckel wrote: > > Suppose I use symbolic manipulations to arrive at a formula like > > > > ex f = sin(x) + tgamma(1+x) + pow(x,5) + more complicated stuff > > > > and I would like to do a Monte Carlo integration > > > > int( f , x=0..1) > > > > and I would like to get an accuracy of 2 or 3 digits in a reasonable > > amount of time. I tried something similar in Maple about 3 years ago (for insiders: Dirk's integral representation for 2loop 2point functions), a 2fold Monte Carlo integration on a not-too-complex function (logs of rational arguments times a rational function). I rewrote Vegas in Maple and used its evalhf() function which evaluates expressions with the floating point hardware in double precision (because - again for insiders - xloops automatically emitted the function as a C program, compiled it, linked it with Vegas, spawned an external process and read the results from a file). I thought it would be faster to stay inside Maple, at least for some "preview mode". However Maple turned out to be unusably slow, IIRC a factor 500. I expect similar results for a GiNaC in double-precision mode. > Alternatively emit the file, automatically add the necessary boilerplate, > compile it and link it back in using dlopen(3). On systems that support > dlopen, such as Linux, with a little effort the whole procedure can be > entirely autmated, as far as I can see. I think it is worth writing a prototype for this and include it in the distribution (or at least documentation) if it is generic enough. cint can use a similar trick (#pragma compile). Alex -- Alexander Frink E-Mail: Alexander.Frink at Uni-Mainz.DE Institut fuer Physik Phone: +49-6131-3923391 Johannes-Gutenberg-Universitaet D-55099 Mainz, Germany