|
GiNaC
1.6.2
|
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 "inifcns.h" 00025 #include "constant.h" 00026 #include "pseries.h" 00027 #include "numeric.h" 00028 #include "power.h" 00029 #include "relational.h" 00030 #include "operators.h" 00031 #include "symbol.h" 00032 #include "symmetry.h" 00033 #include "utils.h" 00034 00035 #include <stdexcept> 00036 #include <vector> 00037 00038 namespace GiNaC { 00039 00041 // Logarithm of Gamma function 00043 00044 static ex lgamma_evalf(const ex & x) 00045 { 00046 if (is_exactly_a<numeric>(x)) { 00047 try { 00048 return lgamma(ex_to<numeric>(x)); 00049 } catch (const dunno &e) { } 00050 } 00051 00052 return lgamma(x).hold(); 00053 } 00054 00055 00060 static ex lgamma_eval(const ex & x) 00061 { 00062 if (x.info(info_flags::numeric)) { 00063 // trap integer arguments: 00064 if (x.info(info_flags::integer)) { 00065 // lgamma(n) -> log((n-1)!) for postitive n 00066 if (x.info(info_flags::posint)) 00067 return log(factorial(x + _ex_1)); 00068 else 00069 throw (pole_error("lgamma_eval(): logarithmic pole",0)); 00070 } 00071 if (!ex_to<numeric>(x).is_rational()) 00072 return lgamma(ex_to<numeric>(x)); 00073 } 00074 00075 return lgamma(x).hold(); 00076 } 00077 00078 00079 static ex lgamma_deriv(const ex & x, unsigned deriv_param) 00080 { 00081 GINAC_ASSERT(deriv_param==0); 00082 00083 // d/dx lgamma(x) -> psi(x) 00084 return psi(x); 00085 } 00086 00087 00088 static ex lgamma_series(const ex & arg, 00089 const relational & rel, 00090 int order, 00091 unsigned options) 00092 { 00093 // method: 00094 // Taylor series where there is no pole falls back to psi function 00095 // evaluation. 00096 // On a pole at -m we could use the recurrence relation 00097 // lgamma(x) == lgamma(x+1)-log(x) 00098 // from which follows 00099 // series(lgamma(x),x==-m,order) == 00100 // series(lgamma(x+m+1)-log(x)...-log(x+m)),x==-m,order); 00101 const ex arg_pt = arg.subs(rel, subs_options::no_pattern); 00102 if (!arg_pt.info(info_flags::integer) || arg_pt.info(info_flags::positive)) 00103 throw do_taylor(); // caught by function::series() 00104 // if we got here we have to care for a simple pole of tgamma(-m): 00105 numeric m = -ex_to<numeric>(arg_pt); 00106 ex recur; 00107 for (numeric p = 0; p<=m; ++p) 00108 recur += log(arg+p); 00109 return (lgamma(arg+m+_ex1)-recur).series(rel, order, options); 00110 } 00111 00112 00113 static ex lgamma_conjugate(const ex & x) 00114 { 00115 // conjugate(lgamma(x))==lgamma(conjugate(x)) unless on the branch cut 00116 // which runs along the negative real axis. 00117 if (x.info(info_flags::positive)) { 00118 return lgamma(x); 00119 } 00120 if (is_exactly_a<numeric>(x) && 00121 !x.imag_part().is_zero()) { 00122 return lgamma(x.conjugate()); 00123 } 00124 return conjugate_function(lgamma(x)).hold(); 00125 } 00126 00127 00128 REGISTER_FUNCTION(lgamma, eval_func(lgamma_eval). 00129 evalf_func(lgamma_evalf). 00130 derivative_func(lgamma_deriv). 00131 series_func(lgamma_series). 00132 conjugate_func(lgamma_conjugate). 00133 latex_name("\\log \\Gamma")); 00134 00135 00137 // true Gamma function 00139 00140 static ex tgamma_evalf(const ex & x) 00141 { 00142 if (is_exactly_a<numeric>(x)) { 00143 try { 00144 return tgamma(ex_to<numeric>(x)); 00145 } catch (const dunno &e) { } 00146 } 00147 00148 return tgamma(x).hold(); 00149 } 00150 00151 00157 static ex tgamma_eval(const ex & x) 00158 { 00159 if (x.info(info_flags::numeric)) { 00160 // trap integer arguments: 00161 const numeric two_x = (*_num2_p)*ex_to<numeric>(x); 00162 if (two_x.is_even()) { 00163 // tgamma(n) -> (n-1)! for postitive n 00164 if (two_x.is_positive()) { 00165 return factorial(ex_to<numeric>(x).sub(*_num1_p)); 00166 } else { 00167 throw (pole_error("tgamma_eval(): simple pole",1)); 00168 } 00169 } 00170 // trap half integer arguments: 00171 if (two_x.is_integer()) { 00172 // trap positive x==(n+1/2) 00173 // tgamma(n+1/2) -> Pi^(1/2)*(1*3*..*(2*n-1))/(2^n) 00174 if (two_x.is_positive()) { 00175 const numeric n = ex_to<numeric>(x).sub(*_num1_2_p); 00176 return (doublefactorial(n.mul(*_num2_p).sub(*_num1_p)).div(pow(*_num2_p,n))) * sqrt(Pi); 00177 } else { 00178 // trap negative x==(-n+1/2) 00179 // tgamma(-n+1/2) -> Pi^(1/2)*(-2)^n/(1*3*..*(2*n-1)) 00180 const numeric n = abs(ex_to<numeric>(x).sub(*_num1_2_p)); 00181 return (pow(*_num_2_p, n).div(doublefactorial(n.mul(*_num2_p).sub(*_num1_p))))*sqrt(Pi); 00182 } 00183 } 00184 if (!ex_to<numeric>(x).is_rational()) 00185 return tgamma(ex_to<numeric>(x)); 00186 } 00187 00188 return tgamma(x).hold(); 00189 } 00190 00191 00192 static ex tgamma_deriv(const ex & x, unsigned deriv_param) 00193 { 00194 GINAC_ASSERT(deriv_param==0); 00195 00196 // d/dx tgamma(x) -> psi(x)*tgamma(x) 00197 return psi(x)*tgamma(x); 00198 } 00199 00200 00201 static ex tgamma_series(const ex & arg, 00202 const relational & rel, 00203 int order, 00204 unsigned options) 00205 { 00206 // method: 00207 // Taylor series where there is no pole falls back to psi function 00208 // evaluation. 00209 // On a pole at -m use the recurrence relation 00210 // tgamma(x) == tgamma(x+1) / x 00211 // from which follows 00212 // series(tgamma(x),x==-m,order) == 00213 // series(tgamma(x+m+1)/(x*(x+1)*...*(x+m)),x==-m,order); 00214 const ex arg_pt = arg.subs(rel, subs_options::no_pattern); 00215 if (!arg_pt.info(info_flags::integer) || arg_pt.info(info_flags::positive)) 00216 throw do_taylor(); // caught by function::series() 00217 // if we got here we have to care for a simple pole at -m: 00218 const numeric m = -ex_to<numeric>(arg_pt); 00219 ex ser_denom = _ex1; 00220 for (numeric p; p<=m; ++p) 00221 ser_denom *= arg+p; 00222 return (tgamma(arg+m+_ex1)/ser_denom).series(rel, order, options); 00223 } 00224 00225 00226 static ex tgamma_conjugate(const ex & x) 00227 { 00228 // conjugate(tgamma(x))==tgamma(conjugate(x)) 00229 return tgamma(x.conjugate()); 00230 } 00231 00232 00233 REGISTER_FUNCTION(tgamma, eval_func(tgamma_eval). 00234 evalf_func(tgamma_evalf). 00235 derivative_func(tgamma_deriv). 00236 series_func(tgamma_series). 00237 conjugate_func(tgamma_conjugate). 00238 latex_name("\\Gamma")); 00239 00240 00242 // beta-function 00244 00245 static ex beta_evalf(const ex & x, const ex & y) 00246 { 00247 if (is_exactly_a<numeric>(x) && is_exactly_a<numeric>(y)) { 00248 try { 00249 return exp(lgamma(ex_to<numeric>(x))+lgamma(ex_to<numeric>(y))-lgamma(ex_to<numeric>(x+y))); 00250 } catch (const dunno &e) { } 00251 } 00252 00253 return beta(x,y).hold(); 00254 } 00255 00256 00257 static ex beta_eval(const ex & x, const ex & y) 00258 { 00259 if (x.is_equal(_ex1)) 00260 return 1/y; 00261 if (y.is_equal(_ex1)) 00262 return 1/x; 00263 if (x.info(info_flags::numeric) && y.info(info_flags::numeric)) { 00264 // treat all problematic x and y that may not be passed into tgamma, 00265 // because they would throw there although beta(x,y) is well-defined 00266 // using the formula beta(x,y) == (-1)^y * beta(1-x-y, y) 00267 const numeric &nx = ex_to<numeric>(x); 00268 const numeric &ny = ex_to<numeric>(y); 00269 if (nx.is_real() && nx.is_integer() && 00270 ny.is_real() && ny.is_integer()) { 00271 if (nx.is_negative()) { 00272 if (nx<=-ny) 00273 return pow(*_num_1_p, ny)*beta(1-x-y, y); 00274 else 00275 throw (pole_error("beta_eval(): simple pole",1)); 00276 } 00277 if (ny.is_negative()) { 00278 if (ny<=-nx) 00279 return pow(*_num_1_p, nx)*beta(1-y-x, x); 00280 else 00281 throw (pole_error("beta_eval(): simple pole",1)); 00282 } 00283 return tgamma(x)*tgamma(y)/tgamma(x+y); 00284 } 00285 // no problem in numerator, but denominator has pole: 00286 if ((nx+ny).is_real() && 00287 (nx+ny).is_integer() && 00288 !(nx+ny).is_positive()) 00289 return _ex0; 00290 if (!ex_to<numeric>(x).is_rational() || !ex_to<numeric>(x).is_rational()) 00291 return evalf(beta(x, y).hold()); 00292 } 00293 00294 return beta(x,y).hold(); 00295 } 00296 00297 00298 static ex beta_deriv(const ex & x, const ex & y, unsigned deriv_param) 00299 { 00300 GINAC_ASSERT(deriv_param<2); 00301 ex retval; 00302 00303 // d/dx beta(x,y) -> (psi(x)-psi(x+y)) * beta(x,y) 00304 if (deriv_param==0) 00305 retval = (psi(x)-psi(x+y))*beta(x,y); 00306 // d/dy beta(x,y) -> (psi(y)-psi(x+y)) * beta(x,y) 00307 if (deriv_param==1) 00308 retval = (psi(y)-psi(x+y))*beta(x,y); 00309 return retval; 00310 } 00311 00312 00313 static ex beta_series(const ex & arg1, 00314 const ex & arg2, 00315 const relational & rel, 00316 int order, 00317 unsigned options) 00318 { 00319 // method: 00320 // Taylor series where there is no pole of one of the tgamma functions 00321 // falls back to beta function evaluation. Otherwise, fall back to 00322 // tgamma series directly. 00323 const ex arg1_pt = arg1.subs(rel, subs_options::no_pattern); 00324 const ex arg2_pt = arg2.subs(rel, subs_options::no_pattern); 00325 GINAC_ASSERT(is_a<symbol>(rel.lhs())); 00326 const symbol &s = ex_to<symbol>(rel.lhs()); 00327 ex arg1_ser, arg2_ser, arg1arg2_ser; 00328 if ((!arg1_pt.info(info_flags::integer) || arg1_pt.info(info_flags::positive)) && 00329 (!arg2_pt.info(info_flags::integer) || arg2_pt.info(info_flags::positive))) 00330 throw do_taylor(); // caught by function::series() 00331 // trap the case where arg1 is on a pole: 00332 if (arg1.info(info_flags::integer) && !arg1.info(info_flags::positive)) 00333 arg1_ser = tgamma(arg1+s); 00334 else 00335 arg1_ser = tgamma(arg1); 00336 // trap the case where arg2 is on a pole: 00337 if (arg2.info(info_flags::integer) && !arg2.info(info_flags::positive)) 00338 arg2_ser = tgamma(arg2+s); 00339 else 00340 arg2_ser = tgamma(arg2); 00341 // trap the case where arg1+arg2 is on a pole: 00342 if ((arg1+arg2).info(info_flags::integer) && !(arg1+arg2).info(info_flags::positive)) 00343 arg1arg2_ser = tgamma(arg2+arg1+s); 00344 else 00345 arg1arg2_ser = tgamma(arg2+arg1); 00346 // compose the result (expanding all the terms): 00347 return (arg1_ser*arg2_ser/arg1arg2_ser).series(rel, order, options).expand(); 00348 } 00349 00350 00351 REGISTER_FUNCTION(beta, eval_func(beta_eval). 00352 evalf_func(beta_evalf). 00353 derivative_func(beta_deriv). 00354 series_func(beta_series). 00355 latex_name("\\mathrm{B}"). 00356 set_symmetry(sy_symm(0, 1))); 00357 00358 00360 // Psi-function (aka digamma-function) 00362 00363 static ex psi1_evalf(const ex & x) 00364 { 00365 if (is_exactly_a<numeric>(x)) { 00366 try { 00367 return psi(ex_to<numeric>(x)); 00368 } catch (const dunno &e) { } 00369 } 00370 00371 return psi(x).hold(); 00372 } 00373 00376 static ex psi1_eval(const ex & x) 00377 { 00378 if (x.info(info_flags::numeric)) { 00379 const numeric &nx = ex_to<numeric>(x); 00380 if (nx.is_integer()) { 00381 // integer case 00382 if (nx.is_positive()) { 00383 // psi(n) -> 1 + 1/2 +...+ 1/(n-1) - Euler 00384 numeric rat = 0; 00385 for (numeric i(nx+(*_num_1_p)); i>0; --i) 00386 rat += i.inverse(); 00387 return rat-Euler; 00388 } else { 00389 // for non-positive integers there is a pole: 00390 throw (pole_error("psi_eval(): simple pole",1)); 00391 } 00392 } 00393 if (((*_num2_p)*nx).is_integer()) { 00394 // half integer case 00395 if (nx.is_positive()) { 00396 // psi((2m+1)/2) -> 2/(2m+1) + 2/2m +...+ 2/1 - Euler - 2log(2) 00397 numeric rat = 0; 00398 for (numeric i = (nx+(*_num_1_p))*(*_num2_p); i>0; i-=(*_num2_p)) 00399 rat += (*_num2_p)*i.inverse(); 00400 return rat-Euler-_ex2*log(_ex2); 00401 } else { 00402 // use the recurrence relation 00403 // psi(-m-1/2) == psi(-m-1/2+1) - 1 / (-m-1/2) 00404 // to relate psi(-m-1/2) to psi(1/2): 00405 // psi(-m-1/2) == psi(1/2) + r 00406 // where r == ((-1/2)^(-1) + ... + (-m-1/2)^(-1)) 00407 numeric recur = 0; 00408 for (numeric p = nx; p<0; ++p) 00409 recur -= pow(p, *_num_1_p); 00410 return recur+psi(_ex1_2); 00411 } 00412 } 00413 // psi1_evalf should be called here once it becomes available 00414 } 00415 00416 return psi(x).hold(); 00417 } 00418 00419 static ex psi1_deriv(const ex & x, unsigned deriv_param) 00420 { 00421 GINAC_ASSERT(deriv_param==0); 00422 00423 // d/dx psi(x) -> psi(1,x) 00424 return psi(_ex1, x); 00425 } 00426 00427 static ex psi1_series(const ex & arg, 00428 const relational & rel, 00429 int order, 00430 unsigned options) 00431 { 00432 // method: 00433 // Taylor series where there is no pole falls back to polygamma function 00434 // evaluation. 00435 // On a pole at -m use the recurrence relation 00436 // psi(x) == psi(x+1) - 1/z 00437 // from which follows 00438 // series(psi(x),x==-m,order) == 00439 // series(psi(x+m+1) - 1/x - 1/(x+1) - 1/(x+m)),x==-m,order); 00440 const ex arg_pt = arg.subs(rel, subs_options::no_pattern); 00441 if (!arg_pt.info(info_flags::integer) || arg_pt.info(info_flags::positive)) 00442 throw do_taylor(); // caught by function::series() 00443 // if we got here we have to care for a simple pole at -m: 00444 const numeric m = -ex_to<numeric>(arg_pt); 00445 ex recur; 00446 for (numeric p; p<=m; ++p) 00447 recur += power(arg+p,_ex_1); 00448 return (psi(arg+m+_ex1)-recur).series(rel, order, options); 00449 } 00450 00451 unsigned psi1_SERIAL::serial = 00452 function::register_new(function_options("psi", 1). 00453 eval_func(psi1_eval). 00454 evalf_func(psi1_evalf). 00455 derivative_func(psi1_deriv). 00456 series_func(psi1_series). 00457 latex_name("\\psi"). 00458 overloaded(2)); 00459 00461 // Psi-functions (aka polygamma-functions) psi(0,x)==psi(x) 00463 00464 static ex psi2_evalf(const ex & n, const ex & x) 00465 { 00466 if (is_exactly_a<numeric>(n) && is_exactly_a<numeric>(x)) { 00467 try { 00468 return psi(ex_to<numeric>(n),ex_to<numeric>(x)); 00469 } catch (const dunno &e) { } 00470 } 00471 00472 return psi(n,x).hold(); 00473 } 00474 00477 static ex psi2_eval(const ex & n, const ex & x) 00478 { 00479 // psi(0,x) -> psi(x) 00480 if (n.is_zero()) 00481 return psi(x); 00482 // psi(-1,x) -> log(tgamma(x)) 00483 if (n.is_equal(_ex_1)) 00484 return log(tgamma(x)); 00485 if (n.info(info_flags::numeric) && n.info(info_flags::posint) && 00486 x.info(info_flags::numeric)) { 00487 const numeric &nn = ex_to<numeric>(n); 00488 const numeric &nx = ex_to<numeric>(x); 00489 if (nx.is_integer()) { 00490 // integer case 00491 if (nx.is_equal(*_num1_p)) 00492 // use psi(n,1) == (-)^(n+1) * n! * zeta(n+1) 00493 return pow(*_num_1_p,nn+(*_num1_p))*factorial(nn)*zeta(ex(nn+(*_num1_p))); 00494 if (nx.is_positive()) { 00495 // use the recurrence relation 00496 // psi(n,m) == psi(n,m+1) - (-)^n * n! / m^(n+1) 00497 // to relate psi(n,m) to psi(n,1): 00498 // psi(n,m) == psi(n,1) + r 00499 // where r == (-)^n * n! * (1^(-n-1) + ... + (m-1)^(-n-1)) 00500 numeric recur = 0; 00501 for (numeric p = 1; p<nx; ++p) 00502 recur += pow(p, -nn+(*_num_1_p)); 00503 recur *= factorial(nn)*pow((*_num_1_p), nn); 00504 return recur+psi(n,_ex1); 00505 } else { 00506 // for non-positive integers there is a pole: 00507 throw (pole_error("psi2_eval(): pole",1)); 00508 } 00509 } 00510 if (((*_num2_p)*nx).is_integer()) { 00511 // half integer case 00512 if (nx.is_equal(*_num1_2_p)) 00513 // use psi(n,1/2) == (-)^(n+1) * n! * (2^(n+1)-1) * zeta(n+1) 00514 return pow(*_num_1_p,nn+(*_num1_p))*factorial(nn)*(pow(*_num2_p,nn+(*_num1_p)) + (*_num_1_p))*zeta(ex(nn+(*_num1_p))); 00515 if (nx.is_positive()) { 00516 const numeric m = nx - (*_num1_2_p); 00517 // use the multiplication formula 00518 // psi(n,2*m) == (psi(n,m) + psi(n,m+1/2)) / 2^(n+1) 00519 // to revert to positive integer case 00520 return psi(n,(*_num2_p)*m)*pow((*_num2_p),nn+(*_num1_p))-psi(n,m); 00521 } else { 00522 // use the recurrence relation 00523 // psi(n,-m-1/2) == psi(n,-m-1/2+1) - (-)^n * n! / (-m-1/2)^(n+1) 00524 // to relate psi(n,-m-1/2) to psi(n,1/2): 00525 // psi(n,-m-1/2) == psi(n,1/2) + r 00526 // where r == (-)^(n+1) * n! * ((-1/2)^(-n-1) + ... + (-m-1/2)^(-n-1)) 00527 numeric recur = 0; 00528 for (numeric p = nx; p<0; ++p) 00529 recur += pow(p, -nn+(*_num_1_p)); 00530 recur *= factorial(nn)*pow(*_num_1_p, nn+(*_num_1_p)); 00531 return recur+psi(n,_ex1_2); 00532 } 00533 } 00534 // psi2_evalf should be called here once it becomes available 00535 } 00536 00537 return psi(n, x).hold(); 00538 } 00539 00540 static ex psi2_deriv(const ex & n, const ex & x, unsigned deriv_param) 00541 { 00542 GINAC_ASSERT(deriv_param<2); 00543 00544 if (deriv_param==0) { 00545 // d/dn psi(n,x) 00546 throw(std::logic_error("cannot diff psi(n,x) with respect to n")); 00547 } 00548 // d/dx psi(n,x) -> psi(n+1,x) 00549 return psi(n+_ex1, x); 00550 } 00551 00552 static ex psi2_series(const ex & n, 00553 const ex & arg, 00554 const relational & rel, 00555 int order, 00556 unsigned options) 00557 { 00558 // method: 00559 // Taylor series where there is no pole falls back to polygamma function 00560 // evaluation. 00561 // On a pole at -m use the recurrence relation 00562 // psi(n,x) == psi(n,x+1) - (-)^n * n! / x^(n+1) 00563 // from which follows 00564 // series(psi(x),x==-m,order) == 00565 // series(psi(x+m+1) - (-1)^n * n! * ((x)^(-n-1) + (x+1)^(-n-1) + ... 00566 // ... + (x+m)^(-n-1))),x==-m,order); 00567 const ex arg_pt = arg.subs(rel, subs_options::no_pattern); 00568 if (!arg_pt.info(info_flags::integer) || arg_pt.info(info_flags::positive)) 00569 throw do_taylor(); // caught by function::series() 00570 // if we got here we have to care for a pole of order n+1 at -m: 00571 const numeric m = -ex_to<numeric>(arg_pt); 00572 ex recur; 00573 for (numeric p; p<=m; ++p) 00574 recur += power(arg+p,-n+_ex_1); 00575 recur *= factorial(n)*power(_ex_1,n); 00576 return (psi(n, arg+m+_ex1)-recur).series(rel, order, options); 00577 } 00578 00579 unsigned psi2_SERIAL::serial = 00580 function::register_new(function_options("psi", 2). 00581 eval_func(psi2_eval). 00582 evalf_func(psi2_evalf). 00583 derivative_func(psi2_deriv). 00584 series_func(psi2_series). 00585 latex_name("\\psi"). 00586 overloaded(2)); 00587 00588 00589 } // namespace GiNaC