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

1384 statements  

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

1""" 

2This module contains functions to: 

3 

4 - solve a single equation for a single variable, in any domain either real or complex. 

5 

6 - solve a single transcendental equation for a single variable in any domain either real or complex. 

7 (currently supports solving in real domain only) 

8 

9 - solve a system of linear equations with N variables and M equations. 

10 

11 - solve a system of Non Linear Equations with N variables and M equations 

12""" 

13from sympy.core.sympify import sympify 

14from sympy.core import (S, Pow, Dummy, pi, Expr, Wild, Mul, Equality, 

15 Add, Basic) 

16from sympy.core.containers import Tuple 

17from sympy.core.function import (Lambda, expand_complex, AppliedUndef, 

18 expand_log, _mexpand, expand_trig, nfloat) 

19from sympy.core.mod import Mod 

20from sympy.core.numbers import igcd, I, Number, Rational, oo, ilcm 

21from sympy.core.power import integer_log 

22from sympy.core.relational import Eq, Ne, Relational 

23from sympy.core.sorting import default_sort_key, ordered 

24from sympy.core.symbol import Symbol, _uniquely_named_symbol 

25from sympy.core.sympify import _sympify 

26from sympy.polys.matrices.linsolve import _linear_eq_to_dict 

27from sympy.polys.polyroots import UnsolvableFactorError 

28from sympy.simplify.simplify import simplify, fraction, trigsimp, nsimplify 

29from sympy.simplify import powdenest, logcombine 

30from sympy.functions import (log, tan, cot, sin, cos, sec, csc, exp, 

31 acos, asin, acsc, asec, 

32 piecewise_fold, Piecewise) 

33from sympy.functions.elementary.complexes import Abs, arg, re, im 

34from sympy.functions.elementary.hyperbolic import HyperbolicFunction 

35from sympy.functions.elementary.miscellaneous import real_root 

36from sympy.functions.elementary.trigonometric import TrigonometricFunction 

37from sympy.logic.boolalg import And, BooleanTrue 

38from sympy.sets import (FiniteSet, imageset, Interval, Intersection, 

39 Union, ConditionSet, ImageSet, Complement, Contains) 

40from sympy.sets.sets import Set, ProductSet 

41from sympy.matrices import zeros, Matrix, MatrixBase 

42from sympy.ntheory import totient 

43from sympy.ntheory.factor_ import divisors 

44from sympy.ntheory.residue_ntheory import discrete_log, nthroot_mod 

45from sympy.polys import (roots, Poly, degree, together, PolynomialError, 

46 RootOf, factor, lcm, gcd) 

47from sympy.polys.polyerrors import CoercionFailed 

48from sympy.polys.polytools import invert, groebner, poly 

49from sympy.polys.solvers import (sympy_eqs_to_ring, solve_lin_sys, 

50 PolyNonlinearError) 

51from sympy.polys.matrices.linsolve import _linsolve 

52from sympy.solvers.solvers import (checksol, denoms, unrad, 

53 _simple_dens, recast_to_symbols) 

54from sympy.solvers.polysys import solve_poly_system 

55from sympy.utilities import filldedent 

56from sympy.utilities.iterables import (numbered_symbols, has_dups, 

57 is_sequence, iterable) 

58from sympy.calculus.util import periodicity, continuous_domain, function_range 

59 

60from types import GeneratorType 

61 

62 

63class NonlinearError(ValueError): 

64 """Raised when unexpectedly encountering nonlinear equations""" 

65 pass 

66 

67 

68_rc = Dummy("R", real=True), Dummy("C", complex=True) 

69 

70 

71def _masked(f, *atoms): 

72 """Return ``f``, with all objects given by ``atoms`` replaced with 

73 Dummy symbols, ``d``, and the list of replacements, ``(d, e)``, 

74 where ``e`` is an object of type given by ``atoms`` in which 

75 any other instances of atoms have been recursively replaced with 

76 Dummy symbols, too. The tuples are ordered so that if they are 

77 applied in sequence, the origin ``f`` will be restored. 

78 

79 Examples 

80 ======== 

81 

82 >>> from sympy import cos 

83 >>> from sympy.abc import x 

84 >>> from sympy.solvers.solveset import _masked 

85 

86 >>> f = cos(cos(x) + 1) 

87 >>> f, reps = _masked(cos(1 + cos(x)), cos) 

88 >>> f 

89 _a1 

90 >>> reps 

91 [(_a1, cos(_a0 + 1)), (_a0, cos(x))] 

92 >>> for d, e in reps: 

93 ... f = f.xreplace({d: e}) 

94 >>> f 

95 cos(cos(x) + 1) 

96 """ 

97 sym = numbered_symbols('a', cls=Dummy, real=True) 

98 mask = [] 

99 for a in ordered(f.atoms(*atoms)): 

100 for i in mask: 

101 a = a.replace(*i) 

102 mask.append((a, next(sym))) 

103 for i, (o, n) in enumerate(mask): 

104 f = f.replace(o, n) 

105 mask[i] = (n, o) 

106 mask = list(reversed(mask)) 

107 return f, mask 

108 

109 

110def _invert(f_x, y, x, domain=S.Complexes): 

111 r""" 

112 Reduce the complex valued equation $f(x) = y$ to a set of equations 

113 

114 $$\left\{g(x) = h_1(y),\ g(x) = h_2(y),\ \dots,\ g(x) = h_n(y) \right\}$$ 

115 

116 where $g(x)$ is a simpler function than $f(x)$. The return value is a tuple 

117 $(g(x), \mathrm{set}_h)$, where $g(x)$ is a function of $x$ and $\mathrm{set}_h$ is 

118 the set of function $\left\{h_1(y), h_2(y), \dots, h_n(y)\right\}$. 

119 Here, $y$ is not necessarily a symbol. 

120 

121 $\mathrm{set}_h$ contains the functions, along with the information 

122 about the domain in which they are valid, through set 

123 operations. For instance, if :math:`y = |x| - n` is inverted 

124 in the real domain, then $\mathrm{set}_h$ is not simply 

125 $\{-n, n\}$ as the nature of `n` is unknown; rather, it is: 

126 

127 $$ \left(\left[0, \infty\right) \cap \left\{n\right\}\right) \cup 

128 \left(\left(-\infty, 0\right] \cap \left\{- n\right\}\right)$$ 

129 

130 By default, the complex domain is used which means that inverting even 

131 seemingly simple functions like $\exp(x)$ will give very different 

132 results from those obtained in the real domain. 

133 (In the case of $\exp(x)$, the inversion via $\log$ is multi-valued 

134 in the complex domain, having infinitely many branches.) 

135 

136 If you are working with real values only (or you are not sure which 

137 function to use) you should probably set the domain to 

138 ``S.Reals`` (or use ``invert_real`` which does that automatically). 

139 

140 

141 Examples 

142 ======== 

143 

144 >>> from sympy.solvers.solveset import invert_complex, invert_real 

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

146 >>> from sympy import exp 

147 

148 When does exp(x) == y? 

149 

150 >>> invert_complex(exp(x), y, x) 

151 (x, ImageSet(Lambda(_n, I*(2*_n*pi + arg(y)) + log(Abs(y))), Integers)) 

152 >>> invert_real(exp(x), y, x) 

153 (x, Intersection({log(y)}, Reals)) 

154 

155 When does exp(x) == 1? 

156 

157 >>> invert_complex(exp(x), 1, x) 

158 (x, ImageSet(Lambda(_n, 2*_n*I*pi), Integers)) 

159 >>> invert_real(exp(x), 1, x) 

160 (x, {0}) 

161 

162 See Also 

163 ======== 

164 invert_real, invert_complex 

165 """ 

166 x = sympify(x) 

167 if not x.is_Symbol: 

168 raise ValueError("x must be a symbol") 

169 f_x = sympify(f_x) 

170 if x not in f_x.free_symbols: 

171 raise ValueError("Inverse of constant function doesn't exist") 

172 y = sympify(y) 

173 if x in y.free_symbols: 

174 raise ValueError("y should be independent of x ") 

175 

176 if domain.is_subset(S.Reals): 

177 x1, s = _invert_real(f_x, FiniteSet(y), x) 

178 else: 

179 x1, s = _invert_complex(f_x, FiniteSet(y), x) 

180 

181 if not isinstance(s, FiniteSet) or x1 != x: 

182 return x1, s 

183 

184 # Avoid adding gratuitous intersections with S.Complexes. Actual 

185 # conditions should be handled by the respective inverters. 

186 if domain is S.Complexes: 

187 return x1, s 

188 else: 

189 return x1, s.intersection(domain) 

190 

191 

192invert_complex = _invert 

193 

194 

195def invert_real(f_x, y, x): 

196 """ 

197 Inverts a real-valued function. Same as :func:`invert_complex`, but sets 

198 the domain to ``S.Reals`` before inverting. 

199 """ 

200 return _invert(f_x, y, x, S.Reals) 

201 

202 

203def _invert_real(f, g_ys, symbol): 

204 """Helper function for _invert.""" 

205 

206 if f == symbol or g_ys is S.EmptySet: 

207 return (f, g_ys) 

208 

209 n = Dummy('n', real=True) 

210 

211 if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1): 

212 return _invert_real(f.exp, 

213 imageset(Lambda(n, log(n)), g_ys), 

214 symbol) 

215 

216 if hasattr(f, 'inverse') and f.inverse() is not None and not isinstance(f, ( 

217 TrigonometricFunction, 

218 HyperbolicFunction, 

219 )): 

220 if len(f.args) > 1: 

221 raise ValueError("Only functions with one argument are supported.") 

222 return _invert_real(f.args[0], 

223 imageset(Lambda(n, f.inverse()(n)), g_ys), 

224 symbol) 

225 

226 if isinstance(f, Abs): 

227 return _invert_abs(f.args[0], g_ys, symbol) 

228 

229 if f.is_Add: 

230 # f = g + h 

231 g, h = f.as_independent(symbol) 

232 if g is not S.Zero: 

233 return _invert_real(h, imageset(Lambda(n, n - g), g_ys), symbol) 

234 

235 if f.is_Mul: 

236 # f = g*h 

237 g, h = f.as_independent(symbol) 

238 

239 if g is not S.One: 

240 return _invert_real(h, imageset(Lambda(n, n/g), g_ys), symbol) 

241 

242 if f.is_Pow: 

243 base, expo = f.args 

244 base_has_sym = base.has(symbol) 

245 expo_has_sym = expo.has(symbol) 

246 

247 if not expo_has_sym: 

248 

249 if expo.is_rational: 

250 num, den = expo.as_numer_denom() 

251 

252 if den % 2 == 0 and num % 2 == 1 and den.is_zero is False: 

253 # Here we have f(x)**(num/den) = y 

254 # where den is nonzero and even and y is an element 

255 # of the set g_ys. 

256 # den is even, so we are only interested in the cases 

257 # where both f(x) and y are positive. 

258 # Restricting y to be positive (using the set g_ys_pos) 

259 # means that y**(den/num) is always positive. 

260 # Therefore it isn't necessary to also constrain f(x) 

261 # to be positive because we are only going to 

262 # find solutions of f(x) = y**(d/n) 

263 # where the rhs is already required to be positive. 

264 root = Lambda(n, real_root(n, expo)) 

265 g_ys_pos = g_ys & Interval(0, oo) 

266 res = imageset(root, g_ys_pos) 

267 _inv, _set = _invert_real(base, res, symbol) 

268 return (_inv, _set) 

269 

270 if den % 2 == 1: 

271 root = Lambda(n, real_root(n, expo)) 

272 res = imageset(root, g_ys) 

273 if num % 2 == 0: 

274 neg_res = imageset(Lambda(n, -n), res) 

275 return _invert_real(base, res + neg_res, symbol) 

276 if num % 2 == 1: 

277 return _invert_real(base, res, symbol) 

278 

279 elif expo.is_irrational: 

280 root = Lambda(n, real_root(n, expo)) 

281 g_ys_pos = g_ys & Interval(0, oo) 

282 res = imageset(root, g_ys_pos) 

283 return _invert_real(base, res, symbol) 

284 

285 else: 

286 # indeterminate exponent, e.g. Float or parity of 

287 # num, den of rational could not be determined 

288 pass # use default return 

289 

290 if not base_has_sym: 

291 rhs = g_ys.args[0] 

292 if base.is_positive: 

293 return _invert_real(expo, 

294 imageset(Lambda(n, log(n, base, evaluate=False)), g_ys), symbol) 

295 elif base.is_negative: 

296 s, b = integer_log(rhs, base) 

297 if b: 

298 return _invert_real(expo, FiniteSet(s), symbol) 

299 else: 

300 return (expo, S.EmptySet) 

301 elif base.is_zero: 

302 one = Eq(rhs, 1) 

303 if one == S.true: 

304 # special case: 0**x - 1 

305 return _invert_real(expo, FiniteSet(0), symbol) 

306 elif one == S.false: 

307 return (expo, S.EmptySet) 

308 

309 

310 if isinstance(f, TrigonometricFunction): 

311 if isinstance(g_ys, FiniteSet): 

312 def inv(trig): 

313 if isinstance(trig, (sin, csc)): 

314 F = asin if isinstance(trig, sin) else acsc 

315 return (lambda a: n*pi + S.NegativeOne**n*F(a),) 

316 if isinstance(trig, (cos, sec)): 

317 F = acos if isinstance(trig, cos) else asec 

318 return ( 

319 lambda a: 2*n*pi + F(a), 

320 lambda a: 2*n*pi - F(a),) 

321 if isinstance(trig, (tan, cot)): 

322 return (lambda a: n*pi + trig.inverse()(a),) 

323 

324 n = Dummy('n', integer=True) 

325 invs = S.EmptySet 

326 for L in inv(f): 

327 invs += Union(*[imageset(Lambda(n, L(g)), S.Integers) for g in g_ys]) 

328 return _invert_real(f.args[0], invs, symbol) 

329 

330 return (f, g_ys) 

331 

332 

333def _invert_complex(f, g_ys, symbol): 

334 """Helper function for _invert.""" 

335 

336 if f == symbol or g_ys is S.EmptySet: 

337 return (f, g_ys) 

338 

339 n = Dummy('n') 

340 

341 if f.is_Add: 

342 # f = g + h 

343 g, h = f.as_independent(symbol) 

344 if g is not S.Zero: 

345 return _invert_complex(h, imageset(Lambda(n, n - g), g_ys), symbol) 

346 

347 if f.is_Mul: 

348 # f = g*h 

349 g, h = f.as_independent(symbol) 

350 

351 if g is not S.One: 

352 if g in {S.NegativeInfinity, S.ComplexInfinity, S.Infinity}: 

353 return (h, S.EmptySet) 

354 return _invert_complex(h, imageset(Lambda(n, n/g), g_ys), symbol) 

355 

356 if f.is_Pow: 

357 base, expo = f.args 

358 # special case: g**r = 0 

359 # Could be improved like `_invert_real` to handle more general cases. 

360 if expo.is_Rational and g_ys == FiniteSet(0): 

361 if expo.is_positive: 

362 return _invert_complex(base, g_ys, symbol) 

363 

364 if hasattr(f, 'inverse') and f.inverse() is not None and \ 

365 not isinstance(f, TrigonometricFunction) and \ 

366 not isinstance(f, HyperbolicFunction) and \ 

367 not isinstance(f, exp): 

368 if len(f.args) > 1: 

369 raise ValueError("Only functions with one argument are supported.") 

370 return _invert_complex(f.args[0], 

371 imageset(Lambda(n, f.inverse()(n)), g_ys), symbol) 

372 

373 if isinstance(f, exp) or (f.is_Pow and f.base == S.Exp1): 

374 if isinstance(g_ys, ImageSet): 

375 # can solve upto `(d*exp(exp(...(exp(a*x + b))...) + c)` format. 

376 # Further can be improved to `(d*exp(exp(...(exp(a*x**n + b*x**(n-1) + ... + f))...) + c)`. 

377 g_ys_expr = g_ys.lamda.expr 

378 g_ys_vars = g_ys.lamda.variables 

379 k = Dummy('k{}'.format(len(g_ys_vars))) 

380 g_ys_vars_1 = (k,) + g_ys_vars 

381 exp_invs = Union(*[imageset(Lambda((g_ys_vars_1,), (I*(2*k*pi + arg(g_ys_expr)) 

382 + log(Abs(g_ys_expr)))), S.Integers**(len(g_ys_vars_1)))]) 

383 return _invert_complex(f.exp, exp_invs, symbol) 

384 

385 elif isinstance(g_ys, FiniteSet): 

386 exp_invs = Union(*[imageset(Lambda(n, I*(2*n*pi + arg(g_y)) + 

387 log(Abs(g_y))), S.Integers) 

388 for g_y in g_ys if g_y != 0]) 

389 return _invert_complex(f.exp, exp_invs, symbol) 

390 

391 return (f, g_ys) 

392 

393 

394def _invert_abs(f, g_ys, symbol): 

395 """Helper function for inverting absolute value functions. 

396 

397 Returns the complete result of inverting an absolute value 

398 function along with the conditions which must also be satisfied. 

399 

400 If it is certain that all these conditions are met, a :class:`~.FiniteSet` 

401 of all possible solutions is returned. If any condition cannot be 

402 satisfied, an :class:`~.EmptySet` is returned. Otherwise, a 

403 :class:`~.ConditionSet` of the solutions, with all the required conditions 

404 specified, is returned. 

405 

406 """ 

407 if not g_ys.is_FiniteSet: 

408 # this could be used for FiniteSet, but the 

409 # results are more compact if they aren't, e.g. 

410 # ConditionSet(x, Contains(n, Interval(0, oo)), {-n, n}) vs 

411 # Union(Intersection(Interval(0, oo), {n}), Intersection(Interval(-oo, 0), {-n})) 

412 # for the solution of abs(x) - n 

413 pos = Intersection(g_ys, Interval(0, S.Infinity)) 

414 parg = _invert_real(f, pos, symbol) 

415 narg = _invert_real(-f, pos, symbol) 

416 if parg[0] != narg[0]: 

417 raise NotImplementedError 

418 return parg[0], Union(narg[1], parg[1]) 

419 

420 # check conditions: all these must be true. If any are unknown 

421 # then return them as conditions which must be satisfied 

422 unknown = [] 

423 for a in g_ys.args: 

424 ok = a.is_nonnegative if a.is_Number else a.is_positive 

425 if ok is None: 

426 unknown.append(a) 

427 elif not ok: 

428 return symbol, S.EmptySet 

429 if unknown: 

430 conditions = And(*[Contains(i, Interval(0, oo)) 

431 for i in unknown]) 

432 else: 

433 conditions = True 

434 n = Dummy('n', real=True) 

435 # this is slightly different than above: instead of solving 

436 # +/-f on positive values, here we solve for f on +/- g_ys 

437 g_x, values = _invert_real(f, Union( 

438 imageset(Lambda(n, n), g_ys), 

439 imageset(Lambda(n, -n), g_ys)), symbol) 

440 return g_x, ConditionSet(g_x, conditions, values) 

441 

442 

443def domain_check(f, symbol, p): 

444 """Returns False if point p is infinite or any subexpression of f 

445 is infinite or becomes so after replacing symbol with p. If none of 

446 these conditions is met then True will be returned. 

447 

448 Examples 

449 ======== 

450 

451 >>> from sympy import Mul, oo 

452 >>> from sympy.abc import x 

453 >>> from sympy.solvers.solveset import domain_check 

454 >>> g = 1/(1 + (1/(x + 1))**2) 

455 >>> domain_check(g, x, -1) 

456 False 

457 >>> domain_check(x**2, x, 0) 

458 True 

459 >>> domain_check(1/x, x, oo) 

460 False 

461 

462 * The function relies on the assumption that the original form 

463 of the equation has not been changed by automatic simplification. 

464 

465 >>> domain_check(x/x, x, 0) # x/x is automatically simplified to 1 

466 True 

467 

468 * To deal with automatic evaluations use evaluate=False: 

469 

470 >>> domain_check(Mul(x, 1/x, evaluate=False), x, 0) 

471 False 

472 """ 

473 f, p = sympify(f), sympify(p) 

474 if p.is_infinite: 

475 return False 

476 return _domain_check(f, symbol, p) 

477 

478 

479def _domain_check(f, symbol, p): 

480 # helper for domain check 

481 if f.is_Atom and f.is_finite: 

482 return True 

483 elif f.subs(symbol, p).is_infinite: 

484 return False 

485 elif isinstance(f, Piecewise): 

486 # Check the cases of the Piecewise in turn. There might be invalid 

487 # expressions in later cases that don't apply e.g. 

488 # solveset(Piecewise((0, Eq(x, 0)), (1/x, True)), x) 

489 for expr, cond in f.args: 

490 condsubs = cond.subs(symbol, p) 

491 if condsubs is S.false: 

492 continue 

493 elif condsubs is S.true: 

494 return _domain_check(expr, symbol, p) 

495 else: 

496 # We don't know which case of the Piecewise holds. On this 

497 # basis we cannot decide whether any solution is in or out of 

498 # the domain. Ideally this function would allow returning a 

499 # symbolic condition for the validity of the solution that 

500 # could be handled in the calling code. In the mean time we'll 

501 # give this particular solution the benefit of the doubt and 

502 # let it pass. 

503 return True 

504 else: 

505 # TODO : We should not blindly recurse through all args of arbitrary expressions like this 

506 return all(_domain_check(g, symbol, p) 

507 for g in f.args) 

508 

509 

510def _is_finite_with_finite_vars(f, domain=S.Complexes): 

511 """ 

512 Return True if the given expression is finite. For symbols that 

513 do not assign a value for `complex` and/or `real`, the domain will 

514 be used to assign a value; symbols that do not assign a value 

515 for `finite` will be made finite. All other assumptions are 

516 left unmodified. 

517 """ 

518 def assumptions(s): 

519 A = s.assumptions0 

520 A.setdefault('finite', A.get('finite', True)) 

521 if domain.is_subset(S.Reals): 

522 # if this gets set it will make complex=True, too 

523 A.setdefault('real', True) 

524 else: 

525 # don't change 'real' because being complex implies 

526 # nothing about being real 

527 A.setdefault('complex', True) 

528 return A 

529 

530 reps = {s: Dummy(**assumptions(s)) for s in f.free_symbols} 

531 return f.xreplace(reps).is_finite 

532 

533 

534def _is_function_class_equation(func_class, f, symbol): 

535 """ Tests whether the equation is an equation of the given function class. 

536 

537 The given equation belongs to the given function class if it is 

538 comprised of functions of the function class which are multiplied by 

539 or added to expressions independent of the symbol. In addition, the 

540 arguments of all such functions must be linear in the symbol as well. 

541 

542 Examples 

543 ======== 

544 

545 >>> from sympy.solvers.solveset import _is_function_class_equation 

546 >>> from sympy import tan, sin, tanh, sinh, exp 

547 >>> from sympy.abc import x 

548 >>> from sympy.functions.elementary.trigonometric import TrigonometricFunction 

549 >>> from sympy.functions.elementary.hyperbolic import HyperbolicFunction 

550 >>> _is_function_class_equation(TrigonometricFunction, exp(x) + tan(x), x) 

551 False 

552 >>> _is_function_class_equation(TrigonometricFunction, tan(x) + sin(x), x) 

553 True 

554 >>> _is_function_class_equation(TrigonometricFunction, tan(x**2), x) 

555 False 

556 >>> _is_function_class_equation(TrigonometricFunction, tan(x + 2), x) 

557 True 

558 >>> _is_function_class_equation(HyperbolicFunction, tanh(x) + sinh(x), x) 

559 True 

560 """ 

561 if f.is_Mul or f.is_Add: 

562 return all(_is_function_class_equation(func_class, arg, symbol) 

563 for arg in f.args) 

564 

565 if f.is_Pow: 

566 if not f.exp.has(symbol): 

567 return _is_function_class_equation(func_class, f.base, symbol) 

568 else: 

569 return False 

570 

571 if not f.has(symbol): 

572 return True 

573 

574 if isinstance(f, func_class): 

575 try: 

576 g = Poly(f.args[0], symbol) 

577 return g.degree() <= 1 

578 except PolynomialError: 

579 return False 

580 else: 

581 return False 

582 

583 

584def _solve_as_rational(f, symbol, domain): 

585 """ solve rational functions""" 

586 f = together(_mexpand(f, recursive=True), deep=True) 

587 g, h = fraction(f) 

588 if not h.has(symbol): 

589 try: 

590 return _solve_as_poly(g, symbol, domain) 

591 except NotImplementedError: 

592 # The polynomial formed from g could end up having 

593 # coefficients in a ring over which finding roots 

594 # isn't implemented yet, e.g. ZZ[a] for some symbol a 

595 return ConditionSet(symbol, Eq(f, 0), domain) 

596 except CoercionFailed: 

597 # contained oo, zoo or nan 

598 return S.EmptySet 

599 else: 

600 valid_solns = _solveset(g, symbol, domain) 

601 invalid_solns = _solveset(h, symbol, domain) 

602 return valid_solns - invalid_solns 

603 

604 

605class _SolveTrig1Error(Exception): 

606 """Raised when _solve_trig1 heuristics do not apply""" 

607 

608def _solve_trig(f, symbol, domain): 

609 """Function to call other helpers to solve trigonometric equations """ 

610 sol = None 

611 try: 

612 sol = _solve_trig1(f, symbol, domain) 

613 except _SolveTrig1Error: 

614 try: 

615 sol = _solve_trig2(f, symbol, domain) 

616 except ValueError: 

617 raise NotImplementedError(filldedent(''' 

618 Solution to this kind of trigonometric equations 

619 is yet to be implemented''')) 

620 return sol 

621 

622 

623def _solve_trig1(f, symbol, domain): 

624 """Primary solver for trigonometric and hyperbolic equations 

625 

626 Returns either the solution set as a ConditionSet (auto-evaluated to a 

627 union of ImageSets if no variables besides 'symbol' are involved) or 

628 raises _SolveTrig1Error if f == 0 cannot be solved. 

629 

630 Notes 

631 ===== 

632 Algorithm: 

633 1. Do a change of variable x -> mu*x in arguments to trigonometric and 

634 hyperbolic functions, in order to reduce them to small integers. (This 

635 step is crucial to keep the degrees of the polynomials of step 4 low.) 

636 2. Rewrite trigonometric/hyperbolic functions as exponentials. 

637 3. Proceed to a 2nd change of variable, replacing exp(I*x) or exp(x) by y. 

638 4. Solve the resulting rational equation. 

639 5. Use invert_complex or invert_real to return to the original variable. 

640 6. If the coefficients of 'symbol' were symbolic in nature, add the 

641 necessary consistency conditions in a ConditionSet. 

642 

643 """ 

644 # Prepare change of variable 

645 x = Dummy('x') 

646 if _is_function_class_equation(HyperbolicFunction, f, symbol): 

647 cov = exp(x) 

648 inverter = invert_real if domain.is_subset(S.Reals) else invert_complex 

649 else: 

650 cov = exp(I*x) 

651 inverter = invert_complex 

652 

653 f = trigsimp(f) 

654 f_original = f 

655 trig_functions = f.atoms(TrigonometricFunction, HyperbolicFunction) 

656 trig_arguments = [e.args[0] for e in trig_functions] 

657 # trigsimp may have reduced the equation to an expression 

658 # that is independent of 'symbol' (e.g. cos**2+sin**2) 

659 if not any(a.has(symbol) for a in trig_arguments): 

660 return solveset(f_original, symbol, domain) 

661 

662 denominators = [] 

663 numerators = [] 

664 for ar in trig_arguments: 

665 try: 

666 poly_ar = Poly(ar, symbol) 

667 except PolynomialError: 

668 raise _SolveTrig1Error("trig argument is not a polynomial") 

669 if poly_ar.degree() > 1: # degree >1 still bad 

670 raise _SolveTrig1Error("degree of variable must not exceed one") 

671 if poly_ar.degree() == 0: # degree 0, don't care 

672 continue 

673 c = poly_ar.all_coeffs()[0] # got the coefficient of 'symbol' 

674 numerators.append(fraction(c)[0]) 

675 denominators.append(fraction(c)[1]) 

676 

677 mu = lcm(denominators)/gcd(numerators) 

678 f = f.subs(symbol, mu*x) 

679 f = f.rewrite(exp) 

680 f = together(f) 

681 g, h = fraction(f) 

682 y = Dummy('y') 

683 g, h = g.expand(), h.expand() 

684 g, h = g.subs(cov, y), h.subs(cov, y) 

685 if g.has(x) or h.has(x): 

686 raise _SolveTrig1Error("change of variable not possible") 

687 

688 solns = solveset_complex(g, y) - solveset_complex(h, y) 

689 if isinstance(solns, ConditionSet): 

690 raise _SolveTrig1Error("polynomial has ConditionSet solution") 

691 

692 if isinstance(solns, FiniteSet): 

693 if any(isinstance(s, RootOf) for s in solns): 

694 raise _SolveTrig1Error("polynomial results in RootOf object") 

695 # revert the change of variable 

696 cov = cov.subs(x, symbol/mu) 

697 result = Union(*[inverter(cov, s, symbol)[1] for s in solns]) 

698 # In case of symbolic coefficients, the solution set is only valid 

699 # if numerator and denominator of mu are non-zero. 

700 if mu.has(Symbol): 

701 syms = (mu).atoms(Symbol) 

702 munum, muden = fraction(mu) 

703 condnum = munum.as_independent(*syms, as_Add=False)[1] 

704 condden = muden.as_independent(*syms, as_Add=False)[1] 

705 cond = And(Ne(condnum, 0), Ne(condden, 0)) 

706 else: 

707 cond = True 

708 # Actual conditions are returned as part of the ConditionSet. Adding an 

709 # intersection with C would only complicate some solution sets due to 

710 # current limitations of intersection code. (e.g. #19154) 

711 if domain is S.Complexes: 

712 # This is a slight abuse of ConditionSet. Ideally this should 

713 # be some kind of "PiecewiseSet". (See #19507 discussion) 

714 return ConditionSet(symbol, cond, result) 

715 else: 

716 return ConditionSet(symbol, cond, Intersection(result, domain)) 

717 elif solns is S.EmptySet: 

718 return S.EmptySet 

719 else: 

720 raise _SolveTrig1Error("polynomial solutions must form FiniteSet") 

721 

722 

723def _solve_trig2(f, symbol, domain): 

724 """Secondary helper to solve trigonometric equations, 

725 called when first helper fails """ 

726 f = trigsimp(f) 

727 f_original = f 

728 trig_functions = f.atoms(sin, cos, tan, sec, cot, csc) 

729 trig_arguments = [e.args[0] for e in trig_functions] 

730 denominators = [] 

731 numerators = [] 

732 

733 # todo: This solver can be extended to hyperbolics if the 

734 # analogous change of variable to tanh (instead of tan) 

735 # is used. 

736 if not trig_functions: 

737 return ConditionSet(symbol, Eq(f_original, 0), domain) 

738 

739 # todo: The pre-processing below (extraction of numerators, denominators, 

740 # gcd, lcm, mu, etc.) should be updated to the enhanced version in 

741 # _solve_trig1. (See #19507) 

742 for ar in trig_arguments: 

743 try: 

744 poly_ar = Poly(ar, symbol) 

745 except PolynomialError: 

746 raise ValueError("give up, we cannot solve if this is not a polynomial in x") 

747 if poly_ar.degree() > 1: # degree >1 still bad 

748 raise ValueError("degree of variable inside polynomial should not exceed one") 

749 if poly_ar.degree() == 0: # degree 0, don't care 

750 continue 

751 c = poly_ar.all_coeffs()[0] # got the coefficient of 'symbol' 

752 try: 

753 numerators.append(Rational(c).p) 

754 denominators.append(Rational(c).q) 

755 except TypeError: 

756 return ConditionSet(symbol, Eq(f_original, 0), domain) 

757 

758 x = Dummy('x') 

759 

760 # ilcm() and igcd() require more than one argument 

761 if len(numerators) > 1: 

762 mu = Rational(2)*ilcm(*denominators)/igcd(*numerators) 

763 else: 

764 assert len(numerators) == 1 

765 mu = Rational(2)*denominators[0]/numerators[0] 

766 

767 f = f.subs(symbol, mu*x) 

768 f = f.rewrite(tan) 

769 f = expand_trig(f) 

770 f = together(f) 

771 

772 g, h = fraction(f) 

773 y = Dummy('y') 

774 g, h = g.expand(), h.expand() 

775 g, h = g.subs(tan(x), y), h.subs(tan(x), y) 

776 

777 if g.has(x) or h.has(x): 

778 return ConditionSet(symbol, Eq(f_original, 0), domain) 

779 solns = solveset(g, y, S.Reals) - solveset(h, y, S.Reals) 

780 

781 if isinstance(solns, FiniteSet): 

782 result = Union(*[invert_real(tan(symbol/mu), s, symbol)[1] 

783 for s in solns]) 

784 dsol = invert_real(tan(symbol/mu), oo, symbol)[1] 

785 if degree(h) > degree(g): # If degree(denom)>degree(num) then there 

786 result = Union(result, dsol) # would be another sol at Lim(denom-->oo) 

787 return Intersection(result, domain) 

788 elif solns is S.EmptySet: 

789 return S.EmptySet 

790 else: 

791 return ConditionSet(symbol, Eq(f_original, 0), S.Reals) 

792 

793 

794def _solve_as_poly(f, symbol, domain=S.Complexes): 

795 """ 

796 Solve the equation using polynomial techniques if it already is a 

797 polynomial equation or, with a change of variables, can be made so. 

798 """ 

799 result = None 

800 if f.is_polynomial(symbol): 

801 solns = roots(f, symbol, cubics=True, quartics=True, 

802 quintics=True, domain='EX') 

803 num_roots = sum(solns.values()) 

804 if degree(f, symbol) <= num_roots: 

805 result = FiniteSet(*solns.keys()) 

806 else: 

807 poly = Poly(f, symbol) 

808 solns = poly.all_roots() 

809 if poly.degree() <= len(solns): 

810 result = FiniteSet(*solns) 

811 else: 

812 result = ConditionSet(symbol, Eq(f, 0), domain) 

813 else: 

814 poly = Poly(f) 

815 if poly is None: 

816 result = ConditionSet(symbol, Eq(f, 0), domain) 

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

818 

819 if len(gens) == 1: 

820 poly = Poly(poly, gens[0]) 

821 gen = poly.gen 

822 deg = poly.degree() 

823 poly = Poly(poly.as_expr(), poly.gen, composite=True) 

824 poly_solns = FiniteSet(*roots(poly, cubics=True, quartics=True, 

825 quintics=True).keys()) 

826 

827 if len(poly_solns) < deg: 

828 result = ConditionSet(symbol, Eq(f, 0), domain) 

829 

830 if gen != symbol: 

831 y = Dummy('y') 

832 inverter = invert_real if domain.is_subset(S.Reals) else invert_complex 

833 lhs, rhs_s = inverter(gen, y, symbol) 

834 if lhs == symbol: 

835 result = Union(*[rhs_s.subs(y, s) for s in poly_solns]) 

836 if isinstance(result, FiniteSet) and isinstance(gen, Pow 

837 ) and gen.base.is_Rational: 

838 result = FiniteSet(*[expand_log(i) for i in result]) 

839 else: 

840 result = ConditionSet(symbol, Eq(f, 0), domain) 

841 else: 

842 result = ConditionSet(symbol, Eq(f, 0), domain) 

843 

844 if result is not None: 

845 if isinstance(result, FiniteSet): 

846 # this is to simplify solutions like -sqrt(-I) to sqrt(2)/2 

847 # - sqrt(2)*I/2. We are not expanding for solution with symbols 

848 # or undefined functions because that makes the solution more complicated. 

849 # For example, expand_complex(a) returns re(a) + I*im(a) 

850 if all(s.atoms(Symbol, AppliedUndef) == set() and not isinstance(s, RootOf) 

851 for s in result): 

852 s = Dummy('s') 

853 result = imageset(Lambda(s, expand_complex(s)), result) 

854 if isinstance(result, FiniteSet) and domain != S.Complexes: 

855 # Avoid adding gratuitous intersections with S.Complexes. Actual 

856 # conditions should be handled elsewhere. 

857 result = result.intersection(domain) 

858 return result 

859 else: 

860 return ConditionSet(symbol, Eq(f, 0), domain) 

861 

862 

863def _solve_radical(f, unradf, symbol, solveset_solver): 

864 """ Helper function to solve equations with radicals """ 

865 res = unradf 

866 eq, cov = res if res else (f, []) 

867 if not cov: 

868 result = solveset_solver(eq, symbol) - \ 

869 Union(*[solveset_solver(g, symbol) for g in denoms(f, symbol)]) 

870 else: 

871 y, yeq = cov 

872 if not solveset_solver(y - I, y): 

873 yreal = Dummy('yreal', real=True) 

874 yeq = yeq.xreplace({y: yreal}) 

875 eq = eq.xreplace({y: yreal}) 

876 y = yreal 

877 g_y_s = solveset_solver(yeq, symbol) 

878 f_y_sols = solveset_solver(eq, y) 

879 result = Union(*[imageset(Lambda(y, g_y), f_y_sols) 

880 for g_y in g_y_s]) 

881 

882 def check_finiteset(solutions): 

883 f_set = [] # solutions for FiniteSet 

884 c_set = [] # solutions for ConditionSet 

885 for s in solutions: 

886 if checksol(f, symbol, s): 

887 f_set.append(s) 

888 else: 

889 c_set.append(s) 

890 return FiniteSet(*f_set) + ConditionSet(symbol, Eq(f, 0), FiniteSet(*c_set)) 

891 

892 def check_set(solutions): 

893 if solutions is S.EmptySet: 

894 return solutions 

895 elif isinstance(solutions, ConditionSet): 

896 # XXX: Maybe the base set should be checked? 

897 return solutions 

898 elif isinstance(solutions, FiniteSet): 

899 return check_finiteset(solutions) 

900 elif isinstance(solutions, Complement): 

901 A, B = solutions.args 

902 return Complement(check_set(A), B) 

903 elif isinstance(solutions, Union): 

904 return Union(*[check_set(s) for s in solutions.args]) 

905 else: 

906 # XXX: There should be more cases checked here. The cases above 

907 # are all those that come up in the test suite for now. 

908 return solutions 

909 

910 solution_set = check_set(result) 

911 

912 return solution_set 

913 

914 

915def _solve_abs(f, symbol, domain): 

916 """ Helper function to solve equation involving absolute value function """ 

917 if not domain.is_subset(S.Reals): 

918 raise ValueError(filldedent(''' 

919 Absolute values cannot be inverted in the 

920 complex domain.''')) 

921 p, q, r = Wild('p'), Wild('q'), Wild('r') 

922 pattern_match = f.match(p*Abs(q) + r) or {} 

923 f_p, f_q, f_r = [pattern_match.get(i, S.Zero) for i in (p, q, r)] 

924 

925 if not (f_p.is_zero or f_q.is_zero): 

926 domain = continuous_domain(f_q, symbol, domain) 

927 from .inequalities import solve_univariate_inequality 

928 q_pos_cond = solve_univariate_inequality(f_q >= 0, symbol, 

929 relational=False, domain=domain, continuous=True) 

930 q_neg_cond = q_pos_cond.complement(domain) 

931 

932 sols_q_pos = solveset_real(f_p*f_q + f_r, 

933 symbol).intersect(q_pos_cond) 

934 sols_q_neg = solveset_real(f_p*(-f_q) + f_r, 

935 symbol).intersect(q_neg_cond) 

936 return Union(sols_q_pos, sols_q_neg) 

937 else: 

938 return ConditionSet(symbol, Eq(f, 0), domain) 

939 

940 

941def solve_decomposition(f, symbol, domain): 

942 """ 

943 Function to solve equations via the principle of "Decomposition 

944 and Rewriting". 

945 

946 Examples 

947 ======== 

948 >>> from sympy import exp, sin, Symbol, pprint, S 

949 >>> from sympy.solvers.solveset import solve_decomposition as sd 

950 >>> x = Symbol('x') 

951 >>> f1 = exp(2*x) - 3*exp(x) + 2 

952 >>> sd(f1, x, S.Reals) 

953 {0, log(2)} 

954 >>> f2 = sin(x)**2 + 2*sin(x) + 1 

955 >>> pprint(sd(f2, x, S.Reals), use_unicode=False) 

956 3*pi 

957 {2*n*pi + ---- | n in Integers} 

958 2 

959 >>> f3 = sin(x + 2) 

960 >>> pprint(sd(f3, x, S.Reals), use_unicode=False) 

961 {2*n*pi - 2 | n in Integers} U {2*n*pi - 2 + pi | n in Integers} 

962 

963 """ 

964 from sympy.solvers.decompogen import decompogen 

965 # decompose the given function 

966 g_s = decompogen(f, symbol) 

967 # `y_s` represents the set of values for which the function `g` is to be 

968 # solved. 

969 # `solutions` represent the solutions of the equations `g = y_s` or 

970 # `g = 0` depending on the type of `y_s`. 

971 # As we are interested in solving the equation: f = 0 

972 y_s = FiniteSet(0) 

973 for g in g_s: 

974 frange = function_range(g, symbol, domain) 

975 y_s = Intersection(frange, y_s) 

976 result = S.EmptySet 

977 if isinstance(y_s, FiniteSet): 

978 for y in y_s: 

979 solutions = solveset(Eq(g, y), symbol, domain) 

980 if not isinstance(solutions, ConditionSet): 

981 result += solutions 

982 

983 else: 

984 if isinstance(y_s, ImageSet): 

985 iter_iset = (y_s,) 

986 

987 elif isinstance(y_s, Union): 

988 iter_iset = y_s.args 

989 

990 elif y_s is S.EmptySet: 

991 # y_s is not in the range of g in g_s, so no solution exists 

992 #in the given domain 

993 return S.EmptySet 

994 

995 for iset in iter_iset: 

996 new_solutions = solveset(Eq(iset.lamda.expr, g), symbol, domain) 

997 dummy_var = tuple(iset.lamda.expr.free_symbols)[0] 

998 (base_set,) = iset.base_sets 

999 if isinstance(new_solutions, FiniteSet): 

1000 new_exprs = new_solutions 

1001 

1002 elif isinstance(new_solutions, Intersection): 

1003 if isinstance(new_solutions.args[1], FiniteSet): 

1004 new_exprs = new_solutions.args[1] 

1005 

1006 for new_expr in new_exprs: 

1007 result += ImageSet(Lambda(dummy_var, new_expr), base_set) 

1008 

1009 if result is S.EmptySet: 

1010 return ConditionSet(symbol, Eq(f, 0), domain) 

1011 

1012 y_s = result 

1013 

1014 return y_s 

1015 

1016 

1017def _solveset(f, symbol, domain, _check=False): 

1018 """Helper for solveset to return a result from an expression 

1019 that has already been sympify'ed and is known to contain the 

1020 given symbol.""" 

1021 # _check controls whether the answer is checked or not 

1022 from sympy.simplify.simplify import signsimp 

1023 

1024 if isinstance(f, BooleanTrue): 

1025 return domain 

1026 

1027 orig_f = f 

1028 if f.is_Mul: 

1029 coeff, f = f.as_independent(symbol, as_Add=False) 

1030 if coeff in {S.ComplexInfinity, S.NegativeInfinity, S.Infinity}: 

1031 f = together(orig_f) 

1032 elif f.is_Add: 

1033 a, h = f.as_independent(symbol) 

1034 m, h = h.as_independent(symbol, as_Add=False) 

1035 if m not in {S.ComplexInfinity, S.Zero, S.Infinity, 

1036 S.NegativeInfinity}: 

1037 f = a/m + h # XXX condition `m != 0` should be added to soln 

1038 

1039 # assign the solvers to use 

1040 solver = lambda f, x, domain=domain: _solveset(f, x, domain) 

1041 inverter = lambda f, rhs, symbol: _invert(f, rhs, symbol, domain) 

1042 

1043 result = S.EmptySet 

1044 

1045 if f.expand().is_zero: 

1046 return domain 

1047 elif not f.has(symbol): 

1048 return S.EmptySet 

1049 elif f.is_Mul and all(_is_finite_with_finite_vars(m, domain) 

1050 for m in f.args): 

1051 # if f(x) and g(x) are both finite we can say that the solution of 

1052 # f(x)*g(x) == 0 is same as Union(f(x) == 0, g(x) == 0) is not true in 

1053 # general. g(x) can grow to infinitely large for the values where 

1054 # f(x) == 0. To be sure that we are not silently allowing any 

1055 # wrong solutions we are using this technique only if both f and g are 

1056 # finite for a finite input. 

1057 result = Union(*[solver(m, symbol) for m in f.args]) 

1058 elif _is_function_class_equation(TrigonometricFunction, f, symbol) or \ 

1059 _is_function_class_equation(HyperbolicFunction, f, symbol): 

1060 result = _solve_trig(f, symbol, domain) 

1061 elif isinstance(f, arg): 

1062 a = f.args[0] 

1063 result = Intersection(_solveset(re(a) > 0, symbol, domain), 

1064 _solveset(im(a), symbol, domain)) 

1065 elif f.is_Piecewise: 

1066 expr_set_pairs = f.as_expr_set_pairs(domain) 

1067 for (expr, in_set) in expr_set_pairs: 

1068 if in_set.is_Relational: 

1069 in_set = in_set.as_set() 

1070 solns = solver(expr, symbol, in_set) 

1071 result += solns 

1072 elif isinstance(f, Eq): 

1073 result = solver(Add(f.lhs, - f.rhs, evaluate=False), symbol, domain) 

1074 

1075 elif f.is_Relational: 

1076 from .inequalities import solve_univariate_inequality 

1077 try: 

1078 result = solve_univariate_inequality( 

1079 f, symbol, domain=domain, relational=False) 

1080 except NotImplementedError: 

1081 result = ConditionSet(symbol, f, domain) 

1082 return result 

1083 elif _is_modular(f, symbol): 

1084 result = _solve_modular(f, symbol, domain) 

1085 else: 

1086 lhs, rhs_s = inverter(f, 0, symbol) 

1087 if lhs == symbol: 

1088 # do some very minimal simplification since 

1089 # repeated inversion may have left the result 

1090 # in a state that other solvers (e.g. poly) 

1091 # would have simplified; this is done here 

1092 # rather than in the inverter since here it 

1093 # is only done once whereas there it would 

1094 # be repeated for each step of the inversion 

1095 if isinstance(rhs_s, FiniteSet): 

1096 rhs_s = FiniteSet(*[Mul(* 

1097 signsimp(i).as_content_primitive()) 

1098 for i in rhs_s]) 

1099 result = rhs_s 

1100 

1101 elif isinstance(rhs_s, FiniteSet): 

1102 for equation in [lhs - rhs for rhs in rhs_s]: 

1103 if equation == f: 

1104 u = unrad(f, symbol) 

1105 if u: 

1106 result += _solve_radical(equation, u, 

1107 symbol, 

1108 solver) 

1109 elif equation.has(Abs): 

1110 result += _solve_abs(f, symbol, domain) 

1111 else: 

1112 result_rational = _solve_as_rational(equation, symbol, domain) 

1113 if not isinstance(result_rational, ConditionSet): 

1114 result += result_rational 

1115 else: 

1116 # may be a transcendental type equation 

1117 t_result = _transolve(equation, symbol, domain) 

1118 if isinstance(t_result, ConditionSet): 

1119 # might need factoring; this is expensive so we 

1120 # have delayed until now. To avoid recursion 

1121 # errors look for a non-trivial factoring into 

1122 # a product of symbol dependent terms; I think 

1123 # that something that factors as a Pow would 

1124 # have already been recognized by now. 

1125 factored = equation.factor() 

1126 if factored.is_Mul and equation != factored: 

1127 _, dep = factored.as_independent(symbol) 

1128 if not dep.is_Add: 

1129 # non-trivial factoring of equation 

1130 # but use form with constants 

1131 # in case they need special handling 

1132 t_results = [] 

1133 for fac in Mul.make_args(factored): 

1134 if fac.has(symbol): 

1135 t_results.append(solver(fac, symbol)) 

1136 t_result = Union(*t_results) 

1137 result += t_result 

1138 else: 

1139 result += solver(equation, symbol) 

1140 

1141 elif rhs_s is not S.EmptySet: 

1142 result = ConditionSet(symbol, Eq(f, 0), domain) 

1143 

1144 if isinstance(result, ConditionSet): 

1145 if isinstance(f, Expr): 

1146 num, den = f.as_numer_denom() 

1147 if den.has(symbol): 

1148 _result = _solveset(num, symbol, domain) 

1149 if not isinstance(_result, ConditionSet): 

1150 singularities = _solveset(den, symbol, domain) 

1151 result = _result - singularities 

1152 

1153 if _check: 

1154 if isinstance(result, ConditionSet): 

1155 # it wasn't solved or has enumerated all conditions 

1156 # -- leave it alone 

1157 return result 

1158 

1159 # whittle away all but the symbol-containing core 

1160 # to use this for testing 

1161 if isinstance(orig_f, Expr): 

1162 fx = orig_f.as_independent(symbol, as_Add=True)[1] 

1163 fx = fx.as_independent(symbol, as_Add=False)[1] 

1164 else: 

1165 fx = orig_f 

1166 

1167 if isinstance(result, FiniteSet): 

1168 # check the result for invalid solutions 

1169 result = FiniteSet(*[s for s in result 

1170 if isinstance(s, RootOf) 

1171 or domain_check(fx, symbol, s)]) 

1172 

1173 return result 

1174 

1175 

1176def _is_modular(f, symbol): 

1177 """ 

1178 Helper function to check below mentioned types of modular equations. 

1179 ``A - Mod(B, C) = 0`` 

1180 

1181 A -> This can or cannot be a function of symbol. 

1182 B -> This is surely a function of symbol. 

1183 C -> It is an integer. 

1184 

1185 Parameters 

1186 ========== 

1187 

1188 f : Expr 

1189 The equation to be checked. 

1190 

1191 symbol : Symbol 

1192 The concerned variable for which the equation is to be checked. 

1193 

1194 Examples 

1195 ======== 

1196 

1197 >>> from sympy import symbols, exp, Mod 

1198 >>> from sympy.solvers.solveset import _is_modular as check 

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

1200 >>> check(Mod(x, 3) - 1, x) 

1201 True 

1202 >>> check(Mod(x, 3) - 1, y) 

1203 False 

1204 >>> check(Mod(x, 3)**2 - 5, x) 

1205 False 

1206 >>> check(Mod(x, 3)**2 - y, x) 

1207 False 

1208 >>> check(exp(Mod(x, 3)) - 1, x) 

1209 False 

1210 >>> check(Mod(3, y) - 1, y) 

1211 False 

1212 """ 

1213 

1214 if not f.has(Mod): 

1215 return False 

1216 

1217 # extract modterms from f. 

1218 modterms = list(f.atoms(Mod)) 

1219 

1220 return (len(modterms) == 1 and # only one Mod should be present 

1221 modterms[0].args[0].has(symbol) and # B-> function of symbol 

1222 modterms[0].args[1].is_integer and # C-> to be an integer. 

1223 any(isinstance(term, Mod) 

1224 for term in list(_term_factors(f))) # free from other funcs 

1225 ) 

1226 

1227 

1228def _invert_modular(modterm, rhs, n, symbol): 

1229 """ 

1230 Helper function to invert modular equation. 

1231 ``Mod(a, m) - rhs = 0`` 

1232 

1233 Generally it is inverted as (a, ImageSet(Lambda(n, m*n + rhs), S.Integers)). 

1234 More simplified form will be returned if possible. 

1235 

1236 If it is not invertible then (modterm, rhs) is returned. 

1237 

1238 The following cases arise while inverting equation ``Mod(a, m) - rhs = 0``: 

1239 

1240 1. If a is symbol then m*n + rhs is the required solution. 

1241 

1242 2. If a is an instance of ``Add`` then we try to find two symbol independent 

1243 parts of a and the symbol independent part gets transferred to the other 

1244 side and again the ``_invert_modular`` is called on the symbol 

1245 dependent part. 

1246 

1247 3. If a is an instance of ``Mul`` then same as we done in ``Add`` we separate 

1248 out the symbol dependent and symbol independent parts and transfer the 

1249 symbol independent part to the rhs with the help of invert and again the 

1250 ``_invert_modular`` is called on the symbol dependent part. 

1251 

1252 4. If a is an instance of ``Pow`` then two cases arise as following: 

1253 

1254 - If a is of type (symbol_indep)**(symbol_dep) then the remainder is 

1255 evaluated with the help of discrete_log function and then the least 

1256 period is being found out with the help of totient function. 

1257 period*n + remainder is the required solution in this case. 

1258 For reference: (https://en.wikipedia.org/wiki/Euler's_theorem) 

1259 

1260 - If a is of type (symbol_dep)**(symbol_indep) then we try to find all 

1261 primitive solutions list with the help of nthroot_mod function. 

1262 m*n + rem is the general solution where rem belongs to solutions list 

1263 from nthroot_mod function. 

1264 

1265 Parameters 

1266 ========== 

1267 

1268 modterm, rhs : Expr 

1269 The modular equation to be inverted, ``modterm - rhs = 0`` 

1270 

1271 symbol : Symbol 

1272 The variable in the equation to be inverted. 

1273 

1274 n : Dummy 

1275 Dummy variable for output g_n. 

1276 

1277 Returns 

1278 ======= 

1279 

1280 A tuple (f_x, g_n) is being returned where f_x is modular independent function 

1281 of symbol and g_n being set of values f_x can have. 

1282 

1283 Examples 

1284 ======== 

1285 

1286 >>> from sympy import symbols, exp, Mod, Dummy, S 

1287 >>> from sympy.solvers.solveset import _invert_modular as invert_modular 

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

1289 >>> n = Dummy('n') 

1290 >>> invert_modular(Mod(exp(x), 7), S(5), n, x) 

1291 (Mod(exp(x), 7), 5) 

1292 >>> invert_modular(Mod(x, 7), S(5), n, x) 

1293 (x, ImageSet(Lambda(_n, 7*_n + 5), Integers)) 

1294 >>> invert_modular(Mod(3*x + 8, 7), S(5), n, x) 

1295 (x, ImageSet(Lambda(_n, 7*_n + 6), Integers)) 

1296 >>> invert_modular(Mod(x**4, 7), S(5), n, x) 

1297 (x, EmptySet) 

1298 >>> invert_modular(Mod(2**(x**2 + x + 1), 7), S(2), n, x) 

1299 (x**2 + x + 1, ImageSet(Lambda(_n, 3*_n + 1), Naturals0)) 

1300 

1301 """ 

1302 a, m = modterm.args 

1303 

1304 if rhs.is_real is False or any(term.is_real is False 

1305 for term in list(_term_factors(a))): 

1306 # Check for complex arguments 

1307 return modterm, rhs 

1308 

1309 if abs(rhs) >= abs(m): 

1310 # if rhs has value greater than value of m. 

1311 return symbol, S.EmptySet 

1312 

1313 if a == symbol: 

1314 return symbol, ImageSet(Lambda(n, m*n + rhs), S.Integers) 

1315 

1316 if a.is_Add: 

1317 # g + h = a 

1318 g, h = a.as_independent(symbol) 

1319 if g is not S.Zero: 

1320 x_indep_term = rhs - Mod(g, m) 

1321 return _invert_modular(Mod(h, m), Mod(x_indep_term, m), n, symbol) 

1322 

1323 if a.is_Mul: 

1324 # g*h = a 

1325 g, h = a.as_independent(symbol) 

1326 if g is not S.One: 

1327 x_indep_term = rhs*invert(g, m) 

1328 return _invert_modular(Mod(h, m), Mod(x_indep_term, m), n, symbol) 

1329 

1330 if a.is_Pow: 

1331 # base**expo = a 

1332 base, expo = a.args 

1333 if expo.has(symbol) and not base.has(symbol): 

1334 # remainder -> solution independent of n of equation. 

1335 # m, rhs are made coprime by dividing igcd(m, rhs) 

1336 try: 

1337 remainder = discrete_log(m / igcd(m, rhs), rhs, a.base) 

1338 except ValueError: # log does not exist 

1339 return modterm, rhs 

1340 # period -> coefficient of n in the solution and also referred as 

1341 # the least period of expo in which it is repeats itself. 

1342 # (a**(totient(m)) - 1) divides m. Here is link of theorem: 

1343 # (https://en.wikipedia.org/wiki/Euler's_theorem) 

1344 period = totient(m) 

1345 for p in divisors(period): 

1346 # there might a lesser period exist than totient(m). 

1347 if pow(a.base, p, m / igcd(m, a.base)) == 1: 

1348 period = p 

1349 break 

1350 # recursion is not applied here since _invert_modular is currently 

1351 # not smart enough to handle infinite rhs as here expo has infinite 

1352 # rhs = ImageSet(Lambda(n, period*n + remainder), S.Naturals0). 

1353 return expo, ImageSet(Lambda(n, period*n + remainder), S.Naturals0) 

1354 elif base.has(symbol) and not expo.has(symbol): 

1355 try: 

1356 remainder_list = nthroot_mod(rhs, expo, m, all_roots=True) 

1357 if remainder_list == []: 

1358 return symbol, S.EmptySet 

1359 except (ValueError, NotImplementedError): 

1360 return modterm, rhs 

1361 g_n = S.EmptySet 

1362 for rem in remainder_list: 

1363 g_n += ImageSet(Lambda(n, m*n + rem), S.Integers) 

1364 return base, g_n 

1365 

1366 return modterm, rhs 

1367 

1368 

1369def _solve_modular(f, symbol, domain): 

1370 r""" 

1371 Helper function for solving modular equations of type ``A - Mod(B, C) = 0``, 

1372 where A can or cannot be a function of symbol, B is surely a function of 

1373 symbol and C is an integer. 

1374 

1375 Currently ``_solve_modular`` is only able to solve cases 

1376 where A is not a function of symbol. 

1377 

1378 Parameters 

1379 ========== 

1380 

1381 f : Expr 

1382 The modular equation to be solved, ``f = 0`` 

1383 

1384 symbol : Symbol 

1385 The variable in the equation to be solved. 

1386 

1387 domain : Set 

1388 A set over which the equation is solved. It has to be a subset of 

1389 Integers. 

1390 

1391 Returns 

1392 ======= 

1393 

1394 A set of integer solutions satisfying the given modular equation. 

1395 A ``ConditionSet`` if the equation is unsolvable. 

1396 

1397 Examples 

1398 ======== 

1399 

1400 >>> from sympy.solvers.solveset import _solve_modular as solve_modulo 

1401 >>> from sympy import S, Symbol, sin, Intersection, Interval, Mod 

1402 >>> x = Symbol('x') 

1403 >>> solve_modulo(Mod(5*x - 8, 7) - 3, x, S.Integers) 

1404 ImageSet(Lambda(_n, 7*_n + 5), Integers) 

1405 >>> solve_modulo(Mod(5*x - 8, 7) - 3, x, S.Reals) # domain should be subset of integers. 

1406 ConditionSet(x, Eq(Mod(5*x + 6, 7) - 3, 0), Reals) 

1407 >>> solve_modulo(-7 + Mod(x, 5), x, S.Integers) 

1408 EmptySet 

1409 >>> solve_modulo(Mod(12**x, 21) - 18, x, S.Integers) 

1410 ImageSet(Lambda(_n, 6*_n + 2), Naturals0) 

1411 >>> solve_modulo(Mod(sin(x), 7) - 3, x, S.Integers) # not solvable 

1412 ConditionSet(x, Eq(Mod(sin(x), 7) - 3, 0), Integers) 

1413 >>> solve_modulo(3 - Mod(x, 5), x, Intersection(S.Integers, Interval(0, 100))) 

1414 Intersection(ImageSet(Lambda(_n, 5*_n + 3), Integers), Range(0, 101, 1)) 

1415 """ 

1416 # extract modterm and g_y from f 

1417 unsolved_result = ConditionSet(symbol, Eq(f, 0), domain) 

1418 modterm = list(f.atoms(Mod))[0] 

1419 rhs = -S.One*(f.subs(modterm, S.Zero)) 

1420 if f.as_coefficients_dict()[modterm].is_negative: 

1421 # checks if coefficient of modterm is negative in main equation. 

1422 rhs *= -S.One 

1423 

1424 if not domain.is_subset(S.Integers): 

1425 return unsolved_result 

1426 

1427 if rhs.has(symbol): 

1428 # TODO Case: A-> function of symbol, can be extended here 

1429 # in future. 

1430 return unsolved_result 

1431 

1432 n = Dummy('n', integer=True) 

1433 f_x, g_n = _invert_modular(modterm, rhs, n, symbol) 

1434 

1435 if f_x == modterm and g_n == rhs: 

1436 return unsolved_result 

1437 

1438 if f_x == symbol: 

1439 if domain is not S.Integers: 

1440 return domain.intersect(g_n) 

1441 return g_n 

1442 

1443 if isinstance(g_n, ImageSet): 

1444 lamda_expr = g_n.lamda.expr 

1445 lamda_vars = g_n.lamda.variables 

1446 base_sets = g_n.base_sets 

1447 sol_set = _solveset(f_x - lamda_expr, symbol, S.Integers) 

1448 if isinstance(sol_set, FiniteSet): 

1449 tmp_sol = S.EmptySet 

1450 for sol in sol_set: 

1451 tmp_sol += ImageSet(Lambda(lamda_vars, sol), *base_sets) 

1452 sol_set = tmp_sol 

1453 else: 

1454 sol_set = ImageSet(Lambda(lamda_vars, sol_set), *base_sets) 

1455 return domain.intersect(sol_set) 

1456 

1457 return unsolved_result 

1458 

1459 

1460def _term_factors(f): 

1461 """ 

1462 Iterator to get the factors of all terms present 

1463 in the given equation. 

1464 

1465 Parameters 

1466 ========== 

1467 f : Expr 

1468 Equation that needs to be addressed 

1469 

1470 Returns 

1471 ======= 

1472 Factors of all terms present in the equation. 

1473 

1474 Examples 

1475 ======== 

1476 

1477 >>> from sympy import symbols 

1478 >>> from sympy.solvers.solveset import _term_factors 

1479 >>> x = symbols('x') 

1480 >>> list(_term_factors(-2 - x**2 + x*(x + 1))) 

1481 [-2, -1, x**2, x, x + 1] 

1482 """ 

1483 for add_arg in Add.make_args(f): 

1484 yield from Mul.make_args(add_arg) 

1485 

1486 

1487def _solve_exponential(lhs, rhs, symbol, domain): 

1488 r""" 

1489 Helper function for solving (supported) exponential equations. 

1490 

1491 Exponential equations are the sum of (currently) at most 

1492 two terms with one or both of them having a power with a 

1493 symbol-dependent exponent. 

1494 

1495 For example 

1496 

1497 .. math:: 5^{2x + 3} - 5^{3x - 1} 

1498 

1499 .. math:: 4^{5 - 9x} - e^{2 - x} 

1500 

1501 Parameters 

1502 ========== 

1503 

1504 lhs, rhs : Expr 

1505 The exponential equation to be solved, `lhs = rhs` 

1506 

1507 symbol : Symbol 

1508 The variable in which the equation is solved 

1509 

1510 domain : Set 

1511 A set over which the equation is solved. 

1512 

1513 Returns 

1514 ======= 

1515 

1516 A set of solutions satisfying the given equation. 

1517 A ``ConditionSet`` if the equation is unsolvable or 

1518 if the assumptions are not properly defined, in that case 

1519 a different style of ``ConditionSet`` is returned having the 

1520 solution(s) of the equation with the desired assumptions. 

1521 

1522 Examples 

1523 ======== 

1524 

1525 >>> from sympy.solvers.solveset import _solve_exponential as solve_expo 

1526 >>> from sympy import symbols, S 

1527 >>> x = symbols('x', real=True) 

1528 >>> a, b = symbols('a b') 

1529 >>> solve_expo(2**x + 3**x - 5**x, 0, x, S.Reals) # not solvable 

1530 ConditionSet(x, Eq(2**x + 3**x - 5**x, 0), Reals) 

1531 >>> solve_expo(a**x - b**x, 0, x, S.Reals) # solvable but incorrect assumptions 

1532 ConditionSet(x, (a > 0) & (b > 0), {0}) 

1533 >>> solve_expo(3**(2*x) - 2**(x + 3), 0, x, S.Reals) 

1534 {-3*log(2)/(-2*log(3) + log(2))} 

1535 >>> solve_expo(2**x - 4**x, 0, x, S.Reals) 

1536 {0} 

1537 

1538 * Proof of correctness of the method 

1539 

1540 The logarithm function is the inverse of the exponential function. 

1541 The defining relation between exponentiation and logarithm is: 

1542 

1543 .. math:: {\log_b x} = y \enspace if \enspace b^y = x 

1544 

1545 Therefore if we are given an equation with exponent terms, we can 

1546 convert every term to its corresponding logarithmic form. This is 

1547 achieved by taking logarithms and expanding the equation using 

1548 logarithmic identities so that it can easily be handled by ``solveset``. 

1549 

1550 For example: 

1551 

1552 .. math:: 3^{2x} = 2^{x + 3} 

1553 

1554 Taking log both sides will reduce the equation to 

1555 

1556 .. math:: (2x)\log(3) = (x + 3)\log(2) 

1557 

1558 This form can be easily handed by ``solveset``. 

1559 """ 

1560 unsolved_result = ConditionSet(symbol, Eq(lhs - rhs, 0), domain) 

1561 newlhs = powdenest(lhs) 

1562 if lhs != newlhs: 

1563 # it may also be advantageous to factor the new expr 

1564 neweq = factor(newlhs - rhs) 

1565 if neweq != (lhs - rhs): 

1566 return _solveset(neweq, symbol, domain) # try again with _solveset 

1567 

1568 if not (isinstance(lhs, Add) and len(lhs.args) == 2): 

1569 # solving for the sum of more than two powers is possible 

1570 # but not yet implemented 

1571 return unsolved_result 

1572 

1573 if rhs != 0: 

1574 return unsolved_result 

1575 

1576 a, b = list(ordered(lhs.args)) 

1577 a_term = a.as_independent(symbol)[1] 

1578 b_term = b.as_independent(symbol)[1] 

1579 

1580 a_base, a_exp = a_term.as_base_exp() 

1581 b_base, b_exp = b_term.as_base_exp() 

1582 

1583 if domain.is_subset(S.Reals): 

1584 conditions = And( 

1585 a_base > 0, 

1586 b_base > 0, 

1587 Eq(im(a_exp), 0), 

1588 Eq(im(b_exp), 0)) 

1589 else: 

1590 conditions = And( 

1591 Ne(a_base, 0), 

1592 Ne(b_base, 0)) 

1593 

1594 L, R = (expand_log(log(i), force=True) for i in (a, -b)) 

1595 solutions = _solveset(L - R, symbol, domain) 

1596 

1597 return ConditionSet(symbol, conditions, solutions) 

1598 

1599 

1600def _is_exponential(f, symbol): 

1601 r""" 

1602 Return ``True`` if one or more terms contain ``symbol`` only in 

1603 exponents, else ``False``. 

1604 

1605 Parameters 

1606 ========== 

1607 

1608 f : Expr 

1609 The equation to be checked 

1610 

1611 symbol : Symbol 

1612 The variable in which the equation is checked 

1613 

1614 Examples 

1615 ======== 

1616 

1617 >>> from sympy import symbols, cos, exp 

1618 >>> from sympy.solvers.solveset import _is_exponential as check 

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

1620 >>> check(y, y) 

1621 False 

1622 >>> check(x**y - 1, y) 

1623 True 

1624 >>> check(x**y*2**y - 1, y) 

1625 True 

1626 >>> check(exp(x + 3) + 3**x, x) 

1627 True 

1628 >>> check(cos(2**x), x) 

1629 False 

1630 

1631 * Philosophy behind the helper 

1632 

1633 The function extracts each term of the equation and checks if it is 

1634 of exponential form w.r.t ``symbol``. 

1635 """ 

1636 rv = False 

1637 for expr_arg in _term_factors(f): 

1638 if symbol not in expr_arg.free_symbols: 

1639 continue 

1640 if (isinstance(expr_arg, Pow) and 

1641 symbol not in expr_arg.base.free_symbols or 

1642 isinstance(expr_arg, exp)): 

1643 rv = True # symbol in exponent 

1644 else: 

1645 return False # dependent on symbol in non-exponential way 

1646 return rv 

1647 

1648 

1649def _solve_logarithm(lhs, rhs, symbol, domain): 

1650 r""" 

1651 Helper to solve logarithmic equations which are reducible 

1652 to a single instance of `\log`. 

1653 

1654 Logarithmic equations are (currently) the equations that contains 

1655 `\log` terms which can be reduced to a single `\log` term or 

1656 a constant using various logarithmic identities. 

1657 

1658 For example: 

1659 

1660 .. math:: \log(x) + \log(x - 4) 

1661 

1662 can be reduced to: 

1663 

1664 .. math:: \log(x(x - 4)) 

1665 

1666 Parameters 

1667 ========== 

1668 

1669 lhs, rhs : Expr 

1670 The logarithmic equation to be solved, `lhs = rhs` 

1671 

1672 symbol : Symbol 

1673 The variable in which the equation is solved 

1674 

1675 domain : Set 

1676 A set over which the equation is solved. 

1677 

1678 Returns 

1679 ======= 

1680 

1681 A set of solutions satisfying the given equation. 

1682 A ``ConditionSet`` if the equation is unsolvable. 

1683 

1684 Examples 

1685 ======== 

1686 

1687 >>> from sympy import symbols, log, S 

1688 >>> from sympy.solvers.solveset import _solve_logarithm as solve_log 

1689 >>> x = symbols('x') 

1690 >>> f = log(x - 3) + log(x + 3) 

1691 >>> solve_log(f, 0, x, S.Reals) 

1692 {-sqrt(10), sqrt(10)} 

1693 

1694 * Proof of correctness 

1695 

1696 A logarithm is another way to write exponent and is defined by 

1697 

1698 .. math:: {\log_b x} = y \enspace if \enspace b^y = x 

1699 

1700 When one side of the equation contains a single logarithm, the 

1701 equation can be solved by rewriting the equation as an equivalent 

1702 exponential equation as defined above. But if one side contains 

1703 more than one logarithm, we need to use the properties of logarithm 

1704 to condense it into a single logarithm. 

1705 

1706 Take for example 

1707 

1708 .. math:: \log(2x) - 15 = 0 

1709 

1710 contains single logarithm, therefore we can directly rewrite it to 

1711 exponential form as 

1712 

1713 .. math:: x = \frac{e^{15}}{2} 

1714 

1715 But if the equation has more than one logarithm as 

1716 

1717 .. math:: \log(x - 3) + \log(x + 3) = 0 

1718 

1719 we use logarithmic identities to convert it into a reduced form 

1720 

1721 Using, 

1722 

1723 .. math:: \log(a) + \log(b) = \log(ab) 

1724 

1725 the equation becomes, 

1726 

1727 .. math:: \log((x - 3)(x + 3)) 

1728 

1729 This equation contains one logarithm and can be solved by rewriting 

1730 to exponents. 

1731 """ 

1732 new_lhs = logcombine(lhs, force=True) 

1733 new_f = new_lhs - rhs 

1734 

1735 return _solveset(new_f, symbol, domain) 

1736 

1737 

1738def _is_logarithmic(f, symbol): 

1739 r""" 

1740 Return ``True`` if the equation is in the form 

1741 `a\log(f(x)) + b\log(g(x)) + ... + c` else ``False``. 

1742 

1743 Parameters 

1744 ========== 

1745 

1746 f : Expr 

1747 The equation to be checked 

1748 

1749 symbol : Symbol 

1750 The variable in which the equation is checked 

1751 

1752 Returns 

1753 ======= 

1754 

1755 ``True`` if the equation is logarithmic otherwise ``False``. 

1756 

1757 Examples 

1758 ======== 

1759 

1760 >>> from sympy import symbols, tan, log 

1761 >>> from sympy.solvers.solveset import _is_logarithmic as check 

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

1763 >>> check(log(x + 2) - log(x + 3), x) 

1764 True 

1765 >>> check(tan(log(2*x)), x) 

1766 False 

1767 >>> check(x*log(x), x) 

1768 False 

1769 >>> check(x + log(x), x) 

1770 False 

1771 >>> check(y + log(x), x) 

1772 True 

1773 

1774 * Philosophy behind the helper 

1775 

1776 The function extracts each term and checks whether it is 

1777 logarithmic w.r.t ``symbol``. 

1778 """ 

1779 rv = False 

1780 for term in Add.make_args(f): 

1781 saw_log = False 

1782 for term_arg in Mul.make_args(term): 

1783 if symbol not in term_arg.free_symbols: 

1784 continue 

1785 if isinstance(term_arg, log): 

1786 if saw_log: 

1787 return False # more than one log in term 

1788 saw_log = True 

1789 else: 

1790 return False # dependent on symbol in non-log way 

1791 if saw_log: 

1792 rv = True 

1793 return rv 

1794 

1795 

1796def _is_lambert(f, symbol): 

1797 r""" 

1798 If this returns ``False`` then the Lambert solver (``_solve_lambert``) will not be called. 

1799 

1800 Explanation 

1801 =========== 

1802 

1803 Quick check for cases that the Lambert solver might be able to handle. 

1804 

1805 1. Equations containing more than two operands and `symbol`s involving any of 

1806 `Pow`, `exp`, `HyperbolicFunction`,`TrigonometricFunction`, `log` terms. 

1807 

1808 2. In `Pow`, `exp` the exponent should have `symbol` whereas for 

1809 `HyperbolicFunction`,`TrigonometricFunction`, `log` should contain `symbol`. 

1810 

1811 3. For `HyperbolicFunction`,`TrigonometricFunction` the number of trigonometric functions in 

1812 equation should be less than number of symbols. (since `A*cos(x) + B*sin(x) - c` 

1813 is not the Lambert type). 

1814 

1815 Some forms of lambert equations are: 

1816 1. X**X = C 

1817 2. X*(B*log(X) + D)**A = C 

1818 3. A*log(B*X + A) + d*X = C 

1819 4. (B*X + A)*exp(d*X + g) = C 

1820 5. g*exp(B*X + h) - B*X = C 

1821 6. A*D**(E*X + g) - B*X = C 

1822 7. A*cos(X) + B*sin(X) - D*X = C 

1823 8. A*cosh(X) + B*sinh(X) - D*X = C 

1824 

1825 Where X is any variable, 

1826 A, B, C, D, E are any constants, 

1827 g, h are linear functions or log terms. 

1828 

1829 Parameters 

1830 ========== 

1831 

1832 f : Expr 

1833 The equation to be checked 

1834 

1835 symbol : Symbol 

1836 The variable in which the equation is checked 

1837 

1838 Returns 

1839 ======= 

1840 

1841 If this returns ``False`` then the Lambert solver (``_solve_lambert``) will not be called. 

1842 

1843 Examples 

1844 ======== 

1845 

1846 >>> from sympy.solvers.solveset import _is_lambert 

1847 >>> from sympy import symbols, cosh, sinh, log 

1848 >>> x = symbols('x') 

1849 

1850 >>> _is_lambert(3*log(x) - x*log(3), x) 

1851 True 

1852 >>> _is_lambert(log(log(x - 3)) + log(x-3), x) 

1853 True 

1854 >>> _is_lambert(cosh(x) - sinh(x), x) 

1855 False 

1856 >>> _is_lambert((x**2 - 2*x + 1).subs(x, (log(x) + 3*x)**2 - 1), x) 

1857 True 

1858 

1859 See Also 

1860 ======== 

1861 

1862 _solve_lambert 

1863 

1864 """ 

1865 term_factors = list(_term_factors(f.expand())) 

1866 

1867 # total number of symbols in equation 

1868 no_of_symbols = len([arg for arg in term_factors if arg.has(symbol)]) 

1869 # total number of trigonometric terms in equation 

1870 no_of_trig = len([arg for arg in term_factors \ 

1871 if arg.has(HyperbolicFunction, TrigonometricFunction)]) 

1872 

1873 if f.is_Add and no_of_symbols >= 2: 

1874 # `log`, `HyperbolicFunction`, `TrigonometricFunction` should have symbols 

1875 # and no_of_trig < no_of_symbols 

1876 lambert_funcs = (log, HyperbolicFunction, TrigonometricFunction) 

1877 if any(isinstance(arg, lambert_funcs)\ 

1878 for arg in term_factors if arg.has(symbol)): 

1879 if no_of_trig < no_of_symbols: 

1880 return True 

1881 # here, `Pow`, `exp` exponent should have symbols 

1882 elif any(isinstance(arg, (Pow, exp)) \ 

1883 for arg in term_factors if (arg.as_base_exp()[1]).has(symbol)): 

1884 return True 

1885 return False 

1886 

1887 

1888def _transolve(f, symbol, domain): 

1889 r""" 

1890 Function to solve transcendental equations. It is a helper to 

1891 ``solveset`` and should be used internally. ``_transolve`` 

1892 currently supports the following class of equations: 

1893 

1894 - Exponential equations 

1895 - Logarithmic equations 

1896 

1897 Parameters 

1898 ========== 

1899 

1900 f : Any transcendental equation that needs to be solved. 

1901 This needs to be an expression, which is assumed 

1902 to be equal to ``0``. 

1903 

1904 symbol : The variable for which the equation is solved. 

1905 This needs to be of class ``Symbol``. 

1906 

1907 domain : A set over which the equation is solved. 

1908 This needs to be of class ``Set``. 

1909 

1910 Returns 

1911 ======= 

1912 

1913 Set 

1914 A set of values for ``symbol`` for which ``f`` is equal to 

1915 zero. An ``EmptySet`` is returned if ``f`` does not have solutions 

1916 in respective domain. A ``ConditionSet`` is returned as unsolved 

1917 object if algorithms to evaluate complete solution are not 

1918 yet implemented. 

1919 

1920 How to use ``_transolve`` 

1921 ========================= 

1922 

1923 ``_transolve`` should not be used as an independent function, because 

1924 it assumes that the equation (``f``) and the ``symbol`` comes from 

1925 ``solveset`` and might have undergone a few modification(s). 

1926 To use ``_transolve`` as an independent function the equation (``f``) 

1927 and the ``symbol`` should be passed as they would have been by 

1928 ``solveset``. 

1929 

1930 Examples 

1931 ======== 

1932 

1933 >>> from sympy.solvers.solveset import _transolve as transolve 

1934 >>> from sympy.solvers.solvers import _tsolve as tsolve 

1935 >>> from sympy import symbols, S, pprint 

1936 >>> x = symbols('x', real=True) # assumption added 

1937 >>> transolve(5**(x - 3) - 3**(2*x + 1), x, S.Reals) 

1938 {-(log(3) + 3*log(5))/(-log(5) + 2*log(3))} 

1939 

1940 How ``_transolve`` works 

1941 ======================== 

1942 

1943 ``_transolve`` uses two types of helper functions to solve equations 

1944 of a particular class: 

1945 

1946 Identifying helpers: To determine whether a given equation 

1947 belongs to a certain class of equation or not. Returns either 

1948 ``True`` or ``False``. 

1949 

1950 Solving helpers: Once an equation is identified, a corresponding 

1951 helper either solves the equation or returns a form of the equation 

1952 that ``solveset`` might better be able to handle. 

1953 

1954 * Philosophy behind the module 

1955 

1956 The purpose of ``_transolve`` is to take equations which are not 

1957 already polynomial in their generator(s) and to either recast them 

1958 as such through a valid transformation or to solve them outright. 

1959 A pair of helper functions for each class of supported 

1960 transcendental functions are employed for this purpose. One 

1961 identifies the transcendental form of an equation and the other 

1962 either solves it or recasts it into a tractable form that can be 

1963 solved by ``solveset``. 

1964 For example, an equation in the form `ab^{f(x)} - cd^{g(x)} = 0` 

1965 can be transformed to 

1966 `\log(a) + f(x)\log(b) - \log(c) - g(x)\log(d) = 0` 

1967 (under certain assumptions) and this can be solved with ``solveset`` 

1968 if `f(x)` and `g(x)` are in polynomial form. 

1969 

1970 How ``_transolve`` is better than ``_tsolve`` 

1971 ============================================= 

1972 

1973 1) Better output 

1974 

1975 ``_transolve`` provides expressions in a more simplified form. 

1976 

1977 Consider a simple exponential equation 

1978 

1979 >>> f = 3**(2*x) - 2**(x + 3) 

1980 >>> pprint(transolve(f, x, S.Reals), use_unicode=False) 

1981 -3*log(2) 

1982 {------------------} 

1983 -2*log(3) + log(2) 

1984 >>> pprint(tsolve(f, x), use_unicode=False) 

1985 / 3 \ 

1986 | --------| 

1987 | log(2/9)| 

1988 [-log\2 /] 

1989 

1990 2) Extensible 

1991 

1992 The API of ``_transolve`` is designed such that it is easily 

1993 extensible, i.e. the code that solves a given class of 

1994 equations is encapsulated in a helper and not mixed in with 

1995 the code of ``_transolve`` itself. 

1996 

1997 3) Modular 

1998 

1999 ``_transolve`` is designed to be modular i.e, for every class of 

2000 equation a separate helper for identification and solving is 

2001 implemented. This makes it easy to change or modify any of the 

2002 method implemented directly in the helpers without interfering 

2003 with the actual structure of the API. 

2004 

2005 4) Faster Computation 

2006 

2007 Solving equation via ``_transolve`` is much faster as compared to 

2008 ``_tsolve``. In ``solve``, attempts are made computing every possibility 

2009 to get the solutions. This series of attempts makes solving a bit 

2010 slow. In ``_transolve``, computation begins only after a particular 

2011 type of equation is identified. 

2012 

2013 How to add new class of equations 

2014 ================================= 

2015 

2016 Adding a new class of equation solver is a three-step procedure: 

2017 

2018 - Identify the type of the equations 

2019 

2020 Determine the type of the class of equations to which they belong: 

2021 it could be of ``Add``, ``Pow``, etc. types. Separate internal functions 

2022 are used for each type. Write identification and solving helpers 

2023 and use them from within the routine for the given type of equation 

2024 (after adding it, if necessary). Something like: 

2025 

2026 .. code-block:: python 

2027 

2028 def add_type(lhs, rhs, x): 

2029 .... 

2030 if _is_exponential(lhs, x): 

2031 new_eq = _solve_exponential(lhs, rhs, x) 

2032 .... 

2033 rhs, lhs = eq.as_independent(x) 

2034 if lhs.is_Add: 

2035 result = add_type(lhs, rhs, x) 

2036 

2037 - Define the identification helper. 

2038 

2039 - Define the solving helper. 

2040 

2041 Apart from this, a few other things needs to be taken care while 

2042 adding an equation solver: 

2043 

2044 - Naming conventions: 

2045 Name of the identification helper should be as 

2046 ``_is_class`` where class will be the name or abbreviation 

2047 of the class of equation. The solving helper will be named as 

2048 ``_solve_class``. 

2049 For example: for exponential equations it becomes 

2050 ``_is_exponential`` and ``_solve_expo``. 

2051 - The identifying helpers should take two input parameters, 

2052 the equation to be checked and the variable for which a solution 

2053 is being sought, while solving helpers would require an additional 

2054 domain parameter. 

2055 - Be sure to consider corner cases. 

2056 - Add tests for each helper. 

2057 - Add a docstring to your helper that describes the method 

2058 implemented. 

2059 The documentation of the helpers should identify: 

2060 

2061 - the purpose of the helper, 

2062 - the method used to identify and solve the equation, 

2063 - a proof of correctness 

2064 - the return values of the helpers 

2065 """ 

2066 

2067 def add_type(lhs, rhs, symbol, domain): 

2068 """ 

2069 Helper for ``_transolve`` to handle equations of 

2070 ``Add`` type, i.e. equations taking the form as 

2071 ``a*f(x) + b*g(x) + .... = c``. 

2072 For example: 4**x + 8**x = 0 

2073 """ 

2074 result = ConditionSet(symbol, Eq(lhs - rhs, 0), domain) 

2075 

2076 # check if it is exponential type equation 

2077 if _is_exponential(lhs, symbol): 

2078 result = _solve_exponential(lhs, rhs, symbol, domain) 

2079 # check if it is logarithmic type equation 

2080 elif _is_logarithmic(lhs, symbol): 

2081 result = _solve_logarithm(lhs, rhs, symbol, domain) 

2082 

2083 return result 

2084 

2085 result = ConditionSet(symbol, Eq(f, 0), domain) 

2086 

2087 # invert_complex handles the call to the desired inverter based 

2088 # on the domain specified. 

2089 lhs, rhs_s = invert_complex(f, 0, symbol, domain) 

2090 

2091 if isinstance(rhs_s, FiniteSet): 

2092 assert (len(rhs_s.args)) == 1 

2093 rhs = rhs_s.args[0] 

2094 

2095 if lhs.is_Add: 

2096 result = add_type(lhs, rhs, symbol, domain) 

2097 else: 

2098 result = rhs_s 

2099 

2100 return result 

2101 

2102 

2103def solveset(f, symbol=None, domain=S.Complexes): 

2104 r"""Solves a given inequality or equation with set as output 

2105 

2106 Parameters 

2107 ========== 

2108 

2109 f : Expr or a relational. 

2110 The target equation or inequality 

2111 symbol : Symbol 

2112 The variable for which the equation is solved 

2113 domain : Set 

2114 The domain over which the equation is solved 

2115 

2116 Returns 

2117 ======= 

2118 

2119 Set 

2120 A set of values for `symbol` for which `f` is True or is equal to 

2121 zero. An :class:`~.EmptySet` is returned if `f` is False or nonzero. 

2122 A :class:`~.ConditionSet` is returned as unsolved object if algorithms 

2123 to evaluate complete solution are not yet implemented. 

2124 

2125 ``solveset`` claims to be complete in the solution set that it returns. 

2126 

2127 Raises 

2128 ====== 

2129 

2130 NotImplementedError 

2131 The algorithms to solve inequalities in complex domain are 

2132 not yet implemented. 

2133 ValueError 

2134 The input is not valid. 

2135 RuntimeError 

2136 It is a bug, please report to the github issue tracker. 

2137 

2138 

2139 Notes 

2140 ===== 

2141 

2142 Python interprets 0 and 1 as False and True, respectively, but 

2143 in this function they refer to solutions of an expression. So 0 and 1 

2144 return the domain and EmptySet, respectively, while True and False 

2145 return the opposite (as they are assumed to be solutions of relational 

2146 expressions). 

2147 

2148 

2149 See Also 

2150 ======== 

2151 

2152 solveset_real: solver for real domain 

2153 solveset_complex: solver for complex domain 

2154 

2155 Examples 

2156 ======== 

2157 

2158 >>> from sympy import exp, sin, Symbol, pprint, S, Eq 

2159 >>> from sympy.solvers.solveset import solveset, solveset_real 

2160 

2161 * The default domain is complex. Not specifying a domain will lead 

2162 to the solving of the equation in the complex domain (and this 

2163 is not affected by the assumptions on the symbol): 

2164 

2165 >>> x = Symbol('x') 

2166 >>> pprint(solveset(exp(x) - 1, x), use_unicode=False) 

2167 {2*n*I*pi | n in Integers} 

2168 

2169 >>> x = Symbol('x', real=True) 

2170 >>> pprint(solveset(exp(x) - 1, x), use_unicode=False) 

2171 {2*n*I*pi | n in Integers} 

2172 

2173 * If you want to use ``solveset`` to solve the equation in the 

2174 real domain, provide a real domain. (Using ``solveset_real`` 

2175 does this automatically.) 

2176 

2177 >>> R = S.Reals 

2178 >>> x = Symbol('x') 

2179 >>> solveset(exp(x) - 1, x, R) 

2180 {0} 

2181 >>> solveset_real(exp(x) - 1, x) 

2182 {0} 

2183 

2184 The solution is unaffected by assumptions on the symbol: 

2185 

2186 >>> p = Symbol('p', positive=True) 

2187 >>> pprint(solveset(p**2 - 4)) 

2188 {-2, 2} 

2189 

2190 When a :class:`~.ConditionSet` is returned, symbols with assumptions that 

2191 would alter the set are replaced with more generic symbols: 

2192 

2193 >>> i = Symbol('i', imaginary=True) 

2194 >>> solveset(Eq(i**2 + i*sin(i), 1), i, domain=S.Reals) 

2195 ConditionSet(_R, Eq(_R**2 + _R*sin(_R) - 1, 0), Reals) 

2196 

2197 * Inequalities can be solved over the real domain only. Use of a complex 

2198 domain leads to a NotImplementedError. 

2199 

2200 >>> solveset(exp(x) > 1, x, R) 

2201 Interval.open(0, oo) 

2202 

2203 """ 

2204 f = sympify(f) 

2205 symbol = sympify(symbol) 

2206 

2207 if f is S.true: 

2208 return domain 

2209 

2210 if f is S.false: 

2211 return S.EmptySet 

2212 

2213 if not isinstance(f, (Expr, Relational, Number)): 

2214 raise ValueError("%s is not a valid SymPy expression" % f) 

2215 

2216 if not isinstance(symbol, (Expr, Relational)) and symbol is not None: 

2217 raise ValueError("%s is not a valid SymPy symbol" % (symbol,)) 

2218 

2219 if not isinstance(domain, Set): 

2220 raise ValueError("%s is not a valid domain" %(domain)) 

2221 

2222 free_symbols = f.free_symbols 

2223 

2224 if f.has(Piecewise): 

2225 f = piecewise_fold(f) 

2226 

2227 if symbol is None and not free_symbols: 

2228 b = Eq(f, 0) 

2229 if b is S.true: 

2230 return domain 

2231 elif b is S.false: 

2232 return S.EmptySet 

2233 else: 

2234 raise NotImplementedError(filldedent(''' 

2235 relationship between value and 0 is unknown: %s''' % b)) 

2236 

2237 if symbol is None: 

2238 if len(free_symbols) == 1: 

2239 symbol = free_symbols.pop() 

2240 elif free_symbols: 

2241 raise ValueError(filldedent(''' 

2242 The independent variable must be specified for a 

2243 multivariate equation.''')) 

2244 elif not isinstance(symbol, Symbol): 

2245 f, s, swap = recast_to_symbols([f], [symbol]) 

2246 # the xreplace will be needed if a ConditionSet is returned 

2247 return solveset(f[0], s[0], domain).xreplace(swap) 

2248 

2249 # solveset should ignore assumptions on symbols 

2250 if symbol not in _rc: 

2251 x = _rc[0] if domain.is_subset(S.Reals) else _rc[1] 

2252 rv = solveset(f.xreplace({symbol: x}), x, domain) 

2253 # try to use the original symbol if possible 

2254 try: 

2255 _rv = rv.xreplace({x: symbol}) 

2256 except TypeError: 

2257 _rv = rv 

2258 if rv.dummy_eq(_rv): 

2259 rv = _rv 

2260 return rv 

2261 

2262 # Abs has its own handling method which avoids the 

2263 # rewriting property that the first piece of abs(x) 

2264 # is for x >= 0 and the 2nd piece for x < 0 -- solutions 

2265 # can look better if the 2nd condition is x <= 0. Since 

2266 # the solution is a set, duplication of results is not 

2267 # an issue, e.g. {y, -y} when y is 0 will be {0} 

2268 f, mask = _masked(f, Abs) 

2269 f = f.rewrite(Piecewise) # everything that's not an Abs 

2270 for d, e in mask: 

2271 # everything *in* an Abs 

2272 e = e.func(e.args[0].rewrite(Piecewise)) 

2273 f = f.xreplace({d: e}) 

2274 f = piecewise_fold(f) 

2275 

2276 return _solveset(f, symbol, domain, _check=True) 

2277 

2278 

2279def solveset_real(f, symbol): 

2280 return solveset(f, symbol, S.Reals) 

2281 

2282 

2283def solveset_complex(f, symbol): 

2284 return solveset(f, symbol, S.Complexes) 

2285 

2286 

2287def _solveset_multi(eqs, syms, domains): 

2288 '''Basic implementation of a multivariate solveset. 

2289 

2290 For internal use (not ready for public consumption)''' 

2291 

2292 rep = {} 

2293 for sym, dom in zip(syms, domains): 

2294 if dom is S.Reals: 

2295 rep[sym] = Symbol(sym.name, real=True) 

2296 eqs = [eq.subs(rep) for eq in eqs] 

2297 syms = [sym.subs(rep) for sym in syms] 

2298 

2299 syms = tuple(syms) 

2300 

2301 if len(eqs) == 0: 

2302 return ProductSet(*domains) 

2303 

2304 if len(syms) == 1: 

2305 sym = syms[0] 

2306 domain = domains[0] 

2307 solsets = [solveset(eq, sym, domain) for eq in eqs] 

2308 solset = Intersection(*solsets) 

2309 return ImageSet(Lambda((sym,), (sym,)), solset).doit() 

2310 

2311 eqs = sorted(eqs, key=lambda eq: len(eq.free_symbols & set(syms))) 

2312 

2313 for n, eq in enumerate(eqs): 

2314 sols = [] 

2315 all_handled = True 

2316 for sym in syms: 

2317 if sym not in eq.free_symbols: 

2318 continue 

2319 sol = solveset(eq, sym, domains[syms.index(sym)]) 

2320 

2321 if isinstance(sol, FiniteSet): 

2322 i = syms.index(sym) 

2323 symsp = syms[:i] + syms[i+1:] 

2324 domainsp = domains[:i] + domains[i+1:] 

2325 eqsp = eqs[:n] + eqs[n+1:] 

2326 for s in sol: 

2327 eqsp_sub = [eq.subs(sym, s) for eq in eqsp] 

2328 sol_others = _solveset_multi(eqsp_sub, symsp, domainsp) 

2329 fun = Lambda((symsp,), symsp[:i] + (s,) + symsp[i:]) 

2330 sols.append(ImageSet(fun, sol_others).doit()) 

2331 else: 

2332 all_handled = False 

2333 if all_handled: 

2334 return Union(*sols) 

2335 

2336 

2337def solvify(f, symbol, domain): 

2338 """Solves an equation using solveset and returns the solution in accordance 

2339 with the `solve` output API. 

2340 

2341 Returns 

2342 ======= 

2343 

2344 We classify the output based on the type of solution returned by `solveset`. 

2345 

2346 Solution | Output 

2347 ---------------------------------------- 

2348 FiniteSet | list 

2349 

2350 ImageSet, | list (if `f` is periodic) 

2351 Union | 

2352 

2353 Union | list (with FiniteSet) 

2354 

2355 EmptySet | empty list 

2356 

2357 Others | None 

2358 

2359 

2360 Raises 

2361 ====== 

2362 

2363 NotImplementedError 

2364 A ConditionSet is the input. 

2365 

2366 Examples 

2367 ======== 

2368 

2369 >>> from sympy.solvers.solveset import solvify 

2370 >>> from sympy.abc import x 

2371 >>> from sympy import S, tan, sin, exp 

2372 >>> solvify(x**2 - 9, x, S.Reals) 

2373 [-3, 3] 

2374 >>> solvify(sin(x) - 1, x, S.Reals) 

2375 [pi/2] 

2376 >>> solvify(tan(x), x, S.Reals) 

2377 [0] 

2378 >>> solvify(exp(x) - 1, x, S.Complexes) 

2379 

2380 >>> solvify(exp(x) - 1, x, S.Reals) 

2381 [0] 

2382 

2383 """ 

2384 solution_set = solveset(f, symbol, domain) 

2385 result = None 

2386 if solution_set is S.EmptySet: 

2387 result = [] 

2388 

2389 elif isinstance(solution_set, ConditionSet): 

2390 raise NotImplementedError('solveset is unable to solve this equation.') 

2391 

2392 elif isinstance(solution_set, FiniteSet): 

2393 result = list(solution_set) 

2394 

2395 else: 

2396 period = periodicity(f, symbol) 

2397 if period is not None: 

2398 solutions = S.EmptySet 

2399 iter_solutions = () 

2400 if isinstance(solution_set, ImageSet): 

2401 iter_solutions = (solution_set,) 

2402 elif isinstance(solution_set, Union): 

2403 if all(isinstance(i, ImageSet) for i in solution_set.args): 

2404 iter_solutions = solution_set.args 

2405 

2406 for solution in iter_solutions: 

2407 solutions += solution.intersect(Interval(0, period, False, True)) 

2408 

2409 if isinstance(solutions, FiniteSet): 

2410 result = list(solutions) 

2411 

2412 else: 

2413 solution = solution_set.intersect(domain) 

2414 if isinstance(solution, Union): 

2415 # concerned about only FiniteSet with Union but not about ImageSet 

2416 # if required could be extend 

2417 if any(isinstance(i, FiniteSet) for i in solution.args): 

2418 result = [sol for soln in solution.args \ 

2419 for sol in soln.args if isinstance(soln,FiniteSet)] 

2420 else: 

2421 return None 

2422 

2423 elif isinstance(solution, FiniteSet): 

2424 result += solution 

2425 

2426 return result 

2427 

2428 

2429############################################################################### 

2430################################ LINSOLVE ##################################### 

2431############################################################################### 

2432 

2433 

2434def linear_coeffs(eq, *syms, dict=False): 

2435 """Return a list whose elements are the coefficients of the 

2436 corresponding symbols in the sum of terms in ``eq``. 

2437 The additive constant is returned as the last element of the 

2438 list. 

2439 

2440 Raises 

2441 ====== 

2442 

2443 NonlinearError 

2444 The equation contains a nonlinear term 

2445 ValueError 

2446 duplicate or unordered symbols are passed 

2447 

2448 Parameters 

2449 ========== 

2450 

2451 dict - (default False) when True, return coefficients as a 

2452 dictionary with coefficients keyed to syms that were present; 

2453 key 1 gives the constant term 

2454 

2455 Examples 

2456 ======== 

2457 

2458 >>> from sympy.solvers.solveset import linear_coeffs 

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

2460 >>> linear_coeffs(3*x + 2*y - 1, x, y) 

2461 [3, 2, -1] 

2462 

2463 It is not necessary to expand the expression: 

2464 

2465 >>> linear_coeffs(x + y*(z*(x*3 + 2) + 3), x) 

2466 [3*y*z + 1, y*(2*z + 3)] 

2467 

2468 When nonlinear is detected, an error will be raised: 

2469 

2470 * even if they would cancel after expansion (so the 

2471 situation does not pass silently past the caller's 

2472 attention) 

2473 

2474 >>> eq = 1/x*(x - 1) + 1/x 

2475 >>> linear_coeffs(eq.expand(), x) 

2476 [0, 1] 

2477 >>> linear_coeffs(eq, x) 

2478 Traceback (most recent call last): 

2479 ... 

2480 NonlinearError: 

2481 nonlinear in given generators 

2482 

2483 * when there are cross terms 

2484 

2485 >>> linear_coeffs(x*(y + 1), x, y) 

2486 Traceback (most recent call last): 

2487 ... 

2488 NonlinearError: 

2489 symbol-dependent cross-terms encountered 

2490 

2491 * when there are terms that contain an expression 

2492 dependent on the symbols that is not linear 

2493 

2494 >>> linear_coeffs(x**2, x) 

2495 Traceback (most recent call last): 

2496 ... 

2497 NonlinearError: 

2498 nonlinear in given generators 

2499 """ 

2500 eq = _sympify(eq) 

2501 if len(syms) == 1 and iterable(syms[0]) and not isinstance(syms[0], Basic): 

2502 raise ValueError('expecting unpacked symbols, *syms') 

2503 symset = set(syms) 

2504 if len(symset) != len(syms): 

2505 raise ValueError('duplicate symbols given') 

2506 try: 

2507 d, c = _linear_eq_to_dict([eq], symset) 

2508 d = d[0] 

2509 c = c[0] 

2510 except PolyNonlinearError as err: 

2511 raise NonlinearError(str(err)) 

2512 if dict: 

2513 if c: 

2514 d[S.One] = c 

2515 return d 

2516 rv = [S.Zero]*(len(syms) + 1) 

2517 rv[-1] = c 

2518 for i, k in enumerate(syms): 

2519 if k not in d: 

2520 continue 

2521 rv[i] = d[k] 

2522 return rv 

2523 

2524 

2525def linear_eq_to_matrix(equations, *symbols): 

2526 r""" 

2527 Converts a given System of Equations into Matrix form. 

2528 Here `equations` must be a linear system of equations in 

2529 `symbols`. Element ``M[i, j]`` corresponds to the coefficient 

2530 of the jth symbol in the ith equation. 

2531 

2532 The Matrix form corresponds to the augmented matrix form. 

2533 For example: 

2534 

2535 .. math:: 4x + 2y + 3z = 1 

2536 .. math:: 3x + y + z = -6 

2537 .. math:: 2x + 4y + 9z = 2 

2538 

2539 This system will return $A$ and $b$ as: 

2540 

2541 $$ A = \left[\begin{array}{ccc} 

2542 4 & 2 & 3 \\ 

2543 3 & 1 & 1 \\ 

2544 2 & 4 & 9 

2545 \end{array}\right] \ \ b = \left[\begin{array}{c} 

2546 1 \\ -6 \\ 2 

2547 \end{array}\right] $$ 

2548 

2549 The only simplification performed is to convert 

2550 ``Eq(a, b)`` $\Rightarrow a - b$. 

2551 

2552 Raises 

2553 ====== 

2554 

2555 NonlinearError 

2556 The equations contain a nonlinear term. 

2557 ValueError 

2558 The symbols are not given or are not unique. 

2559 

2560 Examples 

2561 ======== 

2562 

2563 >>> from sympy import linear_eq_to_matrix, symbols 

2564 >>> c, x, y, z = symbols('c, x, y, z') 

2565 

2566 The coefficients (numerical or symbolic) of the symbols will 

2567 be returned as matrices: 

2568 

2569 >>> eqns = [c*x + z - 1 - c, y + z, x - y] 

2570 >>> A, b = linear_eq_to_matrix(eqns, [x, y, z]) 

2571 >>> A 

2572 Matrix([ 

2573 [c, 0, 1], 

2574 [0, 1, 1], 

2575 [1, -1, 0]]) 

2576 >>> b 

2577 Matrix([ 

2578 [c + 1], 

2579 [ 0], 

2580 [ 0]]) 

2581 

2582 This routine does not simplify expressions and will raise an error 

2583 if nonlinearity is encountered: 

2584 

2585 >>> eqns = [ 

2586 ... (x**2 - 3*x)/(x - 3) - 3, 

2587 ... y**2 - 3*y - y*(y - 4) + x - 4] 

2588 >>> linear_eq_to_matrix(eqns, [x, y]) 

2589 Traceback (most recent call last): 

2590 ... 

2591 NonlinearError: 

2592 symbol-dependent term can be ignored using `strict=False` 

2593 

2594 Simplifying these equations will discard the removable singularity 

2595 in the first and reveal the linear structure of the second: 

2596 

2597 >>> [e.simplify() for e in eqns] 

2598 [x - 3, x + y - 4] 

2599 

2600 Any such simplification needed to eliminate nonlinear terms must 

2601 be done *before* calling this routine. 

2602 """ 

2603 if not symbols: 

2604 raise ValueError(filldedent(''' 

2605 Symbols must be given, for which coefficients 

2606 are to be found. 

2607 ''')) 

2608 

2609 if hasattr(symbols[0], '__iter__'): 

2610 symbols = symbols[0] 

2611 

2612 if has_dups(symbols): 

2613 raise ValueError('Symbols must be unique') 

2614 

2615 equations = sympify(equations) 

2616 if isinstance(equations, MatrixBase): 

2617 equations = list(equations) 

2618 elif isinstance(equations, (Expr, Eq)): 

2619 equations = [equations] 

2620 elif not is_sequence(equations): 

2621 raise ValueError(filldedent(''' 

2622 Equation(s) must be given as a sequence, Expr, 

2623 Eq or Matrix. 

2624 ''')) 

2625 

2626 # construct the dictionaries 

2627 try: 

2628 eq, c = _linear_eq_to_dict(equations, symbols) 

2629 except PolyNonlinearError as err: 

2630 raise NonlinearError(str(err)) 

2631 # prepare output matrices 

2632 n, m = shape = len(eq), len(symbols) 

2633 ix = dict(zip(symbols, range(m))) 

2634 A = zeros(*shape) 

2635 for row, d in enumerate(eq): 

2636 for k in d: 

2637 col = ix[k] 

2638 A[row, col] = d[k] 

2639 b = Matrix(n, 1, [-i for i in c]) 

2640 return A, b 

2641 

2642 

2643def linsolve(system, *symbols): 

2644 r""" 

2645 Solve system of $N$ linear equations with $M$ variables; both 

2646 underdetermined and overdetermined systems are supported. 

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

2648 Zero solutions throws a ValueError, whereas infinite 

2649 solutions are represented parametrically in terms of the given 

2650 symbols. For unique solution a :class:`~.FiniteSet` of ordered tuples 

2651 is returned. 

2652 

2653 All standard input formats are supported: 

2654 For the given set of equations, the respective input types 

2655 are given below: 

2656 

2657 .. math:: 3x + 2y - z = 1 

2658 .. math:: 2x - 2y + 4z = -2 

2659 .. math:: 2x - y + 2z = 0 

2660 

2661 * Augmented matrix form, ``system`` given below: 

2662 

2663 $$ \text{system} = \left[{array}{cccc} 

2664 3 & 2 & -1 & 1\\ 

2665 2 & -2 & 4 & -2\\ 

2666 2 & -1 & 2 & 0 

2667 \end{array}\right] $$ 

2668 

2669 :: 

2670 

2671 system = Matrix([[3, 2, -1, 1], [2, -2, 4, -2], [2, -1, 2, 0]]) 

2672 

2673 * List of equations form 

2674 

2675 :: 

2676 

2677 system = [3x + 2y - z - 1, 2x - 2y + 4z + 2, 2x - y + 2z] 

2678 

2679 * Input $A$ and $b$ in matrix form (from $Ax = b$) are given as: 

2680 

2681 $$ A = \left[\begin{array}{ccc} 

2682 3 & 2 & -1 \\ 

2683 2 & -2 & 4 \\ 

2684 2 & -1 & 2 

2685 \end{array}\right] \ \ b = \left[\begin{array}{c} 

2686 1 \\ -2 \\ 0 

2687 \end{array}\right] $$ 

2688 

2689 :: 

2690 

2691 A = Matrix([[3, 2, -1], [2, -2, 4], [2, -1, 2]]) 

2692 b = Matrix([[1], [-2], [0]]) 

2693 system = (A, b) 

2694 

2695 Symbols can always be passed but are actually only needed 

2696 when 1) a system of equations is being passed and 2) the 

2697 system is passed as an underdetermined matrix and one wants 

2698 to control the name of the free variables in the result. 

2699 An error is raised if no symbols are used for case 1, but if 

2700 no symbols are provided for case 2, internally generated symbols 

2701 will be provided. When providing symbols for case 2, there should 

2702 be at least as many symbols are there are columns in matrix A. 

2703 

2704 The algorithm used here is Gauss-Jordan elimination, which 

2705 results, after elimination, in a row echelon form matrix. 

2706 

2707 Returns 

2708 ======= 

2709 

2710 A FiniteSet containing an ordered tuple of values for the 

2711 unknowns for which the `system` has a solution. (Wrapping 

2712 the tuple in FiniteSet is used to maintain a consistent 

2713 output format throughout solveset.) 

2714 

2715 Returns EmptySet, if the linear system is inconsistent. 

2716 

2717 Raises 

2718 ====== 

2719 

2720 ValueError 

2721 The input is not valid. 

2722 The symbols are not given. 

2723 

2724 Examples 

2725 ======== 

2726 

2727 >>> from sympy import Matrix, linsolve, symbols 

2728 >>> x, y, z = symbols("x, y, z") 

2729 >>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 10]]) 

2730 >>> b = Matrix([3, 6, 9]) 

2731 >>> A 

2732 Matrix([ 

2733 [1, 2, 3], 

2734 [4, 5, 6], 

2735 [7, 8, 10]]) 

2736 >>> b 

2737 Matrix([ 

2738 [3], 

2739 [6], 

2740 [9]]) 

2741 >>> linsolve((A, b), [x, y, z]) 

2742 {(-1, 2, 0)} 

2743 

2744 * Parametric Solution: In case the system is underdetermined, the 

2745 function will return a parametric solution in terms of the given 

2746 symbols. Those that are free will be returned unchanged. e.g. in 

2747 the system below, `z` is returned as the solution for variable z; 

2748 it can take on any value. 

2749 

2750 >>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) 

2751 >>> b = Matrix([3, 6, 9]) 

2752 >>> linsolve((A, b), x, y, z) 

2753 {(z - 1, 2 - 2*z, z)} 

2754 

2755 If no symbols are given, internally generated symbols will be used. 

2756 The ``tau0`` in the third position indicates (as before) that the third 

2757 variable -- whatever it is named -- can take on any value: 

2758 

2759 >>> linsolve((A, b)) 

2760 {(tau0 - 1, 2 - 2*tau0, tau0)} 

2761 

2762 * List of equations as input 

2763 

2764 >>> Eqns = [3*x + 2*y - z - 1, 2*x - 2*y + 4*z + 2, - x + y/2 - z] 

2765 >>> linsolve(Eqns, x, y, z) 

2766 {(1, -2, -2)} 

2767 

2768 * Augmented matrix as input 

2769 

2770 >>> aug = Matrix([[2, 1, 3, 1], [2, 6, 8, 3], [6, 8, 18, 5]]) 

2771 >>> aug 

2772 Matrix([ 

2773 [2, 1, 3, 1], 

2774 [2, 6, 8, 3], 

2775 [6, 8, 18, 5]]) 

2776 >>> linsolve(aug, x, y, z) 

2777 {(3/10, 2/5, 0)} 

2778 

2779 * Solve for symbolic coefficients 

2780 

2781 >>> a, b, c, d, e, f = symbols('a, b, c, d, e, f') 

2782 >>> eqns = [a*x + b*y - c, d*x + e*y - f] 

2783 >>> linsolve(eqns, x, y) 

2784 {((-b*f + c*e)/(a*e - b*d), (a*f - c*d)/(a*e - b*d))} 

2785 

2786 * A degenerate system returns solution as set of given 

2787 symbols. 

2788 

2789 >>> system = Matrix(([0, 0, 0], [0, 0, 0], [0, 0, 0])) 

2790 >>> linsolve(system, x, y) 

2791 {(x, y)} 

2792 

2793 * For an empty system linsolve returns empty set 

2794 

2795 >>> linsolve([], x) 

2796 EmptySet 

2797 

2798 * An error is raised if any nonlinearity is detected, even 

2799 if it could be removed with expansion 

2800 

2801 >>> linsolve([x*(1/x - 1)], x) 

2802 Traceback (most recent call last): 

2803 ... 

2804 NonlinearError: nonlinear term: 1/x 

2805 

2806 >>> linsolve([x*(y + 1)], x, y) 

2807 Traceback (most recent call last): 

2808 ... 

2809 NonlinearError: nonlinear cross-term: x*(y + 1) 

2810 

2811 >>> linsolve([x**2 - 1], x) 

2812 Traceback (most recent call last): 

2813 ... 

2814 NonlinearError: nonlinear term: x**2 

2815 """ 

2816 if not system: 

2817 return S.EmptySet 

2818 

2819 # If second argument is an iterable 

2820 if symbols and hasattr(symbols[0], '__iter__'): 

2821 symbols = symbols[0] 

2822 sym_gen = isinstance(symbols, GeneratorType) 

2823 dup_msg = 'duplicate symbols given' 

2824 

2825 

2826 b = None # if we don't get b the input was bad 

2827 # unpack system 

2828 

2829 if hasattr(system, '__iter__'): 

2830 

2831 # 1). (A, b) 

2832 if len(system) == 2 and isinstance(system[0], MatrixBase): 

2833 A, b = system 

2834 

2835 # 2). (eq1, eq2, ...) 

2836 if not isinstance(system[0], MatrixBase): 

2837 if sym_gen or not symbols: 

2838 raise ValueError(filldedent(''' 

2839 When passing a system of equations, the explicit 

2840 symbols for which a solution is being sought must 

2841 be given as a sequence, too. 

2842 ''')) 

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

2844 raise ValueError(dup_msg) 

2845 

2846 # 

2847 # Pass to the sparse solver implemented in polys. It is important 

2848 # that we do not attempt to convert the equations to a matrix 

2849 # because that would be very inefficient for large sparse systems 

2850 # of equations. 

2851 # 

2852 eqs = system 

2853 eqs = [sympify(eq) for eq in eqs] 

2854 try: 

2855 sol = _linsolve(eqs, symbols) 

2856 except PolyNonlinearError as exc: 

2857 # e.g. cos(x) contains an element of the set of generators 

2858 raise NonlinearError(str(exc)) 

2859 

2860 if sol is None: 

2861 return S.EmptySet 

2862 

2863 sol = FiniteSet(Tuple(*(sol.get(sym, sym) for sym in symbols))) 

2864 return sol 

2865 

2866 elif isinstance(system, MatrixBase) and not ( 

2867 symbols and not isinstance(symbols, GeneratorType) and 

2868 isinstance(symbols[0], MatrixBase)): 

2869 # 3). A augmented with b 

2870 A, b = system[:, :-1], system[:, -1:] 

2871 

2872 if b is None: 

2873 raise ValueError("Invalid arguments") 

2874 if sym_gen: 

2875 symbols = [next(symbols) for i in range(A.cols)] 

2876 symset = set(symbols) 

2877 if any(symset & (A.free_symbols | b.free_symbols)): 

2878 raise ValueError(filldedent(''' 

2879 At least one of the symbols provided 

2880 already appears in the system to be solved. 

2881 One way to avoid this is to use Dummy symbols in 

2882 the generator, e.g. numbered_symbols('%s', cls=Dummy) 

2883 ''' % symbols[0].name.rstrip('1234567890'))) 

2884 elif len(symset) != len(symbols): 

2885 raise ValueError(dup_msg) 

2886 

2887 if not symbols: 

2888 symbols = [Dummy() for _ in range(A.cols)] 

2889 name = _uniquely_named_symbol('tau', (A, b), 

2890 compare=lambda i: str(i).rstrip('1234567890')).name 

2891 gen = numbered_symbols(name) 

2892 else: 

2893 gen = None 

2894 

2895 # This is just a wrapper for solve_lin_sys 

2896 eqs = [] 

2897 rows = A.tolist() 

2898 for rowi, bi in zip(rows, b): 

2899 terms = [elem * sym for elem, sym in zip(rowi, symbols) if elem] 

2900 terms.append(-bi) 

2901 eqs.append(Add(*terms)) 

2902 

2903 eqs, ring = sympy_eqs_to_ring(eqs, symbols) 

2904 sol = solve_lin_sys(eqs, ring, _raw=False) 

2905 if sol is None: 

2906 return S.EmptySet 

2907 #sol = {sym:val for sym, val in sol.items() if sym != val} 

2908 sol = FiniteSet(Tuple(*(sol.get(sym, sym) for sym in symbols))) 

2909 

2910 if gen is not None: 

2911 solsym = sol.free_symbols 

2912 rep = {sym: next(gen) for sym in symbols if sym in solsym} 

2913 sol = sol.subs(rep) 

2914 

2915 return sol 

2916 

2917 

2918############################################################################## 

2919# ------------------------------nonlinsolve ---------------------------------# 

2920############################################################################## 

2921 

2922 

2923def _return_conditionset(eqs, symbols): 

2924 # return conditionset 

2925 eqs = (Eq(lhs, 0) for lhs in eqs) 

2926 condition_set = ConditionSet( 

2927 Tuple(*symbols), And(*eqs), S.Complexes**len(symbols)) 

2928 return condition_set 

2929 

2930 

2931def substitution(system, symbols, result=[{}], known_symbols=[], 

2932 exclude=[], all_symbols=None): 

2933 r""" 

2934 Solves the `system` using substitution method. It is used in 

2935 :func:`~.nonlinsolve`. This will be called from :func:`~.nonlinsolve` when any 

2936 equation(s) is non polynomial equation. 

2937 

2938 Parameters 

2939 ========== 

2940 

2941 system : list of equations 

2942 The target system of equations 

2943 symbols : list of symbols to be solved. 

2944 The variable(s) for which the system is solved 

2945 known_symbols : list of solved symbols 

2946 Values are known for these variable(s) 

2947 result : An empty list or list of dict 

2948 If No symbol values is known then empty list otherwise 

2949 symbol as keys and corresponding value in dict. 

2950 exclude : Set of expression. 

2951 Mostly denominator expression(s) of the equations of the system. 

2952 Final solution should not satisfy these expressions. 

2953 all_symbols : known_symbols + symbols(unsolved). 

2954 

2955 Returns 

2956 ======= 

2957 

2958 A FiniteSet of ordered tuple of values of `all_symbols` for which the 

2959 `system` has solution. Order of values in the tuple is same as symbols 

2960 present in the parameter `all_symbols`. If parameter `all_symbols` is None 

2961 then same as symbols present in the parameter `symbols`. 

2962 

2963 Please note that general FiniteSet is unordered, the solution returned 

2964 here is not simply a FiniteSet of solutions, rather it is a FiniteSet of 

2965 ordered tuple, i.e. the first & only argument to FiniteSet is a tuple of 

2966 solutions, which is ordered, & hence the returned solution is ordered. 

2967 

2968 Also note that solution could also have been returned as an ordered tuple, 

2969 FiniteSet is just a wrapper `{}` around the tuple. It has no other 

2970 significance except for the fact it is just used to maintain a consistent 

2971 output format throughout the solveset. 

2972 

2973 Raises 

2974 ====== 

2975 

2976 ValueError 

2977 The input is not valid. 

2978 The symbols are not given. 

2979 AttributeError 

2980 The input symbols are not :class:`~.Symbol` type. 

2981 

2982 Examples 

2983 ======== 

2984 

2985 >>> from sympy import symbols, substitution 

2986 >>> x, y = symbols('x, y', real=True) 

2987 >>> substitution([x + y], [x], [{y: 1}], [y], set([]), [x, y]) 

2988 {(-1, 1)} 

2989 

2990 * When you want a soln not satisfying $x + 1 = 0$ 

2991 

2992 >>> substitution([x + y], [x], [{y: 1}], [y], set([x + 1]), [y, x]) 

2993 EmptySet 

2994 >>> substitution([x + y], [x], [{y: 1}], [y], set([x - 1]), [y, x]) 

2995 {(1, -1)} 

2996 >>> substitution([x + y - 1, y - x**2 + 5], [x, y]) 

2997 {(-3, 4), (2, -1)} 

2998 

2999 * Returns both real and complex solution 

3000 

3001 >>> x, y, z = symbols('x, y, z') 

3002 >>> from sympy import exp, sin 

3003 >>> substitution([exp(x) - sin(y), y**2 - 4], [x, y]) 

3004 {(ImageSet(Lambda(_n, I*(2*_n*pi + pi) + log(sin(2))), Integers), -2), 

3005 (ImageSet(Lambda(_n, 2*_n*I*pi + log(sin(2))), Integers), 2)} 

3006 

3007 >>> eqs = [z**2 + exp(2*x) - sin(y), -3 + exp(-y)] 

3008 >>> substitution(eqs, [y, z]) 

3009 {(-log(3), -sqrt(-exp(2*x) - sin(log(3)))), 

3010 (-log(3), sqrt(-exp(2*x) - sin(log(3)))), 

3011 (ImageSet(Lambda(_n, 2*_n*I*pi - log(3)), Integers), 

3012 ImageSet(Lambda(_n, -sqrt(-exp(2*x) + sin(2*_n*I*pi - log(3)))), Integers)), 

3013 (ImageSet(Lambda(_n, 2*_n*I*pi - log(3)), Integers), 

3014 ImageSet(Lambda(_n, sqrt(-exp(2*x) + sin(2*_n*I*pi - log(3)))), Integers))} 

3015 

3016 """ 

3017 

3018 if not system: 

3019 return S.EmptySet 

3020 

3021 if not symbols: 

3022 msg = ('Symbols must be given, for which solution of the ' 

3023 'system is to be found.') 

3024 raise ValueError(filldedent(msg)) 

3025 

3026 if not is_sequence(symbols): 

3027 msg = ('symbols should be given as a sequence, e.g. a list.' 

3028 'Not type %s: %s') 

3029 raise TypeError(filldedent(msg % (type(symbols), symbols))) 

3030 

3031 if not getattr(symbols[0], 'is_Symbol', False): 

3032 msg = ('Iterable of symbols must be given as ' 

3033 'second argument, not type %s: %s') 

3034 raise ValueError(filldedent(msg % (type(symbols[0]), symbols[0]))) 

3035 

3036 # By default `all_symbols` will be same as `symbols` 

3037 if all_symbols is None: 

3038 all_symbols = symbols 

3039 

3040 old_result = result 

3041 # storing complements and intersection for particular symbol 

3042 complements = {} 

3043 intersections = {} 

3044 

3045 # when total_solveset_call equals total_conditionset 

3046 # it means that solveset failed to solve all eqs. 

3047 total_conditionset = -1 

3048 total_solveset_call = -1 

3049 

3050 def _unsolved_syms(eq, sort=False): 

3051 """Returns the unsolved symbol present 

3052 in the equation `eq`. 

3053 """ 

3054 free = eq.free_symbols 

3055 unsolved = (free - set(known_symbols)) & set(all_symbols) 

3056 if sort: 

3057 unsolved = list(unsolved) 

3058 unsolved.sort(key=default_sort_key) 

3059 return unsolved 

3060 # end of _unsolved_syms() 

3061 

3062 # sort such that equation with the fewest potential symbols is first. 

3063 # means eq with less number of variable first in the list. 

3064 eqs_in_better_order = list( 

3065 ordered(system, lambda _: len(_unsolved_syms(_)))) 

3066 

3067 def add_intersection_complement(result, intersection_dict, complement_dict): 

3068 # If solveset has returned some intersection/complement 

3069 # for any symbol, it will be added in the final solution. 

3070 final_result = [] 

3071 for res in result: 

3072 res_copy = res 

3073 for key_res, value_res in res.items(): 

3074 intersect_set, complement_set = None, None 

3075 for key_sym, value_sym in intersection_dict.items(): 

3076 if key_sym == key_res: 

3077 intersect_set = value_sym 

3078 for key_sym, value_sym in complement_dict.items(): 

3079 if key_sym == key_res: 

3080 complement_set = value_sym 

3081 if intersect_set or complement_set: 

3082 new_value = FiniteSet(value_res) 

3083 if intersect_set and intersect_set != S.Complexes: 

3084 new_value = Intersection(new_value, intersect_set) 

3085 if complement_set: 

3086 new_value = Complement(new_value, complement_set) 

3087 if new_value is S.EmptySet: 

3088 res_copy = None 

3089 break 

3090 elif new_value.is_FiniteSet and len(new_value) == 1: 

3091 res_copy[key_res] = set(new_value).pop() 

3092 else: 

3093 res_copy[key_res] = new_value 

3094 

3095 if res_copy is not None: 

3096 final_result.append(res_copy) 

3097 return final_result 

3098 # end of def add_intersection_complement() 

3099 

3100 def _extract_main_soln(sym, sol, soln_imageset): 

3101 """Separate the Complements, Intersections, ImageSet lambda expr and 

3102 its base_set. This function returns the unmasks sol from different classes 

3103 of sets and also returns the appended ImageSet elements in a 

3104 soln_imageset (dict: where key as unmasked element and value as ImageSet). 

3105 """ 

3106 # if there is union, then need to check 

3107 # Complement, Intersection, Imageset. 

3108 # Order should not be changed. 

3109 if isinstance(sol, ConditionSet): 

3110 # extracts any solution in ConditionSet 

3111 sol = sol.base_set 

3112 

3113 if isinstance(sol, Complement): 

3114 # extract solution and complement 

3115 complements[sym] = sol.args[1] 

3116 sol = sol.args[0] 

3117 # complement will be added at the end 

3118 # using `add_intersection_complement` method 

3119 

3120 # if there is union of Imageset or other in soln. 

3121 # no testcase is written for this if block 

3122 if isinstance(sol, Union): 

3123 sol_args = sol.args 

3124 sol = S.EmptySet 

3125 # We need in sequence so append finteset elements 

3126 # and then imageset or other. 

3127 for sol_arg2 in sol_args: 

3128 if isinstance(sol_arg2, FiniteSet): 

3129 sol += sol_arg2 

3130 else: 

3131 # ImageSet, Intersection, complement then 

3132 # append them directly 

3133 sol += FiniteSet(sol_arg2) 

3134 

3135 if isinstance(sol, Intersection): 

3136 # Interval/Set will be at 0th index always 

3137 if sol.args[0] not in (S.Reals, S.Complexes): 

3138 # Sometimes solveset returns soln with intersection 

3139 # S.Reals or S.Complexes. We don't consider that 

3140 # intersection. 

3141 intersections[sym] = sol.args[0] 

3142 sol = sol.args[1] 

3143 # after intersection and complement Imageset should 

3144 # be checked. 

3145 if isinstance(sol, ImageSet): 

3146 soln_imagest = sol 

3147 expr2 = sol.lamda.expr 

3148 sol = FiniteSet(expr2) 

3149 soln_imageset[expr2] = soln_imagest 

3150 

3151 if not isinstance(sol, FiniteSet): 

3152 sol = FiniteSet(sol) 

3153 return sol, soln_imageset 

3154 # end of def _extract_main_soln() 

3155 

3156 # helper function for _append_new_soln 

3157 def _check_exclude(rnew, imgset_yes): 

3158 rnew_ = rnew 

3159 if imgset_yes: 

3160 # replace all dummy variables (Imageset lambda variables) 

3161 # with zero before `checksol`. Considering fundamental soln 

3162 # for `checksol`. 

3163 rnew_copy = rnew.copy() 

3164 dummy_n = imgset_yes[0] 

3165 for key_res, value_res in rnew_copy.items(): 

3166 rnew_copy[key_res] = value_res.subs(dummy_n, 0) 

3167 rnew_ = rnew_copy 

3168 # satisfy_exclude == true if it satisfies the expr of `exclude` list. 

3169 try: 

3170 # something like : `Mod(-log(3), 2*I*pi)` can't be 

3171 # simplified right now, so `checksol` returns `TypeError`. 

3172 # when this issue is fixed this try block should be 

3173 # removed. Mod(-log(3), 2*I*pi) == -log(3) 

3174 satisfy_exclude = any( 

3175 checksol(d, rnew_) for d in exclude) 

3176 except TypeError: 

3177 satisfy_exclude = None 

3178 return satisfy_exclude 

3179 # end of def _check_exclude() 

3180 

3181 # helper function for _append_new_soln 

3182 def _restore_imgset(rnew, original_imageset, newresult): 

3183 restore_sym = set(rnew.keys()) & \ 

3184 set(original_imageset.keys()) 

3185 for key_sym in restore_sym: 

3186 img = original_imageset[key_sym] 

3187 rnew[key_sym] = img 

3188 if rnew not in newresult: 

3189 newresult.append(rnew) 

3190 # end of def _restore_imgset() 

3191 

3192 def _append_eq(eq, result, res, delete_soln, n=None): 

3193 u = Dummy('u') 

3194 if n: 

3195 eq = eq.subs(n, 0) 

3196 satisfy = eq if eq in (True, False) else checksol(u, u, eq, minimal=True) 

3197 if satisfy is False: 

3198 delete_soln = True 

3199 res = {} 

3200 else: 

3201 result.append(res) 

3202 return result, res, delete_soln 

3203 

3204 def _append_new_soln(rnew, sym, sol, imgset_yes, soln_imageset, 

3205 original_imageset, newresult, eq=None): 

3206 """If `rnew` (A dict <symbol: soln>) contains valid soln 

3207 append it to `newresult` list. 

3208 `imgset_yes` is (base, dummy_var) if there was imageset in previously 

3209 calculated result(otherwise empty tuple). `original_imageset` is dict 

3210 of imageset expr and imageset from this result. 

3211 `soln_imageset` dict of imageset expr and imageset of new soln. 

3212 """ 

3213 satisfy_exclude = _check_exclude(rnew, imgset_yes) 

3214 delete_soln = False 

3215 # soln should not satisfy expr present in `exclude` list. 

3216 if not satisfy_exclude: 

3217 local_n = None 

3218 # if it is imageset 

3219 if imgset_yes: 

3220 local_n = imgset_yes[0] 

3221 base = imgset_yes[1] 

3222 if sym and sol: 

3223 # when `sym` and `sol` is `None` means no new 

3224 # soln. In that case we will append rnew directly after 

3225 # substituting original imagesets in rnew values if present 

3226 # (second last line of this function using _restore_imgset) 

3227 dummy_list = list(sol.atoms(Dummy)) 

3228 # use one dummy `n` which is in 

3229 # previous imageset 

3230 local_n_list = [ 

3231 local_n for i in range( 

3232 0, len(dummy_list))] 

3233 

3234 dummy_zip = zip(dummy_list, local_n_list) 

3235 lam = Lambda(local_n, sol.subs(dummy_zip)) 

3236 rnew[sym] = ImageSet(lam, base) 

3237 if eq is not None: 

3238 newresult, rnew, delete_soln = _append_eq( 

3239 eq, newresult, rnew, delete_soln, local_n) 

3240 elif eq is not None: 

3241 newresult, rnew, delete_soln = _append_eq( 

3242 eq, newresult, rnew, delete_soln) 

3243 elif sol in soln_imageset.keys(): 

3244 rnew[sym] = soln_imageset[sol] 

3245 # restore original imageset 

3246 _restore_imgset(rnew, original_imageset, newresult) 

3247 else: 

3248 newresult.append(rnew) 

3249 elif satisfy_exclude: 

3250 delete_soln = True 

3251 rnew = {} 

3252 _restore_imgset(rnew, original_imageset, newresult) 

3253 return newresult, delete_soln 

3254 # end of def _append_new_soln() 

3255 

3256 def _new_order_result(result, eq): 

3257 # separate first, second priority. `res` that makes `eq` value equals 

3258 # to zero, should be used first then other result(second priority). 

3259 # If it is not done then we may miss some soln. 

3260 first_priority = [] 

3261 second_priority = [] 

3262 for res in result: 

3263 if not any(isinstance(val, ImageSet) for val in res.values()): 

3264 if eq.subs(res) == 0: 

3265 first_priority.append(res) 

3266 else: 

3267 second_priority.append(res) 

3268 if first_priority or second_priority: 

3269 return first_priority + second_priority 

3270 return result 

3271 

3272 def _solve_using_known_values(result, solver): 

3273 """Solves the system using already known solution 

3274 (result contains the dict <symbol: value>). 

3275 solver is :func:`~.solveset_complex` or :func:`~.solveset_real`. 

3276 """ 

3277 # stores imageset <expr: imageset(Lambda(n, expr), base)>. 

3278 soln_imageset = {} 

3279 total_solvest_call = 0 

3280 total_conditionst = 0 

3281 

3282 # sort such that equation with the fewest potential symbols is first. 

3283 # means eq with less variable first 

3284 for index, eq in enumerate(eqs_in_better_order): 

3285 newresult = [] 

3286 original_imageset = {} 

3287 # if imageset expr is used to solve other symbol 

3288 imgset_yes = False 

3289 result = _new_order_result(result, eq) 

3290 for res in result: 

3291 got_symbol = set() # symbols solved in one iteration 

3292 # find the imageset and use its expr. 

3293 for key_res, value_res in res.items(): 

3294 if isinstance(value_res, ImageSet): 

3295 res[key_res] = value_res.lamda.expr 

3296 original_imageset[key_res] = value_res 

3297 dummy_n = value_res.lamda.expr.atoms(Dummy).pop() 

3298 (base,) = value_res.base_sets 

3299 imgset_yes = (dummy_n, base) 

3300 # update eq with everything that is known so far 

3301 eq2 = eq.subs(res).expand() 

3302 unsolved_syms = _unsolved_syms(eq2, sort=True) 

3303 if not unsolved_syms: 

3304 if res: 

3305 newresult, delete_res = _append_new_soln( 

3306 res, None, None, imgset_yes, soln_imageset, 

3307 original_imageset, newresult, eq2) 

3308 if delete_res: 

3309 # `delete_res` is true, means substituting `res` in 

3310 # eq2 doesn't return `zero` or deleting the `res` 

3311 # (a soln) since it staisfies expr of `exclude` 

3312 # list. 

3313 result.remove(res) 

3314 continue # skip as it's independent of desired symbols 

3315 depen1, depen2 = (eq2.rewrite(Add)).as_independent(*unsolved_syms) 

3316 if (depen1.has(Abs) or depen2.has(Abs)) and solver == solveset_complex: 

3317 # Absolute values cannot be inverted in the 

3318 # complex domain 

3319 continue 

3320 soln_imageset = {} 

3321 for sym in unsolved_syms: 

3322 not_solvable = False 

3323 try: 

3324 soln = solver(eq2, sym) 

3325 total_solvest_call += 1 

3326 soln_new = S.EmptySet 

3327 if isinstance(soln, Complement): 

3328 # separate solution and complement 

3329 complements[sym] = soln.args[1] 

3330 soln = soln.args[0] 

3331 # complement will be added at the end 

3332 if isinstance(soln, Intersection): 

3333 # Interval will be at 0th index always 

3334 if soln.args[0] != Interval(-oo, oo): 

3335 # sometimes solveset returns soln 

3336 # with intersection S.Reals, to confirm that 

3337 # soln is in domain=S.Reals 

3338 intersections[sym] = soln.args[0] 

3339 soln_new += soln.args[1] 

3340 soln = soln_new if soln_new else soln 

3341 if index > 0 and solver == solveset_real: 

3342 # one symbol's real soln, another symbol may have 

3343 # corresponding complex soln. 

3344 if not isinstance(soln, (ImageSet, ConditionSet)): 

3345 soln += solveset_complex(eq2, sym) # might give ValueError with Abs 

3346 except (NotImplementedError, ValueError): 

3347 # If solveset is not able to solve equation `eq2`. Next 

3348 # time we may get soln using next equation `eq2` 

3349 continue 

3350 if isinstance(soln, ConditionSet): 

3351 if soln.base_set in (S.Reals, S.Complexes): 

3352 soln = S.EmptySet 

3353 # don't do `continue` we may get soln 

3354 # in terms of other symbol(s) 

3355 not_solvable = True 

3356 total_conditionst += 1 

3357 else: 

3358 soln = soln.base_set 

3359 

3360 if soln is not S.EmptySet: 

3361 soln, soln_imageset = _extract_main_soln( 

3362 sym, soln, soln_imageset) 

3363 

3364 for sol in soln: 

3365 # sol is not a `Union` since we checked it 

3366 # before this loop 

3367 sol, soln_imageset = _extract_main_soln( 

3368 sym, sol, soln_imageset) 

3369 sol = set(sol).pop() 

3370 free = sol.free_symbols 

3371 if got_symbol and any( 

3372 ss in free for ss in got_symbol 

3373 ): 

3374 # sol depends on previously solved symbols 

3375 # then continue 

3376 continue 

3377 rnew = res.copy() 

3378 # put each solution in res and append the new result 

3379 # in the new result list (solution for symbol `s`) 

3380 # along with old results. 

3381 for k, v in res.items(): 

3382 if isinstance(v, Expr) and isinstance(sol, Expr): 

3383 # if any unsolved symbol is present 

3384 # Then subs known value 

3385 rnew[k] = v.subs(sym, sol) 

3386 # and add this new solution 

3387 if sol in soln_imageset.keys(): 

3388 # replace all lambda variables with 0. 

3389 imgst = soln_imageset[sol] 

3390 rnew[sym] = imgst.lamda( 

3391 *[0 for i in range(0, len( 

3392 imgst.lamda.variables))]) 

3393 else: 

3394 rnew[sym] = sol 

3395 newresult, delete_res = _append_new_soln( 

3396 rnew, sym, sol, imgset_yes, soln_imageset, 

3397 original_imageset, newresult) 

3398 if delete_res: 

3399 # deleting the `res` (a soln) since it staisfies 

3400 # eq of `exclude` list 

3401 result.remove(res) 

3402 # solution got for sym 

3403 if not not_solvable: 

3404 got_symbol.add(sym) 

3405 # next time use this new soln 

3406 if newresult: 

3407 result = newresult 

3408 return result, total_solvest_call, total_conditionst 

3409 # end def _solve_using_know_values() 

3410 

3411 new_result_real, solve_call1, cnd_call1 = _solve_using_known_values( 

3412 old_result, solveset_real) 

3413 new_result_complex, solve_call2, cnd_call2 = _solve_using_known_values( 

3414 old_result, solveset_complex) 

3415 

3416 # If total_solveset_call is equal to total_conditionset 

3417 # then solveset failed to solve all of the equations. 

3418 # In this case we return a ConditionSet here. 

3419 total_conditionset += (cnd_call1 + cnd_call2) 

3420 total_solveset_call += (solve_call1 + solve_call2) 

3421 

3422 if total_conditionset == total_solveset_call and total_solveset_call != -1: 

3423 return _return_conditionset(eqs_in_better_order, all_symbols) 

3424 

3425 # don't keep duplicate solutions 

3426 filtered_complex = [] 

3427 for i in list(new_result_complex): 

3428 for j in list(new_result_real): 

3429 if i.keys() != j.keys(): 

3430 continue 

3431 if all(a.dummy_eq(b) for a, b in zip(i.values(), j.values()) \ 

3432 if not (isinstance(a, int) and isinstance(b, int))): 

3433 break 

3434 else: 

3435 filtered_complex.append(i) 

3436 # overall result 

3437 result = new_result_real + filtered_complex 

3438 

3439 result_all_variables = [] 

3440 result_infinite = [] 

3441 for res in result: 

3442 if not res: 

3443 # means {None : None} 

3444 continue 

3445 # If length < len(all_symbols) means infinite soln. 

3446 # Some or all the soln is dependent on 1 symbol. 

3447 # eg. {x: y+2} then final soln {x: y+2, y: y} 

3448 if len(res) < len(all_symbols): 

3449 solved_symbols = res.keys() 

3450 unsolved = list(filter( 

3451 lambda x: x not in solved_symbols, all_symbols)) 

3452 for unsolved_sym in unsolved: 

3453 res[unsolved_sym] = unsolved_sym 

3454 result_infinite.append(res) 

3455 if res not in result_all_variables: 

3456 result_all_variables.append(res) 

3457 

3458 if result_infinite: 

3459 # we have general soln 

3460 # eg : [{x: -1, y : 1}, {x : -y, y: y}] then 

3461 # return [{x : -y, y : y}] 

3462 result_all_variables = result_infinite 

3463 if intersections or complements: 

3464 result_all_variables = add_intersection_complement( 

3465 result_all_variables, intersections, complements) 

3466 

3467 # convert to ordered tuple 

3468 result = S.EmptySet 

3469 for r in result_all_variables: 

3470 temp = [r[symb] for symb in all_symbols] 

3471 result += FiniteSet(tuple(temp)) 

3472 return result 

3473# end of def substitution() 

3474 

3475 

3476def _solveset_work(system, symbols): 

3477 soln = solveset(system[0], symbols[0]) 

3478 if isinstance(soln, FiniteSet): 

3479 _soln = FiniteSet(*[(s,) for s in soln]) 

3480 return _soln 

3481 else: 

3482 return FiniteSet(tuple(FiniteSet(soln))) 

3483 

3484 

3485def _handle_positive_dimensional(polys, symbols, denominators): 

3486 from sympy.polys.polytools import groebner 

3487 # substitution method where new system is groebner basis of the system 

3488 _symbols = list(symbols) 

3489 _symbols.sort(key=default_sort_key) 

3490 basis = groebner(polys, _symbols, polys=True) 

3491 new_system = [] 

3492 for poly_eq in basis: 

3493 new_system.append(poly_eq.as_expr()) 

3494 result = [{}] 

3495 result = substitution( 

3496 new_system, symbols, result, [], 

3497 denominators) 

3498 return result 

3499# end of def _handle_positive_dimensional() 

3500 

3501 

3502def _handle_zero_dimensional(polys, symbols, system): 

3503 # solve 0 dimensional poly system using `solve_poly_system` 

3504 result = solve_poly_system(polys, *symbols) 

3505 # May be some extra soln is added because 

3506 # we used `unrad` in `_separate_poly_nonpoly`, so 

3507 # need to check and remove if it is not a soln. 

3508 result_update = S.EmptySet 

3509 for res in result: 

3510 dict_sym_value = dict(list(zip(symbols, res))) 

3511 if all(checksol(eq, dict_sym_value) for eq in system): 

3512 result_update += FiniteSet(res) 

3513 return result_update 

3514# end of def _handle_zero_dimensional() 

3515 

3516 

3517def _separate_poly_nonpoly(system, symbols): 

3518 polys = [] 

3519 polys_expr = [] 

3520 nonpolys = [] 

3521 # unrad_changed stores a list of expressions containing 

3522 # radicals that were processed using unrad 

3523 # this is useful if solutions need to be checked later. 

3524 unrad_changed = [] 

3525 denominators = set() 

3526 poly = None 

3527 for eq in system: 

3528 # Store denom expressions that contain symbols 

3529 denominators.update(_simple_dens(eq, symbols)) 

3530 # Convert equality to expression 

3531 if isinstance(eq, Equality): 

3532 eq = eq.rewrite(Add) 

3533 # try to remove sqrt and rational power 

3534 without_radicals = unrad(simplify(eq), *symbols) 

3535 if without_radicals: 

3536 unrad_changed.append(eq) 

3537 eq_unrad, cov = without_radicals 

3538 if not cov: 

3539 eq = eq_unrad 

3540 if isinstance(eq, Expr): 

3541 eq = eq.as_numer_denom()[0] 

3542 poly = eq.as_poly(*symbols, extension=True) 

3543 elif simplify(eq).is_number: 

3544 continue 

3545 if poly is not None: 

3546 polys.append(poly) 

3547 polys_expr.append(poly.as_expr()) 

3548 else: 

3549 nonpolys.append(eq) 

3550 return polys, polys_expr, nonpolys, denominators, unrad_changed 

3551# end of def _separate_poly_nonpoly() 

3552 

3553 

3554def _handle_poly(polys, symbols): 

3555 # _handle_poly(polys, symbols) -> (poly_sol, poly_eqs) 

3556 # 

3557 # We will return possible solution information to nonlinsolve as well as a 

3558 # new system of polynomial equations to be solved if we cannot solve 

3559 # everything directly here. The new system of polynomial equations will be 

3560 # a lex-order Groebner basis for the original system. The lex basis 

3561 # hopefully separate some of the variables and equations and give something 

3562 # easier for substitution to work with. 

3563 

3564 # The format for representing solution sets in nonlinsolve and substitution 

3565 # is a list of dicts. These are the special cases: 

3566 no_information = [{}] # No equations solved yet 

3567 no_solutions = [] # The system is inconsistent and has no solutions. 

3568 

3569 # If there is no need to attempt further solution of these equations then 

3570 # we return no equations: 

3571 no_equations = [] 

3572 

3573 inexact = any(not p.domain.is_Exact for p in polys) 

3574 if inexact: 

3575 # The use of Groebner over RR is likely to result incorrectly in an 

3576 # inconsistent Groebner basis. So, convert any float coefficients to 

3577 # Rational before computing the Groebner basis. 

3578 polys = [poly(nsimplify(p, rational=True)) for p in polys] 

3579 

3580 # Compute a Groebner basis in grevlex order wrt the ordering given. We will 

3581 # try to convert this to lex order later. Usually it seems to be more 

3582 # efficient to compute a lex order basis by computing a grevlex basis and 

3583 # converting to lex with fglm. 

3584 basis = groebner(polys, symbols, order='grevlex', polys=False) 

3585 

3586 # 

3587 # No solutions (inconsistent equations)? 

3588 # 

3589 if 1 in basis: 

3590 

3591 # No solutions: 

3592 poly_sol = no_solutions 

3593 poly_eqs = no_equations 

3594 

3595 # 

3596 # Finite number of solutions (zero-dimensional case) 

3597 # 

3598 elif basis.is_zero_dimensional: 

3599 

3600 # Convert Groebner basis to lex ordering 

3601 basis = basis.fglm('lex') 

3602 

3603 # Convert polynomial coefficients back to float before calling 

3604 # solve_poly_system 

3605 if inexact: 

3606 basis = [nfloat(p) for p in basis] 

3607 

3608 # Solve the zero-dimensional case using solve_poly_system if possible. 

3609 # If some polynomials have factors that cannot be solved in radicals 

3610 # then this will fail. Using solve_poly_system(..., strict=True) 

3611 # ensures that we either get a complete solution set in radicals or 

3612 # UnsolvableFactorError will be raised. 

3613 try: 

3614 result = solve_poly_system(basis, *symbols, strict=True) 

3615 except UnsolvableFactorError: 

3616 # Failure... not fully solvable in radicals. Return the lex-order 

3617 # basis for substitution to handle. 

3618 poly_sol = no_information 

3619 poly_eqs = list(basis) 

3620 else: 

3621 # Success! We have a finite solution set and solve_poly_system has 

3622 # succeeded in finding all solutions. Return the solutions and also 

3623 # an empty list of remaining equations to be solved. 

3624 poly_sol = [dict(zip(symbols, res)) for res in result] 

3625 poly_eqs = no_equations 

3626 

3627 # 

3628 # Infinite families of solutions (positive-dimensional case) 

3629 # 

3630 else: 

3631 # In this case the grevlex basis cannot be converted to lex using the 

3632 # fglm method and also solve_poly_system cannot solve the equations. We 

3633 # would like to return a lex basis but since we can't use fglm we 

3634 # compute the lex basis directly here. The time required to recompute 

3635 # the basis is generally significantly less than the time required by 

3636 # substitution to solve the new system. 

3637 poly_sol = no_information 

3638 poly_eqs = list(groebner(polys, symbols, order='lex', polys=False)) 

3639 

3640 if inexact: 

3641 poly_eqs = [nfloat(p) for p in poly_eqs] 

3642 

3643 return poly_sol, poly_eqs 

3644 

3645 

3646def nonlinsolve(system, *symbols): 

3647 r""" 

3648 Solve system of $N$ nonlinear equations with $M$ variables, which means both 

3649 under and overdetermined systems are supported. Positive dimensional 

3650 system is also supported (A system with infinitely many solutions is said 

3651 to be positive-dimensional). In a positive dimensional system the solution will 

3652 be dependent on at least one symbol. Returns both real solution 

3653 and complex solution (if they exist). 

3654 

3655 Parameters 

3656 ========== 

3657 

3658 system : list of equations 

3659 The target system of equations 

3660 symbols : list of Symbols 

3661 symbols should be given as a sequence eg. list 

3662 

3663 Returns 

3664 ======= 

3665 

3666 A :class:`~.FiniteSet` of ordered tuple of values of `symbols` for which the `system` 

3667 has solution. Order of values in the tuple is same as symbols present in 

3668 the parameter `symbols`. 

3669 

3670 Please note that general :class:`~.FiniteSet` is unordered, the solution 

3671 returned here is not simply a :class:`~.FiniteSet` of solutions, rather it 

3672 is a :class:`~.FiniteSet` of ordered tuple, i.e. the first and only 

3673 argument to :class:`~.FiniteSet` is a tuple of solutions, which is 

3674 ordered, and, hence ,the returned solution is ordered. 

3675 

3676 Also note that solution could also have been returned as an ordered tuple, 

3677 FiniteSet is just a wrapper ``{}`` around the tuple. It has no other 

3678 significance except for the fact it is just used to maintain a consistent 

3679 output format throughout the solveset. 

3680 

3681 For the given set of equations, the respective input types 

3682 are given below: 

3683 

3684 .. math:: xy - 1 = 0 

3685 .. math:: 4x^2 + y^2 - 5 = 0 

3686 

3687 :: 

3688 

3689 system = [x*y - 1, 4*x**2 + y**2 - 5] 

3690 symbols = [x, y] 

3691 

3692 Raises 

3693 ====== 

3694 

3695 ValueError 

3696 The input is not valid. 

3697 The symbols are not given. 

3698 AttributeError 

3699 The input symbols are not `Symbol` type. 

3700 

3701 Examples 

3702 ======== 

3703 

3704 >>> from sympy import symbols, nonlinsolve 

3705 >>> x, y, z = symbols('x, y, z', real=True) 

3706 >>> nonlinsolve([x*y - 1, 4*x**2 + y**2 - 5], [x, y]) 

3707 {(-1, -1), (-1/2, -2), (1/2, 2), (1, 1)} 

3708 

3709 1. Positive dimensional system and complements: 

3710 

3711 >>> from sympy import pprint 

3712 >>> from sympy.polys.polytools import is_zero_dimensional 

3713 >>> a, b, c, d = symbols('a, b, c, d', extended_real=True) 

3714 >>> eq1 = a + b + c + d 

3715 >>> eq2 = a*b + b*c + c*d + d*a 

3716 >>> eq3 = a*b*c + b*c*d + c*d*a + d*a*b 

3717 >>> eq4 = a*b*c*d - 1 

3718 >>> system = [eq1, eq2, eq3, eq4] 

3719 >>> is_zero_dimensional(system) 

3720 False 

3721 >>> pprint(nonlinsolve(system, [a, b, c, d]), use_unicode=False) 

3722 -1 1 1 -1 

3723 {(---, -d, -, {d} \ {0}), (-, -d, ---, {d} \ {0})} 

3724 d d d d 

3725 >>> nonlinsolve([(x+y)**2 - 4, x + y - 2], [x, y]) 

3726 {(2 - y, y)} 

3727 

3728 2. If some of the equations are non-polynomial then `nonlinsolve` 

3729 will call the ``substitution`` function and return real and complex solutions, 

3730 if present. 

3731 

3732 >>> from sympy import exp, sin 

3733 >>> nonlinsolve([exp(x) - sin(y), y**2 - 4], [x, y]) 

3734 {(ImageSet(Lambda(_n, I*(2*_n*pi + pi) + log(sin(2))), Integers), -2), 

3735 (ImageSet(Lambda(_n, 2*_n*I*pi + log(sin(2))), Integers), 2)} 

3736 

3737 3. If system is non-linear polynomial and zero-dimensional then it 

3738 returns both solution (real and complex solutions, if present) using 

3739 :func:`~.solve_poly_system`: 

3740 

3741 >>> from sympy import sqrt 

3742 >>> nonlinsolve([x**2 - 2*y**2 -2, x*y - 2], [x, y]) 

3743 {(-2, -1), (2, 1), (-sqrt(2)*I, sqrt(2)*I), (sqrt(2)*I, -sqrt(2)*I)} 

3744 

3745 4. ``nonlinsolve`` can solve some linear (zero or positive dimensional) 

3746 system (because it uses the :func:`sympy.polys.polytools.groebner` function to get the 

3747 groebner basis and then uses the ``substitution`` function basis as the 

3748 new `system`). But it is not recommended to solve linear system using 

3749 ``nonlinsolve``, because :func:`~.linsolve` is better for general linear systems. 

3750 

3751 >>> nonlinsolve([x + 2*y -z - 3, x - y - 4*z + 9, y + z - 4], [x, y, z]) 

3752 {(3*z - 5, 4 - z, z)} 

3753 

3754 5. System having polynomial equations and only real solution is 

3755 solved using :func:`~.solve_poly_system`: 

3756 

3757 >>> e1 = sqrt(x**2 + y**2) - 10 

3758 >>> e2 = sqrt(y**2 + (-x + 10)**2) - 3 

3759 >>> nonlinsolve((e1, e2), (x, y)) 

3760 {(191/20, -3*sqrt(391)/20), (191/20, 3*sqrt(391)/20)} 

3761 >>> nonlinsolve([x**2 + 2/y - 2, x + y - 3], [x, y]) 

3762 {(1, 2), (1 - sqrt(5), 2 + sqrt(5)), (1 + sqrt(5), 2 - sqrt(5))} 

3763 >>> nonlinsolve([x**2 + 2/y - 2, x + y - 3], [y, x]) 

3764 {(2, 1), (2 - sqrt(5), 1 + sqrt(5)), (2 + sqrt(5), 1 - sqrt(5))} 

3765 

3766 6. It is better to use symbols instead of trigonometric functions or 

3767 :class:`~.Function`. For example, replace $\sin(x)$ with a symbol, replace 

3768 $f(x)$ with a symbol and so on. Get a solution from ``nonlinsolve`` and then 

3769 use :func:`~.solveset` to get the value of $x$. 

3770 

3771 How nonlinsolve is better than old solver ``_solve_system`` : 

3772 ============================================================= 

3773 

3774 1. A positive dimensional system solver: nonlinsolve can return 

3775 solution for positive dimensional system. It finds the 

3776 Groebner Basis of the positive dimensional system(calling it as 

3777 basis) then we can start solving equation(having least number of 

3778 variable first in the basis) using solveset and substituting that 

3779 solved solutions into other equation(of basis) to get solution in 

3780 terms of minimum variables. Here the important thing is how we 

3781 are substituting the known values and in which equations. 

3782 

3783 2. Real and complex solutions: nonlinsolve returns both real 

3784 and complex solution. If all the equations in the system are polynomial 

3785 then using :func:`~.solve_poly_system` both real and complex solution is returned. 

3786 If all the equations in the system are not polynomial equation then goes to 

3787 ``substitution`` method with this polynomial and non polynomial equation(s), 

3788 to solve for unsolved variables. Here to solve for particular variable 

3789 solveset_real and solveset_complex is used. For both real and complex 

3790 solution ``_solve_using_known_values`` is used inside ``substitution`` 

3791 (``substitution`` will be called when any non-polynomial equation is present). 

3792 If a solution is valid its general solution is added to the final result. 

3793 

3794 3. :class:`~.Complement` and :class:`~.Intersection` will be added: 

3795 nonlinsolve maintains dict for complements and intersections. If solveset 

3796 find complements or/and intersections with any interval or set during the 

3797 execution of ``substitution`` function, then complement or/and 

3798 intersection for that variable is added before returning final solution. 

3799 

3800 """ 

3801 if not system: 

3802 return S.EmptySet 

3803 

3804 if not symbols: 

3805 msg = ('Symbols must be given, for which solution of the ' 

3806 'system is to be found.') 

3807 raise ValueError(filldedent(msg)) 

3808 

3809 if hasattr(symbols[0], '__iter__'): 

3810 symbols = symbols[0] 

3811 

3812 if not is_sequence(symbols) or not symbols: 

3813 msg = ('Symbols must be given, for which solution of the ' 

3814 'system is to be found.') 

3815 raise IndexError(filldedent(msg)) 

3816 

3817 symbols = list(map(_sympify, symbols)) 

3818 system, symbols, swap = recast_to_symbols(system, symbols) 

3819 if swap: 

3820 soln = nonlinsolve(system, symbols) 

3821 return FiniteSet(*[tuple(i.xreplace(swap) for i in s) for s in soln]) 

3822 

3823 if len(system) == 1 and len(symbols) == 1: 

3824 return _solveset_work(system, symbols) 

3825 

3826 # main code of def nonlinsolve() starts from here 

3827 

3828 polys, polys_expr, nonpolys, denominators, unrad_changed = \ 

3829 _separate_poly_nonpoly(system, symbols) 

3830 

3831 poly_eqs = [] 

3832 poly_sol = [{}] 

3833 

3834 if polys: 

3835 poly_sol, poly_eqs = _handle_poly(polys, symbols) 

3836 if poly_sol and poly_sol[0]: 

3837 poly_syms = set().union(*(eq.free_symbols for eq in polys)) 

3838 unrad_syms = set().union(*(eq.free_symbols for eq in unrad_changed)) 

3839 if unrad_syms == poly_syms and unrad_changed: 

3840 # if all the symbols have been solved by _handle_poly 

3841 # and unrad has been used then check solutions 

3842 poly_sol = [sol for sol in poly_sol if checksol(unrad_changed, sol)] 

3843 

3844 # Collect together the unsolved polynomials with the non-polynomial 

3845 # equations. 

3846 remaining = poly_eqs + nonpolys 

3847 

3848 # to_tuple converts a solution dictionary to a tuple containing the 

3849 # value for each symbol 

3850 to_tuple = lambda sol: tuple(sol[s] for s in symbols) 

3851 

3852 if not remaining: 

3853 # If there is nothing left to solve then return the solution from 

3854 # solve_poly_system directly. 

3855 return FiniteSet(*map(to_tuple, poly_sol)) 

3856 else: 

3857 # Here we handle: 

3858 # 

3859 # 1. The Groebner basis if solve_poly_system failed. 

3860 # 2. The Groebner basis in the positive-dimensional case. 

3861 # 3. Any non-polynomial equations 

3862 # 

3863 # If solve_poly_system did succeed then we pass those solutions in as 

3864 # preliminary results. 

3865 subs_res = substitution(remaining, symbols, result=poly_sol, exclude=denominators) 

3866 

3867 if not isinstance(subs_res, FiniteSet): 

3868 return subs_res 

3869 

3870 # check solutions produced by substitution. Currently, checking is done for 

3871 # only those solutions which have non-Set variable values. 

3872 if unrad_changed: 

3873 result = [dict(zip(symbols, sol)) for sol in subs_res.args] 

3874 correct_sols = [sol for sol in result if any(isinstance(v, Set) for v in sol) 

3875 or checksol(unrad_changed, sol) != False] 

3876 return FiniteSet(*map(to_tuple, correct_sols)) 

3877 else: 

3878 return subs_res