GiNaC  1.6.2
integral.cpp
Go to the documentation of this file.
00001 
00005 /*
00006  *  GiNaC Copyright (C) 1999-2011 Johannes Gutenberg University Mainz, Germany
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  */
00022 
00023 #include "integral.h"
00024 #include "numeric.h"
00025 #include "symbol.h"
00026 #include "add.h"
00027 #include "mul.h"
00028 #include "power.h"
00029 #include "inifcns.h"
00030 #include "wildcard.h"
00031 #include "archive.h"
00032 #include "registrar.h"
00033 #include "utils.h"
00034 #include "operators.h"
00035 #include "relational.h"
00036 
00037 using namespace std;
00038 
00039 namespace GiNaC {
00040 
00041 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(integral, basic,
00042   print_func<print_dflt>(&integral::do_print).
00043   print_func<print_latex>(&integral::do_print_latex))
00044 
00045 
00046 
00047 // default constructor
00049 
00050 integral::integral()
00051         : 
00052         x((new symbol())->setflag(status_flags::dynallocated))
00053 {}
00054 
00056 // other constructors
00058 
00059 // public
00060 
00061 integral::integral(const ex & x_, const ex & a_, const ex & b_, const ex & f_)
00062         :  x(x_), a(a_), b(b_), f(f_)
00063 {
00064     if (!is_a<symbol>(x)) {
00065         throw(std::invalid_argument("first argument of integral must be of type symbol"));
00066     }
00067 }
00068 
00070 // archiving
00072 
00073 void integral::read_archive(const archive_node& n, lst& sym_lst)
00074 {
00075     inherited::read_archive(n, sym_lst);
00076     n.find_ex("x", x, sym_lst);
00077     n.find_ex("a", a, sym_lst);
00078     n.find_ex("b", b, sym_lst);
00079     n.find_ex("f", f, sym_lst);
00080 }
00081 
00082 void integral::archive(archive_node & n) const
00083 {
00084     inherited::archive(n);
00085     n.add_ex("x", x);
00086     n.add_ex("a", a);
00087     n.add_ex("b", b);
00088     n.add_ex("f", f);
00089 }
00090 
00092 // functions overriding virtual functions from base classes
00094 
00095 void integral::do_print(const print_context & c, unsigned level) const
00096 {
00097     c.s << "integral(";
00098     x.print(c);
00099     c.s << ",";
00100     a.print(c);
00101     c.s << ",";
00102     b.print(c);
00103     c.s << ",";
00104     f.print(c);
00105     c.s << ")";
00106 }
00107 
00108 void integral::do_print_latex(const print_latex & c, unsigned level) const
00109 {
00110     string varname = ex_to<symbol>(x).get_name();
00111     if (level > precedence())
00112         c.s << "\\left(";
00113     c.s << "\\int_{";
00114     a.print(c);
00115     c.s << "}^{";
00116     b.print(c);
00117     c.s << "} d";
00118     if (varname.size() > 1)
00119         c.s << "\\," << varname << "\\:";
00120     else
00121         c.s << varname << "\\,";
00122     f.print(c,precedence());
00123     if (level > precedence())
00124         c.s << "\\right)";
00125 }
00126 
00127 int integral::compare_same_type(const basic & other) const
00128 {
00129     GINAC_ASSERT(is_exactly_a<integral>(other));
00130     const integral &o = static_cast<const integral &>(other);
00131 
00132     int cmpval = x.compare(o.x);
00133     if (cmpval)
00134         return cmpval;
00135     cmpval = a.compare(o.a);
00136     if (cmpval)
00137         return cmpval;
00138     cmpval = b.compare(o.b);
00139     if (cmpval)
00140         return cmpval;
00141     return f.compare(o.f);
00142 }
00143 
00144 ex integral::eval(int level) const
00145 {
00146     if ((level==1) && (flags & status_flags::evaluated))
00147         return *this;
00148     if (level == -max_recursion_level)
00149         throw(std::runtime_error("max recursion level reached"));
00150 
00151     ex eintvar = (level==1) ? x : x.eval(level-1);
00152     ex ea      = (level==1) ? a : a.eval(level-1);
00153     ex eb      = (level==1) ? b : b.eval(level-1);
00154     ex ef      = (level==1) ? f : f.eval(level-1);
00155 
00156     if (!ef.has(eintvar) && !haswild(ef))
00157         return eb*ef-ea*ef;
00158 
00159     if (ea==eb)
00160         return _ex0;
00161 
00162     if (are_ex_trivially_equal(eintvar,x) && are_ex_trivially_equal(ea,a)
00163             && are_ex_trivially_equal(eb,b) && are_ex_trivially_equal(ef,f))
00164         return this->hold();
00165     return (new integral(eintvar, ea, eb, ef))
00166         ->setflag(status_flags::dynallocated | status_flags::evaluated);
00167 }
00168 
00169 ex integral::evalf(int level) const
00170 {
00171     ex ea;
00172     ex eb;
00173     ex ef;
00174 
00175     if (level==1) {
00176         ea = a;
00177         eb = b;
00178         ef = f;
00179     } else if (level == -max_recursion_level) {
00180         throw(runtime_error("max recursion level reached"));
00181     } else {
00182         ea = a.evalf(level-1);
00183         eb = b.evalf(level-1);
00184         ef = f.evalf(level-1);
00185     }
00186 
00187     // 12.34 is just an arbitrary number used to check whether a number
00188     // results after subsituting a number for the integration variable.
00189     if (is_exactly_a<numeric>(ea) && is_exactly_a<numeric>(eb) 
00190             && is_exactly_a<numeric>(ef.subs(x==12.34).evalf())) {
00191             return adaptivesimpson(x, ea, eb, ef);
00192     }
00193 
00194     if (are_ex_trivially_equal(a, ea) && are_ex_trivially_equal(b, eb)
00195                 && are_ex_trivially_equal(f, ef))
00196             return *this;
00197         else
00198             return (new integral(x, ea, eb, ef))
00199                 ->setflag(status_flags::dynallocated);
00200 }
00201 
00202 int integral::max_integration_level = 15;
00203 ex integral::relative_integration_error = 1e-8;
00204 
00205 ex subsvalue(const ex & var, const ex & value, const ex & fun)
00206 {
00207     ex result = fun.subs(var==value).evalf();
00208     if (is_a<numeric>(result))
00209         return result;
00210     throw logic_error("integrand does not evaluate to numeric");
00211 }
00212 
00213 struct error_and_integral
00214 {
00215     error_and_integral(const ex &err, const ex &integ)
00216         :error(err), integral(integ){}
00217     ex error;
00218     ex integral;
00219 };
00220 
00221 struct error_and_integral_is_less
00222 {
00223     bool operator()(const error_and_integral &e1,const error_and_integral &e2) const
00224     {
00225         int c = e1.integral.compare(e2.integral);
00226         if(c < 0)
00227             return true;
00228         if(c > 0)
00229             return false;
00230         return ex_is_less()(e1.error, e2.error);
00231     }
00232 };
00233 
00234 typedef map<error_and_integral, ex, error_and_integral_is_less> lookup_map;
00235 
00243 ex adaptivesimpson(const ex & x, const ex & a_in, const ex & b_in, const ex & f, const ex & error)
00244 {
00245     // Check whether boundaries and error are numbers.
00246     ex a = is_exactly_a<numeric>(a_in) ? a_in : a_in.evalf();
00247     ex b = is_exactly_a<numeric>(b_in) ? b_in : b_in.evalf();
00248     if(!is_exactly_a<numeric>(a) || !is_exactly_a<numeric>(b))
00249         throw std::runtime_error("For numerical integration the boundaries of the integral should evalf into numbers.");
00250     if(!is_exactly_a<numeric>(error))
00251         throw std::runtime_error("For numerical integration the error should be a number.");
00252 
00253     // Use lookup table to be potentially much faster.
00254     static lookup_map lookup;
00255     static symbol ivar("ivar");
00256     ex lookupex = integral(ivar,a,b,f.subs(x==ivar));
00257     lookup_map::iterator emi = lookup.find(error_and_integral(error, lookupex));
00258     if (emi!=lookup.end())
00259         return emi->second;
00260 
00261     ex app = 0;
00262     int i = 1;
00263     exvector avec(integral::max_integration_level+1);
00264     exvector hvec(integral::max_integration_level+1);
00265     exvector favec(integral::max_integration_level+1);
00266     exvector fbvec(integral::max_integration_level+1);
00267     exvector fcvec(integral::max_integration_level+1);
00268     exvector svec(integral::max_integration_level+1);
00269     exvector errorvec(integral::max_integration_level+1);
00270     vector<int> lvec(integral::max_integration_level+1);
00271 
00272     avec[i] = a;
00273     hvec[i] = (b-a)/2;
00274     favec[i] = subsvalue(x, a, f);
00275     fcvec[i] = subsvalue(x, a+hvec[i], f);
00276     fbvec[i] = subsvalue(x, b, f);
00277     svec[i] = hvec[i]*(favec[i]+4*fcvec[i]+fbvec[i])/3;
00278     lvec[i] = 1;
00279     errorvec[i] = error*abs(svec[i]);
00280 
00281     while (i>0) {
00282         ex fd = subsvalue(x, avec[i]+hvec[i]/2, f);
00283         ex fe = subsvalue(x, avec[i]+3*hvec[i]/2, f);
00284         ex s1 = hvec[i]*(favec[i]+4*fd+fcvec[i])/6;
00285         ex s2 = hvec[i]*(fcvec[i]+4*fe+fbvec[i])/6;
00286         ex nu1 = avec[i];
00287         ex nu2 = favec[i];
00288         ex nu3 = fcvec[i];
00289         ex nu4 = fbvec[i];
00290         ex nu5 = hvec[i];
00291         // hopefully prevents a crash if the function is zero sometimes.
00292         ex nu6 = max(errorvec[i], abs(s1+s2)*error);
00293         ex nu7 = svec[i];
00294         int nu8 = lvec[i];
00295         --i;
00296         if (abs(ex_to<numeric>(s1+s2-nu7)) <= nu6)
00297             app+=(s1+s2);
00298         else {
00299             if (nu8>=integral::max_integration_level)
00300                 throw runtime_error("max integration level reached");
00301             ++i;
00302             avec[i] = nu1+nu5;
00303             favec[i] = nu3;
00304             fcvec[i] = fe;
00305             fbvec[i] = nu4;
00306             hvec[i] = nu5/2;
00307             errorvec[i]=nu6/2;
00308             svec[i] = s2;
00309             lvec[i] = nu8+1;
00310             ++i;
00311             avec[i] = nu1;
00312             favec[i] = nu2;
00313             fcvec[i] = fd;
00314             fbvec[i] = nu3;
00315             hvec[i] = hvec[i-1];
00316             errorvec[i]=errorvec[i-1];
00317             svec[i] = s1;
00318             lvec[i] = lvec[i-1];
00319         }
00320     }
00321 
00322     lookup[error_and_integral(error, lookupex)]=app;
00323     return app;
00324 }
00325 
00326 int integral::degree(const ex & s) const
00327 {
00328     return ((b-a)*f).degree(s);
00329 }
00330 
00331 int integral::ldegree(const ex & s) const
00332 {
00333     return ((b-a)*f).ldegree(s);
00334 }
00335 
00336 ex integral::eval_ncmul(const exvector & v) const
00337 {
00338     return f.eval_ncmul(v);
00339 }
00340 
00341 size_t integral::nops() const
00342 {
00343     return 4;
00344 }
00345 
00346 ex integral::op(size_t i) const
00347 {
00348     GINAC_ASSERT(i<4);
00349 
00350     switch (i) {
00351         case 0:
00352             return x;
00353         case 1:
00354             return a;
00355         case 2:
00356             return b;
00357         case 3:
00358             return f;
00359         default:
00360             throw (std::out_of_range("integral::op() out of range"));
00361     }
00362 }
00363 
00364 ex & integral::let_op(size_t i)
00365 {
00366     ensure_if_modifiable();
00367     switch (i) {
00368         case 0:
00369             return x;
00370         case 1:
00371             return a;
00372         case 2:
00373             return b;
00374         case 3:
00375             return f;
00376         default:
00377             throw (std::out_of_range("integral::let_op() out of range"));
00378     }
00379 }
00380 
00381 ex integral::expand(unsigned options) const
00382 {
00383     if (options==0 && (flags & status_flags::expanded))
00384         return *this;
00385 
00386     ex newa = a.expand(options);
00387     ex newb = b.expand(options);
00388     ex newf = f.expand(options);
00389 
00390     if (is_a<add>(newf)) {
00391         exvector v;
00392         v.reserve(newf.nops());
00393         for (size_t i=0; i<newf.nops(); ++i)
00394             v.push_back(integral(x, newa, newb, newf.op(i)).expand(options));
00395         return ex(add(v)).expand(options);
00396     }
00397 
00398     if (is_a<mul>(newf)) {
00399         ex prefactor = 1;
00400         ex rest = 1;
00401         for (size_t i=0; i<newf.nops(); ++i)
00402             if (newf.op(i).has(x))
00403                 rest *= newf.op(i);
00404             else
00405                 prefactor *= newf.op(i);
00406         if (prefactor != 1)
00407             return (prefactor*integral(x, newa, newb, rest)).expand(options);
00408     }
00409 
00410     if (are_ex_trivially_equal(a, newa) && are_ex_trivially_equal(b, newb)
00411             && are_ex_trivially_equal(f, newf)) {
00412         if (options==0)
00413             this->setflag(status_flags::expanded);
00414         return *this;
00415     }
00416 
00417     const basic & newint = (new integral(x, newa, newb, newf))
00418         ->setflag(status_flags::dynallocated);
00419     if (options == 0)
00420         newint.setflag(status_flags::expanded);
00421     return newint;
00422 }
00423 
00424 ex integral::derivative(const symbol & s) const
00425 {
00426     if (s==x)
00427         throw(logic_error("differentiation with respect to dummy variable"));
00428     return b.diff(s)*f.subs(x==b)-a.diff(s)*f.subs(x==a)+integral(x, a, b, f.diff(s));
00429 }
00430 
00431 unsigned integral::return_type() const
00432 {
00433     return f.return_type();
00434 }
00435 
00436 return_type_t integral::return_type_tinfo() const
00437 {
00438     return f.return_type_tinfo();
00439 }
00440 
00441 ex integral::conjugate() const
00442 {
00443     ex conja = a.conjugate();
00444     ex conjb = b.conjugate();
00445     ex conjf = f.conjugate().subs(x.conjugate()==x);
00446 
00447     if (are_ex_trivially_equal(a, conja) && are_ex_trivially_equal(b, conjb)
00448             && are_ex_trivially_equal(f, conjf))
00449         return *this;
00450 
00451     return (new integral(x, conja, conjb, conjf))
00452         ->setflag(status_flags::dynallocated);
00453 }
00454 
00455 ex integral::eval_integ() const
00456 {
00457     if (!(flags & status_flags::expanded))
00458         return this->expand().eval_integ();
00459     
00460     if (f==x)
00461         return b*b/2-a*a/2;
00462     if (is_a<power>(f) && f.op(0)==x) {
00463         if (f.op(1)==-1)
00464             return log(b/a);
00465         if (!f.op(1).has(x)) {
00466             ex primit = power(x,f.op(1)+1)/(f.op(1)+1);
00467             return primit.subs(x==b)-primit.subs(x==a);
00468         }
00469     }
00470 
00471     return *this;
00472 }
00473 
00474 GINAC_BIND_UNARCHIVER(integral);
00475 } // namespace GiNaC

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.