From git at ginac.de Sat Aug 9 10:11:54 2008 From: git at ginac.de (Jens Vollinga) Date: Sat, 9 Aug 2008 10:11:54 +0200 (CEST) Subject: [GiNaC-devel] [SCM] GiNaC -- a C++ library for symbolic computations branch, master, updated. release_1-4-0-60-gd081503 Message-ID: <20080809081155.396E45B4062@cebix.net> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GiNaC -- a C++ library for symbolic computations". The branch, master has been updated via d08150300eb98c6435a4c464c057ba2967a19c8f (commit) via 114449ae6f2cd3151d9b8342c570db021a9e892c (commit) via 5e9875d5f67a68d2cb680454ff4de480bad1b6f1 (commit) from a7693a0f710b49494f95ce5a4a0953752e69c7f9 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit d08150300eb98c6435a4c464c057ba2967a19c8f Author: Jens Vollinga Date: Sat Aug 9 10:14:02 2008 +0200 Added polynomial factorization (univariate case). commit 114449ae6f2cd3151d9b8342c570db021a9e892c Author: Jens Vollinga Date: Sat Aug 9 10:11:40 2008 +0200 Added polynomial factorization (univariate case). commit 5e9875d5f67a68d2cb680454ff4de480bad1b6f1 Author: Alexei Sheplyakov Date: Wed Jul 30 20:32:46 2008 +0400 Any complex number can be (un)archived properly. A complex number can have an exact real part and inexact (floating point) imaginary part, and vice a versa. Handle these cases properly in the archiving code instead of bailing out with a bizzare error message. Thanks to Chris Bouchard for reporting the bug. NOTE: this fix changes the format of GiNaC archives, so formally it breaks the binary compatibility. However, "mixed" complex numbers (i.e. ones with exact real part and inexact imaginary part) can not be archived with previous versions of GiNaC, so there's nothing to break. ----------------------------------------------------------------------- Summary of changes: check/Makefile.am | 8 + check/exam_factor.cpp | 102 ++++ check/numeric_archive.cpp | 48 ++ configure.ac | 4 +- ginac/Makefile.am | 4 +- ginac/factor.cpp | 1221 ++++++++++++++++++++++++++++++++++++++++ ginac/{tostring.h => factor.h} | 25 +- ginac/ginac.h | 2 + ginac/numeric.cpp | 118 +++- ginsh/ginsh.1.in | 3 + ginsh/ginsh_parser.yy | 2 + 11 files changed, 1489 insertions(+), 48 deletions(-) create mode 100644 check/exam_factor.cpp create mode 100644 check/numeric_archive.cpp create mode 100644 ginac/factor.cpp copy ginac/{tostring.h => factor.h} (71%) hooks/post-receive -- GiNaC -- a C++ library for symbolic computations From jensv at nikhef.nl Mon Aug 11 11:41:09 2008 From: jensv at nikhef.nl (Jens Vollinga) Date: Mon, 11 Aug 2008 11:41:09 +0200 Subject: [GiNaC-devel] factorization and stuff Message-ID: <48A00935.70004@nikhef.nl> Hi, I am re-sending this email because it looks like the server problems this morning have swallowed the original posting. Hi, I recently checked in code for polynomial factorization. The multivariate case and maybe algebraic extensions are in the making. Some notes on the code: - The code is absolutely not optimized. Don't look at the code, please! - Alternative methods are missing yet. It is doing the factorization with only one combination of methods and there are a lot of (well, infinitely many) use cases where the code performs amazingly poor. - The code is debugged and it should give the right answer for a proper expressions. But handling of non-proper expressions is still missing. Proper ~ Polynomial with symbols. I want to have the code feature-complete first (because I need it for some other stuff), only then will I try to optimize the code and build in additional algorithms. But if you find a bug in the factorization now please tell me! I also recently updated the todo list on the GiNaC website. Well, factorization will be done, but for the other two new points I have done nothing yet and there is always the chance that I will do nothing in the future. If you have interest in working on them, just do so (but maybe tell me in advance). In case you are planning to add other new features, I could announce them on the todo list as well. @Alexei: if you want to repair that GCD and is_polynomial business go ahead! That would be great! Regards, Jens From kreckel at ginac.de Sun Aug 17 22:03:26 2008 From: kreckel at ginac.de (Richard B. Kreckel) Date: Sun, 17 Aug 2008 22:03:26 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48A00935.70004@nikhef.nl> References: <48A00935.70004@nikhef.nl> Message-ID: <48A8840E.8010208@ginac.de> Hi! Out of curiosity I had a short look at the code. One question: what is factor_sqrfree(const ex&) actually doing? As far as I can tell, it s not squarefree factorization of univariate polynomials. Is it? (I know it's static and inside an anonymous namespace so I'm not supposed to use it, but still.) Best wishes -richy. -- Richard B. Kreckel From git at ginac.de Sun Aug 17 22:10:19 2008 From: git at ginac.de (Richard B. Kreckel) Date: Sun, 17 Aug 2008 22:10:19 +0200 (CEST) Subject: [GiNaC-devel] [SCM] GiNaC -- a C++ library for symbolic computations branch, ginac_1-4, updated. release_1-4-0-54-ge6caa20 Message-ID: <20080817201020.9325E5B4046@cebix.net> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GiNaC -- a C++ library for symbolic computations". The branch, ginac_1-4 has been updated via e6caa2040557bdaaf2262afc0bb47dddd88e38cd (commit) from 1043ee1e38b7a70e737870e6b87f07607c0e9639 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit e6caa2040557bdaaf2262afc0bb47dddd88e38cd Author: Alexei Sheplyakov Date: Wed Jul 30 20:32:46 2008 +0400 Any complex number can be (un)archived properly. A complex number can have an exact real part and inexact (floating point) imaginary part, and vice a versa. Handle these cases properly in the archiving code instead of bailing out with a bizzare error message. Thanks to Chris Bouchard for reporting the bug. NOTE: this fix changes the format of GiNaC archives, so formally it breaks the binary compatibility. However, "mixed" complex numbers (i.e. ones with exact real part and inexact imaginary part) can not be archived with previous versions of GiNaC, so there's nothing to break. ----------------------------------------------------------------------- Summary of changes: check/Makefile.am | 4 ++ check/numeric_archive.cpp | 48 ++++++++++++++++++ configure.ac | 4 +- ginac/numeric.cpp | 118 ++++++++++++++++++++++++++++++++++----------- 4 files changed, 143 insertions(+), 31 deletions(-) create mode 100644 check/numeric_archive.cpp hooks/post-receive -- GiNaC -- a C++ library for symbolic computations From kreckel at ginac.de Sun Aug 17 22:48:37 2008 From: kreckel at ginac.de (Richard B. Kreckel) Date: Sun, 17 Aug 2008 22:48:37 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48A00935.70004@nikhef.nl> References: <48A00935.70004@nikhef.nl> Message-ID: <48A88EA5.4040901@ginac.de> Hi! Jens Vollinga wrote: > I want to have the code feature-complete first (because I need it for > some other stuff), only then will I try to optimize the code and build > in additional algorithms. But if you find a bug in the factorization now > please tell me! Well, sometimes it fails to factor. In ginsh: > factor(expand((x+1)*(x+4))); 4+x^2+5*x -richy. -- Richard B. Kreckel From git at ginac.de Mon Aug 18 21:08:58 2008 From: git at ginac.de (Jens Vollinga) Date: Mon, 18 Aug 2008 21:08:58 +0200 (CEST) Subject: [GiNaC-devel] [SCM] GiNaC -- a C++ library for symbolic computations branch, master, updated. release_1-4-0-61-gf3eefbd Message-ID: <20080818190858.DCE215B4040@cebix.net> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GiNaC -- a C++ library for symbolic computations". The branch, master has been updated via f3eefbda588318e09dcb3180c6f039dc3fc30f87 (commit) from d08150300eb98c6435a4c464c057ba2967a19c8f (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit f3eefbda588318e09dcb3180c6f039dc3fc30f87 Author: Jens Vollinga Date: Mon Aug 18 21:10:48 2008 +0200 Fixed bug in modular square-free factorization. ----------------------------------------------------------------------- Summary of changes: ginac/factor.cpp | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) hooks/post-receive -- GiNaC -- a C++ library for symbolic computations From jensv at nikhef.nl Mon Aug 18 21:46:23 2008 From: jensv at nikhef.nl (Jens Vollinga) Date: Mon, 18 Aug 2008 21:46:23 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48A88EA5.4040901@ginac.de> References: <48A00935.70004@nikhef.nl> <48A88EA5.4040901@ginac.de> Message-ID: <48A9D18F.1070804@nikhef.nl> Hi, Richard B. Kreckel schrieb: > Well, sometimes it fails to factor. In ginsh: >> factor(expand((x+1)*(x+4))); > 4+x^2+5*x thanks! I fixed this bug. Your bug report is probably connected to your question: > Out of curiosity I had a short look at the code. One question: what > is factor_sqrfree(const ex&) actually doing? As far as I can tell, it > s not squarefree factorization of univariate polynomials. Is it? (I > know it's static and inside an anonymous namespace so I'm not > supposed to use it, but still.) It is the modular square-free factorization, but it had a bug ... (the counter for the exponent of a possible factor was not always increased, so the modular sqrfree factorization of your polynomial was (x+1)^1 and not (x+1)^2; also a check for that exponent was missing at another place). Since you already looked at the code I should maybe comment on it with respect to cln: there is quite some stuff in factor.cpp that actually could be part of cln. It is mainly the type/class for modular univariate polynomials. If you compare the features of my class with the equivalent type in cln you can see why I needed this additional type. My plan was/is to finish first the code (including the multivariate case and additional algorithms for the univariate case) in GiNaC and to see exactly what features such a type needs, before I then ask the friendly cln developers to consider upgrading their type. :-) Regards, Jens From kreckel at ginac.de Tue Aug 19 00:00:10 2008 From: kreckel at ginac.de (Richard B. Kreckel) Date: Tue, 19 Aug 2008 00:00:10 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48A9D18F.1070804@nikhef.nl> References: <48A00935.70004@nikhef.nl> <48A88EA5.4040901@ginac.de> <48A9D18F.1070804@nikhef.nl> Message-ID: <48A9F0EA.6010807@ginac.de> Hi! Jens Vollinga wrote: > Richard B. Kreckel schrieb: >> Well, sometimes it fails to factor. In ginsh: >>> factor(expand((x+1)*(x+4))); >> 4+x^2+5*x > > thanks! I fixed this bug. Indeed. Thanks! Now, again in ginsh notation: > factor(expand((x+4+x^2-x^3+43*x^4)*(x+1-x^2-3*x^3+4*x^4))); Segmentation fault > Since you already looked at the code I should maybe comment on it with > respect to cln: there is quite some stuff in factor.cpp that actually > could be part of cln. It is mainly the type/class for modular univariate > polynomials. If you compare the features of my class with the equivalent > type in cln you can see why I needed this additional type. Well, I don't see. :-( What is your point? Cheers -richy. -- Richard B. Kreckel From varg at theor.jinr.ru Tue Aug 19 15:16:00 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Tue, 19 Aug 2008 17:16:00 +0400 Subject: [GiNaC-devel] [PATCH] [BUGFIX] Reclaiming the memory allocated for static objects *is* necessary. Message-ID: <20080819131600.GA10408@theor.jinr.ru> GiNaC allocates memory for static objects (i.e. flyweights, remember tables, etc), but doesn't free it. This is OK if the program lifetime matches libginac lifetime, since the OS will reclaim that memory anyway. However, if the program lifetime is different from that of libginac, this turns into a memory leak. This happens if someone dlopen's libginac.so, and dlclose's it later on (read: if someone uses GiNaC via scripting language bindings). symbol::autoname_prefix(): there's no need for dynamical memory allocation. remember_table::remember_tables(): likewise. function::registered_functions(): likewise. lib_init::~lib_init(): if library usage count drops to 0, reclaim the memory allocated for flyweights. --- ginac/function.pl | 4 +- ginac/remember.cpp | 4 +- ginac/symbol.cpp | 6 ++-- ginac/utils.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/ginac/function.pl b/ginac/function.pl index 3e44735..7a4d5e7 100644 --- a/ginac/function.pl +++ b/ginac/function.pl @@ -1330,8 +1330,8 @@ ${power_switch_statement} std::vector & function::registered_functions() { - static std::vector * rf = new std::vector; - return *rf; + static std::vector rf = std::vector(); + return rf; } bool function::lookup_remember_table(ex & result) const diff --git a/ginac/remember.cpp b/ginac/remember.cpp index faa89d3..701a27e 100644 --- a/ginac/remember.cpp +++ b/ginac/remember.cpp @@ -183,8 +183,8 @@ void remember_table::init_table() std::vector & remember_table::remember_tables() { - static std::vector * rt = new std::vector; - return *rt; + static std::vector rt = std::vector(); + return rt; } } // namespace GiNaC diff --git a/ginac/symbol.cpp b/ginac/symbol.cpp index f1b2be6..e56ade3 100644 --- a/ginac/symbol.cpp +++ b/ginac/symbol.cpp @@ -340,10 +340,10 @@ void symbol::unassign() /** Symbols not constructed with a string get one assigned using this * prefix and a number. */ -std::string & symbol::autoname_prefix() +std::string& symbol::autoname_prefix() { - static std::string *s = new std::string("symbol"); - return *s; + static std::string s("symbol"); + return s; } /** Return default TeX name for symbol. This recognizes some greek letters. */ diff --git a/ginac/utils.cpp b/ginac/utils.cpp index b55948b..f44ed9f 100644 --- a/ginac/utils.cpp +++ b/ginac/utils.cpp @@ -388,9 +388,58 @@ library_init::library_init() library_init::~library_init() { if (--count==0) { - // In theory, we would have to clean up here. But since we were - // only initializing memory in the ctor and that memory is reclaimed - // anyways by the OS when the program exits, we skip this. + // It's really necessary to clean up, since the program + // lifetime might not be the same as libginac.{so,dll} one + // (e.g. consider // dlopen/dlsym/dlclose sequence). + delete _num120_p; + delete _num_120_p; + delete _num60_p; + delete _num_60_p; + delete _num48_p; + delete _num_48_p; + delete _num30_p; + delete _num_30_p; + delete _num25_p; + delete _num_25_p; + delete _num24_p; + delete _num_24_p; + delete _num20_p; + delete _num_20_p; + delete _num18_p; + delete _num_18_p; + delete _num15_p; + delete _num_15_p; + delete _num12_p; + delete _num_12_p; + delete _num11_p; + delete _num_11_p; + delete _num10_p; + delete _num_10_p; + delete _num9_p; + delete _num_9_p; + delete _num8_p; + delete _num_8_p; + delete _num7_p; + delete _num_7_p; + delete _num6_p; + delete _num_6_p; + delete _num5_p; + delete _num_5_p; + delete _num4_p; + delete _num_4_p; + delete _num3_p; + delete _num_3_p; + delete _num2_p; + delete _num_2_p; + delete _num1_p; + delete _num_1_p; + delete _num1_2_p; + delete _num_1_2_p; + delete _num1_3_p; + delete _num_1_3_p; + delete _num1_4_p; + delete _num_1_4_p; + delete _num0_p; } } -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Tue Aug 19 20:09:40 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Tue, 19 Aug 2008 22:09:40 +0400 Subject: [GiNaC-devel] [PATCH] Faster, better (recursive descent) expression parser. Message-ID: <20080819180940.GA22026@theor.jinr.ru> bison generated parser has a number of problems: 1. Bad performance. Parsing a sum seems to be O(N^{2 + a}) (a > 0) [1]. For example, parsing a sum (actually, a univariate polynomial) of 32768 terms takes about 90 sec. on my box, parsing a sum of 10^6 terms takes "eternity". 2. The user is expected to provide list of all symbols in the expression. Often this is very annoying (and useless). 3. Parser is not reentrant (bison *can* produce reentrant parsers, but that won't solve other problems). 4. Parser is difficult to extend. Hence the new parser. Features: 1. Parsing large sums and products is O(N). 2. Parser is reentrant (well, almost). 3. It's possible to insert (shell style) comments inside the expressions. 4. matrices, lists, FAIL are NOT handled. Yes, this *is* a feature :-) Limitations: 1. Error handling is a bit terse: on error exception is thrown, and that's it. 2. Binary, octal, and hexadecimal numbers can not be parsed (yet). 3. Tensors, noncommutative products, etc. can not be parsed. Other notes: 1. ex ctor still uses the old parser. 2. ginsh still uses the old parser. [1] Mesured by this script (requires gnuplot): make_expression_string () { printf "1 + x" local n=2 while test $n -le $1; do printf " + %s*x^%s" $n $n n=$(expr $n + 1) done } single_test () { printf "$1 \t" ( printf "time("; make_expression_string $1; printf ");" ) | \ ginsh | sed -e 's/s$//' } benchmark () { local N=$1 while test $N -le $2; do single_test $N N=$(expr $N \* 2) done } gnuplot_header () { echo "set logscale xy" echo "set xlabel 'degree (# of terms)'" echo "set ylabel 'time, sec.'" echo "set xrange [${1}:${2}]" echo "plot '-' using 1:2 with lines title '1+x+2*x^2+...+n*x^n'" } gnuplot_footer () { echo "e" } benchmark_and_plot () { ( gnuplot_header $1 $2 benchmark $1 $2 | tee $3.txt gnuplot_footer ) | \ gnuplot -persist '-' } N_ini=${1:-1024} N_fin=${2:-32768} out_base=${3:-parser_benchmark} benchmark_and_plot $N_ini $N_fin $out_base --- INSTALL | 3 +- check/Makefile.am | 8 ++- check/time_parser.cpp | 79 +++++++++++++++++ configure.ac | 4 + ginac/Makefile.am | 31 ++++++- ginac/parser/builtin_fcns.def | 90 +++++++++++++++++++ ginac/parser/debug.hpp | 35 ++++++++ ginac/parser/default_reader.tpl | 47 ++++++++++ ginac/parser/lexer.cpp | 132 ++++++++++++++++++++++++++++ ginac/parser/lexer.hpp | 45 ++++++++++ ginac/parser/parse_binop_rhs.cpp | 179 ++++++++++++++++++++++++++++++++++++++ ginac/parser/parse_context.cpp | 27 ++++++ ginac/parser/parse_context.hpp | 82 +++++++++++++++++ ginac/parser/parser.cpp | 172 ++++++++++++++++++++++++++++++++++++ ginac/parser/parser.hpp | 99 +++++++++++++++++++++ 15 files changed, 1028 insertions(+), 5 deletions(-) create mode 100644 check/time_parser.cpp create mode 100644 ginac/parser/builtin_fcns.def create mode 100644 ginac/parser/debug.hpp create mode 100644 ginac/parser/default_reader.tpl create mode 100644 ginac/parser/lexer.cpp create mode 100644 ginac/parser/lexer.hpp create mode 100644 ginac/parser/parse_binop_rhs.cpp create mode 100644 ginac/parser/parse_context.cpp create mode 100644 ginac/parser/parse_context.hpp create mode 100644 ginac/parser/parser.cpp create mode 100644 ginac/parser/parser.hpp diff --git a/INSTALL b/INSTALL index b542d9e..1cad277 100644 --- a/INSTALL +++ b/INSTALL @@ -30,7 +30,8 @@ Known not to work with: is missing there. If you install from CVS, you also need GNU autoconf (>=2.59), automake (>=1.7), -libtool (>= 1.5), bison (>= 2.3), flex (>= 2.5.33) to be installed. +libtool (>= 1.5), bison (>= 2.3), flex (>= 2.5.33), autogen (>= 5.6.0) to be +installed. INSTALLATION diff --git a/check/Makefile.am b/check/Makefile.am index a34bb9f..b656c63 100644 --- a/check/Makefile.am +++ b/check/Makefile.am @@ -50,7 +50,8 @@ TIMES = time_dennyfliegner \ time_lw_Q \ time_lw_Qprime \ time_antipode \ - time_fateman_expand + time_fateman_expand \ + time_parser TESTS = $(CHECKS) $(EXAMS) $(TIMES) check_PROGRAMS = $(CHECKS) $(EXAMS) $(TIMES) @@ -227,6 +228,11 @@ time_fateman_expand_SOURCES = time_fateman_expand.cpp \ randomize_serials.cpp timer.cpp timer.h time_fateman_expand_LDADD = ../ginac/libginac.la +time_parser_SOURCES = time_parser.cpp \ + randomize_serials.cpp timer.cpp timer.h +time_parser_LDADD = ../ginac/libginac.la + + AM_CPPFLAGS = -I$(srcdir)/../ginac -I../ginac CLEANFILES = exam.gar diff --git a/check/time_parser.cpp b/check/time_parser.cpp new file mode 100644 index 0000000..ca35fb6 --- /dev/null +++ b/check/time_parser.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ginac.h" +#include "parser/parser.hpp" +#include "timer.h" +extern void randomify_symbol_serials(); +using namespace std; +using namespace GiNaC; + +/// make a string "1+x+2*x^2+...+n*x^n" +static string prepare_str(const unsigned n, const char x = 'x') +{ + ostringstream s; + s << x; + for (unsigned i = 2; i < n; i++) + s << '+' << i << '*' << x << '^' << i; + return s.str(); +} + +void benchmark_and_cmp(const string& srep, double& t_new, double& t_old) +{ + parser the_parser; + timer RSD10; + RSD10.start(); + ex e = the_parser(srep); + t_new = RSD10.read(); + RSD10.stop(); + if (t_new > 2.0) + cout << '.' << flush; + + symtab syms = the_parser.get_syms(); + const symbol x = find_or_insert_symbol("x", syms, true); + lst sl; + sl = x; + RSD10.start(); + ex e2(srep, sl); + t_old = RSD10.read(); + + if (t_old > 2.0) + cout << '.' << flush; + + ex dif = (e - e2).expand(); + if (!dif.is_zero()) { + cerr << "Got a difference: " << dif << endl; + throw std::logic_error("New and old parser give different results"); + } +} + +int main(int argc, char** argv) +{ + cout << "timing GiNaC parser..." << flush; + randomify_symbol_serials(); + unsigned n_min = 1024; + unsigned n_max = 32768; + if (argc > 1) + n_max = atoi(argv[1]); + + vector times_new, times_old; + vector ns; + for (unsigned n = n_min; n <= n_max; n = n << 1) { + double t_new, t_old; + string srep = prepare_str(n); + benchmark_and_cmp(srep, t_new, t_old); + times_new.push_back(t_new); + times_old.push_back(t_old); + ns.push_back(n); + } + + cout << "OK" << endl; + cout << "# terms new parser, s old parser, s" << endl; + for (size_t i = 0; i < times_new.size(); i++) + cout << " " << ns[i] << '\t' << times_new[i] << '\t' << times_old[i] << endl; + return 0; +} diff --git a/configure.ac b/configure.ac index e4a1e60..22f9c96 100644 --- a/configure.ac +++ b/configure.ac @@ -149,6 +149,10 @@ AM_CONDITIONAL(CONFIG_TEX, [test ! \( -z "$LATEX" -o -z $"PDFLATEX" -o -z "$MAKE AC_PATH_PROG(FIG2DEV, fig2dev, "") AM_CONDITIONAL(CONFIG_FIG2DEV, [test ! -z "$FIG2DEV"]) +dnl generate boilerplate code for the (new) parser. +dnl Only developers need this tool. +AC_PATH_PROG(AUTOGEN, autogen, "") + dnl Output makefiles etc. AC_CONFIG_FILES([ Makefile diff --git a/ginac/Makefile.am b/ginac/Makefile.am index d2ef614..0570fde 100644 --- a/ginac/Makefile.am +++ b/ginac/Makefile.am @@ -9,7 +9,15 @@ libginac_la_SOURCES = add.cpp archive.cpp basic.cpp clifford.cpp color.cpp \ operators.cpp power.cpp registrar.cpp relational.cpp remember.cpp \ pseries.cpp print.cpp symbol.cpp symmetry.cpp tensor.cpp \ utils.cpp wildcard.cpp input_parser.yy input_lexer.ll \ - input_lexer.h remember.h tostring.h utils.h compiler.h + input_lexer.h remember.h tostring.h utils.h compiler.h \ + parser/parse_binop_rhs.cpp \ + parser/parser.cpp \ + parser/parse_context.cpp \ + parser/builtin_fcns.cpp \ + parser/lexer.cpp \ + parser/lexer.hpp \ + parser/debug.hpp + libginac_la_LDFLAGS = -version-info $(LT_VERSION_INFO) -release $(LT_RELEASE) libginac_la_LIBADD = $(DL_LIBS) ginacincludedir = $(includedir)/ginac @@ -18,10 +26,27 @@ ginacinclude_HEADERS = ginac.h add.h archive.h assertion.h basic.h class_info.h exprseq.h fail.h factor.h fderivative.h flags.h function.h hash_map.h idx.h indexed.h \ inifcns.h integral.h lst.h matrix.h mul.h ncmul.h normal.h numeric.h operators.h \ power.h print.h pseries.h ptr.h registrar.h relational.h structure.h \ - symbol.h symmetry.h tensor.h version.h wildcard.h + symbol.h symmetry.h tensor.h version.h wildcard.h \ + parser/parser.hpp \ + parser/parse_context.hpp + AM_LFLAGS = -Pginac_yy -olex.yy.c AM_YFLAGS = -p ginac_yy -d -EXTRA_DIST = function.pl input_parser.h version.h.in +EXTRA_DIST = function.pl input_parser.h version.h.in \ +parser/default_reader.tpl parser/builtin_fcns.def + +# Files produced by autogen(1) from templates +$(srcdir)/parser/builtin_fcns.cpp: $(srcdir)/parser/builtin_fcns.def $(srcdir)/parser/default_reader.tpl + set -e; if [ -n "$(AUTOGEN)" ]; then \ + cd $(srcdir)/parser; \ + $(AUTOGEN) -T default_reader.tpl builtin_fcns.def; \ + elif [ -f $@ ]; then \ + echo "WARNING: AutoGen is not available, the \"$@\" file WON'T be re-generated"; \ + else \ + echo "*** ERROR: the \"$@\" file does not exist, and AutoGen is not installed on your system"; \ + echo "*** Please install AutoGen (http://www.gnu.org/software/autogen)"; \ + fi + # Files which are generated by perl scripts $(srcdir)/function.h $(srcdir)/function.cpp: $(srcdir)/function.pl diff --git a/ginac/parser/builtin_fcns.def b/ginac/parser/builtin_fcns.def new file mode 100644 index 0000000..897b1f7 --- /dev/null +++ b/ginac/parser/builtin_fcns.def @@ -0,0 +1,90 @@ +Autogen definitions ginacfcns; + +function = { name = "log"; }; +function = { name = "exp"; }; +function = { name = "sin"; }; +function = { name = "cos"; }; +function = { name = "tan"; }; +function = { name = "asin"; }; +function = { name = "acos"; }; +function = { name = "atan"; }; + +function = { name = "sinh"; }; +function = { name = "cosh"; }; +function = { name = "tanh"; }; +function = { name = "asinh"; }; +function = { name = "acosh"; }; +function = { name = "atanh"; }; + +function = { + name = "atan2"; + args = 2; +}; + +function = { + name = "Li2"; + comment = "Dilogarithm"; +}; + +function = { + name = "Li3"; + comment = "Trilogarithm"; +}; + +function = { + name = "zetaderiv"; + comment = "Derivatives of Riemann's Zeta-function"; + args = 2; +}; + +function = { + name = "Li"; + args = 2; + comment = "Polylogarithm and multiple polylogarithm"; +}; + +function = { + name = "S"; + args = 3; + comment = "Nielsen's generalized polylogarithm"; +}; + +function = { + name = "H"; + args = 2; + comment = "Harmonic polylogarithm"; +}; + +function = { name = "lgamma"; }; +function = { name = "tgamma"; }; + +function = { + name = "beta"; + args = 2; + comment = "Beta-function"; +}; + +function = { name = "factorial"; }; + +function = { + name = "binomial"; + args = 2; +}; + +function = { + name = "Order"; + comment = "Order term function (for truncated power series)"; +}; + +/* Thease are not functions, but anyway ... */ +function = { name = "sqrt"; }; + +function = { + name = "pow"; + args = 2; +}; + +function = { + name = "power"; + args = 2; +}; diff --git a/ginac/parser/debug.hpp b/ginac/parser/debug.hpp new file mode 100644 index 0000000..a43cc7a --- /dev/null +++ b/ginac/parser/debug.hpp @@ -0,0 +1,35 @@ +#ifndef GINAC_PARSER_DEBUG_HPP +#define GINAC_PARSER_DEBUG_HPP +#include +#include +#include +#include "compiler.h" +#ifndef __GNUC__ +#if __STDC_VERSION__ < 199901L +#define __PRETTY_FUNCTION__ "" +#else +#define __PRETTY_FUNCTION__ __func__ +#endif +#endif + +#define bail_out(exception, message) \ +do { \ + std::ostringstream err; \ + err << __PRETTY_FUNCTION__ << "(" << __FILE__ << ':' << __LINE__ << ": "; \ + err << message; \ + throw exception(err.str()); \ +} while (0) + +#define bug(message) bail_out(std::logic_error, message) + +#define dout(condition, message) \ +do { \ + if (unlikely(condition)) { \ + std::cerr << __PRETTY_FUNCTION__ \ + << " (" << __FILE__ << ':' << __LINE__ << "): " \ + << message << std::endl; \ + } \ +} while (0) + +#endif // GINAC_PARSER_DEBUG_HPP + diff --git a/ginac/parser/default_reader.tpl b/ginac/parser/default_reader.tpl new file mode 100644 index 0000000..f3b150d --- /dev/null +++ b/ginac/parser/default_reader.tpl @@ -0,0 +1,47 @@ +[+ AutoGen5 template .cpp +][+ +COMMENT a part of GiNaC parser -- construct functions from a byte stream. ++][+ +(use-modules (ice-9 format)) + +(define (sequence start end . step) + (let ((step (if (null? step) 1 (car step)))) + (let loop ((n start)) + (if (> n end) '() (cons n (loop (+ step n))))))) ++]/* +[+ (dne " * " " * " ) +] + * + * If you want to change this file, edit either `[+ (def-file) +]' or + * `[+ (tpl-file) +]' file, and run the following command: + * + * autogen -T [+ (tpl-file) +] [+ (def-file) +] + */ +#include "parse_context.hpp" + +namespace GiNaC +{ +[+ FOR function +] +static ex [+ (get "name") +]_reader(const exvector& ev) +{ + return GiNaC::[+ (get "name") +]([+ + (let ((nargs (if (exist? "args") + (string->number (get "args")) 1))) + (format '#f "~{ev[~a]~^, ~}" (sequence 0 (- nargs 1)))) +]); +}[+ ENDFOR +] + +const prototype_table& get_default_reader() +{ + using std::make_pair; + static bool initialized = false; + static prototype_table reader; + if (!initialized) { +[+ FOR function +] + reader[make_pair("[+ (get "name") +]", [+ + (if (exist? "args") (get "args") "1") + +])] = [+ (get "name") +]_reader;[+ + ENDFOR +] + initialized = true; + } + return reader; +} +} // namespace GiNaC + diff --git a/ginac/parser/lexer.cpp b/ginac/parser/lexer.cpp new file mode 100644 index 0000000..ed1f894 --- /dev/null +++ b/ginac/parser/lexer.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include "lexer.hpp" +#include "compiler.h" + +namespace GiNaC +{ +/// Skip to the end of line +static int skipline(std::istream* s); +/// Skip to the next non-whitespace character +static int skipspace(std::istream* s, int c, std::size_t& line); +/// Check if the identifier is predefined literal +static bool literal_p(const std::string& name); + +/// gettok - Return the next token from standard input. +int lexer::gettok() +{ + // Skip any whitespace. + c = skipspace(input, c, line_num); + + // identifier: [a-zA-Z][a-zA-Z0-9]* + if (isalpha(c)) { + str = c; + do { + c = input->get(); + if (isalnum(c)) + str += c; + else + break; + } while (true); + if (unlikely(literal_p(str))) + return token_type::literal; + else + return token_type::identifier; + } + + // Number: [0-9.]+ + if (isdigit(c) || c == '.') { + str = ""; + do { + str += c; + c = input->get(); + } while (isdigit(c) || c == '.'); + return token_type::number; + } + + // Comment until end of line. + if (c == '#') { + c = skipline(input); + ++line_num; + if (c != EOF) + return gettok(); + } + + // Check for end of file. Don't eat the EOF. + if (c == EOF) + return token_type::eof; + + // Otherwise, just return the character as its ascii value. + int current = c; + c = input->get(); + return current; +} + +static int skipline(std::istream* s) +{ + int c; + do { + c = s->get(); + } while (c != EOF && c != '\n' && c != '\r'); + return c; +} + +static int skipspace(std::istream* s, int c, std::size_t& line) +{ + while (isspace(c)) { + if (c == '\n') + ++line; + c = s->get(); + } + return c; +} + +static bool literal_p(const std::string& name) +{ + if (name == "I") + return true; + else if (name == "Pi") + return true; + else if (name == "Euler") + return true; + else if (name == "Catalan") + return true; + else + return false; +} + +lexer::lexer(std::istream* in, std::ostream* out, std::ostream* err) +{ + if (in) + input = in; + else + in = &std::cin; + + if (out) + output = out; + else + output = &std::cout; + + if (err) + error = err; + else + error = &std::cerr; + + c = ' '; + str = ""; + line_num = 0; + column = 0; +} + +lexer::~lexer() { } + +void lexer::switch_input(std::istream* in) +{ + input = in; + line_num = 0; + column = 0; +} + +} // namespace GiNaC + diff --git a/ginac/parser/lexer.hpp b/ginac/parser/lexer.hpp new file mode 100644 index 0000000..b53d08f --- /dev/null +++ b/ginac/parser/lexer.hpp @@ -0,0 +1,45 @@ +#ifndef GINAC_LEXER_HPP_ +#define GINAC_LEXER_HPP_ +#include +#include +#include +namespace GiNaC +{ + +class lexer +{ + std::istream* input; + std::ostream* output; + std::ostream* error; + /// last character read from stream + int c; + /// identifier and number tokens are stored here + std::string str; + std::size_t line_num; + std::size_t column; + friend class parser; +public: + + lexer(std::istream* in = 0, std::ostream* out = 0, std::ostream* err = 0); + ~lexer(); + + int gettok(); + void switch_input(std::istream* in); + + struct token_type + { + enum + { + eof = -1, + identifier = -4, + number = -5, + literal = -6 + + }; + }; +}; + +} // namespace GiNaC + +#endif // GINAC_LEXER_HPP_ + diff --git a/ginac/parser/parse_binop_rhs.cpp b/ginac/parser/parse_binop_rhs.cpp new file mode 100644 index 0000000..b118197 --- /dev/null +++ b/ginac/parser/parse_binop_rhs.cpp @@ -0,0 +1,179 @@ +#ifndef IN_GINAC +#include +#else +#include "ex.h" +#include "symbol.h" +#include "mul.h" +#include "add.h" +#include "power.h" +#endif +#include +#include +#include "parser.hpp" +#include "lexer.hpp" +#include "debug.hpp" + +namespace GiNaC +{ + +/// Make a sum or a product. +static ex make_binop_expr(const int binop, const exvector& args); +/// Check if the token is a binary operator. +static inline bool is_binop(const int c); +/// Get the precedence of the pending binary operator. +static int get_tok_prec(const int c); + +/// binoprhs: ([+*/^-] primary)* +ex parser::parse_binop_rhs(int expr_prec, ex& lhs) +{ + exvector args; + args.push_back(lhs); + int binop = -1, orig_binop = -1; + bool need_sign_flip = false; + while (1) { + // check if this is a binop + if (!is_binop(token)) { + if (args.size() > 1) + return make_binop_expr(orig_binop, args); + else + return lhs; + } + + // Okay, we know this is a binop. + if (args.size() == 1) + orig_binop = token; + + binop = token; + + // If this is a binop that binds at least as tightly as + // the current binop, consume it, otherwise we are done. + int tok_prec = get_tok_prec(token); + if (tok_prec < expr_prec) { + if (args.size() > 1) + return make_binop_expr(orig_binop, args); + else + return lhs; + } + + get_next_tok(); // eat binop + + // Parse the primary expression after the binary operator. + ex rhs = parse_primary(); + + // If binop binds less tightly with rhs than the operator after + // rhs, let the pending operator take rhs as its lhs. + int next_prec = get_tok_prec(token); + if (tok_prec < next_prec) + rhs = parse_binop_rhs(tok_prec + 1, rhs); + + // previous operator was '+', and current one is '-' + // (or vice a versa). + if (need_sign_flip) + rhs = - rhs; + + args.push_back(rhs); + + // Minimize the number of eval() and ctor calls. This is + // crucial for a reasonable performance. If the next operator + // is compatible with the pending one (or the same) don't create + // the expression and continue collecting operands instead. + if (binop == token) + continue; + else if (binop == '+' && token == '-') { + need_sign_flip = token != orig_binop; + continue; + } else if (binop == '-' && token == '+') { + need_sign_flip = token != orig_binop; + continue; + } else { + if (args.size() <= 1) + bug("binop has " << args.size() << " arguments, expected >= 2"); + lhs = make_binop_expr(orig_binop, args); + args.clear(); + args.push_back(lhs); + } + } +} + +extern numeric* _num_1_p; + +static ex make_minus_expr(const exvector& args) +{ + exvector rest_args; + rest_args.reserve(args.size() - 1); + std::copy(args.begin() + 1, args.end(), std::back_inserter(rest_args)); + ex rest_base = (new add(rest_args))->setflag(status_flags::dynallocated); + ex rest = (new mul(rest_base, *_num_1_p))->setflag(status_flags::dynallocated); + ex ret = (new add(args[0], rest))->setflag(status_flags::dynallocated); + return ret; +} + +static ex make_divide_expr(const exvector& args) +{ + exvector rest_args; + rest_args.reserve(args.size() - 1); + std::copy(args.begin() + 1, args.end(), std::back_inserter(rest_args)); + ex rest_base = (new mul(rest_args))->setflag(status_flags::dynallocated); + ex rest = pow(rest_base, *_num_1_p); + return (new mul(args[0], rest))->setflag(status_flags::dynallocated); +} + +static ex make_binop_expr(const int binop, const exvector& args) +{ + switch (binop) { + case '+': + return (new add(args))->setflag(status_flags::dynallocated); + case '-': + return make_minus_expr(args); + case '*': + return (new mul(args))->setflag(status_flags::dynallocated); + case '/': + return make_divide_expr(args); + case '^': + if (args.size() != 2) + throw std::invalid_argument( + std::string(__func__) + + ": power should have exactly 2 operands"); + return pow(args[0], args[1]); + default: + throw std::invalid_argument( + std::string(__func__) + + ": invalid binary operation: " + + char(binop)); + } +} + +static inline bool is_binop(const int c) +{ + switch (c) { + case '+': + case '-': + case '*': + case '/': + case '^': + return true; + default: + return false; + } +} + +/// Get the precedence of the pending binary operator. +static int get_tok_prec(const int c) +{ + switch (c) { + case '+': + case '-': + return 20; + case '*': + case '/': + return 40; + case '^': + return 60; + default: + return -1; + // means 'this is not a binary operator' + } +} + +} // namespace GiNaC + diff --git a/ginac/parser/parse_context.cpp b/ginac/parser/parse_context.cpp new file mode 100644 index 0000000..e4956ba --- /dev/null +++ b/ginac/parser/parse_context.cpp @@ -0,0 +1,27 @@ +#include "parse_context.hpp" +#include +#include +namespace GiNaC +{ + +const symbol& +find_or_insert_symbol(const std::string& name, symtab& syms, const bool strict) +{ + symtab::const_iterator p = syms.find(name); + if (p != syms.end()) + return p->second.first; + + if (strict) + throw std::invalid_argument( + std::string("find_or_insert_symbol: symbol \"") + + name + "\" not found"); + + // false means this symbol was created by parser + const std::pair tmp = std::make_pair(symbol(name), false); + + symtab::iterator i = syms.insert(symtab::value_type(name, tmp)).first; + return i->second.first; +} + +} + diff --git a/ginac/parser/parse_context.hpp b/ginac/parser/parse_context.hpp new file mode 100644 index 0000000..c7cc19b --- /dev/null +++ b/ginac/parser/parse_context.hpp @@ -0,0 +1,82 @@ +#ifndef _GINAC_PARSE_CONTEXT_HPP +#define _GINAC_PARSE_CONTEXT_HPP +#include +#include // size_t +#ifndef IN_GINAC +#include +#else +#include "ex.h" +#include "symbol.h" +#endif +#include +#include + +namespace GiNaC +{ + +/** + * Establishes correspondence between the strings and symbols. + * The parser will create missing symbols (if not instructed otherwise, + * in which case it fails if the expression contains unknown symbols). + * The .second element of pair helps to distinguish between the user + * supplied symbols and parser generated ones. The .first is the symbol + * itself + */ +typedef std::map > symtab; + +/** + * Find the symbol with the @a name in the symbol table @a syms. + * + * If symbol is missing and @a strict = false, insert it, otherwise + * throw an exception. + */ +extern const symbol& +find_or_insert_symbol(const std::string& name, symtab& syms, + const bool strict); + +/** + * Function (or class ctor) prototype + * .first is the name of function(or ctor), + * .second is the number of arguments (each of type ex) + */ +typedef std::pair prototype; + +/** + * A (C++) function for reading functions and classes from the stream. + * + * The parser uses (an associative array of) such functions to construct + * (GiNaC) classes and functions from a sequence of characters. + */ +typedef ex (*reader_func)(const exvector& args); + +/** + * Prototype table. + * + * If parser sees an expression which looks like a function call (e.g. + * foo(x+y, z^2, t)), it looks up such a table to find out which + * function (or class) corresponds to the given name and has the given + * number of the arguments. + * + * N.B. + * + * 1. The function don't have to return a (GiNaC) function or class, it + * can return any expression. + * 2. Overloaded functions/ctors are paritally supported, i.e. there might + * be several functions with the same name, but they should take different + * number of arguments. + * 3. User can extend the parser via custom prototype tables. It's possible + * to read user defined classes, create abbreviations, etc. + */ +typedef std::map prototype_table; + +/** + * Default prototype table. + * + * It supports most of builtin GiNaC functions. + */ +extern const prototype_table& get_default_reader(); + +} + +#endif // _GINAC_PARSE_CONTEXT_HPP + diff --git a/ginac/parser/parser.cpp b/ginac/parser/parser.cpp new file mode 100644 index 0000000..bd44137 --- /dev/null +++ b/ginac/parser/parser.cpp @@ -0,0 +1,172 @@ +#include +#include +#include +#include "parser.hpp" +#include "lexer.hpp" +#include "debug.hpp" + +namespace GiNaC +{ + +/// identifier_expr: identifier | identifier '(' expression* ')' +ex parser::parse_identifier_expr() +{ + std::string name = scanner->str; + get_next_tok(); // eat identifier. + + if (token != '(') // symbol + return find_or_insert_symbol(name, syms, strict); + + // function/ctor call. + get_next_tok(); // eat ( + exvector args; + if (token != ')') { + while (true) { + ex e = parse_expression(); + args.push_back(e); + + if (token == ')') + break; + + if (token != ',') + throw std::invalid_argument("Expected ')' or ',' in argument list"); + + get_next_tok(); + } + } + // Eat the ')'. + get_next_tok(); + prototype the_prototype = make_pair(name, args.size()); + prototype_table::const_iterator reader = funcs.find(the_prototype); + if (reader == funcs.end()) { + bail_out(std::invalid_argument, + "no function \"" << name << "\" with " << args.size() + << " arguments"); + } + ex ret = reader->second(args); + return ret; +} + +/// paren_expr: '(' expression ')' +ex parser::parse_paren_expr() +{ + get_next_tok(); // eat (. + ex e = parse_expression(); + + if (token != ')') + throw std::invalid_argument("parser::parse_paren_expr: expected ')'"); + get_next_tok(); // eat ). + return e; +} + +extern numeric* _num_1_p; + +/// unary_expr: [+-] expression +ex parser::parse_unary_expr(const int s) +{ + // consume '-' (or '+') + get_next_tok(); + ex v = parse_expression(); + switch (s) { + case '-': + return (new mul(v, *_num_1_p))->setflag(status_flags::dynallocated); + case '+': + return v; + default: + throw std::invalid_argument( + std::string(__func__) + + ": invalid unary operator \"" + + char(s) + "\""); + } +} + +/// primary: identifier_expr | number_expr | paren_expr | unary_expr +ex parser::parse_primary() +{ + switch (token) { + case lexer::token_type::identifier: + return parse_identifier_expr(); + case lexer::token_type::number: + return parse_number_expr(); + case '(': + return parse_paren_expr(); + case '-': + return parse_unary_expr('-'); + case '+': + return parse_unary_expr('+'); + case lexer::token_type::literal: + return parse_literal_expr(); + case lexer::token_type::eof: + bail_out(std::invalid_argument, "got EOF while parsing the expression"); + default: + bail_out(std::invalid_argument, "unknown token " << + token << " (\"" << + (token ? std::string("") + char(token) : "NULL") + << "\")"); + } +} + +/// expression ::= primary binoprhs +ex parser::parse_expression() +{ + ex lhs = parse_primary(); + ex res = parse_binop_rhs(0, lhs); + return res; +} + +/// number_expr: number +ex parser::parse_number_expr() +{ + ex n = numeric(scanner->str.c_str()); + get_next_tok(); // consume the number + return n; +} + +/// literal_expr: 'I' | 'Pi' | 'Euler' | 'Catalan' +ex parser::parse_literal_expr() +{ + if (scanner->str == "I") + return I; + else if (scanner->str == "Pi") + return Pi; + else if (scanner->str == "Euler") + return Euler; + else if (scanner->str == "Catalan") + return Catalan; + bug("unknown literal: \"" << scanner->str << "\""); +} + +ex parser::operator()(std::istream& input) +{ + scanner->switch_input(&input); + get_next_tok(); + ex ret = parse_expression(); + return ret; +} + +ex parser::operator()(const std::string& input) +{ + std::istringstream is(input); + ex ret = operator()(is); + return ret; +} + +int parser::get_next_tok() +{ + token = scanner->gettok(); + return token; +} + +parser::parser(const symtab& syms_, const prototype_table& funcs_, + const bool strict_) : strict(strict_), funcs(funcs_), + syms(syms_) +{ + scanner = new lexer(); +} + +parser::~parser() +{ + delete scanner; +} + +} // namespace GiNaC diff --git a/ginac/parser/parser.hpp b/ginac/parser/parser.hpp new file mode 100644 index 0000000..40e7e07 --- /dev/null +++ b/ginac/parser/parser.hpp @@ -0,0 +1,99 @@ +#ifndef GINAC_PARSER_HPP_ + +#include "parse_context.hpp" +#include +#ifndef IN_GINAC +#include +#else +#include "ex.h" +#endif + +namespace GiNaC +{ + +class lexer; + +/** + * Recursive descent parser for GiNaC expressions. + */ +class parser +{ + // The actual parser rules (in EBNF-alike notation): + + /// expression: primary binoprhs + ex parse_expression(); + + /// primary: indentifier_expr | number_expr | paren_expr | unary_expr + ex parse_primary(); + + /// binoprhs: ([+*/^-] primary)* + ex parse_binop_rhs(int, ex&); + + /// identifier_expr: identifier | + /// identifier '(' expression (',' expression)* ')' + ex parse_identifier_expr(); + + /// paren_expr: '(' expression ')' + ex parse_paren_expr(); + + /// number_expr: number + ex parse_number_expr(); + + /// unary_expr: [+-] expression + ex parse_unary_expr(const int c); + + /// literal_expr: 'I' | 'Pi' | 'Euler' | 'Catalan' + ex parse_literal_expr(); + +public: + /** + * @param syms_ symbol table. + * @param funcs_ function/ctors table. + * @param strict_ if true, throw an exception if unknown + * symbol is encountered. + */ + parser(const symtab& syms_ = symtab(), + const prototype_table& funcs_ = get_default_reader(), + const bool strict_ = false); + ~parser(); + + /// parse the stream @a input + ex operator()(std::istream& input); + /// parse the string @a input + ex operator()(const std::string& input); + + /// report the symbol table used by parser + symtab get_syms() const + { + return syms; + } + +private: + /// If true, throw an exception if an unknown symbol is encountered. + const bool strict; + /** + * Function/ctor table, maps a prototype (which is a name and number + * arguments) to a C++ function. Used for parsing identifier_expr's + * (see parse_identifier_expr). If expression contains unknown + * prototype, an exception will be thrown. + */ + const prototype_table funcs; + /** + * Symbol (variable) table. Used for parsing identifier_expr's + * (see parse_identifier_expr). If parser is strict, exception is + * thrown if an unknown symbol is encountered. Non-strict parser + * appends unknown symbols to the symbol table. + */ + symtab syms; + /// token scanner + lexer* scanner; + /// current token the parser is looking at + int token; + /// read the next token from the scanner + int get_next_tok(); +}; + +} // namespace GiNaC + +#endif // GINAC_PARSER_HPP_ + -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From sergstesh at yahoo.com Tue Aug 19 21:30:46 2008 From: sergstesh at yahoo.com (Sergei Steshenko) Date: Tue, 19 Aug 2008 12:30:46 -0700 (PDT) Subject: [GiNaC-devel] [PATCH] Faster, better (recursive descent) expression parser. In-Reply-To: <20080819180940.GA22026@theor.jinr.ru> Message-ID: <328471.48864.qm@web35207.mail.mud.yahoo.com> Just wondering - how good for the purpose can be the Elkhound parser: http://www.cs.berkeley.edu/~smcpeak/elkhound/ . Thanks, Sergei. From varg at theor.jinr.ru Tue Aug 19 21:48:03 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Tue, 19 Aug 2008 23:48:03 +0400 Subject: [GiNaC-devel] [PATCH (try 2)] Faster, better (recursive descent) expression parser. In-Reply-To: <20080819180940.GA22026@theor.jinr.ru> References: <20080819180940.GA22026@theor.jinr.ru> Message-ID: <20080819194803.GA29147@theor.jinr.ru> Ouch, it fails to compile. Here is a correct version: From: Alexei Sheplyakov Date: Tue, 19 Aug 2008 21:50:03 +0400 Subject: [PATCH] Faster, better (recursive descent) expression parser. bison generated parser has a number of problems: 1. Bad performance. Parsing a sum seems to be O(N^{2 + a}) (a > 0) [1]. For example, parsing a sum (actually, a univariate polynomial) of 32768 terms takes about 90 sec. on my box, parsing a sum of 10^6 terms takes "eternity". 2. The user is expected to provide list of all symbols in the expression. Often this is very annoying (and useless). 3. Parser is not reentrant (bison *can* produce reentrant parsers, but that won't solve other problems). 4. Parser is difficult to extend. Hence the new parser. Features: 1. Parsing large sums and products is O(N). 2. Parser is reentrant (well, almost). 3. It's possible to insert (shell style) comments inside the expressions. 4. matrices, lists, FAIL are NOT handled. Yes, this *is* a feature :-) Limitations: 1. Error handling is a bit terse: on error exception is thrown, and that's it. 2. Binary, octal, and hexadecimal numbers can not be parsed (yet). 3. Tensors, noncommutative products, etc. can not be parsed. Other notes: 1. ex ctor still uses the old parser. 2. ginsh still uses the old parser. [1] Mesured by this script (requires gnuplot): make_expression_string () { printf "1 + x" local n=2 while test $n -le $1; do printf " + %s*x^%s" $n $n n=$(expr $n + 1) done } single_test () { printf "$1 \t" ( printf "time("; make_expression_string $1; printf ");" ) | \ ginsh | sed -e 's/s$//' } benchmark () { local N=$1 while test $N -le $2; do single_test $N N=$(expr $N \* 2) done } gnuplot_header () { echo "set logscale xy" echo "set xlabel 'degree (# of terms)'" echo "set ylabel 'time, sec.'" echo "set xrange [${1}:${2}]" echo "plot '-' using 1:2 with lines title '1+x+2*x^2+...+n*x^n'" } gnuplot_footer () { echo "e" } benchmark_and_plot () { ( gnuplot_header $1 $2 benchmark $1 $2 | tee $3.txt gnuplot_footer ) | \ gnuplot -persist '-' } N_ini=${1:-1024} N_fin=${2:-32768} out_base=${3:-parser_benchmark} benchmark_and_plot $N_ini $N_fin $out_base --- INSTALL | 3 +- check/Makefile.am | 8 ++- check/time_parser.cpp | 79 +++++++++++++++++ configure.ac | 4 + ginac/Makefile.am | 31 ++++++- ginac/parser/builtin_fcns.def | 90 +++++++++++++++++++ ginac/parser/debug.hpp | 35 ++++++++ ginac/parser/default_reader.tpl | 50 +++++++++++ ginac/parser/lexer.cpp | 132 ++++++++++++++++++++++++++++ ginac/parser/lexer.hpp | 45 ++++++++++ ginac/parser/parse_binop_rhs.cpp | 176 ++++++++++++++++++++++++++++++++++++++ ginac/parser/parse_context.cpp | 27 ++++++ ginac/parser/parse_context.hpp | 78 +++++++++++++++++ ginac/parser/parser.cpp | 173 +++++++++++++++++++++++++++++++++++++ ginac/parser/parser.hpp | 95 ++++++++++++++++++++ 15 files changed, 1021 insertions(+), 5 deletions(-) create mode 100644 check/time_parser.cpp create mode 100644 ginac/parser/builtin_fcns.def create mode 100644 ginac/parser/debug.hpp create mode 100644 ginac/parser/default_reader.tpl create mode 100644 ginac/parser/lexer.cpp create mode 100644 ginac/parser/lexer.hpp create mode 100644 ginac/parser/parse_binop_rhs.cpp create mode 100644 ginac/parser/parse_context.cpp create mode 100644 ginac/parser/parse_context.hpp create mode 100644 ginac/parser/parser.cpp create mode 100644 ginac/parser/parser.hpp diff --git a/INSTALL b/INSTALL index b542d9e..1cad277 100644 --- a/INSTALL +++ b/INSTALL @@ -30,7 +30,8 @@ Known not to work with: is missing there. If you install from CVS, you also need GNU autoconf (>=2.59), automake (>=1.7), -libtool (>= 1.5), bison (>= 2.3), flex (>= 2.5.33) to be installed. +libtool (>= 1.5), bison (>= 2.3), flex (>= 2.5.33), autogen (>= 5.6.0) to be +installed. INSTALLATION diff --git a/check/Makefile.am b/check/Makefile.am index a34bb9f..b656c63 100644 --- a/check/Makefile.am +++ b/check/Makefile.am @@ -50,7 +50,8 @@ TIMES = time_dennyfliegner \ time_lw_Q \ time_lw_Qprime \ time_antipode \ - time_fateman_expand + time_fateman_expand \ + time_parser TESTS = $(CHECKS) $(EXAMS) $(TIMES) check_PROGRAMS = $(CHECKS) $(EXAMS) $(TIMES) @@ -227,6 +228,11 @@ time_fateman_expand_SOURCES = time_fateman_expand.cpp \ randomize_serials.cpp timer.cpp timer.h time_fateman_expand_LDADD = ../ginac/libginac.la +time_parser_SOURCES = time_parser.cpp \ + randomize_serials.cpp timer.cpp timer.h +time_parser_LDADD = ../ginac/libginac.la + + AM_CPPFLAGS = -I$(srcdir)/../ginac -I../ginac CLEANFILES = exam.gar diff --git a/check/time_parser.cpp b/check/time_parser.cpp new file mode 100644 index 0000000..ca35fb6 --- /dev/null +++ b/check/time_parser.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ginac.h" +#include "parser/parser.hpp" +#include "timer.h" +extern void randomify_symbol_serials(); +using namespace std; +using namespace GiNaC; + +/// make a string "1+x+2*x^2+...+n*x^n" +static string prepare_str(const unsigned n, const char x = 'x') +{ + ostringstream s; + s << x; + for (unsigned i = 2; i < n; i++) + s << '+' << i << '*' << x << '^' << i; + return s.str(); +} + +void benchmark_and_cmp(const string& srep, double& t_new, double& t_old) +{ + parser the_parser; + timer RSD10; + RSD10.start(); + ex e = the_parser(srep); + t_new = RSD10.read(); + RSD10.stop(); + if (t_new > 2.0) + cout << '.' << flush; + + symtab syms = the_parser.get_syms(); + const symbol x = find_or_insert_symbol("x", syms, true); + lst sl; + sl = x; + RSD10.start(); + ex e2(srep, sl); + t_old = RSD10.read(); + + if (t_old > 2.0) + cout << '.' << flush; + + ex dif = (e - e2).expand(); + if (!dif.is_zero()) { + cerr << "Got a difference: " << dif << endl; + throw std::logic_error("New and old parser give different results"); + } +} + +int main(int argc, char** argv) +{ + cout << "timing GiNaC parser..." << flush; + randomify_symbol_serials(); + unsigned n_min = 1024; + unsigned n_max = 32768; + if (argc > 1) + n_max = atoi(argv[1]); + + vector times_new, times_old; + vector ns; + for (unsigned n = n_min; n <= n_max; n = n << 1) { + double t_new, t_old; + string srep = prepare_str(n); + benchmark_and_cmp(srep, t_new, t_old); + times_new.push_back(t_new); + times_old.push_back(t_old); + ns.push_back(n); + } + + cout << "OK" << endl; + cout << "# terms new parser, s old parser, s" << endl; + for (size_t i = 0; i < times_new.size(); i++) + cout << " " << ns[i] << '\t' << times_new[i] << '\t' << times_old[i] << endl; + return 0; +} diff --git a/configure.ac b/configure.ac index e4a1e60..22f9c96 100644 --- a/configure.ac +++ b/configure.ac @@ -149,6 +149,10 @@ AM_CONDITIONAL(CONFIG_TEX, [test ! \( -z "$LATEX" -o -z $"PDFLATEX" -o -z "$MAKE AC_PATH_PROG(FIG2DEV, fig2dev, "") AM_CONDITIONAL(CONFIG_FIG2DEV, [test ! -z "$FIG2DEV"]) +dnl generate boilerplate code for the (new) parser. +dnl Only developers need this tool. +AC_PATH_PROG(AUTOGEN, autogen, "") + dnl Output makefiles etc. AC_CONFIG_FILES([ Makefile diff --git a/ginac/Makefile.am b/ginac/Makefile.am index d2ef614..0570fde 100644 --- a/ginac/Makefile.am +++ b/ginac/Makefile.am @@ -9,7 +9,15 @@ libginac_la_SOURCES = add.cpp archive.cpp basic.cpp clifford.cpp color.cpp \ operators.cpp power.cpp registrar.cpp relational.cpp remember.cpp \ pseries.cpp print.cpp symbol.cpp symmetry.cpp tensor.cpp \ utils.cpp wildcard.cpp input_parser.yy input_lexer.ll \ - input_lexer.h remember.h tostring.h utils.h compiler.h + input_lexer.h remember.h tostring.h utils.h compiler.h \ + parser/parse_binop_rhs.cpp \ + parser/parser.cpp \ + parser/parse_context.cpp \ + parser/builtin_fcns.cpp \ + parser/lexer.cpp \ + parser/lexer.hpp \ + parser/debug.hpp + libginac_la_LDFLAGS = -version-info $(LT_VERSION_INFO) -release $(LT_RELEASE) libginac_la_LIBADD = $(DL_LIBS) ginacincludedir = $(includedir)/ginac @@ -18,10 +26,27 @@ ginacinclude_HEADERS = ginac.h add.h archive.h assertion.h basic.h class_info.h exprseq.h fail.h factor.h fderivative.h flags.h function.h hash_map.h idx.h indexed.h \ inifcns.h integral.h lst.h matrix.h mul.h ncmul.h normal.h numeric.h operators.h \ power.h print.h pseries.h ptr.h registrar.h relational.h structure.h \ - symbol.h symmetry.h tensor.h version.h wildcard.h + symbol.h symmetry.h tensor.h version.h wildcard.h \ + parser/parser.hpp \ + parser/parse_context.hpp + AM_LFLAGS = -Pginac_yy -olex.yy.c AM_YFLAGS = -p ginac_yy -d -EXTRA_DIST = function.pl input_parser.h version.h.in +EXTRA_DIST = function.pl input_parser.h version.h.in \ +parser/default_reader.tpl parser/builtin_fcns.def + +# Files produced by autogen(1) from templates +$(srcdir)/parser/builtin_fcns.cpp: $(srcdir)/parser/builtin_fcns.def $(srcdir)/parser/default_reader.tpl + set -e; if [ -n "$(AUTOGEN)" ]; then \ + cd $(srcdir)/parser; \ + $(AUTOGEN) -T default_reader.tpl builtin_fcns.def; \ + elif [ -f $@ ]; then \ + echo "WARNING: AutoGen is not available, the \"$@\" file WON'T be re-generated"; \ + else \ + echo "*** ERROR: the \"$@\" file does not exist, and AutoGen is not installed on your system"; \ + echo "*** Please install AutoGen (http://www.gnu.org/software/autogen)"; \ + fi + # Files which are generated by perl scripts $(srcdir)/function.h $(srcdir)/function.cpp: $(srcdir)/function.pl diff --git a/ginac/parser/builtin_fcns.def b/ginac/parser/builtin_fcns.def new file mode 100644 index 0000000..897b1f7 --- /dev/null +++ b/ginac/parser/builtin_fcns.def @@ -0,0 +1,90 @@ +Autogen definitions ginacfcns; + +function = { name = "log"; }; +function = { name = "exp"; }; +function = { name = "sin"; }; +function = { name = "cos"; }; +function = { name = "tan"; }; +function = { name = "asin"; }; +function = { name = "acos"; }; +function = { name = "atan"; }; + +function = { name = "sinh"; }; +function = { name = "cosh"; }; +function = { name = "tanh"; }; +function = { name = "asinh"; }; +function = { name = "acosh"; }; +function = { name = "atanh"; }; + +function = { + name = "atan2"; + args = 2; +}; + +function = { + name = "Li2"; + comment = "Dilogarithm"; +}; + +function = { + name = "Li3"; + comment = "Trilogarithm"; +}; + +function = { + name = "zetaderiv"; + comment = "Derivatives of Riemann's Zeta-function"; + args = 2; +}; + +function = { + name = "Li"; + args = 2; + comment = "Polylogarithm and multiple polylogarithm"; +}; + +function = { + name = "S"; + args = 3; + comment = "Nielsen's generalized polylogarithm"; +}; + +function = { + name = "H"; + args = 2; + comment = "Harmonic polylogarithm"; +}; + +function = { name = "lgamma"; }; +function = { name = "tgamma"; }; + +function = { + name = "beta"; + args = 2; + comment = "Beta-function"; +}; + +function = { name = "factorial"; }; + +function = { + name = "binomial"; + args = 2; +}; + +function = { + name = "Order"; + comment = "Order term function (for truncated power series)"; +}; + +/* Thease are not functions, but anyway ... */ +function = { name = "sqrt"; }; + +function = { + name = "pow"; + args = 2; +}; + +function = { + name = "power"; + args = 2; +}; diff --git a/ginac/parser/debug.hpp b/ginac/parser/debug.hpp new file mode 100644 index 0000000..a43cc7a --- /dev/null +++ b/ginac/parser/debug.hpp @@ -0,0 +1,35 @@ +#ifndef GINAC_PARSER_DEBUG_HPP +#define GINAC_PARSER_DEBUG_HPP +#include +#include +#include +#include "compiler.h" +#ifndef __GNUC__ +#if __STDC_VERSION__ < 199901L +#define __PRETTY_FUNCTION__ "" +#else +#define __PRETTY_FUNCTION__ __func__ +#endif +#endif + +#define bail_out(exception, message) \ +do { \ + std::ostringstream err; \ + err << __PRETTY_FUNCTION__ << "(" << __FILE__ << ':' << __LINE__ << ": "; \ + err << message; \ + throw exception(err.str()); \ +} while (0) + +#define bug(message) bail_out(std::logic_error, message) + +#define dout(condition, message) \ +do { \ + if (unlikely(condition)) { \ + std::cerr << __PRETTY_FUNCTION__ \ + << " (" << __FILE__ << ':' << __LINE__ << "): " \ + << message << std::endl; \ + } \ +} while (0) + +#endif // GINAC_PARSER_DEBUG_HPP + diff --git a/ginac/parser/default_reader.tpl b/ginac/parser/default_reader.tpl new file mode 100644 index 0000000..cfb8236 --- /dev/null +++ b/ginac/parser/default_reader.tpl @@ -0,0 +1,50 @@ +[+ AutoGen5 template .cpp +][+ +COMMENT a part of GiNaC parser -- construct functions from a byte stream. ++][+ +(use-modules (ice-9 format)) + +(define (sequence start end . step) + (let ((step (if (null? step) 1 (car step)))) + (let loop ((n start)) + (if (> n end) '() (cons n (loop (+ step n))))))) ++]/* +[+ (dne " * " " * " ) +] + * + * If you want to change this file, edit either `[+ (def-file) +]' or + * `[+ (tpl-file) +]' file, and run the following command: + * + * autogen -T [+ (tpl-file) +] [+ (def-file) +] + */ +#include "parse_context.hpp" +#include "power.h" +#include "operators.h" +#include "inifcns.h" + +namespace GiNaC +{ +[+ FOR function +] +static ex [+ (get "name") +]_reader(const exvector& ev) +{ + return GiNaC::[+ (get "name") +]([+ + (let ((nargs (if (exist? "args") + (string->number (get "args")) 1))) + (format '#f "~{ev[~a]~^, ~}" (sequence 0 (- nargs 1)))) +]); +}[+ ENDFOR +] + +const prototype_table& get_default_reader() +{ + using std::make_pair; + static bool initialized = false; + static prototype_table reader; + if (!initialized) { +[+ FOR function +] + reader[make_pair("[+ (get "name") +]", [+ + (if (exist? "args") (get "args") "1") + +])] = [+ (get "name") +]_reader;[+ + ENDFOR +] + initialized = true; + } + return reader; +} +} // namespace GiNaC + diff --git a/ginac/parser/lexer.cpp b/ginac/parser/lexer.cpp new file mode 100644 index 0000000..ed1f894 --- /dev/null +++ b/ginac/parser/lexer.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include "lexer.hpp" +#include "compiler.h" + +namespace GiNaC +{ +/// Skip to the end of line +static int skipline(std::istream* s); +/// Skip to the next non-whitespace character +static int skipspace(std::istream* s, int c, std::size_t& line); +/// Check if the identifier is predefined literal +static bool literal_p(const std::string& name); + +/// gettok - Return the next token from standard input. +int lexer::gettok() +{ + // Skip any whitespace. + c = skipspace(input, c, line_num); + + // identifier: [a-zA-Z][a-zA-Z0-9]* + if (isalpha(c)) { + str = c; + do { + c = input->get(); + if (isalnum(c)) + str += c; + else + break; + } while (true); + if (unlikely(literal_p(str))) + return token_type::literal; + else + return token_type::identifier; + } + + // Number: [0-9.]+ + if (isdigit(c) || c == '.') { + str = ""; + do { + str += c; + c = input->get(); + } while (isdigit(c) || c == '.'); + return token_type::number; + } + + // Comment until end of line. + if (c == '#') { + c = skipline(input); + ++line_num; + if (c != EOF) + return gettok(); + } + + // Check for end of file. Don't eat the EOF. + if (c == EOF) + return token_type::eof; + + // Otherwise, just return the character as its ascii value. + int current = c; + c = input->get(); + return current; +} + +static int skipline(std::istream* s) +{ + int c; + do { + c = s->get(); + } while (c != EOF && c != '\n' && c != '\r'); + return c; +} + +static int skipspace(std::istream* s, int c, std::size_t& line) +{ + while (isspace(c)) { + if (c == '\n') + ++line; + c = s->get(); + } + return c; +} + +static bool literal_p(const std::string& name) +{ + if (name == "I") + return true; + else if (name == "Pi") + return true; + else if (name == "Euler") + return true; + else if (name == "Catalan") + return true; + else + return false; +} + +lexer::lexer(std::istream* in, std::ostream* out, std::ostream* err) +{ + if (in) + input = in; + else + in = &std::cin; + + if (out) + output = out; + else + output = &std::cout; + + if (err) + error = err; + else + error = &std::cerr; + + c = ' '; + str = ""; + line_num = 0; + column = 0; +} + +lexer::~lexer() { } + +void lexer::switch_input(std::istream* in) +{ + input = in; + line_num = 0; + column = 0; +} + +} // namespace GiNaC + diff --git a/ginac/parser/lexer.hpp b/ginac/parser/lexer.hpp new file mode 100644 index 0000000..b53d08f --- /dev/null +++ b/ginac/parser/lexer.hpp @@ -0,0 +1,45 @@ +#ifndef GINAC_LEXER_HPP_ +#define GINAC_LEXER_HPP_ +#include +#include +#include +namespace GiNaC +{ + +class lexer +{ + std::istream* input; + std::ostream* output; + std::ostream* error; + /// last character read from stream + int c; + /// identifier and number tokens are stored here + std::string str; + std::size_t line_num; + std::size_t column; + friend class parser; +public: + + lexer(std::istream* in = 0, std::ostream* out = 0, std::ostream* err = 0); + ~lexer(); + + int gettok(); + void switch_input(std::istream* in); + + struct token_type + { + enum + { + eof = -1, + identifier = -4, + number = -5, + literal = -6 + + }; + }; +}; + +} // namespace GiNaC + +#endif // GINAC_LEXER_HPP_ + diff --git a/ginac/parser/parse_binop_rhs.cpp b/ginac/parser/parse_binop_rhs.cpp new file mode 100644 index 0000000..4433a3c --- /dev/null +++ b/ginac/parser/parse_binop_rhs.cpp @@ -0,0 +1,176 @@ +#include "ex.h" +#include "symbol.h" +#include "mul.h" +#include "add.h" +#include "power.h" +#include "operators.h" +#include +#include +#include "parser.hpp" +#include "lexer.hpp" +#include "debug.hpp" + +namespace GiNaC +{ + +/// Make a sum or a product. +static ex make_binop_expr(const int binop, const exvector& args); +/// Check if the token is a binary operator. +static inline bool is_binop(const int c); +/// Get the precedence of the pending binary operator. +static int get_tok_prec(const int c); + +/// binoprhs: ([+*/^-] primary)* +ex parser::parse_binop_rhs(int expr_prec, ex& lhs) +{ + exvector args; + args.push_back(lhs); + int binop = -1, orig_binop = -1; + bool need_sign_flip = false; + while (1) { + // check if this is a binop + if (!is_binop(token)) { + if (args.size() > 1) + return make_binop_expr(orig_binop, args); + else + return lhs; + } + + // Okay, we know this is a binop. + if (args.size() == 1) + orig_binop = token; + + binop = token; + + // If this is a binop that binds at least as tightly as + // the current binop, consume it, otherwise we are done. + int tok_prec = get_tok_prec(token); + if (tok_prec < expr_prec) { + if (args.size() > 1) + return make_binop_expr(orig_binop, args); + else + return lhs; + } + + get_next_tok(); // eat binop + + // Parse the primary expression after the binary operator. + ex rhs = parse_primary(); + + // If binop binds less tightly with rhs than the operator after + // rhs, let the pending operator take rhs as its lhs. + int next_prec = get_tok_prec(token); + if (tok_prec < next_prec) + rhs = parse_binop_rhs(tok_prec + 1, rhs); + + // previous operator was '+', and current one is '-' + // (or vice a versa). + if (need_sign_flip) + rhs = - rhs; + + args.push_back(rhs); + + // Minimize the number of eval() and ctor calls. This is + // crucial for a reasonable performance. If the next operator + // is compatible with the pending one (or the same) don't create + // the expression and continue collecting operands instead. + if (binop == token) + continue; + else if (binop == '+' && token == '-') { + need_sign_flip = token != orig_binop; + continue; + } else if (binop == '-' && token == '+') { + need_sign_flip = token != orig_binop; + continue; + } else { + if (args.size() <= 1) + bug("binop has " << args.size() << " arguments, expected >= 2"); + lhs = make_binop_expr(orig_binop, args); + args.clear(); + args.push_back(lhs); + } + } +} + +extern numeric* _num_1_p; + +static ex make_minus_expr(const exvector& args) +{ + exvector rest_args; + rest_args.reserve(args.size() - 1); + std::copy(args.begin() + 1, args.end(), std::back_inserter(rest_args)); + ex rest_base = (new add(rest_args))->setflag(status_flags::dynallocated); + ex rest = (new mul(rest_base, *_num_1_p))->setflag(status_flags::dynallocated); + ex ret = (new add(args[0], rest))->setflag(status_flags::dynallocated); + return ret; +} + +static ex make_divide_expr(const exvector& args) +{ + exvector rest_args; + rest_args.reserve(args.size() - 1); + std::copy(args.begin() + 1, args.end(), std::back_inserter(rest_args)); + ex rest_base = (new mul(rest_args))->setflag(status_flags::dynallocated); + ex rest = pow(rest_base, *_num_1_p); + return (new mul(args[0], rest))->setflag(status_flags::dynallocated); +} + +static ex make_binop_expr(const int binop, const exvector& args) +{ + switch (binop) { + case '+': + return (new add(args))->setflag(status_flags::dynallocated); + case '-': + return make_minus_expr(args); + case '*': + return (new mul(args))->setflag(status_flags::dynallocated); + case '/': + return make_divide_expr(args); + case '^': + if (args.size() != 2) + throw std::invalid_argument( + std::string(__func__) + + ": power should have exactly 2 operands"); + return pow(args[0], args[1]); + default: + throw std::invalid_argument( + std::string(__func__) + + ": invalid binary operation: " + + char(binop)); + } +} + +static inline bool is_binop(const int c) +{ + switch (c) { + case '+': + case '-': + case '*': + case '/': + case '^': + return true; + default: + return false; + } +} + +/// Get the precedence of the pending binary operator. +static int get_tok_prec(const int c) +{ + switch (c) { + case '+': + case '-': + return 20; + case '*': + case '/': + return 40; + case '^': + return 60; + default: + return -1; + // means 'this is not a binary operator' + } +} + +} // namespace GiNaC + diff --git a/ginac/parser/parse_context.cpp b/ginac/parser/parse_context.cpp new file mode 100644 index 0000000..e4956ba --- /dev/null +++ b/ginac/parser/parse_context.cpp @@ -0,0 +1,27 @@ +#include "parse_context.hpp" +#include +#include +namespace GiNaC +{ + +const symbol& +find_or_insert_symbol(const std::string& name, symtab& syms, const bool strict) +{ + symtab::const_iterator p = syms.find(name); + if (p != syms.end()) + return p->second.first; + + if (strict) + throw std::invalid_argument( + std::string("find_or_insert_symbol: symbol \"") + + name + "\" not found"); + + // false means this symbol was created by parser + const std::pair tmp = std::make_pair(symbol(name), false); + + symtab::iterator i = syms.insert(symtab::value_type(name, tmp)).first; + return i->second.first; +} + +} + diff --git a/ginac/parser/parse_context.hpp b/ginac/parser/parse_context.hpp new file mode 100644 index 0000000..d0a3d55 --- /dev/null +++ b/ginac/parser/parse_context.hpp @@ -0,0 +1,78 @@ +#ifndef _GINAC_PARSE_CONTEXT_HPP +#define _GINAC_PARSE_CONTEXT_HPP +#include +#include // size_t +#include "ex.h" +#include "symbol.h" +#include +#include + +namespace GiNaC +{ + +/** + * Establishes correspondence between the strings and symbols. + * The parser will create missing symbols (if not instructed otherwise, + * in which case it fails if the expression contains unknown symbols). + * The .second element of pair helps to distinguish between the user + * supplied symbols and parser generated ones. The .first is the symbol + * itself + */ +typedef std::map > symtab; + +/** + * Find the symbol with the @a name in the symbol table @a syms. + * + * If symbol is missing and @a strict = false, insert it, otherwise + * throw an exception. + */ +extern const symbol& +find_or_insert_symbol(const std::string& name, symtab& syms, + const bool strict); + +/** + * Function (or class ctor) prototype + * .first is the name of function(or ctor), + * .second is the number of arguments (each of type ex) + */ +typedef std::pair prototype; + +/** + * A (C++) function for reading functions and classes from the stream. + * + * The parser uses (an associative array of) such functions to construct + * (GiNaC) classes and functions from a sequence of characters. + */ +typedef ex (*reader_func)(const exvector& args); + +/** + * Prototype table. + * + * If parser sees an expression which looks like a function call (e.g. + * foo(x+y, z^2, t)), it looks up such a table to find out which + * function (or class) corresponds to the given name and has the given + * number of the arguments. + * + * N.B. + * + * 1. The function don't have to return a (GiNaC) function or class, it + * can return any expression. + * 2. Overloaded functions/ctors are paritally supported, i.e. there might + * be several functions with the same name, but they should take different + * number of arguments. + * 3. User can extend the parser via custom prototype tables. It's possible + * to read user defined classes, create abbreviations, etc. + */ +typedef std::map prototype_table; + +/** + * Default prototype table. + * + * It supports most of builtin GiNaC functions. + */ +extern const prototype_table& get_default_reader(); + +} + +#endif // _GINAC_PARSE_CONTEXT_HPP + diff --git a/ginac/parser/parser.cpp b/ginac/parser/parser.cpp new file mode 100644 index 0000000..b33261c --- /dev/null +++ b/ginac/parser/parser.cpp @@ -0,0 +1,173 @@ +#include +#include +#include "parser.hpp" +#include "lexer.hpp" +#include "debug.hpp" +#include "mul.h" +#include "constant.h" + +namespace GiNaC +{ + +/// identifier_expr: identifier | identifier '(' expression* ')' +ex parser::parse_identifier_expr() +{ + std::string name = scanner->str; + get_next_tok(); // eat identifier. + + if (token != '(') // symbol + return find_or_insert_symbol(name, syms, strict); + + // function/ctor call. + get_next_tok(); // eat ( + exvector args; + if (token != ')') { + while (true) { + ex e = parse_expression(); + args.push_back(e); + + if (token == ')') + break; + + if (token != ',') + throw std::invalid_argument("Expected ')' or ',' in argument list"); + + get_next_tok(); + } + } + // Eat the ')'. + get_next_tok(); + prototype the_prototype = make_pair(name, args.size()); + prototype_table::const_iterator reader = funcs.find(the_prototype); + if (reader == funcs.end()) { + bail_out(std::invalid_argument, + "no function \"" << name << "\" with " << args.size() + << " arguments"); + } + ex ret = reader->second(args); + return ret; +} + +/// paren_expr: '(' expression ')' +ex parser::parse_paren_expr() +{ + get_next_tok(); // eat (. + ex e = parse_expression(); + + if (token != ')') + throw std::invalid_argument("parser::parse_paren_expr: expected ')'"); + get_next_tok(); // eat ). + return e; +} + +extern numeric* _num_1_p; + +/// unary_expr: [+-] expression +ex parser::parse_unary_expr(const int s) +{ + // consume '-' (or '+') + get_next_tok(); + ex v = parse_expression(); + switch (s) { + case '-': + return (new mul(v, *_num_1_p))->setflag(status_flags::dynallocated); + case '+': + return v; + default: + throw std::invalid_argument( + std::string(__func__) + + ": invalid unary operator \"" + + char(s) + "\""); + } +} + +/// primary: identifier_expr | number_expr | paren_expr | unary_expr +ex parser::parse_primary() +{ + switch (token) { + case lexer::token_type::identifier: + return parse_identifier_expr(); + case lexer::token_type::number: + return parse_number_expr(); + case '(': + return parse_paren_expr(); + case '-': + return parse_unary_expr('-'); + case '+': + return parse_unary_expr('+'); + case lexer::token_type::literal: + return parse_literal_expr(); + case lexer::token_type::eof: + bail_out(std::invalid_argument, "got EOF while parsing the expression"); + default: + bail_out(std::invalid_argument, "unknown token " << + token << " (\"" << + (token ? std::string("") + char(token) : "NULL") + << "\")"); + } +} + +/// expression ::= primary binoprhs +ex parser::parse_expression() +{ + ex lhs = parse_primary(); + ex res = parse_binop_rhs(0, lhs); + return res; +} + +/// number_expr: number +ex parser::parse_number_expr() +{ + ex n = numeric(scanner->str.c_str()); + get_next_tok(); // consume the number + return n; +} + +/// literal_expr: 'I' | 'Pi' | 'Euler' | 'Catalan' +ex parser::parse_literal_expr() +{ + if (scanner->str == "I") + return I; + else if (scanner->str == "Pi") + return Pi; + else if (scanner->str == "Euler") + return Euler; + else if (scanner->str == "Catalan") + return Catalan; + bug("unknown literal: \"" << scanner->str << "\""); +} + +ex parser::operator()(std::istream& input) +{ + scanner->switch_input(&input); + get_next_tok(); + ex ret = parse_expression(); + return ret; +} + +ex parser::operator()(const std::string& input) +{ + std::istringstream is(input); + ex ret = operator()(is); + return ret; +} + +int parser::get_next_tok() +{ + token = scanner->gettok(); + return token; +} + +parser::parser(const symtab& syms_, const prototype_table& funcs_, + const bool strict_) : strict(strict_), funcs(funcs_), + syms(syms_) +{ + scanner = new lexer(); +} + +parser::~parser() +{ + delete scanner; +} + +} // namespace GiNaC diff --git a/ginac/parser/parser.hpp b/ginac/parser/parser.hpp new file mode 100644 index 0000000..81b7847 --- /dev/null +++ b/ginac/parser/parser.hpp @@ -0,0 +1,95 @@ +#ifndef GINAC_PARSER_HPP_ + +#include "parse_context.hpp" +#include +#include "ex.h" + +namespace GiNaC +{ + +class lexer; + +/** + * Recursive descent parser for GiNaC expressions. + */ +class parser +{ + // The actual parser rules (in EBNF-alike notation): + + /// expression: primary binoprhs + ex parse_expression(); + + /// primary: indentifier_expr | number_expr | paren_expr | unary_expr + ex parse_primary(); + + /// binoprhs: ([+*/^-] primary)* + ex parse_binop_rhs(int, ex&); + + /// identifier_expr: identifier | + /// identifier '(' expression (',' expression)* ')' + ex parse_identifier_expr(); + + /// paren_expr: '(' expression ')' + ex parse_paren_expr(); + + /// number_expr: number + ex parse_number_expr(); + + /// unary_expr: [+-] expression + ex parse_unary_expr(const int c); + + /// literal_expr: 'I' | 'Pi' | 'Euler' | 'Catalan' + ex parse_literal_expr(); + +public: + /** + * @param syms_ symbol table. + * @param funcs_ function/ctors table. + * @param strict_ if true, throw an exception if unknown + * symbol is encountered. + */ + parser(const symtab& syms_ = symtab(), + const prototype_table& funcs_ = get_default_reader(), + const bool strict_ = false); + ~parser(); + + /// parse the stream @a input + ex operator()(std::istream& input); + /// parse the string @a input + ex operator()(const std::string& input); + + /// report the symbol table used by parser + symtab get_syms() const + { + return syms; + } + +private: + /// If true, throw an exception if an unknown symbol is encountered. + const bool strict; + /** + * Function/ctor table, maps a prototype (which is a name and number + * arguments) to a C++ function. Used for parsing identifier_expr's + * (see parse_identifier_expr). If expression contains unknown + * prototype, an exception will be thrown. + */ + const prototype_table funcs; + /** + * Symbol (variable) table. Used for parsing identifier_expr's + * (see parse_identifier_expr). If parser is strict, exception is + * thrown if an unknown symbol is encountered. Non-strict parser + * appends unknown symbols to the symbol table. + */ + symtab syms; + /// token scanner + lexer* scanner; + /// current token the parser is looking at + int token; + /// read the next token from the scanner + int get_next_tok(); +}; + +} // namespace GiNaC + +#endif // GINAC_PARSER_HPP_ + -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Tue Aug 19 22:35:20 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Wed, 20 Aug 2008 00:35:20 +0400 Subject: [GiNaC-devel] [PATCH] Faster, better (recursive descent) expression parser. In-Reply-To: <328471.48864.qm@web35207.mail.mud.yahoo.com> References: <20080819180940.GA22026@theor.jinr.ru> <328471.48864.qm@web35207.mail.mud.yahoo.com> Message-ID: <20080819203520.GB29147@theor.jinr.ru> Hello, On Tue, Aug 19, 2008 at 12:30:46PM -0700, Sergei Steshenko wrote: > Just wondering - how good for the purpose can be the Elkhound parser: > > http://www.cs.berkeley.edu/~smcpeak/elkhound/ When parsing a sum with many terms I need to accumulate all operands, and create an add object from these (otherwise performance would be awful). IMHO LR parser is inconvenient (and unnatural) for this task. Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From git at ginac.de Wed Aug 20 22:23:47 2008 From: git at ginac.de (Jens Vollinga) Date: Wed, 20 Aug 2008 22:23:47 +0200 (CEST) Subject: [GiNaC-devel] [SCM] GiNaC -- a C++ library for symbolic computations branch, master, updated. release_1-4-0-62-g37c418c Message-ID: <20080820202347.EC8BD5B4042@cebix.net> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GiNaC -- a C++ library for symbolic computations". The branch, master has been updated via 37c418c602485be9b023378a1b60ebdb4a592f2a (commit) from f3eefbda588318e09dcb3180c6f039dc3fc30f87 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 37c418c602485be9b023378a1b60ebdb4a592f2a Author: Jens Vollinga Date: Wed Aug 20 22:16:13 2008 +0200 Fixed bug in the Q matrix calculation and the univariate factorization function. ----------------------------------------------------------------------- Summary of changes: check/exam_factor.cpp | 9 +++++++- ginac/factor.cpp | 51 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 17 deletions(-) hooks/post-receive -- GiNaC -- a C++ library for symbolic computations From jensv at nikhef.nl Wed Aug 20 22:33:47 2008 From: jensv at nikhef.nl (Jens Vollinga) Date: Wed, 20 Aug 2008 22:33:47 +0200 Subject: [GiNaC-devel] [PATCH] Faster, better (recursive descent) expression parser. In-Reply-To: <20080819203520.GB29147@theor.jinr.ru> References: <20080819180940.GA22026@theor.jinr.ru> <328471.48864.qm@web35207.mail.mud.yahoo.com> <20080819203520.GB29147@theor.jinr.ru> Message-ID: <48AC7FAB.4030603@nikhef.nl> Hi, concerning your patch: do you really need autogen?!? Could not m4 do the job? While I have no problem installing autogen on every system I use, I know quite some people which would need to go to their admin and explicitly ask for it to be installed (being then asked nasty question like: why, why now, are you sure, ...) which might make it a show-stopper. Regards, Jens From jensv at nikhef.nl Wed Aug 20 22:51:28 2008 From: jensv at nikhef.nl (Jens Vollinga) Date: Wed, 20 Aug 2008 22:51:28 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48A9F0EA.6010807@ginac.de> References: <48A00935.70004@nikhef.nl> <48A88EA5.4040901@ginac.de> <48A9D18F.1070804@nikhef.nl> <48A9F0EA.6010807@ginac.de> Message-ID: <48AC83D0.1060502@nikhef.nl> Hi, Richard B. Kreckel schrieb: > Now, again in ginsh notation: > > factor(expand((x+4+x^2-x^3+43*x^4)*(x+1-x^2-3*x^3+4*x^4))); > Segmentation fault thanks again! >> Since you already looked at the code I should maybe comment on it with >> respect to cln: there is quite some stuff in factor.cpp that actually >> could be part of cln. It is mainly the type/class for modular univariate >> polynomials. If you compare the features of my class with the equivalent >> type in cln you can see why I needed this additional type. > > Well, I don't see. :-( > What is your point? Well, maybe I should have put my statement differently. Of course, there is no ultimate reason why the cln class could not have been used from the beginning on in the factor code. Obviously, everthing can be done with the cln class and several additional functions that do the job of the missing methods! But I started out missing several things and then, not sure what the future algorithms might need in addition, wrote a convenient class. If all is done, maybe some of the methods could go into the cln class, probably not things like to_ex() or operator<(), but other things like unit_normal() or divide(). Regards, Jens From kreckel at ginac.de Thu Aug 21 00:16:12 2008 From: kreckel at ginac.de (Richard B. Kreckel) Date: Thu, 21 Aug 2008 00:16:12 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48AC83D0.1060502@nikhef.nl> References: <48A00935.70004@nikhef.nl> <48A88EA5.4040901@ginac.de> <48A9D18F.1070804@nikhef.nl> <48A9F0EA.6010807@ginac.de> <48AC83D0.1060502@nikhef.nl> Message-ID: <48AC97AC.2020906@ginac.de> Dear Jens, Jens Vollinga wrote: > Richard B. Kreckel schrieb: >> Now, again in ginsh notation: >> > factor(expand((x+4+x^2-x^3+43*x^4)*(x+1-x^2-3*x^3+4*x^4))); >> Segmentation fault > > thanks again! And thank *you* for the patch which indeed fixes that problem! I hope I'm not being too pesky, but that square-free factorization keeps attracting my mind. And now I am getting: > factor(expand((1-x+x^2-x^3)*x^2)); Internal error: statement in file ./float/division/cl_F_ceil1.cc, line 21 has been reached!! Please send the authors of the program a description how you produced this error! Sorry for being such a killjoy. Cheers -richy. -- Richard B. Kreckel From jensv at nikhef.nl Thu Aug 21 00:48:28 2008 From: jensv at nikhef.nl (Jens Vollinga) Date: Thu, 21 Aug 2008 00:48:28 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48AC97AC.2020906@ginac.de> References: <48A00935.70004@nikhef.nl> <48A88EA5.4040901@ginac.de> <48A9D18F.1070804@nikhef.nl> <48A9F0EA.6010807@ginac.de> <48AC83D0.1060502@nikhef.nl> <48AC97AC.2020906@ginac.de> Message-ID: <48AC9F3C.5060008@nikhef.nl> Hi, Richard B. Kreckel schrieb: > I hope I'm not being too pesky, but that square-free factorization keeps > attracting my mind. And now I am getting: what is behind this curiosity? New feature in cln maybe? :-) > > factor(expand((1-x+x^2-x^3)*x^2)); > Internal error: statement in file ./float/division/cl_F_ceil1.cc, line > 21 has been reached!! > Please send the authors of the program a description how you produced > this error! It is no problem of the sqrfree function. Actually, you can help me with this bug. The problematic line is cl_I normmc = ceiling1(the(cln::sqrt(ex_to(maxcoeff).to_cl_N()))); maxcoeff is 4 in this case. The line cl_I normmc = ceiling1(the(cln::sqrt(ex_to(maxcoeff).to_cl_N()))); does work in this case (cl_F -> cl_I in the cast). Why?!? It is just too late to read the manual ... > Sorry for being such a killjoy. Well, I have to seriously warn you: any more bugs and the multivariate factorization will be delayed even more! ;-) Jens From kreckel at ginac.de Thu Aug 21 08:24:44 2008 From: kreckel at ginac.de (Richard B. Kreckel) Date: Thu, 21 Aug 2008 08:24:44 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48AC9F3C.5060008@nikhef.nl> References: <48A00935.70004@nikhef.nl> <48A88EA5.4040901@ginac.de> <48A9D18F.1070804@nikhef.nl> <48A9F0EA.6010807@ginac.de> <48AC83D0.1060502@nikhef.nl> <48AC97AC.2020906@ginac.de> <48AC9F3C.5060008@nikhef.nl> Message-ID: <48AD0A2C.4070703@ginac.de> Good morning Jens, Jens Vollinga wrote: > Richard B. Kreckel schrieb: >> I hope I'm not being too pesky, but that square-free factorization keeps >> attracting my mind. And now I am getting: > > what is behind this curiosity? New feature in cln maybe? :-) No, there's no code in the pipeline. >> > factor(expand((1-x+x^2-x^3)*x^2)); >> Internal error: statement in file ./float/division/cl_F_ceil1.cc, line >> 21 has been reached!! >> Please send the authors of the program a description how you produced >> this error! > > It is no problem of the sqrfree function. Actually, you can help me with > this bug. The problematic line is > > cl_I normmc = > ceiling1(the(cln::sqrt(ex_to(maxcoeff).to_cl_N()))); > > maxcoeff is 4 in this case. > > The line > > cl_I normmc = > ceiling1(the(cln::sqrt(ex_to(maxcoeff).to_cl_N()))); > > does work in this case (cl_F -> cl_I in the cast). > > Why?!? It is just too late to read the manual ... Each of the classes `cl_R', `cl_F', `cl_SF', `cl_FF', `cl_DF', `cl_LF' defines `TYPE sqrt (const TYPE& x)'. `x' must be >= 0. This function returns the square root of `x', normalized to be >= 0. If `x' is the square of a rational number, `sqrt(x)' will be a rational number, else it will return a floating-point approximation. So, cln::sqrt(cln::cl_N(4)) returns cl_N(2), an exact integer, but of static type cl_N. Casting that to cl_F, a float triggers the error. (You can't use the for constructing floating-point numbers, you should use the contructors.) But in your case, converting the(...) will convert to static type cl_R, which can at runtime be either integer or rational or floating-point, but not complex. Indeed, it seems to fix things. >> Sorry for being such a killjoy. > > Well, I have to seriously warn you: any more bugs and the multivariate > factorization will be delayed even more! ;-) I'm looking forward to testing the multivariate case, too! :-) nice day -richy. -- Richard B. Kreckel -------------- next part -------------- A non-text attachment was scrubbed... Name: factor.patch Type: text/x-patch Size: 654 bytes Desc: not available URL: From kreckel at ginac.de Thu Aug 21 08:28:23 2008 From: kreckel at ginac.de (Richard B. Kreckel) Date: Thu, 21 Aug 2008 08:28:23 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48AD0A2C.4070703@ginac.de> References: <48A00935.70004@nikhef.nl> <48A88EA5.4040901@ginac.de> <48A9D18F.1070804@nikhef.nl> <48A9F0EA.6010807@ginac.de> <48AC83D0.1060502@nikhef.nl> <48AC97AC.2020906@ginac.de> <48AC9F3C.5060008@nikhef.nl> <48AD0A2C.4070703@ginac.de> Message-ID: <48AD0B07.5070705@ginac.de> Dear Jens, A couple of minuts ago, I wrote: >>> Sorry for being such a killjoy. >> >> Well, I have to seriously warn you: any more bugs and the multivariate >> factorization will be delayed even more! ;-) > > I'm looking forward to testing the multivariate case, too! :-) > > nice day > -richy. Ah, I haven't had my morning coffee yet. So I forgot to include the bug of the day: > factor(x); Segmentation fault :-) fun -richy. -- Richard B. Kreckel From varg at theor.jinr.ru Thu Aug 21 10:44:54 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Thu, 21 Aug 2008 12:44:54 +0400 Subject: [GiNaC-devel] [PATCH] Faster, better (recursive descent) expression parser. In-Reply-To: <48AC7FAB.4030603@nikhef.nl> References: <20080819180940.GA22026@theor.jinr.ru> <328471.48864.qm@web35207.mail.mud.yahoo.com> <20080819203520.GB29147@theor.jinr.ru> <48AC7FAB.4030603@nikhef.nl> Message-ID: <20080821084453.GA23072@theor.jinr.ru> Hello, On Wed, Aug 20, 2008 at 10:33:47PM +0200, Jens Vollinga wrote: > concerning your patch: do you really need autogen?!? Yes. I don't want to write lots of boilerplate code manually. > Could not m4 do the job? Let's put it this way: the only thing m4 is good for is obfuscating the code. > While I have no problem installing autogen on every system I use, I know > quite some people which would need to go to their admin and explicitly > ask for it to be installed First of all, autogen is not necessary to build GiNaC from tarball. Secondly, installing autogen does not require superuser privileges. > (being then asked nasty question like: why, why now, are you sure, ...) I wonder how those admins compile GCC :) Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From jensv at nikhef.nl Thu Aug 21 10:56:02 2008 From: jensv at nikhef.nl (Jens Vollinga) Date: Thu, 21 Aug 2008 10:56:02 +0200 Subject: [GiNaC-devel] [PATCH] Faster, better (recursive descent) expression parser. In-Reply-To: <20080821084453.GA23072@theor.jinr.ru> References: <20080819180940.GA22026@theor.jinr.ru> <328471.48864.qm@web35207.mail.mud.yahoo.com> <20080819203520.GB29147@theor.jinr.ru> <48AC7FAB.4030603@nikhef.nl> <20080821084453.GA23072@theor.jinr.ru> Message-ID: <48AD2DA2.7020805@nikhef.nl> Hi, Alexei Sheplyakov schrieb: > First of all, autogen is not necessary to build GiNaC from tarball. Secondly, ups, I overlooked this fact. Then I don't mind. > I wonder how those admins compile GCC :) Out of curiosity: What do you mean? I don't have autogen on the two boxes where I regularly compile gcc ... and it works. It is probably only needed if you do fancy things, or isn't it? Regards, Jens From varg at theor.jinr.ru Thu Aug 21 11:53:57 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Thu, 21 Aug 2008 13:53:57 +0400 Subject: [GiNaC-devel] [Slightly OT]: about autogen In-Reply-To: <48AD2DA2.7020805@nikhef.nl> References: <20080819180940.GA22026@theor.jinr.ru> <328471.48864.qm@web35207.mail.mud.yahoo.com> <20080819203520.GB29147@theor.jinr.ru> <48AC7FAB.4030603@nikhef.nl> <20080821084453.GA23072@theor.jinr.ru> <48AD2DA2.7020805@nikhef.nl> Message-ID: <20080821095357.GA24549@theor.jinr.ru> Hi, On Thu, Aug 21, 2008 at 10:56:02AM +0200, Jens Vollinga wrote: > > I wonder how those admins compile GCC :) > > Out of curiosity: What do you mean? GCC's toplevel Makefile.in and fixincludes are produced by autogen, see http://gcc.gnu.org/install/prerequisites.html > I don't have autogen on the two > boxes where I regularly compile gcc ... and it works. Because tarballs already include generated files. Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From sergstesh at yahoo.com Thu Aug 21 12:08:16 2008 From: sergstesh at yahoo.com (Sergei Steshenko) Date: Thu, 21 Aug 2008 03:08:16 -0700 (PDT) Subject: [GiNaC-devel] [PATCH] Faster, better (recursive descent) expression parser. In-Reply-To: <48AD2DA2.7020805@nikhef.nl> Message-ID: <551132.77482.qm@web35204.mail.mud.yahoo.com> --- On Thu, 8/21/08, Jens Vollinga wrote: > From: Jens Vollinga > Subject: Re: [GiNaC-devel] [PATCH] Faster, better (recursive descent) expression parser. > To: "GiNaC development list" > Date: Thursday, August 21, 2008, 1:56 AM > Hi, > > Alexei Sheplyakov schrieb: > > First of all, autogen is not necessary to build GiNaC > from tarball. Secondly, > > ups, I overlooked this fact. Then I don't mind. > > > I wonder how those admins compile GCC :) > > Out of curiosity: What do you mean? I don't have > autogen on the two > boxes where I regularly compile gcc ... and it works. It is > probably > only needed if you do fancy things, or isn't it? > > Regards, > Jens > _______________________________________________ > GiNaC-devel mailing list > GiNaC-devel at ginac.de > https://www.cebix.net/mailman/listinfo/ginac-devel Guys, thanks to the standard ./configure --prefix=...... make make install sequence if one is persistent enough, he/she can build everything from scratch not having root permissions. I have even automated the process in my AppsFromScratch; 'ginac' is built among many other targets. Gnu autotools are also built, as well as 'gcc'. So, in this subthread you are not discussing a critical issue (presence/absence of GNU autottols), but rather an issue of convenience. Regards, Sergei. From git at ginac.de Thu Aug 21 22:36:11 2008 From: git at ginac.de (Jens Vollinga) Date: Thu, 21 Aug 2008 22:36:11 +0200 (CEST) Subject: [GiNaC-devel] [SCM] GiNaC -- a C++ library for symbolic computations branch, master, updated. release_1-4-0-63-g321d5c9 Message-ID: <20080821203612.14BAE5B4046@cebix.net> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GiNaC -- a C++ library for symbolic computations". The branch, master has been updated via 321d5c941f81b1d0b86de91f3f17c5af5b6e4642 (commit) from 37c418c602485be9b023378a1b60ebdb4a592f2a (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 321d5c941f81b1d0b86de91f3f17c5af5b6e4642 Author: Jens Vollinga Date: Thu Aug 21 22:37:09 2008 +0200 Daily bugfix in the polynomial factorization (code didn't catch polynomial "x" and cln::sqrt conversion was buggy). ----------------------------------------------------------------------- Summary of changes: check/exam_factor.cpp | 7 ++++++- ginac/factor.cpp | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) hooks/post-receive -- GiNaC -- a C++ library for symbolic computations From git at ginac.de Thu Aug 21 22:43:51 2008 From: git at ginac.de (Jens Vollinga) Date: Thu, 21 Aug 2008 22:43:51 +0200 (CEST) Subject: [GiNaC-devel] [SCM] GiNaC -- a C++ library for symbolic computations branch, master, updated. release_1-4-0-64-g1222eac Message-ID: <20080821204351.DA6C75B404B@cebix.net> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GiNaC -- a C++ library for symbolic computations". The branch, master has been updated via 1222eac51cee964961d2aad889dc4ceccb144a36 (commit) from 321d5c941f81b1d0b86de91f3f17c5af5b6e4642 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 1222eac51cee964961d2aad889dc4ceccb144a36 Author: Alexei Sheplyakov Date: Tue Aug 19 23:48:03 2008 +0400 Faster, better (recursive descent) expression parser. Ouch, it fails to compile. Here is a correct version: From: Alexei Sheplyakov Date: Tue, 19 Aug 2008 21:50:03 +0400 Subject: [PATCH] Faster, better (recursive descent) expression parser. bison generated parser has a number of problems: 1. Bad performance. Parsing a sum seems to be O(N^{2 + a}) (a > 0) [1]. For example, parsing a sum (actually, a univariate polynomial) of 32768 terms takes about 90 sec. on my box, parsing a sum of 10^6 terms takes "eternity". 2. The user is expected to provide list of all symbols in the expression. Often this is very annoying (and useless). 3. Parser is not reentrant (bison *can* produce reentrant parsers, but that won't solve other problems). 4. Parser is difficult to extend. Hence the new parser. Features: 1. Parsing large sums and products is O(N). 2. Parser is reentrant (well, almost). 3. It's possible to insert (shell style) comments inside the expressions. 4. matrices, lists, FAIL are NOT handled. Yes, this *is* a feature :-) Limitations: 1. Error handling is a bit terse: on error exception is thrown, and that's it. 2. Binary, octal, and hexadecimal numbers can not be parsed (yet). 3. Tensors, noncommutative products, etc. can not be parsed. Other notes: 1. ex ctor still uses the old parser. 2. ginsh still uses the old parser. [1] Mesured by this script (requires gnuplot): make_expression_string () { printf "1 + x" local n=2 while test $n -le $1; do printf " + %s*x^%s" $n $n n=$(expr $n + 1) done } single_test () { printf "$1 \t" ( printf "time("; make_expression_string $1; printf ");" ) | \ ginsh | sed -e 's/s$//' } benchmark () { local N=$1 while test $N -le $2; do single_test $N N=$(expr $N \* 2) done } gnuplot_header () { echo "set logscale xy" echo "set xlabel 'degree (# of terms)'" echo "set ylabel 'time, sec.'" echo "set xrange [${1}:${2}]" echo "plot '-' using 1:2 with lines title '1+x+2*x^2+...+n*x^n'" } gnuplot_footer () { echo "e" } benchmark_and_plot () { ( gnuplot_header $1 $2 benchmark $1 $2 | tee $3.txt gnuplot_footer ) | \ gnuplot -persist '-' } N_ini=${1:-1024} N_fin=${2:-32768} out_base=${3:-parser_benchmark} benchmark_and_plot $N_ini $N_fin $out_base ----------------------------------------------------------------------- Summary of changes: INSTALL | 3 +- check/Makefile.am | 8 ++- check/time_parser.cpp | 79 +++++++++++++++++ configure.ac | 4 + ginac/Makefile.am | 31 ++++++- ginac/parser/builtin_fcns.def | 90 +++++++++++++++++++ ginac/parser/debug.hpp | 35 ++++++++ ginac/parser/default_reader.tpl | 50 +++++++++++ ginac/parser/lexer.cpp | 132 ++++++++++++++++++++++++++++ ginac/parser/lexer.hpp | 45 ++++++++++ ginac/parser/parse_binop_rhs.cpp | 176 ++++++++++++++++++++++++++++++++++++++ ginac/parser/parse_context.cpp | 27 ++++++ ginac/parser/parse_context.hpp | 78 +++++++++++++++++ ginac/parser/parser.cpp | 173 +++++++++++++++++++++++++++++++++++++ ginac/parser/parser.hpp | 95 ++++++++++++++++++++ 15 files changed, 1021 insertions(+), 5 deletions(-) create mode 100644 check/time_parser.cpp create mode 100644 ginac/parser/builtin_fcns.def create mode 100644 ginac/parser/debug.hpp create mode 100644 ginac/parser/default_reader.tpl create mode 100644 ginac/parser/lexer.cpp create mode 100644 ginac/parser/lexer.hpp create mode 100644 ginac/parser/parse_binop_rhs.cpp create mode 100644 ginac/parser/parse_context.cpp create mode 100644 ginac/parser/parse_context.hpp create mode 100644 ginac/parser/parser.cpp create mode 100644 ginac/parser/parser.hpp hooks/post-receive -- GiNaC -- a C++ library for symbolic computations From kreckel at ginac.de Thu Aug 21 23:48:10 2008 From: kreckel at ginac.de (Richard B. Kreckel) Date: Thu, 21 Aug 2008 23:48:10 +0200 Subject: [GiNaC-devel] Master head broken? In-Reply-To: <20080821204351.DA6C75B404B@cebix.net> References: <20080821204351.DA6C75B404B@cebix.net> Message-ID: <48ADE29A.1010409@ginac.de> Hi, The current master branch fails to compile after the last update: > The branch, master has been updated > via 1222eac51cee964961d2aad889dc4ceccb144a36 (commit) > from 321d5c941f81b1d0b86de91f3f17c5af5b6e4642 (commit) > Faster, better (recursive descent) expression parser. G++ complains that parser/builtin_fcns.cpp is missing. Indeed, I only find the file parser/builtin_fcns.def. (I also tried a fresh git clone but the problem is the same there.) Cheers -richy. -- Richard B. Kreckel From varg at theor.jinr.ru Fri Aug 22 00:10:12 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Fri, 22 Aug 2008 02:10:12 +0400 Subject: [GiNaC-devel] Master head broken? -- No. In-Reply-To: <48ADE29A.1010409@ginac.de> References: <20080821204351.DA6C75B404B@cebix.net> <48ADE29A.1010409@ginac.de> Message-ID: <20080821221012.GA14180@theor.jinr.ru> Dear Richard, On Thu, Aug 21, 2008 at 11:48:10PM +0200, Richard B. Kreckel wrote: > The current master branch fails to compile after the last update: > > The branch, master has been updated > > via 1222eac51cee964961d2aad889dc4ceccb144a36 (commit) > > from 321d5c941f81b1d0b86de91f3f17c5af5b6e4642 (commit) > > Faster, better (recursive descent) expression parser. > > G++ complains that parser/builtin_fcns.cpp is missing. That file is supposed to be automatically generated during the build. And if necessary tool (GNU autogen) is not installed, a helpful error message should be printed. Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Fri Aug 22 00:42:17 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Fri, 22 Aug 2008 02:42:17 +0400 Subject: [GiNaC-devel] [PATCH] Bail out if both autogen and autogen'erated file(s) are missing. In-Reply-To: <20080821221012.GA14180@theor.jinr.ru> References: <20080821204351.DA6C75B404B@cebix.net> <48ADE29A.1010409@ginac.de> <20080821221012.GA14180@theor.jinr.ru> Message-ID: <20080821224217.GA14863@theor.jinr.ru> This makes the error message(s) more helpful. --- ginac/Makefile.am | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/ginac/Makefile.am b/ginac/Makefile.am index 0570fde..17b272f 100644 --- a/ginac/Makefile.am +++ b/ginac/Makefile.am @@ -45,6 +45,7 @@ $(srcdir)/parser/builtin_fcns.cpp: $(srcdir)/parser/builtin_fcns.def $(srcdir)/p else \ echo "*** ERROR: the \"$@\" file does not exist, and AutoGen is not installed on your system"; \ echo "*** Please install AutoGen (http://www.gnu.org/software/autogen)"; \ + exit 1; \ fi -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Sun Aug 24 13:25:24 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Sun, 24 Aug 2008 15:25:24 +0400 Subject: [GiNaC-devel] [PATCH] inifscn_nstdsums: make functions handling Li/G transformations reentrant. Message-ID: <20080824112524.GA25941@theor.jinr.ru> Explicitly pass the dummy symbols instead of using a global variable. As a side effect, the code is more clear now (that's a bit subjective, but anyway). --- ginac/inifcns_nstdsums.cpp | 128 +++++++++++++++++++++++++------------------ 1 files changed, 74 insertions(+), 54 deletions(-) diff --git a/ginac/inifcns_nstdsums.cpp b/ginac/inifcns_nstdsums.cpp index 01a5756..dd8625a 100644 --- a/ginac/inifcns_nstdsums.cpp +++ b/ginac/inifcns_nstdsums.cpp @@ -518,16 +518,12 @@ cln::cl_N mLi_do_summation(const lst& m, const lst& x) lst convert_parameter_Li_to_H(const lst& m, const lst& x, ex& pf); -// holding dummy-symbols for the G/Li transformations -std::vector gsyms; - - // type used by the transformation functions for G typedef std::vector Gparameter; // G_eval1-function for G transformations -ex G_eval1(int a, int scale) +ex G_eval1(int a, int scale, const exvector& gsyms) { if (a != 0) { const ex& scs = gsyms[std::abs(scale)]; @@ -544,7 +540,7 @@ ex G_eval1(int a, int scale) // G_eval-function for G transformations -ex G_eval(const Gparameter& a, int scale) +ex G_eval(const Gparameter& a, int scale, const exvector& gsyms) { // check for properties of G ex sc = gsyms[std::abs(scale)]; @@ -578,7 +574,7 @@ ex G_eval(const Gparameter& a, int scale) for (; it != a.end(); ++it) { short_a.push_back(*it); } - ex result = G_eval1(a.front(), scale) * G_eval(short_a, scale); + ex result = G_eval1(a.front(), scale, gsyms) * G_eval(short_a, scale, gsyms); it = short_a.begin(); for (int i=1; i G({1};y)^k / k! if (all_ones && a.size() > 1) { - return pow(G_eval1(a.front(),scale), count_ones) / factorial(count_ones); + return pow(G_eval1(a.front(),scale, gsyms), count_ones) / factorial(count_ones); } // G({0,...,0};y) -> log(y)^k / k! @@ -696,7 +692,7 @@ Gparameter prepare_pending_integrals(const Gparameter& pending_integrals, int sc // handles trailing zeroes for an otherwise convergent integral -ex trailing_zeros_G(const Gparameter& a, int scale) +ex trailing_zeros_G(const Gparameter& a, int scale, const exvector& gsyms) { bool convergent; int depth, trailing_zeros; @@ -708,23 +704,23 @@ ex trailing_zeros_G(const Gparameter& a, int scale) if ((trailing_zeros > 0) && (depth > 0)) { ex result; Gparameter new_a(a.begin(), a.end()-1); - result += G_eval1(0, scale) * trailing_zeros_G(new_a, scale); + result += G_eval1(0, scale, gsyms) * trailing_zeros_G(new_a, scale, gsyms); for (Gparameter::const_iterator it = a.begin(); it != last; ++it) { Gparameter new_a(a.begin(), it); new_a.push_back(0); new_a.insert(new_a.end(), it, a.end()-1); - result -= trailing_zeros_G(new_a, scale); + result -= trailing_zeros_G(new_a, scale, gsyms); } return result / trailing_zeros; } else { - return G_eval(a, scale); + return G_eval(a, scale, gsyms); } } // G transformation [VSW] (57),(58) -ex depth_one_trafo_G(const Gparameter& pending_integrals, const Gparameter& a, int scale) +ex depth_one_trafo_G(const Gparameter& pending_integrals, const Gparameter& a, int scale, const exvector& gsyms) { // pendint = ( y1, b1, ..., br ) // a = ( 0, ..., 0, amin ) @@ -755,15 +751,21 @@ ex depth_one_trafo_G(const Gparameter& pending_integrals, const Gparameter& a, i result -= I*Pi; } if (psize) { - result *= trailing_zeros_G(convert_pending_integrals_G(pending_integrals), pending_integrals.front()); + result *= trailing_zeros_G(convert_pending_integrals_G(pending_integrals), + pending_integrals.front(), + gsyms); } // G(y2_{-+}; sr) - result += trailing_zeros_G(convert_pending_integrals_G(new_pending_integrals), new_pending_integrals.front()); + result += trailing_zeros_G(convert_pending_integrals_G(new_pending_integrals), + new_pending_integrals.front(), + gsyms); // G(0; sr) new_pending_integrals.back() = 0; - result -= trailing_zeros_G(convert_pending_integrals_G(new_pending_integrals), new_pending_integrals.front()); + result -= trailing_zeros_G(convert_pending_integrals_G(new_pending_integrals), + new_pending_integrals.front(), + gsyms); return result; } @@ -775,14 +777,16 @@ ex depth_one_trafo_G(const Gparameter& pending_integrals, const Gparameter& a, i //term zeta_m result -= zeta(a.size()); if (psize) { - result *= trailing_zeros_G(convert_pending_integrals_G(pending_integrals), pending_integrals.front()); + result *= trailing_zeros_G(convert_pending_integrals_G(pending_integrals), + pending_integrals.front(), + gsyms); } // term int_0^sr dt/t G_{m-1}( (1/y2)_{+-}; 1/t ) // = int_0^sr dt/t G_{m-1}( t_{+-}; y2 ) Gparameter new_a(a.begin()+1, a.end()); new_pending_integrals.push_back(0); - result -= depth_one_trafo_G(new_pending_integrals, new_a, scale); + result -= depth_one_trafo_G(new_pending_integrals, new_a, scale, gsyms); // term int_0^y2 dt/t G_{m-1}( (1/y2)_{+-}; 1/t ) // = int_0^y2 dt/t G_{m-1}( t_{+-}; y2 ) @@ -790,10 +794,12 @@ ex depth_one_trafo_G(const Gparameter& pending_integrals, const Gparameter& a, i new_pending_integrals_2.push_back(scale); new_pending_integrals_2.push_back(0); if (psize) { - result += trailing_zeros_G(convert_pending_integrals_G(pending_integrals), pending_integrals.front()) - * depth_one_trafo_G(new_pending_integrals_2, new_a, scale); + result += trailing_zeros_G(convert_pending_integrals_G(pending_integrals), + pending_integrals.front(), + gsyms) + * depth_one_trafo_G(new_pending_integrals_2, new_a, scale, gsyms); } else { - result += depth_one_trafo_G(new_pending_integrals_2, new_a, scale); + result += depth_one_trafo_G(new_pending_integrals_2, new_a, scale, gsyms); } return result; @@ -802,11 +808,13 @@ ex depth_one_trafo_G(const Gparameter& pending_integrals, const Gparameter& a, i // forward declaration ex shuffle_G(const Gparameter & a0, const Gparameter & a1, const Gparameter & a2, - const Gparameter& pendint, const Gparameter& a_old, int scale); + const Gparameter& pendint, const Gparameter& a_old, int scale, + const exvector& gsyms); // G transformation [VSW] -ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale) +ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale, + const exvector& gsyms) { // main recursion routine // @@ -832,10 +840,12 @@ ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale) if (a.size() == 0) { result = 1; } else { - result = G_eval(a, scale); + result = G_eval(a, scale, gsyms); } if (pendint.size() > 0) { - result *= trailing_zeros_G(convert_pending_integrals_G(pendint), pendint.front()); + result *= trailing_zeros_G(convert_pending_integrals_G(pendint), + pendint.front(), + gsyms); } return result; } @@ -844,12 +854,12 @@ ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale) if (trailing_zeros > 0) { ex result; Gparameter new_a(a.begin(), a.end()-1); - result += G_eval1(0, scale) * G_transform(pendint, new_a, scale); + result += G_eval1(0, scale, gsyms) * G_transform(pendint, new_a, scale, gsyms); for (Gparameter::const_iterator it = a.begin(); it != firstzero; ++it) { Gparameter new_a(a.begin(), it); new_a.push_back(0); new_a.insert(new_a.end(), it, a.end()-1); - result -= G_transform(pendint, new_a, scale); + result -= G_transform(pendint, new_a, scale, gsyms); } return result / trailing_zeros; } @@ -857,15 +867,17 @@ ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale) // convergence case if (convergent) { if (pendint.size() > 0) { - return G_eval(convert_pending_integrals_G(pendint), pendint.front()) * G_eval(a, scale); + return G_eval(convert_pending_integrals_G(pendint), + pendint.front(), gsyms)* + G_eval(a, scale, gsyms); } else { - return G_eval(a, scale); + return G_eval(a, scale, gsyms); } } // call basic transformation for depth equal one if (depth == 1) { - return depth_one_trafo_G(pendint, a, scale); + return depth_one_trafo_G(pendint, a, scale, gsyms); } // do recursion @@ -880,9 +892,10 @@ ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale) Gparameter a1(a.begin(),min_it+1); Gparameter a2(min_it+1,a.end()); - ex result = G_transform(pendint,a2,scale)*G_transform(empty,a1,scale); + ex result = G_transform(pendint, a2, scale, gsyms)* + G_transform(empty, a1, scale, gsyms); - result -= shuffle_G(empty,a1,a2,pendint,a,scale); + result -= shuffle_G(empty, a1, a2, pendint, a, scale, gsyms); return result; } @@ -893,9 +906,10 @@ ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale) Gparameter new_pendint = prepare_pending_integrals(pendint, a[min_it_pos]); Gparameter new_a = a; new_a[min_it_pos] = 0; - ex result = G_transform(empty, new_a, scale); + ex result = G_transform(empty, new_a, scale, gsyms); if (pendint.size() > 0) { - result *= trailing_zeros_G(convert_pending_integrals_G(pendint), pendint.front()); + result *= trailing_zeros_G(convert_pending_integrals_G(pendint), + pendint.front(), gsyms); } // other terms @@ -904,29 +918,33 @@ ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale) if (changeit != new_a.begin()) { // smallest in the middle new_pendint.push_back(*changeit); - result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint), new_pendint.front()) - * G_transform(empty, new_a, scale); + result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint), + new_pendint.front(), gsyms)* + G_transform(empty, new_a, scale, gsyms); int buffer = *changeit; *changeit = *min_it; - result += G_transform(new_pendint, new_a, scale); + result += G_transform(new_pendint, new_a, scale, gsyms); *changeit = buffer; new_pendint.pop_back(); --changeit; new_pendint.push_back(*changeit); - result += trailing_zeros_G(convert_pending_integrals_G(new_pendint), new_pendint.front()) - * G_transform(empty, new_a, scale); + result += trailing_zeros_G(convert_pending_integrals_G(new_pendint), + new_pendint.front(), gsyms)* + G_transform(empty, new_a, scale, gsyms); *changeit = *min_it; - result -= G_transform(new_pendint, new_a, scale); + result -= G_transform(new_pendint, new_a, scale, gsyms); } else { // smallest at the front new_pendint.push_back(scale); - result += trailing_zeros_G(convert_pending_integrals_G(new_pendint), new_pendint.front()) - * G_transform(empty, new_a, scale); + result += trailing_zeros_G(convert_pending_integrals_G(new_pendint), + new_pendint.front(), gsyms)* + G_transform(empty, new_a, scale, gsyms); new_pendint.back() = *changeit; - result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint), new_pendint.front()) - * G_transform(empty, new_a, scale); + result -= trailing_zeros_G(convert_pending_integrals_G(new_pendint), + new_pendint.front(), gsyms)* + G_transform(empty, new_a, scale, gsyms); *changeit = *min_it; - result += G_transform(new_pendint, new_a, scale); + result += G_transform(new_pendint, new_a, scale, gsyms); } return result; } @@ -935,27 +953,28 @@ ex G_transform(const Gparameter& pendint, const Gparameter& a, int scale) // shuffles the two parameter list a1 and a2 and calls G_transform for every term except // for the one that is equal to a_old ex shuffle_G(const Gparameter & a0, const Gparameter & a1, const Gparameter & a2, - const Gparameter& pendint, const Gparameter& a_old, int scale) + const Gparameter& pendint, const Gparameter& a_old, int scale, + const exvector& gsyms) { if (a1.size()==0 && a2.size()==0) { // veto the one configuration we don't want if ( a0 == a_old ) return 0; - return G_transform(pendint,a0,scale); + return G_transform(pendint, a0, scale, gsyms); } if (a2.size()==0) { Gparameter empty; Gparameter aa0 = a0; aa0.insert(aa0.end(),a1.begin(),a1.end()); - return shuffle_G(aa0,empty,empty,pendint,a_old,scale); + return shuffle_G(aa0, empty, empty, pendint, a_old, scale, gsyms); } if (a1.size()==0) { Gparameter empty; Gparameter aa0 = a0; aa0.insert(aa0.end(),a2.begin(),a2.end()); - return shuffle_G(aa0,empty,empty,pendint,a_old,scale); + return shuffle_G(aa0, empty, empty, pendint, a_old, scale, gsyms); } Gparameter a1_removed(a1.begin()+1,a1.end()); @@ -967,8 +986,8 @@ ex shuffle_G(const Gparameter & a0, const Gparameter & a1, const Gparameter & a2 a01.push_back( a1[0] ); a02.push_back( a2[0] ); - return shuffle_G(a01,a1_removed,a2,pendint,a_old,scale) - + shuffle_G(a02,a1,a2_removed,pendint,a_old,scale); + return shuffle_G(a01, a1_removed, a2, pendint, a_old, scale, gsyms) + + shuffle_G(a02, a1, a2_removed, pendint, a_old, scale, gsyms); } @@ -1067,7 +1086,8 @@ ex G_numeric(const lst& x, const lst& s, const ex& y) // generate missing dummy-symbols int i = 1; - gsyms.clear(); + // holding dummy-symbols for the G/Li transformations + exvector gsyms; gsyms.push_back(symbol("GSYMS_ERROR")); ex lastentry; for (std::multimap::const_iterator it = sortmap.begin(); it != sortmap.end(); ++it) { @@ -1117,7 +1137,7 @@ ex G_numeric(const lst& x, const lst& s, const ex& y) // do transformation Gparameter pendint; - ex result = G_transform(pendint, a, scale); + ex result = G_transform(pendint, a, scale, gsyms); // replace dummy symbols with their values result = result.eval().expand(); result = result.subs(subslst).evalf(); -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:52:45 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:52:45 +0400 Subject: [GiNaC-devel] [PATCH 01/10] gcd: allow user to override (some of) heuristics. Message-ID: <20080825125245.GA23801@theor.jinr.ru> GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 1: * add new (optional) argument to gcd() to control its behaviour. * introduce gcd_options structure. N.B. No actual code changes so far, the actual handling of newly introduced options is the subject of further patches. --- ginac/normal.cpp | 2 +- ginac/normal.h | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index e756a67..9ec7574 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1426,7 +1426,7 @@ static bool heur_gcd(ex& res, const ex& a, const ex& b, ex *ca, ex *cb, * @param check_args check whether a and b are polynomials with rational * coefficients (defaults to "true") * @return the GCD as a new expression */ -ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args) +ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned options) { #if STATISTICS gcd_called++; diff --git a/ginac/normal.h b/ginac/normal.h index 5f44427..5a735e3 100644 --- a/ginac/normal.h +++ b/ginac/normal.h @@ -30,6 +30,28 @@ namespace GiNaC { +/** + * Flags to control the behaviour of gcd() and friends + */ +struct gcd_options +{ + enum { + /** + * Usually GiNaC tries heuristic GCD algorithm before PRS. + * Some people don't like this, so here's a flag to disable it. + */ + no_heur_gcd = 2, + /** + * GiNaC tries to avoid expanding expressions when computing + * GCDs. This is a good idea, but some people dislike it. + * Hence the flag to disable special handling of partially + * factored polynomials. DON'T SET THIS unless you *really* + * know what are you doing! + */ + no_part_factored = 4 + }; +}; + class ex; class symbol; @@ -52,7 +74,8 @@ extern ex sprem(const ex &a, const ex &b, const ex &x, bool check_args = true); extern bool divide(const ex &a, const ex &b, ex &q, bool check_args = true); // Polynomial GCD in Z[X], cofactors are returned in ca and cb, if desired -extern ex gcd(const ex &a, const ex &b, ex *ca = NULL, ex *cb = NULL, bool check_args = true); +extern ex gcd(const ex &a, const ex &b, ex *ca = NULL, ex *cb = NULL, + bool check_args = true, unsigned options = 0); // Polynomial LCM in Z[X] extern ex lcm(const ex &a, const ex &b, bool check_args = true); -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:53:07 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:53:07 +0400 Subject: [GiNaC-devel] [PATCH 02/10] introduce gcd_pf_pow: gcd helper to handle partially factored expressions. Message-ID: <20080825125307.GA23946@theor.jinr.ru> GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 2: Move the code handling powers from gcd() into a separate function. This is *really* only code move, everything else should be considered a bug. --- ginac/normal.cpp | 212 ++++++++++++++++++++++++++++-------------------------- 1 files changed, 110 insertions(+), 102 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index 9ec7574..0af5aad 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1415,6 +1415,10 @@ static bool heur_gcd(ex& res, const ex& a, const ex& b, ex *ca, ex *cb, } +// gcd helper to handle partially factored polynomials (to avoid expanding +// large expressions). At least one of the arguments should be a power. +static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args); + /** Compute GCD (Greatest Common Divisor) of multivariate polynomials a(X) * and b(X) in Z[X]. Optionally also compute the cofactors of a and b, * defined by a = ca * gcd(a, b) and b = cb * gcd(a, b). @@ -1498,108 +1502,8 @@ factored_b: } #if FAST_COMPARE - // Input polynomials of the form poly^n are sometimes also trivial - if (is_exactly_a(a)) { - ex p = a.op(0); - const ex& exp_a = a.op(1); - if (is_exactly_a(b)) { - ex pb = b.op(0); - const ex& exp_b = b.op(1); - if (p.is_equal(pb)) { - // a = p^n, b = p^m, gcd = p^min(n, m) - if (exp_a < exp_b) { - if (ca) - *ca = _ex1; - if (cb) - *cb = power(p, exp_b - exp_a); - return power(p, exp_a); - } else { - if (ca) - *ca = power(p, exp_a - exp_b); - if (cb) - *cb = _ex1; - return power(p, exp_b); - } - } else { - ex p_co, pb_co; - ex p_gcd = gcd(p, pb, &p_co, &pb_co, check_args); - if (p_gcd.is_equal(_ex1)) { - // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> - // gcd(a,b) = 1 - if (ca) - *ca = a; - if (cb) - *cb = b; - return _ex1; - // XXX: do I need to check for p_gcd = -1? - } else { - // there are common factors: - // a(x) = g(x)^n A(x)^n, b(x) = g(x)^m B(x)^m ==> - // gcd(a, b) = g(x)^n gcd(A(x)^n, g(x)^(n-m) B(x)^m - if (exp_a < exp_b) { - return power(p_gcd, exp_a)* - gcd(power(p_co, exp_a), power(p_gcd, exp_b-exp_a)*power(pb_co, exp_b), ca, cb, false); - } else { - return power(p_gcd, exp_b)* - gcd(power(p_gcd, exp_a - exp_b)*power(p_co, exp_a), power(pb_co, exp_b), ca, cb, false); - } - } // p_gcd.is_equal(_ex1) - } // p.is_equal(pb) - - } else { - if (p.is_equal(b)) { - // a = p^n, b = p, gcd = p - if (ca) - *ca = power(p, a.op(1) - 1); - if (cb) - *cb = _ex1; - return p; - } - - ex p_co, bpart_co; - ex p_gcd = gcd(p, b, &p_co, &bpart_co, false); - - if (p_gcd.is_equal(_ex1)) { - // a(x) = p(x)^n, gcd(p, b) = 1 ==> gcd(a, b) = 1 - if (ca) - *ca = a; - if (cb) - *cb = b; - return _ex1; - } else { - // a(x) = g(x)^n A(x)^n, b(x) = g(x) B(x) ==> gcd(a, b) = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) - return p_gcd*gcd(power(p_gcd, exp_a-1)*power(p_co, exp_a), bpart_co, ca, cb, false); - } - } // is_exactly_a(b) - - } else if (is_exactly_a(b)) { - ex p = b.op(0); - if (p.is_equal(a)) { - // a = p, b = p^n, gcd = p - if (ca) - *ca = _ex1; - if (cb) - *cb = power(p, b.op(1) - 1); - return p; - } - - ex p_co, apart_co; - const ex& exp_b(b.op(1)); - ex p_gcd = gcd(a, p, &apart_co, &p_co, false); - if (p_gcd.is_equal(_ex1)) { - // b=p(x)^n, gcd(a, p) = 1 ==> gcd(a, b) == 1 - if (ca) - *ca = a; - if (cb) - *cb = b; - return _ex1; - } else { - // there are common factors: - // a(x) = g(x) A(x), b(x) = g(x)^n B(x)^n ==> gcd = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) - - return p_gcd*gcd(apart_co, power(p_gcd, exp_b-1)*power(p_co, exp_b), ca, cb, false); - } // p_gcd.is_equal(_ex1) - } + if (is_exactly_a(a) || is_exactly_a(b)) + return gcd_pf_pow(a, b, ca, cb, check_args); #endif // Some trivial cases @@ -1762,6 +1666,110 @@ factored_b: return g; } +static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) +{ + if (is_exactly_a(a)) { + ex p = a.op(0); + const ex& exp_a = a.op(1); + if (is_exactly_a(b)) { + ex pb = b.op(0); + const ex& exp_b = b.op(1); + if (p.is_equal(pb)) { + // a = p^n, b = p^m, gcd = p^min(n, m) + if (exp_a < exp_b) { + if (ca) + *ca = _ex1; + if (cb) + *cb = power(p, exp_b - exp_a); + return power(p, exp_a); + } else { + if (ca) + *ca = power(p, exp_a - exp_b); + if (cb) + *cb = _ex1; + return power(p, exp_b); + } + } else { + ex p_co, pb_co; + ex p_gcd = gcd(p, pb, &p_co, &pb_co, check_args); + if (p_gcd.is_equal(_ex1)) { + // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> + // gcd(a,b) = 1 + if (ca) + *ca = a; + if (cb) + *cb = b; + return _ex1; + // XXX: do I need to check for p_gcd = -1? + } else { + // there are common factors: + // a(x) = g(x)^n A(x)^n, b(x) = g(x)^m B(x)^m ==> + // gcd(a, b) = g(x)^n gcd(A(x)^n, g(x)^(n-m) B(x)^m + if (exp_a < exp_b) { + return power(p_gcd, exp_a)* + gcd(power(p_co, exp_a), power(p_gcd, exp_b-exp_a)*power(pb_co, exp_b), ca, cb, false); + } else { + return power(p_gcd, exp_b)* + gcd(power(p_gcd, exp_a - exp_b)*power(p_co, exp_a), power(pb_co, exp_b), ca, cb, false); + } + } // p_gcd.is_equal(_ex1) + } // p.is_equal(pb) + + } else { + if (p.is_equal(b)) { + // a = p^n, b = p, gcd = p + if (ca) + *ca = power(p, a.op(1) - 1); + if (cb) + *cb = _ex1; + return p; + } + + ex p_co, bpart_co; + ex p_gcd = gcd(p, b, &p_co, &bpart_co, false); + + if (p_gcd.is_equal(_ex1)) { + // a(x) = p(x)^n, gcd(p, b) = 1 ==> gcd(a, b) = 1 + if (ca) + *ca = a; + if (cb) + *cb = b; + return _ex1; + } else { + // a(x) = g(x)^n A(x)^n, b(x) = g(x) B(x) ==> gcd(a, b) = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) + return p_gcd*gcd(power(p_gcd, exp_a-1)*power(p_co, exp_a), bpart_co, ca, cb, false); + } + } // is_exactly_a(b) + + } else if (is_exactly_a(b)) { + ex p = b.op(0); + if (p.is_equal(a)) { + // a = p, b = p^n, gcd = p + if (ca) + *ca = _ex1; + if (cb) + *cb = power(p, b.op(1) - 1); + return p; + } + + ex p_co, apart_co; + const ex& exp_b(b.op(1)); + ex p_gcd = gcd(a, p, &apart_co, &p_co, false); + if (p_gcd.is_equal(_ex1)) { + // b=p(x)^n, gcd(a, p) = 1 ==> gcd(a, b) == 1 + if (ca) + *ca = a; + if (cb) + *cb = b; + return _ex1; + } else { + // there are common factors: + // a(x) = g(x) A(x), b(x) = g(x)^n B(x)^n ==> gcd = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) + + return p_gcd*gcd(apart_co, power(p_gcd, exp_b-1)*power(p_co, exp_b), ca, cb, false); + } // p_gcd.is_equal(_ex1) + } +} /** Compute LCM (Least Common Multiple) of multivariate polynomials in Z[X]. * -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:53:47 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:53:47 +0400 Subject: [GiNaC-devel] [PATCH 03/10] introduce gcd_pf_mul: gcd helper to handle partially factored expressions. Message-ID: <20080825125347.GA24015@theor.jinr.ru> GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 3: Move the code handling products from gcd() into a separate function. This is *really* only code move, everything else should be considered a bug. --- ginac/normal.cpp | 89 +++++++++++++++++++++++++++++------------------------ 1 files changed, 49 insertions(+), 40 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index 0af5aad..0cb9100 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1419,6 +1419,10 @@ static bool heur_gcd(ex& res, const ex& a, const ex& b, ex *ca, ex *cb, // large expressions). At least one of the arguments should be a power. static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args); +// gcd helper to handle partially factored polynomials (to avoid expanding +// large expressions). At least one of the arguments should be a product. +static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args); + /** Compute GCD (Greatest Common Divisor) of multivariate polynomials a(X) * and b(X) in Z[X]. Optionally also compute the cofactors of a and b, * defined by a = ca * gcd(a, b) and b = cb * gcd(a, b). @@ -1461,46 +1465,8 @@ ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned optio } // Partially factored cases (to avoid expanding large expressions) - if (is_exactly_a(a)) { - if (is_exactly_a(b) && b.nops() > a.nops()) - goto factored_b; -factored_a: - size_t num = a.nops(); - exvector g; g.reserve(num); - exvector acc_ca; acc_ca.reserve(num); - ex part_b = b; - for (size_t i=0; isetflag(status_flags::dynallocated); - if (cb) - *cb = part_b; - return (new mul(g))->setflag(status_flags::dynallocated); - } else if (is_exactly_a(b)) { - if (is_exactly_a(a) && a.nops() > b.nops()) - goto factored_a; -factored_b: - size_t num = b.nops(); - exvector g; g.reserve(num); - exvector acc_cb; acc_cb.reserve(num); - ex part_a = a; - for (size_t i=0; isetflag(status_flags::dynallocated); - return (new mul(g))->setflag(status_flags::dynallocated); - } - + if (is_exactly_a(a) || is_exactly_a(b)) + return gcd_pf_mul(a, b, ca, cb, check_args); #if FAST_COMPARE if (is_exactly_a(a) || is_exactly_a(b)) return gcd_pf_pow(a, b, ca, cb, check_args); @@ -1771,6 +1737,49 @@ static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) } } +static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) +{ + if (is_exactly_a(a)) { + if (is_exactly_a(b) && b.nops() > a.nops()) + goto factored_b; +factored_a: + size_t num = a.nops(); + exvector g; g.reserve(num); + exvector acc_ca; acc_ca.reserve(num); + ex part_b = b; + for (size_t i=0; isetflag(status_flags::dynallocated); + if (cb) + *cb = part_b; + return (new mul(g))->setflag(status_flags::dynallocated); + } else if (is_exactly_a(b)) { + if (is_exactly_a(a) && a.nops() > b.nops()) + goto factored_a; +factored_b: + size_t num = b.nops(); + exvector g; g.reserve(num); + exvector acc_cb; acc_cb.reserve(num); + ex part_a = a; + for (size_t i=0; isetflag(status_flags::dynallocated); + return (new mul(g))->setflag(status_flags::dynallocated); + } +} + /** Compute LCM (Least Common Multiple) of multivariate polynomials in Z[X]. * * @param a first multivariate polynomial -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:54:10 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:54:10 +0400 Subject: [GiNaC-devel] [PATCH 04/10] refactor gcd() a little bit (no functional changes). Message-ID: <20080825125410.GA24119@theor.jinr.ru> GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 4: refactor gcd() a little bit, so subsequent patch(es) won't be so big and ugly. --- ginac/normal.cpp | 41 ++++++++++++++++++++++------------------- 1 files changed, 22 insertions(+), 19 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index 0cb9100..0227f4e 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1602,33 +1602,36 @@ ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned optio // Try heuristic algorithm first, fall back to PRS if that failed ex g; bool found = heur_gcd(g, aex, bex, ca, cb, var); - if (!found) { -#if STATISTICS - heur_gcd_failed++; -#endif - g = sr_gcd(aex, bex, var); - if (g.is_equal(_ex1)) { - // Keep cofactors factored if possible - if (ca) - *ca = a; - if (cb) - *cb = b; - } else { - if (ca) - divide(aex, g, *ca, false); - if (cb) - divide(bex, g, *cb, false); - } - } else { + if (found) { + // heur_gcd have already computed cofactors... if (g.is_equal(_ex1)) { - // Keep cofactors factored if possible + // ... but we want to keep them factored if possible. if (ca) *ca = a; if (cb) *cb = b; } + return g; } +#if STATISTICS + else { + heur_gcd_failed++; + } +#endif + g = sr_gcd(aex, bex, var); + if (g.is_equal(_ex1)) { + // Keep cofactors factored if possible + if (ca) + *ca = a; + if (cb) + *cb = b; + } else { + if (ca) + divide(aex, g, *ca, false); + if (cb) + divide(bex, g, *cb, false); + } return g; } -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:54:46 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:54:46 +0400 Subject: [GiNaC-devel] [PATCH 05/10] gcd(): allow user to override (some of) heuristics. Message-ID: <20080825125446.GA24201@theor.jinr.ru> GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 5: * gcd(): don't use heuristic GCD algorithm if gcd_options::no_heur_gcd flag is set. * gcd(): don't handle partially factored expressions in a special way if gcd_options::no_part_factored flag is set. --- ginac/normal.cpp | 40 ++++++++++++++++++++++------------------ 1 files changed, 22 insertions(+), 18 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index 0227f4e..5d044fc 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1465,12 +1465,14 @@ ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned optio } // Partially factored cases (to avoid expanding large expressions) - if (is_exactly_a(a) || is_exactly_a(b)) - return gcd_pf_mul(a, b, ca, cb, check_args); + if (!(options & gcd_options::no_part_factored)) { + if (is_exactly_a(a) || is_exactly_a(b)) + return gcd_pf_mul(a, b, ca, cb, check_args); #if FAST_COMPARE - if (is_exactly_a(a) || is_exactly_a(b)) - return gcd_pf_pow(a, b, ca, cb, check_args); + if (is_exactly_a(a) || is_exactly_a(b)) + return gcd_pf_pow(a, b, ca, cb, check_args); #endif + } // Some trivial cases ex aex = a.expand(), bex = b.expand(); @@ -1601,23 +1603,25 @@ ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned optio // Try heuristic algorithm first, fall back to PRS if that failed ex g; - bool found = heur_gcd(g, aex, bex, ca, cb, var); - if (found) { - // heur_gcd have already computed cofactors... - if (g.is_equal(_ex1)) { - // ... but we want to keep them factored if possible. - if (ca) - *ca = a; - if (cb) - *cb = b; + if (!(options & gcd_options::no_heur_gcd)) { + bool found = heur_gcd(g, aex, bex, ca, cb, var); + if (found) { + // heur_gcd have already computed cofactors... + if (g.is_equal(_ex1)) { + // ... but we want to keep them factored if possible. + if (ca) + *ca = a; + if (cb) + *cb = b; + } + return g; } - return g; - } #if STATISTICS - else { - heur_gcd_failed++; - } + else { + heur_gcd_failed++; + } #endif + } g = sr_gcd(aex, bex, var); if (g.is_equal(_ex1)) { -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:55:13 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:55:13 +0400 Subject: [GiNaC-devel] [PATCH 06/10] gcd_pf_mul: get rid of duplicate code. Message-ID: <20080825125513.GA24277@theor.jinr.ru> This function (which helps gcd() handle partially factored expressions) contained two copies of the same code. Remove one redundant copy. --- ginac/normal.cpp | 62 ++++++++++++++++++++---------------------------------- 1 files changed, 23 insertions(+), 39 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index 5d044fc..2bb3a43 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1746,45 +1746,29 @@ static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) { - if (is_exactly_a(a)) { - if (is_exactly_a(b) && b.nops() > a.nops()) - goto factored_b; -factored_a: - size_t num = a.nops(); - exvector g; g.reserve(num); - exvector acc_ca; acc_ca.reserve(num); - ex part_b = b; - for (size_t i=0; isetflag(status_flags::dynallocated); - if (cb) - *cb = part_b; - return (new mul(g))->setflag(status_flags::dynallocated); - } else if (is_exactly_a(b)) { - if (is_exactly_a(a) && a.nops() > b.nops()) - goto factored_a; -factored_b: - size_t num = b.nops(); - exvector g; g.reserve(num); - exvector acc_cb; acc_cb.reserve(num); - ex part_a = a; - for (size_t i=0; isetflag(status_flags::dynallocated); - return (new mul(g))->setflag(status_flags::dynallocated); - } + if (is_exactly_a(a) && is_exactly_a(b) + && (b.nops() > a.nops())) + return gcd_pf_mul(b, a, cb, ca, check_args); + + if (is_exactly_a(b) && (!is_exactly_a(a))) + return gcd_pf_mul(b, a, cb, ca, check_args); + + GINAC_ASSERT(is_exactly_a(a)); + size_t num = a.nops(); + exvector g; g.reserve(num); + exvector acc_ca; acc_ca.reserve(num); + ex part_b = b; + for (size_t i=0; isetflag(status_flags::dynallocated); + if (cb) + *cb = part_b; + return (new mul(g))->setflag(status_flags::dynallocated); } /** Compute LCM (Least Common Multiple) of multivariate polynomials in Z[X]. -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:55:42 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:55:42 +0400 Subject: [GiNaC-devel] [PATCH 07/10] gcd_pf_{pow, mul}: don't check if the arguments are polynomials. Message-ID: <20080825125542.GA24360@theor.jinr.ru> These functions gets called only from gcd(), which does this check on its own. --- ginac/normal.cpp | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index 2bb3a43..e97a7ce 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1417,11 +1417,11 @@ static bool heur_gcd(ex& res, const ex& a, const ex& b, ex *ca, ex *cb, // gcd helper to handle partially factored polynomials (to avoid expanding // large expressions). At least one of the arguments should be a power. -static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args); +static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb); // gcd helper to handle partially factored polynomials (to avoid expanding // large expressions). At least one of the arguments should be a product. -static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args); +static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb); /** Compute GCD (Greatest Common Divisor) of multivariate polynomials a(X) * and b(X) in Z[X]. Optionally also compute the cofactors of a and b, @@ -1467,10 +1467,10 @@ ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned optio // Partially factored cases (to avoid expanding large expressions) if (!(options & gcd_options::no_part_factored)) { if (is_exactly_a(a) || is_exactly_a(b)) - return gcd_pf_mul(a, b, ca, cb, check_args); + return gcd_pf_mul(a, b, ca, cb); #if FAST_COMPARE if (is_exactly_a(a) || is_exactly_a(b)) - return gcd_pf_pow(a, b, ca, cb, check_args); + return gcd_pf_pow(a, b, ca, cb); #endif } @@ -1639,7 +1639,7 @@ ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned optio return g; } -static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) +static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb) { if (is_exactly_a(a)) { ex p = a.op(0); @@ -1664,7 +1664,7 @@ static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) } } else { ex p_co, pb_co; - ex p_gcd = gcd(p, pb, &p_co, &pb_co, check_args); + ex p_gcd = gcd(p, pb, &p_co, &pb_co, false); if (p_gcd.is_equal(_ex1)) { // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> // gcd(a,b) = 1 @@ -1744,14 +1744,14 @@ static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) } } -static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) +static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb) { if (is_exactly_a(a) && is_exactly_a(b) && (b.nops() > a.nops())) - return gcd_pf_mul(b, a, cb, ca, check_args); + return gcd_pf_mul(b, a, cb, ca); if (is_exactly_a(b) && (!is_exactly_a(a))) - return gcd_pf_mul(b, a, cb, ca, check_args); + return gcd_pf_mul(b, a, cb, ca); GINAC_ASSERT(is_exactly_a(a)); size_t num = a.nops(); @@ -1760,7 +1760,7 @@ static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb, bool check_args) ex part_b = b; for (size_t i=0; i From varg at theor.jinr.ru Mon Aug 25 14:56:04 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:56:04 +0400 Subject: [GiNaC-devel] [PATCH 08/10] introduce gcd_pf_pow_pow: gcd helper to handle partially factored expressions. Message-ID: <20080825125604.GA24436@theor.jinr.ru> gcd_pf_pow_pow handles the case when both arguments of gcd() are powers. N.B. the actual code moved from gcd_pf_pow, no functional changes. --- ginac/normal.cpp | 98 +++++++++++++++++++++++++++++------------------------- 1 files changed, 53 insertions(+), 45 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index e97a7ce..d5319b9 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1639,56 +1639,64 @@ ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned optio return g; } +// gcd helper to handle partially factored polynomials (to avoid expanding +// large expressions). Both arguments should be powers. +static ex gcd_pf_pow_pow(const ex& a, const ex& b, ex* ca, ex* cb) +{ + ex p = a.op(0); + const ex& exp_a = a.op(1); + ex pb = b.op(0); + const ex& exp_b = b.op(1); + if (p.is_equal(pb)) { + // a = p^n, b = p^m, gcd = p^min(n, m) + if (exp_a < exp_b) { + if (ca) + *ca = _ex1; + if (cb) + *cb = power(p, exp_b - exp_a); + return power(p, exp_a); + } else { + if (ca) + *ca = power(p, exp_a - exp_b); + if (cb) + *cb = _ex1; + return power(p, exp_b); + } + } else { + ex p_co, pb_co; + ex p_gcd = gcd(p, pb, &p_co, &pb_co, false); + if (p_gcd.is_equal(_ex1)) { + // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> + // gcd(a,b) = 1 + if (ca) + *ca = a; + if (cb) + *cb = b; + return _ex1; + // XXX: do I need to check for p_gcd = -1? + } else { + // there are common factors: + // a(x) = g(x)^n A(x)^n, b(x) = g(x)^m B(x)^m ==> + // gcd(a, b) = g(x)^n gcd(A(x)^n, g(x)^(n-m) B(x)^m + if (exp_a < exp_b) { + return power(p_gcd, exp_a)* + gcd(power(p_co, exp_a), power(p_gcd, exp_b-exp_a)*power(pb_co, exp_b), ca, cb, false); + } else { + return power(p_gcd, exp_b)* + gcd(power(p_gcd, exp_a - exp_b)*power(p_co, exp_a), power(pb_co, exp_b), ca, cb, false); + } + } // p_gcd.is_equal(_ex1) + } // p.is_equal(pb) +} + static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb) { if (is_exactly_a(a)) { ex p = a.op(0); const ex& exp_a = a.op(1); - if (is_exactly_a(b)) { - ex pb = b.op(0); - const ex& exp_b = b.op(1); - if (p.is_equal(pb)) { - // a = p^n, b = p^m, gcd = p^min(n, m) - if (exp_a < exp_b) { - if (ca) - *ca = _ex1; - if (cb) - *cb = power(p, exp_b - exp_a); - return power(p, exp_a); - } else { - if (ca) - *ca = power(p, exp_a - exp_b); - if (cb) - *cb = _ex1; - return power(p, exp_b); - } - } else { - ex p_co, pb_co; - ex p_gcd = gcd(p, pb, &p_co, &pb_co, false); - if (p_gcd.is_equal(_ex1)) { - // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> - // gcd(a,b) = 1 - if (ca) - *ca = a; - if (cb) - *cb = b; - return _ex1; - // XXX: do I need to check for p_gcd = -1? - } else { - // there are common factors: - // a(x) = g(x)^n A(x)^n, b(x) = g(x)^m B(x)^m ==> - // gcd(a, b) = g(x)^n gcd(A(x)^n, g(x)^(n-m) B(x)^m - if (exp_a < exp_b) { - return power(p_gcd, exp_a)* - gcd(power(p_co, exp_a), power(p_gcd, exp_b-exp_a)*power(pb_co, exp_b), ca, cb, false); - } else { - return power(p_gcd, exp_b)* - gcd(power(p_gcd, exp_a - exp_b)*power(p_co, exp_a), power(pb_co, exp_b), ca, cb, false); - } - } // p_gcd.is_equal(_ex1) - } // p.is_equal(pb) - - } else { + if (is_exactly_a(b)) + return gcd_pf_pow_pow(a, b, ca, cb); + else { if (p.is_equal(b)) { // a = p^n, b = p, gcd = p if (ca) -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:57:02 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:57:02 +0400 Subject: [GiNaC-devel] [PATCH 09/10] gcd_pf_pow: get rid of duplicate code. Message-ID: <20080825125702.GA24533@theor.jinr.ru> This function (which helps gcd() handle partially factored expressions) contained two copies of the same code. Remove one redundant copy. --- ginac/normal.cpp | 80 ++++++++++++++++++----------------------------------- 1 files changed, 27 insertions(+), 53 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index d5319b9..6392e3f 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1691,65 +1691,39 @@ static ex gcd_pf_pow_pow(const ex& a, const ex& b, ex* ca, ex* cb) static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb) { - if (is_exactly_a(a)) { - ex p = a.op(0); - const ex& exp_a = a.op(1); - if (is_exactly_a(b)) - return gcd_pf_pow_pow(a, b, ca, cb); - else { - if (p.is_equal(b)) { - // a = p^n, b = p, gcd = p - if (ca) - *ca = power(p, a.op(1) - 1); - if (cb) - *cb = _ex1; - return p; - } + if (is_exactly_a(a) && is_exactly_a(b)) + return gcd_pf_pow_pow(a, b, ca, cb); - ex p_co, bpart_co; - ex p_gcd = gcd(p, b, &p_co, &bpart_co, false); + if (is_exactly_a(b) && (! is_exactly_a(a))) + return gcd_pf_pow(b, a, cb, ca); - if (p_gcd.is_equal(_ex1)) { - // a(x) = p(x)^n, gcd(p, b) = 1 ==> gcd(a, b) = 1 - if (ca) - *ca = a; - if (cb) - *cb = b; - return _ex1; - } else { - // a(x) = g(x)^n A(x)^n, b(x) = g(x) B(x) ==> gcd(a, b) = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) - return p_gcd*gcd(power(p_gcd, exp_a-1)*power(p_co, exp_a), bpart_co, ca, cb, false); - } - } // is_exactly_a(b) + GINAC_ASSERT(is_exactly_a(a)); - } else if (is_exactly_a(b)) { - ex p = b.op(0); - if (p.is_equal(a)) { - // a = p, b = p^n, gcd = p - if (ca) - *ca = _ex1; - if (cb) - *cb = power(p, b.op(1) - 1); - return p; - } + ex p = a.op(0); + const ex& exp_a = a.op(1); + if (p.is_equal(b)) { + // a = p^n, b = p, gcd = p + if (ca) + *ca = power(p, a.op(1) - 1); + if (cb) + *cb = _ex1; + return p; + } - ex p_co, apart_co; - const ex& exp_b(b.op(1)); - ex p_gcd = gcd(a, p, &apart_co, &p_co, false); - if (p_gcd.is_equal(_ex1)) { - // b=p(x)^n, gcd(a, p) = 1 ==> gcd(a, b) == 1 - if (ca) - *ca = a; - if (cb) - *cb = b; - return _ex1; - } else { - // there are common factors: - // a(x) = g(x) A(x), b(x) = g(x)^n B(x)^n ==> gcd = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) + ex p_co, bpart_co; + ex p_gcd = gcd(p, b, &p_co, &bpart_co, false); - return p_gcd*gcd(apart_co, power(p_gcd, exp_b-1)*power(p_co, exp_b), ca, cb, false); - } // p_gcd.is_equal(_ex1) + // a(x) = p(x)^n, gcd(p, b) = 1 ==> gcd(a, b) = 1 + if (p_gcd.is_equal(_ex1)) { + if (ca) + *ca = a; + if (cb) + *cb = b; + return _ex1; } + // a(x) = g(x)^n A(x)^n, b(x) = g(x) B(x) ==> gcd(a, b) = g(x) gcd(g(x)^(n-1) A(x)^n, B(x)) + ex rg = gcd(power(p_gcd, exp_a-1)*power(p_co, exp_a), bpart_co, ca, cb, false); + return p_gcd*rg; } static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb) -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From varg at theor.jinr.ru Mon Aug 25 14:57:38 2008 From: varg at theor.jinr.ru (Alexei Sheplyakov) Date: Mon, 25 Aug 2008 16:57:38 +0400 Subject: [GiNaC-devel] [PATCH 10/10] gcd_pf_pow_pow: deobfuscate a little bit (no functional changes). Message-ID: <20080825125738.GA24639@theor.jinr.ru> Use if (foo) return bar(); return baz(); instead of if (foo) { return bar(); } else { return baz(); } This makes the code a little bit more readable. --- ginac/normal.cpp | 40 ++++++++++++++++++++-------------------- 1 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ginac/normal.cpp b/ginac/normal.cpp index 6392e3f..09773d3 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1647,8 +1647,9 @@ static ex gcd_pf_pow_pow(const ex& a, const ex& b, ex* ca, ex* cb) const ex& exp_a = a.op(1); ex pb = b.op(0); const ex& exp_b = b.op(1); + + // a = p^n, b = p^m, gcd = p^min(n, m) if (p.is_equal(pb)) { - // a = p^n, b = p^m, gcd = p^min(n, m) if (exp_a < exp_b) { if (ca) *ca = _ex1; @@ -1662,31 +1663,30 @@ static ex gcd_pf_pow_pow(const ex& a, const ex& b, ex* ca, ex* cb) *cb = _ex1; return power(p, exp_b); } - } else { - ex p_co, pb_co; - ex p_gcd = gcd(p, pb, &p_co, &pb_co, false); - if (p_gcd.is_equal(_ex1)) { - // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> - // gcd(a,b) = 1 + } + + ex p_co, pb_co; + ex p_gcd = gcd(p, pb, &p_co, &pb_co, false); + // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> gcd(a,b) = 1 + if (p_gcd.is_equal(_ex1)) { if (ca) *ca = a; if (cb) *cb = b; return _ex1; // XXX: do I need to check for p_gcd = -1? - } else { - // there are common factors: - // a(x) = g(x)^n A(x)^n, b(x) = g(x)^m B(x)^m ==> - // gcd(a, b) = g(x)^n gcd(A(x)^n, g(x)^(n-m) B(x)^m - if (exp_a < exp_b) { - return power(p_gcd, exp_a)* - gcd(power(p_co, exp_a), power(p_gcd, exp_b-exp_a)*power(pb_co, exp_b), ca, cb, false); - } else { - return power(p_gcd, exp_b)* - gcd(power(p_gcd, exp_a - exp_b)*power(p_co, exp_a), power(pb_co, exp_b), ca, cb, false); - } - } // p_gcd.is_equal(_ex1) - } // p.is_equal(pb) + } + + // there are common factors: + // a(x) = g(x)^n A(x)^n, b(x) = g(x)^m B(x)^m ==> + // gcd(a, b) = g(x)^n gcd(A(x)^n, g(x)^(n-m) B(x)^m + if (exp_a < exp_b) { + ex pg = gcd(power(p_co, exp_a), power(p_gcd, exp_b-exp_a)*power(pb_co, exp_b), ca, cb, false); + return power(p_gcd, exp_a)*pg; + } else { + ex pg = gcd(power(p_gcd, exp_a - exp_b)*power(p_co, exp_a), power(pb_co, exp_b), ca, cb, false); + return power(p_gcd, exp_b)*pg; + } } static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb) -- 1.5.6 Best regards, Alexei -- All science is either physics or stamp collecting. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 827 bytes Desc: Digital signature URL: From kreckel at ginac.de Wed Aug 27 00:30:20 2008 From: kreckel at ginac.de (Richard B. Kreckel) Date: Wed, 27 Aug 2008 00:30:20 +0200 Subject: [GiNaC-devel] factorization and stuff In-Reply-To: <48AD0B07.5070705@ginac.de> References: <48A00935.70004@nikhef.nl> <48A88EA5.4040901@ginac.de> <48A9D18F.1070804@nikhef.nl> <48A9F0EA.6010807@ginac.de> <48AC83D0.1060502@nikhef.nl> <48AC97AC.2020906@ginac.de> <48AC9F3C.5060008@nikhef.nl> <48AD0A2C.4070703@ginac.de> <48AD0B07.5070705@ginac.de> Message-ID: <48B483FC.1050303@ginac.de> Dear Jens, On 2008-08-21, I wrote: > Ah, I haven't had my morning coffee yet. So I forgot to include the bug > of the day: > > factor(x); > Segmentation fault That's fixed now. Thank you! Now, I'm getting > factor(expand((x+64*x^4)*(3*x+92*x^4+5*x^2))); Segmentation fault Is this in any way related with this issue? > factor(x^37+1); Not a 32-bit integer: 137438953472 Cheers -richy. -- Richard B. Kreckel From git at ginac.de Wed Aug 27 21:44:33 2008 From: git at ginac.de (Jens Vollinga) Date: Wed, 27 Aug 2008 21:44:33 +0200 (CEST) Subject: [GiNaC-devel] [SCM] GiNaC -- a C++ library for symbolic computations branch, master, updated. release_1-4-0-77-g55af760 Message-ID: <20080827194434.0E6D45B4046@cebix.net> This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "GiNaC -- a C++ library for symbolic computations". The branch, master has been updated via 55af76071727bd6e2fd540ad58eac26dd961f9c9 (commit) via a79a813e7249f793859d1d3b443d1931dbab94b6 (commit) via 6ff92476a4dcd32f9a0c6f59c95c74812ea86fef (commit) via 55e50ea26252dff7432bdce8b010f9fbd058e907 (commit) via 1d09676118944532e6100c93383d1659b1253a60 (commit) via adb1dbd383ae6e5a999b5f8cba72a5c2bfd50c11 (commit) via c77689e7ac8d8f4dbca0f337b6e9acf2419010ff (commit) via 77b6a0304a48d6a306deeda18177680f16025b33 (commit) via 7d7131d3af3de5425b7fe80b1f587740294371bc (commit) via b65fcd7481a401ec23c284c91f6f4e883e967676 (commit) via 8474043d373a19e04008f476fa9b77e45734b604 (commit) via ff09c4f8103f53fe3b7a51eb3c33eff2e5a243f0 (commit) via b4b302fe5d54720b58a23568a4270e04ee1ca216 (commit) from 1222eac51cee964961d2aad889dc4ceccb144a36 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 55af76071727bd6e2fd540ad58eac26dd961f9c9 Author: Jens Vollinga Date: Wed Aug 27 17:25:16 2008 +0200 Added internal code for multivariate factorization. commit a79a813e7249f793859d1d3b443d1931dbab94b6 Author: Alexei Sheplyakov Date: Mon Aug 25 16:57:38 2008 +0400 gcd_pf_pow_pow: deobfuscate a little bit (no functional changes). Use if (foo) return bar(); return baz(); instead of if (foo) { return bar(); } else { return baz(); } This makes the code a little bit more readable. commit 6ff92476a4dcd32f9a0c6f59c95c74812ea86fef Author: Alexei Sheplyakov Date: Mon Aug 25 16:57:02 2008 +0400 gcd_pf_pow: get rid of duplicate code. This function (which helps gcd() handle partially factored expressions) contained two copies of the same code. Remove one redundant copy. commit 55e50ea26252dff7432bdce8b010f9fbd058e907 Author: Alexei Sheplyakov Date: Mon Aug 25 16:56:04 2008 +0400 introduce gcd_pf_pow_pow: gcd helper to handle partially factored expressions. gcd_pf_pow_pow handles the case when both arguments of gcd() are powers. N.B. the actual code moved from gcd_pf_pow, no functional changes. commit 1d09676118944532e6100c93383d1659b1253a60 Author: Alexei Sheplyakov Date: Mon Aug 25 16:55:42 2008 +0400 gcd_pf_{pow, mul}: don't check if the arguments are polynomials. These functions gets called only from gcd(), which does this check on its own. commit adb1dbd383ae6e5a999b5f8cba72a5c2bfd50c11 Author: Alexei Sheplyakov Date: Mon Aug 25 16:55:13 2008 +0400 gcd_pf_mul: get rid of duplicate code. This function (which helps gcd() handle partially factored expressions) contained two copies of the same code. Remove one redundant copy. commit c77689e7ac8d8f4dbca0f337b6e9acf2419010ff Author: Alexei Sheplyakov Date: Mon Aug 25 16:54:46 2008 +0400 gcd(): allow user to override (some of) heuristics. GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 5: * gcd(): don't use heuristic GCD algorithm if gcd_options::no_heur_gcd flag is set. * gcd(): don't handle partially factored expressions in a special way if gcd_options::no_part_factored flag is set. commit 77b6a0304a48d6a306deeda18177680f16025b33 Author: Alexei Sheplyakov Date: Mon Aug 25 16:54:10 2008 +0400 refactor gcd() a little bit (no functional changes). GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 4: refactor gcd() a little bit, so subsequent patch(es) won't be so big and ugly. commit 7d7131d3af3de5425b7fe80b1f587740294371bc Author: Alexei Sheplyakov Date: Mon Aug 25 16:53:47 2008 +0400 introduce gcd_pf_mul: gcd helper to handle partially factored expressions. GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 3: Move the code handling products from gcd() into a separate function. This is *really* only code move, everything else should be considered a bug. commit b65fcd7481a401ec23c284c91f6f4e883e967676 Author: Alexei Sheplyakov Date: Mon Aug 25 16:53:07 2008 +0400 introduce gcd_pf_pow: gcd helper to handle partially factored expressions. GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 2: Move the code handling powers from gcd() into a separate function. This is *really* only code move, everything else should be considered a bug. commit 8474043d373a19e04008f476fa9b77e45734b604 Author: Alexei Sheplyakov Date: Mon Aug 25 16:52:45 2008 +0400 gcd: allow user to override (some of) heuristics. GiNaC tries to avoid expanding expressions while computing GCDs and applies a number of heuristics. Usually this improves performance, but in some cases it doesn't. Allow user to switch off heuristics. Part 1: * add new (optional) argument to gcd() to control its behaviour. * introduce gcd_options structure. N.B. No actual code changes so far, the actual handling of newly introduced options is the subject of further patches. commit ff09c4f8103f53fe3b7a51eb3c33eff2e5a243f0 Author: Alexei Sheplyakov Date: Sun Aug 24 15:25:24 2008 +0400 inifscn_nstdsums: make functions handling Li/G transformations reentrant. Explicitly pass the dummy symbols instead of using a global variable. As a side effect, the code is more clear now (that's a bit subjective, but anyway). commit b4b302fe5d54720b58a23568a4270e04ee1ca216 Author: Alexei Sheplyakov Date: Fri Aug 22 02:42:17 2008 +0400 Bail out if both autogen and autogen'erated file(s) are missing. This makes the error message(s) more helpful. ----------------------------------------------------------------------- Summary of changes: ginac/Makefile.am | 1 + ginac/factor.cpp | 657 +++++++++++++++++++++++++++++++++++++++++++- ginac/inifcns_nstdsums.cpp | 128 +++++---- ginac/normal.cpp | 306 ++++++++++----------- ginac/normal.h | 25 ++- 5 files changed, 897 insertions(+), 220 deletions(-) hooks/post-receive -- GiNaC -- a C++ library for symbolic computations