Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

771

772

773

774

775

776

777

778

779

780

781

782

783

784

785

786

787

788

789

790

791

792

793

794

795

796

797

798

799

800

801

802

803

804

805

806

807

808

809

810

811

812

813

814

815

816

817

818

819

820

821

822

823

824

825

826

827

828

829

830

831

832

833

834

835

836

837

838

839

840

841

842

843

844

845

846

847

848

849

850

851

852

853

854

855

856

857

858

859

860

861

862

863

864

865

866

867

868

869

870

871

872

873

874

875

876

877

878

879

880

881

882

883

884

885

886

887

888

889

890

891

892

893

894

895

896

897

898

899

900

901

902

903

904

905

906

907

908

909

910

911

912

913

914

915

916

917

918

919

920

921

922

923

924

925

926

927

928

929

930

931

932

933

934

935

936

937

938

939

940

941

942

943

944

945

946

947

948

949

950

951

952

953

954

955

956

957

958

959

960

961

962

963

964

965

966

967

968

969

970

971

972

973

974

975

976

977

978

979

980

981

982

983

984

985

986

987

988

989

990

991

992

993

994

995

996

997

998

999

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

1012

1013

1014

1015

1016

1017

1018

1019

1020

1021

1022

1023

1024

1025

1026

1027

1028

1029

1030

1031

1032

1033

1034

1035

1036

1037

1038

1039

1040

1041

1042

1043

1044

1045

1046

1047

1048

1049

1050

1051

1052

1053

1054

1055

1056

1057

1058

1059

1060

1061

1062

1063

1064

1065

1066

1067

1068

1069

1070

1071

1072

1073

1074

1075

1076

1077

1078

1079

1080

1081

1082

1083

1084

1085

1086

1087

1088

1089

1090

1091

1092

1093

1094

1095

1096

1097

1098

1099

1100

1101

1102

1103

1104

1105

1106

1107

1108

1109

1110

1111

1112

1113

1114

1115

1116

1117

1118

1119

1120

1121

1122

1123

1124

1125

1126

1127

1128

1129

1130

1131

1132

1133

1134

1135

1136

1137

1138

1139

1140

1141

1142

1143

1144

1145

1146

1147

1148

1149

1150

1151

1152

1153

1154

1155

1156

1157

1158

1159

1160

1161

1162

1163

1164

1165

1166

1167

1168

1169

1170

1171

1172

1173

1174

1175

1176

1177

1178

1179

1180

1181

1182

1183

1184

1185

1186

1187

1188

1189

1190

1191

1192

1193

1194

1195

1196

1197

1198

1199

1200

1201

1202

1203

1204

1205

1206

1207

1208

1209

1210

1211

1212

1213

1214

1215

1216

1217

1218

1219

1220

1221

1222

1223

1224

1225

1226

1227

1228

1229

1230

1231

1232

1233

1234

1235

1236

1237

1238

1239

1240

1241

1242

1243

1244

1245

1246

1247

1248

1249

1250

1251

1252

1253

1254

1255

1256

1257

1258

1259

1260

1261

1262

1263

1264

1265

1266

1267

1268

1269

1270

1271

1272

1273

1274

1275

1276

1277

1278

1279

1280

1281

1282

1283

1284

1285

1286

1287

1288

1289

1290

1291

1292

1293

1294

1295

1296

1297

1298

1299

1300

1301

1302

1303

1304

1305

1306

from __future__ import print_function, division 

 

from collections import defaultdict 

 

from sympy.core import (Basic, S, Add, Mul, Pow, 

Symbol, sympify, expand_mul, expand_func, 

Function, Dummy, Expr, factor_terms, 

symbols, expand_power_exp) 

from sympy.core.compatibility import (iterable, 

ordered, range, as_int) 

from sympy.core.numbers import Float, I, pi, Rational, Integer 

from sympy.core.function import expand_log, count_ops, _mexpand, _coeff_isneg 

from sympy.core.rules import Transform 

from sympy.core.evaluate import global_evaluate 

from sympy.functions import ( 

gamma, exp, sqrt, log, exp_polar, piecewise_fold) 

from sympy.functions.elementary.exponential import ExpBase 

from sympy.functions.elementary.hyperbolic import HyperbolicFunction 

from sympy.functions.elementary.integers import ceiling 

from sympy.functions.elementary.complexes import unpolarify 

from sympy.functions.elementary.trigonometric import TrigonometricFunction 

from sympy.functions.combinatorial.factorials import CombinatorialFunction 

from sympy.functions.special.bessel import besselj, besseli, besselk, jn, bessely 

 

from sympy.utilities.iterables import has_variety 

 

from sympy.simplify.radsimp import radsimp, fraction 

from sympy.simplify.trigsimp import trigsimp, exptrigsimp 

from sympy.simplify.powsimp import powsimp 

from sympy.simplify.cse_opts import sub_pre, sub_post 

from sympy.simplify.sqrtdenest import sqrtdenest 

from sympy.simplify.combsimp import combsimp 

 

from sympy.polys import (together, cancel, factor) 

 

 

import mpmath 

 

 

 

def separatevars(expr, symbols=[], dict=False, force=False): 

""" 

Separates variables in an expression, if possible. By 

default, it separates with respect to all symbols in an 

expression and collects constant coefficients that are 

independent of symbols. 

 

If dict=True then the separated terms will be returned 

in a dictionary keyed to their corresponding symbols. 

By default, all symbols in the expression will appear as 

keys; if symbols are provided, then all those symbols will 

be used as keys, and any terms in the expression containing 

other symbols or non-symbols will be returned keyed to the 

string 'coeff'. (Passing None for symbols will return the 

expression in a dictionary keyed to 'coeff'.) 

 

If force=True, then bases of powers will be separated regardless 

of assumptions on the symbols involved. 

 

Notes 

===== 

The order of the factors is determined by Mul, so that the 

separated expressions may not necessarily be grouped together. 

 

Although factoring is necessary to separate variables in some 

expressions, it is not necessary in all cases, so one should not 

count on the returned factors being factored. 

 

Examples 

======== 

 

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

>>> from sympy import separatevars, sin 

>>> separatevars((x*y)**y) 

(x*y)**y 

>>> separatevars((x*y)**y, force=True) 

x**y*y**y 

 

>>> e = 2*x**2*z*sin(y)+2*z*x**2 

>>> separatevars(e) 

2*x**2*z*(sin(y) + 1) 

>>> separatevars(e, symbols=(x, y), dict=True) 

{'coeff': 2*z, x: x**2, y: sin(y) + 1} 

>>> separatevars(e, [x, y, alpha], dict=True) 

{'coeff': 2*z, alpha: 1, x: x**2, y: sin(y) + 1} 

 

If the expression is not really separable, or is only partially 

separable, separatevars will do the best it can to separate it 

by using factoring. 

 

>>> separatevars(x + x*y - 3*x**2) 

-x*(3*x - y - 1) 

 

If the expression is not separable then expr is returned unchanged 

or (if dict=True) then None is returned. 

 

>>> eq = 2*x + y*sin(x) 

>>> separatevars(eq) == eq 

True 

>>> separatevars(2*x + y*sin(x), symbols=(x, y), dict=True) == None 

True 

 

""" 

expr = sympify(expr) 

if dict: 

return _separatevars_dict(_separatevars(expr, force), symbols) 

else: 

return _separatevars(expr, force) 

 

 

def _separatevars(expr, force): 

if len(expr.free_symbols) == 1: 

return expr 

# don't destroy a Mul since much of the work may already be done 

if expr.is_Mul: 

args = list(expr.args) 

changed = False 

for i, a in enumerate(args): 

args[i] = separatevars(a, force) 

changed = changed or args[i] != a 

if changed: 

expr = expr.func(*args) 

return expr 

 

# get a Pow ready for expansion 

if expr.is_Pow: 

expr = Pow(separatevars(expr.base, force=force), expr.exp) 

 

# First try other expansion methods 

expr = expr.expand(mul=False, multinomial=False, force=force) 

 

_expr, reps = posify(expr) if force else (expr, {}) 

expr = factor(_expr).subs(reps) 

 

if not expr.is_Add: 

return expr 

 

# Find any common coefficients to pull out 

args = list(expr.args) 

commonc = args[0].args_cnc(cset=True, warn=False)[0] 

for i in args[1:]: 

commonc &= i.args_cnc(cset=True, warn=False)[0] 

commonc = Mul(*commonc) 

commonc = commonc.as_coeff_Mul()[1] # ignore constants 

commonc_set = commonc.args_cnc(cset=True, warn=False)[0] 

 

# remove them 

for i, a in enumerate(args): 

c, nc = a.args_cnc(cset=True, warn=False) 

c = c - commonc_set 

args[i] = Mul(*c)*Mul(*nc) 

nonsepar = Add(*args) 

 

if len(nonsepar.free_symbols) > 1: 

_expr = nonsepar 

_expr, reps = posify(_expr) if force else (_expr, {}) 

_expr = (factor(_expr)).subs(reps) 

 

if not _expr.is_Add: 

nonsepar = _expr 

 

return commonc*nonsepar 

 

 

def _separatevars_dict(expr, symbols): 

if symbols: 

if not all((t.is_Atom for t in symbols)): 

raise ValueError("symbols must be Atoms.") 

symbols = list(symbols) 

elif symbols is None: 

return {'coeff': expr} 

else: 

symbols = list(expr.free_symbols) 

if not symbols: 

return None 

 

ret = dict(((i, []) for i in symbols + ['coeff'])) 

 

for i in Mul.make_args(expr): 

expsym = i.free_symbols 

intersection = set(symbols).intersection(expsym) 

if len(intersection) > 1: 

return None 

if len(intersection) == 0: 

# There are no symbols, so it is part of the coefficient 

ret['coeff'].append(i) 

else: 

ret[intersection.pop()].append(i) 

 

# rebuild 

for k, v in ret.items(): 

ret[k] = Mul(*v) 

 

return ret 

 

 

def _is_sum_surds(p): 

args = p.args if p.is_Add else [p] 

for y in args: 

if not ((y**2).is_Rational and y.is_real): 

return False 

return True 

 

 

def posify(eq): 

"""Return eq (with generic symbols made positive) and a 

dictionary containing the mapping between the old and new 

symbols. 

 

Any symbol that has positive=None will be replaced with a positive dummy 

symbol having the same name. This replacement will allow more symbolic 

processing of expressions, especially those involving powers and 

logarithms. 

 

A dictionary that can be sent to subs to restore eq to its original 

symbols is also returned. 

 

>>> from sympy import posify, Symbol, log, solve 

>>> from sympy.abc import x 

>>> posify(x + Symbol('p', positive=True) + Symbol('n', negative=True)) 

(_x + n + p, {_x: x}) 

 

>>> eq = 1/x 

>>> log(eq).expand() 

log(1/x) 

>>> log(posify(eq)[0]).expand() 

-log(_x) 

>>> p, rep = posify(eq) 

>>> log(p).expand().subs(rep) 

-log(x) 

 

It is possible to apply the same transformations to an iterable 

of expressions: 

 

>>> eq = x**2 - 4 

>>> solve(eq, x) 

[-2, 2] 

>>> eq_x, reps = posify([eq, x]); eq_x 

[_x**2 - 4, _x] 

>>> solve(*eq_x) 

[2] 

""" 

eq = sympify(eq) 

if iterable(eq): 

f = type(eq) 

eq = list(eq) 

syms = set() 

for e in eq: 

syms = syms.union(e.atoms(Symbol)) 

reps = {} 

for s in syms: 

reps.update(dict((v, k) for k, v in posify(s)[1].items())) 

for i, e in enumerate(eq): 

eq[i] = e.subs(reps) 

return f(eq), {r: s for s, r in reps.items()} 

 

reps = dict([(s, Dummy(s.name, positive=True)) 

for s in eq.free_symbols if s.is_positive is None]) 

eq = eq.subs(reps) 

return eq, {r: s for s, r in reps.items()} 

 

 

def hypersimp(f, k): 

"""Given combinatorial term f(k) simplify its consecutive term ratio 

i.e. f(k+1)/f(k). The input term can be composed of functions and 

integer sequences which have equivalent representation in terms 

of gamma special function. 

 

The algorithm performs three basic steps: 

 

1. Rewrite all functions in terms of gamma, if possible. 

 

2. Rewrite all occurrences of gamma in terms of products 

of gamma and rising factorial with integer, absolute 

constant exponent. 

 

3. Perform simplification of nested fractions, powers 

and if the resulting expression is a quotient of 

polynomials, reduce their total degree. 

 

If f(k) is hypergeometric then as result we arrive with a 

quotient of polynomials of minimal degree. Otherwise None 

is returned. 

 

For more information on the implemented algorithm refer to: 

 

1. W. Koepf, Algorithms for m-fold Hypergeometric Summation, 

Journal of Symbolic Computation (1995) 20, 399-417 

""" 

f = sympify(f) 

 

g = f.subs(k, k + 1) / f 

 

g = g.rewrite(gamma) 

g = expand_func(g) 

g = powsimp(g, deep=True, combine='exp') 

 

if g.is_rational_function(k): 

return simplify(g, ratio=S.Infinity) 

else: 

return None 

 

 

def hypersimilar(f, g, k): 

"""Returns True if 'f' and 'g' are hyper-similar. 

 

Similarity in hypergeometric sense means that a quotient of 

f(k) and g(k) is a rational function in k. This procedure 

is useful in solving recurrence relations. 

 

For more information see hypersimp(). 

 

""" 

f, g = list(map(sympify, (f, g))) 

 

h = (f/g).rewrite(gamma) 

h = h.expand(func=True, basic=False) 

 

return h.is_rational_function(k) 

 

 

def signsimp(expr, evaluate=None): 

"""Make all Add sub-expressions canonical wrt sign. 

 

If an Add subexpression, ``a``, can have a sign extracted, 

as determined by could_extract_minus_sign, it is replaced 

with Mul(-1, a, evaluate=False). This allows signs to be 

extracted from powers and products. 

 

Examples 

======== 

 

>>> from sympy import signsimp, exp, symbols 

>>> from sympy.abc import x, y 

>>> i = symbols('i', odd=True) 

>>> n = -1 + 1/x 

>>> n/x/(-n)**2 - 1/n/x 

(-1 + 1/x)/(x*(1 - 1/x)**2) - 1/(x*(-1 + 1/x)) 

>>> signsimp(_) 

0 

>>> x*n + x*-n 

x*(-1 + 1/x) + x*(1 - 1/x) 

>>> signsimp(_) 

0 

 

Since powers automatically handle leading signs 

 

>>> (-2)**i 

-2**i 

 

signsimp can be used to put the base of a power with an integer 

exponent into canonical form: 

 

>>> n**i 

(-1 + 1/x)**i 

 

By default, signsimp doesn't leave behind any hollow simplification: 

if making an Add canonical wrt sign didn't change the expression, the 

original Add is restored. If this is not desired then the keyword 

``evaluate`` can be set to False: 

 

>>> e = exp(y - x) 

>>> signsimp(e) == e 

True 

>>> signsimp(e, evaluate=False) 

exp(-(x - y)) 

 

""" 

if evaluate is None: 

evaluate = global_evaluate[0] 

expr = sympify(expr) 

if not isinstance(expr, Expr) or expr.is_Atom: 

return expr 

e = sub_post(sub_pre(expr)) 

if not isinstance(e, Expr) or e.is_Atom: 

return e 

if e.is_Add: 

return e.func(*[signsimp(a) for a in e.args]) 

if evaluate: 

e = e.xreplace({m: -(-m) for m in e.atoms(Mul) if -(-m) != m}) 

return e 

 

 

def simplify(expr, ratio=1.7, measure=count_ops, fu=False): 

""" 

Simplifies the given expression. 

 

Simplification is not a well defined term and the exact strategies 

this function tries can change in the future versions of SymPy. If 

your algorithm relies on "simplification" (whatever it is), try to 

determine what you need exactly - is it powsimp()?, radsimp()?, 

together()?, logcombine()?, or something else? And use this particular 

function directly, because those are well defined and thus your algorithm 

will be robust. 

 

Nonetheless, especially for interactive use, or when you don't know 

anything about the structure of the expression, simplify() tries to apply 

intelligent heuristics to make the input expression "simpler". For 

example: 

 

>>> from sympy import simplify, cos, sin 

>>> from sympy.abc import x, y 

>>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2) 

>>> a 

(x**2 + x)/(x*sin(y)**2 + x*cos(y)**2) 

>>> simplify(a) 

x + 1 

 

Note that we could have obtained the same result by using specific 

simplification functions: 

 

>>> from sympy import trigsimp, cancel 

>>> trigsimp(a) 

(x**2 + x)/x 

>>> cancel(_) 

x + 1 

 

In some cases, applying :func:`simplify` may actually result in some more 

complicated expression. The default ``ratio=1.7`` prevents more extreme 

cases: if (result length)/(input length) > ratio, then input is returned 

unmodified. The ``measure`` parameter lets you specify the function used 

to determine how complex an expression is. The function should take a 

single argument as an expression and return a number such that if 

expression ``a`` is more complex than expression ``b``, then 

``measure(a) > measure(b)``. The default measure function is 

:func:`count_ops`, which returns the total number of operations in the 

expression. 

 

For example, if ``ratio=1``, ``simplify`` output can't be longer 

than input. 

 

:: 

 

>>> from sympy import sqrt, simplify, count_ops, oo 

>>> root = 1/(sqrt(2)+3) 

 

Since ``simplify(root)`` would result in a slightly longer expression, 

root is returned unchanged instead:: 

 

>>> simplify(root, ratio=1) == root 

True 

 

If ``ratio=oo``, simplify will be applied anyway:: 

 

>>> count_ops(simplify(root, ratio=oo)) > count_ops(root) 

True 

 

Note that the shortest expression is not necessary the simplest, so 

setting ``ratio`` to 1 may not be a good idea. 

Heuristically, the default value ``ratio=1.7`` seems like a reasonable 

choice. 

 

You can easily define your own measure function based on what you feel 

should represent the "size" or "complexity" of the input expression. Note 

that some choices, such as ``lambda expr: len(str(expr))`` may appear to be 

good metrics, but have other problems (in this case, the measure function 

may slow down simplify too much for very large expressions). If you don't 

know what a good metric would be, the default, ``count_ops``, is a good 

one. 

 

For example: 

 

>>> from sympy import symbols, log 

>>> a, b = symbols('a b', positive=True) 

>>> g = log(a) + log(b) + log(a)*log(1/b) 

>>> h = simplify(g) 

>>> h 

log(a*b**(-log(a) + 1)) 

>>> count_ops(g) 

8 

>>> count_ops(h) 

5 

 

So you can see that ``h`` is simpler than ``g`` using the count_ops metric. 

However, we may not like how ``simplify`` (in this case, using 

``logcombine``) has created the ``b**(log(1/a) + 1)`` term. A simple way 

to reduce this would be to give more weight to powers as operations in 

``count_ops``. We can do this by using the ``visual=True`` option: 

 

>>> print(count_ops(g, visual=True)) 

2*ADD + DIV + 4*LOG + MUL 

>>> print(count_ops(h, visual=True)) 

2*LOG + MUL + POW + SUB 

 

>>> from sympy import Symbol, S 

>>> def my_measure(expr): 

... POW = Symbol('POW') 

... # Discourage powers by giving POW a weight of 10 

... count = count_ops(expr, visual=True).subs(POW, 10) 

... # Every other operation gets a weight of 1 (the default) 

... count = count.replace(Symbol, type(S.One)) 

... return count 

>>> my_measure(g) 

8 

>>> my_measure(h) 

14 

>>> 15./8 > 1.7 # 1.7 is the default ratio 

True 

>>> simplify(g, measure=my_measure) 

-log(a)*log(b) + log(a) + log(b) 

 

Note that because ``simplify()`` internally tries many different 

simplification strategies and then compares them using the measure 

function, we get a completely different result that is still different 

from the input expression by doing this. 

""" 

expr = sympify(expr) 

 

try: 

return expr._eval_simplify(ratio=ratio, measure=measure) 

except AttributeError: 

pass 

 

original_expr = expr = signsimp(expr) 

 

from sympy.simplify.hyperexpand import hyperexpand 

from sympy.functions.special.bessel import BesselBase 

from sympy import Sum, Product 

 

if not isinstance(expr, Basic) or not expr.args: # XXX: temporary hack 

return expr 

 

if not isinstance(expr, (Add, Mul, Pow, ExpBase)): 

if isinstance(expr, Function) and hasattr(expr, "inverse"): 

if len(expr.args) == 1 and len(expr.args[0].args) == 1 and \ 

isinstance(expr.args[0], expr.inverse(argindex=1)): 

return simplify(expr.args[0].args[0], ratio=ratio, 

measure=measure, fu=fu) 

return expr.func(*[simplify(x, ratio=ratio, measure=measure, fu=fu) 

for x in expr.args]) 

 

# TODO: Apply different strategies, considering expression pattern: 

# is it a purely rational function? Is there any trigonometric function?... 

# See also https://github.com/sympy/sympy/pull/185. 

 

def shorter(*choices): 

'''Return the choice that has the fewest ops. In case of a tie, 

the expression listed first is selected.''' 

if not has_variety(choices): 

return choices[0] 

return min(choices, key=measure) 

 

expr = bottom_up(expr, lambda w: w.normal()) 

expr = Mul(*powsimp(expr).as_content_primitive()) 

_e = cancel(expr) 

expr1 = shorter(_e, _mexpand(_e).cancel()) # issue 6829 

expr2 = shorter(together(expr, deep=True), together(expr1, deep=True)) 

 

if ratio is S.Infinity: 

expr = expr2 

else: 

expr = shorter(expr2, expr1, expr) 

if not isinstance(expr, Basic): # XXX: temporary hack 

return expr 

 

expr = factor_terms(expr, sign=False) 

 

# hyperexpand automatically only works on hypergeometric terms 

expr = hyperexpand(expr) 

 

expr = piecewise_fold(expr) 

 

if expr.has(BesselBase): 

expr = besselsimp(expr) 

 

if expr.has(TrigonometricFunction) and not fu or expr.has( 

HyperbolicFunction): 

expr = trigsimp(expr, deep=True) 

 

if expr.has(log): 

expr = shorter(expand_log(expr, deep=True), logcombine(expr)) 

 

if expr.has(CombinatorialFunction, gamma): 

expr = combsimp(expr) 

 

if expr.has(Sum): 

expr = sum_simplify(expr) 

 

if expr.has(Product): 

expr = product_simplify(expr) 

 

short = shorter(powsimp(expr, combine='exp', deep=True), powsimp(expr), expr) 

short = shorter(short, factor_terms(short), expand_power_exp(expand_mul(short))) 

if short.has(TrigonometricFunction, HyperbolicFunction, ExpBase): 

short = exptrigsimp(short, simplify=False) 

 

# get rid of hollow 2-arg Mul factorization 

hollow_mul = Transform( 

lambda x: Mul(*x.args), 

lambda x: 

x.is_Mul and 

len(x.args) == 2 and 

x.args[0].is_Number and 

x.args[1].is_Add and 

x.is_commutative) 

expr = short.xreplace(hollow_mul) 

 

numer, denom = expr.as_numer_denom() 

if denom.is_Add: 

n, d = fraction(radsimp(1/denom, symbolic=False, max_terms=1)) 

if n is not S.One: 

expr = (numer*n).expand()/d 

 

if expr.could_extract_minus_sign(): 

n, d = fraction(expr) 

if d != 0: 

expr = signsimp(-n/(-d)) 

 

if measure(expr) > ratio*measure(original_expr): 

expr = original_expr 

 

return expr 

 

 

def sum_simplify(s): 

"""Main function for Sum simplification""" 

from sympy.concrete.summations import Sum 

 

terms = Add.make_args(s) 

s_t = [] # Sum Terms 

o_t = [] # Other Terms 

 

for term in terms: 

if isinstance(term, Mul): 

constant = 1 

other = 1 

s = 0 

n_sum_terms = 0 

for j in range(len(term.args)): 

if isinstance(term.args[j], Sum): 

s = term.args[j] 

n_sum_terms = n_sum_terms + 1 

elif term.args[j].is_number == True: 

constant = constant * term.args[j] 

else: 

other = other * term.args[j] 

if other == 1 and n_sum_terms == 1: 

# Insert the constant inside the Sum 

s_t.append(Sum(constant * s.function, *s.limits)) 

elif other != 1 and n_sum_terms == 1: 

o_t.append(other * Sum(constant * s.function, *s.limits)) 

else: 

o_t.append(term) 

elif isinstance(term, Sum): 

s_t.append(term) 

else: 

o_t.append(term) 

 

used = [False] * len(s_t) 

 

for method in range(2): 

for i, s_term1 in enumerate(s_t): 

if not used[i]: 

for j, s_term2 in enumerate(s_t): 

if not used[j] and i != j: 

temp = sum_add(s_term1, s_term2, method) 

if isinstance(temp, Sum): 

s_t[i] = temp 

s_term1 = s_t[i] 

used[j] = True 

 

result = Add(*o_t) 

 

for i, s_term in enumerate(s_t): 

if not used[i]: 

result = Add(result, s_term) 

 

return result 

 

 

def sum_add(self, other, method=0): 

"""Helper function for Sum simplification""" 

from sympy.concrete.summations import Sum 

 

if type(self) == type(other): 

if method == 0: 

if self.limits == other.limits: 

return Sum(self.function + other.function, *self.limits) 

elif method == 1: 

if simplify(self.function - other.function) == 0: 

if len(self.limits) == len(other.limits) == 1: 

i = self.limits[0][0] 

x1 = self.limits[0][1] 

y1 = self.limits[0][2] 

j = other.limits[0][0] 

x2 = other.limits[0][1] 

y2 = other.limits[0][2] 

 

if i == j: 

if x2 == y1 + 1: 

return Sum(self.function, (i, x1, y2)) 

elif x1 == y2 + 1: 

return Sum(self.function, (i, x2, y1)) 

 

return Add(self, other) 

 

 

def product_simplify(s): 

"""Main function for Product simplification""" 

from sympy.concrete.products import Product 

 

terms = Mul.make_args(s) 

p_t = [] # Product Terms 

o_t = [] # Other Terms 

 

for term in terms: 

if isinstance(term, Product): 

p_t.append(term) 

else: 

o_t.append(term) 

 

used = [False] * len(p_t) 

 

for method in range(2): 

for i, p_term1 in enumerate(p_t): 

if not used[i]: 

for j, p_term2 in enumerate(p_t): 

if not used[j] and i != j: 

if isinstance(product_mul(p_term1, p_term2, method), Product): 

p_t[i] = product_mul(p_term1, p_term2, method) 

used[j] = True 

 

result = Mul(*o_t) 

 

for i, p_term in enumerate(p_t): 

if not used[i]: 

result = Mul(result, p_term) 

 

return result 

 

 

def product_mul(self, other, method=0): 

"""Helper function for Product simplification""" 

from sympy.concrete.products import Product 

 

if type(self) == type(other): 

if method == 0: 

if self.limits == other.limits: 

return Product(self.function * other.function, *self.limits) 

elif method == 1: 

if simplify(self.function - other.function) == 0: 

if len(self.limits) == len(other.limits) == 1: 

i = self.limits[0][0] 

x1 = self.limits[0][1] 

y1 = self.limits[0][2] 

j = other.limits[0][0] 

x2 = other.limits[0][1] 

y2 = other.limits[0][2] 

 

if i == j: 

if x2 == y1 + 1: 

return Product(self.function, (i, x1, y2)) 

elif x1 == y2 + 1: 

return Product(self.function, (i, x2, y1)) 

 

return Mul(self, other) 

 

 

def _nthroot_solve(p, n, prec): 

""" 

helper function for ``nthroot`` 

It denests ``p**Rational(1, n)`` using its minimal polynomial 

""" 

from sympy.polys.numberfields import _minimal_polynomial_sq 

from sympy.solvers import solve 

while n % 2 == 0: 

p = sqrtdenest(sqrt(p)) 

n = n // 2 

if n == 1: 

return p 

pn = p**Rational(1, n) 

x = Symbol('x') 

f = _minimal_polynomial_sq(p, n, x) 

if f is None: 

return None 

sols = solve(f, x) 

for sol in sols: 

if abs(sol - pn).n() < 1./10**prec: 

sol = sqrtdenest(sol) 

if _mexpand(sol**n) == p: 

return sol 

 

 

def logcombine(expr, force=False): 

""" 

Takes logarithms and combines them using the following rules: 

 

- log(x) + log(y) == log(x*y) if both are not negative 

- a*log(x) == log(x**a) if x is positive and a is real 

 

If ``force`` is True then the assumptions above will be assumed to hold if 

there is no assumption already in place on a quantity. For example, if 

``a`` is imaginary or the argument negative, force will not perform a 

combination but if ``a`` is a symbol with no assumptions the change will 

take place. 

 

Examples 

======== 

 

>>> from sympy import Symbol, symbols, log, logcombine, I 

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

>>> logcombine(a*log(x) + log(y) - log(z)) 

a*log(x) + log(y) - log(z) 

>>> logcombine(a*log(x) + log(y) - log(z), force=True) 

log(x**a*y/z) 

>>> x,y,z = symbols('x,y,z', positive=True) 

>>> a = Symbol('a', real=True) 

>>> logcombine(a*log(x) + log(y) - log(z)) 

log(x**a*y/z) 

 

The transformation is limited to factors and/or terms that 

contain logs, so the result depends on the initial state of 

expansion: 

 

>>> eq = (2 + 3*I)*log(x) 

>>> logcombine(eq, force=True) == eq 

True 

>>> logcombine(eq.expand(), force=True) 

log(x**2) + I*log(x**3) 

 

See Also 

======== 

posify: replace all symbols with symbols having positive assumptions 

 

""" 

 

def f(rv): 

if not (rv.is_Add or rv.is_Mul): 

return rv 

 

def gooda(a): 

# bool to tell whether the leading ``a`` in ``a*log(x)`` 

# could appear as log(x**a) 

return (a is not S.NegativeOne and # -1 *could* go, but we disallow 

(a.is_real or force and a.is_real is not False)) 

 

def goodlog(l): 

# bool to tell whether log ``l``'s argument can combine with others 

a = l.args[0] 

return a.is_positive or force and a.is_nonpositive is not False 

 

other = [] 

logs = [] 

log1 = defaultdict(list) 

for a in Add.make_args(rv): 

if a.func is log and goodlog(a): 

log1[()].append(([], a)) 

elif not a.is_Mul: 

other.append(a) 

else: 

ot = [] 

co = [] 

lo = [] 

for ai in a.args: 

if ai.is_Rational and ai < 0: 

ot.append(S.NegativeOne) 

co.append(-ai) 

elif ai.func is log and goodlog(ai): 

lo.append(ai) 

elif gooda(ai): 

co.append(ai) 

else: 

ot.append(ai) 

if len(lo) > 1: 

logs.append((ot, co, lo)) 

elif lo: 

log1[tuple(ot)].append((co, lo[0])) 

else: 

other.append(a) 

 

# if there is only one log at each coefficient and none have 

# an exponent to place inside the log then there is nothing to do 

if not logs and all(len(log1[k]) == 1 and log1[k][0] == [] for k in log1): 

return rv 

 

# collapse multi-logs as far as possible in a canonical way 

# TODO: see if x*log(a)+x*log(a)*log(b) -> x*log(a)*(1+log(b))? 

# -- in this case, it's unambiguous, but if it were were a log(c) in 

# each term then it's arbitrary whether they are grouped by log(a) or 

# by log(c). So for now, just leave this alone; it's probably better to 

# let the user decide 

for o, e, l in logs: 

l = list(ordered(l)) 

e = log(l.pop(0).args[0]**Mul(*e)) 

while l: 

li = l.pop(0) 

e = log(li.args[0]**e) 

c, l = Mul(*o), e 

if l.func is log: # it should be, but check to be sure 

log1[(c,)].append(([], l)) 

else: 

other.append(c*l) 

 

# logs that have the same coefficient can multiply 

for k in list(log1.keys()): 

log1[Mul(*k)] = log(logcombine(Mul(*[ 

l.args[0]**Mul(*c) for c, l in log1.pop(k)]), 

force=force)) 

 

# logs that have oppositely signed coefficients can divide 

for k in ordered(list(log1.keys())): 

if not k in log1: # already popped as -k 

continue 

if -k in log1: 

# figure out which has the minus sign; the one with 

# more op counts should be the one 

num, den = k, -k 

if num.count_ops() > den.count_ops(): 

num, den = den, num 

other.append(num*log(log1.pop(num).args[0]/log1.pop(den).args[0])) 

else: 

other.append(k*log1.pop(k)) 

 

return Add(*other) 

 

return bottom_up(expr, f) 

 

 

def bottom_up(rv, F, atoms=False, nonbasic=False): 

"""Apply ``F`` to all expressions in an expression tree from the 

bottom up. If ``atoms`` is True, apply ``F`` even if there are no args; 

if ``nonbasic`` is True, try to apply ``F`` to non-Basic objects. 

""" 

try: 

if rv.args: 

args = tuple([bottom_up(a, F, atoms, nonbasic) 

for a in rv.args]) 

if args != rv.args: 

rv = rv.func(*args) 

rv = F(rv) 

elif atoms: 

rv = F(rv) 

except AttributeError: 

if nonbasic: 

try: 

rv = F(rv) 

except TypeError: 

pass 

 

return rv 

 

 

def besselsimp(expr): 

""" 

Simplify bessel-type functions. 

 

This routine tries to simplify bessel-type functions. Currently it only 

works on the Bessel J and I functions, however. It works by looking at all 

such functions in turn, and eliminating factors of "I" and "-1" (actually 

their polar equivalents) in front of the argument. Then, functions of 

half-integer order are rewritten using strigonometric functions and 

functions of integer order (> 1) are rewritten using functions 

of low order. Finally, if the expression was changed, compute 

factorization of the result with factor(). 

 

>>> from sympy import besselj, besseli, besselsimp, polar_lift, I, S 

>>> from sympy.abc import z, nu 

>>> besselsimp(besselj(nu, z*polar_lift(-1))) 

exp(I*pi*nu)*besselj(nu, z) 

>>> besselsimp(besseli(nu, z*polar_lift(-I))) 

exp(-I*pi*nu/2)*besselj(nu, z) 

>>> besselsimp(besseli(S(-1)/2, z)) 

sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z)) 

>>> besselsimp(z*besseli(0, z) + z*(besseli(2, z))/2 + besseli(1, z)) 

3*z*besseli(0, z)/2 

""" 

# TODO 

# - better algorithm? 

# - simplify (cos(pi*b)*besselj(b,z) - besselj(-b,z))/sin(pi*b) ... 

# - use contiguity relations? 

 

def replacer(fro, to, factors): 

factors = set(factors) 

 

def repl(nu, z): 

if factors.intersection(Mul.make_args(z)): 

return to(nu, z) 

return fro(nu, z) 

return repl 

 

def torewrite(fro, to): 

def tofunc(nu, z): 

return fro(nu, z).rewrite(to) 

return tofunc 

 

def tominus(fro): 

def tofunc(nu, z): 

return exp(I*pi*nu)*fro(nu, exp_polar(-I*pi)*z) 

return tofunc 

 

orig_expr = expr 

 

ifactors = [I, exp_polar(I*pi/2), exp_polar(-I*pi/2)] 

expr = expr.replace( 

besselj, replacer(besselj, 

torewrite(besselj, besseli), ifactors)) 

expr = expr.replace( 

besseli, replacer(besseli, 

torewrite(besseli, besselj), ifactors)) 

 

minusfactors = [-1, exp_polar(I*pi)] 

expr = expr.replace( 

besselj, replacer(besselj, tominus(besselj), minusfactors)) 

expr = expr.replace( 

besseli, replacer(besseli, tominus(besseli), minusfactors)) 

 

z0 = Dummy('z') 

 

def expander(fro): 

def repl(nu, z): 

if (nu % 1) == S(1)/2: 

return exptrigsimp(trigsimp(unpolarify( 

fro(nu, z0).rewrite(besselj).rewrite(jn).expand( 

func=True)).subs(z0, z))) 

elif nu.is_Integer and nu > 1: 

return fro(nu, z).expand(func=True) 

return fro(nu, z) 

return repl 

 

expr = expr.replace(besselj, expander(besselj)) 

expr = expr.replace(bessely, expander(bessely)) 

expr = expr.replace(besseli, expander(besseli)) 

expr = expr.replace(besselk, expander(besselk)) 

 

if expr != orig_expr: 

expr = expr.factor() 

 

return expr 

 

 

def nthroot(expr, n, max_len=4, prec=15): 

""" 

compute a real nth-root of a sum of surds 

 

Parameters 

========== 

 

expr : sum of surds 

n : integer 

max_len : maximum number of surds passed as constants to ``nsimplify`` 

 

Algorithm 

========= 

 

First ``nsimplify`` is used to get a candidate root; if it is not a 

root the minimal polynomial is computed; the answer is one of its 

roots. 

 

Examples 

======== 

 

>>> from sympy.simplify.simplify import nthroot 

>>> from sympy import Rational, sqrt 

>>> nthroot(90 + 34*sqrt(7), 3) 

sqrt(7) + 3 

 

""" 

expr = sympify(expr) 

n = sympify(n) 

p = expr**Rational(1, n) 

if not n.is_integer: 

return p 

if not _is_sum_surds(expr): 

return p 

surds = [] 

coeff_muls = [x.as_coeff_Mul() for x in expr.args] 

for x, y in coeff_muls: 

if not x.is_rational: 

return p 

if y is S.One: 

continue 

if not (y.is_Pow and y.exp == S.Half and y.base.is_integer): 

return p 

surds.append(y) 

surds.sort() 

surds = surds[:max_len] 

if expr < 0 and n % 2 == 1: 

p = (-expr)**Rational(1, n) 

a = nsimplify(p, constants=surds) 

res = a if _mexpand(a**n) == _mexpand(-expr) else p 

return -res 

a = nsimplify(p, constants=surds) 

if _mexpand(a) is not _mexpand(p) and _mexpand(a**n) == _mexpand(expr): 

return _mexpand(a) 

expr = _nthroot_solve(expr, n, prec) 

if expr is None: 

return p 

return expr 

 

 

def nsimplify(expr, constants=[], tolerance=None, full=False, rational=None): 

""" 

Find a simple representation for a number or, if there are free symbols or 

if rational=True, then replace Floats with their Rational equivalents. If 

no change is made and rational is not False then Floats will at least be 

converted to Rationals. 

 

For numerical expressions, a simple formula that numerically matches the 

given numerical expression is sought (and the input should be possible 

to evalf to a precision of at least 30 digits). 

 

Optionally, a list of (rationally independent) constants to 

include in the formula may be given. 

 

A lower tolerance may be set to find less exact matches. If no tolerance 

is given then the least precise value will set the tolerance (e.g. Floats 

default to 15 digits of precision, so would be tolerance=10**-15). 

 

With full=True, a more extensive search is performed 

(this is useful to find simpler numbers when the tolerance 

is set low). 

 

Examples 

======== 

 

>>> from sympy import nsimplify, sqrt, GoldenRatio, exp, I, exp, pi 

>>> nsimplify(4/(1+sqrt(5)), [GoldenRatio]) 

-2 + 2*GoldenRatio 

>>> nsimplify((1/(exp(3*pi*I/5)+1))) 

1/2 - I*sqrt(sqrt(5)/10 + 1/4) 

>>> nsimplify(I**I, [pi]) 

exp(-pi/2) 

>>> nsimplify(pi, tolerance=0.01) 

22/7 

 

See Also 

======== 

sympy.core.function.nfloat 

 

""" 

try: 

return sympify(as_int(expr)) 

except (TypeError, ValueError): 

pass 

expr = sympify(expr).xreplace({ 

Float('inf'): S.Infinity, 

Float('-inf'): S.NegativeInfinity, 

}) 

if expr is S.Infinity or expr is S.NegativeInfinity: 

return expr 

if rational or expr.free_symbols: 

return _real_to_rational(expr, tolerance) 

 

# SymPy's default tolerance for Rationals is 15; other numbers may have 

# lower tolerances set, so use them to pick the largest tolerance if None 

# was given 

if tolerance is None: 

tolerance = 10**-min([15] + 

[mpmath.libmp.libmpf.prec_to_dps(n._prec) 

for n in expr.atoms(Float)]) 

# XXX should prec be set independent of tolerance or should it be computed 

# from tolerance? 

prec = 30 

bprec = int(prec*3.33) 

 

constants_dict = {} 

for constant in constants: 

constant = sympify(constant) 

v = constant.evalf(prec) 

if not v.is_Float: 

raise ValueError("constants must be real-valued") 

constants_dict[str(constant)] = v._to_mpmath(bprec) 

 

exprval = expr.evalf(prec, chop=True) 

re, im = exprval.as_real_imag() 

 

# safety check to make sure that this evaluated to a number 

if not (re.is_Number and im.is_Number): 

return expr 

 

def nsimplify_real(x): 

orig = mpmath.mp.dps 

xv = x._to_mpmath(bprec) 

try: 

# We'll be happy with low precision if a simple fraction 

if not (tolerance or full): 

mpmath.mp.dps = 15 

rat = mpmath.pslq([xv, 1]) 

if rat is not None: 

return Rational(-int(rat[1]), int(rat[0])) 

mpmath.mp.dps = prec 

newexpr = mpmath.identify(xv, constants=constants_dict, 

tol=tolerance, full=full) 

if not newexpr: 

raise ValueError 

if full: 

newexpr = newexpr[0] 

expr = sympify(newexpr) 

if x and not expr: # don't let x become 0 

raise ValueError 

if expr.is_finite is False and not xv in [mpmath.inf, mpmath.ninf]: 

raise ValueError 

return expr 

finally: 

# even though there are returns above, this is executed 

# before leaving 

mpmath.mp.dps = orig 

try: 

if re: 

re = nsimplify_real(re) 

if im: 

im = nsimplify_real(im) 

except ValueError: 

if rational is None: 

return _real_to_rational(expr) 

return expr 

 

rv = re + im*S.ImaginaryUnit 

# if there was a change or rational is explicitly not wanted 

# return the value, else return the Rational representation 

if rv != expr or rational is False: 

return rv 

return _real_to_rational(expr) 

 

 

def _real_to_rational(expr, tolerance=None): 

""" 

Replace all reals in expr with rationals. 

 

>>> from sympy import nsimplify 

>>> from sympy.abc import x 

 

>>> nsimplify(.76 + .1*x**.5, rational=True) 

sqrt(x)/10 + 19/25 

 

""" 

inf = Float('inf') 

p = expr 

reps = {} 

reduce_num = None 

if tolerance is not None and tolerance < 1: 

reduce_num = ceiling(1/tolerance) 

for float in p.atoms(Float): 

key = float 

if reduce_num is not None: 

r = Rational(float).limit_denominator(reduce_num) 

elif (tolerance is not None and tolerance >= 1 and 

float.is_Integer is False): 

r = Rational(tolerance*round(float/tolerance) 

).limit_denominator(int(tolerance)) 

else: 

r = nsimplify(float, rational=False) 

# e.g. log(3).n() -> log(3) instead of a Rational 

if float and not r: 

r = Rational(float) 

elif not r.is_Rational: 

if float == inf or float == -inf: 

r = S.ComplexInfinity 

elif float < 0: 

float = -float 

d = Pow(10, int((mpmath.log(float)/mpmath.log(10)))) 

r = -Rational(str(float/d))*d 

elif float > 0: 

d = Pow(10, int((mpmath.log(float)/mpmath.log(10)))) 

r = Rational(str(float/d))*d 

else: 

r = Integer(0) 

reps[key] = r 

return p.subs(reps, simultaneous=True) 

 

 

def clear_coefficients(expr, rhs=S.Zero): 

"""Return `p, r` where `p` is the expression obtained when Rational 

additive and multiplicative coefficients of `expr` have been stripped 

away in a naive fashion (i.e. without simplification). The operations 

needed to remove the coefficients will be applied to `rhs` and returned 

as `r`. 

 

Examples 

======== 

 

>>> from sympy.simplify.simplify import clear_coefficients 

>>> from sympy.abc import x, y 

>>> from sympy import Dummy 

>>> expr = 4*y*(6*x + 3) 

>>> clear_coefficients(expr - 2) 

(y*(2*x + 1), 1/6) 

 

When solving 2 or more expressions like `expr = a`, 

`expr = b`, etc..., it is advantageous to provide a Dummy symbol 

for `rhs` and simply replace it with `a`, `b`, etc... in `r`. 

 

>>> rhs = Dummy('rhs') 

>>> clear_coefficients(expr, rhs) 

(y*(2*x + 1), _rhs/12) 

>>> _[1].subs(rhs, 2) 

1/6 

""" 

was = None 

free = expr.free_symbols 

if expr.is_Rational: 

return (S.Zero, rhs - expr) 

while expr and was != expr: 

was = expr 

m, expr = ( 

expr.as_content_primitive() 

if free else 

factor_terms(expr).as_coeff_Mul(rational=True)) 

rhs /= m 

c, expr = expr.as_coeff_Add(rational=True) 

rhs -= c 

expr = signsimp(expr, evaluate = False) 

if _coeff_isneg(expr): 

expr = -expr 

rhs = -rhs 

return expr, rhs