Coverage for /usr/lib/python3/dist-packages/sympy/solvers/solvers.py: 4%

1622 statements  

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

1""" 

2This module contain solvers for all kinds of equations: 

3 

4 - algebraic or transcendental, use solve() 

5 

6 - recurrence, use rsolve() 

7 

8 - differential, use dsolve() 

9 

10 - nonlinear (numerically), use nsolve() 

11 (you will need a good starting point) 

12 

13""" 

14from __future__ import annotations 

15 

16from sympy.core import (S, Add, Symbol, Dummy, Expr, Mul) 

17from sympy.core.assumptions import check_assumptions 

18from sympy.core.exprtools import factor_terms 

19from sympy.core.function import (expand_mul, expand_log, Derivative, 

20 AppliedUndef, UndefinedFunction, nfloat, 

21 Function, expand_power_exp, _mexpand, expand, 

22 expand_func) 

23from sympy.core.logic import fuzzy_not 

24from sympy.core.numbers import ilcm, Float, Rational, _illegal 

25from sympy.core.power import integer_log, Pow 

26from sympy.core.relational import Eq, Ne 

27from sympy.core.sorting import ordered, default_sort_key 

28from sympy.core.sympify import sympify, _sympify 

29from sympy.core.traversal import preorder_traversal 

30from sympy.logic.boolalg import And, BooleanAtom 

31 

32from sympy.functions import (log, exp, LambertW, cos, sin, tan, acos, asin, atan, 

33 Abs, re, im, arg, sqrt, atan2) 

34from sympy.functions.combinatorial.factorials import binomial 

35from sympy.functions.elementary.hyperbolic import HyperbolicFunction 

36from sympy.functions.elementary.piecewise import piecewise_fold, Piecewise 

37from sympy.functions.elementary.trigonometric import TrigonometricFunction 

38from sympy.integrals.integrals import Integral 

39from sympy.ntheory.factor_ import divisors 

40from sympy.simplify import (simplify, collect, powsimp, posify, # type: ignore 

41 powdenest, nsimplify, denom, logcombine, sqrtdenest, fraction, 

42 separatevars) 

43from sympy.simplify.sqrtdenest import sqrt_depth 

44from sympy.simplify.fu import TR1, TR2i 

45from sympy.matrices.common import NonInvertibleMatrixError 

46from sympy.matrices import Matrix, zeros 

47from sympy.polys import roots, cancel, factor, Poly 

48from sympy.polys.polyerrors import GeneratorsNeeded, PolynomialError 

49from sympy.polys.solvers import sympy_eqs_to_ring, solve_lin_sys 

50from sympy.utilities.lambdify import lambdify 

51from sympy.utilities.misc import filldedent, debugf 

52from sympy.utilities.iterables import (connected_components, 

53 generate_bell, uniq, iterable, is_sequence, subsets, flatten) 

54from sympy.utilities.decorator import conserve_mpmath_dps 

55 

56from mpmath import findroot 

57 

58from sympy.solvers.polysys import solve_poly_system 

59 

60from types import GeneratorType 

61from collections import defaultdict 

62from itertools import combinations, product 

63 

64import warnings 

65 

66 

67def recast_to_symbols(eqs, symbols): 

68 """ 

69 Return (e, s, d) where e and s are versions of *eqs* and 

70 *symbols* in which any non-Symbol objects in *symbols* have 

71 been replaced with generic Dummy symbols and d is a dictionary 

72 that can be used to restore the original expressions. 

73 

74 Examples 

75 ======== 

76 

77 >>> from sympy.solvers.solvers import recast_to_symbols 

78 >>> from sympy import symbols, Function 

79 >>> x, y = symbols('x y') 

80 >>> fx = Function('f')(x) 

81 >>> eqs, syms = [fx + 1, x, y], [fx, y] 

82 >>> e, s, d = recast_to_symbols(eqs, syms); (e, s, d) 

83 ([_X0 + 1, x, y], [_X0, y], {_X0: f(x)}) 

84 

85 The original equations and symbols can be restored using d: 

86 

87 >>> assert [i.xreplace(d) for i in eqs] == eqs 

88 >>> assert [d.get(i, i) for i in s] == syms 

89 

90 """ 

91 if not iterable(eqs) and iterable(symbols): 

92 raise ValueError('Both eqs and symbols must be iterable') 

93 orig = list(symbols) 

94 symbols = list(ordered(symbols)) 

95 swap_sym = {} 

96 i = 0 

97 for j, s in enumerate(symbols): 

98 if not isinstance(s, Symbol) and s not in swap_sym: 

99 swap_sym[s] = Dummy('X%d' % i) 

100 i += 1 

101 new_f = [] 

102 for i in eqs: 

103 isubs = getattr(i, 'subs', None) 

104 if isubs is not None: 

105 new_f.append(isubs(swap_sym)) 

106 else: 

107 new_f.append(i) 

108 restore = {v: k for k, v in swap_sym.items()} 

109 return new_f, [swap_sym.get(i, i) for i in orig], restore 

110 

111 

112def _ispow(e): 

113 """Return True if e is a Pow or is exp.""" 

114 return isinstance(e, Expr) and (e.is_Pow or isinstance(e, exp)) 

115 

116 

117def _simple_dens(f, symbols): 

118 # when checking if a denominator is zero, we can just check the 

119 # base of powers with nonzero exponents since if the base is zero 

120 # the power will be zero, too. To keep it simple and fast, we 

121 # limit simplification to exponents that are Numbers 

122 dens = set() 

123 for d in denoms(f, symbols): 

124 if d.is_Pow and d.exp.is_Number: 

125 if d.exp.is_zero: 

126 continue # foo**0 is never 0 

127 d = d.base 

128 dens.add(d) 

129 return dens 

130 

131 

132def denoms(eq, *symbols): 

133 """ 

134 Return (recursively) set of all denominators that appear in *eq* 

135 that contain any symbol in *symbols*; if *symbols* are not 

136 provided then all denominators will be returned. 

137 

138 Examples 

139 ======== 

140 

141 >>> from sympy.solvers.solvers import denoms 

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

143 

144 >>> denoms(x/y) 

145 {y} 

146 

147 >>> denoms(x/(y*z)) 

148 {y, z} 

149 

150 >>> denoms(3/x + y/z) 

151 {x, z} 

152 

153 >>> denoms(x/2 + y/z) 

154 {2, z} 

155 

156 If *symbols* are provided then only denominators containing 

157 those symbols will be returned: 

158 

159 >>> denoms(1/x + 1/y + 1/z, y, z) 

160 {y, z} 

161 

162 """ 

163 

164 pot = preorder_traversal(eq) 

165 dens = set() 

166 for p in pot: 

167 # Here p might be Tuple or Relational 

168 # Expr subtrees (e.g. lhs and rhs) will be traversed after by pot 

169 if not isinstance(p, Expr): 

170 continue 

171 den = denom(p) 

172 if den is S.One: 

173 continue 

174 for d in Mul.make_args(den): 

175 dens.add(d) 

176 if not symbols: 

177 return dens 

178 elif len(symbols) == 1: 

179 if iterable(symbols[0]): 

180 symbols = symbols[0] 

181 return {d for d in dens if any(s in d.free_symbols for s in symbols)} 

182 

183 

184def checksol(f, symbol, sol=None, **flags): 

185 """ 

186 Checks whether sol is a solution of equation f == 0. 

187 

188 Explanation 

189 =========== 

190 

191 Input can be either a single symbol and corresponding value 

192 or a dictionary of symbols and values. When given as a dictionary 

193 and flag ``simplify=True``, the values in the dictionary will be 

194 simplified. *f* can be a single equation or an iterable of equations. 

195 A solution must satisfy all equations in *f* to be considered valid; 

196 if a solution does not satisfy any equation, False is returned; if one or 

197 more checks are inconclusive (and none are False) then None is returned. 

198 

199 Examples 

200 ======== 

201 

202 >>> from sympy import checksol, symbols 

203 >>> x, y = symbols('x,y') 

204 >>> checksol(x**4 - 1, x, 1) 

205 True 

206 >>> checksol(x**4 - 1, x, 0) 

207 False 

208 >>> checksol(x**2 + y**2 - 5**2, {x: 3, y: 4}) 

209 True 

210 

211 To check if an expression is zero using ``checksol()``, pass it 

212 as *f* and send an empty dictionary for *symbol*: 

213 

214 >>> checksol(x**2 + x - x*(x + 1), {}) 

215 True 

216 

217 None is returned if ``checksol()`` could not conclude. 

218 

219 flags: 

220 'numerical=True (default)' 

221 do a fast numerical check if ``f`` has only one symbol. 

222 'minimal=True (default is False)' 

223 a very fast, minimal testing. 

224 'warn=True (default is False)' 

225 show a warning if checksol() could not conclude. 

226 'simplify=True (default)' 

227 simplify solution before substituting into function and 

228 simplify the function before trying specific simplifications 

229 'force=True (default is False)' 

230 make positive all symbols without assumptions regarding sign. 

231 

232 """ 

233 from sympy.physics.units import Unit 

234 

235 minimal = flags.get('minimal', False) 

236 

237 if sol is not None: 

238 sol = {symbol: sol} 

239 elif isinstance(symbol, dict): 

240 sol = symbol 

241 else: 

242 msg = 'Expecting (sym, val) or ({sym: val}, None) but got (%s, %s)' 

243 raise ValueError(msg % (symbol, sol)) 

244 

245 if iterable(f): 

246 if not f: 

247 raise ValueError('no functions to check') 

248 rv = True 

249 for fi in f: 

250 check = checksol(fi, sol, **flags) 

251 if check: 

252 continue 

253 if check is False: 

254 return False 

255 rv = None # don't return, wait to see if there's a False 

256 return rv 

257 

258 f = _sympify(f) 

259 

260 if f.is_number: 

261 return f.is_zero 

262 

263 if isinstance(f, Poly): 

264 f = f.as_expr() 

265 elif isinstance(f, (Eq, Ne)): 

266 if f.rhs in (S.true, S.false): 

267 f = f.reversed 

268 B, E = f.args 

269 if isinstance(B, BooleanAtom): 

270 f = f.subs(sol) 

271 if not f.is_Boolean: 

272 return 

273 else: 

274 f = f.rewrite(Add, evaluate=False, deep=False) 

275 

276 if isinstance(f, BooleanAtom): 

277 return bool(f) 

278 elif not f.is_Relational and not f: 

279 return True 

280 

281 illegal = set(_illegal) 

282 if any(sympify(v).atoms() & illegal for k, v in sol.items()): 

283 return False 

284 

285 attempt = -1 

286 numerical = flags.get('numerical', True) 

287 while 1: 

288 attempt += 1 

289 if attempt == 0: 

290 val = f.subs(sol) 

291 if isinstance(val, Mul): 

292 val = val.as_independent(Unit)[0] 

293 if val.atoms() & illegal: 

294 return False 

295 elif attempt == 1: 

296 if not val.is_number: 

297 if not val.is_constant(*list(sol.keys()), simplify=not minimal): 

298 return False 

299 # there are free symbols -- simple expansion might work 

300 _, val = val.as_content_primitive() 

301 val = _mexpand(val.as_numer_denom()[0], recursive=True) 

302 elif attempt == 2: 

303 if minimal: 

304 return 

305 if flags.get('simplify', True): 

306 for k in sol: 

307 sol[k] = simplify(sol[k]) 

308 # start over without the failed expanded form, possibly 

309 # with a simplified solution 

310 val = simplify(f.subs(sol)) 

311 if flags.get('force', True): 

312 val, reps = posify(val) 

313 # expansion may work now, so try again and check 

314 exval = _mexpand(val, recursive=True) 

315 if exval.is_number: 

316 # we can decide now 

317 val = exval 

318 else: 

319 # if there are no radicals and no functions then this can't be 

320 # zero anymore -- can it? 

321 pot = preorder_traversal(expand_mul(val)) 

322 seen = set() 

323 saw_pow_func = False 

324 for p in pot: 

325 if p in seen: 

326 continue 

327 seen.add(p) 

328 if p.is_Pow and not p.exp.is_Integer: 

329 saw_pow_func = True 

330 elif p.is_Function: 

331 saw_pow_func = True 

332 elif isinstance(p, UndefinedFunction): 

333 saw_pow_func = True 

334 if saw_pow_func: 

335 break 

336 if saw_pow_func is False: 

337 return False 

338 if flags.get('force', True): 

339 # don't do a zero check with the positive assumptions in place 

340 val = val.subs(reps) 

341 nz = fuzzy_not(val.is_zero) 

342 if nz is not None: 

343 # issue 5673: nz may be True even when False 

344 # so these are just hacks to keep a false positive 

345 # from being returned 

346 

347 # HACK 1: LambertW (issue 5673) 

348 if val.is_number and val.has(LambertW): 

349 # don't eval this to verify solution since if we got here, 

350 # numerical must be False 

351 return None 

352 

353 # add other HACKs here if necessary, otherwise we assume 

354 # the nz value is correct 

355 return not nz 

356 break 

357 if val.is_Rational: 

358 return val == 0 

359 if numerical and val.is_number: 

360 return (abs(val.n(18).n(12, chop=True)) < 1e-9) is S.true 

361 

362 if flags.get('warn', False): 

363 warnings.warn("\n\tWarning: could not verify solution %s." % sol) 

364 # returns None if it can't conclude 

365 # TODO: improve solution testing 

366 

367 

368def solve(f, *symbols, **flags): 

369 r""" 

370 Algebraically solves equations and systems of equations. 

371 

372 Explanation 

373 =========== 

374 

375 Currently supported: 

376 - polynomial 

377 - transcendental 

378 - piecewise combinations of the above 

379 - systems of linear and polynomial equations 

380 - systems containing relational expressions 

381 - systems implied by undetermined coefficients 

382 

383 Examples 

384 ======== 

385 

386 The default output varies according to the input and might 

387 be a list (possibly empty), a dictionary, a list of 

388 dictionaries or tuples, or an expression involving relationals. 

389 For specifics regarding different forms of output that may appear, see :ref:`solve_output`. 

390 Let it suffice here to say that to obtain a uniform output from 

391 `solve` use ``dict=True`` or ``set=True`` (see below). 

392 

393 >>> from sympy import solve, Poly, Eq, Matrix, Symbol 

394 >>> from sympy.abc import x, y, z, a, b 

395 

396 The expressions that are passed can be Expr, Equality, or Poly 

397 classes (or lists of the same); a Matrix is considered to be a 

398 list of all the elements of the matrix: 

399 

400 >>> solve(x - 3, x) 

401 [3] 

402 >>> solve(Eq(x, 3), x) 

403 [3] 

404 >>> solve(Poly(x - 3), x) 

405 [3] 

406 >>> solve(Matrix([[x, x + y]]), x, y) == solve([x, x + y], x, y) 

407 True 

408 

409 If no symbols are indicated to be of interest and the equation is 

410 univariate, a list of values is returned; otherwise, the keys in 

411 a dictionary will indicate which (of all the variables used in 

412 the expression(s)) variables and solutions were found: 

413 

414 >>> solve(x**2 - 4) 

415 [-2, 2] 

416 >>> solve((x - a)*(y - b)) 

417 [{a: x}, {b: y}] 

418 >>> solve([x - 3, y - 1]) 

419 {x: 3, y: 1} 

420 >>> solve([x - 3, y**2 - 1]) 

421 [{x: 3, y: -1}, {x: 3, y: 1}] 

422 

423 If you pass symbols for which solutions are sought, the output will vary 

424 depending on the number of symbols you passed, whether you are passing 

425 a list of expressions or not, and whether a linear system was solved. 

426 Uniform output is attained by using ``dict=True`` or ``set=True``. 

427 

428 >>> #### *** feel free to skip to the stars below *** #### 

429 >>> from sympy import TableForm 

430 >>> h = [None, ';|;'.join(['e', 's', 'solve(e, s)', 'solve(e, s, dict=True)', 

431 ... 'solve(e, s, set=True)']).split(';')] 

432 >>> t = [] 

433 >>> for e, s in [ 

434 ... (x - y, y), 

435 ... (x - y, [x, y]), 

436 ... (x**2 - y, [x, y]), 

437 ... ([x - 3, y -1], [x, y]), 

438 ... ]: 

439 ... how = [{}, dict(dict=True), dict(set=True)] 

440 ... res = [solve(e, s, **f) for f in how] 

441 ... t.append([e, '|', s, '|'] + [res[0], '|', res[1], '|', res[2]]) 

442 ... 

443 >>> # ******************************************************* # 

444 >>> TableForm(t, headings=h, alignments="<") 

445 e | s | solve(e, s) | solve(e, s, dict=True) | solve(e, s, set=True) 

446 --------------------------------------------------------------------------------------- 

447 x - y | y | [x] | [{y: x}] | ([y], {(x,)}) 

448 x - y | [x, y] | [(y, y)] | [{x: y}] | ([x, y], {(y, y)}) 

449 x**2 - y | [x, y] | [(x, x**2)] | [{y: x**2}] | ([x, y], {(x, x**2)}) 

450 [x - 3, y - 1] | [x, y] | {x: 3, y: 1} | [{x: 3, y: 1}] | ([x, y], {(3, 1)}) 

451 

452 * If any equation does not depend on the symbol(s) given, it will be 

453 eliminated from the equation set and an answer may be given 

454 implicitly in terms of variables that were not of interest: 

455 

456 >>> solve([x - y, y - 3], x) 

457 {x: y} 

458 

459 When you pass all but one of the free symbols, an attempt 

460 is made to find a single solution based on the method of 

461 undetermined coefficients. If it succeeds, a dictionary of values 

462 is returned. If you want an algebraic solutions for one 

463 or more of the symbols, pass the expression to be solved in a list: 

464 

465 >>> e = a*x + b - 2*x - 3 

466 >>> solve(e, [a, b]) 

467 {a: 2, b: 3} 

468 >>> solve([e], [a, b]) 

469 {a: -b/x + (2*x + 3)/x} 

470 

471 When there is no solution for any given symbol which will make all 

472 expressions zero, the empty list is returned (or an empty set in 

473 the tuple when ``set=True``): 

474 

475 >>> from sympy import sqrt 

476 >>> solve(3, x) 

477 [] 

478 >>> solve(x - 3, y) 

479 [] 

480 >>> solve(sqrt(x) + 1, x, set=True) 

481 ([x], set()) 

482 

483 When an object other than a Symbol is given as a symbol, it is 

484 isolated algebraically and an implicit solution may be obtained. 

485 This is mostly provided as a convenience to save you from replacing 

486 the object with a Symbol and solving for that Symbol. It will only 

487 work if the specified object can be replaced with a Symbol using the 

488 subs method: 

489 

490 >>> from sympy import exp, Function 

491 >>> f = Function('f') 

492 

493 >>> solve(f(x) - x, f(x)) 

494 [x] 

495 >>> solve(f(x).diff(x) - f(x) - x, f(x).diff(x)) 

496 [x + f(x)] 

497 >>> solve(f(x).diff(x) - f(x) - x, f(x)) 

498 [-x + Derivative(f(x), x)] 

499 >>> solve(x + exp(x)**2, exp(x), set=True) 

500 ([exp(x)], {(-sqrt(-x),), (sqrt(-x),)}) 

501 

502 >>> from sympy import Indexed, IndexedBase, Tuple 

503 >>> A = IndexedBase('A') 

504 >>> eqs = Tuple(A[1] + A[2] - 3, A[1] - A[2] + 1) 

505 >>> solve(eqs, eqs.atoms(Indexed)) 

506 {A[1]: 1, A[2]: 2} 

507 

508 * To solve for a function within a derivative, use :func:`~.dsolve`. 

509 

510 To solve for a symbol implicitly, use implicit=True: 

511 

512 >>> solve(x + exp(x), x) 

513 [-LambertW(1)] 

514 >>> solve(x + exp(x), x, implicit=True) 

515 [-exp(x)] 

516 

517 It is possible to solve for anything in an expression that can be 

518 replaced with a symbol using :obj:`~sympy.core.basic.Basic.subs`: 

519 

520 >>> solve(x + 2 + sqrt(3), x + 2) 

521 [-sqrt(3)] 

522 >>> solve((x + 2 + sqrt(3), x + 4 + y), y, x + 2) 

523 {y: -2 + sqrt(3), x + 2: -sqrt(3)} 

524 

525 * Nothing heroic is done in this implicit solving so you may end up 

526 with a symbol still in the solution: 

527 

528 >>> eqs = (x*y + 3*y + sqrt(3), x + 4 + y) 

529 >>> solve(eqs, y, x + 2) 

530 {y: -sqrt(3)/(x + 3), x + 2: -2*x/(x + 3) - 6/(x + 3) + sqrt(3)/(x + 3)} 

531 >>> solve(eqs, y*x, x) 

532 {x: -y - 4, x*y: -3*y - sqrt(3)} 

533 

534 * If you attempt to solve for a number, remember that the number 

535 you have obtained does not necessarily mean that the value is 

536 equivalent to the expression obtained: 

537 

538 >>> solve(sqrt(2) - 1, 1) 

539 [sqrt(2)] 

540 >>> solve(x - y + 1, 1) # /!\ -1 is targeted, too 

541 [x/(y - 1)] 

542 >>> [_.subs(z, -1) for _ in solve((x - y + 1).subs(-1, z), 1)] 

543 [-x + y] 

544 

545 **Additional Examples** 

546 

547 ``solve()`` with check=True (default) will run through the symbol tags to 

548 eliminate unwanted solutions. If no assumptions are included, all possible 

549 solutions will be returned: 

550 

551 >>> x = Symbol("x") 

552 >>> solve(x**2 - 1) 

553 [-1, 1] 

554 

555 By setting the ``positive`` flag, only one solution will be returned: 

556 

557 >>> pos = Symbol("pos", positive=True) 

558 >>> solve(pos**2 - 1) 

559 [1] 

560 

561 When the solutions are checked, those that make any denominator zero 

562 are automatically excluded. If you do not want to exclude such solutions, 

563 then use the check=False option: 

564 

565 >>> from sympy import sin, limit 

566 >>> solve(sin(x)/x) # 0 is excluded 

567 [pi] 

568 

569 If ``check=False``, then a solution to the numerator being zero is found 

570 but the value of $x = 0$ is a spurious solution since $\sin(x)/x$ has the well 

571 known limit (without discontinuity) of 1 at $x = 0$: 

572 

573 >>> solve(sin(x)/x, check=False) 

574 [0, pi] 

575 

576 In the following case, however, the limit exists and is equal to the 

577 value of $x = 0$ that is excluded when check=True: 

578 

579 >>> eq = x**2*(1/x - z**2/x) 

580 >>> solve(eq, x) 

581 [] 

582 >>> solve(eq, x, check=False) 

583 [0] 

584 >>> limit(eq, x, 0, '-') 

585 0 

586 >>> limit(eq, x, 0, '+') 

587 0 

588 

589 **Solving Relationships** 

590 

591 When one or more expressions passed to ``solve`` is a relational, 

592 a relational result is returned (and the ``dict`` and ``set`` flags 

593 are ignored): 

594 

595 >>> solve(x < 3) 

596 (-oo < x) & (x < 3) 

597 >>> solve([x < 3, x**2 > 4], x) 

598 ((-oo < x) & (x < -2)) | ((2 < x) & (x < 3)) 

599 >>> solve([x + y - 3, x > 3], x) 

600 (3 < x) & (x < oo) & Eq(x, 3 - y) 

601 

602 Although checking of assumptions on symbols in relationals 

603 is not done, setting assumptions will affect how certain 

604 relationals might automatically simplify: 

605 

606 >>> solve(x**2 > 4) 

607 ((-oo < x) & (x < -2)) | ((2 < x) & (x < oo)) 

608 

609 >>> r = Symbol('r', real=True) 

610 >>> solve(r**2 > 4) 

611 (2 < r) | (r < -2) 

612 

613 There is currently no algorithm in SymPy that allows you to use 

614 relationships to resolve more than one variable. So the following 

615 does not determine that ``q < 0`` (and trying to solve for ``r`` 

616 and ``q`` will raise an error): 

617 

618 >>> from sympy import symbols 

619 >>> r, q = symbols('r, q', real=True) 

620 >>> solve([r + q - 3, r > 3], r) 

621 (3 < r) & Eq(r, 3 - q) 

622 

623 You can directly call the routine that ``solve`` calls 

624 when it encounters a relational: :func:`~.reduce_inequalities`. 

625 It treats Expr like Equality. 

626 

627 >>> from sympy import reduce_inequalities 

628 >>> reduce_inequalities([x**2 - 4]) 

629 Eq(x, -2) | Eq(x, 2) 

630 

631 If each relationship contains only one symbol of interest, 

632 the expressions can be processed for multiple symbols: 

633 

634 >>> reduce_inequalities([0 <= x - 1, y < 3], [x, y]) 

635 (-oo < y) & (1 <= x) & (x < oo) & (y < 3) 

636 

637 But an error is raised if any relationship has more than one 

638 symbol of interest: 

639 

640 >>> reduce_inequalities([0 <= x*y - 1, y < 3], [x, y]) 

641 Traceback (most recent call last): 

642 ... 

643 NotImplementedError: 

644 inequality has more than one symbol of interest. 

645 

646 **Disabling High-Order Explicit Solutions** 

647 

648 When solving polynomial expressions, you might not want explicit solutions 

649 (which can be quite long). If the expression is univariate, ``CRootOf`` 

650 instances will be returned instead: 

651 

652 >>> solve(x**3 - x + 1) 

653 [-1/((-1/2 - sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)) - 

654 (-1/2 - sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)/3, 

655 -(-1/2 + sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)/3 - 

656 1/((-1/2 + sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)), 

657 -(3*sqrt(69)/2 + 27/2)**(1/3)/3 - 

658 1/(3*sqrt(69)/2 + 27/2)**(1/3)] 

659 >>> solve(x**3 - x + 1, cubics=False) 

660 [CRootOf(x**3 - x + 1, 0), 

661 CRootOf(x**3 - x + 1, 1), 

662 CRootOf(x**3 - x + 1, 2)] 

663 

664 If the expression is multivariate, no solution might be returned: 

665 

666 >>> solve(x**3 - x + a, x, cubics=False) 

667 [] 

668 

669 Sometimes solutions will be obtained even when a flag is False because the 

670 expression could be factored. In the following example, the equation can 

671 be factored as the product of a linear and a quadratic factor so explicit 

672 solutions (which did not require solving a cubic expression) are obtained: 

673 

674 >>> eq = x**3 + 3*x**2 + x - 1 

675 >>> solve(eq, cubics=False) 

676 [-1, -1 + sqrt(2), -sqrt(2) - 1] 

677 

678 **Solving Equations Involving Radicals** 

679 

680 Because of SymPy's use of the principle root, some solutions 

681 to radical equations will be missed unless check=False: 

682 

683 >>> from sympy import root 

684 >>> eq = root(x**3 - 3*x**2, 3) + 1 - x 

685 >>> solve(eq) 

686 [] 

687 >>> solve(eq, check=False) 

688 [1/3] 

689 

690 In the above example, there is only a single solution to the 

691 equation. Other expressions will yield spurious roots which 

692 must be checked manually; roots which give a negative argument 

693 to odd-powered radicals will also need special checking: 

694 

695 >>> from sympy import real_root, S 

696 >>> eq = root(x, 3) - root(x, 5) + S(1)/7 

697 >>> solve(eq) # this gives 2 solutions but misses a 3rd 

698 [CRootOf(7*x**5 - 7*x**3 + 1, 1)**15, 

699 CRootOf(7*x**5 - 7*x**3 + 1, 2)**15] 

700 >>> sol = solve(eq, check=False) 

701 >>> [abs(eq.subs(x,i).n(2)) for i in sol] 

702 [0.48, 0.e-110, 0.e-110, 0.052, 0.052] 

703 

704 The first solution is negative so ``real_root`` must be used to see that it 

705 satisfies the expression: 

706 

707 >>> abs(real_root(eq.subs(x, sol[0])).n(2)) 

708 0.e-110 

709 

710 If the roots of the equation are not real then more care will be 

711 necessary to find the roots, especially for higher order equations. 

712 Consider the following expression: 

713 

714 >>> expr = root(x, 3) - root(x, 5) 

715 

716 We will construct a known value for this expression at x = 3 by selecting 

717 the 1-th root for each radical: 

718 

719 >>> expr1 = root(x, 3, 1) - root(x, 5, 1) 

720 >>> v = expr1.subs(x, -3) 

721 

722 The ``solve`` function is unable to find any exact roots to this equation: 

723 

724 >>> eq = Eq(expr, v); eq1 = Eq(expr1, v) 

725 >>> solve(eq, check=False), solve(eq1, check=False) 

726 ([], []) 

727 

728 The function ``unrad``, however, can be used to get a form of the equation 

729 for which numerical roots can be found: 

730 

731 >>> from sympy.solvers.solvers import unrad 

732 >>> from sympy import nroots 

733 >>> e, (p, cov) = unrad(eq) 

734 >>> pvals = nroots(e) 

735 >>> inversion = solve(cov, x)[0] 

736 >>> xvals = [inversion.subs(p, i) for i in pvals] 

737 

738 Although ``eq`` or ``eq1`` could have been used to find ``xvals``, the 

739 solution can only be verified with ``expr1``: 

740 

741 >>> z = expr - v 

742 >>> [xi.n(chop=1e-9) for xi in xvals if abs(z.subs(x, xi).n()) < 1e-9] 

743 [] 

744 >>> z1 = expr1 - v 

745 >>> [xi.n(chop=1e-9) for xi in xvals if abs(z1.subs(x, xi).n()) < 1e-9] 

746 [-3.0] 

747 

748 Parameters 

749 ========== 

750 

751 f : 

752 - a single Expr or Poly that must be zero 

753 - an Equality 

754 - a Relational expression 

755 - a Boolean 

756 - iterable of one or more of the above 

757 

758 symbols : (object(s) to solve for) specified as 

759 - none given (other non-numeric objects will be used) 

760 - single symbol 

761 - denested list of symbols 

762 (e.g., ``solve(f, x, y)``) 

763 - ordered iterable of symbols 

764 (e.g., ``solve(f, [x, y])``) 

765 

766 flags : 

767 dict=True (default is False) 

768 Return list (perhaps empty) of solution mappings. 

769 set=True (default is False) 

770 Return list of symbols and set of tuple(s) of solution(s). 

771 exclude=[] (default) 

772 Do not try to solve for any of the free symbols in exclude; 

773 if expressions are given, the free symbols in them will 

774 be extracted automatically. 

775 check=True (default) 

776 If False, do not do any testing of solutions. This can be 

777 useful if you want to include solutions that make any 

778 denominator zero. 

779 numerical=True (default) 

780 Do a fast numerical check if *f* has only one symbol. 

781 minimal=True (default is False) 

782 A very fast, minimal testing. 

783 warn=True (default is False) 

784 Show a warning if ``checksol()`` could not conclude. 

785 simplify=True (default) 

786 Simplify all but polynomials of order 3 or greater before 

787 returning them and (if check is not False) use the 

788 general simplify function on the solutions and the 

789 expression obtained when they are substituted into the 

790 function which should be zero. 

791 force=True (default is False) 

792 Make positive all symbols without assumptions regarding sign. 

793 rational=True (default) 

794 Recast Floats as Rational; if this option is not used, the 

795 system containing Floats may fail to solve because of issues 

796 with polys. If rational=None, Floats will be recast as 

797 rationals but the answer will be recast as Floats. If the 

798 flag is False then nothing will be done to the Floats. 

799 manual=True (default is False) 

800 Do not use the polys/matrix method to solve a system of 

801 equations, solve them one at a time as you might "manually." 

802 implicit=True (default is False) 

803 Allows ``solve`` to return a solution for a pattern in terms of 

804 other functions that contain that pattern; this is only 

805 needed if the pattern is inside of some invertible function 

806 like cos, exp, ect. 

807 particular=True (default is False) 

808 Instructs ``solve`` to try to find a particular solution to 

809 a linear system with as many zeros as possible; this is very 

810 expensive. 

811 quick=True (default is False; ``particular`` must be True) 

812 Selects a fast heuristic to find a solution with many zeros 

813 whereas a value of False uses the very slow method guaranteed 

814 to find the largest number of zeros possible. 

815 cubics=True (default) 

816 Return explicit solutions when cubic expressions are encountered. 

817 When False, quartics and quintics are disabled, too. 

818 quartics=True (default) 

819 Return explicit solutions when quartic expressions are encountered. 

820 When False, quintics are disabled, too. 

821 quintics=True (default) 

822 Return explicit solutions (if possible) when quintic expressions 

823 are encountered. 

824 

825 See Also 

826 ======== 

827 

828 rsolve: For solving recurrence relationships 

829 dsolve: For solving differential equations 

830 

831 """ 

832 from .inequalities import reduce_inequalities 

833 

834 # checking/recording flags 

835 ########################################################################### 

836 

837 # set solver types explicitly; as soon as one is False 

838 # all the rest will be False 

839 hints = ('cubics', 'quartics', 'quintics') 

840 default = True 

841 for k in hints: 

842 default = flags.setdefault(k, bool(flags.get(k, default))) 

843 

844 # allow solution to contain symbol if True: 

845 implicit = flags.get('implicit', False) 

846 

847 # record desire to see warnings 

848 warn = flags.get('warn', False) 

849 

850 # this flag will be needed for quick exits below, so record 

851 # now -- but don't record `dict` yet since it might change 

852 as_set = flags.get('set', False) 

853 

854 # keeping track of how f was passed 

855 bare_f = not iterable(f) 

856 

857 # check flag usage for particular/quick which should only be used 

858 # with systems of equations 

859 if flags.get('quick', None) is not None: 

860 if not flags.get('particular', None): 

861 raise ValueError('when using `quick`, `particular` should be True') 

862 if flags.get('particular', False) and bare_f: 

863 raise ValueError(filldedent(""" 

864 The 'particular/quick' flag is usually used with systems of 

865 equations. Either pass your equation in a list or 

866 consider using a solver like `diophantine` if you are 

867 looking for a solution in integers.""")) 

868 

869 # sympify everything, creating list of expressions and list of symbols 

870 ########################################################################### 

871 

872 def _sympified_list(w): 

873 return list(map(sympify, w if iterable(w) else [w])) 

874 f, symbols = (_sympified_list(w) for w in [f, symbols]) 

875 

876 # preprocess symbol(s) 

877 ########################################################################### 

878 

879 ordered_symbols = None # were the symbols in a well defined order? 

880 if not symbols: 

881 # get symbols from equations 

882 symbols = set().union(*[fi.free_symbols for fi in f]) 

883 if len(symbols) < len(f): 

884 for fi in f: 

885 pot = preorder_traversal(fi) 

886 for p in pot: 

887 if isinstance(p, AppliedUndef): 

888 if not as_set: 

889 flags['dict'] = True # better show symbols 

890 symbols.add(p) 

891 pot.skip() # don't go any deeper 

892 ordered_symbols = False 

893 symbols = list(ordered(symbols)) # to make it canonical 

894 else: 

895 if len(symbols) == 1 and iterable(symbols[0]): 

896 symbols = symbols[0] 

897 ordered_symbols = symbols and is_sequence(symbols, 

898 include=GeneratorType) 

899 _symbols = list(uniq(symbols)) 

900 if len(_symbols) != len(symbols): 

901 ordered_symbols = False 

902 symbols = list(ordered(symbols)) 

903 else: 

904 symbols = _symbols 

905 

906 # check for duplicates 

907 if len(symbols) != len(set(symbols)): 

908 raise ValueError('duplicate symbols given') 

909 # remove those not of interest 

910 exclude = flags.pop('exclude', set()) 

911 if exclude: 

912 if isinstance(exclude, Expr): 

913 exclude = [exclude] 

914 exclude = set().union(*[e.free_symbols for e in sympify(exclude)]) 

915 symbols = [s for s in symbols if s not in exclude] 

916 

917 # preprocess equation(s) 

918 ########################################################################### 

919 

920 # automatically ignore True values 

921 if isinstance(f, list): 

922 f = [s for s in f if s is not S.true] 

923 

924 # handle canonicalization of equation types 

925 for i, fi in enumerate(f): 

926 if isinstance(fi, (Eq, Ne)): 

927 if 'ImmutableDenseMatrix' in [type(a).__name__ for a in fi.args]: 

928 fi = fi.lhs - fi.rhs 

929 else: 

930 L, R = fi.args 

931 if isinstance(R, BooleanAtom): 

932 L, R = R, L 

933 if isinstance(L, BooleanAtom): 

934 if isinstance(fi, Ne): 

935 L = ~L 

936 if R.is_Relational: 

937 fi = ~R if L is S.false else R 

938 elif R.is_Symbol: 

939 return L 

940 elif R.is_Boolean and (~R).is_Symbol: 

941 return ~L 

942 else: 

943 raise NotImplementedError(filldedent(''' 

944 Unanticipated argument of Eq when other arg 

945 is True or False. 

946 ''')) 

947 else: 

948 fi = fi.rewrite(Add, evaluate=False, deep=False) 

949 f[i] = fi 

950 

951 # *** dispatch and handle as a system of relationals 

952 # ************************************************** 

953 if fi.is_Relational: 

954 if len(symbols) != 1: 

955 raise ValueError("can only solve for one symbol at a time") 

956 if warn and symbols[0].assumptions0: 

957 warnings.warn(filldedent(""" 

958 \tWarning: assumptions about variable '%s' are 

959 not handled currently.""" % symbols[0])) 

960 return reduce_inequalities(f, symbols=symbols) 

961 

962 # convert Poly to expression 

963 if isinstance(fi, Poly): 

964 f[i] = fi.as_expr() 

965 

966 # rewrite hyperbolics in terms of exp if they have symbols of 

967 # interest 

968 f[i] = f[i].replace(lambda w: isinstance(w, HyperbolicFunction) and \ 

969 w.has_free(*symbols), lambda w: w.rewrite(exp)) 

970 

971 # if we have a Matrix, we need to iterate over its elements again 

972 if f[i].is_Matrix: 

973 bare_f = False 

974 f.extend(list(f[i])) 

975 f[i] = S.Zero 

976 

977 # if we can split it into real and imaginary parts then do so 

978 freei = f[i].free_symbols 

979 if freei and all(s.is_extended_real or s.is_imaginary for s in freei): 

980 fr, fi = f[i].as_real_imag() 

981 # accept as long as new re, im, arg or atan2 are not introduced 

982 had = f[i].atoms(re, im, arg, atan2) 

983 if fr and fi and fr != fi and not any( 

984 i.atoms(re, im, arg, atan2) - had for i in (fr, fi)): 

985 if bare_f: 

986 bare_f = False 

987 f[i: i + 1] = [fr, fi] 

988 

989 # real/imag handling ----------------------------- 

990 if any(isinstance(fi, (bool, BooleanAtom)) for fi in f): 

991 if as_set: 

992 return [], set() 

993 return [] 

994 

995 for i, fi in enumerate(f): 

996 # Abs 

997 while True: 

998 was = fi 

999 fi = fi.replace(Abs, lambda arg: 

1000 separatevars(Abs(arg)).rewrite(Piecewise) if arg.has(*symbols) 

1001 else Abs(arg)) 

1002 if was == fi: 

1003 break 

1004 

1005 for e in fi.find(Abs): 

1006 if e.has(*symbols): 

1007 raise NotImplementedError('solving %s when the argument ' 

1008 'is not real or imaginary.' % e) 

1009 

1010 # arg 

1011 fi = fi.replace(arg, lambda a: arg(a).rewrite(atan2).rewrite(atan)) 

1012 

1013 # save changes 

1014 f[i] = fi 

1015 

1016 # see if re(s) or im(s) appear 

1017 freim = [fi for fi in f if fi.has(re, im)] 

1018 if freim: 

1019 irf = [] 

1020 for s in symbols: 

1021 if s.is_real or s.is_imaginary: 

1022 continue # neither re(x) nor im(x) will appear 

1023 # if re(s) or im(s) appear, the auxiliary equation must be present 

1024 if any(fi.has(re(s), im(s)) for fi in freim): 

1025 irf.append((s, re(s) + S.ImaginaryUnit*im(s))) 

1026 if irf: 

1027 for s, rhs in irf: 

1028 f = [fi.xreplace({s: rhs}) for fi in f] + [s - rhs] 

1029 symbols.extend([re(s), im(s)]) 

1030 if bare_f: 

1031 bare_f = False 

1032 flags['dict'] = True 

1033 # end of real/imag handling ----------------------------- 

1034 

1035 # we can solve for non-symbol entities by replacing them with Dummy symbols 

1036 f, symbols, swap_sym = recast_to_symbols(f, symbols) 

1037 # this set of symbols (perhaps recast) is needed below 

1038 symset = set(symbols) 

1039 

1040 # get rid of equations that have no symbols of interest; we don't 

1041 # try to solve them because the user didn't ask and they might be 

1042 # hard to solve; this means that solutions may be given in terms 

1043 # of the eliminated equations e.g. solve((x-y, y-3), x) -> {x: y} 

1044 newf = [] 

1045 for fi in f: 

1046 # let the solver handle equations that.. 

1047 # - have no symbols but are expressions 

1048 # - have symbols of interest 

1049 # - have no symbols of interest but are constant 

1050 # but when an expression is not constant and has no symbols of 

1051 # interest, it can't change what we obtain for a solution from 

1052 # the remaining equations so we don't include it; and if it's 

1053 # zero it can be removed and if it's not zero, there is no 

1054 # solution for the equation set as a whole 

1055 # 

1056 # The reason for doing this filtering is to allow an answer 

1057 # to be obtained to queries like solve((x - y, y), x); without 

1058 # this mod the return value is [] 

1059 ok = False 

1060 if fi.free_symbols & symset: 

1061 ok = True 

1062 else: 

1063 if fi.is_number: 

1064 if fi.is_Number: 

1065 if fi.is_zero: 

1066 continue 

1067 return [] 

1068 ok = True 

1069 else: 

1070 if fi.is_constant(): 

1071 ok = True 

1072 if ok: 

1073 newf.append(fi) 

1074 if not newf: 

1075 if as_set: 

1076 return symbols, set() 

1077 return [] 

1078 f = newf 

1079 del newf 

1080 

1081 # mask off any Object that we aren't going to invert: Derivative, 

1082 # Integral, etc... so that solving for anything that they contain will 

1083 # give an implicit solution 

1084 seen = set() 

1085 non_inverts = set() 

1086 for fi in f: 

1087 pot = preorder_traversal(fi) 

1088 for p in pot: 

1089 if not isinstance(p, Expr) or isinstance(p, Piecewise): 

1090 pass 

1091 elif (isinstance(p, bool) or 

1092 not p.args or 

1093 p in symset or 

1094 p.is_Add or p.is_Mul or 

1095 p.is_Pow and not implicit or 

1096 p.is_Function and not implicit) and p.func not in (re, im): 

1097 continue 

1098 elif p not in seen: 

1099 seen.add(p) 

1100 if p.free_symbols & symset: 

1101 non_inverts.add(p) 

1102 else: 

1103 continue 

1104 pot.skip() 

1105 del seen 

1106 non_inverts = dict(list(zip(non_inverts, [Dummy() for _ in non_inverts]))) 

1107 f = [fi.subs(non_inverts) for fi in f] 

1108 

1109 # Both xreplace and subs are needed below: xreplace to force substitution 

1110 # inside Derivative, subs to handle non-straightforward substitutions 

1111 non_inverts = [(v, k.xreplace(swap_sym).subs(swap_sym)) for k, v in non_inverts.items()] 

1112 

1113 # rationalize Floats 

1114 floats = False 

1115 if flags.get('rational', True) is not False: 

1116 for i, fi in enumerate(f): 

1117 if fi.has(Float): 

1118 floats = True 

1119 f[i] = nsimplify(fi, rational=True) 

1120 

1121 # capture any denominators before rewriting since 

1122 # they may disappear after the rewrite, e.g. issue 14779 

1123 flags['_denominators'] = _simple_dens(f[0], symbols) 

1124 

1125 # Any embedded piecewise functions need to be brought out to the 

1126 # top level so that the appropriate strategy gets selected. 

1127 # However, this is necessary only if one of the piecewise 

1128 # functions depends on one of the symbols we are solving for. 

1129 def _has_piecewise(e): 

1130 if e.is_Piecewise: 

1131 return e.has(*symbols) 

1132 return any(_has_piecewise(a) for a in e.args) 

1133 for i, fi in enumerate(f): 

1134 if _has_piecewise(fi): 

1135 f[i] = piecewise_fold(fi) 

1136 

1137 # 

1138 # try to get a solution 

1139 ########################################################################### 

1140 if bare_f: 

1141 solution = None 

1142 if len(symbols) != 1: 

1143 solution = _solve_undetermined(f[0], symbols, flags) 

1144 if not solution: 

1145 solution = _solve(f[0], *symbols, **flags) 

1146 else: 

1147 linear, solution = _solve_system(f, symbols, **flags) 

1148 assert type(solution) is list 

1149 assert not solution or type(solution[0]) is dict, solution 

1150 # 

1151 # postprocessing 

1152 ########################################################################### 

1153 # capture as_dict flag now (as_set already captured) 

1154 as_dict = flags.get('dict', False) 

1155 

1156 # define how solution will get unpacked 

1157 tuple_format = lambda s: [tuple([i.get(x, x) for x in symbols]) for i in s] 

1158 if as_dict or as_set: 

1159 unpack = None 

1160 elif bare_f: 

1161 if len(symbols) == 1: 

1162 unpack = lambda s: [i[symbols[0]] for i in s] 

1163 elif len(solution) == 1 and len(solution[0]) == len(symbols): 

1164 # undetermined linear coeffs solution 

1165 unpack = lambda s: s[0] 

1166 elif ordered_symbols: 

1167 unpack = tuple_format 

1168 else: 

1169 unpack = lambda s: s 

1170 else: 

1171 if solution: 

1172 if linear and len(solution) == 1: 

1173 # if you want the tuple solution for the linear 

1174 # case, use `set=True` 

1175 unpack = lambda s: s[0] 

1176 elif ordered_symbols: 

1177 unpack = tuple_format 

1178 else: 

1179 unpack = lambda s: s 

1180 else: 

1181 unpack = None 

1182 

1183 # Restore masked-off objects 

1184 if non_inverts and type(solution) is list: 

1185 solution = [{k: v.subs(non_inverts) for k, v in s.items()} 

1186 for s in solution] 

1187 

1188 # Restore original "symbols" if a dictionary is returned. 

1189 # This is not necessary for 

1190 # - the single univariate equation case 

1191 # since the symbol will have been removed from the solution; 

1192 # - the nonlinear poly_system since that only supports zero-dimensional 

1193 # systems and those results come back as a list 

1194 # 

1195 # ** unless there were Derivatives with the symbols, but those were handled 

1196 # above. 

1197 if swap_sym: 

1198 symbols = [swap_sym.get(k, k) for k in symbols] 

1199 for i, sol in enumerate(solution): 

1200 solution[i] = {swap_sym.get(k, k): v.subs(swap_sym) 

1201 for k, v in sol.items()} 

1202 

1203 # Get assumptions about symbols, to filter solutions. 

1204 # Note that if assumptions about a solution can't be verified, it is still 

1205 # returned. 

1206 check = flags.get('check', True) 

1207 

1208 # restore floats 

1209 if floats and solution and flags.get('rational', None) is None: 

1210 solution = nfloat(solution, exponent=False) 

1211 # nfloat might reveal more duplicates 

1212 solution = _remove_duplicate_solutions(solution) 

1213 

1214 if check and solution: # assumption checking 

1215 warn = flags.get('warn', False) 

1216 got_None = [] # solutions for which one or more symbols gave None 

1217 no_False = [] # solutions for which no symbols gave False 

1218 for sol in solution: 

1219 a_None = False 

1220 for symb, val in sol.items(): 

1221 test = check_assumptions(val, **symb.assumptions0) 

1222 if test: 

1223 continue 

1224 if test is False: 

1225 break 

1226 a_None = True 

1227 else: 

1228 no_False.append(sol) 

1229 if a_None: 

1230 got_None.append(sol) 

1231 

1232 solution = no_False 

1233 if warn and got_None: 

1234 warnings.warn(filldedent(""" 

1235 \tWarning: assumptions concerning following solution(s) 

1236 cannot be checked:""" + '\n\t' + 

1237 ', '.join(str(s) for s in got_None))) 

1238 

1239 # 

1240 # done 

1241 ########################################################################### 

1242 

1243 if not solution: 

1244 if as_set: 

1245 return symbols, set() 

1246 return [] 

1247 

1248 # make orderings canonical for list of dictionaries 

1249 if not as_set: # for set, no point in ordering 

1250 solution = [{k: s[k] for k in ordered(s)} for s in solution] 

1251 solution.sort(key=default_sort_key) 

1252 

1253 if not (as_set or as_dict): 

1254 return unpack(solution) 

1255 

1256 if as_dict: 

1257 return solution 

1258 

1259 # set output: (symbols, {t1, t2, ...}) from list of dictionaries; 

1260 # include all symbols for those that like a verbose solution 

1261 # and to resolve any differences in dictionary keys. 

1262 # 

1263 # The set results can easily be used to make a verbose dict as 

1264 # k, v = solve(eqs, syms, set=True) 

1265 # sol = [dict(zip(k,i)) for i in v] 

1266 # 

1267 if ordered_symbols: 

1268 k = symbols # keep preferred order 

1269 else: 

1270 # just unify the symbols for which solutions were found 

1271 k = list(ordered(set(flatten(tuple(i.keys()) for i in solution)))) 

1272 return k, {tuple([s.get(ki, ki) for ki in k]) for s in solution} 

1273 

1274 

1275def _solve_undetermined(g, symbols, flags): 

1276 """solve helper to return a list with one dict (solution) else None 

1277 

1278 A direct call to solve_undetermined_coeffs is more flexible and 

1279 can return both multiple solutions and handle more than one independent 

1280 variable. Here, we have to be more cautious to keep from solving 

1281 something that does not look like an undetermined coeffs system -- 

1282 to minimize the surprise factor since singularities that cancel are not 

1283 prohibited in solve_undetermined_coeffs. 

1284 """ 

1285 if g.free_symbols - set(symbols): 

1286 sol = solve_undetermined_coeffs(g, symbols, **dict(flags, dict=True, set=None)) 

1287 if len(sol) == 1: 

1288 return sol 

1289 

1290 

1291def _solve(f, *symbols, **flags): 

1292 """Return a checked solution for *f* in terms of one or more of the 

1293 symbols in the form of a list of dictionaries. 

1294 

1295 If no method is implemented to solve the equation, a NotImplementedError 

1296 will be raised. In the case that conversion of an expression to a Poly 

1297 gives None a ValueError will be raised. 

1298 """ 

1299 

1300 not_impl_msg = "No algorithms are implemented to solve equation %s" 

1301 

1302 if len(symbols) != 1: 

1303 # look for solutions for desired symbols that are independent 

1304 # of symbols already solved for, e.g. if we solve for x = y 

1305 # then no symbol having x in its solution will be returned. 

1306 

1307 # First solve for linear symbols (since that is easier and limits 

1308 # solution size) and then proceed with symbols appearing 

1309 # in a non-linear fashion. Ideally, if one is solving a single 

1310 # expression for several symbols, they would have to be 

1311 # appear in factors of an expression, but we do not here 

1312 # attempt factorization. XXX perhaps handling a Mul 

1313 # should come first in this routine whether there is 

1314 # one or several symbols. 

1315 nonlin_s = [] 

1316 got_s = set() 

1317 rhs_s = set() 

1318 result = [] 

1319 for s in symbols: 

1320 xi, v = solve_linear(f, symbols=[s]) 

1321 if xi == s: 

1322 # no need to check but we should simplify if desired 

1323 if flags.get('simplify', True): 

1324 v = simplify(v) 

1325 vfree = v.free_symbols 

1326 if vfree & got_s: 

1327 # was linear, but has redundant relationship 

1328 # e.g. x - y = 0 has y == x is redundant for x == y 

1329 # so ignore 

1330 continue 

1331 rhs_s |= vfree 

1332 got_s.add(xi) 

1333 result.append({xi: v}) 

1334 elif xi: # there might be a non-linear solution if xi is not 0 

1335 nonlin_s.append(s) 

1336 if not nonlin_s: 

1337 return result 

1338 for s in nonlin_s: 

1339 try: 

1340 soln = _solve(f, s, **flags) 

1341 for sol in soln: 

1342 if sol[s].free_symbols & got_s: 

1343 # depends on previously solved symbols: ignore 

1344 continue 

1345 got_s.add(s) 

1346 result.append(sol) 

1347 except NotImplementedError: 

1348 continue 

1349 if got_s: 

1350 return result 

1351 else: 

1352 raise NotImplementedError(not_impl_msg % f) 

1353 

1354 # solve f for a single variable 

1355 

1356 symbol = symbols[0] 

1357 

1358 # expand binomials only if it has the unknown symbol 

1359 f = f.replace(lambda e: isinstance(e, binomial) and e.has(symbol), 

1360 lambda e: expand_func(e)) 

1361 

1362 # checking will be done unless it is turned off before making a 

1363 # recursive call; the variables `checkdens` and `check` are 

1364 # captured here (for reference below) in case flag value changes 

1365 flags['check'] = checkdens = check = flags.pop('check', True) 

1366 

1367 # build up solutions if f is a Mul 

1368 if f.is_Mul: 

1369 result = set() 

1370 for m in f.args: 

1371 if m in {S.NegativeInfinity, S.ComplexInfinity, S.Infinity}: 

1372 result = set() 

1373 break 

1374 soln = _vsolve(m, symbol, **flags) 

1375 result.update(set(soln)) 

1376 result = [{symbol: v} for v in result] 

1377 if check: 

1378 # all solutions have been checked but now we must 

1379 # check that the solutions do not set denominators 

1380 # in any factor to zero 

1381 dens = flags.get('_denominators', _simple_dens(f, symbols)) 

1382 result = [s for s in result if 

1383 not any(checksol(den, s, **flags) for den in 

1384 dens)] 

1385 # set flags for quick exit at end; solutions for each 

1386 # factor were already checked and simplified 

1387 check = False 

1388 flags['simplify'] = False 

1389 

1390 elif f.is_Piecewise: 

1391 result = set() 

1392 for i, (expr, cond) in enumerate(f.args): 

1393 if expr.is_zero: 

1394 raise NotImplementedError( 

1395 'solve cannot represent interval solutions') 

1396 candidates = _vsolve(expr, symbol, **flags) 

1397 # the explicit condition for this expr is the current cond 

1398 # and none of the previous conditions 

1399 args = [~c for _, c in f.args[:i]] + [cond] 

1400 cond = And(*args) 

1401 for candidate in candidates: 

1402 if candidate in result: 

1403 # an unconditional value was already there 

1404 continue 

1405 try: 

1406 v = cond.subs(symbol, candidate) 

1407 _eval_simplify = getattr(v, '_eval_simplify', None) 

1408 if _eval_simplify is not None: 

1409 # unconditionally take the simplification of v 

1410 v = _eval_simplify(ratio=2, measure=lambda x: 1) 

1411 except TypeError: 

1412 # incompatible type with condition(s) 

1413 continue 

1414 if v == False: 

1415 continue 

1416 if v == True: 

1417 result.add(candidate) 

1418 else: 

1419 result.add(Piecewise( 

1420 (candidate, v), 

1421 (S.NaN, True))) 

1422 # solutions already checked and simplified 

1423 # **************************************** 

1424 return [{symbol: r} for r in result] 

1425 else: 

1426 # first see if it really depends on symbol and whether there 

1427 # is only a linear solution 

1428 f_num, sol = solve_linear(f, symbols=symbols) 

1429 if f_num.is_zero or sol is S.NaN: 

1430 return [] 

1431 elif f_num.is_Symbol: 

1432 # no need to check but simplify if desired 

1433 if flags.get('simplify', True): 

1434 sol = simplify(sol) 

1435 return [{f_num: sol}] 

1436 

1437 poly = None 

1438 # check for a single Add generator 

1439 if not f_num.is_Add: 

1440 add_args = [i for i in f_num.atoms(Add) 

1441 if symbol in i.free_symbols] 

1442 if len(add_args) == 1: 

1443 gen = add_args[0] 

1444 spart = gen.as_independent(symbol)[1].as_base_exp()[0] 

1445 if spart == symbol: 

1446 try: 

1447 poly = Poly(f_num, spart) 

1448 except PolynomialError: 

1449 pass 

1450 

1451 result = False # no solution was obtained 

1452 msg = '' # there is no failure message 

1453 

1454 # Poly is generally robust enough to convert anything to 

1455 # a polynomial and tell us the different generators that it 

1456 # contains, so we will inspect the generators identified by 

1457 # polys to figure out what to do. 

1458 

1459 # try to identify a single generator that will allow us to solve this 

1460 # as a polynomial, followed (perhaps) by a change of variables if the 

1461 # generator is not a symbol 

1462 

1463 try: 

1464 if poly is None: 

1465 poly = Poly(f_num) 

1466 if poly is None: 

1467 raise ValueError('could not convert %s to Poly' % f_num) 

1468 except GeneratorsNeeded: 

1469 simplified_f = simplify(f_num) 

1470 if simplified_f != f_num: 

1471 return _solve(simplified_f, symbol, **flags) 

1472 raise ValueError('expression appears to be a constant') 

1473 

1474 gens = [g for g in poly.gens if g.has(symbol)] 

1475 

1476 def _as_base_q(x): 

1477 """Return (b**e, q) for x = b**(p*e/q) where p/q is the leading 

1478 Rational of the exponent of x, e.g. exp(-2*x/3) -> (exp(x), 3) 

1479 """ 

1480 b, e = x.as_base_exp() 

1481 if e.is_Rational: 

1482 return b, e.q 

1483 if not e.is_Mul: 

1484 return x, 1 

1485 c, ee = e.as_coeff_Mul() 

1486 if c.is_Rational and c is not S.One: # c could be a Float 

1487 return b**ee, c.q 

1488 return x, 1 

1489 

1490 if len(gens) > 1: 

1491 # If there is more than one generator, it could be that the 

1492 # generators have the same base but different powers, e.g. 

1493 # >>> Poly(exp(x) + 1/exp(x)) 

1494 # Poly(exp(-x) + exp(x), exp(-x), exp(x), domain='ZZ') 

1495 # 

1496 # If unrad was not disabled then there should be no rational 

1497 # exponents appearing as in 

1498 # >>> Poly(sqrt(x) + sqrt(sqrt(x))) 

1499 # Poly(sqrt(x) + x**(1/4), sqrt(x), x**(1/4), domain='ZZ') 

1500 

1501 bases, qs = list(zip(*[_as_base_q(g) for g in gens])) 

1502 bases = set(bases) 

1503 

1504 if len(bases) > 1 or not all(q == 1 for q in qs): 

1505 funcs = {b for b in bases if b.is_Function} 

1506 

1507 trig = {_ for _ in funcs if 

1508 isinstance(_, TrigonometricFunction)} 

1509 other = funcs - trig 

1510 if not other and len(funcs.intersection(trig)) > 1: 

1511 newf = None 

1512 if f_num.is_Add and len(f_num.args) == 2: 

1513 # check for sin(x)**p = cos(x)**p 

1514 _args = f_num.args 

1515 t = a, b = [i.atoms(Function).intersection( 

1516 trig) for i in _args] 

1517 if all(len(i) == 1 for i in t): 

1518 a, b = [i.pop() for i in t] 

1519 if isinstance(a, cos): 

1520 a, b = b, a 

1521 _args = _args[::-1] 

1522 if isinstance(a, sin) and isinstance(b, cos 

1523 ) and a.args[0] == b.args[0]: 

1524 # sin(x) + cos(x) = 0 -> tan(x) + 1 = 0 

1525 newf, _d = (TR2i(_args[0]/_args[1]) + 1 

1526 ).as_numer_denom() 

1527 if not _d.is_Number: 

1528 newf = None 

1529 if newf is None: 

1530 newf = TR1(f_num).rewrite(tan) 

1531 if newf != f_num: 

1532 # don't check the rewritten form --check 

1533 # solutions in the un-rewritten form below 

1534 flags['check'] = False 

1535 result = _solve(newf, symbol, **flags) 

1536 flags['check'] = check 

1537 

1538 # just a simple case - see if replacement of single function 

1539 # clears all symbol-dependent functions, e.g. 

1540 # log(x) - log(log(x) - 1) - 3 can be solved even though it has 

1541 # two generators. 

1542 

1543 if result is False and funcs: 

1544 funcs = list(ordered(funcs)) # put shallowest function first 

1545 f1 = funcs[0] 

1546 t = Dummy('t') 

1547 # perform the substitution 

1548 ftry = f_num.subs(f1, t) 

1549 

1550 # if no Functions left, we can proceed with usual solve 

1551 if not ftry.has(symbol): 

1552 cv_sols = _solve(ftry, t, **flags) 

1553 cv_inv = list(ordered(_vsolve(t - f1, symbol, **flags)))[0] 

1554 result = [{symbol: cv_inv.subs(sol)} for sol in cv_sols] 

1555 

1556 if result is False: 

1557 msg = 'multiple generators %s' % gens 

1558 

1559 else: 

1560 # e.g. case where gens are exp(x), exp(-x) 

1561 u = bases.pop() 

1562 t = Dummy('t') 

1563 inv = _vsolve(u - t, symbol, **flags) 

1564 if isinstance(u, (Pow, exp)): 

1565 # this will be resolved by factor in _tsolve but we might 

1566 # as well try a simple expansion here to get things in 

1567 # order so something like the following will work now without 

1568 # having to factor: 

1569 # 

1570 # >>> eq = (exp(I*(-x-2))+exp(I*(x+2))) 

1571 # >>> eq.subs(exp(x),y) # fails 

1572 # exp(I*(-x - 2)) + exp(I*(x + 2)) 

1573 # >>> eq.expand().subs(exp(x),y) # works 

1574 # y**I*exp(2*I) + y**(-I)*exp(-2*I) 

1575 def _expand(p): 

1576 b, e = p.as_base_exp() 

1577 e = expand_mul(e) 

1578 return expand_power_exp(b**e) 

1579 ftry = f_num.replace( 

1580 lambda w: w.is_Pow or isinstance(w, exp), 

1581 _expand).subs(u, t) 

1582 if not ftry.has(symbol): 

1583 soln = _solve(ftry, t, **flags) 

1584 result = [{symbol: i.subs(s)} for i in inv for s in soln] 

1585 

1586 elif len(gens) == 1: 

1587 

1588 # There is only one generator that we are interested in, but 

1589 # there may have been more than one generator identified by 

1590 # polys (e.g. for symbols other than the one we are interested 

1591 # in) so recast the poly in terms of our generator of interest. 

1592 # Also use composite=True with f_num since Poly won't update 

1593 # poly as documented in issue 8810. 

1594 

1595 poly = Poly(f_num, gens[0], composite=True) 

1596 

1597 # if we aren't on the tsolve-pass, use roots 

1598 if not flags.pop('tsolve', False): 

1599 soln = None 

1600 deg = poly.degree() 

1601 flags['tsolve'] = True 

1602 hints = ('cubics', 'quartics', 'quintics') 

1603 solvers = {h: flags.get(h) for h in hints} 

1604 soln = roots(poly, **solvers) 

1605 if sum(soln.values()) < deg: 

1606 # e.g. roots(32*x**5 + 400*x**4 + 2032*x**3 + 

1607 # 5000*x**2 + 6250*x + 3189) -> {} 

1608 # so all_roots is used and RootOf instances are 

1609 # returned *unless* the system is multivariate 

1610 # or high-order EX domain. 

1611 try: 

1612 soln = poly.all_roots() 

1613 except NotImplementedError: 

1614 if not flags.get('incomplete', True): 

1615 raise NotImplementedError( 

1616 filldedent(''' 

1617 Neither high-order multivariate polynomials 

1618 nor sorting of EX-domain polynomials is supported. 

1619 If you want to see any results, pass keyword incomplete=True to 

1620 solve; to see numerical values of roots 

1621 for univariate expressions, use nroots. 

1622 ''')) 

1623 else: 

1624 pass 

1625 else: 

1626 soln = list(soln.keys()) 

1627 

1628 if soln is not None: 

1629 u = poly.gen 

1630 if u != symbol: 

1631 try: 

1632 t = Dummy('t') 

1633 inv = _vsolve(u - t, symbol, **flags) 

1634 soln = {i.subs(t, s) for i in inv for s in soln} 

1635 except NotImplementedError: 

1636 # perhaps _tsolve can handle f_num 

1637 soln = None 

1638 else: 

1639 check = False # only dens need to be checked 

1640 if soln is not None: 

1641 if len(soln) > 2: 

1642 # if the flag wasn't set then unset it since high-order 

1643 # results are quite long. Perhaps one could base this 

1644 # decision on a certain critical length of the 

1645 # roots. In addition, wester test M2 has an expression 

1646 # whose roots can be shown to be real with the 

1647 # unsimplified form of the solution whereas only one of 

1648 # the simplified forms appears to be real. 

1649 flags['simplify'] = flags.get('simplify', False) 

1650 if soln is not None: 

1651 result = [{symbol: v} for v in soln] 

1652 

1653 # fallback if above fails 

1654 # ----------------------- 

1655 if result is False: 

1656 # try unrad 

1657 if flags.pop('_unrad', True): 

1658 try: 

1659 u = unrad(f_num, symbol) 

1660 except (ValueError, NotImplementedError): 

1661 u = False 

1662 if u: 

1663 eq, cov = u 

1664 if cov: 

1665 isym, ieq = cov 

1666 inv = _vsolve(ieq, symbol, **flags)[0] 

1667 rv = {inv.subs(xi) for xi in _solve(eq, isym, **flags)} 

1668 else: 

1669 try: 

1670 rv = set(_vsolve(eq, symbol, **flags)) 

1671 except NotImplementedError: 

1672 rv = None 

1673 if rv is not None: 

1674 result = [{symbol: v} for v in rv] 

1675 # if the flag wasn't set then unset it since unrad results 

1676 # can be quite long or of very high order 

1677 flags['simplify'] = flags.get('simplify', False) 

1678 else: 

1679 pass # for coverage 

1680 

1681 # try _tsolve 

1682 if result is False: 

1683 flags.pop('tsolve', None) # allow tsolve to be used on next pass 

1684 try: 

1685 soln = _tsolve(f_num, symbol, **flags) 

1686 if soln is not None: 

1687 result = [{symbol: v} for v in soln] 

1688 except PolynomialError: 

1689 pass 

1690 # ----------- end of fallback ---------------------------- 

1691 

1692 if result is False: 

1693 raise NotImplementedError('\n'.join([msg, not_impl_msg % f])) 

1694 

1695 result = _remove_duplicate_solutions(result) 

1696 

1697 if flags.get('simplify', True): 

1698 result = [{k: d[k].simplify() for k in d} for d in result] 

1699 # Simplification might reveal more duplicates 

1700 result = _remove_duplicate_solutions(result) 

1701 # we just simplified the solution so we now set the flag to 

1702 # False so the simplification doesn't happen again in checksol() 

1703 flags['simplify'] = False 

1704 

1705 if checkdens: 

1706 # reject any result that makes any denom. affirmatively 0; 

1707 # if in doubt, keep it 

1708 dens = _simple_dens(f, symbols) 

1709 result = [r for r in result if 

1710 not any(checksol(d, r, **flags) 

1711 for d in dens)] 

1712 if check: 

1713 # keep only results if the check is not False 

1714 result = [r for r in result if 

1715 checksol(f_num, r, **flags) is not False] 

1716 return result 

1717 

1718 

1719def _remove_duplicate_solutions(solutions: list[dict[Expr, Expr]] 

1720 ) -> list[dict[Expr, Expr]]: 

1721 """Remove duplicates from a list of dicts""" 

1722 solutions_set = set() 

1723 solutions_new = [] 

1724 

1725 for sol in solutions: 

1726 solset = frozenset(sol.items()) 

1727 if solset not in solutions_set: 

1728 solutions_new.append(sol) 

1729 solutions_set.add(solset) 

1730 

1731 return solutions_new 

1732 

1733 

1734def _solve_system(exprs, symbols, **flags): 

1735 """return ``(linear, solution)`` where ``linear`` is True 

1736 if the system was linear, else False; ``solution`` 

1737 is a list of dictionaries giving solutions for the symbols 

1738 """ 

1739 if not exprs: 

1740 return False, [] 

1741 

1742 if flags.pop('_split', True): 

1743 # Split the system into connected components 

1744 V = exprs 

1745 symsset = set(symbols) 

1746 exprsyms = {e: e.free_symbols & symsset for e in exprs} 

1747 E = [] 

1748 sym_indices = {sym: i for i, sym in enumerate(symbols)} 

1749 for n, e1 in enumerate(exprs): 

1750 for e2 in exprs[:n]: 

1751 # Equations are connected if they share a symbol 

1752 if exprsyms[e1] & exprsyms[e2]: 

1753 E.append((e1, e2)) 

1754 G = V, E 

1755 subexprs = connected_components(G) 

1756 if len(subexprs) > 1: 

1757 subsols = [] 

1758 linear = True 

1759 for subexpr in subexprs: 

1760 subsyms = set() 

1761 for e in subexpr: 

1762 subsyms |= exprsyms[e] 

1763 subsyms = sorted(subsyms, key = lambda x: sym_indices[x]) 

1764 flags['_split'] = False # skip split step 

1765 _linear, subsol = _solve_system(subexpr, subsyms, **flags) 

1766 if linear: 

1767 linear = linear and _linear 

1768 if not isinstance(subsol, list): 

1769 subsol = [subsol] 

1770 subsols.append(subsol) 

1771 # Full solution is cartesion product of subsystems 

1772 sols = [] 

1773 for soldicts in product(*subsols): 

1774 sols.append(dict(item for sd in soldicts 

1775 for item in sd.items())) 

1776 return linear, sols 

1777 

1778 polys = [] 

1779 dens = set() 

1780 failed = [] 

1781 result = [] 

1782 solved_syms = [] 

1783 linear = True 

1784 manual = flags.get('manual', False) 

1785 checkdens = check = flags.get('check', True) 

1786 

1787 for j, g in enumerate(exprs): 

1788 dens.update(_simple_dens(g, symbols)) 

1789 i, d = _invert(g, *symbols) 

1790 if d in symbols: 

1791 if linear: 

1792 linear = solve_linear(g, 0, [d])[0] == d 

1793 g = d - i 

1794 g = g.as_numer_denom()[0] 

1795 if manual: 

1796 failed.append(g) 

1797 continue 

1798 

1799 poly = g.as_poly(*symbols, extension=True) 

1800 

1801 if poly is not None: 

1802 polys.append(poly) 

1803 else: 

1804 failed.append(g) 

1805 

1806 if polys: 

1807 if all(p.is_linear for p in polys): 

1808 n, m = len(polys), len(symbols) 

1809 matrix = zeros(n, m + 1) 

1810 

1811 for i, poly in enumerate(polys): 

1812 for monom, coeff in poly.terms(): 

1813 try: 

1814 j = monom.index(1) 

1815 matrix[i, j] = coeff 

1816 except ValueError: 

1817 matrix[i, m] = -coeff 

1818 

1819 # returns a dictionary ({symbols: values}) or None 

1820 if flags.pop('particular', False): 

1821 result = minsolve_linear_system(matrix, *symbols, **flags) 

1822 else: 

1823 result = solve_linear_system(matrix, *symbols, **flags) 

1824 result = [result] if result else [] 

1825 if failed: 

1826 if result: 

1827 solved_syms = list(result[0].keys()) # there is only one result dict 

1828 else: 

1829 solved_syms = [] 

1830 # linear doesn't change 

1831 else: 

1832 linear = False 

1833 if len(symbols) > len(polys): 

1834 

1835 free = set().union(*[p.free_symbols for p in polys]) 

1836 free = list(ordered(free.intersection(symbols))) 

1837 got_s = set() 

1838 result = [] 

1839 for syms in subsets(free, len(polys)): 

1840 try: 

1841 # returns [], None or list of tuples 

1842 res = solve_poly_system(polys, *syms) 

1843 if res: 

1844 for r in set(res): 

1845 skip = False 

1846 for r1 in r: 

1847 if got_s and any(ss in r1.free_symbols 

1848 for ss in got_s): 

1849 # sol depends on previously 

1850 # solved symbols: discard it 

1851 skip = True 

1852 if not skip: 

1853 got_s.update(syms) 

1854 result.append(dict(list(zip(syms, r)))) 

1855 except NotImplementedError: 

1856 pass 

1857 if got_s: 

1858 solved_syms = list(got_s) 

1859 else: 

1860 raise NotImplementedError('no valid subset found') 

1861 else: 

1862 try: 

1863 result = solve_poly_system(polys, *symbols) 

1864 if result: 

1865 solved_syms = symbols 

1866 result = [dict(list(zip(solved_syms, r))) for r in set(result)] 

1867 except NotImplementedError: 

1868 failed.extend([g.as_expr() for g in polys]) 

1869 solved_syms = [] 

1870 

1871 # convert None or [] to [{}] 

1872 result = result or [{}] 

1873 

1874 if failed: 

1875 linear = False 

1876 # For each failed equation, see if we can solve for one of the 

1877 # remaining symbols from that equation. If so, we update the 

1878 # solution set and continue with the next failed equation, 

1879 # repeating until we are done or we get an equation that can't 

1880 # be solved. 

1881 def _ok_syms(e, sort=False): 

1882 rv = e.free_symbols & legal 

1883 

1884 # Solve first for symbols that have lower degree in the equation. 

1885 # Ideally we want to solve firstly for symbols that appear linearly 

1886 # with rational coefficients e.g. if e = x*y + z then we should 

1887 # solve for z first. 

1888 def key(sym): 

1889 ep = e.as_poly(sym) 

1890 if ep is None: 

1891 complexity = (S.Infinity, S.Infinity, S.Infinity) 

1892 else: 

1893 coeff_syms = ep.LC().free_symbols 

1894 complexity = (ep.degree(), len(coeff_syms & rv), len(coeff_syms)) 

1895 return complexity + (default_sort_key(sym),) 

1896 

1897 if sort: 

1898 rv = sorted(rv, key=key) 

1899 return rv 

1900 

1901 legal = set(symbols) # what we are interested in 

1902 # sort so equation with the fewest potential symbols is first 

1903 u = Dummy() # used in solution checking 

1904 for eq in ordered(failed, lambda _: len(_ok_syms(_))): 

1905 newresult = [] 

1906 bad_results = [] 

1907 hit = False 

1908 for r in result: 

1909 got_s = set() 

1910 # update eq with everything that is known so far 

1911 eq2 = eq.subs(r) 

1912 # if check is True then we see if it satisfies this 

1913 # equation, otherwise we just accept it 

1914 if check and r: 

1915 b = checksol(u, u, eq2, minimal=True) 

1916 if b is not None: 

1917 # this solution is sufficient to know whether 

1918 # it is valid or not so we either accept or 

1919 # reject it, then continue 

1920 if b: 

1921 newresult.append(r) 

1922 else: 

1923 bad_results.append(r) 

1924 continue 

1925 # search for a symbol amongst those available that 

1926 # can be solved for 

1927 ok_syms = _ok_syms(eq2, sort=True) 

1928 if not ok_syms: 

1929 if r: 

1930 newresult.append(r) 

1931 break # skip as it's independent of desired symbols 

1932 for s in ok_syms: 

1933 try: 

1934 soln = _vsolve(eq2, s, **flags) 

1935 except NotImplementedError: 

1936 continue 

1937 # put each solution in r and append the now-expanded 

1938 # result in the new result list; use copy since the 

1939 # solution for s is being added in-place 

1940 for sol in soln: 

1941 if got_s and any(ss in sol.free_symbols for ss in got_s): 

1942 # sol depends on previously solved symbols: discard it 

1943 continue 

1944 rnew = r.copy() 

1945 for k, v in r.items(): 

1946 rnew[k] = v.subs(s, sol) 

1947 # and add this new solution 

1948 rnew[s] = sol 

1949 # check that it is independent of previous solutions 

1950 iset = set(rnew.items()) 

1951 for i in newresult: 

1952 if len(i) < len(iset) and not set(i.items()) - iset: 

1953 # this is a superset of a known solution that 

1954 # is smaller 

1955 break 

1956 else: 

1957 # keep it 

1958 newresult.append(rnew) 

1959 hit = True 

1960 got_s.add(s) 

1961 if not hit: 

1962 raise NotImplementedError('could not solve %s' % eq2) 

1963 else: 

1964 result = newresult 

1965 for b in bad_results: 

1966 if b in result: 

1967 result.remove(b) 

1968 

1969 if not result: 

1970 return False, [] 

1971 

1972 # rely on linear/polynomial system solvers to simplify 

1973 # XXX the following tests show that the expressions 

1974 # returned are not the same as they would be if simplify 

1975 # were applied to this: 

1976 # sympy/solvers/ode/tests/test_systems/test__classify_linear_system 

1977 # sympy/solvers/tests/test_solvers/test_issue_4886 

1978 # so the docs should be updated to reflect that or else 

1979 # the following should be `bool(failed) or not linear` 

1980 default_simplify = bool(failed) 

1981 if flags.get('simplify', default_simplify): 

1982 for r in result: 

1983 for k in r: 

1984 r[k] = simplify(r[k]) 

1985 flags['simplify'] = False # don't need to do so in checksol now 

1986 

1987 if checkdens: 

1988 result = [r for r in result 

1989 if not any(checksol(d, r, **flags) for d in dens)] 

1990 

1991 if check and not linear: 

1992 result = [r for r in result 

1993 if not any(checksol(e, r, **flags) is False for e in exprs)] 

1994 

1995 result = [r for r in result if r] 

1996 return linear, result 

1997 

1998 

1999def solve_linear(lhs, rhs=0, symbols=[], exclude=[]): 

2000 r""" 

2001 Return a tuple derived from ``f = lhs - rhs`` that is one of 

2002 the following: ``(0, 1)``, ``(0, 0)``, ``(symbol, solution)``, ``(n, d)``. 

2003 

2004 Explanation 

2005 =========== 

2006 

2007 ``(0, 1)`` meaning that ``f`` is independent of the symbols in *symbols* 

2008 that are not in *exclude*. 

2009 

2010 ``(0, 0)`` meaning that there is no solution to the equation amongst the 

2011 symbols given. If the first element of the tuple is not zero, then the 

2012 function is guaranteed to be dependent on a symbol in *symbols*. 

2013 

2014 ``(symbol, solution)`` where symbol appears linearly in the numerator of 

2015 ``f``, is in *symbols* (if given), and is not in *exclude* (if given). No 

2016 simplification is done to ``f`` other than a ``mul=True`` expansion, so the 

2017 solution will correspond strictly to a unique solution. 

2018 

2019 ``(n, d)`` where ``n`` and ``d`` are the numerator and denominator of ``f`` 

2020 when the numerator was not linear in any symbol of interest; ``n`` will 

2021 never be a symbol unless a solution for that symbol was found (in which case 

2022 the second element is the solution, not the denominator). 

2023 

2024 Examples 

2025 ======== 

2026 

2027 >>> from sympy import cancel, Pow 

2028 

2029 ``f`` is independent of the symbols in *symbols* that are not in 

2030 *exclude*: 

2031 

2032 >>> from sympy import cos, sin, solve_linear 

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

2034 >>> eq = y*cos(x)**2 + y*sin(x)**2 - y # = y*(1 - 1) = 0 

2035 >>> solve_linear(eq) 

2036 (0, 1) 

2037 >>> eq = cos(x)**2 + sin(x)**2 # = 1 

2038 >>> solve_linear(eq) 

2039 (0, 1) 

2040 >>> solve_linear(x, exclude=[x]) 

2041 (0, 1) 

2042 

2043 The variable ``x`` appears as a linear variable in each of the 

2044 following: 

2045 

2046 >>> solve_linear(x + y**2) 

2047 (x, -y**2) 

2048 >>> solve_linear(1/x - y**2) 

2049 (x, y**(-2)) 

2050 

2051 When not linear in ``x`` or ``y`` then the numerator and denominator are 

2052 returned: 

2053 

2054 >>> solve_linear(x**2/y**2 - 3) 

2055 (x**2 - 3*y**2, y**2) 

2056 

2057 If the numerator of the expression is a symbol, then ``(0, 0)`` is 

2058 returned if the solution for that symbol would have set any 

2059 denominator to 0: 

2060 

2061 >>> eq = 1/(1/x - 2) 

2062 >>> eq.as_numer_denom() 

2063 (x, 1 - 2*x) 

2064 >>> solve_linear(eq) 

2065 (0, 0) 

2066 

2067 But automatic rewriting may cause a symbol in the denominator to 

2068 appear in the numerator so a solution will be returned: 

2069 

2070 >>> (1/x)**-1 

2071 x 

2072 >>> solve_linear((1/x)**-1) 

2073 (x, 0) 

2074 

2075 Use an unevaluated expression to avoid this: 

2076 

2077 >>> solve_linear(Pow(1/x, -1, evaluate=False)) 

2078 (0, 0) 

2079 

2080 If ``x`` is allowed to cancel in the following expression, then it 

2081 appears to be linear in ``x``, but this sort of cancellation is not 

2082 done by ``solve_linear`` so the solution will always satisfy the 

2083 original expression without causing a division by zero error. 

2084 

2085 >>> eq = x**2*(1/x - z**2/x) 

2086 >>> solve_linear(cancel(eq)) 

2087 (x, 0) 

2088 >>> solve_linear(eq) 

2089 (x**2*(1 - z**2), x) 

2090 

2091 A list of symbols for which a solution is desired may be given: 

2092 

2093 >>> solve_linear(x + y + z, symbols=[y]) 

2094 (y, -x - z) 

2095 

2096 A list of symbols to ignore may also be given: 

2097 

2098 >>> solve_linear(x + y + z, exclude=[x]) 

2099 (y, -x - z) 

2100 

2101 (A solution for ``y`` is obtained because it is the first variable 

2102 from the canonically sorted list of symbols that had a linear 

2103 solution.) 

2104 

2105 """ 

2106 if isinstance(lhs, Eq): 

2107 if rhs: 

2108 raise ValueError(filldedent(''' 

2109 If lhs is an Equality, rhs must be 0 but was %s''' % rhs)) 

2110 rhs = lhs.rhs 

2111 lhs = lhs.lhs 

2112 dens = None 

2113 eq = lhs - rhs 

2114 n, d = eq.as_numer_denom() 

2115 if not n: 

2116 return S.Zero, S.One 

2117 

2118 free = n.free_symbols 

2119 if not symbols: 

2120 symbols = free 

2121 else: 

2122 bad = [s for s in symbols if not s.is_Symbol] 

2123 if bad: 

2124 if len(bad) == 1: 

2125 bad = bad[0] 

2126 if len(symbols) == 1: 

2127 eg = 'solve(%s, %s)' % (eq, symbols[0]) 

2128 else: 

2129 eg = 'solve(%s, *%s)' % (eq, list(symbols)) 

2130 raise ValueError(filldedent(''' 

2131 solve_linear only handles symbols, not %s. To isolate 

2132 non-symbols use solve, e.g. >>> %s <<<. 

2133 ''' % (bad, eg))) 

2134 symbols = free.intersection(symbols) 

2135 symbols = symbols.difference(exclude) 

2136 if not symbols: 

2137 return S.Zero, S.One 

2138 

2139 # derivatives are easy to do but tricky to analyze to see if they 

2140 # are going to disallow a linear solution, so for simplicity we 

2141 # just evaluate the ones that have the symbols of interest 

2142 derivs = defaultdict(list) 

2143 for der in n.atoms(Derivative): 

2144 csym = der.free_symbols & symbols 

2145 for c in csym: 

2146 derivs[c].append(der) 

2147 

2148 all_zero = True 

2149 for xi in sorted(symbols, key=default_sort_key): # canonical order 

2150 # if there are derivatives in this var, calculate them now 

2151 if isinstance(derivs[xi], list): 

2152 derivs[xi] = {der: der.doit() for der in derivs[xi]} 

2153 newn = n.subs(derivs[xi]) 

2154 dnewn_dxi = newn.diff(xi) 

2155 # dnewn_dxi can be nonzero if it survives differentation by any 

2156 # of its free symbols 

2157 free = dnewn_dxi.free_symbols 

2158 if dnewn_dxi and (not free or any(dnewn_dxi.diff(s) for s in free) or free == symbols): 

2159 all_zero = False 

2160 if dnewn_dxi is S.NaN: 

2161 break 

2162 if xi not in dnewn_dxi.free_symbols: 

2163 vi = -1/dnewn_dxi*(newn.subs(xi, 0)) 

2164 if dens is None: 

2165 dens = _simple_dens(eq, symbols) 

2166 if not any(checksol(di, {xi: vi}, minimal=True) is True 

2167 for di in dens): 

2168 # simplify any trivial integral 

2169 irep = [(i, i.doit()) for i in vi.atoms(Integral) if 

2170 i.function.is_number] 

2171 # do a slight bit of simplification 

2172 vi = expand_mul(vi.subs(irep)) 

2173 return xi, vi 

2174 if all_zero: 

2175 return S.Zero, S.One 

2176 if n.is_Symbol: # no solution for this symbol was found 

2177 return S.Zero, S.Zero 

2178 return n, d 

2179 

2180 

2181def minsolve_linear_system(system, *symbols, **flags): 

2182 r""" 

2183 Find a particular solution to a linear system. 

2184 

2185 Explanation 

2186 =========== 

2187 

2188 In particular, try to find a solution with the minimal possible number 

2189 of non-zero variables using a naive algorithm with exponential complexity. 

2190 If ``quick=True``, a heuristic is used. 

2191 

2192 """ 

2193 quick = flags.get('quick', False) 

2194 # Check if there are any non-zero solutions at all 

2195 s0 = solve_linear_system(system, *symbols, **flags) 

2196 if not s0 or all(v == 0 for v in s0.values()): 

2197 return s0 

2198 if quick: 

2199 # We just solve the system and try to heuristically find a nice 

2200 # solution. 

2201 s = solve_linear_system(system, *symbols) 

2202 def update(determined, solution): 

2203 delete = [] 

2204 for k, v in solution.items(): 

2205 solution[k] = v.subs(determined) 

2206 if not solution[k].free_symbols: 

2207 delete.append(k) 

2208 determined[k] = solution[k] 

2209 for k in delete: 

2210 del solution[k] 

2211 determined = {} 

2212 update(determined, s) 

2213 while s: 

2214 # NOTE sort by default_sort_key to get deterministic result 

2215 k = max((k for k in s.values()), 

2216 key=lambda x: (len(x.free_symbols), default_sort_key(x))) 

2217 kfree = k.free_symbols 

2218 x = next(reversed(list(ordered(kfree)))) 

2219 if len(kfree) != 1: 

2220 determined[x] = S.Zero 

2221 else: 

2222 val = _vsolve(k, x, check=False)[0] 

2223 if not val and not any(v.subs(x, val) for v in s.values()): 

2224 determined[x] = S.One 

2225 else: 

2226 determined[x] = val 

2227 update(determined, s) 

2228 return determined 

2229 else: 

2230 # We try to select n variables which we want to be non-zero. 

2231 # All others will be assumed zero. We try to solve the modified system. 

2232 # If there is a non-trivial solution, just set the free variables to 

2233 # one. If we do this for increasing n, trying all combinations of 

2234 # variables, we will find an optimal solution. 

2235 # We speed up slightly by starting at one less than the number of 

2236 # variables the quick method manages. 

2237 N = len(symbols) 

2238 bestsol = minsolve_linear_system(system, *symbols, quick=True) 

2239 n0 = len([x for x in bestsol.values() if x != 0]) 

2240 for n in range(n0 - 1, 1, -1): 

2241 debugf('minsolve: %s', n) 

2242 thissol = None 

2243 for nonzeros in combinations(range(N), n): 

2244 subm = Matrix([system.col(i).T for i in nonzeros] + [system.col(-1).T]).T 

2245 s = solve_linear_system(subm, *[symbols[i] for i in nonzeros]) 

2246 if s and not all(v == 0 for v in s.values()): 

2247 subs = [(symbols[v], S.One) for v in nonzeros] 

2248 for k, v in s.items(): 

2249 s[k] = v.subs(subs) 

2250 for sym in symbols: 

2251 if sym not in s: 

2252 if symbols.index(sym) in nonzeros: 

2253 s[sym] = S.One 

2254 else: 

2255 s[sym] = S.Zero 

2256 thissol = s 

2257 break 

2258 if thissol is None: 

2259 break 

2260 bestsol = thissol 

2261 return bestsol 

2262 

2263 

2264def solve_linear_system(system, *symbols, **flags): 

2265 r""" 

2266 Solve system of $N$ linear equations with $M$ variables, which means 

2267 both under- and overdetermined systems are supported. 

2268 

2269 Explanation 

2270 =========== 

2271 

2272 The possible number of solutions is zero, one, or infinite. Respectively, 

2273 this procedure will return None or a dictionary with solutions. In the 

2274 case of underdetermined systems, all arbitrary parameters are skipped. 

2275 This may cause a situation in which an empty dictionary is returned. 

2276 In that case, all symbols can be assigned arbitrary values. 

2277 

2278 Input to this function is a $N\times M + 1$ matrix, which means it has 

2279 to be in augmented form. If you prefer to enter $N$ equations and $M$ 

2280 unknowns then use ``solve(Neqs, *Msymbols)`` instead. Note: a local 

2281 copy of the matrix is made by this routine so the matrix that is 

2282 passed will not be modified. 

2283 

2284 The algorithm used here is fraction-free Gaussian elimination, 

2285 which results, after elimination, in an upper-triangular matrix. 

2286 Then solutions are found using back-substitution. This approach 

2287 is more efficient and compact than the Gauss-Jordan method. 

2288 

2289 Examples 

2290 ======== 

2291 

2292 >>> from sympy import Matrix, solve_linear_system 

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

2294 

2295 Solve the following system:: 

2296 

2297 x + 4 y == 2 

2298 -2 x + y == 14 

2299 

2300 >>> system = Matrix(( (1, 4, 2), (-2, 1, 14))) 

2301 >>> solve_linear_system(system, x, y) 

2302 {x: -6, y: 2} 

2303 

2304 A degenerate system returns an empty dictionary: 

2305 

2306 >>> system = Matrix(( (0,0,0), (0,0,0) )) 

2307 >>> solve_linear_system(system, x, y) 

2308 {} 

2309 

2310 """ 

2311 assert system.shape[1] == len(symbols) + 1 

2312 

2313 # This is just a wrapper for solve_lin_sys 

2314 eqs = list(system * Matrix(symbols + (-1,))) 

2315 eqs, ring = sympy_eqs_to_ring(eqs, symbols) 

2316 sol = solve_lin_sys(eqs, ring, _raw=False) 

2317 if sol is not None: 

2318 sol = {sym:val for sym, val in sol.items() if sym != val} 

2319 return sol 

2320 

2321 

2322def solve_undetermined_coeffs(equ, coeffs, *syms, **flags): 

2323 r""" 

2324 Solve a system of equations in $k$ parameters that is formed by 

2325 matching coefficients in variables ``coeffs`` that are on 

2326 factors dependent on the remaining variables (or those given 

2327 explicitly by ``syms``. 

2328 

2329 Explanation 

2330 =========== 

2331 

2332 The result of this function is a dictionary with symbolic values of those 

2333 parameters with respect to coefficients in $q$ -- empty if there 

2334 is no solution or coefficients do not appear in the equation -- else 

2335 None (if the system was not recognized). If there is more than one 

2336 solution, the solutions are passed as a list. The output can be modified using 

2337 the same semantics as for `solve` since the flags that are passed are sent 

2338 directly to `solve` so, for example the flag ``dict=True`` will always return a list 

2339 of solutions as dictionaries. 

2340 

2341 This function accepts both Equality and Expr class instances. 

2342 The solving process is most efficient when symbols are specified 

2343 in addition to parameters to be determined, but an attempt to 

2344 determine them (if absent) will be made. If an expected solution is not 

2345 obtained (and symbols were not specified) try specifying them. 

2346 

2347 Examples 

2348 ======== 

2349 

2350 >>> from sympy import Eq, solve_undetermined_coeffs 

2351 >>> from sympy.abc import a, b, c, h, p, k, x, y 

2352 

2353 >>> solve_undetermined_coeffs(Eq(a*x + a + b, x/2), [a, b], x) 

2354 {a: 1/2, b: -1/2} 

2355 >>> solve_undetermined_coeffs(a - 2, [a]) 

2356 {a: 2} 

2357 

2358 The equation can be nonlinear in the symbols: 

2359 

2360 >>> X, Y, Z = y, x**y, y*x**y 

2361 >>> eq = a*X + b*Y + c*Z - X - 2*Y - 3*Z 

2362 >>> coeffs = a, b, c 

2363 >>> syms = x, y 

2364 >>> solve_undetermined_coeffs(eq, coeffs, syms) 

2365 {a: 1, b: 2, c: 3} 

2366 

2367 And the system can be nonlinear in coefficients, too, but if 

2368 there is only a single solution, it will be returned as a 

2369 dictionary: 

2370 

2371 >>> eq = a*x**2 + b*x + c - ((x - h)**2 + 4*p*k)/4/p 

2372 >>> solve_undetermined_coeffs(eq, (h, p, k), x) 

2373 {h: -b/(2*a), k: (4*a*c - b**2)/(4*a), p: 1/(4*a)} 

2374 

2375 Multiple solutions are always returned in a list: 

2376 

2377 >>> solve_undetermined_coeffs(a**2*x + b - x, [a, b], x) 

2378 [{a: -1, b: 0}, {a: 1, b: 0}] 

2379 

2380 Using flag ``dict=True`` (in keeping with semantics in :func:`~.solve`) 

2381 will force the result to always be a list with any solutions 

2382 as elements in that list. 

2383 

2384 >>> solve_undetermined_coeffs(a*x - 2*x, [a], dict=True) 

2385 [{a: 2}] 

2386 """ 

2387 if not (coeffs and all(i.is_Symbol for i in coeffs)): 

2388 raise ValueError('must provide symbols for coeffs') 

2389 

2390 if isinstance(equ, Eq): 

2391 eq = equ.lhs - equ.rhs 

2392 else: 

2393 eq = equ 

2394 

2395 ceq = cancel(eq) 

2396 xeq = _mexpand(ceq.as_numer_denom()[0], recursive=True) 

2397 

2398 free = xeq.free_symbols 

2399 coeffs = free & set(coeffs) 

2400 if not coeffs: 

2401 return ([], {}) if flags.get('set', None) else [] # solve(0, x) -> [] 

2402 

2403 if not syms: 

2404 # e.g. A*exp(x) + B - (exp(x) + y) separated into parts that 

2405 # don't/do depend on coeffs gives 

2406 # -(exp(x) + y), A*exp(x) + B 

2407 # then see what symbols are common to both 

2408 # {x} = {x, A, B} - {x, y} 

2409 ind, dep = xeq.as_independent(*coeffs, as_Add=True) 

2410 dfree = dep.free_symbols 

2411 syms = dfree & ind.free_symbols 

2412 if not syms: 

2413 # but if the system looks like (a + b)*x + b - c 

2414 # then {} = {a, b, x} - c 

2415 # so calculate {x} = {a, b, x} - {a, b} 

2416 syms = dfree - set(coeffs) 

2417 if not syms: 

2418 syms = [Dummy()] 

2419 else: 

2420 if len(syms) == 1 and iterable(syms[0]): 

2421 syms = syms[0] 

2422 e, s, _ = recast_to_symbols([xeq], syms) 

2423 xeq = e[0] 

2424 syms = s 

2425 

2426 # find the functional forms in which symbols appear 

2427 

2428 gens = set(xeq.as_coefficients_dict(*syms).keys()) - {1} 

2429 cset = set(coeffs) 

2430 if any(g.has_xfree(cset) for g in gens): 

2431 return # a generator contained a coefficient symbol 

2432 

2433 # make sure we are working with symbols for generators 

2434 

2435 e, gens, _ = recast_to_symbols([xeq], list(gens)) 

2436 xeq = e[0] 

2437 

2438 # collect coefficients in front of generators 

2439 

2440 system = list(collect(xeq, gens, evaluate=False).values()) 

2441 

2442 # get a solution 

2443 

2444 soln = solve(system, coeffs, **flags) 

2445 

2446 # unpack unless told otherwise if length is 1 

2447 

2448 settings = flags.get('dict', None) or flags.get('set', None) 

2449 if type(soln) is dict or settings or len(soln) != 1: 

2450 return soln 

2451 return soln[0] 

2452 

2453 

2454def solve_linear_system_LU(matrix, syms): 

2455 """ 

2456 Solves the augmented matrix system using ``LUsolve`` and returns a 

2457 dictionary in which solutions are keyed to the symbols of *syms* as ordered. 

2458 

2459 Explanation 

2460 =========== 

2461 

2462 The matrix must be invertible. 

2463 

2464 Examples 

2465 ======== 

2466 

2467 >>> from sympy import Matrix, solve_linear_system_LU 

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

2469 

2470 >>> solve_linear_system_LU(Matrix([ 

2471 ... [1, 2, 0, 1], 

2472 ... [3, 2, 2, 1], 

2473 ... [2, 0, 0, 1]]), [x, y, z]) 

2474 {x: 1/2, y: 1/4, z: -1/2} 

2475 

2476 See Also 

2477 ======== 

2478 

2479 LUsolve 

2480 

2481 """ 

2482 if matrix.rows != matrix.cols - 1: 

2483 raise ValueError("Rows should be equal to columns - 1") 

2484 A = matrix[:matrix.rows, :matrix.rows] 

2485 b = matrix[:, matrix.cols - 1:] 

2486 soln = A.LUsolve(b) 

2487 solutions = {} 

2488 for i in range(soln.rows): 

2489 solutions[syms[i]] = soln[i, 0] 

2490 return solutions 

2491 

2492 

2493def det_perm(M): 

2494 """ 

2495 Return the determinant of *M* by using permutations to select factors. 

2496 

2497 Explanation 

2498 =========== 

2499 

2500 For sizes larger than 8 the number of permutations becomes prohibitively 

2501 large, or if there are no symbols in the matrix, it is better to use the 

2502 standard determinant routines (e.g., ``M.det()``.) 

2503 

2504 See Also 

2505 ======== 

2506 

2507 det_minor 

2508 det_quick 

2509 

2510 """ 

2511 args = [] 

2512 s = True 

2513 n = M.rows 

2514 list_ = M.flat() 

2515 for perm in generate_bell(n): 

2516 fac = [] 

2517 idx = 0 

2518 for j in perm: 

2519 fac.append(list_[idx + j]) 

2520 idx += n 

2521 term = Mul(*fac) # disaster with unevaluated Mul -- takes forever for n=7 

2522 args.append(term if s else -term) 

2523 s = not s 

2524 return Add(*args) 

2525 

2526 

2527def det_minor(M): 

2528 """ 

2529 Return the ``det(M)`` computed from minors without 

2530 introducing new nesting in products. 

2531 

2532 See Also 

2533 ======== 

2534 

2535 det_perm 

2536 det_quick 

2537 

2538 """ 

2539 n = M.rows 

2540 if n == 2: 

2541 return M[0, 0]*M[1, 1] - M[1, 0]*M[0, 1] 

2542 else: 

2543 return sum([(1, -1)[i % 2]*Add(*[M[0, i]*d for d in 

2544 Add.make_args(det_minor(M.minor_submatrix(0, i)))]) 

2545 if M[0, i] else S.Zero for i in range(n)]) 

2546 

2547 

2548def det_quick(M, method=None): 

2549 """ 

2550 Return ``det(M)`` assuming that either 

2551 there are lots of zeros or the size of the matrix 

2552 is small. If this assumption is not met, then the normal 

2553 Matrix.det function will be used with method = ``method``. 

2554 

2555 See Also 

2556 ======== 

2557 

2558 det_minor 

2559 det_perm 

2560 

2561 """ 

2562 if any(i.has(Symbol) for i in M): 

2563 if M.rows < 8 and all(i.has(Symbol) for i in M): 

2564 return det_perm(M) 

2565 return det_minor(M) 

2566 else: 

2567 return M.det(method=method) if method else M.det() 

2568 

2569 

2570def inv_quick(M): 

2571 """Return the inverse of ``M``, assuming that either 

2572 there are lots of zeros or the size of the matrix 

2573 is small. 

2574 """ 

2575 if not all(i.is_Number for i in M): 

2576 if not any(i.is_Number for i in M): 

2577 det = lambda _: det_perm(_) 

2578 else: 

2579 det = lambda _: det_minor(_) 

2580 else: 

2581 return M.inv() 

2582 n = M.rows 

2583 d = det(M) 

2584 if d == S.Zero: 

2585 raise NonInvertibleMatrixError("Matrix det == 0; not invertible") 

2586 ret = zeros(n) 

2587 s1 = -1 

2588 for i in range(n): 

2589 s = s1 = -s1 

2590 for j in range(n): 

2591 di = det(M.minor_submatrix(i, j)) 

2592 ret[j, i] = s*di/d 

2593 s = -s 

2594 return ret 

2595 

2596 

2597# these are functions that have multiple inverse values per period 

2598multi_inverses = { 

2599 sin: lambda x: (asin(x), S.Pi - asin(x)), 

2600 cos: lambda x: (acos(x), 2*S.Pi - acos(x)), 

2601} 

2602 

2603 

2604def _vsolve(e, s, **flags): 

2605 """return list of scalar values for the solution of e for symbol s""" 

2606 return [i[s] for i in _solve(e, s, **flags)] 

2607 

2608 

2609def _tsolve(eq, sym, **flags): 

2610 """ 

2611 Helper for ``_solve`` that solves a transcendental equation with respect 

2612 to the given symbol. Various equations containing powers and logarithms, 

2613 can be solved. 

2614 

2615 There is currently no guarantee that all solutions will be returned or 

2616 that a real solution will be favored over a complex one. 

2617 

2618 Either a list of potential solutions will be returned or None will be 

2619 returned (in the case that no method was known to get a solution 

2620 for the equation). All other errors (like the inability to cast an 

2621 expression as a Poly) are unhandled. 

2622 

2623 Examples 

2624 ======== 

2625 

2626 >>> from sympy import log, ordered 

2627 >>> from sympy.solvers.solvers import _tsolve as tsolve 

2628 >>> from sympy.abc import x 

2629 

2630 >>> list(ordered(tsolve(3**(2*x + 5) - 4, x))) 

2631 [-5/2 + log(2)/log(3), (-5*log(3)/2 + log(2) + I*pi)/log(3)] 

2632 

2633 >>> tsolve(log(x) + 2*x, x) 

2634 [LambertW(2)/2] 

2635 

2636 """ 

2637 if 'tsolve_saw' not in flags: 

2638 flags['tsolve_saw'] = [] 

2639 if eq in flags['tsolve_saw']: 

2640 return None 

2641 else: 

2642 flags['tsolve_saw'].append(eq) 

2643 

2644 rhs, lhs = _invert(eq, sym) 

2645 

2646 if lhs == sym: 

2647 return [rhs] 

2648 try: 

2649 if lhs.is_Add: 

2650 # it's time to try factoring; powdenest is used 

2651 # to try get powers in standard form for better factoring 

2652 f = factor(powdenest(lhs - rhs)) 

2653 if f.is_Mul: 

2654 return _vsolve(f, sym, **flags) 

2655 if rhs: 

2656 f = logcombine(lhs, force=flags.get('force', True)) 

2657 if f.count(log) != lhs.count(log): 

2658 if isinstance(f, log): 

2659 return _vsolve(f.args[0] - exp(rhs), sym, **flags) 

2660 return _tsolve(f - rhs, sym, **flags) 

2661 

2662 elif lhs.is_Pow: 

2663 if lhs.exp.is_Integer: 

2664 if lhs - rhs != eq: 

2665 return _vsolve(lhs - rhs, sym, **flags) 

2666 

2667 if sym not in lhs.exp.free_symbols: 

2668 return _vsolve(lhs.base - rhs**(1/lhs.exp), sym, **flags) 

2669 

2670 # _tsolve calls this with Dummy before passing the actual number in. 

2671 if any(t.is_Dummy for t in rhs.free_symbols): 

2672 raise NotImplementedError # _tsolve will call here again... 

2673 

2674 # a ** g(x) == 0 

2675 if not rhs: 

2676 # f(x)**g(x) only has solutions where f(x) == 0 and g(x) != 0 at 

2677 # the same place 

2678 sol_base = _vsolve(lhs.base, sym, **flags) 

2679 return [s for s in sol_base if lhs.exp.subs(sym, s) != 0] # XXX use checksol here? 

2680 

2681 # a ** g(x) == b 

2682 if not lhs.base.has(sym): 

2683 if lhs.base == 0: 

2684 return _vsolve(lhs.exp, sym, **flags) if rhs != 0 else [] 

2685 

2686 # Gets most solutions... 

2687 if lhs.base == rhs.as_base_exp()[0]: 

2688 # handles case when bases are equal 

2689 sol = _vsolve(lhs.exp - rhs.as_base_exp()[1], sym, **flags) 

2690 else: 

2691 # handles cases when bases are not equal and exp 

2692 # may or may not be equal 

2693 f = exp(log(lhs.base)*lhs.exp) - exp(log(rhs)) 

2694 sol = _vsolve(f, sym, **flags) 

2695 

2696 # Check for duplicate solutions 

2697 def equal(expr1, expr2): 

2698 _ = Dummy() 

2699 eq = checksol(expr1 - _, _, expr2) 

2700 if eq is None: 

2701 if nsimplify(expr1) != nsimplify(expr2): 

2702 return False 

2703 # they might be coincidentally the same 

2704 # so check more rigorously 

2705 eq = expr1.equals(expr2) # XXX expensive but necessary? 

2706 return eq 

2707 

2708 # Guess a rational exponent 

2709 e_rat = nsimplify(log(abs(rhs))/log(abs(lhs.base))) 

2710 e_rat = simplify(posify(e_rat)[0]) 

2711 n, d = fraction(e_rat) 

2712 if expand(lhs.base**n - rhs**d) == 0: 

2713 sol = [s for s in sol if not equal(lhs.exp.subs(sym, s), e_rat)] 

2714 sol.extend(_vsolve(lhs.exp - e_rat, sym, **flags)) 

2715 

2716 return list(set(sol)) 

2717 

2718 # f(x) ** g(x) == c 

2719 else: 

2720 sol = [] 

2721 logform = lhs.exp*log(lhs.base) - log(rhs) 

2722 if logform != lhs - rhs: 

2723 try: 

2724 sol.extend(_vsolve(logform, sym, **flags)) 

2725 except NotImplementedError: 

2726 pass 

2727 

2728 # Collect possible solutions and check with substitution later. 

2729 check = [] 

2730 if rhs == 1: 

2731 # f(x) ** g(x) = 1 -- g(x)=0 or f(x)=+-1 

2732 check.extend(_vsolve(lhs.exp, sym, **flags)) 

2733 check.extend(_vsolve(lhs.base - 1, sym, **flags)) 

2734 check.extend(_vsolve(lhs.base + 1, sym, **flags)) 

2735 elif rhs.is_Rational: 

2736 for d in (i for i in divisors(abs(rhs.p)) if i != 1): 

2737 e, t = integer_log(rhs.p, d) 

2738 if not t: 

2739 continue # rhs.p != d**b 

2740 for s in divisors(abs(rhs.q)): 

2741 if s**e== rhs.q: 

2742 r = Rational(d, s) 

2743 check.extend(_vsolve(lhs.base - r, sym, **flags)) 

2744 check.extend(_vsolve(lhs.base + r, sym, **flags)) 

2745 check.extend(_vsolve(lhs.exp - e, sym, **flags)) 

2746 elif rhs.is_irrational: 

2747 b_l, e_l = lhs.base.as_base_exp() 

2748 n, d = (e_l*lhs.exp).as_numer_denom() 

2749 b, e = sqrtdenest(rhs).as_base_exp() 

2750 check = [sqrtdenest(i) for i in (_vsolve(lhs.base - b, sym, **flags))] 

2751 check.extend([sqrtdenest(i) for i in (_vsolve(lhs.exp - e, sym, **flags))]) 

2752 if e_l*d != 1: 

2753 check.extend(_vsolve(b_l**n - rhs**(e_l*d), sym, **flags)) 

2754 for s in check: 

2755 ok = checksol(eq, sym, s) 

2756 if ok is None: 

2757 ok = eq.subs(sym, s).equals(0) 

2758 if ok: 

2759 sol.append(s) 

2760 return list(set(sol)) 

2761 

2762 elif lhs.is_Function and len(lhs.args) == 1: 

2763 if lhs.func in multi_inverses: 

2764 # sin(x) = 1/3 -> x - asin(1/3) & x - (pi - asin(1/3)) 

2765 soln = [] 

2766 for i in multi_inverses[type(lhs)](rhs): 

2767 soln.extend(_vsolve(lhs.args[0] - i, sym, **flags)) 

2768 return list(set(soln)) 

2769 elif lhs.func == LambertW: 

2770 return _vsolve(lhs.args[0] - rhs*exp(rhs), sym, **flags) 

2771 

2772 rewrite = lhs.rewrite(exp) 

2773 if rewrite != lhs: 

2774 return _vsolve(rewrite - rhs, sym, **flags) 

2775 except NotImplementedError: 

2776 pass 

2777 

2778 # maybe it is a lambert pattern 

2779 if flags.pop('bivariate', True): 

2780 # lambert forms may need some help being recognized, e.g. changing 

2781 # 2**(3*x) + x**3*log(2)**3 + 3*x**2*log(2)**2 + 3*x*log(2) + 1 

2782 # to 2**(3*x) + (x*log(2) + 1)**3 

2783 

2784 # make generator in log have exponent of 1 

2785 logs = eq.atoms(log) 

2786 spow = min( 

2787 {i.exp for j in logs for i in j.atoms(Pow) 

2788 if i.base == sym} or {1}) 

2789 if spow != 1: 

2790 p = sym**spow 

2791 u = Dummy('bivariate-cov') 

2792 ueq = eq.subs(p, u) 

2793 if not ueq.has_free(sym): 

2794 sol = _vsolve(ueq, u, **flags) 

2795 inv = _vsolve(p - u, sym) 

2796 return [i.subs(u, s) for i in inv for s in sol] 

2797 

2798 g = _filtered_gens(eq.as_poly(), sym) 

2799 up_or_log = set() 

2800 for gi in g: 

2801 if isinstance(gi, (exp, log)) or (gi.is_Pow and gi.base == S.Exp1): 

2802 up_or_log.add(gi) 

2803 elif gi.is_Pow: 

2804 gisimp = powdenest(expand_power_exp(gi)) 

2805 if gisimp.is_Pow and sym in gisimp.exp.free_symbols: 

2806 up_or_log.add(gi) 

2807 eq_down = expand_log(expand_power_exp(eq)).subs( 

2808 dict(list(zip(up_or_log, [0]*len(up_or_log))))) 

2809 eq = expand_power_exp(factor(eq_down, deep=True) + (eq - eq_down)) 

2810 rhs, lhs = _invert(eq, sym) 

2811 if lhs.has(sym): 

2812 try: 

2813 poly = lhs.as_poly() 

2814 g = _filtered_gens(poly, sym) 

2815 _eq = lhs - rhs 

2816 sols = _solve_lambert(_eq, sym, g) 

2817 # use a simplified form if it satisfies eq 

2818 # and has fewer operations 

2819 for n, s in enumerate(sols): 

2820 ns = nsimplify(s) 

2821 if ns != s and ns.count_ops() <= s.count_ops(): 

2822 ok = checksol(_eq, sym, ns) 

2823 if ok is None: 

2824 ok = _eq.subs(sym, ns).equals(0) 

2825 if ok: 

2826 sols[n] = ns 

2827 return sols 

2828 except NotImplementedError: 

2829 # maybe it's a convoluted function 

2830 if len(g) == 2: 

2831 try: 

2832 gpu = bivariate_type(lhs - rhs, *g) 

2833 if gpu is None: 

2834 raise NotImplementedError 

2835 g, p, u = gpu 

2836 flags['bivariate'] = False 

2837 inversion = _tsolve(g - u, sym, **flags) 

2838 if inversion: 

2839 sol = _vsolve(p, u, **flags) 

2840 return list({i.subs(u, s) 

2841 for i in inversion for s in sol}) 

2842 except NotImplementedError: 

2843 pass 

2844 else: 

2845 pass 

2846 

2847 if flags.pop('force', True): 

2848 flags['force'] = False 

2849 pos, reps = posify(lhs - rhs) 

2850 if rhs == S.ComplexInfinity: 

2851 return [] 

2852 for u, s in reps.items(): 

2853 if s == sym: 

2854 break 

2855 else: 

2856 u = sym 

2857 if pos.has(u): 

2858 try: 

2859 soln = _vsolve(pos, u, **flags) 

2860 return [s.subs(reps) for s in soln] 

2861 except NotImplementedError: 

2862 pass 

2863 else: 

2864 pass # here for coverage 

2865 

2866 return # here for coverage 

2867 

2868 

2869# TODO: option for calculating J numerically 

2870 

2871@conserve_mpmath_dps 

2872def nsolve(*args, dict=False, **kwargs): 

2873 r""" 

2874 Solve a nonlinear equation system numerically: ``nsolve(f, [args,] x0, 

2875 modules=['mpmath'], **kwargs)``. 

2876 

2877 Explanation 

2878 =========== 

2879 

2880 ``f`` is a vector function of symbolic expressions representing the system. 

2881 *args* are the variables. If there is only one variable, this argument can 

2882 be omitted. ``x0`` is a starting vector close to a solution. 

2883 

2884 Use the modules keyword to specify which modules should be used to 

2885 evaluate the function and the Jacobian matrix. Make sure to use a module 

2886 that supports matrices. For more information on the syntax, please see the 

2887 docstring of ``lambdify``. 

2888 

2889 If the keyword arguments contain ``dict=True`` (default is False) ``nsolve`` 

2890 will return a list (perhaps empty) of solution mappings. This might be 

2891 especially useful if you want to use ``nsolve`` as a fallback to solve since 

2892 using the dict argument for both methods produces return values of 

2893 consistent type structure. Please note: to keep this consistent with 

2894 ``solve``, the solution will be returned in a list even though ``nsolve`` 

2895 (currently at least) only finds one solution at a time. 

2896 

2897 Overdetermined systems are supported. 

2898 

2899 Examples 

2900 ======== 

2901 

2902 >>> from sympy import Symbol, nsolve 

2903 >>> import mpmath 

2904 >>> mpmath.mp.dps = 15 

2905 >>> x1 = Symbol('x1') 

2906 >>> x2 = Symbol('x2') 

2907 >>> f1 = 3 * x1**2 - 2 * x2**2 - 1 

2908 >>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8 

2909 >>> print(nsolve((f1, f2), (x1, x2), (-1, 1))) 

2910 Matrix([[-1.19287309935246], [1.27844411169911]]) 

2911 

2912 For one-dimensional functions the syntax is simplified: 

2913 

2914 >>> from sympy import sin, nsolve 

2915 >>> from sympy.abc import x 

2916 >>> nsolve(sin(x), x, 2) 

2917 3.14159265358979 

2918 >>> nsolve(sin(x), 2) 

2919 3.14159265358979 

2920 

2921 To solve with higher precision than the default, use the prec argument: 

2922 

2923 >>> from sympy import cos 

2924 >>> nsolve(cos(x) - x, 1) 

2925 0.739085133215161 

2926 >>> nsolve(cos(x) - x, 1, prec=50) 

2927 0.73908513321516064165531208767387340401341175890076 

2928 >>> cos(_) 

2929 0.73908513321516064165531208767387340401341175890076 

2930 

2931 To solve for complex roots of real functions, a nonreal initial point 

2932 must be specified: 

2933 

2934 >>> from sympy import I 

2935 >>> nsolve(x**2 + 2, I) 

2936 1.4142135623731*I 

2937 

2938 ``mpmath.findroot`` is used and you can find their more extensive 

2939 documentation, especially concerning keyword parameters and 

2940 available solvers. Note, however, that functions which are very 

2941 steep near the root, the verification of the solution may fail. In 

2942 this case you should use the flag ``verify=False`` and 

2943 independently verify the solution. 

2944 

2945 >>> from sympy import cos, cosh 

2946 >>> f = cos(x)*cosh(x) - 1 

2947 >>> nsolve(f, 3.14*100) 

2948 Traceback (most recent call last): 

2949 ... 

2950 ValueError: Could not find root within given tolerance. (1.39267e+230 > 2.1684e-19) 

2951 >>> ans = nsolve(f, 3.14*100, verify=False); ans 

2952 312.588469032184 

2953 >>> f.subs(x, ans).n(2) 

2954 2.1e+121 

2955 >>> (f/f.diff(x)).subs(x, ans).n(2) 

2956 7.4e-15 

2957 

2958 One might safely skip the verification if bounds of the root are known 

2959 and a bisection method is used: 

2960 

2961 >>> bounds = lambda i: (3.14*i, 3.14*(i + 1)) 

2962 >>> nsolve(f, bounds(100), solver='bisect', verify=False) 

2963 315.730061685774 

2964 

2965 Alternatively, a function may be better behaved when the 

2966 denominator is ignored. Since this is not always the case, however, 

2967 the decision of what function to use is left to the discretion of 

2968 the user. 

2969 

2970 >>> eq = x**2/(1 - x)/(1 - 2*x)**2 - 100 

2971 >>> nsolve(eq, 0.46) 

2972 Traceback (most recent call last): 

2973 ... 

2974 ValueError: Could not find root within given tolerance. (10000 > 2.1684e-19) 

2975 Try another starting point or tweak arguments. 

2976 >>> nsolve(eq.as_numer_denom()[0], 0.46) 

2977 0.46792545969349058 

2978 

2979 """ 

2980 # there are several other SymPy functions that use method= so 

2981 # guard against that here 

2982 if 'method' in kwargs: 

2983 raise ValueError(filldedent(''' 

2984 Keyword "method" should not be used in this context. When using 

2985 some mpmath solvers directly, the keyword "method" is 

2986 used, but when using nsolve (and findroot) the keyword to use is 

2987 "solver".''')) 

2988 

2989 if 'prec' in kwargs: 

2990 import mpmath 

2991 mpmath.mp.dps = kwargs.pop('prec') 

2992 

2993 # keyword argument to return result as a dictionary 

2994 as_dict = dict 

2995 from builtins import dict # to unhide the builtin 

2996 

2997 # interpret arguments 

2998 if len(args) == 3: 

2999 f = args[0] 

3000 fargs = args[1] 

3001 x0 = args[2] 

3002 if iterable(fargs) and iterable(x0): 

3003 if len(x0) != len(fargs): 

3004 raise TypeError('nsolve expected exactly %i guess vectors, got %i' 

3005 % (len(fargs), len(x0))) 

3006 elif len(args) == 2: 

3007 f = args[0] 

3008 fargs = None 

3009 x0 = args[1] 

3010 if iterable(f): 

3011 raise TypeError('nsolve expected 3 arguments, got 2') 

3012 elif len(args) < 2: 

3013 raise TypeError('nsolve expected at least 2 arguments, got %i' 

3014 % len(args)) 

3015 else: 

3016 raise TypeError('nsolve expected at most 3 arguments, got %i' 

3017 % len(args)) 

3018 modules = kwargs.get('modules', ['mpmath']) 

3019 if iterable(f): 

3020 f = list(f) 

3021 for i, fi in enumerate(f): 

3022 if isinstance(fi, Eq): 

3023 f[i] = fi.lhs - fi.rhs 

3024 f = Matrix(f).T 

3025 if iterable(x0): 

3026 x0 = list(x0) 

3027 if not isinstance(f, Matrix): 

3028 # assume it's a SymPy expression 

3029 if isinstance(f, Eq): 

3030 f = f.lhs - f.rhs 

3031 syms = f.free_symbols 

3032 if fargs is None: 

3033 fargs = syms.copy().pop() 

3034 if not (len(syms) == 1 and (fargs in syms or fargs[0] in syms)): 

3035 raise ValueError(filldedent(''' 

3036 expected a one-dimensional and numerical function''')) 

3037 

3038 # the function is much better behaved if there is no denominator 

3039 # but sending the numerator is left to the user since sometimes 

3040 # the function is better behaved when the denominator is present 

3041 # e.g., issue 11768 

3042 

3043 f = lambdify(fargs, f, modules) 

3044 x = sympify(findroot(f, x0, **kwargs)) 

3045 if as_dict: 

3046 return [{fargs: x}] 

3047 return x 

3048 

3049 if len(fargs) > f.cols: 

3050 raise NotImplementedError(filldedent(''' 

3051 need at least as many equations as variables''')) 

3052 verbose = kwargs.get('verbose', False) 

3053 if verbose: 

3054 print('f(x):') 

3055 print(f) 

3056 # derive Jacobian 

3057 J = f.jacobian(fargs) 

3058 if verbose: 

3059 print('J(x):') 

3060 print(J) 

3061 # create functions 

3062 f = lambdify(fargs, f.T, modules) 

3063 J = lambdify(fargs, J, modules) 

3064 # solve the system numerically 

3065 x = findroot(f, x0, J=J, **kwargs) 

3066 if as_dict: 

3067 return [dict(zip(fargs, [sympify(xi) for xi in x]))] 

3068 return Matrix(x) 

3069 

3070 

3071def _invert(eq, *symbols, **kwargs): 

3072 """ 

3073 Return tuple (i, d) where ``i`` is independent of *symbols* and ``d`` 

3074 contains symbols. 

3075 

3076 Explanation 

3077 =========== 

3078 

3079 ``i`` and ``d`` are obtained after recursively using algebraic inversion 

3080 until an uninvertible ``d`` remains. If there are no free symbols then 

3081 ``d`` will be zero. Some (but not necessarily all) solutions to the 

3082 expression ``i - d`` will be related to the solutions of the original 

3083 expression. 

3084 

3085 Examples 

3086 ======== 

3087 

3088 >>> from sympy.solvers.solvers import _invert as invert 

3089 >>> from sympy import sqrt, cos 

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

3091 >>> invert(x - 3) 

3092 (3, x) 

3093 >>> invert(3) 

3094 (3, 0) 

3095 >>> invert(2*cos(x) - 1) 

3096 (1/2, cos(x)) 

3097 >>> invert(sqrt(x) - 3) 

3098 (3, sqrt(x)) 

3099 >>> invert(sqrt(x) + y, x) 

3100 (-y, sqrt(x)) 

3101 >>> invert(sqrt(x) + y, y) 

3102 (-sqrt(x), y) 

3103 >>> invert(sqrt(x) + y, x, y) 

3104 (0, sqrt(x) + y) 

3105 

3106 If there is more than one symbol in a power's base and the exponent 

3107 is not an Integer, then the principal root will be used for the 

3108 inversion: 

3109 

3110 >>> invert(sqrt(x + y) - 2) 

3111 (4, x + y) 

3112 >>> invert(sqrt(x + y) - 2) 

3113 (4, x + y) 

3114 

3115 If the exponent is an Integer, setting ``integer_power`` to True 

3116 will force the principal root to be selected: 

3117 

3118 >>> invert(x**2 - 4, integer_power=True) 

3119 (2, x) 

3120 

3121 """ 

3122 eq = sympify(eq) 

3123 if eq.args: 

3124 # make sure we are working with flat eq 

3125 eq = eq.func(*eq.args) 

3126 free = eq.free_symbols 

3127 if not symbols: 

3128 symbols = free 

3129 if not free & set(symbols): 

3130 return eq, S.Zero 

3131 

3132 dointpow = bool(kwargs.get('integer_power', False)) 

3133 

3134 lhs = eq 

3135 rhs = S.Zero 

3136 while True: 

3137 was = lhs 

3138 while True: 

3139 indep, dep = lhs.as_independent(*symbols) 

3140 

3141 # dep + indep == rhs 

3142 if lhs.is_Add: 

3143 # this indicates we have done it all 

3144 if indep.is_zero: 

3145 break 

3146 

3147 lhs = dep 

3148 rhs -= indep 

3149 

3150 # dep * indep == rhs 

3151 else: 

3152 # this indicates we have done it all 

3153 if indep is S.One: 

3154 break 

3155 

3156 lhs = dep 

3157 rhs /= indep 

3158 

3159 # collect like-terms in symbols 

3160 if lhs.is_Add: 

3161 terms = {} 

3162 for a in lhs.args: 

3163 i, d = a.as_independent(*symbols) 

3164 terms.setdefault(d, []).append(i) 

3165 if any(len(v) > 1 for v in terms.values()): 

3166 args = [] 

3167 for d, i in terms.items(): 

3168 if len(i) > 1: 

3169 args.append(Add(*i)*d) 

3170 else: 

3171 args.append(i[0]*d) 

3172 lhs = Add(*args) 

3173 

3174 # if it's a two-term Add with rhs = 0 and two powers we can get the 

3175 # dependent terms together, e.g. 3*f(x) + 2*g(x) -> f(x)/g(x) = -2/3 

3176 if lhs.is_Add and not rhs and len(lhs.args) == 2 and \ 

3177 not lhs.is_polynomial(*symbols): 

3178 a, b = ordered(lhs.args) 

3179 ai, ad = a.as_independent(*symbols) 

3180 bi, bd = b.as_independent(*symbols) 

3181 if any(_ispow(i) for i in (ad, bd)): 

3182 a_base, a_exp = ad.as_base_exp() 

3183 b_base, b_exp = bd.as_base_exp() 

3184 if a_base == b_base: 

3185 # a = -b 

3186 lhs = powsimp(powdenest(ad/bd)) 

3187 rhs = -bi/ai 

3188 else: 

3189 rat = ad/bd 

3190 _lhs = powsimp(ad/bd) 

3191 if _lhs != rat: 

3192 lhs = _lhs 

3193 rhs = -bi/ai 

3194 elif ai == -bi: 

3195 if isinstance(ad, Function) and ad.func == bd.func: 

3196 if len(ad.args) == len(bd.args) == 1: 

3197 lhs = ad.args[0] - bd.args[0] 

3198 elif len(ad.args) == len(bd.args): 

3199 # should be able to solve 

3200 # f(x, y) - f(2 - x, 0) == 0 -> x == 1 

3201 raise NotImplementedError( 

3202 'equal function with more than 1 argument') 

3203 else: 

3204 raise ValueError( 

3205 'function with different numbers of args') 

3206 

3207 elif lhs.is_Mul and any(_ispow(a) for a in lhs.args): 

3208 lhs = powsimp(powdenest(lhs)) 

3209 

3210 if lhs.is_Function: 

3211 if hasattr(lhs, 'inverse') and lhs.inverse() is not None and len(lhs.args) == 1: 

3212 # -1 

3213 # f(x) = g -> x = f (g) 

3214 # 

3215 # /!\ inverse should not be defined if there are multiple values 

3216 # for the function -- these are handled in _tsolve 

3217 # 

3218 rhs = lhs.inverse()(rhs) 

3219 lhs = lhs.args[0] 

3220 elif isinstance(lhs, atan2): 

3221 y, x = lhs.args 

3222 lhs = 2*atan(y/(sqrt(x**2 + y**2) + x)) 

3223 elif lhs.func == rhs.func: 

3224 if len(lhs.args) == len(rhs.args) == 1: 

3225 lhs = lhs.args[0] 

3226 rhs = rhs.args[0] 

3227 elif len(lhs.args) == len(rhs.args): 

3228 # should be able to solve 

3229 # f(x, y) == f(2, 3) -> x == 2 

3230 # f(x, x + y) == f(2, 3) -> x == 2 

3231 raise NotImplementedError( 

3232 'equal function with more than 1 argument') 

3233 else: 

3234 raise ValueError( 

3235 'function with different numbers of args') 

3236 

3237 

3238 if rhs and lhs.is_Pow and lhs.exp.is_Integer and lhs.exp < 0: 

3239 lhs = 1/lhs 

3240 rhs = 1/rhs 

3241 

3242 # base**a = b -> base = b**(1/a) if 

3243 # a is an Integer and dointpow=True (this gives real branch of root) 

3244 # a is not an Integer and the equation is multivariate and the 

3245 # base has more than 1 symbol in it 

3246 # The rationale for this is that right now the multi-system solvers 

3247 # doesn't try to resolve generators to see, for example, if the whole 

3248 # system is written in terms of sqrt(x + y) so it will just fail, so we 

3249 # do that step here. 

3250 if lhs.is_Pow and ( 

3251 lhs.exp.is_Integer and dointpow or not lhs.exp.is_Integer and 

3252 len(symbols) > 1 and len(lhs.base.free_symbols & set(symbols)) > 1): 

3253 rhs = rhs**(1/lhs.exp) 

3254 lhs = lhs.base 

3255 

3256 if lhs == was: 

3257 break 

3258 return rhs, lhs 

3259 

3260 

3261def unrad(eq, *syms, **flags): 

3262 """ 

3263 Remove radicals with symbolic arguments and return (eq, cov), 

3264 None, or raise an error. 

3265 

3266 Explanation 

3267 =========== 

3268 

3269 None is returned if there are no radicals to remove. 

3270 

3271 NotImplementedError is raised if there are radicals and they cannot be 

3272 removed or if the relationship between the original symbols and the 

3273 change of variable needed to rewrite the system as a polynomial cannot 

3274 be solved. 

3275 

3276 Otherwise the tuple, ``(eq, cov)``, is returned where: 

3277 

3278 *eq*, ``cov`` 

3279 *eq* is an equation without radicals (in the symbol(s) of 

3280 interest) whose solutions are a superset of the solutions to the 

3281 original expression. *eq* might be rewritten in terms of a new 

3282 variable; the relationship to the original variables is given by 

3283 ``cov`` which is a list containing ``v`` and ``v**p - b`` where 

3284 ``p`` is the power needed to clear the radical and ``b`` is the 

3285 radical now expressed as a polynomial in the symbols of interest. 

3286 For example, for sqrt(2 - x) the tuple would be 

3287 ``(c, c**2 - 2 + x)``. The solutions of *eq* will contain 

3288 solutions to the original equation (if there are any). 

3289 

3290 *syms* 

3291 An iterable of symbols which, if provided, will limit the focus of 

3292 radical removal: only radicals with one or more of the symbols of 

3293 interest will be cleared. All free symbols are used if *syms* is not 

3294 set. 

3295 

3296 *flags* are used internally for communication during recursive calls. 

3297 Two options are also recognized: 

3298 

3299 ``take``, when defined, is interpreted as a single-argument function 

3300 that returns True if a given Pow should be handled. 

3301 

3302 Radicals can be removed from an expression if: 

3303 

3304 * All bases of the radicals are the same; a change of variables is 

3305 done in this case. 

3306 * If all radicals appear in one term of the expression. 

3307 * There are only four terms with sqrt() factors or there are less than 

3308 four terms having sqrt() factors. 

3309 * There are only two terms with radicals. 

3310 

3311 Examples 

3312 ======== 

3313 

3314 >>> from sympy.solvers.solvers import unrad 

3315 >>> from sympy.abc import x 

3316 >>> from sympy import sqrt, Rational, root 

3317 

3318 >>> unrad(sqrt(x)*x**Rational(1, 3) + 2) 

3319 (x**5 - 64, []) 

3320 >>> unrad(sqrt(x) + root(x + 1, 3)) 

3321 (-x**3 + x**2 + 2*x + 1, []) 

3322 >>> eq = sqrt(x) + root(x, 3) - 2 

3323 >>> unrad(eq) 

3324 (_p**3 + _p**2 - 2, [_p, _p**6 - x]) 

3325 

3326 """ 

3327 

3328 uflags = {"check": False, "simplify": False} 

3329 

3330 def _cov(p, e): 

3331 if cov: 

3332 # XXX - uncovered 

3333 oldp, olde = cov 

3334 if Poly(e, p).degree(p) in (1, 2): 

3335 cov[:] = [p, olde.subs(oldp, _vsolve(e, p, **uflags)[0])] 

3336 else: 

3337 raise NotImplementedError 

3338 else: 

3339 cov[:] = [p, e] 

3340 

3341 def _canonical(eq, cov): 

3342 if cov: 

3343 # change symbol to vanilla so no solutions are eliminated 

3344 p, e = cov 

3345 rep = {p: Dummy(p.name)} 

3346 eq = eq.xreplace(rep) 

3347 cov = [p.xreplace(rep), e.xreplace(rep)] 

3348 

3349 # remove constants and powers of factors since these don't change 

3350 # the location of the root; XXX should factor or factor_terms be used? 

3351 eq = factor_terms(_mexpand(eq.as_numer_denom()[0], recursive=True), clear=True) 

3352 if eq.is_Mul: 

3353 args = [] 

3354 for f in eq.args: 

3355 if f.is_number: 

3356 continue 

3357 if f.is_Pow: 

3358 args.append(f.base) 

3359 else: 

3360 args.append(f) 

3361 eq = Mul(*args) # leave as Mul for more efficient solving 

3362 

3363 # make the sign canonical 

3364 margs = list(Mul.make_args(eq)) 

3365 changed = False 

3366 for i, m in enumerate(margs): 

3367 if m.could_extract_minus_sign(): 

3368 margs[i] = -m 

3369 changed = True 

3370 if changed: 

3371 eq = Mul(*margs, evaluate=False) 

3372 

3373 return eq, cov 

3374 

3375 def _Q(pow): 

3376 # return leading Rational of denominator of Pow's exponent 

3377 c = pow.as_base_exp()[1].as_coeff_Mul()[0] 

3378 if not c.is_Rational: 

3379 return S.One 

3380 return c.q 

3381 

3382 # define the _take method that will determine whether a term is of interest 

3383 def _take(d): 

3384 # return True if coefficient of any factor's exponent's den is not 1 

3385 for pow in Mul.make_args(d): 

3386 if not pow.is_Pow: 

3387 continue 

3388 if _Q(pow) == 1: 

3389 continue 

3390 if pow.free_symbols & syms: 

3391 return True 

3392 return False 

3393 _take = flags.setdefault('_take', _take) 

3394 

3395 if isinstance(eq, Eq): 

3396 eq = eq.lhs - eq.rhs # XXX legacy Eq as Eqn support 

3397 elif not isinstance(eq, Expr): 

3398 return 

3399 

3400 cov, nwas, rpt = [flags.setdefault(k, v) for k, v in 

3401 sorted({"cov": [], "n": None, "rpt": 0}.items())] 

3402 

3403 # preconditioning 

3404 eq = powdenest(factor_terms(eq, radical=True, clear=True)) 

3405 eq = eq.as_numer_denom()[0] 

3406 eq = _mexpand(eq, recursive=True) 

3407 if eq.is_number: 

3408 return 

3409 

3410 # see if there are radicals in symbols of interest 

3411 syms = set(syms) or eq.free_symbols # _take uses this 

3412 poly = eq.as_poly() 

3413 gens = [g for g in poly.gens if _take(g)] 

3414 if not gens: 

3415 return 

3416 

3417 # recast poly in terms of eigen-gens 

3418 poly = eq.as_poly(*gens) 

3419 

3420 # not a polynomial e.g. 1 + sqrt(x)*exp(sqrt(x)) with gen sqrt(x) 

3421 if poly is None: 

3422 return 

3423 

3424 # - an exponent has a symbol of interest (don't handle) 

3425 if any(g.exp.has(*syms) for g in gens): 

3426 return 

3427 

3428 def _rads_bases_lcm(poly): 

3429 # if all the bases are the same or all the radicals are in one 

3430 # term, `lcm` will be the lcm of the denominators of the 

3431 # exponents of the radicals 

3432 lcm = 1 

3433 rads = set() 

3434 bases = set() 

3435 for g in poly.gens: 

3436 q = _Q(g) 

3437 if q != 1: 

3438 rads.add(g) 

3439 lcm = ilcm(lcm, q) 

3440 bases.add(g.base) 

3441 return rads, bases, lcm 

3442 rads, bases, lcm = _rads_bases_lcm(poly) 

3443 

3444 covsym = Dummy('p', nonnegative=True) 

3445 

3446 # only keep in syms symbols that actually appear in radicals; 

3447 # and update gens 

3448 newsyms = set() 

3449 for r in rads: 

3450 newsyms.update(syms & r.free_symbols) 

3451 if newsyms != syms: 

3452 syms = newsyms 

3453 # get terms together that have common generators 

3454 drad = dict(zip(rads, range(len(rads)))) 

3455 rterms = {(): []} 

3456 args = Add.make_args(poly.as_expr()) 

3457 for t in args: 

3458 if _take(t): 

3459 common = set(t.as_poly().gens).intersection(rads) 

3460 key = tuple(sorted([drad[i] for i in common])) 

3461 else: 

3462 key = () 

3463 rterms.setdefault(key, []).append(t) 

3464 others = Add(*rterms.pop(())) 

3465 rterms = [Add(*rterms[k]) for k in rterms.keys()] 

3466 

3467 # the output will depend on the order terms are processed, so 

3468 # make it canonical quickly 

3469 rterms = list(reversed(list(ordered(rterms)))) 

3470 

3471 ok = False # we don't have a solution yet 

3472 depth = sqrt_depth(eq) 

3473 

3474 if len(rterms) == 1 and not (rterms[0].is_Add and lcm > 2): 

3475 eq = rterms[0]**lcm - ((-others)**lcm) 

3476 ok = True 

3477 else: 

3478 if len(rterms) == 1 and rterms[0].is_Add: 

3479 rterms = list(rterms[0].args) 

3480 if len(bases) == 1: 

3481 b = bases.pop() 

3482 if len(syms) > 1: 

3483 x = b.free_symbols 

3484 else: 

3485 x = syms 

3486 x = list(ordered(x))[0] 

3487 try: 

3488 inv = _vsolve(covsym**lcm - b, x, **uflags) 

3489 if not inv: 

3490 raise NotImplementedError 

3491 eq = poly.as_expr().subs(b, covsym**lcm).subs(x, inv[0]) 

3492 _cov(covsym, covsym**lcm - b) 

3493 return _canonical(eq, cov) 

3494 except NotImplementedError: 

3495 pass 

3496 

3497 if len(rterms) == 2: 

3498 if not others: 

3499 eq = rterms[0]**lcm - (-rterms[1])**lcm 

3500 ok = True 

3501 elif not log(lcm, 2).is_Integer: 

3502 # the lcm-is-power-of-two case is handled below 

3503 r0, r1 = rterms 

3504 if flags.get('_reverse', False): 

3505 r1, r0 = r0, r1 

3506 i0 = _rads0, _bases0, lcm0 = _rads_bases_lcm(r0.as_poly()) 

3507 i1 = _rads1, _bases1, lcm1 = _rads_bases_lcm(r1.as_poly()) 

3508 for reverse in range(2): 

3509 if reverse: 

3510 i0, i1 = i1, i0 

3511 r0, r1 = r1, r0 

3512 _rads1, _, lcm1 = i1 

3513 _rads1 = Mul(*_rads1) 

3514 t1 = _rads1**lcm1 

3515 c = covsym**lcm1 - t1 

3516 for x in syms: 

3517 try: 

3518 sol = _vsolve(c, x, **uflags) 

3519 if not sol: 

3520 raise NotImplementedError 

3521 neweq = r0.subs(x, sol[0]) + covsym*r1/_rads1 + \ 

3522 others 

3523 tmp = unrad(neweq, covsym) 

3524 if tmp: 

3525 eq, newcov = tmp 

3526 if newcov: 

3527 newp, newc = newcov 

3528 _cov(newp, c.subs(covsym, 

3529 _vsolve(newc, covsym, **uflags)[0])) 

3530 else: 

3531 _cov(covsym, c) 

3532 else: 

3533 eq = neweq 

3534 _cov(covsym, c) 

3535 ok = True 

3536 break 

3537 except NotImplementedError: 

3538 if reverse: 

3539 raise NotImplementedError( 

3540 'no successful change of variable found') 

3541 else: 

3542 pass 

3543 if ok: 

3544 break 

3545 elif len(rterms) == 3: 

3546 # two cube roots and another with order less than 5 

3547 # (so an analytical solution can be found) or a base 

3548 # that matches one of the cube root bases 

3549 info = [_rads_bases_lcm(i.as_poly()) for i in rterms] 

3550 RAD = 0 

3551 BASES = 1 

3552 LCM = 2 

3553 if info[0][LCM] != 3: 

3554 info.append(info.pop(0)) 

3555 rterms.append(rterms.pop(0)) 

3556 elif info[1][LCM] != 3: 

3557 info.append(info.pop(1)) 

3558 rterms.append(rterms.pop(1)) 

3559 if info[0][LCM] == info[1][LCM] == 3: 

3560 if info[1][BASES] != info[2][BASES]: 

3561 info[0], info[1] = info[1], info[0] 

3562 rterms[0], rterms[1] = rterms[1], rterms[0] 

3563 if info[1][BASES] == info[2][BASES]: 

3564 eq = rterms[0]**3 + (rterms[1] + rterms[2] + others)**3 

3565 ok = True 

3566 elif info[2][LCM] < 5: 

3567 # a*root(A, 3) + b*root(B, 3) + others = c 

3568 a, b, c, d, A, B = [Dummy(i) for i in 'abcdAB'] 

3569 # zz represents the unraded expression into which the 

3570 # specifics for this case are substituted 

3571 zz = (c - d)*(A**3*a**9 + 3*A**2*B*a**6*b**3 - 

3572 3*A**2*a**6*c**3 + 9*A**2*a**6*c**2*d - 9*A**2*a**6*c*d**2 + 

3573 3*A**2*a**6*d**3 + 3*A*B**2*a**3*b**6 + 21*A*B*a**3*b**3*c**3 - 

3574 63*A*B*a**3*b**3*c**2*d + 63*A*B*a**3*b**3*c*d**2 - 

3575 21*A*B*a**3*b**3*d**3 + 3*A*a**3*c**6 - 18*A*a**3*c**5*d + 

3576 45*A*a**3*c**4*d**2 - 60*A*a**3*c**3*d**3 + 45*A*a**3*c**2*d**4 - 

3577 18*A*a**3*c*d**5 + 3*A*a**3*d**6 + B**3*b**9 - 3*B**2*b**6*c**3 + 

3578 9*B**2*b**6*c**2*d - 9*B**2*b**6*c*d**2 + 3*B**2*b**6*d**3 + 

3579 3*B*b**3*c**6 - 18*B*b**3*c**5*d + 45*B*b**3*c**4*d**2 - 

3580 60*B*b**3*c**3*d**3 + 45*B*b**3*c**2*d**4 - 18*B*b**3*c*d**5 + 

3581 3*B*b**3*d**6 - c**9 + 9*c**8*d - 36*c**7*d**2 + 84*c**6*d**3 - 

3582 126*c**5*d**4 + 126*c**4*d**5 - 84*c**3*d**6 + 36*c**2*d**7 - 

3583 9*c*d**8 + d**9) 

3584 def _t(i): 

3585 b = Mul(*info[i][RAD]) 

3586 return cancel(rterms[i]/b), Mul(*info[i][BASES]) 

3587 aa, AA = _t(0) 

3588 bb, BB = _t(1) 

3589 cc = -rterms[2] 

3590 dd = others 

3591 eq = zz.xreplace(dict(zip( 

3592 (a, A, b, B, c, d), 

3593 (aa, AA, bb, BB, cc, dd)))) 

3594 ok = True 

3595 # handle power-of-2 cases 

3596 if not ok: 

3597 if log(lcm, 2).is_Integer and (not others and 

3598 len(rterms) == 4 or len(rterms) < 4): 

3599 def _norm2(a, b): 

3600 return a**2 + b**2 + 2*a*b 

3601 

3602 if len(rterms) == 4: 

3603 # (r0+r1)**2 - (r2+r3)**2 

3604 r0, r1, r2, r3 = rterms 

3605 eq = _norm2(r0, r1) - _norm2(r2, r3) 

3606 ok = True 

3607 elif len(rterms) == 3: 

3608 # (r1+r2)**2 - (r0+others)**2 

3609 r0, r1, r2 = rterms 

3610 eq = _norm2(r1, r2) - _norm2(r0, others) 

3611 ok = True 

3612 elif len(rterms) == 2: 

3613 # r0**2 - (r1+others)**2 

3614 r0, r1 = rterms 

3615 eq = r0**2 - _norm2(r1, others) 

3616 ok = True 

3617 

3618 new_depth = sqrt_depth(eq) if ok else depth 

3619 rpt += 1 # XXX how many repeats with others unchanging is enough? 

3620 if not ok or ( 

3621 nwas is not None and len(rterms) == nwas and 

3622 new_depth is not None and new_depth == depth and 

3623 rpt > 3): 

3624 raise NotImplementedError('Cannot remove all radicals') 

3625 

3626 flags.update({"cov": cov, "n": len(rterms), "rpt": rpt}) 

3627 neq = unrad(eq, *syms, **flags) 

3628 if neq: 

3629 eq, cov = neq 

3630 eq, cov = _canonical(eq, cov) 

3631 return eq, cov 

3632 

3633 

3634# delayed imports 

3635from sympy.solvers.bivariate import ( 

3636 bivariate_type, _solve_lambert, _filtered_gens)