GiNaC  1.6.2
pseries.cpp
Go to the documentation of this file.
00001 
00006 /*
00007  *  GiNaC Copyright (C) 1999-2011 Johannes Gutenberg University Mainz, Germany
00008  *
00009  *  This program is free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program; if not, write to the Free Software
00021  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00022  */
00023 
00024 #include "pseries.h"
00025 #include "add.h"
00026 #include "inifcns.h" // for Order function
00027 #include "lst.h"
00028 #include "mul.h"
00029 #include "power.h"
00030 #include "relational.h"
00031 #include "operators.h"
00032 #include "symbol.h"
00033 #include "integral.h"
00034 #include "archive.h"
00035 #include "utils.h"
00036 
00037 #include <limits>
00038 #include <numeric>
00039 #include <stdexcept>
00040 
00041 namespace GiNaC {
00042 
00043 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(pseries, basic,
00044   print_func<print_context>(&pseries::do_print).
00045   print_func<print_latex>(&pseries::do_print_latex).
00046   print_func<print_tree>(&pseries::do_print_tree).
00047   print_func<print_python>(&pseries::do_print_python).
00048   print_func<print_python_repr>(&pseries::do_print_python_repr))
00049 
00050 
00051 /*
00052  *  Default constructor
00053  */
00054 
00055 pseries::pseries() { }
00056 
00057 
00058 /*
00059  *  Other ctors
00060  */
00061 
00071 pseries::pseries(const ex &rel_, const epvector &ops_) : seq(ops_)
00072 {
00073     GINAC_ASSERT(is_a<relational>(rel_));
00074     GINAC_ASSERT(is_a<symbol>(rel_.lhs()));
00075     point = rel_.rhs();
00076     var = rel_.lhs();
00077 }
00078 
00079 
00080 /*
00081  *  Archiving
00082  */
00083 
00084 void pseries::read_archive(const archive_node &n, lst &sym_lst) 
00085 {
00086     inherited::read_archive(n, sym_lst);
00087     archive_node::archive_node_cit first = n.find_first("coeff");
00088     archive_node::archive_node_cit last = n.find_last("power");
00089     ++last;
00090     seq.reserve((last-first)/2);
00091 
00092     for (archive_node::archive_node_cit loc = first; loc < last;) {
00093         ex rest;
00094         ex coeff;
00095         n.find_ex_by_loc(loc++, rest, sym_lst);
00096         n.find_ex_by_loc(loc++, coeff, sym_lst);
00097         seq.push_back(expair(rest, coeff));
00098     }
00099 
00100     n.find_ex("var", var, sym_lst);
00101     n.find_ex("point", point, sym_lst);
00102 }
00103 
00104 void pseries::archive(archive_node &n) const
00105 {
00106     inherited::archive(n);
00107     epvector::const_iterator i = seq.begin(), iend = seq.end();
00108     while (i != iend) {
00109         n.add_ex("coeff", i->rest);
00110         n.add_ex("power", i->coeff);
00111         ++i;
00112     }
00113     n.add_ex("var", var);
00114     n.add_ex("point", point);
00115 }
00116 
00117 
00119 // functions overriding virtual functions from base classes
00121 
00122 void pseries::print_series(const print_context & c, const char *openbrace, const char *closebrace, const char *mul_sym, const char *pow_sym, unsigned level) const
00123 {
00124     if (precedence() <= level)
00125         c.s << '(';
00126         
00127     // objects of type pseries must not have any zero entries, so the
00128     // trivial (zero) pseries needs a special treatment here:
00129     if (seq.empty())
00130         c.s << '0';
00131 
00132     epvector::const_iterator i = seq.begin(), end = seq.end();
00133     while (i != end) {
00134 
00135         // print a sign, if needed
00136         if (i != seq.begin())
00137             c.s << '+';
00138 
00139         if (!is_order_function(i->rest)) {
00140 
00141             // print 'rest', i.e. the expansion coefficient
00142             if (i->rest.info(info_flags::numeric) &&
00143                 i->rest.info(info_flags::positive)) {
00144                 i->rest.print(c);
00145             } else {
00146                 c.s << openbrace << '(';
00147                 i->rest.print(c);
00148                 c.s << ')' << closebrace;
00149             }
00150 
00151             // print 'coeff', something like (x-1)^42
00152             if (!i->coeff.is_zero()) {
00153                 c.s << mul_sym;
00154                 if (!point.is_zero()) {
00155                     c.s << openbrace << '(';
00156                     (var-point).print(c);
00157                     c.s << ')' << closebrace;
00158                 } else
00159                     var.print(c);
00160                 if (i->coeff.compare(_ex1)) {
00161                     c.s << pow_sym;
00162                     c.s << openbrace;
00163                     if (i->coeff.info(info_flags::negative)) {
00164                         c.s << '(';
00165                         i->coeff.print(c);
00166                         c.s << ')';
00167                     } else
00168                         i->coeff.print(c);
00169                     c.s << closebrace;
00170                 }
00171             }
00172         } else
00173             Order(power(var-point,i->coeff)).print(c);
00174         ++i;
00175     }
00176 
00177     if (precedence() <= level)
00178         c.s << ')';
00179 }
00180 
00181 void pseries::do_print(const print_context & c, unsigned level) const
00182 {
00183     print_series(c, "", "", "*", "^", level);
00184 }
00185 
00186 void pseries::do_print_latex(const print_latex & c, unsigned level) const
00187 {
00188     print_series(c, "{", "}", " ", "^", level);
00189 }
00190 
00191 void pseries::do_print_python(const print_python & c, unsigned level) const
00192 {
00193     print_series(c, "", "", "*", "**", level);
00194 }
00195 
00196 void pseries::do_print_tree(const print_tree & c, unsigned level) const
00197 {
00198     c.s << std::string(level, ' ') << class_name() << " @" << this
00199         << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
00200         << std::endl;
00201     size_t num = seq.size();
00202     for (size_t i=0; i<num; ++i) {
00203         seq[i].rest.print(c, level + c.delta_indent);
00204         seq[i].coeff.print(c, level + c.delta_indent);
00205         c.s << std::string(level + c.delta_indent, ' ') << "-----" << std::endl;
00206     }
00207     var.print(c, level + c.delta_indent);
00208     point.print(c, level + c.delta_indent);
00209 }
00210 
00211 void pseries::do_print_python_repr(const print_python_repr & c, unsigned level) const
00212 {
00213     c.s << class_name() << "(relational(";
00214     var.print(c);
00215     c.s << ',';
00216     point.print(c);
00217     c.s << "),[";
00218     size_t num = seq.size();
00219     for (size_t i=0; i<num; ++i) {
00220         if (i)
00221             c.s << ',';
00222         c.s << '(';
00223         seq[i].rest.print(c);
00224         c.s << ',';
00225         seq[i].coeff.print(c);
00226         c.s << ')';
00227     }
00228     c.s << "])";
00229 }
00230 
00231 int pseries::compare_same_type(const basic & other) const
00232 {
00233     GINAC_ASSERT(is_a<pseries>(other));
00234     const pseries &o = static_cast<const pseries &>(other);
00235     
00236     // first compare the lengths of the series...
00237     if (seq.size()>o.seq.size())
00238         return 1;
00239     if (seq.size()<o.seq.size())
00240         return -1;
00241     
00242     // ...then the expansion point...
00243     int cmpval = var.compare(o.var);
00244     if (cmpval)
00245         return cmpval;
00246     cmpval = point.compare(o.point);
00247     if (cmpval)
00248         return cmpval;
00249     
00250     // ...and if that failed the individual elements
00251     epvector::const_iterator it = seq.begin(), o_it = o.seq.begin();
00252     while (it!=seq.end() && o_it!=o.seq.end()) {
00253         cmpval = it->compare(*o_it);
00254         if (cmpval)
00255             return cmpval;
00256         ++it;
00257         ++o_it;
00258     }
00259 
00260     // so they are equal.
00261     return 0;
00262 }
00263 
00265 size_t pseries::nops() const
00266 {
00267     return seq.size();
00268 }
00269 
00271 ex pseries::op(size_t i) const
00272 {
00273     if (i >= seq.size())
00274         throw (std::out_of_range("op() out of range"));
00275 
00276     if (is_order_function(seq[i].rest))
00277         return Order(power(var-point, seq[i].coeff));
00278     return seq[i].rest * power(var - point, seq[i].coeff);
00279 }
00280 
00284 int pseries::degree(const ex &s) const
00285 {
00286     if (var.is_equal(s)) {
00287         // Return last exponent
00288         if (seq.size())
00289             return ex_to<numeric>((seq.end()-1)->coeff).to_int();
00290         else
00291             return 0;
00292     } else {
00293         epvector::const_iterator it = seq.begin(), itend = seq.end();
00294         if (it == itend)
00295             return 0;
00296         int max_pow = std::numeric_limits<int>::min();
00297         while (it != itend) {
00298             int pow = it->rest.degree(s);
00299             if (pow > max_pow)
00300                 max_pow = pow;
00301             ++it;
00302         }
00303         return max_pow;
00304     }
00305 }
00306 
00312 int pseries::ldegree(const ex &s) const
00313 {
00314     if (var.is_equal(s)) {
00315         // Return first exponent
00316         if (seq.size())
00317             return ex_to<numeric>((seq.begin())->coeff).to_int();
00318         else
00319             return 0;
00320     } else {
00321         epvector::const_iterator it = seq.begin(), itend = seq.end();
00322         if (it == itend)
00323             return 0;
00324         int min_pow = std::numeric_limits<int>::max();
00325         while (it != itend) {
00326             int pow = it->rest.ldegree(s);
00327             if (pow < min_pow)
00328                 min_pow = pow;
00329             ++it;
00330         }
00331         return min_pow;
00332     }
00333 }
00334 
00342 ex pseries::coeff(const ex &s, int n) const
00343 {
00344     if (var.is_equal(s)) {
00345         if (seq.empty())
00346             return _ex0;
00347         
00348         // Binary search in sequence for given power
00349         numeric looking_for = numeric(n);
00350         int lo = 0, hi = seq.size() - 1;
00351         while (lo <= hi) {
00352             int mid = (lo + hi) / 2;
00353             GINAC_ASSERT(is_exactly_a<numeric>(seq[mid].coeff));
00354             int cmp = ex_to<numeric>(seq[mid].coeff).compare(looking_for);
00355             switch (cmp) {
00356                 case -1:
00357                     lo = mid + 1;
00358                     break;
00359                 case 0:
00360                     return seq[mid].rest;
00361                 case 1:
00362                     hi = mid - 1;
00363                     break;
00364                 default:
00365                     throw(std::logic_error("pseries::coeff: compare() didn't return -1, 0 or 1"));
00366             }
00367         }
00368         return _ex0;
00369     } else
00370         return convert_to_poly().coeff(s, n);
00371 }
00372 
00374 ex pseries::collect(const ex &s, bool distributed) const
00375 {
00376     return *this;
00377 }
00378 
00380 ex pseries::eval(int level) const
00381 {
00382     if (level == 1)
00383         return this->hold();
00384     
00385     if (level == -max_recursion_level)
00386         throw (std::runtime_error("pseries::eval(): recursion limit exceeded"));
00387     
00388     // Construct a new series with evaluated coefficients
00389     epvector new_seq;
00390     new_seq.reserve(seq.size());
00391     epvector::const_iterator it = seq.begin(), itend = seq.end();
00392     while (it != itend) {
00393         new_seq.push_back(expair(it->rest.eval(level-1), it->coeff));
00394         ++it;
00395     }
00396     return (new pseries(relational(var,point), new_seq))->setflag(status_flags::dynallocated | status_flags::evaluated);
00397 }
00398 
00400 ex pseries::evalf(int level) const
00401 {
00402     if (level == 1)
00403         return *this;
00404     
00405     if (level == -max_recursion_level)
00406         throw (std::runtime_error("pseries::evalf(): recursion limit exceeded"));
00407     
00408     // Construct a new series with evaluated coefficients
00409     epvector new_seq;
00410     new_seq.reserve(seq.size());
00411     epvector::const_iterator it = seq.begin(), itend = seq.end();
00412     while (it != itend) {
00413         new_seq.push_back(expair(it->rest.evalf(level-1), it->coeff));
00414         ++it;
00415     }
00416     return (new pseries(relational(var,point), new_seq))->setflag(status_flags::dynallocated | status_flags::evaluated);
00417 }
00418 
00419 ex pseries::conjugate() const
00420 {
00421     if(!var.info(info_flags::real))
00422         return conjugate_function(*this).hold();
00423 
00424     epvector * newseq = conjugateepvector(seq);
00425     ex newpoint = point.conjugate();
00426 
00427     if (!newseq && are_ex_trivially_equal(point, newpoint)) {
00428         return *this;
00429     }
00430 
00431     ex result = (new pseries(var==newpoint, newseq ? *newseq : seq))->setflag(status_flags::dynallocated);
00432     delete newseq;
00433     return result;
00434 }
00435 
00436 ex pseries::real_part() const
00437 {
00438     if(!var.info(info_flags::real))
00439         return real_part_function(*this).hold();
00440     ex newpoint = point.real_part();
00441     if(newpoint != point)
00442         return real_part_function(*this).hold();
00443 
00444     epvector v;
00445     v.reserve(seq.size());
00446     for(epvector::const_iterator i=seq.begin(); i!=seq.end(); ++i)
00447         v.push_back(expair((i->rest).real_part(), i->coeff));
00448     return (new pseries(var==point, v))->setflag(status_flags::dynallocated);
00449 }
00450 
00451 ex pseries::imag_part() const
00452 {
00453     if(!var.info(info_flags::real))
00454         return imag_part_function(*this).hold();
00455     ex newpoint = point.real_part();
00456     if(newpoint != point)
00457         return imag_part_function(*this).hold();
00458 
00459     epvector v;
00460     v.reserve(seq.size());
00461     for(epvector::const_iterator i=seq.begin(); i!=seq.end(); ++i)
00462         v.push_back(expair((i->rest).imag_part(), i->coeff));
00463     return (new pseries(var==point, v))->setflag(status_flags::dynallocated);
00464 }
00465 
00466 ex pseries::eval_integ() const
00467 {
00468     epvector *newseq = NULL;
00469     for (epvector::const_iterator i=seq.begin(); i!=seq.end(); ++i) {
00470         if (newseq) {
00471             newseq->push_back(expair(i->rest.eval_integ(), i->coeff));
00472             continue;
00473         }
00474         ex newterm = i->rest.eval_integ();
00475         if (!are_ex_trivially_equal(newterm, i->rest)) {
00476             newseq = new epvector;
00477             newseq->reserve(seq.size());
00478             for (epvector::const_iterator j=seq.begin(); j!=i; ++j)
00479                 newseq->push_back(*j);
00480             newseq->push_back(expair(newterm, i->coeff));
00481         }
00482     }
00483 
00484     ex newpoint = point.eval_integ();
00485     if (newseq || !are_ex_trivially_equal(newpoint, point))
00486         return (new pseries(var==newpoint, *newseq))
00487                ->setflag(status_flags::dynallocated);
00488     return *this;
00489 }
00490 
00491 ex pseries::evalm() const
00492 {
00493     // evalm each coefficient
00494     epvector newseq;
00495     bool something_changed = false;
00496     for (epvector::const_iterator i=seq.begin(); i!=seq.end(); ++i) {
00497         if (something_changed) {
00498             ex newcoeff = i->rest.evalm();
00499             if (!newcoeff.is_zero())
00500                 newseq.push_back(expair(newcoeff, i->coeff));
00501         }
00502         else {
00503             ex newcoeff = i->rest.evalm();
00504             if (!are_ex_trivially_equal(newcoeff, i->rest)) {
00505                 something_changed = true;
00506                 newseq.reserve(seq.size());
00507                 std::copy(seq.begin(), i, std::back_inserter<epvector>(newseq));
00508                 if (!newcoeff.is_zero())
00509                     newseq.push_back(expair(newcoeff, i->coeff));
00510             }
00511         }
00512     }
00513     if (something_changed)
00514         return (new pseries(var==point, newseq))->setflag(status_flags::dynallocated);
00515     else
00516         return *this;
00517 }
00518 
00519 ex pseries::subs(const exmap & m, unsigned options) const
00520 {
00521     // If expansion variable is being substituted, convert the series to a
00522     // polynomial and do the substitution there because the result might
00523     // no longer be a power series
00524     if (m.find(var) != m.end())
00525         return convert_to_poly(true).subs(m, options);
00526     
00527     // Otherwise construct a new series with substituted coefficients and
00528     // expansion point
00529     epvector newseq;
00530     newseq.reserve(seq.size());
00531     epvector::const_iterator it = seq.begin(), itend = seq.end();
00532     while (it != itend) {
00533         newseq.push_back(expair(it->rest.subs(m, options), it->coeff));
00534         ++it;
00535     }
00536     return (new pseries(relational(var,point.subs(m, options)), newseq))->setflag(status_flags::dynallocated);
00537 }
00538 
00541 ex pseries::expand(unsigned options) const
00542 {
00543     epvector newseq;
00544     epvector::const_iterator i = seq.begin(), end = seq.end();
00545     while (i != end) {
00546         ex restexp = i->rest.expand();
00547         if (!restexp.is_zero())
00548             newseq.push_back(expair(restexp, i->coeff));
00549         ++i;
00550     }
00551     return (new pseries(relational(var,point), newseq))
00552             ->setflag(status_flags::dynallocated | (options == 0 ? status_flags::expanded : 0));
00553 }
00554 
00557 ex pseries::derivative(const symbol & s) const
00558 {
00559     epvector new_seq;
00560     epvector::const_iterator it = seq.begin(), itend = seq.end();
00561 
00562     if (s == var) {
00563         
00564         // FIXME: coeff might depend on var
00565         while (it != itend) {
00566             if (is_order_function(it->rest)) {
00567                 new_seq.push_back(expair(it->rest, it->coeff - 1));
00568             } else {
00569                 ex c = it->rest * it->coeff;
00570                 if (!c.is_zero())
00571                     new_seq.push_back(expair(c, it->coeff - 1));
00572             }
00573             ++it;
00574         }
00575 
00576     } else {
00577 
00578         while (it != itend) {
00579             if (is_order_function(it->rest)) {
00580                 new_seq.push_back(*it);
00581             } else {
00582                 ex c = it->rest.diff(s);
00583                 if (!c.is_zero())
00584                     new_seq.push_back(expair(c, it->coeff));
00585             }
00586             ++it;
00587         }
00588     }
00589 
00590     return pseries(relational(var,point), new_seq);
00591 }
00592 
00593 ex pseries::convert_to_poly(bool no_order) const
00594 {
00595     ex e;
00596     epvector::const_iterator it = seq.begin(), itend = seq.end();
00597     
00598     while (it != itend) {
00599         if (is_order_function(it->rest)) {
00600             if (!no_order)
00601                 e += Order(power(var - point, it->coeff));
00602         } else
00603             e += it->rest * power(var - point, it->coeff);
00604         ++it;
00605     }
00606     return e;
00607 }
00608 
00609 bool pseries::is_terminating() const
00610 {
00611     return seq.empty() || !is_order_function((seq.end()-1)->rest);
00612 }
00613 
00614 ex pseries::coeffop(size_t i) const
00615 {
00616     if (i >=nops())
00617         throw (std::out_of_range("coeffop() out of range"));
00618     return seq[i].rest;
00619 }
00620 
00621 ex pseries::exponop(size_t i) const
00622 {
00623     if (i >= nops())
00624         throw (std::out_of_range("exponop() out of range"));
00625     return seq[i].coeff;
00626 }
00627 
00628 
00629 /*
00630  *  Implementations of series expansion
00631  */
00632 
00635 ex basic::series(const relational & r, int order, unsigned options) const
00636 {
00637     epvector seq;
00638     const symbol &s = ex_to<symbol>(r.lhs());
00639 
00640     // default for order-values that make no sense for Taylor expansion
00641     if ((order <= 0) && this->has(s)) {
00642         seq.push_back(expair(Order(_ex1), order));
00643         return pseries(r, seq);
00644     }
00645 
00646     // do Taylor expansion
00647     numeric fac = 1;
00648     ex deriv = *this;
00649     ex coeff = deriv.subs(r, subs_options::no_pattern);
00650 
00651     if (!coeff.is_zero()) {
00652         seq.push_back(expair(coeff, _ex0));
00653     }
00654 
00655     int n;
00656     for (n=1; n<order; ++n) {
00657         fac = fac.mul(n);
00658         // We need to test for zero in order to see if the series terminates.
00659         // The problem is that there is no such thing as a perfect test for
00660         // zero.  Expanding the term occasionally helps a little...
00661         deriv = deriv.diff(s).expand();
00662         if (deriv.is_zero())  // Series terminates
00663             return pseries(r, seq);
00664 
00665         coeff = deriv.subs(r, subs_options::no_pattern);
00666         if (!coeff.is_zero())
00667             seq.push_back(expair(fac.inverse() * coeff, n));
00668     }
00669     
00670     // Higher-order terms, if present
00671     deriv = deriv.diff(s);
00672     if (!deriv.expand().is_zero())
00673         seq.push_back(expair(Order(_ex1), n));
00674     return pseries(r, seq);
00675 }
00676 
00677 
00680 ex symbol::series(const relational & r, int order, unsigned options) const
00681 {
00682     epvector seq;
00683     const ex point = r.rhs();
00684     GINAC_ASSERT(is_a<symbol>(r.lhs()));
00685 
00686     if (this->is_equal_same_type(ex_to<symbol>(r.lhs()))) {
00687         if (order > 0 && !point.is_zero())
00688             seq.push_back(expair(point, _ex0));
00689         if (order > 1)
00690             seq.push_back(expair(_ex1, _ex1));
00691         else
00692             seq.push_back(expair(Order(_ex1), numeric(order)));
00693     } else
00694         seq.push_back(expair(*this, _ex0));
00695     return pseries(r, seq);
00696 }
00697 
00698 
00704 ex pseries::add_series(const pseries &other) const
00705 {
00706     // Adding two series with different variables or expansion points
00707     // results in an empty (constant) series 
00708     if (!is_compatible_to(other)) {
00709         epvector nul;
00710         nul.push_back(expair(Order(_ex1), _ex0));
00711         return pseries(relational(var,point), nul);
00712     }
00713     
00714     // Series addition
00715     epvector new_seq;
00716     epvector::const_iterator a = seq.begin();
00717     epvector::const_iterator b = other.seq.begin();
00718     epvector::const_iterator a_end = seq.end();
00719     epvector::const_iterator b_end = other.seq.end();
00720     int pow_a = std::numeric_limits<int>::max(), pow_b = std::numeric_limits<int>::max();
00721     for (;;) {
00722         // If a is empty, fill up with elements from b and stop
00723         if (a == a_end) {
00724             while (b != b_end) {
00725                 new_seq.push_back(*b);
00726                 ++b;
00727             }
00728             break;
00729         } else
00730             pow_a = ex_to<numeric>((*a).coeff).to_int();
00731         
00732         // If b is empty, fill up with elements from a and stop
00733         if (b == b_end) {
00734             while (a != a_end) {
00735                 new_seq.push_back(*a);
00736                 ++a;
00737             }
00738             break;
00739         } else
00740             pow_b = ex_to<numeric>((*b).coeff).to_int();
00741         
00742         // a and b are non-empty, compare powers
00743         if (pow_a < pow_b) {
00744             // a has lesser power, get coefficient from a
00745             new_seq.push_back(*a);
00746             if (is_order_function((*a).rest))
00747                 break;
00748             ++a;
00749         } else if (pow_b < pow_a) {
00750             // b has lesser power, get coefficient from b
00751             new_seq.push_back(*b);
00752             if (is_order_function((*b).rest))
00753                 break;
00754             ++b;
00755         } else {
00756             // Add coefficient of a and b
00757             if (is_order_function((*a).rest) || is_order_function((*b).rest)) {
00758                 new_seq.push_back(expair(Order(_ex1), (*a).coeff));
00759                 break;  // Order term ends the sequence
00760             } else {
00761                 ex sum = (*a).rest + (*b).rest;
00762                 if (!(sum.is_zero()))
00763                     new_seq.push_back(expair(sum, numeric(pow_a)));
00764                 ++a;
00765                 ++b;
00766             }
00767         }
00768     }
00769     return pseries(relational(var,point), new_seq);
00770 }
00771 
00772 
00776 ex add::series(const relational & r, int order, unsigned options) const
00777 {
00778     ex acc; // Series accumulator
00779     
00780     // Get first term from overall_coeff
00781     acc = overall_coeff.series(r, order, options);
00782     
00783     // Add remaining terms
00784     epvector::const_iterator it = seq.begin();
00785     epvector::const_iterator itend = seq.end();
00786     for (; it!=itend; ++it) {
00787         ex op;
00788         if (is_exactly_a<pseries>(it->rest))
00789             op = it->rest;
00790         else
00791             op = it->rest.series(r, order, options);
00792         if (!it->coeff.is_equal(_ex1))
00793             op = ex_to<pseries>(op).mul_const(ex_to<numeric>(it->coeff));
00794         
00795         // Series addition
00796         acc = ex_to<pseries>(acc).add_series(ex_to<pseries>(op));
00797     }
00798     return acc;
00799 }
00800 
00801 
00807 ex pseries::mul_const(const numeric &other) const
00808 {
00809     epvector new_seq;
00810     new_seq.reserve(seq.size());
00811     
00812     epvector::const_iterator it = seq.begin(), itend = seq.end();
00813     while (it != itend) {
00814         if (!is_order_function(it->rest))
00815             new_seq.push_back(expair(it->rest * other, it->coeff));
00816         else
00817             new_seq.push_back(*it);
00818         ++it;
00819     }
00820     return pseries(relational(var,point), new_seq);
00821 }
00822 
00823 
00829 ex pseries::mul_series(const pseries &other) const
00830 {
00831     // Multiplying two series with different variables or expansion points
00832     // results in an empty (constant) series 
00833     if (!is_compatible_to(other)) {
00834         epvector nul;
00835         nul.push_back(expair(Order(_ex1), _ex0));
00836         return pseries(relational(var,point), nul);
00837     }
00838 
00839     if (seq.empty() || other.seq.empty()) {
00840         return (new pseries(var==point, epvector()))
00841                ->setflag(status_flags::dynallocated);
00842     }
00843     
00844     // Series multiplication
00845     epvector new_seq;
00846     int a_max = degree(var);
00847     int b_max = other.degree(var);
00848     int a_min = ldegree(var);
00849     int b_min = other.ldegree(var);
00850     int cdeg_min = a_min + b_min;
00851     int cdeg_max = a_max + b_max;
00852     
00853     int higher_order_a = std::numeric_limits<int>::max();
00854     int higher_order_b = std::numeric_limits<int>::max();
00855     if (is_order_function(coeff(var, a_max)))
00856         higher_order_a = a_max + b_min;
00857     if (is_order_function(other.coeff(var, b_max)))
00858         higher_order_b = b_max + a_min;
00859     int higher_order_c = std::min(higher_order_a, higher_order_b);
00860     if (cdeg_max >= higher_order_c)
00861         cdeg_max = higher_order_c - 1;
00862     
00863     for (int cdeg=cdeg_min; cdeg<=cdeg_max; ++cdeg) {
00864         ex co = _ex0;
00865         // c(i)=a(0)b(i)+...+a(i)b(0)
00866         for (int i=a_min; cdeg-i>=b_min; ++i) {
00867             ex a_coeff = coeff(var, i);
00868             ex b_coeff = other.coeff(var, cdeg-i);
00869             if (!is_order_function(a_coeff) && !is_order_function(b_coeff))
00870                 co += a_coeff * b_coeff;
00871         }
00872         if (!co.is_zero())
00873             new_seq.push_back(expair(co, numeric(cdeg)));
00874     }
00875     if (higher_order_c < std::numeric_limits<int>::max())
00876         new_seq.push_back(expair(Order(_ex1), numeric(higher_order_c)));
00877     return pseries(relational(var, point), new_seq);
00878 }
00879 
00880 
00884 ex mul::series(const relational & r, int order, unsigned options) const
00885 {
00886     pseries acc; // Series accumulator
00887 
00888     GINAC_ASSERT(is_a<symbol>(r.lhs()));
00889     const ex& sym = r.lhs();
00890         
00891     // holds ldegrees of the series of individual factors
00892     std::vector<int> ldegrees;
00893     std::vector<bool> ldegree_redo;
00894 
00895     // find minimal degrees
00896     const epvector::const_iterator itbeg = seq.begin();
00897     const epvector::const_iterator itend = seq.end();
00898     // first round: obtain a bound up to which minimal degrees have to be
00899     // considered
00900     for (epvector::const_iterator it=itbeg; it!=itend; ++it) {
00901 
00902         ex expon = it->coeff;
00903         int factor = 1;
00904         ex buf;
00905         if (expon.info(info_flags::integer)) {
00906             buf = it->rest;
00907             factor = ex_to<numeric>(expon).to_int();
00908         } else {
00909             buf = recombine_pair_to_ex(*it);
00910         }
00911 
00912         int real_ldegree = 0;
00913         bool flag_redo = false;
00914         try {
00915             real_ldegree = buf.expand().ldegree(sym-r.rhs());
00916         } catch (std::runtime_error) {}
00917 
00918         if (real_ldegree == 0) {
00919             if ( factor < 0 ) {
00920                 // This case must terminate, otherwise we would have division by
00921                 // zero.
00922                 int orderloop = 0;
00923                 do {
00924                     orderloop++;
00925                     real_ldegree = buf.series(r, orderloop, options).ldegree(sym);
00926                 } while (real_ldegree == orderloop);
00927             } else {
00928                 // Here it is possible that buf does not have a ldegree, therefore
00929                 // check only if ldegree is negative, otherwise reconsider the case
00930                 // in the second round.
00931                 real_ldegree = buf.series(r, 0, options).ldegree(sym);
00932                 if (real_ldegree == 0)
00933                     flag_redo = true;
00934             }
00935         }
00936 
00937         ldegrees.push_back(factor * real_ldegree);
00938         ldegree_redo.push_back(flag_redo);
00939     }
00940 
00941     int degbound = order-std::accumulate(ldegrees.begin(), ldegrees.end(), 0);
00942     // Second round: determine the remaining positive ldegrees by the series
00943     // method.
00944     // here we can ignore ldegrees larger than degbound
00945     size_t j = 0;
00946     for (epvector::const_iterator it=itbeg; it!=itend; ++it) {
00947         if ( ldegree_redo[j] ) {
00948             ex expon = it->coeff;
00949             int factor = 1;
00950             ex buf;
00951             if (expon.info(info_flags::integer)) {
00952                 buf = it->rest;
00953                 factor = ex_to<numeric>(expon).to_int();
00954             } else {
00955                 buf = recombine_pair_to_ex(*it);
00956             }
00957             int real_ldegree = 0;
00958             int orderloop = 0;
00959             do {
00960                 orderloop++;
00961                 real_ldegree = buf.series(r, orderloop, options).ldegree(sym);
00962             } while ((real_ldegree == orderloop)
00963                     && ( factor*real_ldegree < degbound));
00964             ldegrees[j] = factor * real_ldegree;
00965             degbound -= factor * real_ldegree;
00966         }
00967         j++;
00968     }
00969 
00970     int degsum = std::accumulate(ldegrees.begin(), ldegrees.end(), 0);
00971 
00972     if (degsum >= order) {
00973         epvector epv;
00974         epv.push_back(expair(Order(_ex1), order));
00975         return (new pseries(r, epv))->setflag(status_flags::dynallocated);
00976     }
00977 
00978     // Multiply with remaining terms
00979     std::vector<int>::const_iterator itd = ldegrees.begin();
00980     for (epvector::const_iterator it=itbeg; it!=itend; ++it, ++itd) {
00981 
00982         // do series expansion with adjusted order
00983         ex op = recombine_pair_to_ex(*it).series(r, order-degsum+(*itd), options);
00984 
00985         // Series multiplication
00986         if (it == itbeg)
00987             acc = ex_to<pseries>(op);
00988         else
00989             acc = ex_to<pseries>(acc.mul_series(ex_to<pseries>(op)));
00990     }
00991 
00992     return acc.mul_const(ex_to<numeric>(overall_coeff));
00993 }
00994 
00995 
01000 ex pseries::power_const(const numeric &p, int deg) const
01001 {
01002     // method:
01003     // (due to Leonhard Euler)
01004     // let A(x) be this series and for the time being let it start with a
01005     // constant (later we'll generalize):
01006     //     A(x) = a_0 + a_1*x + a_2*x^2 + ...
01007     // We want to compute
01008     //     C(x) = A(x)^p
01009     //     C(x) = c_0 + c_1*x + c_2*x^2 + ...
01010     // Taking the derivative on both sides and multiplying with A(x) one
01011     // immediately arrives at
01012     //     C'(x)*A(x) = p*C(x)*A'(x)
01013     // Multiplying this out and comparing coefficients we get the recurrence
01014     // formula
01015     //     c_i = (i*p*a_i*c_0 + ((i-1)*p-1)*a_{i-1}*c_1 + ...
01016     //                    ... + (p-(i-1))*a_1*c_{i-1})/(a_0*i)
01017     // which can easily be solved given the starting value c_0 = (a_0)^p.
01018     // For the more general case where the leading coefficient of A(x) is not
01019     // a constant, just consider A2(x) = A(x)*x^m, with some integer m and
01020     // repeat the above derivation.  The leading power of C2(x) = A2(x)^2 is
01021     // then of course x^(p*m) but the recurrence formula still holds.
01022     
01023     if (seq.empty()) {
01024         // as a special case, handle the empty (zero) series honoring the
01025         // usual power laws such as implemented in power::eval()
01026         if (p.real().is_zero())
01027             throw std::domain_error("pseries::power_const(): pow(0,I) is undefined");
01028         else if (p.real().is_negative())
01029             throw pole_error("pseries::power_const(): division by zero",1);
01030         else
01031             return *this;
01032     }
01033     
01034     const int ldeg = ldegree(var);
01035     if (!(p*ldeg).is_integer())
01036         throw std::runtime_error("pseries::power_const(): trying to assemble a Puiseux series");
01037 
01038     // adjust number of coefficients
01039     int numcoeff = deg - (p*ldeg).to_int();
01040     if (numcoeff <= 0) {
01041         epvector epv;
01042         epv.reserve(1);
01043         epv.push_back(expair(Order(_ex1), deg));
01044         return (new pseries(relational(var,point), epv))
01045                ->setflag(status_flags::dynallocated);
01046     }
01047     
01048     // O(x^n)^(-m) is undefined
01049     if (seq.size() == 1 && is_order_function(seq[0].rest) && p.real().is_negative())
01050         throw pole_error("pseries::power_const(): division by zero",1);
01051     
01052     // Compute coefficients of the powered series
01053     exvector co;
01054     co.reserve(numcoeff);
01055     co.push_back(power(coeff(var, ldeg), p));
01056     for (int i=1; i<numcoeff; ++i) {
01057         ex sum = _ex0;
01058         for (int j=1; j<=i; ++j) {
01059             ex c = coeff(var, j + ldeg);
01060             if (is_order_function(c)) {
01061                 co.push_back(Order(_ex1));
01062                 break;
01063             } else
01064                 sum += (p * j - (i - j)) * co[i - j] * c;
01065         }
01066         co.push_back(sum / coeff(var, ldeg) / i);
01067     }
01068     
01069     // Construct new series (of non-zero coefficients)
01070     epvector new_seq;
01071     bool higher_order = false;
01072     for (int i=0; i<numcoeff; ++i) {
01073         if (!co[i].is_zero())
01074             new_seq.push_back(expair(co[i], p * ldeg + i));
01075         if (is_order_function(co[i])) {
01076             higher_order = true;
01077             break;
01078         }
01079     }
01080     if (!higher_order)
01081         new_seq.push_back(expair(Order(_ex1), p * ldeg + numcoeff));
01082 
01083     return pseries(relational(var,point), new_seq);
01084 }
01085 
01086 
01088 pseries pseries::shift_exponents(int deg) const
01089 {
01090     epvector newseq = seq;
01091     epvector::iterator i = newseq.begin(), end  = newseq.end();
01092     while (i != end) {
01093         i->coeff += deg;
01094         ++i;
01095     }
01096     return pseries(relational(var, point), newseq);
01097 }
01098 
01099 
01103 ex power::series(const relational & r, int order, unsigned options) const
01104 {
01105     // If basis is already a series, just power it
01106     if (is_exactly_a<pseries>(basis))
01107         return ex_to<pseries>(basis).power_const(ex_to<numeric>(exponent), order);
01108 
01109     // Basis is not a series, may there be a singularity?
01110     bool must_expand_basis = false;
01111     try {
01112         basis.subs(r, subs_options::no_pattern);
01113     } catch (pole_error) {
01114         must_expand_basis = true;
01115     }
01116 
01117     bool exponent_is_regular = true;
01118     try {
01119         exponent.subs(r, subs_options::no_pattern);
01120     } catch (pole_error) {
01121         exponent_is_regular = false;
01122     }
01123 
01124     if (!exponent_is_regular) {
01125         ex l = exponent*log(basis);
01126         // this == exp(l);
01127         ex le = l.series(r, order, options);
01128         // Note: expanding exp(l) won't help, since that will attempt
01129         // Taylor expansion, and fail (because exponent is "singular")
01130         // Still l itself might be expanded in Taylor series.
01131         // Examples:
01132         // sin(x)/x*log(cos(x))
01133         // 1/x*log(1 + x)
01134         return exp(le).series(r, order, options);
01135         // Note: if l happens to have a Laurent expansion (with
01136         // negative powers of (var - point)), expanding exp(le)
01137         // will barf (which is The Right Thing).
01138     }
01139 
01140     // Is the expression of type something^(-int)?
01141     if (!must_expand_basis && !exponent.info(info_flags::negint)
01142      && (!is_a<add>(basis) || !is_a<numeric>(exponent)))
01143         return basic::series(r, order, options);
01144 
01145     // Is the expression of type 0^something?
01146     if (!must_expand_basis && !basis.subs(r, subs_options::no_pattern).is_zero()
01147      && (!is_a<add>(basis) || !is_a<numeric>(exponent)))
01148         return basic::series(r, order, options);
01149 
01150     // Singularity encountered, is the basis equal to (var - point)?
01151     if (basis.is_equal(r.lhs() - r.rhs())) {
01152         epvector new_seq;
01153         if (ex_to<numeric>(exponent).to_int() < order)
01154             new_seq.push_back(expair(_ex1, exponent));
01155         else
01156             new_seq.push_back(expair(Order(_ex1), exponent));
01157         return pseries(r, new_seq);
01158     }
01159 
01160     // No, expand basis into series
01161 
01162     numeric numexp;
01163     if (is_a<numeric>(exponent)) {
01164         numexp = ex_to<numeric>(exponent);
01165     } else {
01166         numexp = 0;
01167     }
01168     const ex& sym = r.lhs();
01169     // find existing minimal degree
01170     ex eb = basis.expand();
01171     int real_ldegree = 0;
01172     if (eb.info(info_flags::rational_function))
01173         real_ldegree = eb.ldegree(sym-r.rhs());
01174     if (real_ldegree == 0) {
01175         int orderloop = 0;
01176         do {
01177             orderloop++;
01178             real_ldegree = basis.series(r, orderloop, options).ldegree(sym);
01179         } while (real_ldegree == orderloop);
01180     }
01181 
01182     if (!(real_ldegree*numexp).is_integer())
01183         throw std::runtime_error("pseries::power_const(): trying to assemble a Puiseux series");
01184     ex e = basis.series(r, (order + real_ldegree*(1-numexp)).to_int(), options);
01185     
01186     ex result;
01187     try {
01188         result = ex_to<pseries>(e).power_const(numexp, order);
01189     } catch (pole_error) {
01190         epvector ser;
01191         ser.push_back(expair(Order(_ex1), order));
01192         result = pseries(r, ser);
01193     }
01194 
01195     return result;
01196 }
01197 
01198 
01200 ex pseries::series(const relational & r, int order, unsigned options) const
01201 {
01202     const ex p = r.rhs();
01203     GINAC_ASSERT(is_a<symbol>(r.lhs()));
01204     const symbol &s = ex_to<symbol>(r.lhs());
01205     
01206     if (var.is_equal(s) && point.is_equal(p)) {
01207         if (order > degree(s))
01208             return *this;
01209         else {
01210             epvector new_seq;
01211             epvector::const_iterator it = seq.begin(), itend = seq.end();
01212             while (it != itend) {
01213                 int o = ex_to<numeric>(it->coeff).to_int();
01214                 if (o >= order) {
01215                     new_seq.push_back(expair(Order(_ex1), o));
01216                     break;
01217                 }
01218                 new_seq.push_back(*it);
01219                 ++it;
01220             }
01221             return pseries(r, new_seq);
01222         }
01223     } else
01224         return convert_to_poly().series(r, order, options);
01225 }
01226 
01227 ex integral::series(const relational & r, int order, unsigned options) const
01228 {
01229     if (x.subs(r) != x)
01230         throw std::logic_error("Cannot series expand wrt dummy variable");
01231     
01232     // Expanding integrant with r substituted taken in boundaries.
01233     ex fseries = f.series(r, order, options);
01234     epvector fexpansion;
01235     fexpansion.reserve(fseries.nops());
01236     for (size_t i=0; i<fseries.nops(); ++i) {
01237         ex currcoeff = ex_to<pseries>(fseries).coeffop(i);
01238         currcoeff = (currcoeff == Order(_ex1))
01239             ? currcoeff
01240             : integral(x, a.subs(r), b.subs(r), currcoeff);
01241         if (currcoeff != 0)
01242             fexpansion.push_back(
01243                 expair(currcoeff, ex_to<pseries>(fseries).exponop(i)));
01244     }
01245 
01246     // Expanding lower boundary
01247     ex result = (new pseries(r, fexpansion))->setflag(status_flags::dynallocated);
01248     ex aseries = (a-a.subs(r)).series(r, order, options);
01249     fseries = f.series(x == (a.subs(r)), order, options);
01250     for (size_t i=0; i<fseries.nops(); ++i) {
01251         ex currcoeff = ex_to<pseries>(fseries).coeffop(i);
01252         if (is_order_function(currcoeff))
01253             break;
01254         ex currexpon = ex_to<pseries>(fseries).exponop(i);
01255         int orderforf = order-ex_to<numeric>(currexpon).to_int()-1;
01256         currcoeff = currcoeff.series(r, orderforf);
01257         ex term = ex_to<pseries>(aseries).power_const(ex_to<numeric>(currexpon+1),order);
01258         term = ex_to<pseries>(term).mul_const(ex_to<numeric>(-1/(currexpon+1)));
01259         term = ex_to<pseries>(term).mul_series(ex_to<pseries>(currcoeff));
01260         result = ex_to<pseries>(result).add_series(ex_to<pseries>(term));
01261     }
01262 
01263     // Expanding upper boundary
01264     ex bseries = (b-b.subs(r)).series(r, order, options);
01265     fseries = f.series(x == (b.subs(r)), order, options);
01266     for (size_t i=0; i<fseries.nops(); ++i) {
01267         ex currcoeff = ex_to<pseries>(fseries).coeffop(i);
01268         if (is_order_function(currcoeff))
01269             break;
01270         ex currexpon = ex_to<pseries>(fseries).exponop(i);
01271         int orderforf = order-ex_to<numeric>(currexpon).to_int()-1;
01272         currcoeff = currcoeff.series(r, orderforf);
01273         ex term = ex_to<pseries>(bseries).power_const(ex_to<numeric>(currexpon+1),order);
01274         term = ex_to<pseries>(term).mul_const(ex_to<numeric>(1/(currexpon+1)));
01275         term = ex_to<pseries>(term).mul_series(ex_to<pseries>(currcoeff));
01276         result = ex_to<pseries>(result).add_series(ex_to<pseries>(term));
01277     }
01278 
01279     return result;
01280 }
01281 
01282 
01292 ex ex::series(const ex & r, int order, unsigned options) const
01293 {
01294     ex e;
01295     relational rel_;
01296     
01297     if (is_a<relational>(r))
01298         rel_ = ex_to<relational>(r);
01299     else if (is_a<symbol>(r))
01300         rel_ = relational(r,_ex0);
01301     else
01302         throw (std::logic_error("ex::series(): expansion point has unknown type"));
01303     
01304     e = bp->series(rel_, order, options);
01305     return e;
01306 }
01307 
01308 GINAC_BIND_UNARCHIVER(pseries);
01309 
01310 } // namespace GiNaC

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