1 /** @file upoly-ginac.cpp
2 *
3 * This file implements several functions that work on univariate and
4 * rational functions.
5 * These functions include polynomial quotient and remainder, GCD and LCM
6 * computation, square-free factorization and rational function normalization. */
7
8 /*
9 * GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
10 * (C) 2016 Ralf Stephan
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "pynac-config.h"
29 #endif
30
31 #include "ex.h"
32 #include "ex_utils.h"
33 #include "numeric.h"
34 #include "normal.h"
35 #include "upoly.h"
36 #include "symbol.h"
37 #include "exprseq.h"
38 #include "add.h"
39 #include "mul.h"
40 #include "power.h"
41 #include "matrix.h"
42 #include "operators.h"
43 #include "utils.h"
44
45 #include <map>
46 #include <sstream>
47
48 namespace GiNaC {
49
50 // If comparing expressions (ex::compare()) is fast, you can set this to 1.
51 // Some routines like quo(), rem() and gcd() will then return a quick answer
52 // when they are called with two identical arguments.
53 #define FAST_COMPARE 1
54
55 /*
56 * Polynomial quotients and remainders
57 */
58
59 /** Quotient q(x) of polynomials a(x) and b(x) in Q[x].
60 * It satisfies a(x)=b(x)*q(x)+r(x).
61 *
62 * @param a first polynomial in x (dividend)
63 * @param b second polynomial in x (divisor)
64 * @param x a and b are polynomials in x
65 * @param check_args check whether a and b are polynomials with rational
66 * coefficients (defaults to "true")
67 * @return quotient of a and b in Q[x] */
quo(const ex & a,const ex & b,const ex & x,bool check_args)68 ex quo(const ex &a, const ex &b, const ex &x, bool check_args)
69 {
70 if (b.is_zero())
71 throw(std::overflow_error("quo: division by zero"));
72 if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b))
73 return a / b;
74 #if FAST_COMPARE
75 if (a.is_equal(b))
76 return _ex1;
77 #endif
78 if (check_args && (!a.info(info_flags::rational_polynomial) || !b.info(info_flags::rational_polynomial)))
79 throw(std::invalid_argument("quo: arguments must be polynomials over the rationals"));
80 // Polynomial long division
81 ex r = a.expand();
82 if (r.is_zero())
83 return r;
84 numeric bdeg = b.degree(x);
85 numeric rdeg = r.degree(x);
86 ex blcoeff = b.expand().coeff(x, bdeg);
87 bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
88 exvector v; //v.reserve(std::max(rdeg - bdeg + 1, 0));
89 while (rdeg >= bdeg) {
90 ex term, rcoeff = r.coeff(x, rdeg);
91 if (blcoeff_is_numeric)
92 term = rcoeff / blcoeff;
93 else {
94 if (!divide(rcoeff, blcoeff, term, false))
95 throw std::logic_error("");
96 }
97 term *= power(x, rdeg - bdeg);
98 v.push_back(term);
99 r -= (term * b).expand();
100 if (r.is_zero())
101 break;
102 rdeg = r.degree(x);
103 }
104 return (new add(v))->setflag(status_flags::dynallocated);
105 }
106
107 /** Remainder r(x) of polynomials a(x) and b(x) in Q[x].
108 * It satisfies a(x)=b(x)*q(x)+r(x).
109 *
110 * @param a first polynomial in x (dividend)
111 * @param b second polynomial in x (divisor)
112 * @param x a and b are polynomials in x
113 * @param check_args check whether a and b are polynomials with rational
114 * coefficients (defaults to "true")
115 * @return remainder of a(x) and b(x) in Q[x] */
rem(const ex & a,const ex & b,const ex & x,bool check_args)116 ex rem(const ex &a, const ex &b, const ex &x, bool check_args)
117 {
118 if (b.is_zero())
119 throw(std::overflow_error("rem: division by zero"));
120 if (is_exactly_a<numeric>(a)) {
121 if (is_exactly_a<numeric>(b))
122 return _ex0;
123 return a;
124 }
125 #if FAST_COMPARE
126 if (a.is_equal(b))
127 return _ex0;
128 #endif
129 if (check_args && (!a.info(info_flags::rational_polynomial) || !b.info(info_flags::rational_polynomial)))
130 throw(std::invalid_argument("rem: arguments must be polynomials over the rationals"));
131
132 // Polynomial long division
133 ex r = a.expand();
134 if (r.is_zero())
135 return r;
136 numeric bdeg = b.degree(x);
137 numeric rdeg = r.degree(x);
138 ex blcoeff = b.expand().coeff(x, bdeg);
139 bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
140 while (rdeg >= bdeg) {
141 ex term, rcoeff = r.coeff(x, rdeg);
142 if (blcoeff_is_numeric)
143 term = rcoeff / blcoeff;
144 else {
145 if (!divide(rcoeff, blcoeff, term, false))
146 throw std::logic_error("");
147 }
148 term *= power(x, rdeg - bdeg);
149 r -= (term * b).expand();
150 if (r.is_zero())
151 break;
152 rdeg = r.degree(x);
153 }
154 return r;
155 }
156
157
quo_rem(const ex & a,const ex & b,const ex & x,bool check_args)158 std::pair<ex,ex> quo_rem(const ex &a, const ex &b, const ex &x, bool check_args)
159 {
160 if (b.is_zero())
161 throw(std::overflow_error("quo: division by zero"));
162 if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b))
163 return std::make_pair(a / b, _ex0);
164 #if FAST_COMPARE
165 if (a.is_equal(b))
166 return std::make_pair(_ex1, _ex0);
167 #endif
168 expairvec vec1, vec2;
169 using pair_t = std::pair<ex,ex>;
170 a.expand().coefficients(x, vec1);
171 b.expand().coefficients(x, vec2);
172 if (check_args) {
173 if (std::any_of(vec1.begin(), vec1.end(),
174 [](const pair_t& p) {
175 return not is_exactly_a<numeric>(p.second)
176 or (not p.second.is_zero()
177 and not p.second.info(info_flags::posint)); }
178 ))
179 throw std::logic_error("nonposint exponent in quo()");
180 if (std::any_of(vec2.begin(), vec2.end(),
181 [](const pair_t& p) {
182 return not is_exactly_a<numeric>(p.second)
183 or (not p.second.is_zero()
184 and not p.second.info(info_flags::posint)); }
185 ))
186 throw std::logic_error("nonposint exponent in quo()");
187 }
188 const auto& mit1 = std::max_element(vec1.begin(), vec1.end(),
189 [](const pair_t& p1, const pair_t& p2) {
190 return ex_to<numeric>(p1.second) <
191 ex_to<numeric>(p2.second); } );
192 const auto& mit2 = std::max_element(vec2.begin(), vec2.end(),
193 [](const pair_t& p1, const pair_t& p2) {
194 return ex_to<numeric>(p1.second) <
195 ex_to<numeric>(p2.second); } );
196 size_t adeg = ex_to<numeric>(mit1->second).to_long();
197 size_t bdeg = ex_to<numeric>(mit2->second).to_long();
198 if (adeg < bdeg)
199 return std::make_pair(_ex0, a);
200 // make flat coeff vectors
201 std::vector<ex> avec, bvec;
202 avec.assign(adeg+1, _ex0);
203 bvec.assign(bdeg+1, _ex0);
204 for (const pair_t& p : vec1)
205 *(avec.begin() + ex_to<numeric>(p.second).to_long()) = p.first;
206 for (const pair_t& p : vec2)
207 *(bvec.begin() + ex_to<numeric>(p.second).to_long()) = p.first;
208
209 // poly division
210 ex lc2 = bvec[bdeg];
211 exvector qvec;
212 for (int k=adeg-bdeg; k>=0; --k) {
213 ex q = avec[bdeg+k] / lc2;
214 avec[bdeg+k] = _ex0;
215 for (int j=bdeg+k-1; j>=k; --j)
216 avec[j] = avec[j] - q * bvec[j-k];
217 qvec.insert(qvec.begin(), q);
218 }
219 for (size_t i=0; i<qvec.size(); ++i)
220 if (not qvec[i].is_zero())
221 qvec[i] = qvec[i] * power(x, numeric(i));
222 if (avec.size() > bdeg)
223 avec.resize(bdeg);
224 for (size_t i=0; i<bdeg; ++i)
225 if (not avec[i].is_zero())
226 avec[i] = avec[i] * power(x, numeric(i));
227 return std::make_pair(add(qvec), add(avec));
228 }
229
230
231 /** Decompose rational function a(x)=N(x)/D(x) into P(x)+n(x)/D(x)
232 * with degree(n, x) < degree(D, x).
233 *
234 * @param a rational function in x
235 * @param x a is a function of x
236 * @return decomposed function. */
decomp_rational(const ex & a,const ex & x)237 ex decomp_rational(const ex &a, const ex &x)
238 {
239 ex nd = numer_denom(a);
240 ex numer = nd.op(0), denom = nd.op(1);
241 ex q;
242 try {
243 q = quo(numer, denom, x);
244 }
245 catch (std::logic_error) {
246 return a;
247 }
248 return q + rem(numer, denom, x) / denom;
249 }
250
251
252 /** Pseudo-remainder of polynomials a(x) and b(x) in Q[x].
253 *
254 * @param a first polynomial in x (dividend)
255 * @param b second polynomial in x (divisor)
256 * @param x a and b are polynomials in x
257 * @param check_args check whether a and b are polynomials with rational
258 * coefficients (defaults to "true")
259 * @return pseudo-remainder of a(x) and b(x) in Q[x] */
prem(const ex & a,const ex & b,const ex & x,bool check_args)260 ex prem(const ex &a, const ex &b, const ex &x, bool check_args)
261 {
262 if (b.is_zero())
263 throw(std::overflow_error("prem: division by zero"));
264 if (is_exactly_a<numeric>(a)) {
265 if (is_exactly_a<numeric>(b))
266 return _ex0;
267 return b;
268 }
269 if (check_args && (!a.info(info_flags::rational_polynomial) || !b.info(info_flags::rational_polynomial)))
270 throw(std::invalid_argument("prem: arguments must be polynomials over the rationals"));
271
272 // Polynomial long division
273 ex r = a.expand();
274 ex eb = b.expand();
275 numeric rdeg = r.degree(x);
276 numeric bdeg = eb.degree(x);
277 ex blcoeff;
278 if (bdeg <= rdeg) {
279 blcoeff = eb.coeff(x, bdeg);
280 if (bdeg == 0)
281 eb = _ex0;
282 else
283 eb -= blcoeff * power(x, bdeg);
284 } else
285 blcoeff = _ex1;
286
287 numeric delta = rdeg - bdeg + 1, i = 0;
288 while (rdeg >= bdeg && !r.is_zero()) {
289 ex rlcoeff = r.coeff(x, rdeg);
290 ex term = (power(x, rdeg - bdeg) * eb * rlcoeff).expand();
291 if (rdeg == 0)
292 r = _ex0;
293 else
294 r -= rlcoeff * power(x, rdeg);
295 r = (blcoeff * r).expand() - term;
296 rdeg = r.degree(x);
297 i++;
298 }
299 return power(blcoeff, delta - i) * r;
300 }
301
302
303 /** Sparse pseudo-remainder of polynomials a(x) and b(x) in Q[x].
304 *
305 * @param a first polynomial in x (dividend)
306 * @param b second polynomial in x (divisor)
307 * @param x a and b are polynomials in x
308 * @param check_args check whether a and b are polynomials with rational
309 * coefficients (defaults to "true")
310 * @return sparse pseudo-remainder of a(x) and b(x) in Q[x] */
sprem(const ex & a,const ex & b,const ex & x,bool check_args)311 ex sprem(const ex &a, const ex &b, const ex &x, bool check_args)
312 {
313 if (b.is_zero())
314 throw(std::overflow_error("prem: division by zero"));
315 if (is_exactly_a<numeric>(a)) {
316 if (is_exactly_a<numeric>(b))
317 return _ex0;
318 return b;
319 }
320 if (check_args && (!a.info(info_flags::rational_polynomial) || !b.info(info_flags::rational_polynomial)))
321 throw(std::invalid_argument("prem: arguments must be polynomials over the rationals"));
322
323 // Polynomial long division
324 ex r = a.expand();
325 ex eb = b.expand();
326 numeric rdeg = r.degree(x);
327 numeric bdeg = eb.degree(x);
328 ex blcoeff;
329 if (bdeg <= rdeg) {
330 blcoeff = eb.coeff(x, bdeg);
331 if (bdeg == 0)
332 eb = _ex0;
333 else
334 eb -= blcoeff * power(x, bdeg);
335 } else
336 blcoeff = _ex1;
337
338 while (rdeg >= bdeg && !r.is_zero()) {
339 ex rlcoeff = r.coeff(x, rdeg);
340 ex term = (power(x, rdeg - bdeg) * eb * rlcoeff).expand();
341 if (rdeg == 0)
342 r = _ex0;
343 else
344 r -= rlcoeff * power(x, rdeg);
345 r = (blcoeff * r).expand() - term;
346 rdeg = r.degree(x);
347 }
348 return r;
349 }
350
351
352 /** Exact polynomial division of a(X) by b(X) in Q[X].
353 *
354 * @param a first multivariate polynomial (dividend)
355 * @param b second multivariate polynomial (divisor)
356 * @param q quotient (returned)
357 * @param check_args check whether a and b are polynomials with rational
358 * coefficients (defaults to "true")
359 * @return "true" when exact division succeeds (quotient returned in q),
360 * "false" otherwise (q left untouched) */
divide(const ex & a,const ex & b,ex & q,bool check_args)361 bool divide(const ex &a, const ex &b, ex &q, bool check_args)
362 {
363 if (b.is_zero())
364 throw(std::overflow_error("divide: division by zero"));
365 if (a.is_zero()) {
366 q = _ex0;
367 return true;
368 }
369 if (is_exactly_a<numeric>(b)) {
370 q = a / b;
371 return true;
372 }
373 if (is_exactly_a<numeric>(a))
374 return false;
375 #if FAST_COMPARE
376 if (a.is_equal(b)) {
377 q = _ex1;
378 return true;
379 }
380 #endif
381 if (check_args && (!a.info(info_flags::rational_polynomial) ||
382 !b.info(info_flags::rational_polynomial)))
383 throw(std::invalid_argument("divide: arguments must be polynomials over the rationals"));
384
385 // Find first symbol
386 ex x;
387 if (!a.get_first_symbol(x) && !b.get_first_symbol(x)) {
388 std::ostringstream os;
389 os << "invalid expression in divide(): " << a << " / " << b;
390 throw(std::invalid_argument(os.str()));
391 }
392
393 // Try to avoid expanding partially factored expressions.
394 if (is_exactly_a<mul>(b)) {
395 // Divide sequentially by each term
396 ex rem_new, rem_old = a;
397 for (size_t i=0; i < b.nops(); i++) {
398 if (! divide(rem_old, b.op(i), rem_new, false))
399 return false;
400 rem_old = rem_new;
401 }
402 q = rem_new;
403 return true;
404 }
405 if (is_exactly_a<power>(b)) {
406 const ex& bb(b.op(0));
407 int exp_b = ex_to<numeric>(b.op(1)).to_int();
408 ex rem_new, rem_old = a;
409 for (int i=exp_b; i>0; i--) {
410 if (! divide(rem_old, bb, rem_new, false))
411 return false;
412 rem_old = rem_new;
413 }
414 q = rem_new;
415 return true;
416 }
417
418 if (is_exactly_a<mul>(a)) {
419 // Divide sequentially each term. If some term in a is divisible
420 // by b we are done... and if not, we can't really say anything.
421 size_t i;
422 ex rem_i;
423 bool divisible_p = false;
424 for (i=0; i < a.nops(); ++i) {
425 if (divide(a.op(i), b, rem_i, false)) {
426 divisible_p = true;
427 break;
428 }
429 }
430 if (divisible_p) {
431 exvector resv;
432 resv.reserve(a.nops());
433 for (size_t j=0; j < a.nops(); j++) {
434 if (j==i)
435 resv.push_back(rem_i);
436 else
437 resv.push_back(a.op(j));
438 }
439 q = (new mul(resv))->setflag(status_flags::dynallocated);
440 return true;
441 }
442 } else if (is_exactly_a<power>(a)) {
443 // The base itself might be divisible by b, in that case we don't
444 // need to expand a
445 const ex& ab(a.op(0));
446 int a_exp = ex_to<numeric>(a.op(1)).to_int();
447 ex rem_i;
448 if (divide(ab, b, rem_i, false)) {
449 q = rem_i*power(ab, a_exp - 1);
450 return true;
451 }
452 // code below is commented-out because it leads to a significant slowdown
453 // for (int i=2; i < a_exp; i++) {
454 // if (divide(power(ab, i), b, rem_i, false)) {
455 // q = rem_i*power(ab, a_exp - i);
456 // return true;
457 // }
458 // } // ... so we *really* need to expand expression.
459 }
460
461 // Polynomial long division (recursive)
462 ex r = a.expand();
463 if (r.is_zero()) {
464 q = _ex0;
465 return true;
466 }
467 numeric bdeg = b.degree(x);
468 numeric rdeg = r.degree(x);
469 ex blcoeff = b.expand().coeff(x, bdeg);
470 bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
471 exvector v; //v.reserve(std::max(rdeg - bdeg + 1, 0));
472 while (rdeg >= bdeg) {
473 ex term, rcoeff = r.coeff(x, rdeg);
474 if (blcoeff_is_numeric)
475 term = rcoeff / blcoeff;
476 else
477 if (!divide(rcoeff, blcoeff, term, false))
478 return false;
479 term *= power(x, rdeg - bdeg);
480 v.push_back(term);
481 r -= (term * b).expand();
482 if (r.is_zero()) {
483 q = (new add(v))->setflag(status_flags::dynallocated);
484 return true;
485 }
486 rdeg = r.degree(x);
487 }
488 return false;
489 }
490
491 // Distribute a factor over all sum terms
dist(const ex & f,const ex & p)492 static ex dist(const ex& f, const ex& p)
493 {
494 if (not is_exactly_a<add>(p))
495 return mul(f, p);
496 ex s = _ex0;
497 for (size_t i=0; i<p.nops(); ++i)
498 s += f*p.op(i);
499 return s;
500 }
501
502 /** Compute partial fraction decomposition of expression
503 *
504 * @param a expression
505 * @param x variable to factor in
506 * @return decomposed rational function */
parfrac(const ex & a,const ex & x)507 ex parfrac(const ex & a, const ex & x)
508 {
509 if (is_exactly_a<add>(a)) {
510 ex s = _ex0;
511 for (size_t i=0; i<a.nops(); ++i)
512 s += parfrac(a.op(i), x);
513 return s;
514 }
515 if (not is_exactly_a<mul>(a))
516 return a;
517 // Find numerator and denominator
518 ex nd = numer_denom(a);
519 ex numer = nd.op(0), denom = nd.op(1);
520 // Expand sum powers
521 if (is_exactly_a<add>(numer))
522 return parfrac(dist(_ex1/denom, numer), x);
523 if (is_exactly_a<power>(numer)
524 and is_exactly_a<add>(numer.op(0))
525 and numer.op(1).info(info_flags::posint))
526 return parfrac(dist(_ex1/denom, numer.expand()), x);
527
528 if (is_exactly_a<mul>(numer))
529 for (size_t i=0; i<numer.nops(); ++i) {
530 const ex& t = numer.op(i);
531 if (is_exactly_a<power>(t)
532 and is_exactly_a<add>(t.op(0))
533 and t.op(1).info(info_flags::posint))
534 return parfrac(dist(_ex1/denom, numer.expand()), x);
535 }
536 std::pair<ex,ex> qr;
537 try {
538 // Convert N(x)/D(x) -> Q(x) + R(x)/D(x), so degree(R) < degree(D)
539 qr = quo_rem(numer, denom, x, true);
540 }
541 catch (std::logic_error) {
542 return a;
543 }
544 // Factorize denominator and compute cofactors
545 ex facmul;
546 bool r = factor(denom, facmul);
547 if (not r)
548 facmul = denom;
549 // return qr.first + qr.second/denom;
550 ex oc = _ex1;
551 exvector factor, cofac;
552 if (is_exactly_a<mul>(facmul)) {
553 size_t fsize = facmul.nops();
554 for (size_t i=0; i<fsize; i++) {
555 const ex& e = facmul.op(i);
556 if (is_exactly_a<numeric>(e)) {
557 oc = e;
558 continue;
559 }
560 if (is_exactly_a<power>(e)) {
561 if (not has(e.op(0), x))
562 continue;
563 const ex& ee = e.op(1);
564 if (not is_exactly_a<numeric>(ee)
565 or not ee.is_integer()
566 or e.op(0).is_equal(x))
567 return a;
568 size_t expo = ex_to<numeric>(ee).to_int();
569 for (size_t j=1; j<=expo; ++j) {
570 ex eee = power(e.op(0), numeric(j));
571 factor.push_back(eee);
572 cofac.push_back((facmul/eee).expand());
573 }
574 }
575 else {
576 factor.push_back(e);
577 cofac.push_back((facmul/e).expand());
578 }
579 }
580 }
581 else if (is_exactly_a<power>(facmul)
582 and is_exactly_a<numeric>(facmul.op(1))) {
583 if (facmul.op(0).is_equal(x))
584 return a;
585 size_t expo = ex_to<numeric>(facmul.op(1)).to_int();
586 for (size_t j=1; j<=expo; ++j) {
587 ex ee = power(facmul.op(0), numeric(j));
588 factor.push_back(ee);
589 cofac.push_back((facmul/ee).expand());
590 }
591 }
592 else {
593 return qr.first + qr.second/denom;
594 }
595 size_t num_factors = factor.size();
596 //std::cerr << "factors : " << exprseq(factor) << std::endl;
597 //std::cerr << "cofactors: " << exprseq(cofac) << std::endl;
598
599 // Construct coefficient matrix for decomposition
600 int max_denom_deg = ex_to<numeric>(denom.degree(x)).to_int();
601 matrix sys(max_denom_deg + 1, num_factors);
602 matrix rhs(max_denom_deg + 1, 1);
603 for (int i=0; i<=max_denom_deg; i++) {
604 for (size_t j=0; j<num_factors; j++)
605 sys(i, j) = cofac[j].coeff(x, i);
606 rhs(i, 0) = qr.second.coeff(x, i);
607 }
608 //clog << "coeffs: " << sys << endl;
609 //clog << "rhs : " << rhs << endl;
610
611 // Solve resulting linear system
612 matrix vars(num_factors, 1);
613 for (size_t i=0; i<num_factors; i++)
614 vars(i, 0) = symbol();
615 matrix sol = sys.solve(vars, rhs);
616
617 // Sum up decomposed fractions
618 ex sum = _ex0;
619 for (size_t i=0; i<num_factors; i++)
620 sum += sol(i, 0) / oc / factor[i];
621
622 return qr.first + sum;
623 }
624
625
626 } // namespace GiNaC
627