From: Richard Kreckel Date: Wed, 16 Dec 2015 20:22:36 +0000 (+0100) Subject: Make .eval() evaluate top-level only. X-Git-Tag: release_1-7-0~7^2~36 X-Git-Url: https://www.ginac.de/ginac.git//ginac.git?p=ginac.git;a=commitdiff_plain;h=6c946d4c762f5a0d6a3b742f03556dd018d63886;ds=sidebyside Make .eval() evaluate top-level only. With the previous patch, some old workarounds have become unnecessary: If all expressions are evaluated, any object which is an aggregate of expressions will only ever have to evaluate the top level. As such, it has become pointless to evaluate child objects of an expression prior to doing its own term-rewriting. This patch removes the evaluation of children from all GiNaC objects. It also removes the now superfluous parameter 'level' of the eval methods. --- diff --git a/check/exam_inifcns_nstdsums.cpp b/check/exam_inifcns_nstdsums.cpp index 6e4d846a..edba7769 100644 --- a/check/exam_inifcns_nstdsums.cpp +++ b/check/exam_inifcns_nstdsums.cpp @@ -146,7 +146,7 @@ static unsigned inifcns_test_HLi() res.append(H(lst{-2,1,3},numeric(1)/3).hold() - -Li(lst{2,1,3},lst{-numeric(1)/3,-1,1}).hold()); res.append(H(lst{-2,1,3},numeric(98)/100).hold() - -Li(lst{2,1,3},lst{-numeric(98)/100,-1,1}).hold()); res.append(H(lst{-2,1,3},numeric(245)/100).hold() - -Li(lst{2,1,3},lst{-numeric(245)/100,-1,1}).hold()); - res.append(H(lst{-3,1,-2,0,0},numeric(3)/10).hold() - convert_H_to_Li(lst{-3,1,-2,0,0},numeric(3)/10).eval()); + res.append(H(lst{-3,1,-2,0,0},numeric(3)/10).hold() - convert_H_to_Li(lst{-3,1,-2,0,0},numeric(3)/10)); for (lst::const_iterator it = res.begin(); it != res.end(); it++) { ex diff = abs((*it).evalf()); diff --git a/check/exam_paranoia.cpp b/check/exam_paranoia.cpp index 6ab452ba..ad7ee8d2 100644 --- a/check/exam_paranoia.cpp +++ b/check/exam_paranoia.cpp @@ -73,23 +73,10 @@ static unsigned exam_paranoia2() f = e*y; g = f - e*y; - // After .expand(), g should be zero: - if (!g.expand().is_zero()) { - clog << "e = (x + z*x); f = e*y; expand(f - e*y) erroneously returned " - << g.expand() << endl; - ++result; - } // After .eval(), g should be zero: - if (!g.eval().is_zero()) { - clog << "e = (x + z*x); f = e*y; eval(f - e*y) erroneously returned " - << g.eval() << endl; - ++result; - } - // This actually worked already back in April 1999. - // But we are *very* paranoic! - if (!g.expand().eval().is_zero()) { - clog << "e = (x + z*x); f = e*y; eval(expand(f - e*y)) erroneously returned " - << g.expand().eval() << endl; + if (!g.is_zero()) { + clog << "e = (x + z*x); f = e*y; g = (f - e*y) erroneously returned g == " + << g << endl; ++result; } @@ -113,16 +100,6 @@ static unsigned exam_paranoia3() << f << endl; ++result; } - if (!f.eval().is_equal(y)) { - clog << "e = x*y - y; eval(e.subs(x == 2)) erroneously returned " - << f.eval() << endl; - ++result; - } - if (!f.expand().is_equal(y)) { - clog << "e = x*y - y; expand(e.subs(x == 2)) erroneously returned " - << f.expand() << endl; - ++result; - } return result; } @@ -143,11 +120,6 @@ static unsigned exam_paranoia4() << g << endl; ++result; } - if (!g.is_zero()) { - clog << "e = pow(x,2) + x + 1; f = pow(x,2) + x + 1; g = e-f; g.eval() erroneously returned " - << g.eval() << endl; - ++result; - } return result; } @@ -264,7 +236,7 @@ static unsigned exam_paranoia10() ex r; try { - r = pow(b,e).eval(); + r = pow(b, e); if (!(r-2*sqrt(ex(2))).is_zero()) { clog << "2^(3/2) erroneously returned " << r << " instead of 2*sqrt(2)" << endl; ++result; diff --git a/check/exam_structure.cpp b/check/exam_structure.cpp index 56f205ec..bc5bc837 100644 --- a/check/exam_structure.cpp +++ b/check/exam_structure.cpp @@ -58,7 +58,7 @@ template <> void sprod::print(const print_context & c, unsigned level) const c.s << "<" << sp.left << "|" << sp.right << ">"; } -template <> ex sprod::eval(int level) const +template <> ex sprod::eval() const { // symmetric scalar product const sprod_s & sp = get_struct(); diff --git a/doc/tutorial/ginac.texi b/doc/tutorial/ginac.texi index 0d86eeee..fd5550fb 100644 --- a/doc/tutorial/ginac.texi +++ b/doc/tutorial/ginac.texi @@ -838,8 +838,8 @@ some immediate simplifications. Internally, the anonymous evaluator in GiNaC is implemented by the methods @example -ex ex::eval(int level = 0) const; -ex basic::eval(int level = 0) const; +ex ex::eval() const; +ex basic::eval() const; @end example but unless you are extending GiNaC with your own classes or functions, there @@ -8241,11 +8241,11 @@ class mystring : public basic @{ ... public: - ex eval(int level = 0) const; + ex eval() const override; ... @}; -ex mystring::eval(int level) const +ex mystring::eval() const @{ string new_str; for (size_t i=0; ihold();}. +The @code{hold()} member function sets a flag in the object that prevents +further evaluation. Otherwise we might end up in an endless loop. When you +want to return the object unmodified, use @code{return this->hold();}. + +If our class had subobjects, we would have to evaluate them first (unless +they are all of type @code{ex}, which are automatically evaluated). We don't +have any subexpressions in the @code{mystring} class, so we are not concerned +with this. Let's confirm that it works: @@ -8293,8 +8293,8 @@ required but will make operations with objects of the class more efficient: @cindex @code{calchash()} @cindex @code{is_equal_same_type()} @example -unsigned calchash() const; -bool is_equal_same_type(const basic & other) const; +unsigned calchash() const override; +bool is_equal_same_type(const basic & other) const override; @end example The @code{calchash()} method returns an @code{unsigned} hash value for the @@ -8315,10 +8315,10 @@ For a real algebraic class, there are probably some more functions that you might want to provide: @example -bool info(unsigned inf) const; -ex evalf(int level = 0) const; -ex series(const relational & r, int order, unsigned options = 0) const; -ex derivative(const symbol & s) const; +bool info(unsigned inf) const override; +ex evalf(int level = 0) const override; +ex series(const relational & r, int order, unsigned options = 0) const override; +ex derivative(const symbol & s) const override; @end example If your class stores sub-expressions (see the scalar product example in the @@ -8326,11 +8326,11 @@ previous section) you will probably want to override @cindex @code{let_op()} @example -size_t nops() cont; -ex op(size_t i) const; -ex & let_op(size_t i); -ex subs(const lst & ls, const lst & lr, unsigned options = 0) const; -ex map(map_function & f) const; +size_t nops() const override; +ex op(size_t i) const override; +ex & let_op(size_t i) override; +ex subs(const lst & ls, const lst & lr, unsigned options = 0) const override; +ex map(map_function & f) const override; @end example @code{let_op()} is a variant of @code{op()} that allows write access. The diff --git a/ginac/add.cpp b/ginac/add.cpp index ed587309..2a2e2f83 100644 --- a/ginac/add.cpp +++ b/ginac/add.cpp @@ -326,17 +326,16 @@ ex add::coeff(const ex & s, int n) const * an expression that contain a plain number. * - +(;c) -> c * - +(x;0) -> x - * - * @param level cut-off in recursive evaluation */ -ex add::eval(int level) const + */ +ex add::eval() const { - if ((level == 1) && (flags & status_flags::evaluated)) { + if (flags & status_flags::evaluated) { GINAC_ASSERT(seq.size()>0); GINAC_ASSERT(seq.size()>1 || !overall_coeff.is_zero()); return *this; } - const epvector evaled = evalchildren(level); + const epvector evaled = evalchildren(); if (unlikely(!evaled.empty())) { // start over evaluating a new object return dynallocate(std::move(evaled), overall_coeff); diff --git a/ginac/add.h b/ginac/add.h index 659692b9..42761576 100644 --- a/ginac/add.h +++ b/ginac/add.h @@ -51,7 +51,7 @@ public: int degree(const ex & s) const override; int ldegree(const ex & s) const override; ex coeff(const ex & s, int n=1) const override; - ex eval(int level=0) const override; + ex eval() const override; ex evalm() const override; ex series(const relational & r, int order, unsigned options = 0) const override; ex normal(exmap & repl, exmap & rev_lookup, int level=0) const override; diff --git a/ginac/basic.cpp b/ginac/basic.cpp index 96cfd14c..a2be1ca5 100644 --- a/ginac/basic.cpp +++ b/ginac/basic.cpp @@ -410,7 +410,7 @@ ex basic::collect(const ex & s, bool distributed) const } /** Perform automatic non-interruptive term rewriting rules. */ -ex basic::eval(int level) const +ex basic::eval() const { // There is nothing to do for basic objects: return hold(); diff --git a/ginac/basic.h b/ginac/basic.h index a8062c15..0cec003a 100644 --- a/ginac/basic.h +++ b/ginac/basic.h @@ -135,7 +135,7 @@ public: // only const functions please (may break reference counting) } // evaluation - virtual ex eval(int level = 0) const; + virtual ex eval() const; virtual ex evalf(int level = 0) const; virtual ex evalm() const; virtual ex eval_integ() const; diff --git a/ginac/container.h b/ginac/container.h index 04ab31d1..0881095a 100644 --- a/ginac/container.h +++ b/ginac/container.h @@ -205,7 +205,6 @@ public: size_t nops() const override { return this->seq.size(); } ex op(size_t i) const override; ex & let_op(size_t i) override; - ex eval(int level = 0) const override; ex subs(const exmap & m, unsigned options = 0) const override; void read_archive(const archive_node &n, lst &sym_lst) override @@ -336,7 +335,6 @@ protected: void do_print_tree(const print_tree & c, unsigned level) const; void do_print_python(const print_python & c, unsigned level) const; void do_print_python_repr(const print_python_repr & c, unsigned level) const; - STLT evalchildren(int level) const; STLT subschildren(const exmap & m, unsigned options = 0) const; }; @@ -482,15 +480,6 @@ ex & container::let_op(size_t i) return *it; } -template