Coverage for /usr/lib/python3/dist-packages/sympy/core/exprtools.py: 37%

779 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1"""Tools for manipulating of large commutative expressions. """ 

2 

3from .add import Add 

4from .mul import Mul, _keep_coeff 

5from .power import Pow 

6from .basic import Basic 

7from .expr import Expr 

8from .function import expand_power_exp 

9from .sympify import sympify 

10from .numbers import Rational, Integer, Number, I, equal_valued 

11from .singleton import S 

12from .sorting import default_sort_key, ordered 

13from .symbol import Dummy 

14from .traversal import preorder_traversal 

15from .coreerrors import NonCommutativeExpression 

16from .containers import Tuple, Dict 

17from sympy.external.gmpy import SYMPY_INTS 

18from sympy.utilities.iterables import (common_prefix, common_suffix, 

19 variations, iterable, is_sequence) 

20 

21from collections import defaultdict 

22from typing import Tuple as tTuple 

23 

24 

25_eps = Dummy(positive=True) 

26 

27 

28def _isnumber(i): 

29 return isinstance(i, (SYMPY_INTS, float)) or i.is_Number 

30 

31 

32def _monotonic_sign(self): 

33 """Return the value closest to 0 that ``self`` may have if all symbols 

34 are signed and the result is uniformly the same sign for all values of symbols. 

35 If a symbol is only signed but not known to be an 

36 integer or the result is 0 then a symbol representative of the sign of self 

37 will be returned. Otherwise, None is returned if a) the sign could be positive 

38 or negative or b) self is not in one of the following forms: 

39 

40 - L(x, y, ...) + A: a function linear in all symbols x, y, ... with an 

41 additive constant; if A is zero then the function can be a monomial whose 

42 sign is monotonic over the range of the variables, e.g. (x + 1)**3 if x is 

43 nonnegative. 

44 - A/L(x, y, ...) + B: the inverse of a function linear in all symbols x, y, ... 

45 that does not have a sign change from positive to negative for any set 

46 of values for the variables. 

47 - M(x, y, ...) + A: a monomial M whose factors are all signed and a constant, A. 

48 - A/M(x, y, ...) + B: the inverse of a monomial and constants A and B. 

49 - P(x): a univariate polynomial 

50 

51 Examples 

52 ======== 

53 

54 >>> from sympy.core.exprtools import _monotonic_sign as F 

55 >>> from sympy import Dummy 

56 >>> nn = Dummy(integer=True, nonnegative=True) 

57 >>> p = Dummy(integer=True, positive=True) 

58 >>> p2 = Dummy(integer=True, positive=True) 

59 >>> F(nn + 1) 

60 1 

61 >>> F(p - 1) 

62 _nneg 

63 >>> F(nn*p + 1) 

64 1 

65 >>> F(p2*p + 1) 

66 2 

67 >>> F(nn - 1) # could be negative, zero or positive 

68 """ 

69 if not self.is_extended_real: 

70 return 

71 

72 if (-self).is_Symbol: 

73 rv = _monotonic_sign(-self) 

74 return rv if rv is None else -rv 

75 

76 if not self.is_Add and self.as_numer_denom()[1].is_number: 

77 s = self 

78 if s.is_prime: 

79 if s.is_odd: 

80 return Integer(3) 

81 else: 

82 return Integer(2) 

83 elif s.is_composite: 

84 if s.is_odd: 

85 return Integer(9) 

86 else: 

87 return Integer(4) 

88 elif s.is_positive: 

89 if s.is_even: 

90 if s.is_prime is False: 

91 return Integer(4) 

92 else: 

93 return Integer(2) 

94 elif s.is_integer: 

95 return S.One 

96 else: 

97 return _eps 

98 elif s.is_extended_negative: 

99 if s.is_even: 

100 return Integer(-2) 

101 elif s.is_integer: 

102 return S.NegativeOne 

103 else: 

104 return -_eps 

105 if s.is_zero or s.is_extended_nonpositive or s.is_extended_nonnegative: 

106 return S.Zero 

107 return None 

108 

109 # univariate polynomial 

110 free = self.free_symbols 

111 if len(free) == 1: 

112 if self.is_polynomial(): 

113 from sympy.polys.polytools import real_roots 

114 from sympy.polys.polyroots import roots 

115 from sympy.polys.polyerrors import PolynomialError 

116 x = free.pop() 

117 x0 = _monotonic_sign(x) 

118 if x0 in (_eps, -_eps): 

119 x0 = S.Zero 

120 if x0 is not None: 

121 d = self.diff(x) 

122 if d.is_number: 

123 currentroots = [] 

124 else: 

125 try: 

126 currentroots = real_roots(d) 

127 except (PolynomialError, NotImplementedError): 

128 currentroots = [r for r in roots(d, x) if r.is_extended_real] 

129 y = self.subs(x, x0) 

130 if x.is_nonnegative and all( 

131 (r - x0).is_nonpositive for r in currentroots): 

132 if y.is_nonnegative and d.is_positive: 

133 if y: 

134 return y if y.is_positive else Dummy('pos', positive=True) 

135 else: 

136 return Dummy('nneg', nonnegative=True) 

137 if y.is_nonpositive and d.is_negative: 

138 if y: 

139 return y if y.is_negative else Dummy('neg', negative=True) 

140 else: 

141 return Dummy('npos', nonpositive=True) 

142 elif x.is_nonpositive and all( 

143 (r - x0).is_nonnegative for r in currentroots): 

144 if y.is_nonnegative and d.is_negative: 

145 if y: 

146 return Dummy('pos', positive=True) 

147 else: 

148 return Dummy('nneg', nonnegative=True) 

149 if y.is_nonpositive and d.is_positive: 

150 if y: 

151 return Dummy('neg', negative=True) 

152 else: 

153 return Dummy('npos', nonpositive=True) 

154 else: 

155 n, d = self.as_numer_denom() 

156 den = None 

157 if n.is_number: 

158 den = _monotonic_sign(d) 

159 elif not d.is_number: 

160 if _monotonic_sign(n) is not None: 

161 den = _monotonic_sign(d) 

162 if den is not None and (den.is_positive or den.is_negative): 

163 v = n*den 

164 if v.is_positive: 

165 return Dummy('pos', positive=True) 

166 elif v.is_nonnegative: 

167 return Dummy('nneg', nonnegative=True) 

168 elif v.is_negative: 

169 return Dummy('neg', negative=True) 

170 elif v.is_nonpositive: 

171 return Dummy('npos', nonpositive=True) 

172 return None 

173 

174 # multivariate 

175 c, a = self.as_coeff_Add() 

176 v = None 

177 if not a.is_polynomial(): 

178 # F/A or A/F where A is a number and F is a signed, rational monomial 

179 n, d = a.as_numer_denom() 

180 if not (n.is_number or d.is_number): 

181 return 

182 if ( 

183 a.is_Mul or a.is_Pow) and \ 

184 a.is_rational and \ 

185 all(p.exp.is_Integer for p in a.atoms(Pow) if p.is_Pow) and \ 

186 (a.is_positive or a.is_negative): 

187 v = S.One 

188 for ai in Mul.make_args(a): 

189 if ai.is_number: 

190 v *= ai 

191 continue 

192 reps = {} 

193 for x in ai.free_symbols: 

194 reps[x] = _monotonic_sign(x) 

195 if reps[x] is None: 

196 return 

197 v *= ai.subs(reps) 

198 elif c: 

199 # signed linear expression 

200 if not any(p for p in a.atoms(Pow) if not p.is_number) and (a.is_nonpositive or a.is_nonnegative): 

201 free = list(a.free_symbols) 

202 p = {} 

203 for i in free: 

204 v = _monotonic_sign(i) 

205 if v is None: 

206 return 

207 p[i] = v or (_eps if i.is_nonnegative else -_eps) 

208 v = a.xreplace(p) 

209 if v is not None: 

210 rv = v + c 

211 if v.is_nonnegative and rv.is_positive: 

212 return rv.subs(_eps, 0) 

213 if v.is_nonpositive and rv.is_negative: 

214 return rv.subs(_eps, 0) 

215 

216 

217def decompose_power(expr: Expr) -> tTuple[Expr, int]: 

218 """ 

219 Decompose power into symbolic base and integer exponent. 

220 

221 Examples 

222 ======== 

223 

224 >>> from sympy.core.exprtools import decompose_power 

225 >>> from sympy.abc import x, y 

226 >>> from sympy import exp 

227 

228 >>> decompose_power(x) 

229 (x, 1) 

230 >>> decompose_power(x**2) 

231 (x, 2) 

232 >>> decompose_power(exp(2*y/3)) 

233 (exp(y/3), 2) 

234 

235 """ 

236 base, exp = expr.as_base_exp() 

237 

238 if exp.is_Number: 

239 if exp.is_Rational: 

240 if not exp.is_Integer: 

241 base = Pow(base, Rational(1, exp.q)) # type: ignore 

242 e = exp.p # type: ignore 

243 else: 

244 base, e = expr, 1 

245 else: 

246 exp, tail = exp.as_coeff_Mul(rational=True) 

247 

248 if exp is S.NegativeOne: 

249 base, e = Pow(base, tail), -1 

250 elif exp is not S.One: 

251 # todo: after dropping python 3.7 support, use overload and Literal 

252 # in as_coeff_Mul to make exp Rational, and remove these 2 ignores 

253 tail = _keep_coeff(Rational(1, exp.q), tail) # type: ignore 

254 base, e = Pow(base, tail), exp.p # type: ignore 

255 else: 

256 base, e = expr, 1 

257 

258 return base, e 

259 

260 

261def decompose_power_rat(expr: Expr) -> tTuple[Expr, Rational]: 

262 """ 

263 Decompose power into symbolic base and rational exponent; 

264 if the exponent is not a Rational, then separate only the 

265 integer coefficient. 

266 

267 Examples 

268 ======== 

269 

270 >>> from sympy.core.exprtools import decompose_power_rat 

271 >>> from sympy.abc import x 

272 >>> from sympy import sqrt, exp 

273 

274 >>> decompose_power_rat(sqrt(x)) 

275 (x, 1/2) 

276 >>> decompose_power_rat(exp(-3*x/2)) 

277 (exp(x/2), -3) 

278 

279 """ 

280 _ = base, exp = expr.as_base_exp() 

281 return _ if exp.is_Rational else decompose_power(expr) 

282 

283 

284class Factors: 

285 """Efficient representation of ``f_1*f_2*...*f_n``.""" 

286 

287 __slots__ = ('factors', 'gens') 

288 

289 def __init__(self, factors=None): # Factors 

290 """Initialize Factors from dict or expr. 

291 

292 Examples 

293 ======== 

294 

295 >>> from sympy.core.exprtools import Factors 

296 >>> from sympy.abc import x 

297 >>> from sympy import I 

298 >>> e = 2*x**3 

299 >>> Factors(e) 

300 Factors({2: 1, x: 3}) 

301 >>> Factors(e.as_powers_dict()) 

302 Factors({2: 1, x: 3}) 

303 >>> f = _ 

304 >>> f.factors # underlying dictionary 

305 {2: 1, x: 3} 

306 >>> f.gens # base of each factor 

307 frozenset({2, x}) 

308 >>> Factors(0) 

309 Factors({0: 1}) 

310 >>> Factors(I) 

311 Factors({I: 1}) 

312 

313 Notes 

314 ===== 

315 

316 Although a dictionary can be passed, only minimal checking is 

317 performed: powers of -1 and I are made canonical. 

318 

319 """ 

320 if isinstance(factors, (SYMPY_INTS, float)): 

321 factors = S(factors) 

322 if isinstance(factors, Factors): 

323 factors = factors.factors.copy() 

324 elif factors in (None, S.One): 

325 factors = {} 

326 elif factors is S.Zero or factors == 0: 

327 factors = {S.Zero: S.One} 

328 elif isinstance(factors, Number): 

329 n = factors 

330 factors = {} 

331 if n < 0: 

332 factors[S.NegativeOne] = S.One 

333 n = -n 

334 if n is not S.One: 

335 if n.is_Float or n.is_Integer or n is S.Infinity: 

336 factors[n] = S.One 

337 elif n.is_Rational: 

338 # since we're processing Numbers, the denominator is 

339 # stored with a negative exponent; all other factors 

340 # are left . 

341 if n.p != 1: 

342 factors[Integer(n.p)] = S.One 

343 factors[Integer(n.q)] = S.NegativeOne 

344 else: 

345 raise ValueError('Expected Float|Rational|Integer, not %s' % n) 

346 elif isinstance(factors, Basic) and not factors.args: 

347 factors = {factors: S.One} 

348 elif isinstance(factors, Expr): 

349 c, nc = factors.args_cnc() 

350 i = c.count(I) 

351 for _ in range(i): 

352 c.remove(I) 

353 factors = dict(Mul._from_args(c).as_powers_dict()) 

354 # Handle all rational Coefficients 

355 for f in list(factors.keys()): 

356 if isinstance(f, Rational) and not isinstance(f, Integer): 

357 p, q = Integer(f.p), Integer(f.q) 

358 factors[p] = (factors[p] if p in factors else S.Zero) + factors[f] 

359 factors[q] = (factors[q] if q in factors else S.Zero) - factors[f] 

360 factors.pop(f) 

361 if i: 

362 factors[I] = factors.get(I, S.Zero) + i 

363 if nc: 

364 factors[Mul(*nc, evaluate=False)] = S.One 

365 else: 

366 factors = factors.copy() # /!\ should be dict-like 

367 

368 # tidy up -/+1 and I exponents if Rational 

369 

370 handle = [k for k in factors if k is I or k in (-1, 1)] 

371 if handle: 

372 i1 = S.One 

373 for k in handle: 

374 if not _isnumber(factors[k]): 

375 continue 

376 i1 *= k**factors.pop(k) 

377 if i1 is not S.One: 

378 for a in i1.args if i1.is_Mul else [i1]: # at worst, -1.0*I*(-1)**e 

379 if a is S.NegativeOne: 

380 factors[a] = S.One 

381 elif a is I: 

382 factors[I] = S.One 

383 elif a.is_Pow: 

384 factors[a.base] = factors.get(a.base, S.Zero) + a.exp 

385 elif equal_valued(a, 1): 

386 factors[a] = S.One 

387 elif equal_valued(a, -1): 

388 factors[-a] = S.One 

389 factors[S.NegativeOne] = S.One 

390 else: 

391 raise ValueError('unexpected factor in i1: %s' % a) 

392 

393 self.factors = factors 

394 keys = getattr(factors, 'keys', None) 

395 if keys is None: 

396 raise TypeError('expecting Expr or dictionary') 

397 self.gens = frozenset(keys()) 

398 

399 def __hash__(self): # Factors 

400 keys = tuple(ordered(self.factors.keys())) 

401 values = [self.factors[k] for k in keys] 

402 return hash((keys, values)) 

403 

404 def __repr__(self): # Factors 

405 return "Factors({%s})" % ', '.join( 

406 ['%s: %s' % (k, v) for k, v in ordered(self.factors.items())]) 

407 

408 @property 

409 def is_zero(self): # Factors 

410 """ 

411 >>> from sympy.core.exprtools import Factors 

412 >>> Factors(0).is_zero 

413 True 

414 """ 

415 f = self.factors 

416 return len(f) == 1 and S.Zero in f 

417 

418 @property 

419 def is_one(self): # Factors 

420 """ 

421 >>> from sympy.core.exprtools import Factors 

422 >>> Factors(1).is_one 

423 True 

424 """ 

425 return not self.factors 

426 

427 def as_expr(self): # Factors 

428 """Return the underlying expression. 

429 

430 Examples 

431 ======== 

432 

433 >>> from sympy.core.exprtools import Factors 

434 >>> from sympy.abc import x, y 

435 >>> Factors((x*y**2).as_powers_dict()).as_expr() 

436 x*y**2 

437 

438 """ 

439 

440 args = [] 

441 for factor, exp in self.factors.items(): 

442 if exp != 1: 

443 if isinstance(exp, Integer): 

444 b, e = factor.as_base_exp() 

445 e = _keep_coeff(exp, e) 

446 args.append(b**e) 

447 else: 

448 args.append(factor**exp) 

449 else: 

450 args.append(factor) 

451 return Mul(*args) 

452 

453 def mul(self, other): # Factors 

454 """Return Factors of ``self * other``. 

455 

456 Examples 

457 ======== 

458 

459 >>> from sympy.core.exprtools import Factors 

460 >>> from sympy.abc import x, y, z 

461 >>> a = Factors((x*y**2).as_powers_dict()) 

462 >>> b = Factors((x*y/z).as_powers_dict()) 

463 >>> a.mul(b) 

464 Factors({x: 2, y: 3, z: -1}) 

465 >>> a*b 

466 Factors({x: 2, y: 3, z: -1}) 

467 """ 

468 if not isinstance(other, Factors): 

469 other = Factors(other) 

470 if any(f.is_zero for f in (self, other)): 

471 return Factors(S.Zero) 

472 factors = dict(self.factors) 

473 

474 for factor, exp in other.factors.items(): 

475 if factor in factors: 

476 exp = factors[factor] + exp 

477 

478 if not exp: 

479 del factors[factor] 

480 continue 

481 

482 factors[factor] = exp 

483 

484 return Factors(factors) 

485 

486 def normal(self, other): 

487 """Return ``self`` and ``other`` with ``gcd`` removed from each. 

488 The only differences between this and method ``div`` is that this 

489 is 1) optimized for the case when there are few factors in common and 

490 2) this does not raise an error if ``other`` is zero. 

491 

492 See Also 

493 ======== 

494 div 

495 

496 """ 

497 if not isinstance(other, Factors): 

498 other = Factors(other) 

499 if other.is_zero: 

500 return (Factors(), Factors(S.Zero)) 

501 if self.is_zero: 

502 return (Factors(S.Zero), Factors()) 

503 

504 self_factors = dict(self.factors) 

505 other_factors = dict(other.factors) 

506 

507 for factor, self_exp in self.factors.items(): 

508 try: 

509 other_exp = other.factors[factor] 

510 except KeyError: 

511 continue 

512 

513 exp = self_exp - other_exp 

514 

515 if not exp: 

516 del self_factors[factor] 

517 del other_factors[factor] 

518 elif _isnumber(exp): 

519 if exp > 0: 

520 self_factors[factor] = exp 

521 del other_factors[factor] 

522 else: 

523 del self_factors[factor] 

524 other_factors[factor] = -exp 

525 else: 

526 r = self_exp.extract_additively(other_exp) 

527 if r is not None: 

528 if r: 

529 self_factors[factor] = r 

530 del other_factors[factor] 

531 else: # should be handled already 

532 del self_factors[factor] 

533 del other_factors[factor] 

534 else: 

535 sc, sa = self_exp.as_coeff_Add() 

536 if sc: 

537 oc, oa = other_exp.as_coeff_Add() 

538 diff = sc - oc 

539 if diff > 0: 

540 self_factors[factor] -= oc 

541 other_exp = oa 

542 elif diff < 0: 

543 self_factors[factor] -= sc 

544 other_factors[factor] -= sc 

545 other_exp = oa - diff 

546 else: 

547 self_factors[factor] = sa 

548 other_exp = oa 

549 if other_exp: 

550 other_factors[factor] = other_exp 

551 else: 

552 del other_factors[factor] 

553 

554 return Factors(self_factors), Factors(other_factors) 

555 

556 def div(self, other): # Factors 

557 """Return ``self`` and ``other`` with ``gcd`` removed from each. 

558 This is optimized for the case when there are many factors in common. 

559 

560 Examples 

561 ======== 

562 

563 >>> from sympy.core.exprtools import Factors 

564 >>> from sympy.abc import x, y, z 

565 >>> from sympy import S 

566 

567 >>> a = Factors((x*y**2).as_powers_dict()) 

568 >>> a.div(a) 

569 (Factors({}), Factors({})) 

570 >>> a.div(x*z) 

571 (Factors({y: 2}), Factors({z: 1})) 

572 

573 The ``/`` operator only gives ``quo``: 

574 

575 >>> a/x 

576 Factors({y: 2}) 

577 

578 Factors treats its factors as though they are all in the numerator, so 

579 if you violate this assumption the results will be correct but will 

580 not strictly correspond to the numerator and denominator of the ratio: 

581 

582 >>> a.div(x/z) 

583 (Factors({y: 2}), Factors({z: -1})) 

584 

585 Factors is also naive about bases: it does not attempt any denesting 

586 of Rational-base terms, for example the following does not become 

587 2**(2*x)/2. 

588 

589 >>> Factors(2**(2*x + 2)).div(S(8)) 

590 (Factors({2: 2*x + 2}), Factors({8: 1})) 

591 

592 factor_terms can clean up such Rational-bases powers: 

593 

594 >>> from sympy import factor_terms 

595 >>> n, d = Factors(2**(2*x + 2)).div(S(8)) 

596 >>> n.as_expr()/d.as_expr() 

597 2**(2*x + 2)/8 

598 >>> factor_terms(_) 

599 2**(2*x)/2 

600 

601 """ 

602 quo, rem = dict(self.factors), {} 

603 

604 if not isinstance(other, Factors): 

605 other = Factors(other) 

606 if other.is_zero: 

607 raise ZeroDivisionError 

608 if self.is_zero: 

609 return (Factors(S.Zero), Factors()) 

610 

611 for factor, exp in other.factors.items(): 

612 if factor in quo: 

613 d = quo[factor] - exp 

614 if _isnumber(d): 

615 if d <= 0: 

616 del quo[factor] 

617 

618 if d >= 0: 

619 if d: 

620 quo[factor] = d 

621 

622 continue 

623 

624 exp = -d 

625 

626 else: 

627 r = quo[factor].extract_additively(exp) 

628 if r is not None: 

629 if r: 

630 quo[factor] = r 

631 else: # should be handled already 

632 del quo[factor] 

633 else: 

634 other_exp = exp 

635 sc, sa = quo[factor].as_coeff_Add() 

636 if sc: 

637 oc, oa = other_exp.as_coeff_Add() 

638 diff = sc - oc 

639 if diff > 0: 

640 quo[factor] -= oc 

641 other_exp = oa 

642 elif diff < 0: 

643 quo[factor] -= sc 

644 other_exp = oa - diff 

645 else: 

646 quo[factor] = sa 

647 other_exp = oa 

648 if other_exp: 

649 rem[factor] = other_exp 

650 else: 

651 assert factor not in rem 

652 continue 

653 

654 rem[factor] = exp 

655 

656 return Factors(quo), Factors(rem) 

657 

658 def quo(self, other): # Factors 

659 """Return numerator Factor of ``self / other``. 

660 

661 Examples 

662 ======== 

663 

664 >>> from sympy.core.exprtools import Factors 

665 >>> from sympy.abc import x, y, z 

666 >>> a = Factors((x*y**2).as_powers_dict()) 

667 >>> b = Factors((x*y/z).as_powers_dict()) 

668 >>> a.quo(b) # same as a/b 

669 Factors({y: 1}) 

670 """ 

671 return self.div(other)[0] 

672 

673 def rem(self, other): # Factors 

674 """Return denominator Factors of ``self / other``. 

675 

676 Examples 

677 ======== 

678 

679 >>> from sympy.core.exprtools import Factors 

680 >>> from sympy.abc import x, y, z 

681 >>> a = Factors((x*y**2).as_powers_dict()) 

682 >>> b = Factors((x*y/z).as_powers_dict()) 

683 >>> a.rem(b) 

684 Factors({z: -1}) 

685 >>> a.rem(a) 

686 Factors({}) 

687 """ 

688 return self.div(other)[1] 

689 

690 def pow(self, other): # Factors 

691 """Return self raised to a non-negative integer power. 

692 

693 Examples 

694 ======== 

695 

696 >>> from sympy.core.exprtools import Factors 

697 >>> from sympy.abc import x, y 

698 >>> a = Factors((x*y**2).as_powers_dict()) 

699 >>> a**2 

700 Factors({x: 2, y: 4}) 

701 

702 """ 

703 if isinstance(other, Factors): 

704 other = other.as_expr() 

705 if other.is_Integer: 

706 other = int(other) 

707 if isinstance(other, SYMPY_INTS) and other >= 0: 

708 factors = {} 

709 

710 if other: 

711 for factor, exp in self.factors.items(): 

712 factors[factor] = exp*other 

713 

714 return Factors(factors) 

715 else: 

716 raise ValueError("expected non-negative integer, got %s" % other) 

717 

718 def gcd(self, other): # Factors 

719 """Return Factors of ``gcd(self, other)``. The keys are 

720 the intersection of factors with the minimum exponent for 

721 each factor. 

722 

723 Examples 

724 ======== 

725 

726 >>> from sympy.core.exprtools import Factors 

727 >>> from sympy.abc import x, y, z 

728 >>> a = Factors((x*y**2).as_powers_dict()) 

729 >>> b = Factors((x*y/z).as_powers_dict()) 

730 >>> a.gcd(b) 

731 Factors({x: 1, y: 1}) 

732 """ 

733 if not isinstance(other, Factors): 

734 other = Factors(other) 

735 if other.is_zero: 

736 return Factors(self.factors) 

737 

738 factors = {} 

739 

740 for factor, exp in self.factors.items(): 

741 factor, exp = sympify(factor), sympify(exp) 

742 if factor in other.factors: 

743 lt = (exp - other.factors[factor]).is_negative 

744 if lt == True: 

745 factors[factor] = exp 

746 elif lt == False: 

747 factors[factor] = other.factors[factor] 

748 

749 return Factors(factors) 

750 

751 def lcm(self, other): # Factors 

752 """Return Factors of ``lcm(self, other)`` which are 

753 the union of factors with the maximum exponent for 

754 each factor. 

755 

756 Examples 

757 ======== 

758 

759 >>> from sympy.core.exprtools import Factors 

760 >>> from sympy.abc import x, y, z 

761 >>> a = Factors((x*y**2).as_powers_dict()) 

762 >>> b = Factors((x*y/z).as_powers_dict()) 

763 >>> a.lcm(b) 

764 Factors({x: 1, y: 2, z: -1}) 

765 """ 

766 if not isinstance(other, Factors): 

767 other = Factors(other) 

768 if any(f.is_zero for f in (self, other)): 

769 return Factors(S.Zero) 

770 

771 factors = dict(self.factors) 

772 

773 for factor, exp in other.factors.items(): 

774 if factor in factors: 

775 exp = max(exp, factors[factor]) 

776 

777 factors[factor] = exp 

778 

779 return Factors(factors) 

780 

781 def __mul__(self, other): # Factors 

782 return self.mul(other) 

783 

784 def __divmod__(self, other): # Factors 

785 return self.div(other) 

786 

787 def __truediv__(self, other): # Factors 

788 return self.quo(other) 

789 

790 def __mod__(self, other): # Factors 

791 return self.rem(other) 

792 

793 def __pow__(self, other): # Factors 

794 return self.pow(other) 

795 

796 def __eq__(self, other): # Factors 

797 if not isinstance(other, Factors): 

798 other = Factors(other) 

799 return self.factors == other.factors 

800 

801 def __ne__(self, other): # Factors 

802 return not self == other 

803 

804 

805class Term: 

806 """Efficient representation of ``coeff*(numer/denom)``. """ 

807 

808 __slots__ = ('coeff', 'numer', 'denom') 

809 

810 def __init__(self, term, numer=None, denom=None): # Term 

811 if numer is None and denom is None: 

812 if not term.is_commutative: 

813 raise NonCommutativeExpression( 

814 'commutative expression expected') 

815 

816 coeff, factors = term.as_coeff_mul() 

817 numer, denom = defaultdict(int), defaultdict(int) 

818 

819 for factor in factors: 

820 base, exp = decompose_power(factor) 

821 

822 if base.is_Add: 

823 cont, base = base.primitive() 

824 coeff *= cont**exp 

825 

826 if exp > 0: 

827 numer[base] += exp 

828 else: 

829 denom[base] += -exp 

830 

831 numer = Factors(numer) 

832 denom = Factors(denom) 

833 else: 

834 coeff = term 

835 

836 if numer is None: 

837 numer = Factors() 

838 

839 if denom is None: 

840 denom = Factors() 

841 

842 self.coeff = coeff 

843 self.numer = numer 

844 self.denom = denom 

845 

846 def __hash__(self): # Term 

847 return hash((self.coeff, self.numer, self.denom)) 

848 

849 def __repr__(self): # Term 

850 return "Term(%s, %s, %s)" % (self.coeff, self.numer, self.denom) 

851 

852 def as_expr(self): # Term 

853 return self.coeff*(self.numer.as_expr()/self.denom.as_expr()) 

854 

855 def mul(self, other): # Term 

856 coeff = self.coeff*other.coeff 

857 numer = self.numer.mul(other.numer) 

858 denom = self.denom.mul(other.denom) 

859 

860 numer, denom = numer.normal(denom) 

861 

862 return Term(coeff, numer, denom) 

863 

864 def inv(self): # Term 

865 return Term(1/self.coeff, self.denom, self.numer) 

866 

867 def quo(self, other): # Term 

868 return self.mul(other.inv()) 

869 

870 def pow(self, other): # Term 

871 if other < 0: 

872 return self.inv().pow(-other) 

873 else: 

874 return Term(self.coeff ** other, 

875 self.numer.pow(other), 

876 self.denom.pow(other)) 

877 

878 def gcd(self, other): # Term 

879 return Term(self.coeff.gcd(other.coeff), 

880 self.numer.gcd(other.numer), 

881 self.denom.gcd(other.denom)) 

882 

883 def lcm(self, other): # Term 

884 return Term(self.coeff.lcm(other.coeff), 

885 self.numer.lcm(other.numer), 

886 self.denom.lcm(other.denom)) 

887 

888 def __mul__(self, other): # Term 

889 if isinstance(other, Term): 

890 return self.mul(other) 

891 else: 

892 return NotImplemented 

893 

894 def __truediv__(self, other): # Term 

895 if isinstance(other, Term): 

896 return self.quo(other) 

897 else: 

898 return NotImplemented 

899 

900 def __pow__(self, other): # Term 

901 if isinstance(other, SYMPY_INTS): 

902 return self.pow(other) 

903 else: 

904 return NotImplemented 

905 

906 def __eq__(self, other): # Term 

907 return (self.coeff == other.coeff and 

908 self.numer == other.numer and 

909 self.denom == other.denom) 

910 

911 def __ne__(self, other): # Term 

912 return not self == other 

913 

914 

915def _gcd_terms(terms, isprimitive=False, fraction=True): 

916 """Helper function for :func:`gcd_terms`. 

917 

918 Parameters 

919 ========== 

920 

921 isprimitive : boolean, optional 

922 If ``isprimitive`` is True then the call to primitive 

923 for an Add will be skipped. This is useful when the 

924 content has already been extracted. 

925 

926 fraction : boolean, optional 

927 If ``fraction`` is True then the expression will appear over a common 

928 denominator, the lcm of all term denominators. 

929 """ 

930 

931 if isinstance(terms, Basic) and not isinstance(terms, Tuple): 

932 terms = Add.make_args(terms) 

933 

934 terms = list(map(Term, [t for t in terms if t])) 

935 

936 # there is some simplification that may happen if we leave this 

937 # here rather than duplicate it before the mapping of Term onto 

938 # the terms 

939 if len(terms) == 0: 

940 return S.Zero, S.Zero, S.One 

941 

942 if len(terms) == 1: 

943 cont = terms[0].coeff 

944 numer = terms[0].numer.as_expr() 

945 denom = terms[0].denom.as_expr() 

946 

947 else: 

948 cont = terms[0] 

949 for term in terms[1:]: 

950 cont = cont.gcd(term) 

951 

952 for i, term in enumerate(terms): 

953 terms[i] = term.quo(cont) 

954 

955 if fraction: 

956 denom = terms[0].denom 

957 

958 for term in terms[1:]: 

959 denom = denom.lcm(term.denom) 

960 

961 numers = [] 

962 for term in terms: 

963 numer = term.numer.mul(denom.quo(term.denom)) 

964 numers.append(term.coeff*numer.as_expr()) 

965 else: 

966 numers = [t.as_expr() for t in terms] 

967 denom = Term(S.One).numer 

968 

969 cont = cont.as_expr() 

970 numer = Add(*numers) 

971 denom = denom.as_expr() 

972 

973 if not isprimitive and numer.is_Add: 

974 _cont, numer = numer.primitive() 

975 cont *= _cont 

976 

977 return cont, numer, denom 

978 

979 

980def gcd_terms(terms, isprimitive=False, clear=True, fraction=True): 

981 """Compute the GCD of ``terms`` and put them together. 

982 

983 Parameters 

984 ========== 

985 

986 terms : Expr 

987 Can be an expression or a non-Basic sequence of expressions 

988 which will be handled as though they are terms from a sum. 

989 

990 isprimitive : bool, optional 

991 If ``isprimitive`` is True the _gcd_terms will not run the primitive 

992 method on the terms. 

993 

994 clear : bool, optional 

995 It controls the removal of integers from the denominator of an Add 

996 expression. When True (default), all numerical denominator will be cleared; 

997 when False the denominators will be cleared only if all terms had numerical 

998 denominators other than 1. 

999 

1000 fraction : bool, optional 

1001 When True (default), will put the expression over a common 

1002 denominator. 

1003 

1004 Examples 

1005 ======== 

1006 

1007 >>> from sympy import gcd_terms 

1008 >>> from sympy.abc import x, y 

1009 

1010 >>> gcd_terms((x + 1)**2*y + (x + 1)*y**2) 

1011 y*(x + 1)*(x + y + 1) 

1012 >>> gcd_terms(x/2 + 1) 

1013 (x + 2)/2 

1014 >>> gcd_terms(x/2 + 1, clear=False) 

1015 x/2 + 1 

1016 >>> gcd_terms(x/2 + y/2, clear=False) 

1017 (x + y)/2 

1018 >>> gcd_terms(x/2 + 1/x) 

1019 (x**2 + 2)/(2*x) 

1020 >>> gcd_terms(x/2 + 1/x, fraction=False) 

1021 (x + 2/x)/2 

1022 >>> gcd_terms(x/2 + 1/x, fraction=False, clear=False) 

1023 x/2 + 1/x 

1024 

1025 >>> gcd_terms(x/2/y + 1/x/y) 

1026 (x**2 + 2)/(2*x*y) 

1027 >>> gcd_terms(x/2/y + 1/x/y, clear=False) 

1028 (x**2/2 + 1)/(x*y) 

1029 >>> gcd_terms(x/2/y + 1/x/y, clear=False, fraction=False) 

1030 (x/2 + 1/x)/y 

1031 

1032 The ``clear`` flag was ignored in this case because the returned 

1033 expression was a rational expression, not a simple sum. 

1034 

1035 See Also 

1036 ======== 

1037 

1038 factor_terms, sympy.polys.polytools.terms_gcd 

1039 

1040 """ 

1041 def mask(terms): 

1042 """replace nc portions of each term with a unique Dummy symbols 

1043 and return the replacements to restore them""" 

1044 args = [(a, []) if a.is_commutative else a.args_cnc() for a in terms] 

1045 reps = [] 

1046 for i, (c, nc) in enumerate(args): 

1047 if nc: 

1048 nc = Mul(*nc) 

1049 d = Dummy() 

1050 reps.append((d, nc)) 

1051 c.append(d) 

1052 args[i] = Mul(*c) 

1053 else: 

1054 args[i] = c 

1055 return args, dict(reps) 

1056 

1057 isadd = isinstance(terms, Add) 

1058 addlike = isadd or not isinstance(terms, Basic) and \ 

1059 is_sequence(terms, include=set) and \ 

1060 not isinstance(terms, Dict) 

1061 

1062 if addlike: 

1063 if isadd: # i.e. an Add 

1064 terms = list(terms.args) 

1065 else: 

1066 terms = sympify(terms) 

1067 terms, reps = mask(terms) 

1068 cont, numer, denom = _gcd_terms(terms, isprimitive, fraction) 

1069 numer = numer.xreplace(reps) 

1070 coeff, factors = cont.as_coeff_Mul() 

1071 if not clear: 

1072 c, _coeff = coeff.as_coeff_Mul() 

1073 if not c.is_Integer and not clear and numer.is_Add: 

1074 n, d = c.as_numer_denom() 

1075 _numer = numer/d 

1076 if any(a.as_coeff_Mul()[0].is_Integer 

1077 for a in _numer.args): 

1078 numer = _numer 

1079 coeff = n*_coeff 

1080 return _keep_coeff(coeff, factors*numer/denom, clear=clear) 

1081 

1082 if not isinstance(terms, Basic): 

1083 return terms 

1084 

1085 if terms.is_Atom: 

1086 return terms 

1087 

1088 if terms.is_Mul: 

1089 c, args = terms.as_coeff_mul() 

1090 return _keep_coeff(c, Mul(*[gcd_terms(i, isprimitive, clear, fraction) 

1091 for i in args]), clear=clear) 

1092 

1093 def handle(a): 

1094 # don't treat internal args like terms of an Add 

1095 if not isinstance(a, Expr): 

1096 if isinstance(a, Basic): 

1097 if not a.args: 

1098 return a 

1099 return a.func(*[handle(i) for i in a.args]) 

1100 return type(a)([handle(i) for i in a]) 

1101 return gcd_terms(a, isprimitive, clear, fraction) 

1102 

1103 if isinstance(terms, Dict): 

1104 return Dict(*[(k, handle(v)) for k, v in terms.args]) 

1105 return terms.func(*[handle(i) for i in terms.args]) 

1106 

1107 

1108def _factor_sum_int(expr, **kwargs): 

1109 """Return Sum or Integral object with factors that are not 

1110 in the wrt variables removed. In cases where there are additive 

1111 terms in the function of the object that are independent, the 

1112 object will be separated into two objects. 

1113 

1114 Examples 

1115 ======== 

1116 

1117 >>> from sympy import Sum, factor_terms 

1118 >>> from sympy.abc import x, y 

1119 >>> factor_terms(Sum(x + y, (x, 1, 3))) 

1120 y*Sum(1, (x, 1, 3)) + Sum(x, (x, 1, 3)) 

1121 >>> factor_terms(Sum(x*y, (x, 1, 3))) 

1122 y*Sum(x, (x, 1, 3)) 

1123 

1124 Notes 

1125 ===== 

1126 

1127 If a function in the summand or integrand is replaced 

1128 with a symbol, then this simplification should not be 

1129 done or else an incorrect result will be obtained when 

1130 the symbol is replaced with an expression that depends 

1131 on the variables of summation/integration: 

1132 

1133 >>> eq = Sum(y, (x, 1, 3)) 

1134 >>> factor_terms(eq).subs(y, x).doit() 

1135 3*x 

1136 >>> eq.subs(y, x).doit() 

1137 6 

1138 """ 

1139 result = expr.function 

1140 if result == 0: 

1141 return S.Zero 

1142 limits = expr.limits 

1143 

1144 # get the wrt variables 

1145 wrt = {i.args[0] for i in limits} 

1146 

1147 # factor out any common terms that are independent of wrt 

1148 f = factor_terms(result, **kwargs) 

1149 i, d = f.as_independent(*wrt) 

1150 if isinstance(f, Add): 

1151 return i * expr.func(1, *limits) + expr.func(d, *limits) 

1152 else: 

1153 return i * expr.func(d, *limits) 

1154 

1155 

1156def factor_terms(expr, radical=False, clear=False, fraction=False, sign=True): 

1157 """Remove common factors from terms in all arguments without 

1158 changing the underlying structure of the expr. No expansion or 

1159 simplification (and no processing of non-commutatives) is performed. 

1160 

1161 Parameters 

1162 ========== 

1163 

1164 radical: bool, optional 

1165 If radical=True then a radical common to all terms will be factored 

1166 out of any Add sub-expressions of the expr. 

1167 

1168 clear : bool, optional 

1169 If clear=False (default) then coefficients will not be separated 

1170 from a single Add if they can be distributed to leave one or more 

1171 terms with integer coefficients. 

1172 

1173 fraction : bool, optional 

1174 If fraction=True (default is False) then a common denominator will be 

1175 constructed for the expression. 

1176 

1177 sign : bool, optional 

1178 If sign=True (default) then even if the only factor in common is a -1, 

1179 it will be factored out of the expression. 

1180 

1181 Examples 

1182 ======== 

1183 

1184 >>> from sympy import factor_terms, Symbol 

1185 >>> from sympy.abc import x, y 

1186 >>> factor_terms(x + x*(2 + 4*y)**3) 

1187 x*(8*(2*y + 1)**3 + 1) 

1188 >>> A = Symbol('A', commutative=False) 

1189 >>> factor_terms(x*A + x*A + x*y*A) 

1190 x*(y*A + 2*A) 

1191 

1192 When ``clear`` is False, a rational will only be factored out of an 

1193 Add expression if all terms of the Add have coefficients that are 

1194 fractions: 

1195 

1196 >>> factor_terms(x/2 + 1, clear=False) 

1197 x/2 + 1 

1198 >>> factor_terms(x/2 + 1, clear=True) 

1199 (x + 2)/2 

1200 

1201 If a -1 is all that can be factored out, to *not* factor it out, the 

1202 flag ``sign`` must be False: 

1203 

1204 >>> factor_terms(-x - y) 

1205 -(x + y) 

1206 >>> factor_terms(-x - y, sign=False) 

1207 -x - y 

1208 >>> factor_terms(-2*x - 2*y, sign=False) 

1209 -2*(x + y) 

1210 

1211 See Also 

1212 ======== 

1213 

1214 gcd_terms, sympy.polys.polytools.terms_gcd 

1215 

1216 """ 

1217 def do(expr): 

1218 from sympy.concrete.summations import Sum 

1219 from sympy.integrals.integrals import Integral 

1220 is_iterable = iterable(expr) 

1221 

1222 if not isinstance(expr, Basic) or expr.is_Atom: 

1223 if is_iterable: 

1224 return type(expr)([do(i) for i in expr]) 

1225 return expr 

1226 

1227 if expr.is_Pow or expr.is_Function or \ 

1228 is_iterable or not hasattr(expr, 'args_cnc'): 

1229 args = expr.args 

1230 newargs = tuple([do(i) for i in args]) 

1231 if newargs == args: 

1232 return expr 

1233 return expr.func(*newargs) 

1234 

1235 if isinstance(expr, (Sum, Integral)): 

1236 return _factor_sum_int(expr, 

1237 radical=radical, clear=clear, 

1238 fraction=fraction, sign=sign) 

1239 

1240 cont, p = expr.as_content_primitive(radical=radical, clear=clear) 

1241 if p.is_Add: 

1242 list_args = [do(a) for a in Add.make_args(p)] 

1243 # get a common negative (if there) which gcd_terms does not remove 

1244 if not any(a.as_coeff_Mul()[0].extract_multiplicatively(-1) is None 

1245 for a in list_args): 

1246 cont = -cont 

1247 list_args = [-a for a in list_args] 

1248 # watch out for exp(-(x+2)) which gcd_terms will change to exp(-x-2) 

1249 special = {} 

1250 for i, a in enumerate(list_args): 

1251 b, e = a.as_base_exp() 

1252 if e.is_Mul and e != Mul(*e.args): 

1253 list_args[i] = Dummy() 

1254 special[list_args[i]] = a 

1255 # rebuild p not worrying about the order which gcd_terms will fix 

1256 p = Add._from_args(list_args) 

1257 p = gcd_terms(p, 

1258 isprimitive=True, 

1259 clear=clear, 

1260 fraction=fraction).xreplace(special) 

1261 elif p.args: 

1262 p = p.func( 

1263 *[do(a) for a in p.args]) 

1264 rv = _keep_coeff(cont, p, clear=clear, sign=sign) 

1265 return rv 

1266 expr = sympify(expr) 

1267 return do(expr) 

1268 

1269 

1270def _mask_nc(eq, name=None): 

1271 """ 

1272 Return ``eq`` with non-commutative objects replaced with Dummy 

1273 symbols. A dictionary that can be used to restore the original 

1274 values is returned: if it is None, the expression is noncommutative 

1275 and cannot be made commutative. The third value returned is a list 

1276 of any non-commutative symbols that appear in the returned equation. 

1277 

1278 Explanation 

1279 =========== 

1280 

1281 All non-commutative objects other than Symbols are replaced with 

1282 a non-commutative Symbol. Identical objects will be identified 

1283 by identical symbols. 

1284 

1285 If there is only 1 non-commutative object in an expression it will 

1286 be replaced with a commutative symbol. Otherwise, the non-commutative 

1287 entities are retained and the calling routine should handle 

1288 replacements in this case since some care must be taken to keep 

1289 track of the ordering of symbols when they occur within Muls. 

1290 

1291 Parameters 

1292 ========== 

1293 

1294 name : str 

1295 ``name``, if given, is the name that will be used with numbered Dummy 

1296 variables that will replace the non-commutative objects and is mainly 

1297 used for doctesting purposes. 

1298 

1299 Examples 

1300 ======== 

1301 

1302 >>> from sympy.physics.secondquant import Commutator, NO, F, Fd 

1303 >>> from sympy import symbols 

1304 >>> from sympy.core.exprtools import _mask_nc 

1305 >>> from sympy.abc import x, y 

1306 >>> A, B, C = symbols('A,B,C', commutative=False) 

1307 

1308 One nc-symbol: 

1309 

1310 >>> _mask_nc(A**2 - x**2, 'd') 

1311 (_d0**2 - x**2, {_d0: A}, []) 

1312 

1313 Multiple nc-symbols: 

1314 

1315 >>> _mask_nc(A**2 - B**2, 'd') 

1316 (A**2 - B**2, {}, [A, B]) 

1317 

1318 An nc-object with nc-symbols but no others outside of it: 

1319 

1320 >>> _mask_nc(1 + x*Commutator(A, B), 'd') 

1321 (_d0*x + 1, {_d0: Commutator(A, B)}, []) 

1322 >>> _mask_nc(NO(Fd(x)*F(y)), 'd') 

1323 (_d0, {_d0: NO(CreateFermion(x)*AnnihilateFermion(y))}, []) 

1324 

1325 Multiple nc-objects: 

1326 

1327 >>> eq = x*Commutator(A, B) + x*Commutator(A, C)*Commutator(A, B) 

1328 >>> _mask_nc(eq, 'd') 

1329 (x*_d0 + x*_d1*_d0, {_d0: Commutator(A, B), _d1: Commutator(A, C)}, [_d0, _d1]) 

1330 

1331 Multiple nc-objects and nc-symbols: 

1332 

1333 >>> eq = A*Commutator(A, B) + B*Commutator(A, C) 

1334 >>> _mask_nc(eq, 'd') 

1335 (A*_d0 + B*_d1, {_d0: Commutator(A, B), _d1: Commutator(A, C)}, [_d0, _d1, A, B]) 

1336 

1337 """ 

1338 name = name or 'mask' 

1339 # Make Dummy() append sequential numbers to the name 

1340 

1341 def numbered_names(): 

1342 i = 0 

1343 while True: 

1344 yield name + str(i) 

1345 i += 1 

1346 

1347 names = numbered_names() 

1348 

1349 def Dummy(*args, **kwargs): 

1350 from .symbol import Dummy 

1351 return Dummy(next(names), *args, **kwargs) 

1352 

1353 expr = eq 

1354 if expr.is_commutative: 

1355 return eq, {}, [] 

1356 

1357 # identify nc-objects; symbols and other 

1358 rep = [] 

1359 nc_obj = set() 

1360 nc_syms = set() 

1361 pot = preorder_traversal(expr, keys=default_sort_key) 

1362 for i, a in enumerate(pot): 

1363 if any(a == r[0] for r in rep): 

1364 pot.skip() 

1365 elif not a.is_commutative: 

1366 if a.is_symbol: 

1367 nc_syms.add(a) 

1368 pot.skip() 

1369 elif not (a.is_Add or a.is_Mul or a.is_Pow): 

1370 nc_obj.add(a) 

1371 pot.skip() 

1372 

1373 # If there is only one nc symbol or object, it can be factored regularly 

1374 # but polys is going to complain, so replace it with a Dummy. 

1375 if len(nc_obj) == 1 and not nc_syms: 

1376 rep.append((nc_obj.pop(), Dummy())) 

1377 elif len(nc_syms) == 1 and not nc_obj: 

1378 rep.append((nc_syms.pop(), Dummy())) 

1379 

1380 # Any remaining nc-objects will be replaced with an nc-Dummy and 

1381 # identified as an nc-Symbol to watch out for 

1382 nc_obj = sorted(nc_obj, key=default_sort_key) 

1383 for n in nc_obj: 

1384 nc = Dummy(commutative=False) 

1385 rep.append((n, nc)) 

1386 nc_syms.add(nc) 

1387 expr = expr.subs(rep) 

1388 

1389 nc_syms = list(nc_syms) 

1390 nc_syms.sort(key=default_sort_key) 

1391 return expr, {v: k for k, v in rep}, nc_syms 

1392 

1393 

1394def factor_nc(expr): 

1395 """Return the factored form of ``expr`` while handling non-commutative 

1396 expressions. 

1397 

1398 Examples 

1399 ======== 

1400 

1401 >>> from sympy import factor_nc, Symbol 

1402 >>> from sympy.abc import x 

1403 >>> A = Symbol('A', commutative=False) 

1404 >>> B = Symbol('B', commutative=False) 

1405 >>> factor_nc((x**2 + 2*A*x + A**2).expand()) 

1406 (x + A)**2 

1407 >>> factor_nc(((x + A)*(x + B)).expand()) 

1408 (x + A)*(x + B) 

1409 """ 

1410 expr = sympify(expr) 

1411 if not isinstance(expr, Expr) or not expr.args: 

1412 return expr 

1413 if not expr.is_Add: 

1414 return expr.func(*[factor_nc(a) for a in expr.args]) 

1415 expr = expr.func(*[expand_power_exp(i) for i in expr.args]) 

1416 

1417 from sympy.polys.polytools import gcd, factor 

1418 expr, rep, nc_symbols = _mask_nc(expr) 

1419 

1420 if rep: 

1421 return factor(expr).subs(rep) 

1422 else: 

1423 args = [a.args_cnc() for a in Add.make_args(expr)] 

1424 c = g = l = r = S.One 

1425 hit = False 

1426 # find any commutative gcd term 

1427 for i, a in enumerate(args): 

1428 if i == 0: 

1429 c = Mul._from_args(a[0]) 

1430 elif a[0]: 

1431 c = gcd(c, Mul._from_args(a[0])) 

1432 else: 

1433 c = S.One 

1434 if c is not S.One: 

1435 hit = True 

1436 c, g = c.as_coeff_Mul() 

1437 if g is not S.One: 

1438 for i, (cc, _) in enumerate(args): 

1439 cc = list(Mul.make_args(Mul._from_args(list(cc))/g)) 

1440 args[i][0] = cc 

1441 for i, (cc, _) in enumerate(args): 

1442 if cc: 

1443 cc[0] = cc[0]/c 

1444 else: 

1445 cc = [1/c] 

1446 args[i][0] = cc 

1447 # find any noncommutative common prefix 

1448 for i, a in enumerate(args): 

1449 if i == 0: 

1450 n = a[1][:] 

1451 else: 

1452 n = common_prefix(n, a[1]) 

1453 if not n: 

1454 # is there a power that can be extracted? 

1455 if not args[0][1]: 

1456 break 

1457 b, e = args[0][1][0].as_base_exp() 

1458 ok = False 

1459 if e.is_Integer: 

1460 for t in args: 

1461 if not t[1]: 

1462 break 

1463 bt, et = t[1][0].as_base_exp() 

1464 if et.is_Integer and bt == b: 

1465 e = min(e, et) 

1466 else: 

1467 break 

1468 else: 

1469 ok = hit = True 

1470 l = b**e 

1471 il = b**-e 

1472 for _ in args: 

1473 _[1][0] = il*_[1][0] 

1474 break 

1475 if not ok: 

1476 break 

1477 else: 

1478 hit = True 

1479 lenn = len(n) 

1480 l = Mul(*n) 

1481 for _ in args: 

1482 _[1] = _[1][lenn:] 

1483 # find any noncommutative common suffix 

1484 for i, a in enumerate(args): 

1485 if i == 0: 

1486 n = a[1][:] 

1487 else: 

1488 n = common_suffix(n, a[1]) 

1489 if not n: 

1490 # is there a power that can be extracted? 

1491 if not args[0][1]: 

1492 break 

1493 b, e = args[0][1][-1].as_base_exp() 

1494 ok = False 

1495 if e.is_Integer: 

1496 for t in args: 

1497 if not t[1]: 

1498 break 

1499 bt, et = t[1][-1].as_base_exp() 

1500 if et.is_Integer and bt == b: 

1501 e = min(e, et) 

1502 else: 

1503 break 

1504 else: 

1505 ok = hit = True 

1506 r = b**e 

1507 il = b**-e 

1508 for _ in args: 

1509 _[1][-1] = _[1][-1]*il 

1510 break 

1511 if not ok: 

1512 break 

1513 else: 

1514 hit = True 

1515 lenn = len(n) 

1516 r = Mul(*n) 

1517 for _ in args: 

1518 _[1] = _[1][:len(_[1]) - lenn] 

1519 if hit: 

1520 mid = Add(*[Mul(*cc)*Mul(*nc) for cc, nc in args]) 

1521 else: 

1522 mid = expr 

1523 

1524 from sympy.simplify.powsimp import powsimp 

1525 

1526 # sort the symbols so the Dummys would appear in the same 

1527 # order as the original symbols, otherwise you may introduce 

1528 # a factor of -1, e.g. A**2 - B**2) -- {A:y, B:x} --> y**2 - x**2 

1529 # and the former factors into two terms, (A - B)*(A + B) while the 

1530 # latter factors into 3 terms, (-1)*(x - y)*(x + y) 

1531 rep1 = [(n, Dummy()) for n in sorted(nc_symbols, key=default_sort_key)] 

1532 unrep1 = [(v, k) for k, v in rep1] 

1533 unrep1.reverse() 

1534 new_mid, r2, _ = _mask_nc(mid.subs(rep1)) 

1535 new_mid = powsimp(factor(new_mid)) 

1536 

1537 new_mid = new_mid.subs(r2).subs(unrep1) 

1538 

1539 if new_mid.is_Pow: 

1540 return _keep_coeff(c, g*l*new_mid*r) 

1541 

1542 if new_mid.is_Mul: 

1543 def _pemexpand(expr): 

1544 "Expand with the minimal set of hints necessary to check the result." 

1545 return expr.expand(deep=True, mul=True, power_exp=True, 

1546 power_base=False, basic=False, multinomial=True, log=False) 

1547 # XXX TODO there should be a way to inspect what order the terms 

1548 # must be in and just select the plausible ordering without 

1549 # checking permutations 

1550 cfac = [] 

1551 ncfac = [] 

1552 for f in new_mid.args: 

1553 if f.is_commutative: 

1554 cfac.append(f) 

1555 else: 

1556 b, e = f.as_base_exp() 

1557 if e.is_Integer: 

1558 ncfac.extend([b]*e) 

1559 else: 

1560 ncfac.append(f) 

1561 pre_mid = g*Mul(*cfac)*l 

1562 target = _pemexpand(expr/c) 

1563 for s in variations(ncfac, len(ncfac)): 

1564 ok = pre_mid*Mul(*s)*r 

1565 if _pemexpand(ok) == target: 

1566 return _keep_coeff(c, ok) 

1567 

1568 # mid was an Add that didn't factor successfully 

1569 return _keep_coeff(c, g*l*mid*r)