Be more careful about final top-level substitution.
authorRichard Kreckel <kreckel@ginac.de>
Wed, 15 Sep 2010 07:11:57 +0000 (09:11 +0200)
committerRichard Kreckel <kreckel@ginac.de>
Wed, 15 Sep 2010 07:11:57 +0000 (09:11 +0200)
Substituting x==log(x) in exp(x) erroneously returned log(x) because of a
final subst(x==log(x)) after having eval'ed exp(log(x)) -> x. This final
substitution is wrong in the general case. On the other hand, the intent
is to syntactically substitute functions of a given kind etc. This patch
suppresses the final top-level substitution unless the intermediate result
is a container.

Thanks to Burcin Erocal for reporting this bug (originally described by
Kees van Schaik on sage-support@googlegroops.com).

check/exam_misc.cpp
doc/tutorial/ginac.texi
ginac/container.h

index 9dbfd6b..fc9c41e 100644 (file)
@@ -219,6 +219,15 @@ static unsigned exam_subs()
                ++result;
        }
 
+       // And this used to fail in GiNaC 1.5.8 because it first substituted
+       // exp(x) -> exp(log(x)) -> x, and then substitued again x -> log(x)
+       e1 = exp(x);
+       e2 = e1.subs(x == log(x));
+       if (!e2.is_equal(x)) {
+               clog << "exp(x).subs(x==log(x)) erroneously returned " << e2 << " instead of x" << endl;
+               ++result;
+       }
+
        e1 = sin(1+sin(x));
        e2 = e1.subs(sin(wild()) == cos(wild()));
        if (!e2.is_equal(cos(1+cos(x)))) {
index 571144a..e8f0395 100644 (file)
@@ -4263,7 +4263,7 @@ In the first form, @code{subs()} accepts a relational of the form
 @{
     symbol x("x"), y("y");
 
-    ex e1 = 2*x^2-4*x+3;
+    ex e1 = 2*x*x-4*x+3;
     cout << "e1(7) = " << e1.subs(x == 7) << endl;
      // -> 73
 
index 632145e..456cf5c 100644 (file)
@@ -574,11 +574,27 @@ ex container<C>::eval(int level) const
 template <template <class T, class = std::allocator<T> > class C>
 ex container<C>::subs(const exmap & m, unsigned options) const
 {
+       // After having subs'ed all children, this method subs'es one final
+       // level, but only if the intermediate result is a container! This is
+       // because if the intermediate result has eval'ed to a non-container a
+       // last level substitution would be wrong, as this example involving a
+       // function f and its inverse f^-1 shows:
+       // f(x).subs(x==f^-1(x))
+       //   -> f(f^-1(x))  [subschildren]
+       //   -> x           [eval]   /* must not subs(x==f^-1(x))! */
        std::auto_ptr<STLT> vp = subschildren(m, options);
-       if (vp.get())
-               return ex_to<basic>(thiscontainer(vp)).subs_one_level(m, options);
-       else
-               return subs_one_level(m, options);
+       if (vp.get()) {
+               ex result(thiscontainer(vp));
+               if (is_a<container<C> >(result))
+                       return ex_to<basic>(result).subs_one_level(m, options);
+               else
+                       return result;
+       } else {
+               if (is_a<container<C> >(*this))
+                       return subs_one_level(m, options);
+               else
+                       return *this;
+       }
 }
 
 /** Compare two containers of the same type. */